9e3f1d6b59
- add SSR-safe I18nProvider, Translate, locale hooks, catalog activation, and typed runtime APIs - add consumer-owned Lingui configuration helpers and a CLI for locale creation, extraction, compilation, and project discovery - add catalog read/write services plus a Vite development API plugin scoped to each consuming application - add the portal-based I18nDevtool, message editor, dark-theme tracking, and standalone Message Studio - document the consumer workflow and cover runtime, configuration, catalog, CLI, and Devtool behavior with tests
123 lines
2.8 KiB
TypeScript
123 lines
2.8 KiB
TypeScript
import * as React from "react"
|
|
import { setupI18n, type AllMessages, type I18n } from "@lingui/core"
|
|
import {
|
|
I18nProvider as LinguiI18nProvider,
|
|
type I18nProviderProps as LinguiI18nProviderProps,
|
|
} from "@lingui/react"
|
|
|
|
import { I18nRuntimeContext } from "./context"
|
|
import type { LocaleDefinition, LocaleDirection, LocaleInput } from "./types"
|
|
|
|
const RTL_LANGUAGES = new Set([
|
|
"ar",
|
|
"ckb",
|
|
"dv",
|
|
"fa",
|
|
"he",
|
|
"ku",
|
|
"ps",
|
|
"sd",
|
|
"ug",
|
|
"ur",
|
|
"yi",
|
|
])
|
|
|
|
function inferLocaleDirection(locale: string): LocaleDirection {
|
|
const language = locale.split("-")[0]?.toLowerCase()
|
|
|
|
return language && RTL_LANGUAGES.has(language) ? "rtl" : "ltr"
|
|
}
|
|
|
|
function normalizeLocales(
|
|
locale: string,
|
|
locales: readonly LocaleInput[] | undefined,
|
|
catalogs: AllMessages
|
|
): readonly LocaleDefinition[] {
|
|
const inputs =
|
|
locales && locales.length > 0
|
|
? locales
|
|
: Array.from(new Set([locale, ...Object.keys(catalogs)]))
|
|
|
|
const definitions = inputs.map<LocaleDefinition>((input) => {
|
|
const definition = typeof input === "string" ? { locale: input } : input
|
|
|
|
return {
|
|
...definition,
|
|
direction:
|
|
definition.direction ?? inferLocaleDirection(definition.locale),
|
|
label: definition.label ?? definition.locale,
|
|
}
|
|
})
|
|
|
|
if (!definitions.some((definition) => definition.locale === locale)) {
|
|
return [
|
|
...definitions,
|
|
{
|
|
direction: inferLocaleDirection(locale),
|
|
label: locale,
|
|
locale,
|
|
},
|
|
]
|
|
}
|
|
|
|
return definitions
|
|
}
|
|
|
|
export interface I18nProviderProps extends Pick<
|
|
LinguiI18nProviderProps,
|
|
"children" | "defaultComponent"
|
|
> {
|
|
catalogs?: AllMessages
|
|
i18n?: I18n
|
|
locale: string
|
|
locales?: readonly LocaleInput[]
|
|
missing?: ConstructorParameters<typeof I18n>[0]["missing"]
|
|
}
|
|
|
|
export function I18nProvider({
|
|
catalogs = {},
|
|
children,
|
|
defaultComponent,
|
|
i18n: providedI18n,
|
|
locale,
|
|
locales,
|
|
missing,
|
|
}: I18nProviderProps) {
|
|
const i18n = React.useMemo(() => {
|
|
const instance =
|
|
providedI18n ??
|
|
setupI18n({
|
|
locale,
|
|
messages: catalogs,
|
|
missing,
|
|
})
|
|
|
|
if (providedI18n) {
|
|
providedI18n.load(catalogs)
|
|
providedI18n.activate(locale)
|
|
}
|
|
|
|
return instance
|
|
}, [catalogs, locale, missing, providedI18n])
|
|
const normalizedLocales = React.useMemo(
|
|
() => normalizeLocales(locale, locales, catalogs),
|
|
[catalogs, locale, locales]
|
|
)
|
|
const contextValue = React.useMemo(
|
|
() => ({
|
|
catalogs,
|
|
locale,
|
|
locales: normalizedLocales,
|
|
}),
|
|
[catalogs, locale, normalizedLocales]
|
|
)
|
|
|
|
return (
|
|
<I18nRuntimeContext.Provider value={contextValue}>
|
|
<LinguiI18nProvider i18n={i18n} defaultComponent={defaultComponent}>
|
|
{children}
|
|
</LinguiI18nProvider>
|
|
</I18nRuntimeContext.Provider>
|
|
)
|
|
}
|