| 
                        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 | 
                        - const fs = require('node:fs')
 - const path = require('node:path')
 - const vm = require('node:vm')
 - const transpile = require('typescript').transpile
 - 
 - const targetLanguage = 'en-US'
 - const data = require('./languages.json')
 - const languages = data.languages.filter(language => language.supported).map(language => language.value)
 - 
 - async function getKeysFromLanguage(language) {
 -   return new Promise((resolve, reject) => {
 -     const folderPath = path.resolve(__dirname, '../i18n', language)
 -     const allKeys = []
 -     fs.readdir(folderPath, (err, files) => {
 -       if (err) {
 -         console.error('Error reading folder:', err)
 -         reject(err)
 -         return
 -       }
 - 
 -       // Filter only .ts and .js files
 -       const translationFiles = files.filter(file => /\.(ts|js)$/.test(file))
 - 
 -       translationFiles.forEach((file) => {
 -         const filePath = path.join(folderPath, file)
 -         const fileName = file.replace(/\.[^/.]+$/, '') // Remove file extension
 -         const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) =>
 -           c.toUpperCase(),
 -         ) // Convert to camel case
 - 
 -         try {
 -           const content = fs.readFileSync(filePath, 'utf8')
 - 
 -           // Create a safer module environment for vm
 -           const moduleExports = {}
 -           const context = {
 -             exports: moduleExports,
 -             module: { exports: moduleExports },
 -             require,
 -             console,
 -             __filename: filePath,
 -             __dirname: folderPath,
 -           }
 - 
 -           // Use vm.runInNewContext instead of eval for better security
 -           vm.runInNewContext(transpile(content), context)
 - 
 -           // Extract the translation object
 -           const translationObj = moduleExports.default || moduleExports
 - 
 -           if(!translationObj || typeof translationObj !== 'object') {
 -             console.error(`Error parsing file: ${filePath}`)
 -             reject(new Error(`Error parsing file: ${filePath}`))
 -             return
 -           }
 - 
 -           const nestedKeys = []
 -           const iterateKeys = (obj, prefix = '') => {
 -             for (const key in obj) {
 -               const nestedKey = prefix ? `${prefix}.${key}` : key
 -               if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
 -                 // This is an object (but not array), recurse into it but don't add it as a key
 -                 iterateKeys(obj[key], nestedKey)
 -               }
 -  else {
 -                 // This is a leaf node (string, number, boolean, array, etc.), add it as a key
 -                 nestedKeys.push(nestedKey)
 -               }
 -             }
 -           }
 -           iterateKeys(translationObj)
 - 
 -           // Fixed: accumulate keys instead of overwriting
 -           const fileKeys = nestedKeys.map(key => `${camelCaseFileName}.${key}`)
 -           allKeys.push(...fileKeys)
 -         }
 -  catch (error) {
 -           console.error(`Error processing file ${filePath}:`, error.message)
 -           reject(error)
 -         }
 -       })
 -       resolve(allKeys)
 -     })
 -   })
 - }
 - 
 - function removeKeysFromObject(obj, keysToRemove, prefix = '') {
 -   let modified = false
 -   for (const key in obj) {
 -     const fullKey = prefix ? `${prefix}.${key}` : key
 - 
 -     if (keysToRemove.includes(fullKey)) {
 -       delete obj[key]
 -       modified = true
 -       console.log(`🗑️  Removed key: ${fullKey}`)
 -     }
 -  else if (typeof obj[key] === 'object' && obj[key] !== null) {
 -       const subModified = removeKeysFromObject(obj[key], keysToRemove, fullKey)
 -       modified = modified || subModified
 -     }
 -   }
 -   return modified
 - }
 - 
 - async function removeExtraKeysFromFile(language, fileName, extraKeys) {
 -   const filePath = path.resolve(__dirname, '../i18n', language, `${fileName}.ts`)
 - 
 -   if (!fs.existsSync(filePath)) {
 -     console.log(`⚠️  File not found: ${filePath}`)
 -     return false
 -   }
 - 
 -   try {
 -     // Filter keys that belong to this file
 -     const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) => c.toUpperCase())
 -     const fileSpecificKeys = extraKeys
 -       .filter(key => key.startsWith(`${camelCaseFileName}.`))
 -       .map(key => key.substring(camelCaseFileName.length + 1)) // Remove file prefix
 - 
 -     if (fileSpecificKeys.length === 0)
 -       return false
 - 
 -     console.log(`🔄 Processing file: ${filePath}`)
 - 
 -     // Read the original file content
 -     const content = fs.readFileSync(filePath, 'utf8')
 -     const lines = content.split('\n')
 - 
 -     let modified = false
 -     const linesToRemove = []
 - 
 -     // Find lines to remove for each key (including multiline values)
 -     for (const keyToRemove of fileSpecificKeys) {
 -       const keyParts = keyToRemove.split('.')
 -       let targetLineIndex = -1
 -       const linesToRemoveForKey = []
 - 
 -       // Build regex pattern for the exact key path
 -       if (keyParts.length === 1) {
 -         // Simple key at root level like "pickDate: 'value'"
 -         for (let i = 0; i < lines.length; i++) {
 -           const line = lines[i]
 -           const simpleKeyPattern = new RegExp(`^\\s*${keyParts[0]}\\s*:`)
 -           if (simpleKeyPattern.test(line)) {
 -             targetLineIndex = i
 -             break
 -           }
 -         }
 -       }
 -  else {
 -         // Nested key - need to find the exact path
 -         const currentPath = []
 -         let braceDepth = 0
 - 
 -         for (let i = 0; i < lines.length; i++) {
 -           const line = lines[i]
 -           const trimmedLine = line.trim()
 - 
 -           // Track current object path
 -           const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*{/)
 -           if (keyMatch) {
 -             currentPath.push(keyMatch[1])
 -             braceDepth++
 -           }
 -  else if (trimmedLine === '},' || trimmedLine === '}') {
 -             if (braceDepth > 0) {
 -               braceDepth--
 -               currentPath.pop()
 -             }
 -           }
 - 
 -           // Check if this line matches our target key
 -           const leafKeyMatch = trimmedLine.match(/^(\w+)\s*:/)
 -           if (leafKeyMatch) {
 -             const fullPath = [...currentPath, leafKeyMatch[1]]
 -             const fullPathString = fullPath.join('.')
 - 
 -             if (fullPathString === keyToRemove) {
 -               targetLineIndex = i
 -               break
 -             }
 -           }
 -         }
 -       }
 - 
 -       if (targetLineIndex !== -1) {
 -         linesToRemoveForKey.push(targetLineIndex)
 - 
 -         // Check if this is a multiline key-value pair
 -         const keyLine = lines[targetLineIndex]
 -         const trimmedKeyLine = keyLine.trim()
 - 
 -         // If key line ends with ":" (not ":", "{ " or complete value), it's likely multiline
 -         if (trimmedKeyLine.endsWith(':') && !trimmedKeyLine.includes('{') && !trimmedKeyLine.match(/:\s*['"`]/)) {
 -           // Find the value lines that belong to this key
 -           let currentLine = targetLineIndex + 1
 -           let foundValue = false
 - 
 -           while (currentLine < lines.length) {
 -             const line = lines[currentLine]
 -             const trimmed = line.trim()
 - 
 -             // Skip empty lines
 -             if (trimmed === '') {
 -               currentLine++
 -               continue
 -             }
 - 
 -             // Check if this line starts a new key (indicates end of current value)
 -             if (trimmed.match(/^\w+\s*:/))
 -               break
 - 
 -             // Check if this line is part of the value
 -             if (trimmed.startsWith('\'') || trimmed.startsWith('"') || trimmed.startsWith('`') || foundValue) {
 -               linesToRemoveForKey.push(currentLine)
 -               foundValue = true
 - 
 -               // Check if this line ends the value (ends with quote and comma/no comma)
 -               if ((trimmed.endsWith('\',') || trimmed.endsWith('",') || trimmed.endsWith('`,')
 -                    || trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`'))
 -                   && !trimmed.startsWith('//'))
 -                 break
 -             }
 -  else {
 -               break
 -             }
 - 
 -             currentLine++
 -           }
 -         }
 - 
 -         linesToRemove.push(...linesToRemoveForKey)
 -         console.log(`🗑️  Found key to remove: ${keyToRemove} at line ${targetLineIndex + 1}${linesToRemoveForKey.length > 1 ? ` (multiline, ${linesToRemoveForKey.length} lines)` : ''}`)
 -         modified = true
 -       }
 -  else {
 -         console.log(`⚠️  Could not find key: ${keyToRemove}`)
 -       }
 -     }
 - 
 -     if (modified) {
 -       // Remove duplicates and sort in reverse order to maintain correct indices
 -       const uniqueLinesToRemove = [...new Set(linesToRemove)].sort((a, b) => b - a)
 - 
 -       for (const lineIndex of uniqueLinesToRemove) {
 -         const line = lines[lineIndex]
 -         console.log(`🗑️  Removing line ${lineIndex + 1}: ${line.trim()}`)
 -         lines.splice(lineIndex, 1)
 - 
 -         // Also remove trailing comma from previous line if it exists and the next line is a closing brace
 -         if (lineIndex > 0 && lineIndex < lines.length) {
 -           const prevLine = lines[lineIndex - 1]
 -           const nextLine = lines[lineIndex] ? lines[lineIndex].trim() : ''
 - 
 -           if (prevLine.trim().endsWith(',') && (nextLine.startsWith('}') || nextLine === ''))
 -             lines[lineIndex - 1] = prevLine.replace(/,\s*$/, '')
 -         }
 -       }
 - 
 -       // Write back to file
 -       const newContent = lines.join('\n')
 -       fs.writeFileSync(filePath, newContent)
 -       console.log(`💾 Updated file: ${filePath}`)
 -       return true
 -     }
 - 
 -     return false
 -   }
 -  catch (error) {
 -     console.error(`Error processing file ${filePath}:`, error.message)
 -     return false
 -   }
 - }
 - 
 - // Add command line argument support
 - const targetFile = process.argv.find(arg => arg.startsWith('--file='))?.split('=')[1]
 - const targetLang = process.argv.find(arg => arg.startsWith('--lang='))?.split('=')[1]
 - const autoRemove = process.argv.includes('--auto-remove')
 - 
 - async function main() {
 -   const compareKeysCount = async () => {
 -     const allTargetKeys = await getKeysFromLanguage(targetLanguage)
 - 
 -     // Filter target keys by file if specified
 -     const targetKeys = targetFile
 -       ? allTargetKeys.filter(key => key.startsWith(`${targetFile.replace(/[-_](.)/g, (_, c) => c.toUpperCase())}.`))
 -       : allTargetKeys
 - 
 -     // Filter languages by target language if specified
 -     const languagesToProcess = targetLang ? [targetLang] : languages
 - 
 -     const allLanguagesKeys = await Promise.all(languagesToProcess.map(language => getKeysFromLanguage(language)))
 - 
 -     // Filter language keys by file if specified
 -     const languagesKeys = targetFile
 -       ? allLanguagesKeys.map(keys => keys.filter(key => key.startsWith(`${targetFile.replace(/[-_](.)/g, (_, c) => c.toUpperCase())}.`)))
 -       : allLanguagesKeys
 - 
 -     const keysCount = languagesKeys.map(keys => keys.length)
 -     const targetKeysCount = targetKeys.length
 - 
 -     const comparison = languagesToProcess.reduce((result, language, index) => {
 -       const languageKeysCount = keysCount[index]
 -       const difference = targetKeysCount - languageKeysCount
 -       result[language] = difference
 -       return result
 -     }, {})
 - 
 -     console.log(comparison)
 - 
 -     // Print missing keys and extra keys
 -     for (let index = 0; index < languagesToProcess.length; index++) {
 -       const language = languagesToProcess[index]
 -       const languageKeys = languagesKeys[index]
 -       const missingKeys = targetKeys.filter(key => !languageKeys.includes(key))
 -       const extraKeys = languageKeys.filter(key => !targetKeys.includes(key))
 - 
 -       console.log(`Missing keys in ${language}:`, missingKeys)
 - 
 -       // Show extra keys only when there are extra keys (negative difference)
 -       if (extraKeys.length > 0) {
 -         console.log(`Extra keys in ${language} (not in ${targetLanguage}):`, extraKeys)
 - 
 -         // Auto-remove extra keys if flag is set
 -         if (autoRemove) {
 -           console.log(`\n🤖 Auto-removing extra keys from ${language}...`)
 - 
 -           // Get all translation files
 -           const i18nFolder = path.resolve(__dirname, '../i18n', language)
 -           const files = fs.readdirSync(i18nFolder)
 -             .filter(file => /\.ts$/.test(file))
 -             .map(file => file.replace(/\.ts$/, ''))
 -             .filter(f => !targetFile || f === targetFile) // Filter by target file if specified
 - 
 -           let totalRemoved = 0
 -           for (const fileName of files) {
 -             const removed = await removeExtraKeysFromFile(language, fileName, extraKeys)
 -             if (removed) totalRemoved++
 -           }
 - 
 -           console.log(`✅ Auto-removal completed for ${language}. Modified ${totalRemoved} files.`)
 -         }
 -       }
 -     }
 -   }
 - 
 -   console.log('🚀 Starting check-i18n script...')
 -   if (targetFile)
 -     console.log(`📁 Checking file: ${targetFile}`)
 - 
 -   if (targetLang)
 -     console.log(`🌍 Checking language: ${targetLang}`)
 - 
 -   if (autoRemove)
 -     console.log('🤖 Auto-remove mode: ENABLED')
 - 
 -   compareKeysCount()
 - }
 - 
 - main()
 
 
  |