release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-02 23:18:51 +05:30
parent 87507edf59
commit 586a6db8ff
625 changed files with 243608 additions and 11210 deletions
+41
View File
@@ -0,0 +1,41 @@
import { expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
createLocaleFormatter,
formatMessage,
loadRouteMessages,
resolveI18n,
withTenantMessages,
} from "../src/index.ts";
test("formats ICU plural, exact and gender-aware select messages", () => {
expect(
formatMessage("{count, plural, =0 {None} one {# item} other {# items}}", { count: 2 }, "en"),
).toBe("2 items");
expect(
formatMessage(
"{gender, select, female {She} male {He} other {They}} approved",
{ gender: "female" },
"en",
),
).toBe("She approved");
});
test("supports calendars, tenant overrides and route-scoped loading", () => {
const formatter = createLocaleFormatter("en-US", "UTC", "indian");
expect(
formatter.date(new Date("2026-08-02T00:00:00Z"), { year: "numeric", calendar: "indian" }),
).toBeTruthy();
const base = resolveI18n({ en: { title: "Default" } }, { default: "en" });
expect(withTenantMessages(base, { en: { title: "Tenant" } }).messages.en?.title).toBe("Tenant");
expect(base.messages.en?.title).toBe("Default");
const root = mkdtempSync(join(tmpdir(), "wrnexus-i18n-route-"));
mkdirSync(join(root, "en/routes/users"), { recursive: true });
writeFileSync(join(root, "en/common.json"), JSON.stringify({ common: "Common" }));
writeFileSync(join(root, "en/routes/users/index.json"), JSON.stringify({ title: "Users" }));
expect(loadRouteMessages(root, "en", "/users/index")).toEqual({
common: "Common",
title: "Users",
});
});
+10
View File
@@ -38,3 +38,13 @@ test("translateHtml is a no-op without markers", () => {
const t = makeT(i18n, "en");
expect(translateHtml("<p>plain</p>", t)).toBe("<p>plain</p>");
});
test("translateHtml preserves authored fallback text when a key is unavailable", () => {
const t = (key: string) => key;
expect(translateHtml('<h1 data-t="missing.title">Readable fallback</h1>', t)).toBe(
'<h1 data-t="missing.title">Readable fallback</h1>',
);
expect(translateHtml('<input t:placeholder="missing.placeholder" />', t)).toBe(
'<input placeholder="" />',
);
});
+78
View File
@@ -0,0 +1,78 @@
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 });
}
});
});
+40
View File
@@ -0,0 +1,40 @@
import { expect, test } from "bun:test";
import {
auditLocaleKeys,
createPseudoLocale,
extractTranslationKeys,
flattenMessageKeys,
localeDirection,
pseudoLocalize,
} from "../src/index.ts";
test("extracts static translation keys from code and WRN attributes", () => {
const keys = extractTranslationKeys(`
const title = t("account.title")
const copy = i18n.t('account.copy')
const dynamic = t(key)
<p data-i18n="account.help"></p>
`);
expect(keys.map(({ key }) => key)).toEqual(["account.copy", "account.help", "account.title"]);
});
test("audits locale completeness against a reference locale", () => {
const messages = {
en: { common: { hello: "Hello", bye: "Bye" } },
fr: { common: { hello: "Bonjour", extra: "Extra" } },
};
expect(flattenMessageKeys(messages.en)).toEqual(["common.bye", "common.hello"]);
expect(auditLocaleKeys(messages, "en").fr).toEqual({
missing: ["common.bye"],
extra: ["common.extra"],
});
});
test("pseudo-locales preserve placeholders and exercise LTR and RTL layouts", () => {
expect(pseudoLocalize("Hello {name} <strong>today</strong>")).toContain("{name}");
const pseudo = createPseudoLocale({ greeting: "Hello {name}", nested: { save: "Save" } });
expect(pseudo.greeting).toBe("[Hëllô {name}~~~]");
expect((pseudo.nested as Record<string, string>).save).toBe("[Sàvë~~~]");
expect(pseudoLocalize("Hello {name}", { rtl: true }).startsWith("\u202e[")).toBe(true);
expect(localeDirection("ar-XB")).toBe("rtl");
});