| Crates.io | ampel-i18n-builder |
| lib.rs | ampel-i18n-builder |
| version | 0.2.0 |
| created_at | 2026-01-18 00:17:08.483475+00 |
| updated_at | 2026-01-20 03:19:41.074228+00 |
| description | CLI tool and library for building i18n translations using AI providers |
| homepage | |
| repository | https://github.com/pacphi/ampel |
| max_upload_size | |
| id | 2051467 |
| size | 823,756 |
Translation file parser, validator, and code generator for Ampel internationalization system with enterprise-grade 4-tier translation provider architecture.
Never installed a Rust tool before? No problem! This tool is designed to be beginner-friendly.
If you have Claude Code CLI installed, follow these steps:
Step 1: Install the Claude Code skill (one-time setup)
# Option A: Install globally for all projects
cp -r skills/ampel-i18n ~/.claude/skills/
# Option B: Install for this project only
mkdir -p .claude/skills
cp -r skills/ampel-i18n .claude/skills/
Step 2: Restart Claude Code to recognize the new skill
Step 3: Use the skill
/ampel-i18n:localize
Help me install and set up ampel-i18n-builder. I want to translate my [React/Vue/Rust] app using [OpenAI/DeepL/Google].
Claude will:
That's it! Claude handles everything step-by-step.
Step 1: Install Rust (one-time setup)
# macOS/Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows: Download installer from https://rustup.rs/
Step 2: Install ampel-i18n-builder
cargo install ampel-i18n-builder
Step 3: Interactive Setup
ampel-i18n init
This wizard will:
Step 4: Run Your First Translation
ampel-i18n sync
Done! Your app is now multi-lingual. 🌍
For detailed installation help, see: skills/ampel-i18n/references/install-guide.md
button.saveChanges, error.invalidEmail)ampel-i18n init - No manual config neededampel-i18n doctor - Diagnose issues automaticallyAmpel i18n builder uses a 4-tier translation provider architecture with automatic fallback:
Tier 1: Systran - Enterprise neural MT (primary provider)
Tier 2: DeepL - High-quality European languages
Tier 3: Google Translate - Broad language coverage
Tier 4: OpenAI - Fallback for complex content
If Tier 1 (Systran) fails, the system automatically falls back to Tier 2 (DeepL), then Tier 3 (Google), then Tier 4 (OpenAI). This ensures 99.9% translation success rate.
Example Fallback Flow:
Systran (Tier 1) → Timeout
↓ Automatic Fallback
DeepL (Tier 2) → Success ✓
Option A: .env file (Recommended)
# Copy example file
cp .env.example .env
# Edit .env and add your API keys
# At least one provider key is required
SYSTRAN_API_KEY=your-systran-key # Tier 1 (recommended)
DEEPL_API_KEY=your-deepl-key # Tier 2
GOOGLE_API_KEY=your-google-key # Tier 3
OPENAI_API_KEY=your-openai-key # Tier 4 (optional, fallback)
Option B: System Environment Variables
# Set API keys (overrides .env file)
export SYSTRAN_API_KEY="your-systran-api-key"
export DEEPL_API_KEY="your-deepl-api-key"
export GOOGLE_API_KEY="your-google-api-key"
Configuration Precedence:
.env file.ampel-i18n.yaml configuration fileExtract from React/TypeScript:
# Extract strings from frontend code
cargo i18n extract \
--source frontend/src \
--patterns "*.tsx" "*.ts" \
--format json \
--output frontend/public/locales/en/extracted.json \
--merge
# Extract with semantic key generation
cargo i18n extract \
--source frontend/src/pages \
--patterns "*.tsx" \
--key-strategy semantic \
--namespace dashboard \
--output frontend/public/locales/en/dashboard.json
Extract from Rust:
# Extract error messages and UI strings from Rust
cargo i18n extract \
--source crates/ampel-api/src \
--patterns "*.rs" \
--format yaml \
--output crates/ampel-api/locales/en/errors.yaml \
--merge
Note: Currently supports React/TypeScript and Rust extraction. Java source code extraction is planned for a future release, but you can manually create .properties files and use the translation features.
Options:
--source: Source directories to scan (can specify multiple)--patterns: File patterns like *.tsx, *.rs, *.java--format: Output format (json, yaml, or properties)--key-strategy: Key generation strategy (semantic, hash, or incremental)--merge: Merge with existing translations (preserves existing keys)--dry-run: Preview extraction without writing files--namespace: Organize extracted keys under a namespaceWhat gets extracted:
<Button>Click me</Button><Input placeholder="Enter name" />, aria-label, title`Welcome, ${userName}!` (with variable detection)anyhow!("Auth failed"), #[error("Not found")]const error = "Invalid email";What gets skipped:
t('key'), t!("key")println!(), console.log()After extracting strings, automatically replace hardcoded text with i18n calls:
# Refactor a single file
cargo i18n refactor \
--target frontend/src/components/Button.tsx \
--mapping frontend/public/locales/en/extracted.json \
--namespace common
# Refactor entire directory
cargo i18n refactor \
--target frontend/src \
--mapping frontend/public/locales/en/dashboard.json \
--namespace dashboard \
--patterns "*.tsx" "*.ts"
# Preview changes without modifying files
cargo i18n refactor \
--target frontend/src \
--mapping frontend/public/locales/en/common.json \
--dry-run
# Refactor without creating backups
cargo i18n refactor \
--target crates/ampel-api/src \
--mapping crates/ampel-api/locales/en/errors.yaml \
--patterns "*.rs" \
--no-backup
Options:
--target: File or directory to refactor (required)--mapping: Translation mapping file from extract command (required)--namespace: Namespace for generated keys (default: "common")--patterns: File patterns to match (default: *.tsx, *.ts, *.rs)--dry-run: Preview changes without modifying files--no-backup: Skip creating backups (backups saved to .ampel-i18n-backups/)What it does:
t('key') calls (TypeScript/React)t!("key") macro calls (Rust)import { useTranslation } or use_t!)Example transformation:
// Before
<Button>Save Changes</Button>;
// After (using mapping: "Save Changes" → "button.saveChanges")
const { t } = useTranslation('common');
<Button>{t('button.saveChanges')}</Button>;
Automatic Mode (Recommended) - Uses all available providers with fallback:
# Automatic provider selection with fallback
cargo i18n translate --lang fi
# With custom timeout and batch size
cargo i18n translate --lang fi --timeout 60 --batch-size 50
Single Provider Mode - No fallback, uses specific provider:
# Use only DeepL (no fallback)
cargo i18n translate --lang fi --provider deepl --no-fallback
# Use only Google (no fallback)
cargo i18n translate --lang ar --provider google --no-fallback
Override Settings - CLI parameters override configuration:
# Override timeout for all providers
cargo i18n translate --lang fi --timeout 60
# Override batch size
cargo i18n translate --lang fi --batch-size 25
# Override retry attempts
cargo i18n translate --lang fi --max-retries 5
# Disable specific providers
cargo i18n translate --lang fi --disable-provider openai
# Check coverage (requires ≥95%)
cargo i18n validate \
--input-dir crates/ampel-api/locales \
--base-locale en \
--min-coverage 95
# Check for missing keys
cargo i18n validate \
--input-dir frontend/public/locales \
--check missing
# Check for variable mismatches
cargo i18n validate \
--check variables
# Generate TypeScript types (CLI command)
cargo i18n generate-types \
--output frontend/src/types/i18n.generated.ts \
--translation-dir frontend/public/locales
# Note: CLI only supports TypeScript generation
# For Rust types, use the library API (see "Usage as a Library" section)
translation:
# API Keys (or use environment variables)
systran_api_key: '${SYSTRAN_API_KEY}'
deepl_api_key: '${DEEPL_API_KEY}'
google_api_key: '${GOOGLE_API_KEY}'
openai_api_key: '${OPENAI_API_KEY}'
# Global Defaults
default_timeout_secs: 30
default_batch_size: 50
default_max_retries: 3
# Fallback Strategy
fallback:
skip_on_missing_key: true # Skip providers without API keys
stop_on_first_success: true # Don't try more providers after success
log_fallback_events: true # Log when falling back to next tier
See docs/localization/PROVIDER-CONFIGURATION.md for complete configuration guide.
translation:
providers:
systran:
enabled: true
priority: 1 # Tier 1 (highest)
timeout_secs: 45
max_retries: 3
batch_size: 50
rate_limit_per_sec: 100
deepl:
enabled: true
priority: 2 # Tier 2
timeout_secs: 30
max_retries: 3
batch_size: 50
rate_limit_per_sec: 10
google:
enabled: true
priority: 3 # Tier 3
timeout_secs: 30
max_retries: 3
batch_size: 100
rate_limit_per_sec: 100
openai:
enabled: true
priority: 4 # Tier 4 (fallback)
timeout_secs: 60
max_retries: 2
model: 'gpt-5-mini'
temperature: 0.3
use ampel_i18n_builder::formats::{TranslationFormat, JsonFormat, YamlFormat};
// Parse JSON
let format = JsonFormat::new();
let translations = format.parse(r#"{
"greeting": "Hello, {name}!",
"items": {
"one": "1 item",
"other": "{{count}} items"
}
}"#)?;
// Parse YAML
let format = YamlFormat::new();
let translations = format.parse(r#"
greeting: "Hello, {name}!"
items:
one: "1 item"
other: "{{count}} items"
"#)?;
use ampel_i18n_builder::validation::{CoverageValidator, MissingKeysValidator, Validator};
use std::collections::HashMap;
// Check coverage
let validator = CoverageValidator::new(
source_translations.clone(),
target_translations.clone(),
95.0 // Minimum coverage threshold
);
let result = validator.validate();
if !result.is_valid() {
for error in result.errors {
eprintln!("Validation error: {}", error);
}
}
// Check for missing keys
let validator = MissingKeysValidator::new(
source_translations,
target_translations,
"fi"
);
let result = validator.validate();
use ampel_i18n_builder::translator::{Translator, TranslationProvider};
use ampel_i18n_builder::config::Config;
use std::collections::HashMap;
use serde_json::json;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = Config::load()?;
let translator = Translator::new(TranslationProvider::DeepL, &config)?;
let mut texts = HashMap::new();
texts.insert("greeting".to_string(), json!("Hello, world!"));
texts.insert("farewell".to_string(), json!("Goodbye!"));
let translations = translator.translate_batch(&texts, "fi").await?;
println!("Finnish:");
println!(" greeting: {}", translations["greeting"]);
println!(" farewell: {}", translations["farewell"]);
Ok(())
}
use ampel_i18n_builder::codegen::{typescript::TypeScriptGenerator, rust::RustGenerator, CodeGenerator, GeneratorOptions};
use std::path::PathBuf;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Generate TypeScript types
let ts_generator = TypeScriptGenerator::new();
let options = GeneratorOptions {
pretty_print: true,
include_metadata: true,
split_by_namespace: false,
create_index: true,
};
let result = ts_generator.generate(
&translations,
"en",
&PathBuf::from("frontend/src/types"),
options.clone()
).await?;
println!("Generated {} TypeScript files", result.files_created.len());
// Generate Rust types
let rust_generator = RustGenerator::new();
let result = rust_generator.generate(
&translations,
"en",
&PathBuf::from("src/i18n"),
options
).await?;
println!("Generated {} Rust files", result.files_created.len());
Ok(())
}
ampel-i18n-builder/
├── src/
│ ├── translator/ # Translation providers (4-tier)
│ │ ├── mod.rs # TranslationService trait
│ │ ├── systran.rs # Systran API (Tier 1)
│ │ ├── deepl.rs # DeepL API (Tier 2)
│ │ ├── google.rs # Google Cloud (Tier 3)
│ │ ├── openai.rs # OpenAI API (Tier 4)
│ │ ├── router.rs # FallbackRouter orchestrator
│ │ └── cache.rs # Translation cache (LRU)
│ │
│ ├── formats/ # Format parsers
│ │ ├── yaml.rs # YAML parser (backend)
│ │ └── json.rs # JSON parser (frontend)
│ │
│ ├── validation/ # Validation suite
│ │ ├── coverage.rs # Coverage analysis
│ │ ├── missing.rs # Missing key detection
│ │ ├── duplicates.rs # Duplicate key detection
│ │ └── variables.rs # Variable consistency
│ │
│ ├── codegen/ # Code generators
│ │ ├── typescript.rs # TypeScript type definitions
│ │ └── rust.rs # Rust type definitions
│ │
│ ├── extraction/ # String extraction
│ │ ├── extractor.rs # Extraction engine
│ │ ├── typescript.rs # TypeScript/React extractor
│ │ ├── rust.rs # Rust extractor
│ │ ├── key_generator.rs # Key generation strategies
│ │ └── merger.rs # Translation merging
│ │
│ ├── refactor/ # Code refactoring
│ │ ├── typescript_oxc.rs # TypeScript refactoring
│ │ ├── rust_syn.rs # Rust refactoring
│ │ └── backup.rs # Backup management
│ │
│ ├── config.rs # Configuration management
│ └── cli/ # CLI interface
│ ├── translate.rs # Translation command
│ ├── validate.rs # Validation command
│ ├── generate_types.rs # Type generation command
│ ├── extract.rs # Extraction command
│ └── refactor.rs # Refactoring command
│
└── tests/
├── integration/ # Integration tests
│ ├── fallback_tests.rs # Fallback behavior tests
│ └── translation_api_tests.rs # Provider integration tests
└── fixtures/ # Test data
The 4-tier architecture provides coverage for 133+ languages:
| Provider | Languages | Best For |
|---|---|---|
| Systran (Tier 1) | 55+ | All languages with enterprise quality |
| DeepL (Tier 2) | 28 | European languages (de, fr, fi, sv, pl, cs, etc.) |
| Google (Tier 3) | 133+ | Asian/Middle Eastern (ar, th, vi, zh, ja, hi) |
| OpenAI (Tier 4) | All | Fallback for complex content and technical text |
The system selects providers based on:
Example:
# Unit tests
cargo test
# Integration tests (requires API keys)
DEEPL_API_KEY=xxx cargo test -- --ignored
# With logging
RUST_LOG=debug cargo test
cargo bench
cargo fmt
cargo clippy --all-targets --all-features -- -D warnings
See tests/integration/ for complete examples:
api_tests.rsformat_parser_tests.rsvalidation_tests.rscode_generation_tests.rsMIT OR Apache-2.0
Full Documentation: docs/localization/
Issues: File with [i18n] prefix
Questions: See DEVELOPER-GUIDE.md