check-locales.cjs 3.9 KB

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