diff options
| author | Adam <[email protected]> | 2026-02-06 08:54:51 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-06 08:54:51 -0600 |
| commit | 812597bb8b101896a8988493d37261ff851ae502 (patch) | |
| tree | 3b713c13f49c65665e6a7296ed483a3f6f8befbf /packages/console/app/src/context | |
| parent | 0ec5f6608bdfea5be62dbbdc4c04a61de6d3e67c (diff) | |
| download | opencode-812597bb8b101896a8988493d37261ff851ae502.tar.gz opencode-812597bb8b101896a8988493d37261ff851ae502.zip | |
feat(web): i18n (#12471)
Diffstat (limited to 'packages/console/app/src/context')
| -rw-r--r-- | packages/console/app/src/context/i18n.tsx | 27 | ||||
| -rw-r--r-- | packages/console/app/src/context/language.tsx | 68 |
2 files changed, 95 insertions, 0 deletions
diff --git a/packages/console/app/src/context/i18n.tsx b/packages/console/app/src/context/i18n.tsx new file mode 100644 index 000000000..5d178c8b8 --- /dev/null +++ b/packages/console/app/src/context/i18n.tsx @@ -0,0 +1,27 @@ +import { createMemo } from "solid-js" +import { createSimpleContext } from "@opencode-ai/ui/context" +import { i18n, type Key } from "~/i18n" +import { useLanguage } from "~/context/language" + +function resolve(text: string, params?: Record<string, string | number>) { + if (!params) return text + return text.replace(/\{\{(\w+)\}\}/g, (raw, key) => { + const value = params[key] + if (value === undefined || value === null) return raw + return String(value) + }) +} + +export const { use: useI18n, provider: I18nProvider } = createSimpleContext({ + name: "I18n", + init: () => { + const language = useLanguage() + const dict = createMemo(() => i18n(language.locale())) + + return { + t(key: Key, params?: Record<string, string | number>) { + return resolve(dict()[key], params) + }, + } + }, +}) diff --git a/packages/console/app/src/context/language.tsx b/packages/console/app/src/context/language.tsx new file mode 100644 index 000000000..7e3e5286c --- /dev/null +++ b/packages/console/app/src/context/language.tsx @@ -0,0 +1,68 @@ +import { createEffect } from "solid-js" +import { createStore } from "solid-js/store" +import { getRequestEvent } from "solid-js/web" +import { createSimpleContext } from "@opencode-ai/ui/context" +import { + LOCALES, + type Locale, + clearCookie, + cookie, + detectFromLanguages, + dir as localeDir, + label as localeLabel, + localeFromCookieHeader, + localeFromRequest, + parseLocale, + tag as localeTag, +} from "~/lib/language" + +function initial() { + const evt = getRequestEvent() + if (evt) return localeFromRequest(evt.request) + + if (typeof document === "object") { + const fromDom = parseLocale(document.documentElement.dataset.locale) + if (fromDom) return fromDom + + const fromCookie = localeFromCookieHeader(document.cookie) + if (fromCookie) return fromCookie + } + + if (typeof navigator !== "object") return "en" satisfies Locale + + const languages = navigator.languages?.length ? navigator.languages : [navigator.language] + return detectFromLanguages(languages) +} + +export const { use: useLanguage, provider: LanguageProvider } = createSimpleContext({ + name: "Language", + init: () => { + const [store, setStore] = createStore({ + locale: initial(), + }) + + createEffect(() => { + if (typeof document !== "object") return + document.documentElement.lang = localeTag(store.locale) + document.documentElement.dir = localeDir(store.locale) + document.documentElement.dataset.locale = store.locale + }) + + return { + locale: () => store.locale, + locales: LOCALES, + label: localeLabel, + tag: localeTag, + dir: localeDir, + setLocale(next: Locale) { + setStore("locale", next) + if (typeof document !== "object") return + document.cookie = cookie(next) + }, + clear() { + if (typeof document !== "object") return + document.cookie = clearCookie() + }, + } + }, +}) |
