mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #13946 from overleaf/jpa-i18n-variable-check-ci
[web] flag mismatching translations variables in CI GitOrigin-RevId: 33bfda0975258a18a07db5057bd3a57ee9ad4b6b
This commit is contained in:
parent
f010acb135
commit
c7c77d0851
2 changed files with 46 additions and 18 deletions
|
@ -8,6 +8,9 @@ node scripts/translations/sort.js --check
|
||||||
# Ensure all locales are still in use
|
# Ensure all locales are still in use
|
||||||
node scripts/translations/cleanupUnusedLocales.js --check
|
node scripts/translations/cleanupUnusedLocales.js --check
|
||||||
|
|
||||||
|
# Ensure all locales use the same variables
|
||||||
|
node scripts/translations/checkVariables.js --ignore-orphaned-translations
|
||||||
|
|
||||||
# Ensure all locales used in the frontend are tracked
|
# Ensure all locales used in the frontend are tracked
|
||||||
OUTPUT=data/dumpFolder/i18next-scanner
|
OUTPUT=data/dumpFolder/i18next-scanner
|
||||||
trap "rm -rf $OUTPUT" EXIT
|
trap "rm -rf $OUTPUT" EXIT
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
// Usage: node checkVariables.js <locale>
|
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
|
|
||||||
const GLOBALS = ['__appName__']
|
const GLOBALS = ['__appName__']
|
||||||
const LOCALES = Path.join(__dirname, '../../locales')
|
const LOCALES = Path.join(__dirname, '../../locales')
|
||||||
const baseLocalePath = Path.join(LOCALES, 'en.json')
|
const baseLocalePath = Path.join(LOCALES, 'en.json')
|
||||||
|
|
||||||
if (process.argv.length < 3) {
|
|
||||||
console.error('Usage: node checkVariables.js <locale>')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const localeName = process.argv[2]
|
|
||||||
const localePath = Path.join(LOCALES, `${localeName}.json`)
|
|
||||||
|
|
||||||
const baseLocale = JSON.parse(fs.readFileSync(baseLocalePath, 'utf-8'))
|
const baseLocale = JSON.parse(fs.readFileSync(baseLocalePath, 'utf-8'))
|
||||||
const locale = JSON.parse(fs.readFileSync(localePath, 'utf-8'))
|
const baseLocaleKeys = Object.keys(baseLocale)
|
||||||
|
|
||||||
|
const IGNORE_ORPHANED_TRANSLATIONS = process.argv.includes(
|
||||||
|
'--ignore-orphaned-translations'
|
||||||
|
)
|
||||||
|
|
||||||
|
const IGNORE_NESTING_FOR = {
|
||||||
|
over_x_templates_easy_getting_started: ['__templates__'],
|
||||||
|
all_packages_and_templates: ['__templatesLink__'],
|
||||||
|
}
|
||||||
|
|
||||||
function fetchKeys(str) {
|
function fetchKeys(str) {
|
||||||
const matches = str.matchAll(/__.*?__/g)
|
const matches = str.matchAll(/__.*?__/g)
|
||||||
|
@ -26,8 +24,11 @@ function fetchKeys(str) {
|
||||||
return Array.from(matches).map(match => match[0])
|
return Array.from(matches).map(match => match[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
function difference(base, target) {
|
function difference(key, base, target) {
|
||||||
const keysInBaseButNotInTarget = base.filter(key => !target.includes(key))
|
const nesting = IGNORE_NESTING_FOR[key] || []
|
||||||
|
const keysInBaseButNotInTarget = base.filter(
|
||||||
|
key => !target.includes(key) && !nesting.includes(key)
|
||||||
|
)
|
||||||
const keysInTargetButNotInBase = target.filter(
|
const keysInTargetButNotInBase = target.filter(
|
||||||
key => !base.includes(key) && !GLOBALS.includes(key)
|
key => !base.includes(key) && !GLOBALS.includes(key)
|
||||||
)
|
)
|
||||||
|
@ -37,25 +38,49 @@ function difference(base, target) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let violations = 0
|
||||||
|
for (const localeName of fs.readdirSync(LOCALES)) {
|
||||||
|
if (localeName === 'README.md') continue
|
||||||
|
const localePath = Path.join(LOCALES, localeName)
|
||||||
|
|
||||||
|
const locale = JSON.parse(fs.readFileSync(localePath, 'utf-8'))
|
||||||
|
|
||||||
for (const key of Object.keys(locale)) {
|
for (const key of Object.keys(locale)) {
|
||||||
if (Object.prototype.hasOwnProperty.call(baseLocale, key)) {
|
if (!baseLocaleKeys.includes(key)) {
|
||||||
|
if (IGNORE_ORPHANED_TRANSLATIONS) continue
|
||||||
|
violations += 1
|
||||||
|
console.warn(`[${localeName}] Orphaned key "${key}" not found in en.json`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const keysInTranslation = fetchKeys(locale[key])
|
const keysInTranslation = fetchKeys(locale[key])
|
||||||
const keysInBase = fetchKeys(baseLocale[key])
|
const keysInBase = fetchKeys(baseLocale[key])
|
||||||
const { keysInBaseButNotInTarget, keysInTargetButNotInBase } = difference(
|
const { keysInBaseButNotInTarget, keysInTargetButNotInBase } = difference(
|
||||||
|
key,
|
||||||
keysInBase,
|
keysInBase,
|
||||||
keysInTranslation
|
keysInTranslation
|
||||||
)
|
)
|
||||||
if (keysInBaseButNotInTarget.length) {
|
if (keysInBaseButNotInTarget.length) {
|
||||||
|
violations += keysInBaseButNotInTarget.length
|
||||||
console.warn(
|
console.warn(
|
||||||
`Warning: Missing variables in key ${key}:`,
|
`[${localeName}] Missing variables in key "${key}":`,
|
||||||
keysInBaseButNotInTarget
|
keysInBaseButNotInTarget
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (keysInTargetButNotInBase.length) {
|
if (keysInTargetButNotInBase.length) {
|
||||||
|
violations += keysInTargetButNotInBase.length
|
||||||
console.warn(
|
console.warn(
|
||||||
`Warning: Extra variables in key ${key}:`,
|
`[${localeName}] Extra variables in key "${key}":`,
|
||||||
keysInTargetButNotInBase
|
keysInTargetButNotInBase
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (violations) {
|
||||||
|
console.warn('Variables are not in sync between translations.')
|
||||||
|
process.exit(1)
|
||||||
|
} else {
|
||||||
|
console.log('Variables are in sync.')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue