tr-format.ts 3.0 KB

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