Update all translations (#7380)

# Description of Changes

Update all translations

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have run `task check` to verify linters, typechecks, and tests
pass
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.
This commit is contained in:
Anthony Stirling
2026-08-09 12:45:32 +01:00
committed by GitHub
parent c1275763af
commit faba9f53ea
42 changed files with 71936 additions and 28547 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+48 -16
View File
@@ -10,6 +10,7 @@ import sys
import argparse
import os
import subprocess
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import time
@@ -141,27 +142,39 @@ def extract_untranslated(language_code, batch_size=500, include_existing=False):
def translate_batches(
batch_files, language_code, api_key, timeout=600, model="gpt-5.5"
batch_files, language_code, api_key, timeout=600, model="gpt-5.5", parallel=1
):
"""Translate all batch files using the given OpenAI model."""
if not batch_files:
return []
print(f"\n🤖 Translating {len(batch_files)} batches using {model}...")
total = len(batch_files)
print(f"\n🤖 Translating {total} batches using {model}...")
print(f"Timeout: {timeout}s ({timeout // 60} minutes) per batch")
if parallel > 1:
print(f"Running up to {parallel} batches in parallel")
translated_files = []
def translate_one(numbered_batch):
i, batch_file = numbered_batch
translated_file = batch_file.replace(".json", "_translated.json")
for i, batch_file in enumerate(batch_files, 1):
print(f"\n[{i}/{len(batch_files)}] Translating {batch_file}...")
# Resume: an existing output means this batch is already paid for
if Path(translated_file).exists():
print(f"\n[{i}/{total}] ✓ {batch_file} already translated, skipping")
return translated_file
print(f"\n[{i}/{total}] Translating {batch_file}...")
# Always pass API key since it's required
cmd = f'python3 scripts/translations/batch_translator.py "{batch_file}" --language {language_code} --api-key "{api_key}" --model {model}'
# Run with timeout
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True, timeout=timeout
)
try:
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True, timeout=timeout
)
except subprocess.TimeoutExpired:
print(f"✗ Timed out after {timeout}s: {batch_file}", file=sys.stderr)
return None
if result.stdout:
print(result.stdout)
@@ -172,14 +185,27 @@ def translate_batches(
print(f"✗ Failed to translate {batch_file}")
return None
translated_file = batch_file.replace(".json", "_translated.json")
translated_files.append(translated_file)
return translated_file
# Small delay between batches
if i < len(batch_files):
time.sleep(1)
numbered = list(enumerate(batch_files, 1))
print(f"\n✓ All {len(batch_files)} batches translated successfully")
if parallel > 1:
with ThreadPoolExecutor(max_workers=parallel) as pool:
translated_files = list(pool.map(translate_one, numbered))
else:
translated_files = []
for i, numbered_batch in enumerate(numbered, 1):
translated_files.append(translate_one(numbered_batch))
# Small delay between batches
if i < total:
time.sleep(1)
if any(f is None for f in translated_files):
failed = sum(1 for f in translated_files if f is None)
print(f"\n{failed}/{total} batches failed")
return None
print(f"\n✓ All {total} batches translated successfully")
return translated_files
@@ -310,6 +336,12 @@ Examples:
action="store_true",
help="Also retranslate existing keys that match English (default: only translate missing keys)",
)
parser.add_argument(
"--parallel",
type=int,
default=1,
help="Batches to translate concurrently (default: 1 = sequential)",
)
parser.add_argument(
"--model",
default="gpt-5.5",
@@ -349,7 +381,7 @@ Examples:
# Step 2: Translate all batches
translated_files = translate_batches(
batch_files, args.language, api_key, args.timeout, args.model
batch_files, args.language, api_key, args.timeout, args.model, args.parallel
)
if translated_files is None:
sys.exit(1)