summaryrefslogtreecommitdiffhomepage
path: root/packages/enterprise/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/enterprise/src')
-rw-r--r--packages/enterprise/src/app.tsx68
-rw-r--r--packages/enterprise/src/entry-server.tsx48
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>
+ )
+ }}
/>
))