Skip to content

Commit bc72e32

Browse files
committed
feat(web): add local GT catalog pipeline for website and docs
Keep the dictionary runtime as the one i18n path and give updated English marketing/docs copy a fail-closed, lockfile-pinned translation lane. Do not wrap the TUI, completions, or /translate.
1 parent 7cc2422 commit bc72e32

10 files changed

Lines changed: 4676 additions & 91 deletions

File tree

docs/LOCALIZATION.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ locale, Chinese included.** `web/app/[locale]/page.tsx`,
8181
`getChrome(locale)`. `web/lib/i18n/dictionaries/zh/` now exists (it used to
8282
be inline TSX), and nav/footer link sets are generated once in
8383
`web/lib/i18n/links.ts` so every locale gets the identical route shape.
84+
85+
**Website/docs translation pipeline (General Translation CLI, 2026-08-28).**
86+
Runtime stays the dictionaries above — do not add `gt-next` beside them.
87+
`web/gt-catalog/[locale].json` is the local JSON interchange (`en` + live
88+
`zh` first). `npm run i18n:gt -- export` writes catalogs from dictionaries;
89+
`check` (hooked from `check:locales`) requires them to match; `import`
90+
writes reviewed JSON back to website dictionary TS only. `translate` is
91+
fail-closed unless BYOK `GT_API_KEY` and `GT_PROJECT_ID` are set in the
92+
environment — never commit those values, never point this config at
93+
`crates/tui/locales`, and never wrap model completions. `gt generate` is
94+
not used: it is a framework JSX scanner, not a JSON-catalog tool.
8495
Reference shape: **`ChromeDict` 52 keys, `HomeDict` 62 keys.** Bilingual
8596
secondary nav labels, the masthead seal and issue line, the ticker live
8697
label, and the per-locale `Intl` date tag are dictionary values — no locale

web/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,10 @@ MAINTAINER_GITHUB_PAT=
2525
# Set to 1 once the Gitee mirror at gitee.com/Hmbown/...
2626
# exists. Until then leave blank to hide Gitee links.
2727
NEXT_PUBLIC_GITEE_ENABLED=
28+
29+
# Optional BYOK for website/docs machine translation via the General
30+
# Translation CLI (`npm run i18n:gt -- translate`). Never commit values.
31+
# Check/export/import are local and do not need these. Runtime does not
32+
# call GT. Fail-closed without both. Never wrap model completions.
33+
# GT_API_KEY=
34+
# GT_PROJECT_ID=

web/AGENTS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ site.
1212
- Localization migrations are one-way. New or touched pages use the shared
1313
dictionary/content spine; do not add page-local language booleans, `isZh`
1414
copy forks, or hard-coded translated page clones. A compatibility exception
15-
must name the external contract that still requires it.
15+
must name the external contract that still requires it. Website/docs
16+
machine translation uses `npm run i18n:gt` (local JSON catalogs into the
17+
same dictionaries). Do not add `gt-next` beside that path, and do not
18+
point GT at TUI locale packs.
1619
- Real-session media is owned by `lib/media-manifest.ts`. `pending` is the
1720
truthful state until the complete asset set passes manifest tests.
1821
- `AGENT.md` (singular) documents maintainer-owned community automation. Do not

web/gt-catalog/en.json

Lines changed: 504 additions & 0 deletions
Large diffs are not rendered by default.

web/gt-catalog/zh.json

Lines changed: 504 additions & 0 deletions
Large diffs are not rendered by default.

web/gt.config.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"defaultLocale": "en",
3+
"locales": ["zh"],
4+
"files": {
5+
"json": {
6+
"include": ["gt-catalog/[locale].json"]
7+
}
8+
}
9+
}

web/lib/i18n/gt-site.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { spawnSync } from "node:child_process";
2+
import { readFileSync } from "node:fs";
3+
import { join } from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import { describe, expect, it } from "vitest";
6+
import { getChrome, getDocsGuide, getHome } from "./dictionaries";
7+
8+
const webRoot = fileURLToPath(new URL("../..", import.meta.url));
9+
10+
function runGtSite(command: string, extraEnv: NodeJS.ProcessEnv = {}) {
11+
return spawnSync(process.execPath, [join(webRoot, "scripts", "gt-site.mjs"), command], {
12+
cwd: webRoot,
13+
encoding: "utf8",
14+
env: {
15+
...process.env,
16+
GT_API_KEY: "",
17+
GT_PROJECT_ID: "",
18+
...extraEnv,
19+
},
20+
});
21+
}
22+
23+
describe("website GT catalog pipeline", () => {
24+
it("keeps local catalogs in sync with the dictionary runtime", () => {
25+
const check = runGtSite("check");
26+
expect(check.status, check.stderr || check.stdout).toBe(0);
27+
expect(check.stdout).toContain("TUI packs untouched");
28+
29+
const zh = JSON.parse(readFileSync(join(webRoot, "gt-catalog", "zh.json"), "utf8"));
30+
expect(zh.chrome.navDocs).toBe(getChrome("zh").navDocs);
31+
expect(zh.home.kicker).toBe(getHome("zh").kicker);
32+
expect(zh["docs-guide"].overviewTitle).toBe(getDocsGuide("zh").overviewTitle);
33+
34+
const config = readFileSync(join(webRoot, "gt.config.json"), "utf8");
35+
expect(config).toContain("gt-catalog/[locale].json");
36+
expect(config).not.toContain("crates/tui");
37+
expect(config).not.toContain("prompts/text.rs");
38+
});
39+
40+
it("fails closed when translate is asked to call the GT API without BYOK env", () => {
41+
const translate = runGtSite("translate");
42+
expect(translate.status).not.toBe(0);
43+
expect(`${translate.stderr}${translate.stdout}`).toMatch(/fail-closed|GT_API_KEY/);
44+
});
45+
});

0 commit comments

Comments
 (0)