Change default language to en-US and add US language (#6621)

This commit is contained in:
Anthony Stirling
2026-06-11 20:36:23 +01:00
committed by GitHub
parent 88adb7adad
commit 946c032fb5
41 changed files with 8578 additions and 188 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ A script to update language progress status in README.md based on
frontend locale TOML file comparisons.
This script compares the default (reference) TOML file,
`frontend/editor/public/locales/en-GB/translation.toml`, with other translation
`frontend/editor/public/locales/en-US/translation.toml`, with other translation
files in `frontend/editor/public/locales/*/translation.toml`.
It determines how many keys are fully translated and automatically updates
progress badges in the `README.md`.
@@ -346,7 +346,7 @@ def main() -> None:
# Project layout assumptions
cwd = os.getcwd()
locales_dir = os.path.join(cwd, "frontend", "editor", "public", "locales")
reference_file = os.path.join(locales_dir, "en-GB", "translation.toml")
reference_file = os.path.join(locales_dir, "en-US", "translation.toml")
scripts_directory = os.path.join(cwd, "scripts")
translation_state_file = os.path.join(scripts_directory, "ignore_translation.toml")
+21 -21
View File
@@ -1,6 +1,6 @@
# Translation Management Scripts
This directory contains Python scripts for managing frontend translations in Stirling PDF. These tools help analyze, merge, validate, and manage translations against the en-GB golden truth file.
This directory contains Python scripts for managing frontend translations in Stirling PDF. These tools help analyze, merge, validate, and manage translations against the en-US golden truth file.
## Current Format: TOML
@@ -33,7 +33,7 @@ python3 scripts/translations/auto_translate.py es-ES --no-cleanup
4. Validates placeholders are preserved
5. Merges translated batches
6. Applies translations to language file
7. Beautifies structure to match en-GB
7. Beautifies structure to match en-US
8. Cleans up temporary files
9. Reports final completion percentage
@@ -86,7 +86,7 @@ python scripts/translations/json_validator.py --all-batches ar_AR --quiet
- Trailing commas before closing braces
#### `validate_placeholders.py`
Validates that translation files have correct placeholders matching en-GB (source of truth).
Validates that translation files have correct placeholders matching en-US (source of truth).
**Usage:**
```bash
@@ -105,12 +105,12 @@ python scripts/translations/validate_placeholders.py --json
**Features:**
- Detects missing placeholders (e.g., {n}, {total}, {filename})
- Detects extra placeholders not in en-GB
- Detects extra placeholders not in en-US
- Shows exact keys and text where issues occur
- Exit code 1 if issues found (good for CI/CD)
#### `validate_json_structure.py`
Validates JSON structure and key consistency with en-GB.
Validates JSON structure and key consistency with en-US.
**Usage:**
```bash
@@ -130,7 +130,7 @@ python scripts/translations/validate_json_structure.py --json
**Features:**
- Validates JSON syntax
- Detects missing keys (not translated yet)
- Detects extra keys (not in en-GB, should be removed)
- Detects extra keys (not in en-US, should be removed)
- Reports key counts and structure differences
- Exit code 1 if issues found (good for CI/CD)
@@ -160,21 +160,21 @@ python scripts/translations/translation_analyzer.py --format json
**Features:**
- Finds missing translation keys
- Identifies untranslated entries (identical to en-GB and [UNTRANSLATED] markers)
- Identifies untranslated entries (identical to en-US and [UNTRANSLATED] markers)
- Shows accurate completion percentages using ignore patterns
- Identifies extra keys not in en-GB
- Identifies extra keys not in en-US
- Supports JSON and text output formats
- Uses `scripts/ignore_translation.toml` for language-specific exclusions
### 2. `translation_merger.py`
Merges missing translations from en-GB into target language files and manages translation workflows.
Merges missing translations from en-US into target language files and manages translation workflows.
**Usage:**
```bash
# Operate on all locales (except en-GB) when language is omitted
# Operate on all locales (except en-US) when language is omitted
python scripts/translations/translation_merger.py add-missing
# Add missing translations from en-GB to French
# Add missing translations from en-US to French
python scripts/translations/translation_merger.py fr-FR add-missing
# Create backups before modifying files
@@ -192,19 +192,19 @@ python scripts/translations/translation_merger.py fr-FR apply-translations --tra
# Override default paths if needed
python scripts/translations/translation_merger.py fr-FR add-missing --locales-dir ./frontend/editor/public/locales --ignore-file ./scripts/ignore_translation.toml
# Remove unused translations not present in en-GB
# Remove unused translations not present in en-US
python scripts/translations/translation_merger.py fr-FR remove-unused
```
**Features:**
- Adds missing keys from en-GB (copies English text directly)
- Adds missing keys from en-US (copies English text directly)
- Runs across all locales for add-missing/remove-unused when language is omitted
- Extracts untranslated entries for external translation
- Creates structured templates for AI translation
- Applies translated content back to language files (template format or plain JSON)
- Supports `--backup` on mutating commands
- Automatic backup creation
- Removes unused translations not present in en-GB
- Removes unused translations not present in en-US
### 3. `ai_translation_helper.py`
Specialized tool for AI-assisted translation workflows with batch processing and validation.
@@ -290,7 +290,7 @@ python3 scripts/translations/auto_translate.py es-ES --skip-verification
4. **Validate**: Ensures placeholders are preserved
5. **Merge**: Combines all translated batches
6. **Apply**: Updates the language file
7. **Beautify**: Restructures to match en-GB format
7. **Beautify**: Restructures to match en-US format
8. **Cleanup**: Removes temporary files
9. **Verify**: Reports final completion percentage
@@ -337,11 +337,11 @@ python3 scripts/translations/batch_translator.py batch.json --language it-IT --s
- `gpt-5-nano`: Fastest, most economical
### 7. `json_beautifier.py`
Restructures and beautifies translation JSON files to match en-GB structure exactly.
Restructures and beautifies translation JSON files to match en-US structure exactly.
**Usage:**
```bash
# Restructure single language to match en-GB structure
# Restructure single language to match en-US structure
python scripts/translations/json_beautifier.py --language de-DE
# Restructure all languages
@@ -355,7 +355,7 @@ python scripts/translations/json_beautifier.py --language de-DE --no-backup
```
**Features:**
- Restructures JSON to match en-GB nested structure exactly
- Restructures JSON to match en-US nested structure exactly
- Preserves key ordering for line-by-line comparison
- Creates automatic backups before modification
- Validates structure and key ordering
@@ -585,7 +585,7 @@ python scripts/translations/json_validator.py --all-batches ar_AR
#### JSON Structure Mismatches
**Problem**: Flattened dot-notation keys instead of proper nested objects
**Solution**: Use `json_beautifier.py` to restructure files to match en-GB exactly
**Solution**: Use `json_beautifier.py` to restructure files to match en-US exactly
## Real-World Examples
@@ -629,7 +629,7 @@ EOF
python scripts/translations/translation_merger.py ar-AR apply-translations --translations-file ar_AR_merged.json
# Result: Applied 1088 translations
# Beautify to match en-GB structure
# Beautify to match en-US structure
python scripts/translations/json_beautifier.py --language ar-AR
# Check final progress
@@ -711,4 +711,4 @@ These scripts integrate with the existing translation system:
3. **Updating Existing Language**: Use analyzer to find gaps, then compact or batch method
4. **Quality Assurance**: Use analyzer with `--summary` for completion metrics and issue detection
5. **External Translation Services**: Use export functionality to generate CSV files for translators
6. **Structure Maintenance**: Use json_beautifier to keep files aligned with en-GB structure
6. **Structure Maintenance**: Use json_beautifier to keep files aligned with en-US structure
@@ -20,7 +20,7 @@ import tomli_w
class AITranslationHelper:
def __init__(self, locales_dir: str = "frontend/editor/public/locales"):
self.locales_dir = Path(locales_dir)
self.golden_truth_file = self.locales_dir / "en-GB" / "translation.toml"
self.golden_truth_file = self.locales_dir / "en-US" / "translation.toml"
def _load_translation_file(self, file_path: Path) -> Dict:
"""Load TOML translation file."""
@@ -47,7 +47,7 @@ class AITranslationHelper:
batch_data = {
"metadata": {
"created_at": datetime.now().isoformat(),
"source_language": "en-GB",
"source_language": "en-US",
"target_languages": languages,
"max_entries_per_language": max_entries_per_language,
"instructions": {
@@ -320,7 +320,7 @@ class AITranslationHelper:
)
with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
fieldnames = ["key", "context", "en_GB"] + languages
fieldnames = ["key", "context", "en_US"] + languages
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
@@ -331,7 +331,7 @@ class AITranslationHelper:
row = {
"key": key,
"context": self._get_key_context(key),
"en_GB": en_value,
"en_US": en_value,
}
for lang in languages:
@@ -363,7 +363,7 @@ class AITranslationHelper:
continue
export_data["translations"][key] = {
"en_GB": en_value,
"en_US": en_value,
"context": self._get_key_context(key),
}
+3 -3
View File
@@ -55,14 +55,14 @@ def extract_untranslated(language_code, batch_size=500, include_existing=False):
print(f"\n🔍 Extracting {mode} entries for {language_code}...")
# Load files
golden_path = find_translation_file(Path("frontend/editor/public/locales/en-GB"))
golden_path = find_translation_file(Path("frontend/editor/public/locales/en-US"))
lang_path = find_translation_file(
Path(f"frontend/editor/public/locales/{language_code}")
)
if not golden_path:
print(
"Error: Golden truth file not found in frontend/editor/public/locales/en-GB"
"Error: Golden truth file not found in frontend/editor/public/locales/en-US"
)
return None
@@ -222,7 +222,7 @@ def apply_translations(merged_file, language_code):
def beautify_translations(language_code):
"""Beautify translation file to match en-GB structure."""
"""Beautify translation file to match en-US structure."""
print(f"\n✨ Beautifying {language_code} translation file...")
cmd = f"python3 scripts/translations/toml_beautifier.py --language {language_code}"
+11 -11
View File
@@ -37,7 +37,7 @@ def get_all_languages(locales_dir: Path) -> List[str]:
return []
for lang_dir in sorted(locales_dir.iterdir()):
if lang_dir.is_dir() and lang_dir.name != "en-GB":
if lang_dir.is_dir() and lang_dir.name != "en-US":
toml_file = lang_dir / "translation.toml"
if toml_file.exists():
languages.append(lang_dir.name)
@@ -57,10 +57,10 @@ def get_language_completion(locales_dir: Path, language: str) -> Optional[float]
with open(toml_file, "rb") as f:
target_data = tomllib.load(f)
# Load en-GB reference
en_gb_file = locales_dir / "en-GB" / "translation.toml"
with open(en_gb_file, "rb") as f:
en_gb_data = tomllib.load(f)
# Load en-US reference
en_us_file = locales_dir / "en-US" / "translation.toml"
with open(en_us_file, "rb") as f:
en_us_data = tomllib.load(f)
# Flatten and count
def flatten(d, parent=""):
@@ -73,16 +73,16 @@ def get_language_completion(locales_dir: Path, language: str) -> Optional[float]
items[key] = v
return items
en_gb_flat = flatten(en_gb_data)
en_us_flat = flatten(en_us_data)
target_flat = flatten(target_data)
# Count translated (not equal to en-GB)
# Count translated (not equal to en-US)
translated = sum(
1
for k in en_gb_flat
if k in target_flat and target_flat[k] != en_gb_flat[k]
for k in en_us_flat
if k in target_flat and target_flat[k] != en_us_flat[k]
)
total = len(en_gb_flat)
total = len(en_us_flat)
return (translated / total * 100) if total > 0 else 0.0
@@ -246,7 +246,7 @@ Note: Requires OPENAI_API_KEY environment variable or --api-key argument.
print(f"Translating specified languages: {', '.join(languages)}")
else:
languages = get_all_languages(locales_dir)
print(f"Found {len(languages)} languages (excluding en-GB)")
print(f"Found {len(languages)} languages (excluding en-US)")
if not languages:
print("No languages to translate!")
+3 -3
View File
@@ -19,10 +19,10 @@ class CompactTranslationExtractor:
ignore_file: str = "scripts/ignore_translation.toml",
):
self.locales_dir = Path(locales_dir)
self.golden_truth_file = self.locales_dir / "en-GB" / "translation.toml"
self.golden_truth_file = self.locales_dir / "en-US" / "translation.toml"
if not self.golden_truth_file.exists():
print(
f"Error: en-GB translation file not found at {self.golden_truth_file}",
f"Error: en-US translation file not found at {self.golden_truth_file}",
file=sys.stderr,
)
sys.exit(1)
@@ -95,7 +95,7 @@ class CompactTranslationExtractor:
# Find missing translations
missing_keys = set(golden_flat.keys()) - set(target_flat.keys()) - ignore_set
# Find untranslated entries (identical to en-GB or marked [UNTRANSLATED])
# Find untranslated entries (identical to en-US or marked [UNTRANSLATED])
untranslated_keys = set()
for key in target_flat:
if key in golden_flat and key not in ignore_set:
+5 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
TOML Beautifier and Structure Fixer for Stirling PDF Frontend
Restructures translation TOML files to match en-GB structure and key order exactly.
Restructures translation TOML files to match en-US structure and key order exactly.
"""
import sys
@@ -17,7 +17,7 @@ import tomli_w
class TOMLBeautifier:
def __init__(self, locales_dir: str = "frontend/editor/public/locales"):
self.locales_dir = Path(locales_dir)
self.golden_truth_file = self.locales_dir / "en-GB" / "translation.toml"
self.golden_truth_file = self.locales_dir / "en-US" / "translation.toml"
self.golden_structure = self._load_toml(self.golden_truth_file)
def _load_toml(self, file_path: Path) -> Dict:
@@ -95,7 +95,7 @@ class TOMLBeautifier:
return build_recursive(reference_structure) or OrderedDict()
def restructure_translation_file(self, target_file: Path) -> Dict[str, Any]:
"""Restructure a translation file to match en-GB structure exactly."""
"""Restructure a translation file to match en-US structure exactly."""
if not target_file.exists():
print(f"Error: Target file does not exist: {target_file}")
return {}
@@ -177,7 +177,7 @@ class TOMLBeautifier:
}
def validate_key_order(self, target_file: Path) -> Dict[str, Any]:
"""Validate that keys appear in the same order as en-GB."""
"""Validate that keys appear in the same order as en-US."""
target_data = self._load_toml(target_file)
def get_key_order(obj: Dict, path: str = "") -> List[str]:
@@ -276,7 +276,7 @@ def main():
elif args.all_languages:
results = []
for lang_dir in Path(args.locales_dir).iterdir():
if lang_dir.is_dir() and lang_dir.name != "en-GB":
if lang_dir.is_dir() and lang_dir.name != "en-US":
translation_file = lang_dir / "translation.toml"
if translation_file.exists():
if args.validate_only:
+12 -12
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
Translation Analyzer for Stirling PDF Frontend
Compares language files against en-GB golden truth file.
Compares language files against en-US golden truth file.
"""
import json
@@ -19,7 +19,7 @@ class TranslationAnalyzer:
ignore_file: str = "scripts/ignore_translation.toml",
):
self.locales_dir = Path(locales_dir)
self.golden_truth_file = self.locales_dir / "en-GB" / "translation.toml"
self.golden_truth_file = self.locales_dir / "en-US" / "translation.toml"
self.golden_truth = self._load_translation_file(self.golden_truth_file)
self.ignore_file = Path(ignore_file)
self.ignore_patterns = self._load_ignore_patterns()
@@ -70,17 +70,17 @@ class TranslationAnalyzer:
return dict(items)
def get_all_language_files(self) -> List[Path]:
"""Get all translation files except en-GB."""
"""Get all translation files except en-US."""
files = []
for lang_dir in self.locales_dir.iterdir():
if lang_dir.is_dir() and lang_dir.name != "en-GB":
if lang_dir.is_dir() and lang_dir.name != "en-US":
toml_file = lang_dir / "translation.toml"
if toml_file.exists():
files.append(toml_file)
return sorted(files)
def find_missing_translations(self, target_file: Path) -> Set[str]:
"""Find keys that exist in en-GB but missing in target file."""
"""Find keys that exist in en-US but missing in target file."""
target_data = self._load_translation_file(target_file)
golden_flat = self._flatten_dict(self.golden_truth)
@@ -94,7 +94,7 @@ class TranslationAnalyzer:
return missing - ignore_set
def find_untranslated_entries(self, target_file: Path) -> Set[str]:
"""Find entries that appear to be untranslated (identical to en-GB)."""
"""Find entries that appear to be untranslated (identical to en-US)."""
target_data = self._load_translation_file(target_file)
golden_flat = self._flatten_dict(self.golden_truth)
@@ -109,7 +109,7 @@ class TranslationAnalyzer:
target_value = target_flat[key]
golden_value = golden_flat[key]
# Check if marked as [UNTRANSLATED] or identical to en-GB
# Check if marked as [UNTRANSLATED] or identical to en-US
if (
isinstance(target_value, str)
and target_value.startswith("[UNTRANSLATED]")
@@ -139,7 +139,7 @@ class TranslationAnalyzer:
return False
def find_extra_translations(self, target_file: Path) -> Set[str]:
"""Find keys that exist in target file but not in en-GB."""
"""Find keys that exist in target file but not in en-US."""
target_data = self._load_translation_file(target_file)
golden_flat = self._flatten_dict(self.golden_truth)
@@ -174,7 +174,7 @@ class TranslationAnalyzer:
if not (isinstance(value, str) and value.startswith("[UNTRANSLATED]")):
if (
key not in untranslated
): # Not identical to en-GB (unless expected)
): # Not identical to en-US (unless expected)
properly_translated += 1
completion_rate = (
@@ -204,7 +204,7 @@ class TranslationAnalyzer:
def main():
parser = argparse.ArgumentParser(
description="Analyze translation files against en-GB golden truth"
description="Analyze translation files against en-US golden truth"
)
parser.add_argument(
"--locales-dir",
@@ -260,7 +260,7 @@ def main():
print(f"Language: {lang}")
print(f"File: {result['file']}")
print(f"Completion Rate: {result['completion_rate']:.1f}%")
print(f"Total Keys in en-GB: {result['total_keys']}")
print(f"Total Keys in en-US: {result['total_keys']}")
if not args.summary:
if not args.untranslated_only:
@@ -278,7 +278,7 @@ def main():
print(f" ... and {len(result['untranslated_keys']) - 10} more")
if result["extra_count"] > 0:
print(f"\nExtra Keys Not in en-GB ({result['extra_count']}):")
print(f"\nExtra Keys Not in en-US ({result['extra_count']}):")
for key in result["extra_keys"][:5]:
print(f" - {key}")
if len(result["extra_keys"]) > 5:
+10 -10
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
Translation Merger for Stirling PDF Frontend
Merges missing translations from en-GB into target language files.
Merges missing translations from en-US into target language files.
Useful for AI-assisted translation workflows.
TOML format only.
"""
@@ -30,7 +30,7 @@ class TranslationMerger:
),
):
self.locales_dir = Path(locales_dir)
self.golden_truth_file = self.locales_dir / "en-GB" / "translation.toml"
self.golden_truth_file = self.locales_dir / "en-US" / "translation.toml"
self.golden_truth = self._load_translation_file(self.golden_truth_file)
self.ignore_file = Path(ignore_file)
self.ignore_patterns = self._load_ignore_patterns()
@@ -178,7 +178,7 @@ class TranslationMerger:
save: bool = True,
backup: bool = False,
) -> dict[str, Any]:
"""Add missing translations from en-GB to target file and optionally save."""
"""Add missing translations from en-US to target file and optionally save."""
if not target_file.parent.exists():
target_file.parent.mkdir(parents=True, exist_ok=True)
target_data = {}
@@ -210,7 +210,7 @@ class TranslationMerger:
def extract_untranslated_entries(
self, target_file: Path, output_file: Path | None = None
) -> dict[str, Any]:
"""Extract entries marked as untranslated or identical to en-GB for AI translation."""
"""Extract entries marked as untranslated or identical to en-US for AI translation."""
if not target_file.exists():
print(f"Error: Target file does not exist: {target_file}")
return {}
@@ -335,7 +335,7 @@ class TranslationMerger:
template = {
"metadata": {
"source_language": "en-GB",
"source_language": "en-US",
"target_language": target_file.parent.name,
"total_entries": len(untranslated),
"created_at": datetime.now().isoformat(),
@@ -384,14 +384,14 @@ def main():
parser.add_argument(
"language",
nargs="?",
help="Target language code (e.g., fr-FR). If omitted, add-missing and remove-unused run for all locales except en-GB.",
help="Target language code (e.g., fr-FR). If omitted, add-missing and remove-unused run for all locales except en-US.",
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# Add missing command
add_parser = subparsers.add_parser(
"add-missing", help="Add missing translations from en-GB"
"add-missing", help="Add missing translations from en-US"
)
add_parser.add_argument(
"--backup", action="store_true", help="Create backup before modifying files"
@@ -424,7 +424,7 @@ def main():
# Remove unused translations command
remove_parser = subparsers.add_parser(
"remove-unused", help="Remove unused translations not present in en-GB"
"remove-unused", help="Remove unused translations not present in en-US"
)
remove_parser.add_argument(
"--backup", action="store_true", help="Create backup before modifying files"
@@ -449,7 +449,7 @@ def main():
else:
total_added = 0
for lang_dir in sorted(Path(args.locales_dir).iterdir()):
if not lang_dir.is_dir() or lang_dir.name == "en-GB":
if not lang_dir.is_dir() or lang_dir.name == "en-US":
continue
target_file = lang_dir / "translation.toml"
print(f"Processing {lang_dir.name}...")
@@ -471,7 +471,7 @@ def main():
else:
total_removed = 0
for lang_dir in sorted(Path(args.locales_dir).iterdir()):
if not lang_dir.is_dir() or lang_dir.name == "en-GB":
if not lang_dir.is_dir() or lang_dir.name == "en-US":
continue
target_file = lang_dir / "translation.toml"
print(f"Processing {lang_dir.name}...")
+22 -22
View File
@@ -4,9 +4,9 @@ Validate TOML structure and formatting of translation files.
Checks for:
- Valid TOML syntax
- Consistent key structure with en-GB
- Consistent key structure with en-US
- Missing keys
- Extra keys not in en-GB
- Extra keys not in en-US
- Malformed entries
Usage:
@@ -43,18 +43,18 @@ def validate_translation_file(file_path: Path) -> tuple[bool, str]:
def validate_structure(
en_gb_keys: Set[str], lang_keys: Set[str], lang_code: str
en_us_keys: Set[str], lang_keys: Set[str], lang_code: str
) -> Dict:
"""Compare structure between en-GB and target language."""
missing_keys = en_gb_keys - lang_keys
extra_keys = lang_keys - en_gb_keys
"""Compare structure between en-US and target language."""
missing_keys = en_us_keys - lang_keys
extra_keys = lang_keys - en_us_keys
return {
"language": lang_code,
"missing_keys": sorted(missing_keys),
"extra_keys": sorted(extra_keys),
"total_keys": len(lang_keys),
"expected_keys": len(en_gb_keys),
"expected_keys": len(en_us_keys),
"missing_count": len(missing_keys),
"extra_count": len(extra_keys),
}
@@ -68,12 +68,12 @@ def print_validation_result(result: Dict, verbose: bool = False):
print(f"Language: {lang}")
print(f"{'=' * 100}")
print(f" Total keys: {result['total_keys']}")
print(f" Expected keys (en-GB): {result['expected_keys']}")
print(f" Expected keys (en-US): {result['expected_keys']}")
print(f" Missing keys: {result['missing_count']}")
print(f" Extra keys: {result['extra_count']}")
if result["missing_count"] == 0 and result["extra_count"] == 0:
print(" ✅ Structure matches en-GB perfectly!")
print(" ✅ Structure matches en-US perfectly!")
else:
if result["missing_count"] > 0:
print(f"\n ⚠️ Missing {result['missing_count']} key(s):")
@@ -86,7 +86,7 @@ def print_validation_result(result: Dict, verbose: bool = False):
print(" (use --verbose to see all)")
if result["extra_count"] > 0:
print(f"\n ⚠️ Extra {result['extra_count']} key(s) not in en-GB:")
print(f"\n ⚠️ Extra {result['extra_count']} key(s) not in en-US:")
if verbose or result["extra_count"] <= 20:
for key in result["extra_keys"][:50]:
print(f" - {key}")
@@ -120,31 +120,31 @@ def main():
# Define paths
locales_dir = Path("frontend/editor/public/locales")
en_gb_path = locales_dir / "en-GB" / "translation.toml"
en_us_path = locales_dir / "en-US" / "translation.toml"
if not en_gb_path.exists():
print(f"❌ Error: en-GB translation file not found at {en_gb_path}")
if not en_us_path.exists():
print(f"❌ Error: en-US translation file not found at {en_us_path}")
sys.exit(1)
# Validate en-GB itself
is_valid, message = validate_translation_file(en_gb_path)
# Validate en-US itself
is_valid, message = validate_translation_file(en_us_path)
if not is_valid:
print(f"❌ Error in en-GB file: {message}")
print(f"❌ Error in en-US file: {message}")
sys.exit(1)
# Load en-GB structure
en_gb = load_translation_file(en_gb_path)
# Load en-US structure
en_us = load_translation_file(en_us_path)
en_gb_keys = get_all_keys(en_gb)
en_us_keys = get_all_keys(en_us)
# Get list of languages to validate
if args.language:
languages = [args.language]
else:
# Validate all languages except en-GB
# Validate all languages except en-US
languages = []
for d in locales_dir.iterdir():
if d.is_dir() and d.name != "en-GB":
if d.is_dir() and d.name != "en-US":
if (d / "translation.toml").exists():
languages.append(d.name)
@@ -171,7 +171,7 @@ def main():
lang_data = load_translation_file(lang_path)
lang_keys = get_all_keys(lang_data)
result = validate_structure(en_gb_keys, lang_keys, lang_code)
result = validate_structure(en_us_keys, lang_keys, lang_code)
results.append(result)
# Output results
+16 -16
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Validate that translation files have the same placeholders as en-GB (source of truth).
Validate that translation files have the same placeholders as en-US (source of truth).
Usage:
python scripts/translations/validate_placeholders.py [--language LANG] [--fix]
@@ -38,16 +38,16 @@ def flatten_dict(d: dict, parent_key: str = "", sep: str = ".") -> Dict[str, str
def validate_language(
en_gb_flat: Dict[str, str], lang_flat: Dict[str, str], lang_code: str
en_us_flat: Dict[str, str], lang_flat: Dict[str, str], lang_code: str
) -> List[Dict]:
"""Validate placeholders for a language against en-GB."""
"""Validate placeholders for a language against en-US."""
issues = []
for key in en_gb_flat:
for key in en_us_flat:
if key not in lang_flat:
continue
en_placeholders = find_placeholders(en_gb_flat[key])
en_placeholders = find_placeholders(en_us_flat[key])
lang_placeholders = find_placeholders(lang_flat[key])
if en_placeholders != lang_placeholders:
@@ -59,7 +59,7 @@ def validate_language(
"key": key,
"missing": missing,
"extra": extra,
"en_text": en_gb_flat[key],
"en_text": en_us_flat[key],
"lang_text": lang_flat[key],
}
issues.append(issue)
@@ -113,26 +113,26 @@ def main():
# Define paths
locales_dir = Path("frontend/editor/public/locales")
en_gb_path = locales_dir / "en-GB" / "translation.toml"
en_us_path = locales_dir / "en-US" / "translation.toml"
if not en_gb_path.exists():
print(f"❌ Error: en-GB translation file not found at {en_gb_path}")
if not en_us_path.exists():
print(f"❌ Error: en-US translation file not found at {en_us_path}")
sys.exit(1)
# Load en-GB (source of truth)
with open(en_gb_path, "rb") as f:
en_gb = tomllib.load(f)
# Load en-US (source of truth)
with open(en_us_path, "rb") as f:
en_us = tomllib.load(f)
en_gb_flat = flatten_dict(en_gb)
en_us_flat = flatten_dict(en_us)
# Get list of languages to validate
if args.language:
languages = [args.language]
else:
# Validate all languages except en-GB
# Validate all languages except en-US
languages = []
for d in locales_dir.iterdir():
if d.is_dir() and d.name != "en-GB":
if d.is_dir() and d.name != "en-US":
if (d / "translation.toml").exists():
languages.append(d.name)
@@ -151,7 +151,7 @@ def main():
lang_data = tomllib.load(f)
lang_flat = flatten_dict(lang_data)
issues = validate_language(en_gb_flat, lang_flat, lang_code)
issues = validate_language(en_us_flat, lang_flat, lang_code)
all_issues.extend(issues)
# Output results