mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 16:05:39 +00:00
100 lines
3.7 KiB
JavaScript
100 lines
3.7 KiB
JavaScript
// Collect t('key', 'English fallback') calls whose key exists in neither the
|
|
// vendored webmail catalog nor the RN overlay, and add them to
|
|
// locales/rn/en.json using the inline fallback as the English text.
|
|
// Run from the RN repo root: `node scripts/harvest-rn-keys.mjs [--check]`.
|
|
// `--check` only reports (exit 1 when something is missing) - used by the
|
|
// translation coverage test message.
|
|
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
import { join, dirname, relative } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = join(__dirname, '..');
|
|
const SRC = join(ROOT, 'src');
|
|
const EN = join(ROOT, 'locales', 'en', 'common.json');
|
|
const OVERLAY = join(ROOT, 'locales', 'rn', 'en.json');
|
|
const CHECK_ONLY = process.argv.includes('--check');
|
|
|
|
function flatten(obj, prefix = '', out = new Map()) {
|
|
for (const [k, v] of Object.entries(obj)) {
|
|
const key = prefix ? `${prefix}.${k}` : k;
|
|
if (v && typeof v === 'object' && !Array.isArray(v)) flatten(v, key, out);
|
|
else out.set(key, v);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function walk(dir, out = []) {
|
|
for (const entry of readdirSync(dir)) {
|
|
const p = join(dir, entry);
|
|
if (statSync(p).isDirectory()) {
|
|
if (entry === '__tests__' || entry === 'node_modules') continue;
|
|
walk(p, out);
|
|
} else if (/\.tsx?$/.test(entry)) {
|
|
out.push(p);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function setDeep(obj, key, value) {
|
|
const parts = key.split('.');
|
|
let cur = obj;
|
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
if (!cur[parts[i]] || typeof cur[parts[i]] !== 'object') cur[parts[i]] = {};
|
|
cur = cur[parts[i]];
|
|
}
|
|
if (cur[parts[parts.length - 1]] === undefined) cur[parts[parts.length - 1]] = value;
|
|
}
|
|
|
|
function sortDeep(obj) {
|
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj;
|
|
return Object.keys(obj).sort().reduce((acc, k) => {
|
|
acc[k] = sortDeep(obj[k]);
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
const en = flatten(JSON.parse(readFileSync(EN, 'utf8')));
|
|
const overlay = JSON.parse(readFileSync(OVERLAY, 'utf8'));
|
|
const overlayFlat = flatten(overlay);
|
|
|
|
// t('key', 'fallback') / t('key', "fallback") / t('key', `fallback`) - the
|
|
// fallback may span lines; keep it simple: single-quoted or double-quoted,
|
|
// non-nested strings.
|
|
const CALL_RE = /\bt\(\s*'([^']+)'\s*,\s*(?:'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)")/g;
|
|
const KEY_ONLY_RE = /\bt\(\s*'([^']+)'/g;
|
|
|
|
const added = [];
|
|
const missingNoFallback = [];
|
|
for (const file of walk(SRC)) {
|
|
const text = readFileSync(file, 'utf8');
|
|
const withFallback = new Map();
|
|
for (const m of text.matchAll(CALL_RE)) {
|
|
const value = (m[2] ?? m[3] ?? '').replace(/\\(['"])/g, '$1');
|
|
if (value) withFallback.set(m[1], value);
|
|
}
|
|
for (const m of text.matchAll(KEY_ONLY_RE)) {
|
|
const key = m[1];
|
|
if (en.has(key) || overlayFlat.has(key)) continue;
|
|
const fallback = withFallback.get(key);
|
|
if (!fallback) {
|
|
missingNoFallback.push(`${key} (${relative(ROOT, file)})`);
|
|
continue;
|
|
}
|
|
if (!added.some((a) => a.key === key)) added.push({ key, fallback, file: relative(ROOT, file) });
|
|
}
|
|
}
|
|
|
|
if (added.length === 0 && missingNoFallback.length === 0) {
|
|
console.log('All t() keys are covered.');
|
|
process.exit(0);
|
|
}
|
|
for (const a of added) console.log(`${CHECK_ONLY ? 'missing' : 'added'}: ${a.key} = ${JSON.stringify(a.fallback)} (${a.file})`);
|
|
for (const m of missingNoFallback) console.log(`missing (no inline fallback, add by hand): ${m}`);
|
|
|
|
if (CHECK_ONLY) process.exit(1);
|
|
for (const a of added) setDeep(overlay, a.key, a.fallback);
|
|
writeFileSync(OVERLAY, JSON.stringify(sortDeep(overlay), null, 2) + '\n');
|
|
console.log(`Wrote ${added.length} key(s) to locales/rn/en.json`);
|