mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Shared Claude Code translation pipeline (scripts/i18n, no API key) plus Astro/VitePress/Scalar i18n wiring. Landing and API reference translated into all 20 languages; docs i18n wiring + English source anchors. The translated docs markdown (apps/docs/<locale>/**, 3,620 files) follows in a companion PR because it exceeds GitHub's per-PR CI file limit.
58 lines
1.9 KiB
JavaScript
58 lines
1.9 KiB
JavaScript
// scripts/i18n/lib/batch.mjs
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { mask, restore } from "./mask.mjs";
|
|
|
|
function pendingPath(dir, surface, locale) {
|
|
return join(dir, `${surface}.${locale}.pending.json`);
|
|
}
|
|
function donePath(dir, surface, locale) {
|
|
return join(dir, `${surface}.${locale}.done.json`);
|
|
}
|
|
|
|
/**
|
|
* Write the masked source of each pending unit for subagents to translate.
|
|
* @param {string} dir
|
|
* @param {string} surface
|
|
* @param {string} locale
|
|
* @param {Array<{unit: {id: string, sourceText: string}}>} pending
|
|
* @returns {string} the file path written
|
|
*/
|
|
export function writePending(dir, surface, locale, pending) {
|
|
const rows = pending.map(({ unit }) => ({ id: unit.id, masked: mask(unit.sourceText).masked }));
|
|
const path = pendingPath(dir, surface, locale);
|
|
writeFileSync(path, `${JSON.stringify(rows, null, 2)}\n`);
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Read a subagent-produced done file: { [id]: translatedMaskedText }.
|
|
* @returns {Map<string, string>}
|
|
*/
|
|
export function readDone(dir, surface, locale) {
|
|
const obj = JSON.parse(readFileSync(donePath(dir, surface, locale), "utf8"));
|
|
return new Map(Object.entries(obj));
|
|
}
|
|
|
|
/**
|
|
* Build a translate(units, locale) that reads translations from a done map,
|
|
* re-masking each unit's source to recover its tokens and restoring them.
|
|
* Returns null for any id absent from the done map (core records it as failed).
|
|
* @param {Map<string, string>} doneMap
|
|
*/
|
|
export function batchResultTranslator(doneMap) {
|
|
return async function translate(units, _locale) {
|
|
const out = new Map();
|
|
for (const unit of units) {
|
|
const raw = doneMap.get(unit.id);
|
|
if (raw == null) {
|
|
out.set(unit.id, null);
|
|
continue;
|
|
}
|
|
const { tokens } = mask(unit.sourceText);
|
|
out.set(unit.id, restore(raw, tokens));
|
|
}
|
|
return out;
|
|
};
|
|
}
|