import { describe, expect, it } from "vitest"; import { getLanguageTag, setLocale, t } from "../src/lib/i18n"; import { formatMinor } from "../src/lib/money"; import sv from "../src/locales/sv/common.json"; import en from "../src/locales/en/common.json"; import es from "../src/locales/es/common.json"; import it_ from "../src/locales/it/common.json"; import de from "../src/locales/de/common.json"; import fr from "../src/locales/fr/common.json"; import da from "../src/locales/da/common.json"; import nb from "../src/locales/nb/common.json"; import fi from "../src/locales/fi/common.json"; import nl from "../src/locales/nl/common.json"; import pl from "../src/locales/pl/common.json"; import pt from "../src/locales/pt/common.json"; describe("i18n (i18n-spec §5, §7–8 – M4, D-031)", () => { it("ALLA 12 språk har samma basnycklar (pluralvarianter får skilja per språk)", () => { const baseKeys = (obj: Record) => [ ...new Set( Object.keys(obj).map((k) => { const suffix = k.split("_").pop(); return suffix && ["one", "few", "many", "other"].includes(suffix) ? k.slice(0, k.lastIndexOf("_")) : k; }), ), ].sort(); const base = baseKeys(sv); const locales = { en, es, it: it_, de, fr, da, nb, fi, nl, pl, pt }; for (const [name, locale] of Object.entries(locales)) { expect(baseKeys(locale), `språk: ${name}`).toEqual(base); } }); it("interpolerar {brand} från konfig – aldrig hårdkodat namn", () => { setLocale("sv-SE"); const text = t("memory.title"); expect(text).not.toContain("{brand}"); expect(text.length).toBeGreaterThan(0); }); it("plural via Intl.PluralRules: 1 dag / 3 dagar", () => { setLocale("sv-SE"); expect(t("home.expiresIn", { days: 1, count: 1 })).toBe("1 dag kvar"); expect(t("home.expiresIn", { days: 3, count: 3 })).toBe("3 dagar kvar"); }); it("språkbyte utan omstart + fallback till sv för okända språk", () => { setLocale("en-US"); expect(t("tabs.home")).toBe("At home"); expect(getLanguageTag()).toBe("en-US"); setLocale("zz-ZZ"); // stöds inte -> sv expect(t("tabs.home")).toBe("Hemma"); setLocale("sv-SE"); }); it("pengar formatteras per språk ur minor units (i18n-spec §20)", () => { setLocale("sv-SE"); const svText = formatMinor(7900, "SEK"); expect(svText).toMatch(/79/); setLocale("en-US"); const enText = formatMinor(7900, "SEK"); expect(enText).toMatch(/79/); expect(enText).not.toBe(svText); // olika locale -> olika formatering setLocale("sv-SE"); }); }); describe("språkpolicy D-031 – automatik + manuellt val", () => { it("spanska väljs och pluralformer fungerar", () => { setLocale("es-ES"); expect(t("tabs.whatToEat")).toBe("¿Qué comemos?"); expect(t("home.expiresIn", { days: 1, count: 1 })).toBe("Queda 1 día"); expect(t("home.expiresIn", { days: 3, count: 3 })).toBe("Quedan 3 días"); setLocale("sv-SE"); }); it("regional variant faller till primärspråket (es-MX -> es)", () => { setLocale("es-MX"); expect(t("tabs.home")).toBe("En casa"); expect(getLanguageTag()).toBe("es-MX"); // Intl behåller regionen setLocale("sv-SE"); }); it("italienska, tyska och franska svarar på sina språk", () => { setLocale("it-IT"); expect(t("tabs.whatToEat")).toBe("Cosa mangiamo?"); setLocale("de-DE"); expect(t("tabs.whatToEat")).toBe("Was essen wir?"); setLocale("fr-FR"); expect(t("tabs.whatToEat")).toBe("On mange quoi ?"); setLocale("sv-SE"); }); it("polska pluralformer: one/few/many via Intl.PluralRules", () => { setLocale("pl-PL"); expect(t("recipe.portions", { count: 1 })).toBe("1 porcja"); expect(t("recipe.portions", { count: 3 })).toBe("3 porcje"); expect(t("recipe.portions", { count: 5 })).toBe("5 porcji"); expect(t("recipe.portions", { count: 22 })).toBe("22 porcje"); setLocale("sv-SE"); }); it("nordiska språk + nederländska/portugisiska svarar rätt", () => { setLocale("da-DK"); expect(t("tabs.whatToEat")).toBe("Hvad skal vi spise?"); setLocale("nb-NO"); expect(t("tabs.whatToEat")).toBe("Hva skal vi spise?"); setLocale("fi-FI"); expect(t("tabs.whatToEat")).toBe("Mitä syödään?"); setLocale("nl-NL"); expect(t("tabs.whatToEat")).toBe("Wat eten we?"); setLocale("pt-PT"); expect(t("tabs.whatToEat")).toBe("O que vamos comer?"); setLocale("sv-SE"); }); it("ostött språk faller till svenska i t(), utan krasch", () => { setLocale("el-GR"); // grekiska stöds inte ännu expect(t("tabs.home")).toBe("Hemma"); setLocale("sv-SE"); }); it("pt-BR får portugisiska resurser med brasiliansk Intl-formattering", () => { setLocale("pt-BR"); expect(t("tabs.home")).toBe("Em casa"); expect(getLanguageTag()).toBe("pt-BR"); setLocale("sv-SE"); }); });