mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 03:42:21 +03:00
test(i18n): derive remaining locale counts from config and add reverse surface parity
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import i18nConfig from "../../config/i18n.json" with { type: "json" };
|
||||
@@ -11,7 +11,9 @@ import i18nConfig from "../../config/i18n.json" with { type: "json" };
|
||||
* docs mirror (README.md / llm.txt / CHANGELOG.md), README flag link and the
|
||||
* docs/i18n/README.md index row — and the README headline count must match.
|
||||
* Locales listed in `docsExcluded` (the English source) only need the two
|
||||
* catalogs.
|
||||
* catalogs. The reverse direction is guarded as well: every docs-surface entry
|
||||
* (index row, README flag link, docs/i18n/ directory) must map back to a
|
||||
* configured docs locale, so a retired locale cannot leave orphans behind.
|
||||
*/
|
||||
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
||||
@@ -43,3 +45,44 @@ test("README headline count matches config/i18n.json", () => {
|
||||
assert.ok(m, "README must carry the 'In N languages' headline");
|
||||
assert.equal(Number(m[1]), i18nConfig.locales.length);
|
||||
});
|
||||
|
||||
test("docs/i18n/README.md counts sentence matches config/i18n.json", () => {
|
||||
const sentence = `into ${i18nConfig.locales.length - 1} languages; together with the English source, the UI supports ${i18nConfig.locales.length} locales`;
|
||||
assert.ok(docsIndex.includes(sentence), `docs/i18n/README.md must contain "${sentence}"`);
|
||||
});
|
||||
|
||||
test("no orphan locale surface outside config/i18n.json (reverse parity)", () => {
|
||||
const docsLocales = new Set(
|
||||
i18nConfig.locales.map(({ code }) => code).filter((code) => !docsExcluded.has(code))
|
||||
);
|
||||
const offenders: string[] = [];
|
||||
|
||||
// docs/i18n/README.md rows: "- … (`code`): [Docs Root](./code/README.md)"
|
||||
for (const line of docsIndex.split("\n")) {
|
||||
const row = line.match(/^- .*\(`([^`]+)`\): \[Docs Root\]\(\.\/([^/]+)\/README\.md\)/);
|
||||
if (!row) continue;
|
||||
const [, code, linkCode] = row;
|
||||
if (!docsLocales.has(code)) offenders.push(`docs/i18n/README.md row: ${code}`);
|
||||
if (linkCode !== code) {
|
||||
offenders.push(`docs/i18n/README.md row ${code} links to ./${linkCode}/README.md`);
|
||||
}
|
||||
}
|
||||
|
||||
// Root README.md language block: docs/i18n/<code>/README.md flag links.
|
||||
for (const [, code] of readme.matchAll(/docs\/i18n\/([^/\s"')]+)\/README\.md/g)) {
|
||||
if (!docsLocales.has(code)) offenders.push(`README.md link: ${code}`);
|
||||
}
|
||||
|
||||
// Every directory under docs/i18n/ must be a configured docs locale.
|
||||
for (const entry of readdirSync(path.join(ROOT, "docs", "i18n"), { withFileTypes: true })) {
|
||||
if (entry.isDirectory() && !docsLocales.has(entry.name)) {
|
||||
offenders.push(`docs/i18n/ directory: ${entry.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(
|
||||
offenders,
|
||||
[],
|
||||
`orphan locale surfaces that are not a configured docs locale (config/i18n.json minus docsExcluded) — retired locale left behind?: ${offenders.join("; ")}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import i18nConfig from "../../config/i18n.json" with { type: "json" };
|
||||
|
||||
const PAGE_PATH = path.resolve(process.cwd(), "src/app/(dashboard)/dashboard/radar/page.tsx");
|
||||
const PAGE_SRC = fs.readFileSync(PAGE_PATH, "utf-8");
|
||||
@@ -70,7 +71,7 @@ test("radar page: references the 5 new claim-section t(...) keys", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("radar page + all 43 locale files: no price/monetary value in the claim section copy (D14)", () => {
|
||||
test(`radar page + all ${i18nConfig.locales.length} locale files: no price/monetary value in the claim section copy (D14)`, () => {
|
||||
// D14: no pricing anywhere in the OSS repo, only a link to the plans page.
|
||||
const PRICE_PATTERN =
|
||||
/\$\s?\d|R\$\s?\d|\d+[.,]\d{2}\s?(USD|BRL|EUR)|\b(lifetime|life-time)\b.{0,20}\$/i;
|
||||
@@ -78,7 +79,10 @@ test("radar page + all 43 locale files: no price/monetary value in the claim sec
|
||||
|
||||
const messagesDir = path.resolve(process.cwd(), "src/i18n/messages");
|
||||
const files = fs.readdirSync(messagesDir).filter((f) => f.endsWith(".json"));
|
||||
assert.ok(files.length >= 40, `expected ~43 locale files, found ${files.length}`);
|
||||
assert.ok(
|
||||
files.length >= i18nConfig.locales.length,
|
||||
`expected the ${i18nConfig.locales.length} configured locales, found ${files.length}`
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const data = JSON.parse(fs.readFileSync(path.join(messagesDir, file), "utf-8"));
|
||||
|
||||
@@ -26,6 +26,7 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import i18nConfig from "../../config/i18n.json" with { type: "json" };
|
||||
|
||||
const PAGE_PATH = path.resolve(process.cwd(), "src/app/(dashboard)/dashboard/radar/page.tsx");
|
||||
const PAGE_SRC = fs.readFileSync(PAGE_PATH, "utf-8");
|
||||
@@ -114,14 +115,17 @@ test("radar page: references the 4 new key-input t(...) keys", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("radar page + all 43 locale files: no price/monetary value in the key-input copy (D14)", () => {
|
||||
test(`radar page + all ${i18nConfig.locales.length} locale files: no price/monetary value in the key-input copy (D14)`, () => {
|
||||
const PRICE_PATTERN =
|
||||
/\$\s?\d|R\$\s?\d|\d+[.,]\d{2}\s?(USD|BRL|EUR)|\b(lifetime|life-time)\b.{0,20}\$/i;
|
||||
assert.ok(!PRICE_PATTERN.test(PAGE_SRC), "page.tsx must not contain a price/monetary value");
|
||||
|
||||
const messagesDir = path.resolve(process.cwd(), "src/i18n/messages");
|
||||
const files = fs.readdirSync(messagesDir).filter((f) => f.endsWith(".json"));
|
||||
assert.ok(files.length >= 40, `expected ~43 locale files, found ${files.length}`);
|
||||
assert.ok(
|
||||
files.length >= i18nConfig.locales.length,
|
||||
`expected the ${i18nConfig.locales.length} configured locales, found ${files.length}`
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const data = JSON.parse(fs.readFileSync(path.join(messagesDir, file), "utf-8"));
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import i18nConfig from "../../config/i18n.json" with { type: "json" };
|
||||
|
||||
function radarCopy(locale: "en" | "pt-BR"): Record<string, string> {
|
||||
const file = path.resolve(process.cwd(), `src/i18n/messages/${locale}.json`);
|
||||
@@ -101,7 +102,11 @@ test("Brazilian Portuguese Radar opt-in copy states D32 without a PR-count grant
|
||||
test("every UI locale carries the D32 keys and none retains the superseded 5+ PR promise", () => {
|
||||
const messagesDir = path.resolve(process.cwd(), "src/i18n/messages");
|
||||
const files = fs.readdirSync(messagesDir).filter((file) => file.endsWith(".json"));
|
||||
assert.equal(files.length, 43, "expected the complete 43-locale UI catalog");
|
||||
assert.equal(
|
||||
files.length,
|
||||
i18nConfig.locales.length,
|
||||
`expected the complete ${i18nConfig.locales.length}-locale UI catalog`
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const messages = JSON.parse(fs.readFileSync(path.join(messagesDir, file), "utf8")) as {
|
||||
|
||||
Reference in New Issue
Block a user