tr-format.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { readFile, writeFile } from "node:fs/promises";
  2. import k from "kleur";
  3. import type { TrLocale } from "../utils/index.js";
  4. import locales from "../../assets/locales.json" with { type: "json" };
  5. const prepTranslate = process.argv.find((v) => v.match(/--prep(are)?/) || v.toLowerCase() === "-p");
  6. const onlyLocalesRaw = process.argv.find((v) => v.startsWith("--only") || v.toLowerCase().startsWith("-o"));
  7. const onlyLocales = onlyLocalesRaw?.split("=")[1]?.replace(/"/g, "")
  8. ?.replace(/\s/g, "")?.split(",") as TrLocale[] | undefined;
  9. const includeBased = Boolean(process.argv.find((v) => v.match(/--include-based/) || v.toLowerCase() === "-b"));
  10. async function run() {
  11. console.log("\nReformatting translation files...");
  12. const enUS = await readFile("./assets/translations/en-US.json", "utf-8");
  13. const enUS_obj = JSON.parse(enUS);
  14. const localeKeysRaw = Object.keys(locales) as TrLocale[];
  15. const localeKeys = localeKeysRaw.filter((key) => key !== "en-US") as Exclude<TrLocale, "en-US">[];
  16. let reformattedAmt = 0;
  17. for(const locale of localeKeys) {
  18. if(onlyLocales && !onlyLocales.includes(locale))
  19. continue;
  20. // use en-US as base, replace values with values from locale file
  21. let localeFile = enUS;
  22. const localeObj = JSON.parse(await readFile(`./assets/translations/${locale}.json`, "utf-8"));
  23. if(!includeBased && localeObj.base)
  24. continue;
  25. for(const k of Object.keys(enUS_obj)) {
  26. const val = localeObj?.[k];
  27. if(val)
  28. localeFile = localeFile.replace(new RegExp(`"${k}":\\s+".*"`, "m"), `"${k}": "${escapeJsonVal(val).trim()}"`);
  29. else {
  30. if(prepTranslate)
  31. localeFile = localeFile.replace(new RegExp(`\\n\\s+"${k}":\\s+".*",?`, "m"), `\n "${k}": "",\n "${k}": "${escapeJsonVal(enUS_obj[k]).trim()}",`);
  32. else
  33. localeFile = localeFile.replace(new RegExp(`\\n\\s+"${k}":\\s+".*",?`, "m"), "");
  34. }
  35. }
  36. // remove last trailing comma if present
  37. const pattern = /^\s*".*":\s+".*",?$/gm;
  38. const matchesAmt = localeFile.match(pattern)?.length ?? 0;
  39. let match: RegExpExecArray | null = null;
  40. let i = 0;
  41. while(match = pattern.exec(localeFile)) {
  42. const part = localeFile.substring(match.index, pattern.lastIndex);
  43. if(i === matchesAmt - 1) {
  44. if(part.endsWith(","))
  45. localeFile = localeFile.replace(part, part.substring(0, part.length - 1));
  46. }
  47. i++;
  48. }
  49. // reinsert base if present in locale file
  50. if(localeObj.base)
  51. localeFile = localeFile.replace(/\s*\{\s*/, `{\n "base": "${localeObj.base}",\n `);
  52. // add EOL newline if not present
  53. if(!localeFile.endsWith("\n"))
  54. localeFile += "\n";
  55. // overwrite original file
  56. // (backup is available through git history so idc)
  57. await writeFile(`./assets/translations/${locale}.json`, localeFile);
  58. reformattedAmt++;
  59. }
  60. console.log(`\nDone reformatting ${k.green(reformattedAmt)} translation file${reformattedAmt === 1 ? "" : "s"}!\n`);
  61. }
  62. /** Escapes various characters for use as a JSON value */
  63. function escapeJsonVal(val: string) {
  64. return val
  65. .replace(/\n/gm, "\\n")
  66. .replace(/"/gm, "\\\"");
  67. }
  68. run();