summaryrefslogtreecommitdiffhomepage
path: root/packages/enterprise/src/app.tsx
blob: b73888ec5d6c670e7ed7d25cb9e59081595a48c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { Router } from "@solidjs/router"
import { FileRoutes } from "@solidjs/start/router"
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 { 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 zh = uiZh as Partial<Record<string, string>>
  const t = (key: keyof typeof uiEn, params?: UiI18nParams) => {
    const value = locale() === "zh" ? (zh[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
      root={(props) => (
        <MetaProvider>
          <DialogProvider>
            <MarkedProvider>
              <Favicon />
              <Font />
              <UiI18nBridge>
                <Suspense>{props.children}</Suspense>
              </UiI18nBridge>
            </MarkedProvider>
          </DialogProvider>
        </MetaProvider>
      )}
    >
      <FileRoutes />
    </Router>
  )
}