docs: generate full locale nav during publish sync

This commit is contained in:
Peter Steinberger
2026-04-05 14:29:14 +01:00
parent 9a0d88a868
commit 98bac6a0e4
2 changed files with 108 additions and 11 deletions

View File

@@ -32,7 +32,8 @@ Generated locale trees and live translation memory now live in the publish repo:
## Files in this folder
- `glossary.<lang>.json` — preferred term mappings used as prompt guidance.
- `ar-navigation.json`, `de-navigation.json`, `es-navigation.json`, `fr-navigation.json`, `ja-navigation.json`, `ko-navigation.json`, `pt-BR-navigation.json`, `zh-Hans-navigation.json` — Mintlify locale picker blocks reinserted into the publish repo during sync.
- `zh-Hans-navigation.json` curated zh-Hans Mintlify locale navigation reinserted into the publish repo during sync.
- `ar-navigation.json`, `de-navigation.json`, `es-navigation.json`, `fr-navigation.json`, `ja-navigation.json`, `ko-navigation.json`, `pt-BR-navigation.json` — starter locale metadata kept alongside the source repo, but the publish sync now clones the full English nav tree for these locales so translated pages are visible in Mintlify without hand-maintaining per-locale nav JSON.
- `<lang>.tm.jsonl` — translation memory keyed by workflow + model + text hash.
In this repo, generated locale TM files such as `docs/.i18n/zh-CN.tm.jsonl`, `docs/.i18n/ja-JP.tm.jsonl`, `docs/.i18n/es.tm.jsonl`, `docs/.i18n/pt-BR.tm.jsonl`, `docs/.i18n/ko.tm.jsonl`, `docs/.i18n/de.tm.jsonl`, `docs/.i18n/fr.tm.jsonl`, and `docs/.i18n/ar.tm.jsonl` are intentionally no longer committed.

View File

@@ -15,14 +15,57 @@ const GENERATED_LOCALES = [
dir: "zh-CN",
navFile: "zh-Hans-navigation.json",
tmFile: "zh-CN.tm.jsonl",
navMode: "overlay",
},
{
language: "ja",
dir: "ja-JP",
navFile: "ja-navigation.json",
tmFile: "ja-JP.tm.jsonl",
navMode: "clone-en",
},
{
language: "es",
dir: "es",
navFile: "es-navigation.json",
tmFile: "es.tm.jsonl",
navMode: "clone-en",
},
{
language: "pt-BR",
dir: "pt-BR",
navFile: "pt-BR-navigation.json",
tmFile: "pt-BR.tm.jsonl",
navMode: "clone-en",
},
{
language: "ko",
dir: "ko",
navFile: "ko-navigation.json",
tmFile: "ko.tm.jsonl",
navMode: "clone-en",
},
{
language: "de",
dir: "de",
navFile: "de-navigation.json",
tmFile: "de.tm.jsonl",
navMode: "clone-en",
},
{
language: "fr",
dir: "fr",
navFile: "fr-navigation.json",
tmFile: "fr.tm.jsonl",
navMode: "clone-en",
},
{
language: "ar",
dir: "ar",
navFile: "ar-navigation.json",
tmFile: "ar.tm.jsonl",
navMode: "clone-en",
},
{ language: "ja", dir: "ja-JP", navFile: "ja-navigation.json", tmFile: "ja-JP.tm.jsonl" },
{ language: "es", dir: "es", navFile: "es-navigation.json", tmFile: "es.tm.jsonl" },
{ language: "pt-BR", dir: "pt-BR", navFile: "pt-BR-navigation.json", tmFile: "pt-BR.tm.jsonl" },
{ language: "ko", dir: "ko", navFile: "ko-navigation.json", tmFile: "ko.tm.jsonl" },
{ language: "de", dir: "de", navFile: "de-navigation.json", tmFile: "de.tm.jsonl" },
{ language: "fr", dir: "fr", navFile: "fr-navigation.json", tmFile: "fr.tm.jsonl" },
{ language: "ar", dir: "ar", navFile: "ar-navigation.json", tmFile: "ar.tm.jsonl" },
];
function parseArgs(argv) {
@@ -80,6 +123,60 @@ function writeJson(filePath, value) {
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
}
function prefixLocalePage(entry, localeDir) {
if (typeof entry === "string") {
return `${localeDir}/${entry}`;
}
if (Array.isArray(entry)) {
return entry.map((item) => prefixLocalePage(item, localeDir));
}
if (!entry || typeof entry !== "object") {
return entry;
}
const clone = { ...entry };
if (typeof clone.page === "string") {
clone.page = `${localeDir}/${clone.page}`;
}
if (Array.isArray(clone.pages)) {
clone.pages = clone.pages.map((item) => prefixLocalePage(item, localeDir));
}
return clone;
}
function cloneEnglishLanguageNav(englishNav, locale) {
if (!englishNav) {
throw new Error("docs/docs.json is missing navigation.languages.en");
}
return {
...englishNav,
language: locale.language,
tabs: Array.isArray(englishNav.tabs)
? englishNav.tabs.map((tab) => ({
...tab,
pages: Array.isArray(tab.pages)
? tab.pages.map((entry) => prefixLocalePage(entry, locale.dir))
: tab.pages,
groups: Array.isArray(tab.groups)
? tab.groups.map((group) => ({
...group,
pages: Array.isArray(group.pages)
? group.pages.map((entry) => prefixLocalePage(entry, locale.dir))
: group.pages,
}))
: tab.groups,
}))
: englishNav.tabs,
};
}
function composeLocaleNav(locale, englishNav) {
if (locale.navMode === "clone-en") {
return cloneEnglishLanguageNav(englishNav, locale);
}
return readJson(path.join(SOURCE_DOCS_DIR, ".i18n", locale.navFile));
}
function composeDocsConfig() {
const sourceConfig = readJson(SOURCE_CONFIG_PATH);
const languages = sourceConfig?.navigation?.languages;
@@ -88,12 +185,11 @@ function composeDocsConfig() {
throw new Error("docs/docs.json is missing navigation.languages");
}
const englishNav = languages.find((entry) => entry?.language === "en");
const generatedLanguageSet = new Set(GENERATED_LOCALES.map((entry) => entry.language));
const withoutGenerated = languages.filter((entry) => !generatedLanguageSet.has(entry?.language));
const enIndex = withoutGenerated.findIndex((entry) => entry?.language === "en");
const generated = GENERATED_LOCALES.map((entry) =>
readJson(path.join(SOURCE_DOCS_DIR, ".i18n", entry.navFile)),
);
const generated = GENERATED_LOCALES.map((entry) => composeLocaleNav(entry, englishNav));
if (enIndex === -1) {
withoutGenerated.push(...generated);
} else {