mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 11:52:26 +03:00
One command brings a new locale to every surface, in order: config/i18n.json entry, flag SVG (lipis/flag-icons), dashboard catalog (scaffold + sync-ui-keys --translate-markers), docs mirrors (run-translation over the core set every existing locale carries, then the llm.txt / CHANGELOG.md stubs), CLI catalog (generate-locales --code + batched common/program translation), README flag link / docs index row / I18N guide row / locale counts (+ sync-llm-mirrors), language bars, and the marketing site (lang JSON, SUPPORTED_LANGS, dropdowns). Every phase checks presence first, so a re-run is a no-op apart from filling in what is still missing; --dry-run prints the whole plan and touches nothing (no files, network or child processes); --only / --skip select phases. - scripts/i18n/lib/docs-core-set.mjs: computeDocsCoreSet — intersection of the existing non-docsExcluded mirrors minus llm.txt / CHANGELOG.md / I18N.md and paths without an English source (22 files today). - scripts/i18n/lib/site-scaffold.mjs: addSupportedLang (re-flows the array in the file's own fill style) and addDropdownOption (code order, every menu). - bin/cli/scripts/generate-locales.mjs: --code=<locale> filter. - translate-backend parseBatchResponse: Object.hasOwn + trimmed values. - package.json: i18n:add-locale script.
59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
/**
|
|
* The docs "core set": the source documents every existing locale mirror carries.
|
|
*
|
|
* The translation pipeline (`scripts/i18n/run-translation.mjs`) knows ~150
|
|
* source files, but only a core of them is translated in every locale under
|
|
* `docs/i18n/`. A new locale should reach parity with its peers rather than
|
|
* translate the full set, so the core is derived from the tree itself — the
|
|
* intersection of the relative paths present under every existing,
|
|
* non-`docsExcluded` locale directory — instead of being hard-coded.
|
|
*
|
|
* docsLocaleDirs({ root, config }) → ["ar", "az", …] locale codes whose
|
|
* docs/i18n/<code>/ directory exists
|
|
* computeDocsCoreSet({ root, config }) → sorted repo-relative source paths
|
|
*
|
|
* Paths that are not translation targets are dropped from the result: the
|
|
* strict-mirror stubs (`llm.txt`, `CHANGELOG.md`), the operator-only
|
|
* `docs/guides/I18N.md`, and any mirror whose English source no longer exists
|
|
* at `<root>/<path>`. Synchronous and filesystem-only — no network, no writes.
|
|
*/
|
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
export const CORE_SET_EXCLUDED = ["llm.txt", "CHANGELOG.md", "docs/guides/I18N.md"];
|
|
|
|
function walkFiles(dir, base = dir, out = []) {
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
const abs = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) walkFiles(abs, base, out);
|
|
else if (entry.isFile()) out.push(path.relative(base, abs).split(path.sep).join("/"));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function isDirectory(abs) {
|
|
return existsSync(abs) && statSync(abs).isDirectory();
|
|
}
|
|
|
|
function isFile(abs) {
|
|
return existsSync(abs) && statSync(abs).isFile();
|
|
}
|
|
|
|
export function docsLocaleDirs({ root, config }) {
|
|
const excluded = new Set(config.docsExcluded ?? ["en"]);
|
|
return config.locales
|
|
.map((locale) => locale.code)
|
|
.filter((code) => !excluded.has(code) && isDirectory(path.join(root, "docs", "i18n", code)));
|
|
}
|
|
|
|
export function computeDocsCoreSet({ root, config }) {
|
|
let shared = null;
|
|
for (const code of docsLocaleDirs({ root, config })) {
|
|
const files = new Set(walkFiles(path.join(root, "docs", "i18n", code)));
|
|
shared = shared === null ? files : new Set([...shared].filter((rel) => files.has(rel)));
|
|
}
|
|
if (shared === null) return [];
|
|
const dropped = new Set(CORE_SET_EXCLUDED);
|
|
return [...shared].filter((rel) => !dropped.has(rel) && isFile(path.join(root, rel))).sort();
|
|
}
|