lgtm
This commit is contained in:
parent
a08b5d2ae0
commit
b98f0e3904
38 changed files with 3732 additions and 483 deletions
|
|
@ -12,6 +12,8 @@
|
|||
// 5. The lazy locale loader map covers every non-English supported language.
|
||||
// 6. Selected visible UI strings that previously slipped through are not
|
||||
// hardcoded outside the i18n files.
|
||||
// 7. Server-derived feature/group names from server-rs/src/features.rs are
|
||||
// present in en.ts > server so they can be translated.
|
||||
//
|
||||
// The script parses the TypeScript source with the compiler API and walks the
|
||||
// AST — no runtime import, no transpilation, no temp files. Run it with:
|
||||
|
|
@ -26,6 +28,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|||
const I18N_DIR = join(__dirname, '..', 'src', 'i18n');
|
||||
const LOCALES_DIR = join(I18N_DIR, 'locales');
|
||||
const SRC_DIR = join(__dirname, '..', 'src');
|
||||
const FEATURES_RS = join(__dirname, '..', '..', 'server-rs', 'src', 'features.rs');
|
||||
|
||||
const PLACEHOLDER_RE = /\{\{\s*[a-zA-Z_][\w]*\s*\}\}/g;
|
||||
const HTML_TAG_RE = /<\/?[a-zA-Z][\w]*\b[^>]*>/g;
|
||||
|
|
@ -204,6 +207,26 @@ function readLocale(code) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
function readServerFeatureNames() {
|
||||
const src = readFileSync(FEATURES_RS, 'utf8');
|
||||
const names = [];
|
||||
const re = /\bname:\s*"((?:\\.|[^"\\])*)"/g;
|
||||
for (const match of src.matchAll(re)) {
|
||||
names.push(JSON.parse(`"${match[1]}"`));
|
||||
}
|
||||
return [...new Set(names)];
|
||||
}
|
||||
|
||||
function readServerFeatureConfigNames() {
|
||||
const src = readFileSync(FEATURES_RS, 'utf8');
|
||||
const names = [];
|
||||
const re = /Feature::(?:Enum|Numeric)\([^]*?name:\s*"((?:\\.|[^"\\])*)"/g;
|
||||
for (const match of src.matchAll(re)) {
|
||||
names.push(JSON.parse(`"${match[1]}"`));
|
||||
}
|
||||
return [...new Set(names)];
|
||||
}
|
||||
|
||||
function readNamedRecord(file, varName) {
|
||||
const sf = parseFile(join(I18N_DIR, file));
|
||||
const init = findVarInitializer(sf, varName);
|
||||
|
|
@ -335,7 +358,7 @@ function checkLocales(supportedCodes) {
|
|||
}
|
||||
}
|
||||
|
||||
function checkRecordCoverage(file, varName, supportedCodes, serverKeys) {
|
||||
function checkRecordCoverage(file, varName, supportedCodes, serverKeys, requiredKeys) {
|
||||
const record = readNamedRecord(file, varName);
|
||||
const expected = supportedCodes.filter((c) => c !== 'en');
|
||||
const present = Object.keys(record);
|
||||
|
|
@ -357,6 +380,12 @@ function checkRecordCoverage(file, varName, supportedCodes, serverKeys) {
|
|||
if (record[code]) for (const k of Object.keys(record[code])) union.add(k);
|
||||
}
|
||||
|
||||
for (const key of requiredKeys) {
|
||||
if (!union.has(key)) {
|
||||
fail(`${file}: missing translations for API feature "${key}"`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const code of expected) {
|
||||
const langKeys = new Set(Object.keys(record[code] ?? {}));
|
||||
for (const key of union) {
|
||||
|
|
@ -380,6 +409,14 @@ function checkRecordCoverage(file, varName, supportedCodes, serverKeys) {
|
|||
}
|
||||
}
|
||||
|
||||
function checkServerSourceCoverage(serverKeys) {
|
||||
for (const name of readServerFeatureNames()) {
|
||||
if (!serverKeys.has(name)) {
|
||||
fail(`en.ts > server is missing API feature/group name "${name}" from features.rs`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function collectSourceFiles(dir, out = []) {
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const path = join(dir, entry.name);
|
||||
|
|
@ -444,8 +481,16 @@ function main() {
|
|||
checkLocaleLoaders(supportedCodes);
|
||||
const en = readLocale('en');
|
||||
const serverKeys = new Set(Object.keys(en.server ?? {}));
|
||||
checkRecordCoverage('descriptions.ts', 'descriptions', supportedCodes, serverKeys);
|
||||
checkRecordCoverage('details.ts', 'details', supportedCodes, serverKeys);
|
||||
const sourceFeatureKeys = readServerFeatureConfigNames();
|
||||
checkServerSourceCoverage(serverKeys);
|
||||
checkRecordCoverage(
|
||||
'descriptions.ts',
|
||||
'descriptions',
|
||||
supportedCodes,
|
||||
serverKeys,
|
||||
sourceFeatureKeys
|
||||
);
|
||||
checkRecordCoverage('details.ts', 'details', supportedCodes, serverKeys, sourceFeatureKeys);
|
||||
checkForbiddenVisibleStrings();
|
||||
|
||||
for (const w of warnings) console.warn(`warn: ${w}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue