evOo-local/apps/web/src/lib/i18n-activate.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

191 lines
6.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import {
activateUiLocale,
getActiveUiLocale,
i18n,
setCatalogLoadersForTests,
setUiLocale,
} from "./i18n";
import { UI_LOCALE_STORAGE_KEY } from "./ui-locale";
vi.mock("./apply-ui-direction", () => ({
applyUiDirection: vi.fn(),
}));
function stubLocaleStorage() {
const store = new Map<string, string>();
vi.stubGlobal("localStorage", {
getItem: (key: string) => store.get(key) ?? null,
setItem: (key: string, value: string) => {
store.set(key, value);
},
removeItem: (key: string) => {
store.delete(key);
},
clear: () => {
store.clear();
},
key: () => null,
length: 0,
});
return store;
}
describe("activateUiLocale", () => {
beforeEach(() => {
setCatalogLoadersForTests(null);
i18n.load("en", {});
i18n.activate("en");
vi.unstubAllGlobals();
});
it("falls back to English when the preferred catalog fails to load", async () => {
setCatalogLoadersForTests({
en: async () => ({ messages: { Settings: "Settings" } }),
de: async () => {
throw new Error("de catalog missing");
},
ko: async () => ({ messages: { Settings: "설정" } }),
tr: async () => ({ messages: { Settings: "Ayarlar" } }),
hi: async () => ({ messages: { Settings: "सेटिंग्स" } }),
"pt-BR": async () => ({ messages: { Settings: "Configurações" } }),
"zh-CN": async () => ({ messages: { Settings: "设置" } }),
es: async () => ({ messages: { Settings: "Configuración" } }),
ru: async () => ({ messages: { Settings: "Настройки" } }),
});
const locale = await activateUiLocale("de");
expect(locale).toBe("en");
expect(getActiveUiLocale()).toBe("en");
expect(i18n.locale).toBe("en");
expect(i18n._({ id: "Settings", message: "Settings" })).toBe("Settings");
});
it("falls back to empty English when every catalog fails", async () => {
setCatalogLoadersForTests({
en: async () => {
throw new Error("en missing");
},
de: async () => {
throw new Error("de missing");
},
ko: async () => {
throw new Error("ko missing");
},
tr: async () => {
throw new Error("tr missing");
},
hi: async () => {
throw new Error("hi missing");
},
"pt-BR": async () => {
throw new Error("pt-BR missing");
},
"zh-CN": async () => {
throw new Error("zh-CN missing");
},
es: async () => {
throw new Error("es missing");
},
ru: async () => {
throw new Error("ru missing");
},
});
const locale = await activateUiLocale("ko");
expect(locale).toBe("en");
expect(i18n.locale).toBe("en");
});
it("lets the latest locale selection win under concurrency", async () => {
let resolveDe!: (value: { messages: Record<string, string> }) => void;
const dePromise = new Promise<{ messages: Record<string, string> }>((resolve) => {
resolveDe = resolve;
});
setCatalogLoadersForTests({
en: async () => ({ messages: { Settings: "Settings" } }),
de: async () => dePromise,
ko: async () => ({ messages: { Settings: "설정" } }),
tr: async () => ({ messages: { Settings: "Ayarlar" } }),
hi: async () => ({ messages: { Settings: "सेटिंग्स" } }),
"pt-BR": async () => ({ messages: { Settings: "Configurações" } }),
"zh-CN": async () => ({ messages: { Settings: "设置" } }),
es: async () => ({ messages: { Settings: "Configuración" } }),
ru: async () => ({ messages: { Settings: "Настройки" } }),
});
const first = activateUiLocale("de");
const second = activateUiLocale("ko");
resolveDe({ messages: { Settings: "Einstellungen" } });
await expect(first).resolves.toBe("ko");
await expect(second).resolves.toBe("ko");
expect(getActiveUiLocale()).toBe("ko");
expect(i18n._({ id: "Settings", message: "Settings" })).toBe("설정");
});
it("cancels a pending activation when re-selecting the already-active locale", async () => {
let resolveDe!: (value: { messages: Record<string, string> }) => void;
const dePromise = new Promise<{ messages: Record<string, string> }>((resolve) => {
resolveDe = resolve;
});
setCatalogLoadersForTests({
en: async () => ({ messages: { Settings: "Settings" } }),
de: async () => dePromise,
ko: async () => ({ messages: { Settings: "설정" } }),
tr: async () => ({ messages: { Settings: "Ayarlar" } }),
hi: async () => ({ messages: { Settings: "सेटिंग्स" } }),
"pt-BR": async () => ({ messages: { Settings: "Configurações" } }),
"zh-CN": async () => ({ messages: { Settings: "设置" } }),
es: async () => ({ messages: { Settings: "Configuración" } }),
ru: async () => ({ messages: { Settings: "Настройки" } }),
});
await activateUiLocale("en");
expect(getActiveUiLocale()).toBe("en");
const pendingDe = activateUiLocale("de");
const backToEn = activateUiLocale("en");
resolveDe({ messages: { Settings: "Einstellungen" } });
await expect(pendingDe).resolves.toBe("en");
await expect(backToEn).resolves.toBe("en");
expect(getActiveUiLocale()).toBe("en");
expect(i18n.locale).toBe("en");
expect(i18n._({ id: "Settings", message: "Settings" })).toBe("Settings");
});
it("does not let a stale setUiLocale fallback overwrite the latest stored locale", async () => {
const store = stubLocaleStorage();
let resolveDe!: (value: { messages: Record<string, string> }) => void;
const dePromise = new Promise<{ messages: Record<string, string> }>((resolve) => {
resolveDe = resolve;
});
setCatalogLoadersForTests({
en: async () => ({ messages: { Settings: "Settings" } }),
de: async () => dePromise,
ko: async () => ({ messages: { Settings: "설정" } }),
tr: async () => ({ messages: { Settings: "Ayarlar" } }),
hi: async () => ({ messages: { Settings: "सेटिंग्स" } }),
"pt-BR": async () => ({ messages: { Settings: "Configurações" } }),
"zh-CN": async () => ({ messages: { Settings: "设置" } }),
es: async () => ({ messages: { Settings: "Configuración" } }),
ru: async () => ({ messages: { Settings: "Настройки" } }),
});
await activateUiLocale("en");
const pendingDe = setUiLocale("de");
const pendingKo = setUiLocale("ko");
expect(store.get(UI_LOCALE_STORAGE_KEY)).toBe("ko");
resolveDe({ messages: { Settings: "Einstellungen" } });
await expect(pendingDe).resolves.toBe("ko");
await expect(pendingKo).resolves.toBe("ko");
expect(getActiveUiLocale()).toBe("ko");
expect(store.get(UI_LOCALE_STORAGE_KEY)).toBe("ko");
});
});