check-locales.cjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/env node
  2. // This file does a few things to ensure that the Locales are present and valid:
  3. // - Ensures that the name of the locale exists in the language list
  4. // - Ensures that each locale contains the translations used in the application
  5. // - Ensures that there are no unused translations in the locale files
  6. // - Also checks the error messages returned by the backend
  7. const allLocales = [
  8. ["en", "en-US"],
  9. ["es", "es-ES"],
  10. ["de", "de-DE"],
  11. ["ru", "ru-RU"],
  12. ["zh", "zh-CN"],
  13. ];
  14. const ignoreUnused = [
  15. /^.*$/,
  16. ];
  17. const { spawnSync } = require("child_process");
  18. const fs = require("fs");
  19. const tmp = require("tmp");
  20. // Parse backend errors
  21. const BACKEND_ERRORS_FILE = "../backend/internal/errors/errors.go";
  22. const BACKEND_ERRORS = [];
  23. /*
  24. try {
  25. const backendErrorsContent = fs.readFileSync(BACKEND_ERRORS_FILE, "utf8");
  26. const backendErrorsContentRes = [
  27. ...backendErrorsContent.matchAll(/(?:errors|eris)\.New\("([^"]+)"\)/g),
  28. ];
  29. backendErrorsContentRes.map((item) => {
  30. BACKEND_ERRORS.push("error." + item[1]);
  31. return null;
  32. });
  33. } catch (err) {
  34. console.log("\x1b[31m%s\x1b[0m", err);
  35. process.exit(1);
  36. }
  37. */
  38. // get all translations used in frontend code
  39. const tmpobj = tmp.fileSync({ postfix: ".json" });
  40. spawnSync("yarn", ["locale-extract", "--out-file", tmpobj.name]);
  41. const allLocalesInProject = require(tmpobj.name);
  42. // get list og language names and locales
  43. const langList = require("./src/locale/src/lang-list.json");
  44. // store a list of all validation errors
  45. const allErrors = [];
  46. const allWarnings = [];
  47. const allKeys = [];
  48. const checkLangList = (fullCode) => {
  49. const key = "locale-" + fullCode;
  50. if (typeof langList[key] === "undefined") {
  51. allErrors.push(
  52. "ERROR: `" + key + "` language does not exist in lang-list.json",
  53. );
  54. }
  55. };
  56. const compareLocale = (locale) => {
  57. const projectLocaleKeys = Object.keys(allLocalesInProject);
  58. // Check that locale contains the items used in the codebase
  59. projectLocaleKeys.map((key) => {
  60. if (typeof locale.data[key] === "undefined") {
  61. allErrors.push(
  62. "ERROR: `" + locale[0] + "` does not contain item: `" + key + "`",
  63. );
  64. }
  65. return null;
  66. });
  67. // Check that locale contains all error.* items
  68. BACKEND_ERRORS.forEach((key) => {
  69. if (typeof locale.data[key] === "undefined") {
  70. allErrors.push(
  71. "ERROR: `" + locale[0] + "` does not contain item: `" + key + "`",
  72. );
  73. }
  74. return null;
  75. });
  76. // Check that locale does not contain items not used in the codebase
  77. const localeKeys = Object.keys(locale.data);
  78. localeKeys.map((key) => {
  79. let ignored = false;
  80. ignoreUnused.map((regex) => {
  81. if (key.match(regex)) {
  82. ignored = true;
  83. }
  84. return null;
  85. });
  86. if (!ignored && typeof allLocalesInProject[key] === "undefined") {
  87. // ensure this key doesn't exist in the backend errors either
  88. if (!BACKEND_ERRORS.includes(key)) {
  89. allErrors.push(
  90. "ERROR: `" + locale[0] + "` contains unused item: `" + key + "`",
  91. );
  92. }
  93. }
  94. // Add this key to allKeys
  95. if (allKeys.indexOf(key) === -1) {
  96. allKeys.push(key);
  97. }
  98. return null;
  99. });
  100. };
  101. // Checks for any keys missing from this locale, that
  102. // have been defined in any other locales
  103. const checkForMissing = (locale) => {
  104. allKeys.forEach((key) => {
  105. if (typeof locale.data[key] === "undefined") {
  106. allWarnings.push(
  107. "WARN: `" + locale[0] + "` does not contain item: `" + key + "`",
  108. );
  109. }
  110. return null;
  111. });
  112. };
  113. // Local all locale data
  114. allLocales.map((locale, idx) => {
  115. checkLangList(locale[1]);
  116. allLocales[idx].data = require("./src/locale/src/" + locale[0] + ".json");
  117. return null;
  118. });
  119. // Verify all locale data
  120. allLocales.map((locale) => {
  121. compareLocale(locale);
  122. checkForMissing(locale);
  123. return null;
  124. });
  125. if (allErrors.length) {
  126. allErrors.map((err) => {
  127. console.log("\x1b[31m%s\x1b[0m", err);
  128. return null;
  129. });
  130. }
  131. if (allWarnings.length) {
  132. allWarnings.map((err) => {
  133. console.log("\x1b[33m%s\x1b[0m", err);
  134. return null;
  135. });
  136. }
  137. if (allErrors.length) {
  138. process.exit(1);
  139. }
  140. console.log("\x1b[32m%s\x1b[0m", "Locale check passed");
  141. process.exit(0);