Files
OmniRoute/scripts/i18n/lib/language-bar.mjs
Diego Rodrigues de Sa e Souza 9debec71ec feat(i18n): 9 new locales — all 24 official EU languages (51 locales) (#13044)
Batch 1 of the locale expansion: Greek, Croatian, Serbian, Lithuanian, Estonian, Latvian, Slovenian, Maltese and Irish across the dashboard catalog, docs mirrors, CLI catalog, README, locale index and the site. 42 → 51 locales.

Also fixes the ICU literal escape the translation backend dropped around angle placeholders, four translations that invented or renamed a placeholder, the language bars that linked to mirrors that do not exist, and the migration count drift (171 → 172).

⚠️ base-red inherited: #12732 — the four unit shards and Fast Quality Gates fail identically on unrelated PRs cut from the same base.
2026-09-10 10:13:09 -03:00

86 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Shared builders for the `🌐 **Languages:** …` bars that head every English
* doc and every translated mirror. `config` is the parsed `config/i18n.json`
* (`locales[]` with `code`, `flag`, `native`); all paths are repo-relative POSIX
* (`docs/guides/USER_GUIDE.md`, `README.md`) and the links are relative to the
* file that carries the bar.
*
* buildMirrorBar(rel, locale, config) → docs/i18n/<locale>/… format:
* 🌐 **Languages:** 🇺🇸 [English](../../../README.md) · 🇸🇦 [ar](../ar/README.md) · …
* buildSourceBar(rel, config) → English source format:
* 🌐 **Languages:** 🇺🇸 [English](./USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](../i18n/pt-BR/docs/guides/USER_GUIDE.md) | …
* replaceLanguageBar(markdown, bar) → swaps EVERY language-bar line (any
* `🌐 **<label>:** …[…](…)`, translated labels
* included), `null` when there is none.
*/
import path from "node:path";
const DOCS_I18N = "docs/i18n";
function mirrorPath(relSource, locale) {
return path.posix.join(DOCS_I18N, locale, relSource);
}
/**
* `hasMirror(relSource, locale)` — optional existence predicate. A bar must not
* link to a mirror that is not on disk: `docs/guides/I18N.md` is deliberately
* English-only (see `DOCS_EXCLUDED_NAMES` in run-translation.mjs) yet carries
* legacy mirrors for the older locales, so every newly added locale would get a
* dead link. Omit it and every configured locale is listed, as before.
*/
export function buildMirrorBar(relSource, locale, config, { hasMirror } = {}) {
const targetDir = path.posix.dirname(mirrorPath(relSource, locale));
const parts = [`🇺🇸 [English](${path.posix.relative(targetDir, relSource)})`];
for (const entry of config.locales) {
if (entry.code === "en" || entry.code === locale) continue;
if (hasMirror && !hasMirror(relSource, entry.code)) continue;
const peer = path.posix.relative(targetDir, mirrorPath(relSource, entry.code));
parts.push(`${entry.flag} [${entry.code}](${peer})`);
}
return `🌐 **Languages:** ${parts.join(" · ")}`;
}
export function buildSourceBar(relSource, config, { hasMirror } = {}) {
const sourceDir = path.posix.dirname(relSource);
const parts = [`🇺🇸 [English](./${path.posix.basename(relSource)})`];
for (const entry of config.locales) {
if (entry.code === "en") continue;
if (hasMirror && !hasMirror(relSource, entry.code)) continue;
const peer = path.posix.relative(sourceDir, mirrorPath(relSource, entry.code));
parts.push(`${entry.flag} [${entry.native ?? entry.name}](${peer})`);
}
return `🌐 **Languages:** ${parts.join(" | ")}`;
}
/**
* A language-bar line has BOTH halves of the shape `🌐 **<label>:** …[…](…)`:
*
* 1. a bold label of 140 non-`*` characters closed by a colon (ASCII `:` or the
* fullwidth `` the CJK mirrors use), followed by a space, and
* 2. at least one markdown link — a bar without links is not a bar.
*
* The label is NOT pinned to the canonical English "Languages": older mirrors carry
* whatever the translation backend produced (`🌐 **Idiomas:**`, `🌐 **語言:**`,
* `🌐 **Available in:**`, …) and those bars are exactly the ones that still listed
* retired locales, so syncLanguageBars must normalize them too. Both halves are
* required so a future `🌐 **Website**` or `🌐 **Note:** we support many languages`
* line is never silently overwritten. The label class is bounded and anchored, so
* the regex cannot backtrack on the ~6 KB bar lines.
*/
const LANGUAGE_BAR_LABEL = /^🌐 \*\*[^*]{1,40}[:]\*\* /;
const isLanguageBar = (line) => LANGUAGE_BAR_LABEL.test(line) && line.includes("](");
export function replaceLanguageBar(markdown, bar) {
const lines = markdown.split("\n");
let found = false;
for (let index = 0; index < lines.length; index += 1) {
if (!isLanguageBar(lines[index])) continue;
lines[index] = bar;
found = true;
}
// Every bar in the file describes the same file, so they all get the same
// (correct) list: a stale second bar left in a translated body would otherwise
// keep pointing at locales that no longer exist.
return found ? lines.join("\n") : null;
}