Language
This commit is contained in:
parent
c2c4ed6708
commit
2efa4d9f47
9 changed files with 1324 additions and 125 deletions
|
|
@ -214,16 +214,41 @@ function readServerFeatureNames() {
|
|||
return [...new Set(names)];
|
||||
}
|
||||
|
||||
// Names of the Enum/Numeric feature *configs* (each needs a description + detail
|
||||
// translation). We take the FIRST `name:` field after every Feature::Enum( /
|
||||
// Feature::Numeric( opening. This deliberately skips macro-generated configs
|
||||
// whose name is a `concat!(...)` expression (the crime rates — handled via
|
||||
// deriveLegacyCrimeKeys instead) and stops the lazy match from running past such
|
||||
// a config into an unrelated FeatureGroup `name:` (which previously made the
|
||||
// group name "Properties" look like a required feature).
|
||||
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]}"`));
|
||||
const re = /Feature::(?:Enum|Numeric)\(/g;
|
||||
let m;
|
||||
while ((m = re.exec(src))) {
|
||||
const after = src.slice(m.index + m[0].length);
|
||||
const nm = after.match(/\bname:\s*(?:"((?:\\.|[^"\\])*)"|concat!)/);
|
||||
if (nm && nm[1] !== undefined) names.push(JSON.parse(`"${nm[1]}"`));
|
||||
}
|
||||
return [...new Set(names)];
|
||||
}
|
||||
|
||||
// The crime-rate features are generated by the crime_rate_features! macro and
|
||||
// named "<type> (/yr, 7y|2y)". Their description/detail translations are shared
|
||||
// across both windows under a single legacy "<type> (avg/yr)" key. This mirror
|
||||
// of legacyCrimeFeatureKey() in descriptions.ts MUST use the same suffix regex,
|
||||
// derived straight from the server keys so the checker and the runtime lookup
|
||||
// stay in lock-step.
|
||||
function deriveLegacyCrimeKeys(serverKeys) {
|
||||
const legacy = new Set();
|
||||
for (const key of serverKeys) {
|
||||
const match = key.match(/^(.*) \(\/yr, \d+y\)$/);
|
||||
if (match) legacy.add(`${match[1]} (avg/yr)`);
|
||||
}
|
||||
return legacy;
|
||||
}
|
||||
|
||||
function readNamedRecord(file, varName) {
|
||||
const sf = parseFile(join(I18N_DIR, file));
|
||||
const init = findVarInitializer(sf, varName);
|
||||
|
|
@ -355,7 +380,7 @@ function checkLocales(supportedCodes) {
|
|||
}
|
||||
}
|
||||
|
||||
function checkRecordCoverage(file, varName, supportedCodes, serverKeys, requiredKeys) {
|
||||
function checkRecordCoverage(file, varName, supportedCodes, serverKeys, requiredKeys, legacyCrimeKeys) {
|
||||
const record = readNamedRecord(file, varName);
|
||||
const expected = supportedCodes.filter((c) => c !== 'en');
|
||||
const present = Object.keys(record);
|
||||
|
|
@ -397,10 +422,12 @@ function checkRecordCoverage(file, varName, supportedCodes, serverKeys, required
|
|||
}
|
||||
}
|
||||
|
||||
// Every key here must also be a translatable feature name in en.ts > server.
|
||||
// Otherwise the description is unreachable — ts() looks up server.${name}.
|
||||
// Every key here must also be a translatable feature name in en.ts > server,
|
||||
// or a legacy crime key that maps onto the per-window server keys (see
|
||||
// deriveLegacyCrimeKeys / legacyCrimeFeatureKey). Otherwise the description is
|
||||
// unreachable — ts() looks up server.${name}.
|
||||
for (const key of union) {
|
||||
if (!serverKeys.has(key)) {
|
||||
if (!serverKeys.has(key) && !legacyCrimeKeys.has(key)) {
|
||||
fail(`${file}: key "${key}" has no matching entry in en.ts > server`);
|
||||
}
|
||||
}
|
||||
|
|
@ -478,16 +505,25 @@ function main() {
|
|||
checkLocaleLoaders(supportedCodes);
|
||||
const en = readLocale('en');
|
||||
const serverKeys = new Set(Object.keys(en.server ?? {}));
|
||||
const sourceFeatureKeys = readServerFeatureConfigNames();
|
||||
const legacyCrimeKeys = deriveLegacyCrimeKeys(serverKeys);
|
||||
const requiredKeys = [...readServerFeatureConfigNames(), ...legacyCrimeKeys];
|
||||
checkServerSourceCoverage(serverKeys);
|
||||
checkRecordCoverage(
|
||||
'descriptions.ts',
|
||||
'descriptions',
|
||||
supportedCodes,
|
||||
serverKeys,
|
||||
sourceFeatureKeys
|
||||
requiredKeys,
|
||||
legacyCrimeKeys
|
||||
);
|
||||
checkRecordCoverage(
|
||||
'details.ts',
|
||||
'details',
|
||||
supportedCodes,
|
||||
serverKeys,
|
||||
requiredKeys,
|
||||
legacyCrimeKeys
|
||||
);
|
||||
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