File size: 2,403 Bytes
eb1202c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { createContext, useContext, type Accessor, type ParentProps } from "solid-js"
import { I18nProvider } from "@kobalte/core/i18n"
import { dict as en } from "../i18n/en"

export type UiI18nKey = keyof typeof en

export const UI_PLURAL_KEYS = [
  "ui.sessionTurn.diffs.changed",
  "ui.messagePart.context.read",
  "ui.messagePart.context.search",
  "ui.messagePart.context.list",
] as const
export type UiI18nPluralKey = (typeof UI_PLURAL_KEYS)[number]
export type UiPluralCategory = "zero" | "one" | "two" | "few" | "many" | "other"
export type UiI18nPluralLookupKey = `${UiI18nPluralKey}.${UiPluralCategory}`

export type UiI18nParams = Record<string, string | number | boolean>

export type UiI18n = {
  locale: Accessor<string>
  layoutLocale?: Accessor<string>
  t: (key: UiI18nKey, params?: UiI18nParams) => string
  plural: (key: UiI18nPluralKey, count: number, params?: UiI18nParams) => string
}

const rules = new Map<string, Intl.PluralRules>()

export function pluralCategory(locale: string, count: number): UiPluralCategory {
  const cached = rules.get(locale)
  if (cached) return cached.select(count)
  const next = new Intl.PluralRules(locale)
  if (rules.size >= 32) rules.delete(rules.keys().next().value!)
  rules.set(locale, next)
  return next.select(count)
}

export function pluralKey(key: UiI18nPluralKey, category: UiPluralCategory) {
  return `${key}.${category}` as UiI18nPluralLookupKey
}

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)
  })
}

const fallback: UiI18n = {
  locale: () => "en",
  t: (key, params) => {
    const value = en[key] ?? String(key)
    return resolveTemplate(value, params)
  },
  plural: (key, count, params) =>
    fallback.t(pluralKey(key, pluralCategory(fallback.locale(), count)), { ...params, count }),
}

const Context = createContext<UiI18n>(fallback)

function UiI18nProvider(props: ParentProps<{ value: UiI18n }>) {
  return (
    <I18nProvider locale={(props.value.layoutLocale ?? props.value.locale)()}>
      <Context.Provider value={props.value}>{props.children}</Context.Provider>
    </I18nProvider>
  )
}

export { UiI18nProvider as I18nProvider }

export function useI18n() {
  return useContext(Context)
}