tinymist/scripts/build-l10n.mjs
Myriad-Dreamin 4cbe35a286
feat: employ l10n to tinymist-cli and vscode extension (#1505)
* feat: runtime translation

* feat: poc of rust translation

* feat: clean up implementation

* feat: initialize correctly

* dev: remove dirty log

* dev: rename l10nMsg

* fix: desc

* feat: update assets building

* feat: update assets building

* build: update cargo.lock

* fix: warnings

* fix: warnings

* dev: expose api

* fix: compile error

* fix: compile errors in scripts
2025-03-15 10:38:07 +08:00

104 lines
3 KiB
JavaScript

// The toml looks like that:
// # The translation are partially generated by copilot
// [description]
// en = "An integrated language service for Typst"
// zh-CN = "Typst 的集成语言服务"
import * as fs from "fs";
import * as path from "path";
const projectRoot = path.resolve(import.meta.dirname, "..");
/**
*
* @param {string} output
* @param {string[]} inputs
* @returns
*/
function translate(output, kind, inputs) {
const lines = inputs.flatMap((input) =>
fs
.readFileSync(path.resolve(projectRoot, input), "utf-8")
.split("\n")
.map((line) => line.trim())
.filter((line) => !line.startsWith("#") && line.length > 0),
);
const translations = {};
let key = "";
for (let line of lines) {
if (line.startsWith("[")) {
key = line.substring(1, line.length - 1);
if (key.startsWith('"')) {
key = JSON.parse(key);
}
} else {
const equalIndex = line.indexOf("=");
const lang = line.substring(0, equalIndex).trim();
const value = line.substring(equalIndex + 1).trim();
translations[lang] ||= {};
translations[lang][key] = JSON.parse(value);
}
}
const langs = Object.keys(translations);
const langEn = langs.find((lang) => lang === "en");
if (!langEn) {
console.error("en is required");
return;
}
const langRest = langs.filter((lang) => lang !== "en");
const langDir = path.resolve(projectRoot, output);
fs.mkdirSync(langDir, { recursive: true });
const langEnPath = `${langDir}/${kind}.json`;
const langEnData = translations["en"];
fs.writeFileSync(langEnPath, JSON.stringify(langEnData, null, 2));
for (let lang of langRest) {
const langPath = `${langDir}/${kind}.${lang}.json`;
const langData = translations[lang];
const langPack = JSON.stringify(langData, null, 2);
fs.writeFileSync(langPath, langPack);
// alias zh-cn
if (kind === "bundle.l10n" && lang === "zh") {
const langZhCNPath = `${langDir}/${kind}.zh-cn.json`;
fs.writeFileSync(langZhCNPath, langPack);
}
}
return translations;
}
// todo: verify using rust
function genVscodeExt() {
const translations = translate("editors/vscode", "package.nls", ["locales/tinymist-vscode.toml"]);
translate("editors/vscode/l10n", "bundle.l10n", ["locales/tinymist-vscode-rt.toml"]);
const pat = /\%(extension\.tinymist\..*?)\%/g;
const data = fs.readFileSync(path.resolve(projectRoot, "editors/vscode/package.json"), "utf-8");
const matchAll = data.matchAll(pat);
const used = Array.from(matchAll).map((m) => m[1]);
used.push("description");
const en = translations["en"];
const enKeys = Object.keys(en);
const missing = used.filter((key) => !enKeys.includes(key));
if (missing.length > 0) {
console.error("Missing translations", missing);
}
const extra = enKeys.filter((key) => !used.includes(key));
if (extra.length > 0) {
console.error("Extra translations", extra);
}
return translations;
}
export const vscodeExtTranslations = genVscodeExt();