|
| 1 | +/** |
| 2 | + * Sorts the strings in the lang/*.json source files so we |
| 3 | + * don't have to care about where we add new strings. |
| 4 | + */ |
| 5 | +const fs = require("fs"); |
| 6 | +const path = require("path"); |
| 7 | + |
| 8 | +const en = JSON.parse(fs.readFileSync("lang/ui.en.json")); |
| 9 | +const validKeys = new Set(Object.keys(en)); |
| 10 | + |
| 11 | +const variableRegExp = /({[a-zA-Z0-9]+})/g; |
| 12 | + |
| 13 | +// This is just a best effort check that variables haven't been changed. |
| 14 | +const areTranslationsValid = (file, enJson, translatedJson) => { |
| 15 | + let valid = true; |
| 16 | + const keys = Object.keys(en); |
| 17 | + for (const k of keys) { |
| 18 | + const en = enJson[k].defaultMessage; |
| 19 | + const translated = translatedJson[k].defaultMessage; |
| 20 | + if (en.match(/, plural/)) { |
| 21 | + // Skip ICU strings as we don't understand them. |
| 22 | + continue; |
| 23 | + } |
| 24 | + const variablesEn = new Set(en.match(variableRegExp) ?? []); |
| 25 | + const variablesTranslated = new Set(translated.match(variableRegExp) ?? []); |
| 26 | + const areSetsEqual = (a, b) => |
| 27 | + a.size === b.size && Array.from(a).every((value) => b.has(value)); |
| 28 | + if (!areSetsEqual(variablesEn, variablesTranslated)) { |
| 29 | + if (valid) { |
| 30 | + console.error(file); |
| 31 | + valid = false; |
| 32 | + } |
| 33 | + console.error(` ${en}`); |
| 34 | + console.error(` ${translated}`); |
| 35 | + console.error(` Differing variables!`); |
| 36 | + console.error(); |
| 37 | + } |
| 38 | + } |
| 39 | + return valid; |
| 40 | +}; |
| 41 | + |
| 42 | +const valid = fs |
| 43 | + .readdirSync("lang") |
| 44 | + .filter((f) => f.endsWith(".json")) |
| 45 | + .map((messages) => { |
| 46 | + const file = path.join("lang", messages); |
| 47 | + const data = { |
| 48 | + // Ensure we fallback to English even if we haven't roundtripped via Crowdin yet. |
| 49 | + ...en, |
| 50 | + ...JSON.parse(fs.readFileSync(file)), |
| 51 | + }; |
| 52 | + Object.keys(data).forEach((k) => { |
| 53 | + if (!validKeys.has(k)) { |
| 54 | + delete data[k]; |
| 55 | + } |
| 56 | + }); |
| 57 | + const sortedKeys = Object.keys(data).sort(); |
| 58 | + const result = Object.create(null); |
| 59 | + sortedKeys.forEach((k) => (result[k] = data[k])); |
| 60 | + fs.writeFileSync(file, JSON.stringify(result, null, 2)); |
| 61 | + return areTranslationsValid(file, en, result); |
| 62 | + }) |
| 63 | + .reduce((prev, curr) => prev && curr, true); |
| 64 | +process.exit(valid ? 0 : 2); |
0 commit comments