feat(app): initial i18n stubbing

This commit is contained in:
Adam
2026-01-19 15:50:23 -06:00
parent 7f50b27996
commit 0470717c7f
7 changed files with 151 additions and 9 deletions

View File

@@ -0,0 +1,77 @@
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"
export type Locale = "en" | "zh"
type RawDictionary = typeof en
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)
const dict = createMemo<Dictionary>(() => {
if (locale() === "en") return base
return { ...base, ...i18n.flatten(zh) }
})
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)
},
}
},
})