diff --git a/apps/docs/content/docs/3.0-beta/translation-management.mdx b/apps/docs/content/docs/3.0-beta/translation-management.mdx
index 4078e20..4205d34 100644
--- a/apps/docs/content/docs/3.0-beta/translation-management.mdx
+++ b/apps/docs/content/docs/3.0-beta/translation-management.mdx
@@ -6,7 +6,7 @@ icon: Languages
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import { Callout } from "fumadocs-ui/components/callout";
-Palmr includes a comprehensive translation management system that automates synchronization, validation, and translation of the application's internationalization files.
+Palmr includes a comprehensive translation key management system that automates synchronization and validation of the application's internationalization files.
## Overview
@@ -14,7 +14,7 @@ The translation management system consists of Python scripts that help maintain
- **Synchronization**: Automatically add missing translation keys
- **Validation**: Check translation status and completeness
-- **Auto-translation**: Use Google Translate API for initial translations
+- **Key Management**: Manage translation keys structure and consistency
- **Reporting**: Generate detailed translation reports
## Quick Start
@@ -31,10 +31,7 @@ The translation management system consists of Python scripts that help maintain
# Synchronize missing keys
pnpm run translations:sync
- # Auto-translate [TO_TRANSLATE] strings
- pnpm run translations:translate
-
- # Dry run mode (test without changes)
+ # Test workflow without changes (dry run)
pnpm run translations:dry-run
# Show help
@@ -52,7 +49,7 @@ The translation management system consists of Python scripts that help maintain
# Individual commands
python3 run_translations.py check
python3 run_translations.py sync
- python3 run_translations.py translate
+ python3 run_translations.py all --dry-run
python3 run_translations.py help
```
@@ -63,14 +60,13 @@ The translation management system consists of Python scripts that help maintain
### Main Commands (npm/pnpm)
-| Command | Description |
-| --------------------------------- | ------------------------------------------- |
-| `pnpm run translations` | Complete workflow: sync + translate + check |
-| `pnpm run translations:check` | Check translation status and completeness |
-| `pnpm run translations:sync` | Synchronize missing keys from en-US.json |
-| `pnpm run translations:translate` | Auto-translate [TO_TRANSLATE] strings |
-| `pnpm run translations:dry-run` | Test workflow without making changes |
-| `pnpm run translations:help` | Show detailed help and examples |
+| Command | Description |
+| ------------------------------- | ----------------------------------------- |
+| `pnpm run translations` | Complete workflow: sync + check |
+| `pnpm run translations:check` | Check translation status and completeness |
+| `pnpm run translations:sync` | Synchronize missing keys from en-US.json |
+| `pnpm run translations:dry-run` | Test workflow without making changes |
+| `pnpm run translations:help` | Show detailed help and examples |
## Workflow
@@ -80,13 +76,13 @@ When you add new text to the application:
1. **Add to English**: Update `apps/web/messages/en-US.json` with your new keys
2. **Sync translations**: Run `pnpm run translations:sync` to add missing keys to all languages
-3. **Auto-translate**: Run `pnpm run translations:translate` for automatic translations
-4. **Review translations**: **Mandatory step** - Check all auto-generated translations for accuracy
-5. **Test in UI**: Verify translations work correctly in the application interface
+3. **Manual translation**: Translate strings marked with `[TO_TRANSLATE]` manually
+4. **Test in UI**: Verify translations work correctly in the application interface
- **Never skip step 4**: Auto-generated translations must be reviewed before
- committing to production. They are a starting point, not a final solution.
+ **Manual translation required**: All strings marked with `[TO_TRANSLATE]` must
+ be manually translated by native speakers or professional translators for
+ accuracy.
### 2. Checking Translation Status
@@ -110,7 +106,7 @@ The report shows:
### 3. Manual Translation Process
-For critical strings or when automatic translation isn't sufficient:
+For all translation strings:
1. **Find untranslated strings**: Look for `[TO_TRANSLATE] Original text` in language files
2. **Replace with translation**: Remove the prefix and add proper translation
@@ -130,13 +126,13 @@ apps/web/
├── run_translations.py # Main wrapper
├── sync_translations.py # Synchronization
├── check_translations.py # Status checking
- └── translate_missing.py # Auto-translation
+ └── clean_translations.py # Cleanup utilities
```
## Prerequisites
- **Python 3.6 or higher** - Required for running the translation scripts
-- **No external dependencies** - Scripts use only Python standard libraries (googletrans auto-installs when needed)
+- **No external dependencies** - Scripts use only Python standard libraries
- **UTF-8 support** - Ensure your terminal supports UTF-8 for proper display of translations
## Script Details
@@ -149,8 +145,7 @@ The main script provides a unified interface for all translation operations:
- `check` - Check translation status and generate reports
- `sync` - Synchronize missing keys from reference language
-- `translate` - Automatically translate marked strings
-- `all` - Run complete workflow (sync + translate + check)
+- `all` - Run complete workflow (sync + check)
- `help` - Show detailed help with examples
#### How it Works
@@ -197,27 +192,6 @@ Provides comprehensive translation analysis:
- **Quality insights**: Identifies potential translation issues
- **Export friendly**: Output can be redirected to files for reports
-### Auto-Translation Script (`translate_missing.py`)
-
-Automated translation using Google Translate API:
-
-#### Process
-
-1. **Dependency check**: Auto-installs `googletrans` if needed
-2. **Scan for work**: Finds all `[TO_TRANSLATE]` prefixed strings
-3. **Language mapping**: Maps file names to Google Translate language codes
-4. **Batch translation**: Processes strings with rate limiting
-5. **Update files**: Replaces marked strings with translations
-6. **Error handling**: Retries failed translations, reports results
-
-#### Safety Features
-
-- **Rate limiting**: Configurable delay between requests
-- **Retry logic**: Multiple attempts for failed translations
-- **Dry run mode**: Preview changes without modifications
-- **Language skipping**: Exclude specific languages from processing
-- **Progress tracking**: Real-time status of translation progress
-
## Advanced Usage
### Custom Parameters
@@ -240,22 +214,6 @@ python3 scripts/run_translations.py sync --messages-dir /path/to/messages
python3 scripts/run_translations.py sync --dry-run
```
-#### Translation Parameters (`translate`)
-
-```bash
-# Custom delay between translation requests (avoid rate limiting)
-python3 scripts/run_translations.py translate --delay 2.0
-
-# Skip specific languages from translation
-python3 scripts/run_translations.py translate --skip-languages pt-BR.json fr-FR.json
-
-# Dry run - see what would be translated
-python3 scripts/run_translations.py translate --dry-run
-
-# Specify custom messages directory
-python3 scripts/run_translations.py translate --messages-dir /path/to/messages
-```
-
#### Check Parameters (`check`)
```bash
@@ -268,58 +226,46 @@ python3 scripts/run_translations.py check --messages-dir /path/to/messages
### Parameter Reference
-| Parameter | Commands | Description |
-| ------------------------ | -------------------------- | --------------------------------------------- |
-| `--dry-run` | `sync`, `translate`, `all` | Preview changes without modifying files |
-| `--messages-dir` | All | Custom directory containing translation files |
-| `--reference` | `sync`, `check` | Reference file to use (default: en-US.json) |
-| `--no-mark-untranslated` | `sync` | Don't add [TO_TRANSLATE] prefix to new keys |
-| `--delay` | `translate` | Delay in seconds between translation requests |
-| `--skip-languages` | `translate` | List of language files to skip |
+| Parameter | Commands | Description |
+| ------------------------ | --------------- | --------------------------------------------- |
+| `--dry-run` | `sync`, `all` | Preview changes without modifying files |
+| `--messages-dir` | All | Custom directory containing translation files |
+| `--reference` | `sync`, `check` | Reference file to use (default: en-US.json) |
+| `--no-mark-untranslated` | `sync` | Don't add [TO_TRANSLATE] prefix to new keys |
### Dry Run Mode
Always test changes first:
```bash
-# Test complete workflow
+# Test complete workflow (recommended)
pnpm run translations:dry-run
-# Test individual commands
+# Alternative: Direct Python commands
+python3 scripts/run_translations.py all --dry-run
python3 scripts/run_translations.py sync --dry-run
-python3 scripts/run_translations.py translate --dry-run
```
### Common Use Cases
-#### Scenario 1: Careful Translation with Review
+#### Scenario 1: Adding Keys Without Marking for Translation
```bash
-# 1. Sync new keys without auto-marking for translation
+# Sync new keys without auto-marking for translation
python3 scripts/run_translations.py sync --no-mark-untranslated
-# 2. Manually mark specific keys that need translation
+# Manually mark specific keys that need translation
# Edit files to add [TO_TRANSLATE] prefix where needed
-
-# 3. Translate only marked strings with slower rate
-python3 scripts/run_translations.py translate --delay 2.0
```
-#### Scenario 2: Skip Already Reviewed Languages
-
-```bash
-# Skip languages that were already manually reviewed
-python3 scripts/run_translations.py translate --skip-languages pt-BR.json es-ES.json
-```
-
-#### Scenario 3: Custom Project Structure
+#### Scenario 2: Custom Project Structure
```bash
# Work with translations in different directory
python3 scripts/run_translations.py all --messages-dir ../custom-translations
```
-#### Scenario 4: Quality Assurance
+#### Scenario 3: Quality Assurance
```bash
# Use different language as reference for comparison
@@ -352,15 +298,15 @@ Translation files use nested JSON structure:
}
```
-## Automatic Translation
+## Manual Translation
- **Review Required**: Automatic translations are provided for convenience but
- **must be reviewed** before production use. They serve as a starting point,
- not a final solution.
+ **Professional translation recommended**: For production applications,
+ consider using professional translation services or native speakers to ensure
+ high-quality, culturally appropriate translations.
-The system uses Google Translate (free API) to automatically translate strings marked with `[TO_TRANSLATE]`:
+The system marks strings that need translation with the `[TO_TRANSLATE]` prefix:
```json
{
@@ -368,7 +314,7 @@ The system uses Google Translate (free API) to automatically translate strings m
}
```
-After auto-translation:
+After manual translation:
```json
{
@@ -378,14 +324,15 @@ After auto-translation:
### Translation Review Process
-1. **Generate**: Use `pnpm run translations:translate` to auto-translate
-2. **Review**: Check each translation for:
+1. **Identify**: Use `pnpm run translations:check` to find untranslated strings
+2. **Translate**: Replace `[TO_TRANSLATE]` strings with proper translations
+3. **Review**: Check each translation for:
- **Context accuracy**: Does it make sense in the UI?
- **Technical terms**: Are they correctly translated?
- **Tone consistency**: Matches the application's voice?
- **Cultural appropriateness**: Suitable for target audience?
-3. **Test**: Verify translations in the actual application interface
-4. **Document**: Note any translation decisions for future reference
+4. **Test**: Verify translations in the actual application interface
+5. **Document**: Note any translation decisions for future reference
### Common Review Points
@@ -405,15 +352,15 @@ After auto-translation:
### Translation Workflow for Development
1. **English First**: Add all new text to `apps/web/messages/en-US.json`
-2. **Auto-generate**: Use scripts to generate translations for other languages
-3. **Review Required**: All auto-generated translations must be reviewed before production use
+2. **Sync keys**: Use scripts to generate key structure for other languages
+3. **Manual translation**: All strings must be manually translated for accuracy
4. **Quality Check**: Run translation validation before merging PRs
### Why English as Parent Language?
- **Consistency**: Ensures all languages have the same keys structure
- **Reference**: English serves as the source of truth for meaning
-- **Auto-translation**: Scripts use English as source for automatic translation
+- **Key management**: Scripts use English as source for key synchronization
- **Documentation**: Most technical documentation is in English
## Best Practices
@@ -422,8 +369,8 @@ After auto-translation:
1. **Always use English as reference**: Add new keys to `en-US.json` first - never add keys directly to other languages
2. **Use semantic key names**: `dashboard.welcome` instead of `text1`
-3. **Test translations**: Run `pnpm run translations:dry-run` before committing
-4. **Review auto-translations**: All generated translations must be reviewed before production
+3. **Test key sync**: Run `pnpm run translations:dry-run` before committing
+4. **Coordinate with translators**: Ensure translation team is aware of new keys
5. **Maintain consistency**: Use existing patterns for similar UI elements
### For Translators
@@ -432,6 +379,7 @@ After auto-translation:
2. **Check identical strings**: May need localization even if identical to English
3. **Use proper formatting**: Maintain HTML tags and placeholders
4. **Test in context**: Verify translations work in the actual UI
+5. **Maintain glossary**: Keep consistent terminology across translations
## Troubleshooting
@@ -439,17 +387,9 @@ After auto-translation:
**Python not found**: Ensure Python 3.6+ is installed and in PATH
-**googletrans errors**: The system auto-installs dependencies, but you can manually install:
+**Permission errors**: Ensure write permissions to message files
-```bash
-pip install googletrans==4.0.0rc1
-```
-
-**Rate limiting**: Increase delay between requests:
-
-```bash
-python3 scripts/run_translations.py translate --delay 2.0
-```
+**Encoding issues**: Ensure your terminal supports UTF-8
### Getting Help
@@ -491,28 +431,6 @@ LANGUAGE COMPLETENESS STRINGS UNTRANSLATED POSSIBLE MATCHES
================================================================================
```
-### Auto-Translation Progress
-
-```
-🌍 Translating 3 languages...
-⏱️ Delay between requests: 1.0s
-
-[1/3] 🌐 Language: PT
-🔍 Processing: pt-BR.json
- 📝 Found 12 strings to translate
- 📍 (1/12) Translating: dashboard.welcome
- ✅ "Welcome to Palmr" → "Bem-vindo ao Palmr"
- 💾 File saved with 12 translations
-
-📊 FINAL SUMMARY
-================================================================================
-✅ Translations performed:
- • 34 successes
- • 2 failures
- • 36 total processed
- • Success rate: 94.4%
-```
-
## Contributing
When contributing translations:
@@ -520,6 +438,6 @@ When contributing translations:
1. **Follow the workflow**: Use the provided scripts for consistency
2. **Test thoroughly**: Run complete checks before submitting
3. **Document changes**: Note any significant translation decisions
-4. **Maintain quality**: Review auto-translations for accuracy
+4. **Maintain quality**: Ensure manual translations are accurate and appropriate
The translation management system ensures consistency and makes it easy to maintain high-quality localization across all supported languages.
diff --git a/apps/web/package.json b/apps/web/package.json
index 164f741..454adfd 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -24,9 +24,8 @@
"translations": "python3 scripts/run_translations.py all",
"translations:check": "python3 scripts/run_translations.py check",
"translations:sync": "python3 scripts/run_translations.py sync",
- "translations:translate": "python3 scripts/run_translations.py translate",
- "translations:help": "python3 scripts/run_translations.py help",
- "translations:dry-run": "python3 scripts/run_translations.py all --dry-run"
+ "translations:dry-run": "python3 scripts/run_translations.py all --dry-run",
+ "translations:help": "python3 scripts/run_translations.py help"
},
"dependencies": {
"@hookform/resolvers": "^5.0.1",
diff --git a/apps/web/scripts/run_translations.py b/apps/web/scripts/run_translations.py
index c075e0c..a6a276e 100755
--- a/apps/web/scripts/run_translations.py
+++ b/apps/web/scripts/run_translations.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
-Main script to run all Palmr translation operations.
+Main script to run Palmr translation management operations.
Makes it easy to run scripts without remembering specific names.
"""
@@ -17,25 +17,65 @@ def run_command(script_name: str, args: list) -> int:
return subprocess.run(cmd).returncode
+def filter_args_for_script(script_name: str, args: list) -> list:
+ """Filter arguments based on what each script accepts."""
+
+ # Arguments that check_translations.py accepts
+ check_args = ['--messages-dir', '--reference']
+
+ # Arguments that sync_translations.py accepts
+ sync_args = ['--messages-dir', '--reference', '--no-mark-untranslated', '--dry-run']
+
+ if script_name == 'check_translations.py':
+ filtered = []
+ skip_next = False
+ for i, arg in enumerate(args):
+ if skip_next:
+ skip_next = False
+ continue
+ if arg in check_args:
+ filtered.append(arg)
+ # Add the value for the argument if it exists
+ if i + 1 < len(args) and not args[i + 1].startswith('--'):
+ filtered.append(args[i + 1])
+ skip_next = True
+ return filtered
+
+ elif script_name == 'sync_translations.py':
+ filtered = []
+ skip_next = False
+ for i, arg in enumerate(args):
+ if skip_next:
+ skip_next = False
+ continue
+ if arg in sync_args:
+ filtered.append(arg)
+ # Add the value for the argument if it exists
+ if i + 1 < len(args) and not args[i + 1].startswith('--'):
+ filtered.append(args[i + 1])
+ skip_next = True
+ return filtered
+
+ return args
+
+
def main():
parser = argparse.ArgumentParser(
description='Main script to manage Palmr translations',
epilog='Examples:\n'
' python3 run_translations.py check\n'
' python3 run_translations.py sync --dry-run\n'
- ' python3 run_translations.py translate --delay 2.0\n'
- ' python3 run_translations.py all # Complete workflow\n',
+ ' python3 run_translations.py all --dry-run\n',
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
'command',
- choices=['check', 'sync', 'translate', 'all', 'help'],
+ choices=['check', 'sync', 'all', 'help'],
help='Command to execute:\n'
'check - Check translation status\n'
'sync - Synchronize missing keys\n'
- 'translate - Automatically translate strings\n'
- 'all - Run complete workflow\n'
+ 'all - Run complete workflow (sync + check)\n'
'help - Show detailed help'
)
@@ -57,13 +97,7 @@ def main():
print(" python3 run_translations.py sync --dry-run")
print(" python3 run_translations.py sync --no-mark-untranslated")
print()
- print("🌐 translate - Automatically translate")
- print(" python3 run_translations.py translate")
- print(" python3 run_translations.py translate --dry-run")
- print(" python3 run_translations.py translate --delay 2.0")
- print(" python3 run_translations.py translate --skip-languages pt-BR.json")
- print()
- print("⚡ all - Complete workflow (sync + translate)")
+ print("⚡ all - Complete workflow (sync + check)")
print(" python3 run_translations.py all")
print(" python3 run_translations.py all --dry-run")
print()
@@ -72,22 +106,21 @@ def main():
print(" apps/web/messages/ - Translation files")
print()
print("💡 TIPS:")
- print("• Use --dry-run on any command to test")
+ print("• Use --dry-run on sync or all commands to test")
print("• Use --help on any command for specific options")
- print("• Read https://docs.palmr.dev/docs/3.0-beta/translation-management for complete documentation")
+ print("• Manually translate strings marked with [TO_TRANSLATE]")
+ print("• Read documentation for complete translation guidelines")
return 0
elif args.command == 'check':
print("🔍 Checking translation status...")
- return run_command('check_translations.py', remaining_args)
+ filtered_args = filter_args_for_script('check_translations.py', remaining_args)
+ return run_command('check_translations.py', filtered_args)
elif args.command == 'sync':
print("🔄 Synchronizing translation keys...")
- return run_command('sync_translations.py', remaining_args)
-
- elif args.command == 'translate':
- print("🌐 Automatically translating strings...")
- return run_command('translate_missing.py', remaining_args)
+ filtered_args = filter_args_for_script('sync_translations.py', remaining_args)
+ return run_command('sync_translations.py', filtered_args)
elif args.command == 'all':
print("⚡ Running complete translation workflow...")
@@ -98,7 +131,8 @@ def main():
# 1. Initial check
print("1️⃣ Checking initial status...")
- result = run_command('check_translations.py', remaining_args)
+ check_args = filter_args_for_script('check_translations.py', remaining_args)
+ result = run_command('check_translations.py', check_args)
if result != 0:
print("❌ Error in initial check")
return result
@@ -107,33 +141,27 @@ def main():
# 2. Sync
print("2️⃣ Synchronizing missing keys...")
- result = run_command('sync_translations.py', remaining_args)
+ sync_args = filter_args_for_script('sync_translations.py', remaining_args)
+ result = run_command('sync_translations.py', sync_args)
if result != 0:
print("❌ Error in synchronization")
return result
- if not is_dry_run:
- print("\n" + "="*50)
-
- # 3. Translate
- print("3️⃣ Automatically translating strings...")
- result = run_command('translate_missing.py', remaining_args)
- if result != 0:
- print("❌ Error in translation")
- return result
-
- print("\n" + "="*50)
-
- # 4. Final check
- print("4️⃣ Final check...")
- result = run_command('check_translations.py', remaining_args)
- if result != 0:
- print("❌ Error in final check")
- return result
+ print("\n" + "="*50)
+
+ # 3. Final check
+ print("3️⃣ Final check...")
+ check_args = filter_args_for_script('check_translations.py', remaining_args)
+ result = run_command('check_translations.py', check_args)
+ if result != 0:
+ print("❌ Error in final check")
+ return result
print("\n🎉 Complete workflow executed successfully!")
if is_dry_run:
print("💡 Run without --dry-run to apply changes")
+ else:
+ print("💡 Review strings marked with [TO_TRANSLATE] and translate them manually")
return 0
diff --git a/apps/web/scripts/translate_missing.py b/apps/web/scripts/translate_missing.py
deleted file mode 100755
index ea2f8c0..0000000
--- a/apps/web/scripts/translate_missing.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/env python3
-"""
-Script to automatically translate strings marked with [TO_TRANSLATE]
-using free Google Translate.
-"""
-
-import json
-import time
-import re
-from pathlib import Path
-from typing import Dict, Any, List, Tuple, Optional
-import argparse
-import sys
-
-# Language code mapping from file names to Google Translate codes
-LANGUAGE_MAPPING = {
- 'pt-BR.json': 'pt', # Portuguese (Brazil) -> Portuguese
- 'es-ES.json': 'es', # Spanish (Spain) -> Spanish
- 'fr-FR.json': 'fr', # French (France) -> French
- 'de-DE.json': 'de', # German -> German
- 'it-IT.json': 'it', # Italian -> Italian
- 'ru-RU.json': 'ru', # Russian -> Russian
- 'ja-JP.json': 'ja', # Japanese -> Japanese
- 'ko-KR.json': 'ko', # Korean -> Korean
- 'zh-CN.json': 'zh-cn', # Chinese (Simplified) -> Simplified Chinese
- 'ar-SA.json': 'ar', # Arabic -> Arabic
- 'hi-IN.json': 'hi', # Hindi -> Hindi
- 'nl-NL.json': 'nl', # Dutch -> Dutch
- 'tr-TR.json': 'tr', # Turkish -> Turkish
- 'pl-PL.json': 'pl', # Polish -> Polish
-}
-
-# Prefix to identify untranslated strings
-TO_TRANSLATE_PREFIX = '[TO_TRANSLATE] '
-
-
-def load_json_file(file_path: Path) -> Dict[str, Any]:
- """Load a JSON file."""
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- return json.load(f)
- except Exception as e:
- print(f"❌ Error loading {file_path}: {e}")
- return {}
-
-
-def save_json_file(file_path: Path, data: Dict[str, Any], indent: int = 2) -> bool:
- """Save a JSON file with consistent formatting."""
- try:
- with open(file_path, 'w', encoding='utf-8') as f:
- json.dump(data, f, ensure_ascii=False, indent=indent, separators=(',', ': '))
- f.write('\n') # Add newline at the end
- return True
- except Exception as e:
- print(f"❌ Error saving {file_path}: {e}")
- return False
-
-
-def get_nested_value(data: Dict[str, Any], key_path: str) -> Any:
- """Get a nested value using a key with dots as separator."""
- keys = key_path.split('.')
- current = data
-
- for key in keys:
- if isinstance(current, dict) and key in current:
- current = current[key]
- else:
- return None
-
- return current
-
-
-def set_nested_value(data: Dict[str, Any], key_path: str, value: Any) -> None:
- """Set a nested value using a key with dots as separator."""
- keys = key_path.split('.')
- current = data
-
- # Navigate to the second-to-last level, creating dictionaries as needed
- for key in keys[:-1]:
- if key not in current:
- current[key] = {}
- elif not isinstance(current[key], dict):
- current[key] = {}
- current = current[key]
-
- # Set the value at the last level
- current[keys[-1]] = value
-
-
-def find_untranslated_strings(data: Dict[str, Any], prefix: str = '') -> List[Tuple[str, str]]:
- """Find all strings marked with [TO_TRANSLATE] recursively."""
- untranslated = []
-
- for key, value in data.items():
- current_key = f"{prefix}.{key}" if prefix else key
-
- if isinstance(value, str) and value.startswith(TO_TRANSLATE_PREFIX):
- # Remove prefix to get original text
- original_text = value[len(TO_TRANSLATE_PREFIX):].strip()
- untranslated.append((current_key, original_text))
- elif isinstance(value, dict):
- untranslated.extend(find_untranslated_strings(value, current_key))
-
- return untranslated
-
-
-def install_googletrans():
- """Install the googletrans library if not available."""
- try:
- import googletrans
- return True
- except ImportError:
- print("📦 'googletrans' library not found. Attempting to install...")
- import subprocess
- try:
- subprocess.check_call([sys.executable, "-m", "pip", "install", "googletrans==4.0.0rc1"])
- print("✅ googletrans installed successfully!")
- return True
- except subprocess.CalledProcessError:
- print("❌ Failed to install googletrans. Install manually with:")
- print("pip install googletrans==4.0.0rc1")
- return False
-
-
-def translate_text(text: str, target_language: str, max_retries: int = 3) -> Optional[str]:
- """Translate text using free Google Translate."""
- try:
- from googletrans import Translator
-
- translator = Translator()
-
- for attempt in range(max_retries):
- try:
- # Translate from English to target language
- result = translator.translate(text, src='en', dest=target_language)
-
- if result and result.text:
- return result.text.strip()
-
- except Exception as e:
- if attempt < max_retries - 1:
- print(f" ⚠️ Attempt {attempt + 1} failed: {str(e)[:50]}... Retrying in 2s...")
- time.sleep(2)
- else:
- print(f" ❌ Failed after {max_retries} attempts: {str(e)[:50]}...")
-
- return None
-
- except ImportError:
- print("❌ googletrans library not available")
- return None
-
-
-def translate_file(file_path: Path, target_language: str, dry_run: bool = False,
- delay_between_requests: float = 1.0) -> Tuple[int, int, int]:
- """
- Translate all [TO_TRANSLATE] strings in a file.
- Returns: (total_found, successful_translations, failed_translations)
- """
- print(f"🔍 Processing: {file_path.name}")
-
- # Load file
- data = load_json_file(file_path)
- if not data:
- return 0, 0, 0
-
- # Find untranslated strings
- untranslated_strings = find_untranslated_strings(data)
-
- if not untranslated_strings:
- print(f" ✅ No strings to translate")
- return 0, 0, 0
-
- print(f" 📝 Found {len(untranslated_strings)} strings to translate")
-
- if dry_run:
- print(f" 🔍 [DRY RUN] Strings that would be translated:")
- for key, text in untranslated_strings[:3]:
- print(f" - {key}: \"{text[:50]}{'...' if len(text) > 50 else ''}\"")
- if len(untranslated_strings) > 3:
- print(f" ... and {len(untranslated_strings) - 3} more")
- return len(untranslated_strings), 0, 0
-
- # Translate each string
- successful = 0
- failed = 0
- updated_data = data.copy()
-
- for i, (key_path, original_text) in enumerate(untranslated_strings, 1):
- print(f" 📍 ({i}/{len(untranslated_strings)}) Translating: {key_path}")
-
- # Translate text
- translated_text = translate_text(original_text, target_language)
-
- if translated_text and translated_text != original_text:
- # Update in dictionary
- set_nested_value(updated_data, key_path, translated_text)
- successful += 1
- print(f" ✅ \"{original_text[:30]}...\" → \"{translated_text[:30]}...\"")
- else:
- failed += 1
- print(f" ❌ Translation failed")
-
- # Delay between requests to avoid rate limiting
- if i < len(untranslated_strings): # Don't wait after the last one
- time.sleep(delay_between_requests)
-
- # Save updated file
- if successful > 0:
- if save_json_file(file_path, updated_data):
- print(f" 💾 File saved with {successful} translations")
- else:
- print(f" ❌ Error saving file")
- failed += successful # Count as failure if couldn't save
- successful = 0
-
- return len(untranslated_strings), successful, failed
-
-
-def translate_all_files(messages_dir: Path, delay_between_requests: float = 1.0,
- dry_run: bool = False, skip_languages: List[str] = None) -> None:
- """Translate all language files that have [TO_TRANSLATE] strings."""
-
- if not install_googletrans():
- return
-
- skip_languages = skip_languages or []
-
- # Find language JSON files
- language_files = []
- for file_name, lang_code in LANGUAGE_MAPPING.items():
- file_path = messages_dir / file_name
- if file_path.exists() and file_name not in skip_languages:
- language_files.append((file_path, lang_code))
-
- if not language_files:
- print("❌ No language files found")
- return
-
- print(f"🌍 Translating {len(language_files)} languages...")
- print(f"⏱️ Delay between requests: {delay_between_requests}s")
- if dry_run:
- print("🔍 DRY RUN MODE - No changes will be made")
- print("-" * 60)
-
- total_found = 0
- total_successful = 0
- total_failed = 0
-
- for i, (file_path, lang_code) in enumerate(language_files, 1):
- print(f"\n[{i}/{len(language_files)}] 🌐 Language: {lang_code.upper()}")
-
- found, successful, failed = translate_file(
- file_path, lang_code, dry_run, delay_between_requests
- )
-
- total_found += found
- total_successful += successful
- total_failed += failed
-
- # Pause between files (except the last one)
- if i < len(language_files) and not dry_run:
- print(f" ⏸️ Pausing {delay_between_requests * 2}s before next language...")
- time.sleep(delay_between_requests * 2)
-
- # Final summary
- print("\n" + "=" * 60)
- print("📊 FINAL SUMMARY")
- print("=" * 60)
-
- if dry_run:
- print(f"🔍 DRY RUN MODE:")
- print(f" • {total_found} strings would be translated")
- else:
- print(f"✅ Translations performed:")
- print(f" • {total_successful} successes")
- print(f" • {total_failed} failures")
- print(f" • {total_found} total processed")
-
- if total_successful > 0:
- success_rate = (total_successful / total_found) * 100
- print(f" • Success rate: {success_rate:.1f}%")
-
- print("\n💡 TIPS:")
- print("• Run 'python3 check_translations.py' to verify results")
- print("• Strings that failed translation keep the [TO_TRANSLATE] prefix")
- print("• Consider reviewing automatic translations to ensure quality")
-
-
-def main():
- parser = argparse.ArgumentParser(
- description='Automatically translate strings marked with [TO_TRANSLATE]'
- )
- parser.add_argument(
- '--messages-dir',
- type=Path,
- default=Path(__file__).parent.parent / 'messages',
- help='Directory containing message files (default: ../messages)'
- )
- parser.add_argument(
- '--dry-run',
- action='store_true',
- help='Only show what would be translated without making changes'
- )
- parser.add_argument(
- '--delay',
- type=float,
- default=1.0,
- help='Delay in seconds between translation requests (default: 1.0)'
- )
- parser.add_argument(
- '--skip-languages',
- nargs='*',
- default=[],
- help='List of languages to skip (ex: pt-BR.json fr-FR.json)'
- )
-
- args = parser.parse_args()
-
- if not args.messages_dir.exists():
- print(f"❌ Directory not found: {args.messages_dir}")
- return 1
-
- print(f"📁 Directory: {args.messages_dir}")
- print(f"🔍 Dry run: {args.dry_run}")
- print(f"⏱️ Delay: {args.delay}s")
- if args.skip_languages:
- print(f"⏭️ Skipping: {', '.join(args.skip_languages)}")
- print("-" * 60)
-
- translate_all_files(
- messages_dir=args.messages_dir,
- delay_between_requests=args.delay,
- dry_run=args.dry_run,
- skip_languages=args.skip_languages
- )
-
- return 0
-
-
-if __name__ == '__main__':
- exit(main())
\ No newline at end of file
diff --git a/infra/build-docker.sh b/infra/build-docker.sh
index 9ed9471..f316602 100755
--- a/infra/build-docker.sh
+++ b/infra/build-docker.sh
@@ -23,7 +23,7 @@ docker buildx build \
--no-cache \
-t kyantech/palmr:latest \
-t kyantech/palmr:$TAG \
- --load \
+ --push \
.
if [ $? -eq 0 ]; then