Skip to content

Commit a7a5088

Browse files
Update options
1 parent 39d30ae commit a7a5088

3 files changed

Lines changed: 107 additions & 15 deletions

File tree

src/main/TRANSLATION_README.md

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ python batch_translate.py glossary-sync --target-langs es pt # Export gl
9090
**Step-by-Step:**
9191
```bash
9292
python batch_translate.py sync --source-dir en/travel --target-langs es pt # Extract changed files
93+
python batch_translate.py sync --target-langs it --force-incomplete # Force extraction for incomplete languages
9394
python batch_translate.py validate-csv # Pre-import validation
9495
python batch_translate.py import # Import validated CSVs
9596
python batch_translate.py generate --target-langs es --require-complete # Generate HTML
@@ -130,10 +131,20 @@ GLOSSARY_DIR = "../photography"
130131

131132
Pipeline Options:
132133
```bash
133-
--target-langs es pt # Languages to process
134-
--auto-import # Skip "Import now?" prompt, auto-import if valid
135-
--skip-generation # Stop after import, don't generate HTML
136-
--force # Force re-extract all files (ignore change detection)
134+
--target-langs es pt # Languages to process
135+
--auto-import # Skip "Import now?" prompt, auto-import if valid
136+
--skip-generation # Stop after import, don't generate HTML
137+
--force # Force re-extract all files (ignore change detection)
138+
```
139+
140+
Sync Options:
141+
```bash
142+
--target-langs es pt # Languages to process
143+
--source-dir PATH # Source directory (default: en/photography)
144+
--workers N # Parallel workers (default: 4)
145+
--force # Force re-extract all files (ignore change detection)
146+
--force-incomplete # Force extraction for incomplete languages (below coverage threshold)
147+
--coverage-threshold PCT # Coverage %% below which --force-incomplete triggers (default: 80.0)
137148
```
138149

139150
## Path Mappings (Required for Generation)
@@ -210,7 +221,14 @@ Output: Detailed pipeline report with all metrics
210221

211222
1. **Extract Changed Files**:
212223
```bash
224+
# Normal sync: Extract only changed files (uses SHA256 change detection)
225+
python batch_translate.py sync --source-dir en/travel --target-langs es pt
226+
227+
# Force sync: Extract all files regardless of changes
213228
python batch_translate.py sync --source-dir en/travel --target-langs es pt --force
229+
230+
# Force incomplete: Extract all files for languages below coverage threshold (useful for new languages)
231+
python batch_translate.py sync --target-langs it --force-incomplete --coverage-threshold 80.0
214232
```
215233
Output: CSVs in `translations_pending/`
216234

@@ -510,8 +528,31 @@ Glossaries include context awareness via suggestion system.
510528

511529
**Pipeline stops at import prompt**: Type "yes" to import, "no" to review CSVs first
512530

531+
**"No changes detected" error on new language**: Use `--force-incomplete` flag
532+
```bash
533+
python batch_translate.py sync --target-langs it --force-incomplete
534+
# This forces extraction even when source files haven't changed
535+
# Useful for new languages with low coverage
536+
```
537+
513538
## Advanced Workflows
514539

540+
**New language onboarding (incomplete translation coverage):**
541+
```bash
542+
# When adding a new language with low coverage, use --force-incomplete
543+
# Example: Italian language only has 32% coverage, normal sync would skip it
544+
python batch_translate.py sync --target-langs it --force-incomplete
545+
# Output:
546+
# [WARNING] [SYNC] it: coverage 32.0% < 80.0% => forcing extraction
547+
# [INFO] Changed files: 0
548+
# [INFO] Files extracted: 150
549+
# [INFO] Translations: 3520
550+
551+
# You can customize the coverage threshold:
552+
python batch_translate.py sync --target-langs it --force-incomplete --coverage-threshold 95.0
553+
# Forces extraction if coverage is below 95%
554+
```
555+
515556
**Skip import, review only:**
516557
```bash
517558
python batch_translate.py pipeline --skip-generation
@@ -530,6 +571,19 @@ python batch_translate.py pipeline --force
530571
# Extracts all files, even unchanged ones
531572
```
532573

574+
**Mixed: Force incomplete + manual edits:**
575+
```bash
576+
# 1. Register manual edits done outside the pipeline
577+
python batch_translate.py register-manual-edit it/index.html --lang it
578+
579+
# 2. Force extraction for remaining untranslated strings
580+
python batch_translate.py sync --target-langs it --force-incomplete --coverage-threshold 95.0
581+
582+
# 3. Continue with normal pipeline workflow
583+
python batch_translate.py serve --port 8000 # Review in dashboard
584+
python batch_translate.py pipeline --target-langs it
585+
```
586+
533587
## Performance Notes
534588

535589
- **Sync Command**: ~20-30 files/second with 4 workers

src/main/batch_translate.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,11 +689,38 @@ def cmd_sync(args):
689689
print_info(f"Starting sync check (workers: {args.workers}, force: {args.force})")
690690
print_info(f"Source directory: {args.source_dir}")
691691

692+
# Compute per-language coverage if --force-incomplete is set
693+
force_langs = set()
694+
if getattr(args, 'force_incomplete', False):
695+
threshold = getattr(args, 'coverage_threshold', 80.0)
696+
from translate_manager import TranslationManager
697+
from pathlib import Path
698+
699+
tm = TranslationManager(source_lang=args.source_lang,
700+
target_langs=args.target_langs,
701+
db_path=args.db_path)
702+
source_files = [str(f) for f in Path(args.source_dir).rglob('*.html')]
703+
704+
for lang in args.target_langs:
705+
total_all, found_all = 0, 0
706+
for source_file in source_files:
707+
result_file = tm.process_file(source_file, lang, check_existing=False, force=True)
708+
total_all += result_file.get('total', 0)
709+
found_all += result_file.get('found', 0)
710+
711+
pct = (found_all / total_all * 100) if total_all > 0 else 0.0
712+
if pct < threshold:
713+
print_warning(f"[SYNC] {lang}: coverage {pct:.1f}% < {threshold}% => forcing extraction")
714+
force_langs.add(lang)
715+
else:
716+
print_info(f"[SYNC] {lang}: coverage {pct:.1f}% >= {threshold}% => hash-based sync")
717+
692718
result = sync.check_and_extract(
693719
args.source_dir,
694720
args.output_dir,
695721
target_langs=args.target_langs,
696-
force=args.force
722+
force=args.force,
723+
force_langs=force_langs
697724
)
698725

699726
if result['status'] == 'no_files':
@@ -1014,6 +1041,10 @@ def main():
10141041
help='Force extract all files, not just changed')
10151042
sync_parser.add_argument('--workers', type=int, default=4,
10161043
help='Number of parallel workers (default: 4)')
1044+
sync_parser.add_argument('--force-incomplete', action='store_true',
1045+
help='Force full extraction for languages below coverage threshold')
1046+
sync_parser.add_argument('--coverage-threshold', type=float, default=80.0,
1047+
help='Coverage %% below which --force-incomplete triggers (default: 80.0)')
10171048

10181049
# validate-csv command
10191050
validate_csv_parser = subparsers.add_parser('validate-csv', help='Validate CSV files for QA issues')

src/main/sync_manager.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def check_and_extract(
5656
source_dir: str,
5757
output_dir: str,
5858
target_langs: List[str] = None,
59-
force: bool = False
59+
force: bool = False,
60+
force_langs: set = None
6061
) -> Dict[str, any]:
6162
"""
6263
Check for changed source files and extract translations
@@ -74,19 +75,18 @@ def check_and_extract(
7475
'extracted_batches': {}
7576
}
7677

77-
# Determine which files changed
78+
# Determine which files changed (per-language or global)
79+
force_langs = force_langs or set()
80+
81+
# If force=True globally, extract all files for all langs
82+
# Otherwise check per-language via force_langs set
7883
if force:
7984
changed_files = all_files
8085
else:
86+
# For now, detect globally (if any lang needs changes, extract)
87+
# Per-language filtering happens below
8188
changed_files = self._detect_changed_files(all_files)
8289

83-
if not changed_files:
84-
return {
85-
'status': 'no_changes',
86-
'changed_files': [],
87-
'extracted_batches': {}
88-
}
89-
9090
# Update hashes for all files
9191
for file_path in all_files:
9292
hash_val = self._compute_file_hash(file_path)
@@ -108,7 +108,14 @@ def check_and_extract(
108108
'csv_files': []
109109
}
110110

111-
for file_path in changed_files:
111+
# Per-language force check: use full extraction if lang is in force_langs
112+
lang_force = force or (lang in force_langs)
113+
if lang_force:
114+
lang_files = all_files
115+
else:
116+
lang_files = changed_files
117+
118+
for file_path in lang_files:
112119
future = executor.submit(
113120
self._extract_and_prefill,
114121
file_path,

0 commit comments

Comments
 (0)