80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import * as i18n from "@solid-primitives/i18n"
|
|
import { createEffect, createMemo } from "solid-js"
|
|
import { createStore } from "solid-js/store"
|
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
|
import { Persist, persisted } from "@/utils/persist"
|
|
import { dict as en } from "@/i18n/en"
|
|
import { dict as zh } from "@/i18n/zh"
|
|
import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
|
|
import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
|
|
|
|
export type Locale = "en" | "zh"
|
|
|
|
type RawDictionary = typeof en & typeof uiEn
|
|
type Dictionary = i18n.Flatten<RawDictionary>
|
|
|
|
const LOCALES: readonly Locale[] = ["en", "zh"]
|
|
|
|
function detectLocale(): Locale {
|
|
if (typeof navigator !== "object") return "en"
|
|
|
|
const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
|
|
for (const language of languages) {
|
|
if (!language) continue
|
|
if (language.toLowerCase().startsWith("zh")) return "zh"
|
|
}
|
|
|
|
return "en"
|
|
}
|
|
|
|
export const { use: useLanguage, provider: LanguageProvider } = createSimpleContext({
|
|
name: "Language",
|
|
init: () => {
|
|
const [store, setStore, _, ready] = persisted(
|
|
Persist.global("language", ["language.v1"]),
|
|
createStore({
|
|
locale: detectLocale() as Locale,
|
|
}),
|
|
)
|
|
|
|
const locale = createMemo<Locale>(() => (store.locale === "zh" ? "zh" : "en"))
|
|
|
|
createEffect(() => {
|
|
const current = locale()
|
|
if (store.locale === current) return
|
|
setStore("locale", current)
|
|
})
|
|
|
|
const base = i18n.flatten({ ...en, ...uiEn })
|
|
const dict = createMemo<Dictionary>(() => {
|
|
if (locale() === "en") return base
|
|
return { ...base, ...i18n.flatten({ ...zh, ...uiZh }) }
|
|
})
|
|
|
|
const t = i18n.translator(dict, i18n.resolveTemplate)
|
|
|
|
const labelKey: Record<Locale, keyof Dictionary> = {
|
|
en: "language.en",
|
|
zh: "language.zh",
|
|
}
|
|
|
|
const label = (value: Locale) => t(labelKey[value])
|
|
|
|
createEffect(() => {
|
|
if (typeof document !== "object") return
|
|
document.documentElement.lang = locale()
|
|
})
|
|
|
|
return {
|
|
ready,
|
|
locale,
|
|
locales: LOCALES,
|
|
label,
|
|
t,
|
|
setLocale(next: Locale) {
|
|
setStore("locale", next)
|
|
},
|
|
}
|
|
},
|
|
})
|