Files
Cibello-app/apps/mobile/scripts/i18n-add-language.mjs
T
Claude 6d36ec7459 feat(i18n): i18n:add-language – nytt språk i ett kommando
node scripts/i18n-add-language.mjs <kod> <bcp47> "<eget namn>"
Skapar locale-fil (sv-platshållare), registrerar i i18n.ts (import +
SUPPORTED_LANGUAGES + resources), lägger språkväljar-etiketten i alla filer.
Endast textinsättning → minimala diffar. Verifierat: testspråk -> typecheck+vakt gröna.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 02:32:31 +00:00

102 lines
4.4 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.
#!/usr/bin/env node
/**
* i18n:add-language lägg till ett nytt språk i ett kommando.
*
* node scripts/i18n-add-language.mjs <kod> <bcp47-tagg> "<eget namn>"
* ex: node scripts/i18n-add-language.mjs el el-GR "Ελληνικά"
*
* Gör ALL plumbing automatiskt:
* 1. Skapar src/locales/<kod>/common.json kopia av svenska (platshållare),
* så appen funkar direkt och paritetsvakten är nöjd från start.
* 2. Registrerar språket i src/lib/i18n.ts (import + SUPPORTED_LANGUAGES + resources).
* 3. Lägger språkväljar-etiketten profile.language.<kod> = "<eget namn>" i ALLA
* locale-filer (annars faller vakten på saknad nyckel).
*
* Kvar för dig: översätt värdena i src/locales/<kod>/common.json och kör
* `pnpm --filter @app/mobile i18n:check` den listar exakt vad som ev. fattas.
*
* Endast textinsättning (aldrig omsortering) → minimala diffar på befintliga filer.
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
const ROOT = process.cwd(); // apps/mobile
const LOC = join(ROOT, "src/locales");
const I18N = join(ROOT, "src/lib/i18n.ts");
const [code, tag, ...labelParts] = process.argv.slice(2);
const label = labelParts.join(" ");
if (!code || !tag || !label) {
console.error('Användning: node scripts/i18n-add-language.mjs <kod> <bcp47-tagg> "<eget namn>"');
console.error('Exempel: node scripts/i18n-add-language.mjs el el-GR "Ελληνικά"');
process.exit(1);
}
if (!/^[a-z]{2,3}$/.test(code)) {
console.error(`Ogiltig språkkod "${code}" 23 gemener (t.ex. el, cs, uk).`);
process.exit(1);
}
if (!/^[a-z]{2,3}-[A-Za-z0-9]{2,4}$/.test(tag)) {
console.error(`Ogiltig BCP 47-tagg "${tag}" t.ex. el-GR, cs-CZ, pt-BR.`);
process.exit(1);
}
if (existsSync(join(LOC, code, "common.json"))) {
console.error(`src/locales/${code}/common.json finns redan avbryter.`);
process.exit(1);
}
const labelKey = `profile.language.${code}`;
const labelLine = ` ${JSON.stringify(labelKey)}: ${JSON.stringify(label)},`;
const ANCHOR = '"profile.language.sv":';
/** Sätt in etikettraden direkt efter profile.language.sv, annars först i objektet. */
function withLabel(raw) {
if (raw.includes(labelKey + '"') || raw.includes(labelKey + "'")) return raw; // finns redan
const lines = raw.split("\n");
let at = lines.findIndex((l) => l.includes(ANCHOR));
if (at === -1) at = lines.findIndex((l) => l.trim().startsWith("{")); // fallback: efter {
lines.splice(at + 1, 0, labelLine);
return lines.join("\n");
}
// ---- 1 + 3. Ny fil (kopia av sv + etikett) + etikett i alla befintliga -----
const svRaw = readFileSync(join(LOC, "sv/common.json"), "utf8");
mkdirSync(join(LOC, code), { recursive: true });
writeFileSync(join(LOC, code, "common.json"), withLabel(svRaw));
const existing = readdirSync(LOC).filter((d) => d !== code && statSync(join(LOC, d)).isDirectory());
for (const lang of existing) {
const p = join(LOC, lang, "common.json");
writeFileSync(p, withLabel(readFileSync(p, "utf8")));
}
// ---- 2. Registrera i i18n.ts (import + SUPPORTED_LANGUAGES + resources) -----
{
let lines = readFileSync(I18N, "utf8").split("\n");
const importRe = /^import \w+ from "\.\.\/locales\/\w+\/common\.json";$/;
let lastImport = -1;
lines.forEach((l, i) => importRe.test(l) && (lastImport = i));
lines.splice(lastImport + 1, 0, `import ${code} from "../locales/${code}/common.json";`);
const arrEnd = lines.findIndex((l) => l.trim() === "] as const;");
lines.splice(arrEnd, 0, ` { code: "${code}", tag: "${tag}", labelKey: "${labelKey}" },`);
const resRe = /^\s+\w+: \{ common: \w+ \},$/;
let lastRes = -1;
lines.forEach((l, i) => resRe.test(l) && (lastRes = i));
lines.splice(lastRes + 1, 0, ` ${code}: { common: ${code} },`);
writeFileSync(I18N, lines.join("\n"));
}
const keyCount = svRaw.split("\n").filter((l) => l.includes('": ')).length;
console.log(`\n✓ La till "${code}" (${tag}, "${label}").`);
console.log(
` • src/locales/${code}/common.json skapad (~${keyCount} nycklar, svenska platshållare)`,
);
console.log(` • registrerad i src/lib/i18n.ts (import + SUPPORTED_LANGUAGES + resources)`);
console.log(`${labelKey} = "${label}" i alla locale-filer`);
console.log(`\nNästa steg:`);
console.log(` 1. Översätt värdena i src/locales/${code}/common.json`);
console.log(` 2. pnpm --filter @app/mobile i18n:check`);
console.log(` 3. pnpm --filter @app/mobile typecheck`);