diff --git a/ui/package.json b/ui/package.json index a73c568ed2d..65caa25ee74 100644 --- a/ui/package.json +++ b/ui/package.json @@ -20,7 +20,6 @@ "build-storybook": "storybook build -c storybook/.storybook -o storybook-static", "preview": "vite preview", "typecheck": "tsc -b", - "test": "vitest run", "clean": "rm -rf dist storybook-static tsconfig.tsbuildinfo", "prepack": "rm -f package.dev.json && cp package.json package.dev.json && node ../scripts/generate-ui-package-json.mjs", "postpack": "if [ -f package.dev.json ]; then mv package.dev.json package.json; fi" @@ -53,14 +52,12 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "hermes-paperclip-adapter": "^0.2.0", - "i18next": "^26.1.0", "lexical": "0.35.0", "lucide-react": "^0.574.0", "mermaid": "^11.12.0", "radix-ui": "^1.4.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-i18next": "^17.0.7", "react-markdown": "^10.1.0", "react-router-dom": "^7.1.5", "remark-gfm": "^4.0.1", diff --git a/ui/src/i18n/index.ts b/ui/src/i18n/index.ts index 2ab68d20d6b..ee0f1974da5 100644 --- a/ui/src/i18n/index.ts +++ b/ui/src/i18n/index.ts @@ -1,24 +1,25 @@ -import i18n, { type InitOptions, type TOptions } from "i18next"; -import { initReactI18next, useTranslation as useReactI18nextTranslation } from "react-i18next"; +import { DEFAULT_LOCALE, localeMessages } from "./locales"; -import { DEFAULT_LOCALE, i18nextResources, supportedLocales } from "./locales"; - -const i18nextOptions: InitOptions = { - resources: i18nextResources, - lng: DEFAULT_LOCALE, - fallbackLng: DEFAULT_LOCALE, - supportedLngs: supportedLocales, - defaultNS: "translation", - interpolation: { escapeValue: false }, - returnObjects: false, - initAsync: false, +type TranslationOptions = { + defaultValue?: string; }; -i18n.use(initReactI18next).init(i18nextOptions); +function resolveLocaleValue(key: string) { + const path = key.split("."); + let value: unknown = localeMessages[DEFAULT_LOCALE]; -export function t(key: string, options: TOptions = {}) { - return i18n.t(key, options); + for (const segment of path) { + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + value = (value as Record)[segment]; + } + + return typeof value === "string" ? value : null; } -export const useTranslation = useReactI18nextTranslation; -export { i18n }; +export function t(key: string, options: TranslationOptions = {}) { + return resolveLocaleValue(key) ?? options.defaultValue ?? key; +} + +export function useTranslation() { + return { t }; +} diff --git a/ui/src/i18n/locales.ts b/ui/src/i18n/locales.ts index 188ee2b117d..c900f001fa9 100644 --- a/ui/src/i18n/locales.ts +++ b/ui/src/i18n/locales.ts @@ -1,7 +1,3 @@ -import type { Resource } from "i18next"; - -import { assertValidLocaleMessages } from "./locale-validation"; - export const DEFAULT_LOCALE = "en" as const; const localeModules = import.meta.glob("./locales/*.json", { @@ -23,19 +19,4 @@ if (!(DEFAULT_LOCALE in localeMessages)) { throw new Error(`Missing default locale messages for ${DEFAULT_LOCALE}`); } -for (const [locale, messages] of Object.entries(localeMessages)) { - try { - assertValidLocaleMessages(messages); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - throw new Error(`Invalid ${locale} locale messages: ${message}`); - } -} - -export const supportedLocales = Object.keys(localeMessages); - -export const i18nextResources: Resource = Object.fromEntries( - Object.entries(localeMessages).map(([locale, messages]) => [locale, { translation: messages }]), -) as Resource; - export type SupportedLocale = keyof typeof localeMessages;