check-locales.cjs 4.0 KB

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