mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add script to find missing translations
This commit is contained in:
parent
be191ab81d
commit
2df634c836
3 changed files with 222 additions and 2 deletions
|
|
@ -22,9 +22,10 @@
|
|||
"slug": "translate",
|
||||
"name": "Translate",
|
||||
"roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
|
||||
"customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n- Don't translate the word \"token\" as it means something specific in English that all languages will understand\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n\n# Supported Languages\n- Localize all strings into the following locale files: ar, ca, cs, de, en, es, fr, hi, hu, it, ja, ko, pl, pt, pt-BR, ru, tr, zh-CN, zh-TW",
|
||||
"customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n- Don't translate the word \"token\" as it means something specific in English that all languages will understand\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n- Always use apply_diff instead of write_to_file when editing existing translation files as it's much faster and more reliable\n- When using apply_diff, make sure to carefully identify the exact JSON structure to edit to avoid syntax errors\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n- Always validate your translation work by running the missing translations script:\n ```\n node scripts/find-missing-translations.js\n ```\n- Before completing any translation task, ensure there are no missing translations by running the script with the target locale(s):\n ```\n node scripts/find-missing-translations.js --locale=<locale-code>\n ```\n- Address any missing translations identified by the script to ensure complete coverage across all locales\n\n# Supported Languages\n- Localize all strings into the following locale files: ar, ca, cs, de, en, es, fr, hi, hu, it, ja, ko, pl, pt, pt-BR, ru, tr, zh-CN, zh-TW",
|
||||
"groups": [
|
||||
"read",
|
||||
"command",
|
||||
[
|
||||
"edit",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
"e2e/**",
|
||||
"src/activate/**",
|
||||
"src/exports/**",
|
||||
"src/extension.ts"
|
||||
"src/extension.ts",
|
||||
"scripts/**"
|
||||
],
|
||||
"workspaces": {
|
||||
"webview-ui": {
|
||||
|
|
|
|||
218
scripts/find-missing-translations.js
Normal file
218
scripts/find-missing-translations.js
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/**
|
||||
* Script to find missing translations in locale files
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/find-missing-translations.js [options]
|
||||
*
|
||||
* Options:
|
||||
* --locale=<locale> Only check a specific locale (e.g. --locale=fr)
|
||||
* --file=<file> Only check a specific file (e.g. --file=chat.json)
|
||||
* --help Show this help message
|
||||
*/
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
// Process command line arguments
|
||||
const args = process.argv.slice(2).reduce((acc, arg) => {
|
||||
if (arg === "--help") {
|
||||
acc.help = true
|
||||
} else if (arg.startsWith("--locale=")) {
|
||||
acc.locale = arg.split("=")[1]
|
||||
} else if (arg.startsWith("--file=")) {
|
||||
acc.file = arg.split("=")[1]
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
// Show help if requested
|
||||
if (args.help) {
|
||||
console.log(`
|
||||
Find Missing Translations
|
||||
|
||||
A utility script to identify missing translations across locale files.
|
||||
Compares non-English locale files to the English ones to find any missing keys.
|
||||
|
||||
Usage:
|
||||
node scripts/find-missing-translations.js [options]
|
||||
|
||||
Options:
|
||||
--locale=<locale> Only check a specific locale (e.g. --locale=fr)
|
||||
--file=<file> Only check a specific file (e.g. --file=chat.json)
|
||||
--help Show this help message
|
||||
|
||||
Output:
|
||||
- Generates a report of missing translations
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Path to the locales directory
|
||||
const LOCALES_DIR = path.join(__dirname, "../webview-ui/src/i18n/locales")
|
||||
|
||||
// Recursively find all keys in an object
|
||||
function findKeys(obj, parentKey = "") {
|
||||
let keys = []
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const currentKey = parentKey ? `${parentKey}.${key}` : key
|
||||
|
||||
if (typeof value === "object" && value !== null) {
|
||||
// If value is an object, recurse
|
||||
keys = [...keys, ...findKeys(value, currentKey)]
|
||||
} else {
|
||||
// If value is a primitive, add the key
|
||||
keys.push(currentKey)
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// Get value at a dotted path in an object
|
||||
function getValueAtPath(obj, path) {
|
||||
const parts = path.split(".")
|
||||
let current = obj
|
||||
|
||||
for (const part of parts) {
|
||||
if (current === undefined || current === null) {
|
||||
return undefined
|
||||
}
|
||||
current = current[part]
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
// Main function to find missing translations
|
||||
function findMissingTranslations() {
|
||||
try {
|
||||
// Get all locale directories (or filter to the specified locale)
|
||||
const allLocales = fs.readdirSync(LOCALES_DIR).filter((item) => {
|
||||
const stats = fs.statSync(path.join(LOCALES_DIR, item))
|
||||
return stats.isDirectory() && item !== "en" // Exclude English as it's our source
|
||||
})
|
||||
|
||||
// Filter to the specified locale if provided
|
||||
const locales = args.locale ? allLocales.filter((locale) => locale === args.locale) : allLocales
|
||||
|
||||
if (args.locale && locales.length === 0) {
|
||||
console.error(`Error: Locale '${args.locale}' not found in ${LOCALES_DIR}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(`Checking ${locales.length} non-English locale(s): ${locales.join(", ")}`)
|
||||
|
||||
// Get all English JSON files
|
||||
const englishDir = path.join(LOCALES_DIR, "en")
|
||||
let englishFiles = fs.readdirSync(englishDir).filter((file) => file.endsWith(".json") && !file.startsWith("."))
|
||||
|
||||
// Filter to the specified file if provided
|
||||
if (args.file) {
|
||||
if (!englishFiles.includes(args.file)) {
|
||||
console.error(`Error: File '${args.file}' not found in ${englishDir}`)
|
||||
process.exit(1)
|
||||
}
|
||||
englishFiles = englishFiles.filter((file) => file === args.file)
|
||||
}
|
||||
|
||||
// Load file contents
|
||||
const englishFileContents = englishFiles.map((file) => ({
|
||||
name: file,
|
||||
content: JSON.parse(fs.readFileSync(path.join(englishDir, file), "utf8")),
|
||||
}))
|
||||
|
||||
console.log(
|
||||
`Checking ${englishFileContents.length} translation file(s): ${englishFileContents.map((f) => f.name).join(", ")}`,
|
||||
)
|
||||
|
||||
// Results object to store missing translations
|
||||
const missingTranslations = {}
|
||||
|
||||
// For each locale, check for missing translations
|
||||
for (const locale of locales) {
|
||||
missingTranslations[locale] = {}
|
||||
|
||||
for (const { name, content: englishContent } of englishFileContents) {
|
||||
const localeFilePath = path.join(LOCALES_DIR, locale, name)
|
||||
|
||||
// Check if the file exists in the locale
|
||||
if (!fs.existsSync(localeFilePath)) {
|
||||
missingTranslations[locale][name] = { file: "File is missing entirely" }
|
||||
continue
|
||||
}
|
||||
|
||||
// Load the locale file
|
||||
const localeContent = JSON.parse(fs.readFileSync(localeFilePath, "utf8"))
|
||||
|
||||
// Find all keys in the English file
|
||||
const englishKeys = findKeys(englishContent)
|
||||
|
||||
// Check for missing keys in the locale file
|
||||
const missingKeys = []
|
||||
|
||||
for (const key of englishKeys) {
|
||||
const englishValue = getValueAtPath(englishContent, key)
|
||||
const localeValue = getValueAtPath(localeContent, key)
|
||||
|
||||
if (localeValue === undefined) {
|
||||
missingKeys.push({
|
||||
key,
|
||||
englishValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
missingTranslations[locale][name] = missingKeys
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output results
|
||||
let hasMissingTranslations = false
|
||||
|
||||
console.log("\nMissing Translations Report:\n")
|
||||
|
||||
for (const [locale, files] of Object.entries(missingTranslations)) {
|
||||
if (Object.keys(files).length === 0) {
|
||||
console.log(`✅ ${locale}: No missing translations`)
|
||||
continue
|
||||
}
|
||||
|
||||
hasMissingTranslations = true
|
||||
console.log(`📝 ${locale}:`)
|
||||
|
||||
for (const [fileName, missingItems] of Object.entries(files)) {
|
||||
if (missingItems.file) {
|
||||
console.log(` - ${fileName}: ${missingItems.file}`)
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(` - ${fileName}: ${missingItems.length} missing translations`)
|
||||
|
||||
for (const { key, englishValue } of missingItems) {
|
||||
console.log(` ${key}: "${englishValue}"`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log("")
|
||||
}
|
||||
|
||||
if (!hasMissingTranslations) {
|
||||
console.log("\n✅ All translations are complete!")
|
||||
} else {
|
||||
console.log("✏️ To add missing translations:")
|
||||
console.log("1. Add the missing keys to the corresponding locale files")
|
||||
console.log("2. Translate the English values to the appropriate language")
|
||||
console.log("3. Run this script again to verify all translations are complete")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error.message)
|
||||
console.error(error.stack)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
findMissingTranslations()
|
||||
Loading…
Add table
Reference in a new issue