diff --git a/scripts/scan-locales.mjs b/scripts/scan-locales.mjs index 6c1df9f9d..ad05da669 100644 --- a/scripts/scan-locales.mjs +++ b/scripts/scan-locales.mjs @@ -1,108 +1,52 @@ import fs from 'fs' -import path from 'path' + +import glob from 'glob' import { BASE_LOCALE, getLocaleData, getLocalePaths } from './locale-utils.mjs' const baseLocale = getLocalePaths(BASE_LOCALE) -;(() => { - const { keys, namespaces } = getLocaleData(baseLocale) - - function createRegex(text, { caseInsensitive = true } = {}) { - const escapedPattern = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - const flags = caseInsensitive ? 'gi' : 'g' - return new RegExp(`\\b${escapedPattern}`, flags) - } - - function filterByExt(text, exts = []) { - const regex = new RegExp(`(${exts.join('|')})$`) - return regex.test(text) - } - - const dir = './src' - const search = `t('` - const regex = createRegex(search) +const { keys: translationKeys, namespaces } = getLocaleData(baseLocale) - const results = { - results: [], - files: [], - } - - function extractKey(str) { - const keyRegex = /t\(\s*['"](.+?)['"]\s*,?/ - const keyMatch = str.match(keyRegex) - return keyMatch ? keyMatch[1] : null - } +// Path to your source code +const sourceCodePath = './src/**/*.{js,jsx,ts,tsx}' // adjust for relevant file types - function extractMatch(filePath) { - let match = true - const matches = [] - let content = fs.readFileSync(filePath, 'utf-8') +// Search for translation keys in the source code +function searchForTranslationKeysInCode(pattern) { + const regex = /t[cs]?\(['"`]([a-zA-Z0-9_.]+)['"`]\s*,?\s*{?/g + // = /t\(['"`]([a-zA-Z0-9_.]+)['"`]\s*,?\s*{?/g + // /t\(['"`]([a-zA-Z0-9_.]+)['"`]\)/g // regex to match t('key') + const files = glob.sync(pattern) + const foundKeys = new Set() - while ((match = regex.exec(content))) { - // /\b(?:t)\s*\(\s*(['\s\S']*?)\s*\)/g - const line = /\b(?:t)\s*\(['"][^'"]+['"][^)]*\)/g.exec(content)?.at(0) - content = content.replace(match?.[0], '').replace(line, '') - matches.push(extractKey(line)) + files.forEach((file) => { + const content = fs.readFileSync(file, 'utf-8') + let match + while ((match = regex.exec(content)) !== null) { + foundKeys.add(match[1]) // Add the matched key } + }) - return matches - } - - function handleResults(filePath) { - const matches = extractMatch(filePath) - - if (!matches.length) return - - // console.log(`Found ${matches.length} ${search} in ${filePath}:`) - matches.forEach((m) => console.log(m)) - // console.log('\n') - results.results = [...results.results, ...matches] - results.files = [...results.files, filePath] - } - - // Function to recursively scan files in a directory - function scanFiles({ dir, fn, ext = [] }) { - const files = fs.readdirSync(dir) - - files.forEach((file) => { - const filePath = path.join(dir, file) - const stat = fs.statSync(filePath) - - if (stat.isDirectory()) { - scanFiles({ dir: filePath, fn, ext }) // Recursively scan subdirectories - } else if (stat.isFile() && filterByExt(file, ext)) { - fn(filePath) - } - }) - } + return foundKeys +} - scanFiles({ - dir, - fn: handleResults, - ext: ['.ts', '.tsx'], - }) +// Find unused translation keys +function findUnusedKeys() { + const usedKeys = searchForTranslationKeysInCode(sourceCodePath) const unusedKeys = [] - const foundKeys = [ - ...new Set( - results.results.map((key) => key.replace(new RegExp(`^(${namespaces.join('|')}).`), '')), - ), - ] - const modifiedKeys = [ - ...new Set(keys.map((key) => key.replace(new RegExp(`^(${namespaces.join('|')}).`), ''))), - ] - for (const key of modifiedKeys) { - const foundKey = foundKeys.find((k) => key === k) + const regex = new RegExp(`^(${namespaces.join('|')}).`) - if (!foundKey) { + for (const key of translationKeys.map((key) => key.replace(regex, ''))) { + if (!usedKeys.has(key)) { unusedKeys.push(key) } } - - console.log('PROBABLY UNSED KEYS\n') - for (const key of unusedKeys) { + console.log(`PROBABLY ${unusedKeys.length} UNSED KEYS:`) + unusedKeys.forEach((key) => { console.log(key) - } -})() + }) +} + +findUnusedKeys()