mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
- Add namespace-based JSON locale structure (en/es) - Add I18n provider/context with locale persistence and fallback logic - Add i18n parity checker script (npm run i18n:check) - Wire app root/title shell to translation API - Add TRANSLATION_GUIDE.md for contributors
86 lines
2.3 KiB
JavaScript
86 lines
2.3 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const root = process.cwd()
|
|
const localesDir = path.join(root, 'src', 'i18n', 'locales')
|
|
|
|
const locales = fs.readdirSync(localesDir).filter((entry) => {
|
|
const fullPath = path.join(localesDir, entry)
|
|
return fs.statSync(fullPath).isDirectory()
|
|
})
|
|
|
|
if (!locales.includes('en')) {
|
|
console.error('i18n-check: expected base locale directory "en"')
|
|
process.exit(1)
|
|
}
|
|
|
|
function loadJson(filePath) {
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
}
|
|
|
|
function collectKeyPaths(obj, prefix = '') {
|
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
return prefix ? [prefix] : []
|
|
}
|
|
|
|
const keys = Object.keys(obj)
|
|
if (keys.length === 0) {
|
|
return prefix ? [prefix] : []
|
|
}
|
|
|
|
const paths = []
|
|
for (const key of keys) {
|
|
const nextPrefix = prefix ? `${prefix}.${key}` : key
|
|
const value = obj[key]
|
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
paths.push(...collectKeyPaths(value, nextPrefix))
|
|
} else {
|
|
paths.push(nextPrefix)
|
|
}
|
|
}
|
|
return paths
|
|
}
|
|
|
|
const baseLocaleDir = path.join(localesDir, 'en')
|
|
const namespaceFiles = fs.readdirSync(baseLocaleDir).filter((file) => file.endsWith('.json'))
|
|
|
|
let hasErrors = false
|
|
|
|
for (const namespaceFile of namespaceFiles) {
|
|
const baseData = loadJson(path.join(baseLocaleDir, namespaceFile))
|
|
const baseKeys = new Set(collectKeyPaths(baseData))
|
|
|
|
for (const locale of locales) {
|
|
if (locale === 'en') continue
|
|
|
|
const localeFile = path.join(localesDir, locale, namespaceFile)
|
|
if (!fs.existsSync(localeFile)) {
|
|
console.error(`i18n-check: missing namespace file ${locale}/${namespaceFile}`)
|
|
hasErrors = true
|
|
continue
|
|
}
|
|
|
|
const localeData = loadJson(localeFile)
|
|
const localeKeys = new Set(collectKeyPaths(localeData))
|
|
|
|
for (const key of baseKeys) {
|
|
if (!localeKeys.has(key)) {
|
|
console.error(`i18n-check: missing key ${locale}/${namespaceFile}:${key}`)
|
|
hasErrors = true
|
|
}
|
|
}
|
|
|
|
for (const key of localeKeys) {
|
|
if (!baseKeys.has(key)) {
|
|
console.error(`i18n-check: extra key ${locale}/${namespaceFile}:${key}`)
|
|
hasErrors = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasErrors) {
|
|
process.exit(1)
|
|
}
|
|
|
|
console.log('i18n-check: locale files are structurally consistent') |