diff options
Diffstat (limited to 'packages/enterprise/src')
| -rw-r--r-- | packages/enterprise/src/app.tsx | 68 | ||||
| -rw-r--r-- | packages/enterprise/src/entry-server.tsx | 48 |
2 files changed, 98 insertions, 18 deletions
diff --git a/packages/enterprise/src/app.tsx b/packages/enterprise/src/app.tsx index 0fd3a009c..1d408ac94 100644 --- a/packages/enterprise/src/app.tsx +++ b/packages/enterprise/src/app.tsx @@ -4,10 +4,72 @@ import { Font } from "@opencode-ai/ui/font" import { MetaProvider } from "@solidjs/meta" import { MarkedProvider } from "@opencode-ai/ui/context/marked" import { DialogProvider } from "@opencode-ai/ui/context/dialog" -import { Suspense } from "solid-js" +import { I18nProvider, type UiI18nParams } from "@opencode-ai/ui/context" +import { dict as uiEn } from "@opencode-ai/ui/i18n/en" +import { dict as uiZh } from "@opencode-ai/ui/i18n/zh" +import { createEffect, createMemo, Suspense, type ParentProps } from "solid-js" +import { getRequestEvent } from "solid-js/web" import "./app.css" import { Favicon } from "@opencode-ai/ui/favicon" +function resolveTemplate(text: string, params?: UiI18nParams) { + if (!params) return text + return text.replace(/{{\s*([^}]+?)\s*}}/g, (_, rawKey) => { + const key = String(rawKey) + const value = params[key] + return value === undefined ? "" : String(value) + }) +} + +function detectLocaleFromHeader(header: string | null | undefined) { + if (!header) return + for (const item of header.split(",")) { + const value = item.trim().split(";")[0]?.toLowerCase() + if (!value) continue + if (value.startsWith("zh")) return "zh" as const + if (value.startsWith("en")) return "en" as const + } +} + +function detectLocale() { + const event = getRequestEvent() + const header = event?.request.headers.get("accept-language") + const headerLocale = detectLocaleFromHeader(header) + if (headerLocale) return headerLocale + + if (typeof document === "object") { + const value = document.documentElement.lang?.toLowerCase() ?? "" + if (value.startsWith("zh")) return "zh" as const + if (value.startsWith("en")) return "en" as const + } + + if (typeof navigator === "object") { + const languages = navigator.languages?.length ? navigator.languages : [navigator.language] + for (const language of languages) { + if (!language) continue + if (language.toLowerCase().startsWith("zh")) return "zh" as const + } + } + + return "en" as const +} + +function UiI18nBridge(props: ParentProps) { + const locale = createMemo(() => detectLocale()) + const t = (key: keyof typeof uiEn, params?: UiI18nParams) => { + const value = locale() === "zh" ? uiZh[key] ?? uiEn[key] : uiEn[key] + const text = value ?? String(key) + return resolveTemplate(text, params) + } + + createEffect(() => { + if (typeof document !== "object") return + document.documentElement.lang = locale() + }) + + return <I18nProvider value={{ locale, t }}>{props.children}</I18nProvider> +} + export default function App() { return ( <Router @@ -17,7 +79,9 @@ export default function App() { <MarkedProvider> <Favicon /> <Font /> - <Suspense>{props.children}</Suspense> + <UiI18nBridge> + <Suspense>{props.children}</Suspense> + </UiI18nBridge> </MarkedProvider> </DialogProvider> </MetaProvider> diff --git a/packages/enterprise/src/entry-server.tsx b/packages/enterprise/src/entry-server.tsx index 989c3c088..b61448c95 100644 --- a/packages/enterprise/src/entry-server.tsx +++ b/packages/enterprise/src/entry-server.tsx @@ -1,23 +1,39 @@ // @refresh reload import { createHandler, StartServer } from "@solidjs/start/server" +import { getRequestEvent } from "solid-js/web" export default createHandler(() => ( <StartServer - document={({ assets, children, scripts }) => ( - <html lang="en"> - <head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <title>OpenCode</title> - <meta name="theme-color" content="#F8F7F7" /> - <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" /> - {assets} - </head> - <body class="antialiased overscroll-none text-12-regular"> - <div id="app">{children}</div> - {scripts} - </body> - </html> - )} + document={({ assets, children, scripts }) => { + const lang = (() => { + const event = getRequestEvent() + const header = event?.request.headers.get("accept-language") + if (!header) return "en" + for (const item of header.split(",")) { + const value = item.trim().split(";")[0]?.toLowerCase() + if (!value) continue + if (value.startsWith("zh")) return "zh" + if (value.startsWith("en")) return "en" + } + return "en" + })() + + return ( + <html lang={lang}> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <title>OpenCode</title> + <meta name="theme-color" content="#F8F7F7" /> + <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" /> + {assets} + </head> + <body class="antialiased overscroll-none text-12-regular"> + <div id="app">{children}</div> + {scripts} + </body> + </html> + ) + }} /> )) |
