Files
Alireza HassaniandClaude Opus 5 02aaf61bb6 Initial import: Majd Trading website
Next.js 15 site for گروه بازرگانی بین المللی آریو مجد آسیا, rebuilt from
majdtrading.com. Persian, English and Arabic; RTL and LTR.

- Pages: home, about, contact, six product categories, blog index and detail
- i18n: prose in per-locale JSON, structure in TypeScript, drift checked by
  `npm run i18n:check`. Blog stays single-language by design
- Meshkee backend: blog content, tenant resolution, session via the customer
  portal, and /meshkee/sitemap-config.json
- Live market data from livedata.ir, cached server-side once a minute
- Self-hosted fonts: IRANYekan (fa), IBM Plex Sans Arabic (ar), Space Grotesk
  and BBH Bartle (Latin)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 19:16:32 +03:30

67 lines
2.3 KiB
JavaScript

/**
* Compares every locale file against the reference locale (fa).
*
* The `Dictionary` annotation on each wrapper already fails the build on a
* MISSING key. This catches the other two cases it cannot see: keys left behind
* after a rename, and arrays whose length drifted apart (a mission list of 14
* items in one language and 13 in another renders fine and is wrong).
*
* Run after editing fa.json, before touching en/ar: npm run i18n:check
*/
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const here = dirname(fileURLToPath(import.meta.url));
const dir = join(here, "..", "src", "i18n", "locales");
const REFERENCE = "fa";
const OTHERS = ["en", "ar"];
const load = (l) => JSON.parse(readFileSync(join(dir, `${l}.json`), "utf8"));
/** Flattens to `a.b.c` paths; arrays report as one path plus their length. */
function paths(value, prefix = "", out = new Map()) {
if (Array.isArray(value)) {
out.set(prefix, `array:${value.length}`);
return out;
}
if (value && typeof value === "object") {
for (const [k, v] of Object.entries(value)) {
paths(v, prefix ? `${prefix}.${k}` : k, out);
}
return out;
}
out.set(prefix, typeof value);
return out;
}
const reference = paths(load(REFERENCE));
let problems = 0;
for (const locale of OTHERS) {
const current = paths(load(locale));
const missing = [...reference.keys()].filter((k) => !current.has(k));
const stale = [...current.keys()].filter((k) => !reference.has(k));
const lengths = [...reference.entries()].filter(
([k, v]) => current.has(k) && v !== current.get(k) && v.startsWith("array")
);
if (!missing.length && !stale.length && !lengths.length) {
console.log(` ${locale}: in sync with ${REFERENCE} (${reference.size} keys)`);
continue;
}
problems += missing.length + stale.length + lengths.length;
console.log(` ${locale}:`);
for (const k of missing) console.log(` missing ${k}`);
for (const k of stale) console.log(` stale ${k} (not in ${REFERENCE})`);
for (const [k, v] of lengths)
console.log(` length ${k} ${REFERENCE}=${v} ${locale}=${current.get(k)}`);
}
if (problems) {
console.error(`\ni18n check failed: ${problems} problem(s).`);
process.exit(1);
}
console.log("\ni18n check passed.");