Restore dependency-free i18n foundation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-05-15 10:34:33 -05:00
1 parent e8f3e9764c
commit 1abf637864
3 files changed
+19 -40

No files matched your search

-3
View File
@@ -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",
+19 -18
View File
@@ -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<string, unknown>)[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 };
}
-19
View File
@@ -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;