-
-
Notifications
You must be signed in to change notification settings - Fork 424
Expand file tree
/
Copy pathfind-invalid-translations.ts
More file actions
84 lines (68 loc) · 2.64 KB
/
find-invalid-translations.ts
File metadata and controls
84 lines (68 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* eslint-disable no-console */
import { join } from 'node:path'
import { createI18NReport, type I18NItem } from 'vue-i18n-extract'
import { colors } from './utils/colors.ts'
const LOCALES_DIRECTORY = join(import.meta.dirname, '../i18n/locales')
const REFERENCE_FILE_NAME = 'en.json'
const VUE_FILES_GLOB = './app/**/*.?(vue|ts|js)'
function printSection(
title: string,
items: I18NItem[],
status: 'error' | 'warning' | 'success',
): void {
const icon = status === 'error' ? '❌' : status === 'warning' ? '⚠️' : '✅'
const colorFn =
status === 'error' ? colors.red : status === 'warning' ? colors.yellow : colors.green
console.log(`\n${icon} ${colors.bold(title)}: ${colorFn(String(items.length))}`)
if (items.length === 0) return
const groupedByFile = items.reduce<Record<string, string[]>>((acc, item) => {
const file = item.file ?? 'unknown'
acc[file] ??= []
acc[file].push(item.path)
return acc
}, {})
for (const [file, keys] of Object.entries(groupedByFile)) {
console.log(` ${colors.dim(file)}`)
for (const key of keys) {
console.log(` ${colors.cyan(key)}`)
}
}
}
async function run(): Promise<void> {
console.log(colors.bold('\n🔍 Analyzing i18n translations...\n'))
const { missingKeys, unusedKeys, maybeDynamicKeys } = await createI18NReport({
vueFiles: VUE_FILES_GLOB,
languageFiles: join(LOCALES_DIRECTORY, REFERENCE_FILE_NAME),
exclude: ['$schema'],
})
const hasMissingKeys = missingKeys.length > 0
const hasUnusedKeys = unusedKeys.length > 0
const hasDynamicKeys = maybeDynamicKeys.length > 0
printSection('Missing keys', missingKeys, hasMissingKeys ? 'error' : 'success')
printSection('Unused keys', unusedKeys, hasUnusedKeys ? 'error' : 'success')
printSection(
'Dynamic keys (cannot be statically analyzed)',
maybeDynamicKeys,
hasDynamicKeys ? 'error' : 'success',
)
// Summary
console.log('\n' + colors.dim('─'.repeat(50)))
const shouldFail = hasMissingKeys || hasDynamicKeys || hasUnusedKeys
if (shouldFail) {
console.log(colors.red('\n❌ Build failed: missing, unused or dynamic keys detected'))
console.log(colors.dim(' Fix missing keys by adding them to the locale file'))
console.log(colors.dim(' Fix dynamic keys by using static translation keys\n'))
console.log(
colors.dim(
' Fix unused keys by removing them from the locale file (pnpm run i18n:report:fix)\n',
),
)
process.exit(1)
} else {
console.log(colors.green('\n✅ All translations are valid!\n'))
}
}
run().catch((error: unknown) => {
console.error(colors.red('\n❌ Unexpected error:'), error)
process.exit(1)
})