no-orphan-locale-keys.js 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // Detect translation keys in non-en locale files that don't exist in
  2. // en.json (orphans).
  3. const fs = require('node:fs')
  4. const Path = require('node:path')
  5. const enCache = new Map()
  6. function loadEnKeys(localesDir) {
  7. if (enCache.has(localesDir)) return enCache.get(localesDir)
  8. const path = Path.join(localesDir, 'en.json')
  9. const data = JSON.parse(fs.readFileSync(path, 'utf8'))
  10. const keys = new Set(Object.keys(data))
  11. enCache.set(localesDir, keys)
  12. return keys
  13. }
  14. module.exports = {
  15. meta: {
  16. type: 'problem',
  17. fixable: 'code',
  18. docs: {
  19. description:
  20. 'Detect translation keys in non-en locale files that are not present in en.json.',
  21. },
  22. schema: [],
  23. messages: {
  24. orphan:
  25. 'Translation key "{{key}}" is not present in en.json (orphan key).',
  26. },
  27. },
  28. create(context) {
  29. const filename = context.filename
  30. const localesDir = Path.dirname(filename)
  31. if (Path.basename(filename) === 'en.json') return {}
  32. let enKeys
  33. try {
  34. enKeys = loadEnKeys(localesDir)
  35. } catch {
  36. return {}
  37. }
  38. return {
  39. 'Document > Object'(node) {
  40. const orphans = node.members.filter(m => !enKeys.has(m.name.value))
  41. if (orphans.length === 0) return
  42. const text = context.sourceCode.text
  43. const parsed = JSON.parse(text)
  44. for (const member of orphans) {
  45. delete parsed[member.name.value]
  46. }
  47. const sortedRemaining = Object.keys(parsed).sort()
  48. const cleaned = JSON.stringify(parsed, sortedRemaining, 2) + '\n'
  49. for (const member of orphans) {
  50. context.report({
  51. node: member,
  52. messageId: 'orphan',
  53. data: { key: member.name.value },
  54. fix(fixer) {
  55. return fixer.replaceTextRange([0, text.length], cleaned)
  56. },
  57. })
  58. }
  59. },
  60. }
  61. },
  62. }