translateLocales.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. const yargs = require('yargs/yargs')
  2. const { hideBin } = require('yargs/helpers')
  3. const Path = require('path')
  4. const fs = require('fs')
  5. const LOCALES = Path.join(__dirname, '../../locales')
  6. const VALID_LOCALES = Object.keys(
  7. require('../../config/settings.defaults').translatedLanguages
  8. ).filter(locale => locale !== 'en')
  9. const readline = require('readline').createInterface({
  10. input: process.stdin,
  11. output: process.stdout,
  12. })
  13. const argv = yargs(hideBin(process.argv))
  14. .usage('Translate locales')
  15. .option('locale', {
  16. alias: 'l',
  17. type: 'string',
  18. required: 'true',
  19. description: 'Target 2-letter locale code',
  20. choices: VALID_LOCALES,
  21. })
  22. .option('skip-until', {
  23. type: 'string',
  24. description: 'Skip locales until after the provided key',
  25. })
  26. .parse()
  27. async function translateLocales() {
  28. let { locale, skip } = argv
  29. console.log(`Looking for missing [${locale}] translations...`)
  30. const keysToUploadFolder = Path.join(__dirname, `translated-keys-to-upload`)
  31. if (!fs.existsSync(keysToUploadFolder)) {
  32. fs.mkdirSync(keysToUploadFolder)
  33. }
  34. const localeKeysToUploadPath = Path.join(keysToUploadFolder, `${locale}.json`)
  35. const keysToUpload = fs.existsSync(localeKeysToUploadPath)
  36. ? JSON.parse(fs.readFileSync(localeKeysToUploadPath, 'utf-8'))
  37. : []
  38. const englishTranslations = await loadTranslations('en')
  39. const englishKeys = Object.keys(englishTranslations)
  40. const localeTranslations = await loadTranslations(locale)
  41. const translatedKeys = Object.keys(localeTranslations)
  42. console.log(
  43. `Currently translated: ${translatedKeys.length} / ${englishKeys.length}`
  44. )
  45. for (const key of englishKeys) {
  46. if (skip) {
  47. if (key === skip) skip = ''
  48. continue
  49. }
  50. const translation = localeTranslations[key]
  51. if (!translation || translation.length === 0) {
  52. let value = await prompt(
  53. `\nMissing translation for: ${key}\n"${englishTranslations[key]}"\n`
  54. )
  55. while (value.includes("'")) {
  56. value = await prompt(
  57. `\nTranslations should not contain single-quote characters, please use curvy quotes (‘ or ’) instead:\n`
  58. )
  59. }
  60. if (!value) {
  61. console.log(`Skipping ${key}`)
  62. continue
  63. }
  64. localeTranslations[key] = value
  65. const path = Path.join(LOCALES, `${locale}.json`)
  66. const sorted =
  67. JSON.stringify(
  68. localeTranslations,
  69. Object.keys(localeTranslations).sort(),
  70. 2
  71. ) + '\n'
  72. fs.writeFileSync(path, sorted)
  73. keysToUpload.push(key)
  74. const formattedKeysToUpload =
  75. JSON.stringify(Array.from(new Set(keysToUpload)), null, 2) + '\n'
  76. await fs.writeFileSync(localeKeysToUploadPath, formattedKeysToUpload)
  77. console.log(`"${key}": "${value}" added to ${locale}.json`)
  78. }
  79. }
  80. }
  81. async function loadTranslations(locale) {
  82. return JSON.parse(
  83. fs.readFileSync(Path.join(LOCALES, `${locale}.json`), 'utf-8')
  84. )
  85. }
  86. function prompt(text) {
  87. return new Promise((resolve, reject) =>
  88. readline.question(text, value => {
  89. resolve(value)
  90. })
  91. )
  92. }
  93. translateLocales()
  94. .then(() => {
  95. process.exit(0)
  96. })
  97. .catch(error => {
  98. console.error(error)
  99. process.exit(1)
  100. })