79 lines
3.0 KiB
TypeScript
79 lines
3.0 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import {
|
|
loadLocales,
|
|
localeDirection,
|
|
makeT,
|
|
renderI18nData,
|
|
resolveI18n,
|
|
resolveLang,
|
|
} from "../src/index.ts";
|
|
|
|
describe("i18n package kit", () => {
|
|
test("loads top-level and namespaced locale files recursively", () => {
|
|
const directory = mkdtempSync(join(tmpdir(), "wrn-i18n-"));
|
|
try {
|
|
writeFileSync(join(directory, "en.json"), JSON.stringify({ app: { title: "Portal" } }));
|
|
mkdirSync(join(directory, "fr"));
|
|
writeFileSync(
|
|
join(directory, "fr", "common.json"),
|
|
JSON.stringify({ hello: "Bonjour {name}" }),
|
|
);
|
|
const i18n = resolveI18n(loadLocales(directory, { strict: true }), {
|
|
default: "en",
|
|
locales: ["en", "fr"],
|
|
});
|
|
expect(makeT(i18n, "fr")("common.hello", { name: "Ajay" })).toBe("Bonjour Ajay");
|
|
expect(resolveLang(i18n, undefined, "en;q=0.6, fr;q=0.9")).toBe("fr");
|
|
expect(resolveLang(i18n, undefined, "*")).toBe("en");
|
|
expect(renderI18nData(i18n, "fr")).toContain("Bonjour");
|
|
expect(renderI18nData(i18n, "fr")).toContain('"directions"');
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("ships a cookie-backed language switcher that targets the native select", () => {
|
|
const source = readFileSync(
|
|
join(import.meta.dir, "../components/LanguageSwitcher.wrn"),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain("<select");
|
|
expect(source).toContain("data-wire-lang");
|
|
expect(source).toMatch(/<select[\s\S]*data-wire-lang/);
|
|
expect(source).toContain("selected='{locale.value == current}'");
|
|
expect(source).toContain('variant == "segmented"');
|
|
expect(source).toContain("wire-language-switcher--compact");
|
|
});
|
|
|
|
test("replaces primitive namespace collisions safely", () => {
|
|
const directory = mkdtempSync(join(tmpdir(), "wrn-i18n-collision-"));
|
|
try {
|
|
writeFileSync(join(directory, "en.json"), JSON.stringify({ common: "legacy" }));
|
|
mkdirSync(join(directory, "en"));
|
|
writeFileSync(join(directory, "en", "common.json"), JSON.stringify({ save: "Save" }));
|
|
const i18n = resolveI18n(loadLocales(directory, { strict: true }), { default: "en" });
|
|
expect(makeT(i18n, "en")("common.save")).toBe("Save");
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("resolves RTL direction from language subtags", () => {
|
|
expect(localeDirection("ar-IN")).toBe("rtl");
|
|
expect(localeDirection("en-IN")).toBe("ltr");
|
|
});
|
|
|
|
test("rejects prototype-polluting locale keys", () => {
|
|
const directory = mkdtempSync(join(tmpdir(), "wrn-i18n-unsafe-"));
|
|
try {
|
|
writeFileSync(join(directory, "en.json"), '{"constructor":{"prototype":{"polluted":true}}}');
|
|
expect(() => loadLocales(directory, { strict: true })).toThrow();
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|