check-locales.cjs 3.9 KB

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