Add translation management tool suite and update Chinese translations

- Introduced a comprehensive guide for the translation management tool suite, detailing components, usage, and best practices.
- Added `translation-manager.py`, `translation-cleaner.py`, and `manage-translations.sh` scripts for managing translations.
- Updated the Chinese translation file (`zh_CN.json`) to improve existing translations and remove unused keys.
- Enhanced documentation with examples and troubleshooting tips for better user experience.
This commit is contained in:
月月
2025-06-17 14:29:33 +08:00
parent b32734b9f5
commit 89e25e3504
8 changed files with 1348 additions and 661 deletions
@@ -0,0 +1,111 @@
# Translation Management Tools
This directory contains a toolset for managing project translation files.
## Directory Structure
```
translations/
├── tools/ # Translation management tools directory
│ ├── translation-manager.py # Main translation manager
│ ├── translation-cleaner.py # Translation maintenance tool
│ ├── manage-translations.sh # Convenient wrapper script
│ ├── translation-tools-guide.md # Detailed usage documentation
│ └── README.md # This file
├── en_US.json # English translation file
├── zh_CN.json # Chinese translation file
└── ... # Other language files
```
## Quick Start
### Running from tools directory
```bash
# Enter tools directory
cd .config/quickshell/translations/tools
# Check current translation status
./manage-translations.sh status
# Update all translation files
./manage-translations.sh update
# Update specific language
./manage-translations.sh update -l zh_CN
# Clean unused keys
./manage-translations.sh clean
# Sync all language files
./manage-translations.sh sync
```
### Running from project root directory
```bash
# Run from project root directory (recommended to use relative paths)
.config/quickshell/translations/tools/manage-translations.sh status
.config/quickshell/translations/tools/manage-translations.sh update
```
## Tool Description
### 🛠️ `manage-translations.sh` - Main Entry Point
Convenient command-line interface that integrates all translation management functions.
### 🔍 `translation-manager.py` - Core Manager
- Extract translatable texts
- Compare translation file differences
- Interactive translation updates
### 🧹 `translation-cleaner.py` - Maintenance Tool
- Clean unused translation keys
- Sync language file structure
- Create backup files
## Common Workflows
### After adding new translatable texts
```bash
./manage-translations.sh update
```
### Clean up after code refactoring
```bash
./manage-translations.sh clean
```
### Add new language
```bash
./manage-translations.sh update -l new_language_code
```
### Check translation status
```bash
./manage-translations.sh status
```
## Documentation
- 📖 [Detailed Usage Guide](./translation-tools-guide.md)
## Important Notes
1. **Running Location**: Tools automatically detect relative paths, can be run from tools directory or project root
2. **Backup**: Cleanup operations automatically create backup files
3. **Encoding**: All files use UTF-8 encoding
4. **Permissions**: Ensure scripts have execution permissions
## Supported Translation Formats
The tool recognizes translatable texts in the following formats:
```qml
qsTr("Your text here")
qsTr('Single quotes work too')
i18n.t("JavaScript translations")
```
---
If you have any issues, please refer to the detailed documentation or check error messages in script output.
@@ -0,0 +1,149 @@
#!/bin/bash
# Translation management script - convenient wrapper
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TRANSLATIONS_DIR="$(dirname "$SCRIPT_DIR")"
SOURCE_DIR="$(dirname "$(dirname "$TRANSLATIONS_DIR")")"
show_help() {
echo "Translation Management Tool - Convenient Wrapper"
echo ""
echo "Usage: $0 [options] <command>"
echo ""
echo "Commands:"
echo " extract Extract translatable texts to temporary file"
echo " update Update translation files (add missing/remove extra keys)"
echo " clean Clean unused translation keys"
echo " sync Sync keys across all language files"
echo " status Show translation status"
echo ""
echo "Options:"
echo " -l, --lang LANG Specify language (e.g.: zh_CN)"
echo " -t, --trans-dir DIR Translation files directory (default: $TRANSLATIONS_DIR)"
echo " -s, --source-dir DIR Source code directory (default: $SOURCE_DIR)"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 extract # Extract translatable texts"
echo " $0 update -l zh_CN # Update Chinese translations"
echo " $0 update # Update all translations"
echo " $0 clean # Clean unused keys"
echo " $0 sync # Sync keys across all languages"
echo " $0 status # Show translation status"
}
show_status() {
echo "Analyzing translation status..."
# Extract current text count
echo "=== Current Project Status ==="
python3 "$SCRIPT_DIR/translation-manager.py" \
--translations-dir "$TRANSLATIONS_DIR" \
--source-dir "$SOURCE_DIR" \
--extract-only | grep "Extracted"
echo ""
echo "=== Translation File Status ==="
if [ -d "$TRANSLATIONS_DIR" ]; then
for file in "$TRANSLATIONS_DIR"/*.json; do
if [ -f "$file" ]; then
lang=$(basename "$file" .json)
count=$(jq 'length' "$file" 2>/dev/null || echo "error")
echo " $lang: $count keys"
fi
done
else
echo " Translation directory does not exist: $TRANSLATIONS_DIR"
fi
}
# Parse command line arguments
LANG_CODE=""
COMMAND=""
while [[ $# -gt 0 ]]; do
case $1 in
-l|--lang)
LANG_CODE="$2"
shift 2
;;
-t|--trans-dir)
TRANSLATIONS_DIR="$2"
shift 2
;;
-s|--source-dir)
SOURCE_DIR="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
extract|update|clean|sync|status)
if [ -n "$COMMAND" ]; then
echo "Error: Only one command can be specified"
exit 1
fi
COMMAND="$1"
shift
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
if [ -z "$COMMAND" ]; then
echo "Error: A command must be specified"
show_help
exit 1
fi
# Check dependencies
if ! command -v python3 >/dev/null 2>&1; then
echo "Error: python3 is required"
exit 1
fi
if [ "$COMMAND" = "status" ] && ! command -v jq >/dev/null 2>&1; then
echo "Warning: jq is not installed, status display may be incomplete"
fi
# Build base arguments
BASE_ARGS="--translations-dir $TRANSLATIONS_DIR --source-dir $SOURCE_DIR"
case $COMMAND in
extract)
echo "Extracting translatable texts..."
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --extract-only --show-temp
;;
update)
echo "Updating translation files..."
if [ -n "$LANG_CODE" ]; then
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --language "$LANG_CODE"
else
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS
fi
;;
clean)
echo "Cleaning unused translation keys..."
python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --clean
;;
sync)
echo "Syncing translation keys..."
python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --sync
;;
status)
show_status
;;
*)
echo "Unknown command: $COMMAND"
show_help
exit 1
;;
esac
@@ -0,0 +1,196 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Translation File Maintenance Helper
Used to clean and organize translation files, removing unused keys
"""
import os
import sys
import json
import argparse
import importlib.util
from pathlib import Path
from typing import Dict, Set, List
# Import from the same directory using importlib
current_dir = os.path.dirname(os.path.abspath(__file__))
manager_path = os.path.join(current_dir, 'translation-manager.py')
spec = importlib.util.spec_from_file_location("translation_manager", manager_path)
translation_manager = importlib.util.module_from_spec(spec)
spec.loader.exec_module(translation_manager)
TranslationManager = translation_manager.TranslationManager
def clean_translation_files(translations_dir: str, source_dir: str, backup: bool = True):
"""Clean translation files by removing unused keys"""
print("Starting translation file cleanup...")
# Create manager
manager = TranslationManager(translations_dir, source_dir)
# Extract currently used texts
print("Extracting currently used translatable texts...")
current_texts = manager.extract_translatable_texts()
print(f"Extracted {len(current_texts)} currently used texts")
# Get all language files
languages = manager.get_available_languages()
if not languages:
print("No translation files found")
return
print(f"Found language files: {', '.join(languages)}")
total_removed = 0
for lang in languages:
print(f"\nProcessing language: {lang}")
# Load translation file
translations = manager.load_translation_file(lang)
original_count = len(translations)
if backup:
# Create backup
backup_file = Path(translations_dir) / f"{lang}.json.backup"
with open(backup_file, 'w', encoding='utf-8') as f:
json.dump(translations, f, ensure_ascii=False, indent=2)
print(f"Created backup: {backup_file}")
# Find unused keys
unused_keys = set(translations.keys()) - current_texts
if unused_keys:
print(f"Found {len(unused_keys)} unused keys:")
for i, key in enumerate(sorted(unused_keys)[:10], 1): # Only show first 10
print(f" {i}. \"{key[:50]}{'...' if len(key) > 50 else ''}\"")
if len(unused_keys) > 10:
print(f" ... and {len(unused_keys) - 10} more keys")
response = input(f"Delete these {len(unused_keys)} unused keys? (y/n): ")
if response.lower().strip() in ['y', 'yes']:
# Delete unused keys
for key in unused_keys:
del translations[key]
# Save cleaned file
manager.save_translation_file(lang, translations)
removed_count = len(unused_keys)
total_removed += removed_count
print(f"Deleted {removed_count} keys")
else:
print("Skipped deletion")
else:
print("No unused keys found")
new_count = len(translations)
print(f"Original key count: {original_count}, after cleanup: {new_count}")
print(f"\nCleanup completed! Total deleted {total_removed} unused keys.")
def sync_translations(translations_dir: str, source_lang: str = "en_US", target_langs: List[str] = None):
"""Sync translation keys to ensure all language files have the same keys"""
print(f"Starting translation key sync using {source_lang} as reference...")
translations_path = Path(translations_dir)
# Load source language file
source_file = translations_path / f"{source_lang}.json"
if not source_file.exists():
print(f"Error: Source language file does not exist: {source_file}")
return
with open(source_file, 'r', encoding='utf-8') as f:
source_translations = json.load(f)
source_keys = set(source_translations.keys())
print(f"Source language {source_lang} has {len(source_keys)} keys")
# Get target language list
if target_langs is None:
target_langs = []
for file_path in translations_path.glob("*.json"):
lang_code = file_path.stem
if lang_code != source_lang:
target_langs.append(lang_code)
if not target_langs:
print("No target language files found")
return
print(f"Target languages: {', '.join(target_langs)}")
for target_lang in target_langs:
print(f"\nSyncing language: {target_lang}")
target_file = translations_path / f"{target_lang}.json"
if target_file.exists():
with open(target_file, 'r', encoding='utf-8') as f:
target_translations = json.load(f)
else:
target_translations = {}
target_keys = set(target_translations.keys())
# Find missing and extra keys
missing_keys = source_keys - target_keys
extra_keys = target_keys - source_keys
print(f" Missing keys: {len(missing_keys)}")
print(f" Extra keys: {len(extra_keys)}")
# Add missing keys
if missing_keys:
for key in missing_keys:
# Use source language value as placeholder by default
target_translations[key] = source_translations[key]
print(f" Added {len(missing_keys)} missing keys")
# Ask whether to delete extra keys
if extra_keys:
response = input(f" Delete {len(extra_keys)} extra keys? (y/n): ")
if response.lower().strip() in ['y', 'yes']:
for key in extra_keys:
del target_translations[key]
print(f" Deleted {len(extra_keys)} extra keys")
# Save file
with open(target_file, 'w', encoding='utf-8') as f:
json.dump(target_translations, f, ensure_ascii=False, indent=2)
print(f" Saved: {target_file}")
def main():
parser = argparse.ArgumentParser(description="Translation File Maintenance Helper")
parser.add_argument("--translations-dir", "-t",
default=".config/quickshell/translations",
help="Translation files directory")
parser.add_argument("--source-dir", "-s",
default=".config/quickshell",
help="Source code directory")
parser.add_argument("--clean", "-c", action="store_true",
help="Clean unused translation keys")
parser.add_argument("--sync", action="store_true",
help="Sync translation keys")
parser.add_argument("--source-lang", default="en_US",
help="Source language for syncing (default: en_US)")
parser.add_argument("--no-backup", action="store_true",
help="Do not create backup files when cleaning")
args = parser.parse_args()
# Convert to absolute paths
translations_dir = os.path.abspath(args.translations_dir)
source_dir = os.path.abspath(args.source_dir)
if args.clean:
clean_translation_files(translations_dir, source_dir, backup=not args.no_backup)
elif args.sync:
sync_translations(translations_dir, args.source_lang)
else:
print("Please specify an operation:")
print(" --clean: Clean unused translation keys")
print(" --sync: Sync translation keys")
if __name__ == "__main__":
main()
@@ -0,0 +1,290 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Translation File Management Script
Used to update and extract translatable texts, manage JSON translation file key comparison
"""
import os
import json
import re
import sys
import argparse
from pathlib import Path
from typing import Dict, Set, List, Tuple
import tempfile
import subprocess
class TranslationManager:
def __init__(self, translations_dir: str, source_dir: str):
self.translations_dir = Path(translations_dir)
self.source_dir = Path(source_dir)
self.temp_extracted_file = None
# Ensure translation directory exists
self.translations_dir.mkdir(parents=True, exist_ok=True)
def extract_translatable_texts(self) -> Set[str]:
"""Extract translatable texts from source code"""
translatable_texts = set()
# Search patterns: Translation.tr("text") or Translation.tr('text')
# Improved regex that handles nested quotes correctly
patterns = [
r'Translation\.tr\s*\(\s*(["\'])(((?!\1)[^\\]|\\.)*)(\1)\s*\)', # Double or single quotes with escape support
r'Translation\.tr\s*\(\s*`([^`]*(?:\\.[^`]*)*?)`\s*\)', # Backticks (template strings)
]
# Search all .qml and .js files
file_extensions = ['*.qml', '*.js']
for ext in file_extensions:
for file_path in self.source_dir.rglob(ext):
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)
for match in matches:
# Handle different match group structures
if isinstance(match, tuple):
# For improved regex, text is in the second group
if len(match) >= 3:
text = match[1] # Second group is the text content
else:
text = match[0] if match else ""
else:
text = match
# Decode escape characters
try:
clean_text = text.encode().decode('unicode_escape')
except:
clean_text = text
# Clean text (remove extra whitespace)
clean_text = clean_text.strip()
if clean_text:
translatable_texts.add(clean_text)
except (UnicodeDecodeError, IOError) as e:
print(f"Warning: Cannot read file {file_path}: {e}")
return translatable_texts
def create_temp_translation_file(self, texts: Set[str]) -> str:
"""Create temporary JSON file containing extracted texts"""
temp_data = {}
for text in sorted(texts):
temp_data[text] = text # Key and value are the same, indicating untranslated
# Create temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f:
json.dump(temp_data, f, ensure_ascii=False, indent=2)
self.temp_extracted_file = f.name
return self.temp_extracted_file
def load_translation_file(self, lang_code: str) -> Dict[str, str]:
"""Load translation file for specified language"""
file_path = self.translations_dir / f"{lang_code}.json"
if file_path.exists():
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
print(f"Warning: Cannot load translation file {file_path}: {e}")
return {}
return {}
def save_translation_file(self, lang_code: str, translations: Dict[str, str]):
"""Save translation file"""
file_path = self.translations_dir / f"{lang_code}.json"
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(translations, f, ensure_ascii=False, indent=2)
print(f"Translation file saved: {file_path}")
except IOError as e:
print(f"Error: Cannot save translation file {file_path}: {e}")
def get_available_languages(self) -> List[str]:
"""Get list of available languages"""
languages = []
for file_path in self.translations_dir.glob("*.json"):
lang_code = file_path.stem
languages.append(lang_code)
return sorted(languages)
def compare_translations(self, extracted_texts: Set[str], target_lang: str) -> Tuple[Set[str], Set[str]]:
"""Compare extracted texts with existing translation file"""
existing_translations = self.load_translation_file(target_lang)
existing_keys = set(existing_translations.keys())
missing_keys = extracted_texts - existing_keys # Missing keys
extra_keys = existing_keys - extracted_texts # Extra keys
return missing_keys, extra_keys
def interactive_update(self, lang_code: str, missing_keys: Set[str], extra_keys: Set[str]):
"""Interactively update translation file"""
translations = self.load_translation_file(lang_code)
modified = False
# Handle missing keys
if missing_keys:
print(f"\nFound {len(missing_keys)} missing translation keys:")
for i, key in enumerate(sorted(missing_keys), 1):
print(f"{i}. \"{key}\"")
if self.ask_yes_no(f"\nAdd these {len(missing_keys)} missing keys?"):
for key in missing_keys:
translations[key] = key # Default value is the key itself
modified = True
print(f"Added {len(missing_keys)} keys")
# Handle extra keys
if extra_keys:
print(f"\nFound {len(extra_keys)} extra translation keys:")
for i, key in enumerate(sorted(extra_keys), 1):
print(f"{i}. \"{key}\" -> \"{translations.get(key, '')}\"")
if self.ask_yes_no(f"\nDelete these {len(extra_keys)} extra keys?"):
for key in extra_keys:
if key in translations:
del translations[key]
modified = True
print(f"Deleted {len(extra_keys)} keys")
# Save changes
if modified:
self.save_translation_file(lang_code, translations)
else:
print("No changes made")
def ask_yes_no(self, question: str) -> bool:
"""Ask user for confirmation"""
while True:
response = input(f"{question} (y/n): ").lower().strip()
if response in ['y', 'yes']:
return True
elif response in ['n', 'no']:
return False
else:
print("Please enter y/yes or n/no")
def cleanup(self):
"""Clean up temporary files"""
if self.temp_extracted_file and os.path.exists(self.temp_extracted_file):
os.unlink(self.temp_extracted_file)
def main():
parser = argparse.ArgumentParser(description="Translation file management tool")
parser.add_argument("--translations-dir", "-t",
default=".config/quickshell/translations",
help="Translation files directory (default: .config/quickshell/translations)")
parser.add_argument("--source-dir", "-s",
default=".config/quickshell",
help="Source code directory (default: .config/quickshell)")
parser.add_argument("--language", "-l",
help="Specify language code to process (e.g., zh_CN)")
parser.add_argument("--extract-only", "-e", action="store_true",
help="Only extract translatable texts to temporary file")
parser.add_argument("--show-temp", action="store_true",
help="Show temporary extracted file content")
args = parser.parse_args()
# Convert to absolute paths
translations_dir = os.path.abspath(args.translations_dir)
source_dir = os.path.abspath(args.source_dir)
print(f"Translation directory: {translations_dir}")
print(f"Source code directory: {source_dir}")
# Check if directories exist
if not os.path.exists(source_dir):
print(f"Error: Source code directory does not exist: {source_dir}")
sys.exit(1)
# Create manager
manager = TranslationManager(translations_dir, source_dir)
try:
# Extract translatable texts
print("\nExtracting translatable texts...")
extracted_texts = manager.extract_translatable_texts()
print(f"Extracted {len(extracted_texts)} translatable texts")
# Create temporary file
temp_file = manager.create_temp_translation_file(extracted_texts)
print(f"Created temporary file: {temp_file}")
if args.show_temp:
print("\nTemporary file contents:")
with open(temp_file, 'r', encoding='utf-8') as f:
print(f.read())
if args.extract_only:
print("Extract-only mode, program finished")
return
# Get available languages
available_languages = manager.get_available_languages()
if args.language:
target_languages = [args.language]
else:
print(f"\nAvailable languages: {', '.join(available_languages) if available_languages else 'None'}")
if not available_languages:
print("No existing translation files found")
lang_input = input("Enter language code to create (e.g.: zh_CN): ").strip()
if lang_input:
target_languages = [lang_input]
else:
print("No language specified, program finished")
return
else:
print("Choose language to process:")
for i, lang in enumerate(available_languages, 1):
print(f"{i}. {lang}")
print("a. Process all languages")
choice = input("Please choose (enter number, language code, or 'a'): ").strip()
if choice.lower() == 'a':
target_languages = available_languages
elif choice.isdigit() and 1 <= int(choice) <= len(available_languages):
target_languages = [available_languages[int(choice) - 1]]
elif choice in available_languages:
target_languages = [choice]
else:
print("Invalid choice, program finished")
return
# Process each language
for lang in target_languages:
print(f"\n{'='*50}")
print(f"Processing language: {lang}")
print('='*50)
missing_keys, extra_keys = manager.compare_translations(extracted_texts, lang)
if not missing_keys and not extra_keys:
print(f"Translation file for language {lang} is already up to date")
continue
print(f"Analysis results:")
print(f" Missing keys: {len(missing_keys)}")
print(f" Extra keys: {len(extra_keys)}")
if missing_keys or extra_keys:
manager.interactive_update(lang, missing_keys, extra_keys)
finally:
# Clean up temporary files
manager.cleanup()
if __name__ == "__main__":
main()
@@ -0,0 +1,246 @@
# Translation Management Tool Suite
This tool suite is used to manage project translation files, automatically extract translatable texts, compare differences between different language files, and provide maintenance functions.
## Tool Components
### 1. `translation-manager.py` - Main Translation Manager
- Extract translatable texts
- Compare and update translation files
- Interactive adding/removing translation keys
### 2. `translation-cleaner.py` - Translation File Maintenance Tool
- Clean unused translation keys
- Sync key structure across different language files
### 3. `manage-translations.sh` - Convenient Wrapper Script
- Provide unified command-line interface
- Display translation status
- Simplify common operations
## Quick Start
### Check Translation Status
```bash
./manage-translations.sh status
```
### Extract Translatable Texts
```bash
./manage-translations.sh extract
```
### Update Translation Files
```bash
# Update all languages
./manage-translations.sh update
# Update specific language
./manage-translations.sh update -l zh_CN
```
### Clean Unused Keys
```bash
./manage-translations.sh clean
```
### Sync Keys Across Languages
```bash
./manage-translations.sh sync
```
## Detailed Usage
### translation-manager.py
The main translation management tool that extracts translatable texts from source code and manages translation files.
#### Command Line Options
```bash
python3 translation-manager.py [options]
Options:
-h, --help Show help message
-t, --translations-dir DIR Translation files directory (default: .config/quickshell/translations)
-s, --source-dir DIR Source code directory (default: .config/quickshell)
-l, --language LANG Specify language code to process (e.g., zh_CN)
-e, --extract-only Only extract translatable texts to temporary file
--show-temp Show temporary extracted file content
```
#### Features
1. **Text Extraction**: Uses regex patterns to extract translatable texts from QML and JavaScript files
2. **Smart Filtering**: Automatically removes duplicates and cleans up extracted texts
3. **Interactive Updates**: Guides users through adding missing keys and removing extra ones
4. **Backup Support**: Creates backups before making changes
#### Supported Text Patterns
- `qsTr("text")` and `qsTr('text')`
- `i18n.t("text")` and `i18n.t('text')`
- Supports nested quotes and escape characters
- Handles multiline strings
### translation-cleaner.py
A maintenance tool for cleaning up and synchronizing translation files.
#### Command Line Options
```bash
python3 translation-cleaner.py [options]
Options:
-h, --help Show help message
-t, --translations-dir DIR Translation files directory
-s, --source-dir DIR Source code directory
-c, --clean Clean unused translation keys
--sync Sync translation keys
--source-lang LANG Source language for syncing (default: en_US)
--no-backup Do not create backup files when cleaning
```
#### Features
1. **Unused Key Cleanup**: Identifies and removes translation keys that are no longer used in the source code
2. **Key Synchronization**: Ensures all language files have the same set of keys
3. **Backup Protection**: Creates backup files before making destructive changes
4. **Interactive Confirmation**: Asks for user confirmation before deleting keys
### manage-translations.sh
A convenient wrapper script that provides a unified interface to all translation tools.
#### Commands
```bash
./manage-translations.sh [options] <command>
Commands:
extract Extract translatable texts to temporary file
update Update translation files (add missing/remove extra keys)
clean Clean unused translation keys
sync Sync keys across all language files
status Show translation status
Options:
-l, --lang LANG Specify language (e.g.: zh_CN)
-t, --trans-dir DIR Translation files directory
-s, --source-dir DIR Source code directory
-h, --help Show help message
```
## Workflow Examples
### Initial Setup
1. Create translation directory structure
2. Extract all translatable texts: `./manage-translations.sh extract`
3. Create initial translation files: `./manage-translations.sh update`
### Regular Maintenance
1. Check status: `./manage-translations.sh status`
2. Update translations after code changes: `./manage-translations.sh update`
3. Clean up unused keys periodically: `./manage-translations.sh clean`
### Adding New Language
1. Create new language file: `./manage-translations.sh update -l new_lang`
2. Sync keys if needed: `./manage-translations.sh sync`
## File Structure
```
translations/
├── tools/ # Translation management tools
│ ├── translation-manager.py # Main extraction and update tool
│ ├── translation-cleaner.py # Cleanup and sync tool
│ ├── manage-translations.sh # Wrapper script
│ └── translation-tools-guide.md # This documentation
├── en_US.json # English translations (reference)
├── zh_CN.json # Chinese translations
└── [other_lang].json # Other language files
```
## Configuration
### Text Extraction Patterns
The tool uses regex patterns to identify translatable texts. Current patterns include:
1. **QML qsTr patterns**:
- `qsTr("text")` and `qsTr('text')`
- Supports escaped quotes and nested quotes
2. **JavaScript i18n patterns**:
- `i18n.t("text")` and `i18n.t('text')`
- Supports template literals and complex expressions
3. **Custom patterns** can be added by modifying the patterns list in `translation-manager.py`
### File Extensions
By default, the tool processes:
- `.qml` files (QML/QtQuick)
- `.js` files (JavaScript)
Additional file types can be added by modifying the file extension filters.
## Best Practices
1. **Regular Updates**: Run `./manage-translations.sh status` regularly to check for new translatable texts
2. **Clean Periodically**: Use `./manage-translations.sh clean` to remove unused keys
3. **Backup Important**: Always backup translation files before major changes
4. **Consistent Patterns**: Use consistent function calls (`qsTr`, `i18n.t`) for translatable texts
5. **Review Changes**: Always review the changes before confirming deletions or additions
## Troubleshooting
### Common Issues
1. **Missing Texts**: If some translatable texts are not extracted, check if they match the supported patterns
2. **Path Issues**: Ensure the source and translation directory paths are correct
3. **Permission Errors**: Make sure the script has write permissions to the translation directory
4. **Encoding Issues**: All files should be saved in UTF-8 encoding
### Getting Help
For additional help:
- Use `--help` option with any tool
- Check the console output for error messages
- Verify file paths and permissions
## Advanced Usage
### Custom Regex Patterns
To add support for new translation function patterns, modify the `patterns` list in `TranslationManager.extract_translatable_texts()`:
```python
patterns = [
# Existing patterns...
r'customTranslate\s*\(\s*(["\'])((?:\\.|(?!\1)[^\\])*?)\1\s*\)', # Custom pattern
]
```
### Batch Processing
For processing multiple projects or directories:
```bash
# Process multiple directories
for dir in project1 project2 project3; do
./manage-translations.sh -s "$dir" -t "$dir/translations" update
done
```
### Integration with Build Systems
The tools can be integrated into build systems to automatically update translations:
```bash
# In your build script
./manage-translations.sh update --non-interactive
```
## Version History
- **v1.0**: Initial version with basic extraction and update functionality
- **v1.1**: Added improved regex patterns for better text extraction
- **v1.2**: Added cleaning and synchronization tools
- **v1.3**: Added English output and improved user interface
- **v1.4**: Moved all tools to dedicated tools directory and improved documentation
@@ -0,0 +1,287 @@
# 翻译管理工具套件
这套工具用于管理项目的翻译文件,自动提取可翻译文本,比较不同语言文件之间的差异,并提供维护功能。
## 工具组成
### 1. `translation-manager.py` - 主要翻译管理器
- 提取可翻译文本
- 比较和更新翻译文件
- 交互式添加/删除翻译键
### 2. `translation-cleaner.py` - 翻译文件维护工具
- 清理不再使用的翻译键
- 同步不同语言文件的键结构
### 3. `manage-translations.sh` - 便捷包装脚本
- 提供统一的命令行界面
- 显示翻译状态
- 简化常用操作
## 快速开始
### 使用便捷脚本(推荐)
```bash
# 进入工具目录
cd .config/quickshell/translations/tools
# 查看帮助
./manage-translations.sh --help
# 显示当前翻译状态
./manage-translations.sh status
# 提取可翻译文本
./manage-translations.sh extract
# 更新所有翻译文件
./manage-translations.sh update
# 更新特定语言
./manage-translations.sh update -l zh_CN
# 清理不再使用的键
./manage-translations.sh clean
# 同步所有语言文件的键
./manage-translations.sh sync
```
或者从项目根目录运行:
```bash
# 从项目根目录运行
.config/quickshell/translations/tools/manage-translations.sh status
.config/quickshell/translations/tools/manage-translations.sh update
```
## 详细使用说明
### 翻译管理器 (`translation-manager.py`)
基本用法:
```bash
# 处理所有语言
./translation-manager.py
# 指定特定语言
./translation-manager.py --language zh_CN
# 仅提取可翻译文本
./translation-manager.py --extract-only
# 显示提取的文本
./translation-manager.py --extract-only --show-temp
```
参数说明:
- `--translations-dir`, `-t`: 翻译文件目录(默认:`.config/quickshell/translations`
- `--source-dir`, `-s`: 源代码目录(默认:`.config/quickshell`
- `--language`, `-l`: 指定要处理的语言代码
- `--extract-only`, `-e`: 仅提取可翻译文本
- `--show-temp`: 显示临时提取文件的内容
### 翻译清理器 (`translation-cleaner.py`)
```bash
# 清理不再使用的翻译键
./translation-cleaner.py --clean
# 同步翻译键(以 en_US 为基准)
./translation-cleaner.py --sync
# 指定不同的源语言进行同步
./translation-cleaner.py --sync --source-lang zh_CN
# 清理时不创建备份
./translation-cleaner.py --clean --no-backup
```
## 工作流程
### 日常翻译更新流程
1. **检查状态**
```bash
./manage-translations.sh status
```
2. **更新翻译**
```bash
./manage-translations.sh update
```
3. **清理无用键**(可选):
```bash
./manage-translations.sh clean
```
### 新增语言流程
1. **创建新语言文件**
```bash
./manage-translations.sh update -l new_lang
```
2. **同步键结构**
```bash
./manage-translations.sh sync
```
### 大规模重构后的清理流程
1. **备份翻译文件**
```bash
cp -r .config/quickshell/translations .config/quickshell/translations.backup
```
2. **清理无用键**
```bash
./manage-translations.sh clean
```
3. **同步所有语言**
```bash
./manage-translations.sh sync
```
## 支持的翻译文本格式
工具可以识别以下格式的可翻译文本:
```qml
// 基本格式
Translation.tr("Hello, world!")
Translation.tr('Hello, world!')
Translation.tr(`Hello, world!`)
// 带换行符
Translation.tr("Line 1\nLine 2")
// 带转义字符
Translation.tr("Say \"Hello\"")
// 带参数占位符
Translation.tr("Hello, %1!").arg(name)
Translation.tr("{0} files selected").arg(count)
```
## 示例输出
### 状态显示
```
$ ./manage-translations.sh status
正在分析翻译状态...
=== 当前项目状态 ===
提取到 166 个可翻译文本
=== 翻译文件状态 ===
en_US: 470 个键
zh_CN: 470 个键
```
### 更新翻译
```
$ ./manage-translations.sh update -l zh_CN
更新翻译文件...
==================================================
处理语言: zh_CN
==================================================
分析结果:
缺少的键: 5
多余的键: 20
发现 5 个缺少的翻译键:
1. "New feature text"
2. "Another new text"
...
是否添加这 5 个缺少的键? (y/n): y
已添加 5 个键
发现 20 个多余的翻译键:
1. "Removed old text" -> "已删除的旧文本"
...
是否删除这 20 个多余的键? (y/n): y
已删除 20 个键
已保存翻译文件
```
### 清理无用键
```
$ ./manage-translations.sh clean
清理不再使用的翻译键...
处理语言: zh_CN
发现 50 个不再使用的键:
1. "old_unused_text"
2. "deprecated_message"
...
是否删除这 50 个不再使用的键? (y/n): y
已删除 50 个键
原始键数: 470, 清理后: 420
```
## 高级功能
### 自定义目录结构
```bash
# 使用自定义目录
./translation-manager.py \
--translations-dir /path/to/translations \
--source-dir /path/to/source
```
### 批量处理
```bash
# 创建处理脚本
cat > update-all-translations.sh << 'EOF'
#!/bin/bash
for lang in zh_CN ja_JP ko_KR; do
echo "Processing $lang..."
./manage-translations.sh update -l $lang
done
EOF
chmod +x update-all-translations.sh
./update-all-translations.sh
```
## 注意事项
1. **备份重要**:在执行清理操作前,工具会自动创建备份,但建议手动备份重要文件
2. **文本提取限制**
- 只支持静态字符串,不支持动态构建的字符串
- 必须使用 `Translation.tr()` 格式
3. **文件编码**:所有文件必须使用 UTF-8 编码
4. **键名规范**:建议使用英文作为键名,避免使用特殊字符
## 故障排除
### 常见问题
**Q: 提取的文本数量与预期不符?**
A: 检查是否所有可翻译文本都使用了 `Translation.tr()` 格式,确保没有动态构建的字符串。
**Q: 同步后某些翻译丢失?**
A: 检查源语言文件是否包含所有必要的键,考虑使用不同的源语言进行同步。
**Q: 清理操作删除了需要的键?**
A: 从自动创建的备份文件中恢复,检查源代码中是否正确使用了 `Translation.tr()`。
### 恢复备份
```bash
# 恢复单个文件
cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translations/zh_CN.json
# 恢复所有文件
cp .config/quickshell/translations.backup/* .config/quickshell/translations/
```