// Unit tests for the lightweight i18n primitive in // `src/lib/i18n/index.svelte.ts`. The locale singleton is reset // between cases through `resetForTests`; the default constructor // derives the locale from JSDOM's `navigator.language`, so the // reset takes an explicit value when a case needs a deterministic // starting state. import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { DEFAULT_LOCALE, SUPPORTED_LOCALES, detectInitialLocale, i18n, type Locale, } from "../src/lib/i18n/index.svelte"; beforeEach(() => { i18n.resetForTests("en"); }); afterEach(() => { i18n.resetForTests("en"); }); describe("detectInitialLocale", () => { test("matches the first supported primary subtag", () => { expect(detectInitialLocale(["ru-RU", "en-US"])).toBe("ru"); expect(detectInitialLocale(["en-GB", "ru"])).toBe("en"); expect(detectInitialLocale(["RU"])).toBe("ru"); }); test("skips unsupported tags and falls back to the default locale", () => { expect(detectInitialLocale(["fr-FR", "de-DE", "ja"])).toBe(DEFAULT_LOCALE); expect(detectInitialLocale([])).toBe(DEFAULT_LOCALE); }); test("ignores whitespace and casing", () => { expect(detectInitialLocale([" En-us "])).toBe("en"); expect(detectInitialLocale(["RU_ru"])).toBe("ru"); }); }); describe("SUPPORTED_LOCALES", () => { test("each entry exposes a code, native name and translation table", () => { for (const entry of SUPPORTED_LOCALES) { expect(entry.code).toMatch(/^[a-z]{2}$/); expect(entry.nativeName.length).toBeGreaterThan(0); expect(entry.translations["login.title"].length).toBeGreaterThan(0); } }); test("native names cover the two phase-7 locales", () => { const codes = SUPPORTED_LOCALES.map((l) => l.code); const names = SUPPORTED_LOCALES.map((l) => l.nativeName); expect(codes).toEqual(["en", "ru"]); expect(names).toEqual(["English", "Русский"]); }); }); describe("i18n.t", () => { test("returns the active locale's translation", () => { i18n.setLocale("en"); expect(i18n.t("login.title")).toBe("sign in to Galaxy"); i18n.setLocale("ru"); expect(i18n.t("login.title")).toBe("вход в Galaxy"); }); test("returns the key itself for an unknown identifier", () => { // `t` is typed `TranslationKey`, but bracket access at runtime // gracefully handles unknown keys — the fallback chain tries // the active locale, then the default locale, then the literal // key. This is the safety net for a future locale that adds // keys before another locale catches up. i18n.setLocale("en"); expect( i18n.t("does.not.exist" as unknown as Parameters[0]), ).toBe("does.not.exist"); }); test("interpolates {placeholder} parameters", () => { i18n.setLocale("en"); expect(i18n.t("login.code_sent_to", { email: "x@y.z" })).toBe( "code sent to x@y.z", ); i18n.setLocale("ru"); expect(i18n.t("login.code_sent_to", { email: "x@y.z" })).toBe( "код отправлен на x@y.z", ); }); test("leaves unresolved placeholders intact", () => { i18n.setLocale("en"); expect(i18n.t("login.code_sent_to")).toContain("{email}"); expect(i18n.t("login.code_sent_to", { other: "x" })).toContain( "{email}", ); }); }); describe("i18n.setLocale", () => { test("setLocale updates the reactive state", () => { i18n.setLocale("en"); expect(i18n.locale).toBe("en"); i18n.setLocale("ru"); expect(i18n.locale).toBe("ru"); }); });