check-locales.cjs 4.0 KB

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