Node.js CLI tool for managing i18n translation workflow in Angular projects using LLM (Large Language Models).
- Description
- Features
- Prerequisites
- Installation
- Project Structure
- Configuration
- Commands
- Workflow
- Supported LLM Providers
- Security
- Troubleshooting
Angular i18n Translator is a command-line tool that automates the translation process of XLF (XLIFF) files used in Angular's internationalization system. It uses OpenAI-compatible LLM language models to perform high-quality translations while respecting interpolations, placeholders, and ICU formats.
messages.xlf --> messages.csv --> batches/*.csv --> LLM Translation --> batches/translated/*.csv --> messages.translated.csv --> dist-i18n/*.xlf
- XLF to CSV Conversion: Transforms Angular XLF files to CSV format for easier editing and translation
- LLM Translation: Integration with multiple LLM providers (DeepSeek, OpenAI, Azure, Ollama, etc.)
- Batch Processing: Splits large translations into configurable batches to optimize API usage
- Sequential Language Processing: Languages are processed sequentially (one at a time) to avoid file conflicts between translated batches
- Parallel Batch Processing: Within each language, batches are processed in parallel according to the
concurrencyparameter - Complete Validation: Verifies interpolations, duplicate IDs, and translation coverage
- Automatic Retries: Exponential backoff system to handle network errors and rate limiting
- Structure Preservation: Keeps interpolations
{{variable}}, placeholders<x id="..."/>and ICU formats intact
- Node.js >= 18.0.0 (requires native
fetch) - API Key from a compatible LLM provider (DeepSeek, OpenAI, Azure OpenAI, etc.)
- Angular CLI (to extract i18n strings with
ng extract-i18n)
cd angular-i18n-translatornpm install# Copy the example file
cp .env.example .env
# Edit .env and add your API key
# LLM_API_KEY=your-api-key-hereEdit i18n.config.json as needed (see Configuration section).
angular-i18n-translator/
├── src/
│ ├── index.js # CLI entry point and command handlers
│ ├── config.js # Configuration loading and validation
│ ├── xlf-parser.js # XLF file parser and generator
│ ├── csv-converter.js # Conversion between XLF and CSV
│ ├── batch-manager.js # Batch management for translation
│ ├── llm-client.js # HTTP client for LLM APIs
│ ├── validator.js # CSV validation (interpolations, IDs, coverage)
│ └── cleaner.js # Cleanup of generated directories
├── batches/
│ ├── pending/ # Batches pending translation
│ └── translated/ # Already translated batches
├── dist-i18n/ # Translated XLF files (output)
├── .env.example # Environment variables template
├── .env # Environment variables (DO NOT version)
├── .gitignore # Files excluded from Git
├── i18n.config.json # Main configuration
├── messages.xlf # Source XLF file (from Angular)
├── messages.csv # Intermediate CSV
├── messages.translated.csv # CSV with translations
├── package.json # npm dependencies and scripts
└── README.md # This documentation
{
"languages": [
{ "code": "en", "name": "English", "file": "messages.en.xlf" },
{ "code": "es", "name": "Spanish", "file": "messages.es.xlf" },
{ "code": "fr", "name": "French", "file": "messages.fr.xlf" }
],
"sourceLanguage": "en",
"sourceFile": "messages.xlf",
"csvOutput": "messages.csv",
"outputDir": "dist-i18n",
"batchDir": "batches",
"llm": {
"baseURL": "${LLM_BASE_URL}",
"apiKey": "${LLM_API_KEY}",
"model": "${LLM_MODEL}",
"batchSize": 50,
"concurrency": 5,
"systemPrompt": "You are a professional translator..."
}
}| Field | Type | Description |
|---|---|---|
languages |
Array | List of supported languages with code, name, and output file |
sourceLanguage |
String | Source language code (must be in languages) |
sourceFile |
String | Source XLF file name extracted from Angular |
csvOutput |
String | Intermediate CSV file name |
outputDir |
String | Directory where translated XLF files are saved |
batchDir |
String | Directory for storing translation batches |
llm.baseURL |
String | Base URL of the LLM provider API (use ${LLM_BASE_URL} for environment variable) |
llm.apiKey |
String | API key (use ${LLM_API_KEY} for environment variable) |
llm.model |
String | Model name to use (use ${LLM_MODEL} for environment variable) |
llm.batchSize |
Number | Records per batch (default: 50) |
llm.concurrency |
Number | Simultaneous batches per language (default: 5) |
llm.systemPrompt |
String | System prompt for the LLM (optional) |
| Command | Description |
|---|---|
npm run init |
Interactive configuration wizard |
npm run xlf-to-csv |
Converts XLF file to CSV format |
npm run csv-to-xlf |
Converts translated CSV to XLF files |
npm run translate:run |
Executes translation with LLM |
npm run translate:all |
Complete translation pipeline |
npm run validate |
Validates CSV consistency |
npm run clean |
Cleans generated files |
| Option | Description |
|---|---|
--quiet |
Suppress non-essential output |
--verbose |
Enable verbose output |
--version |
Show version number |
--help |
Show help |
| Option | Description |
|---|---|
-f, --force |
Force re-translation of existing batches |
Run the interactive setup wizard to generate configuration files:
npm run initThis will guide you through:
- API key configuration
- LLM provider selection (DeepSeek, OpenAI, Anthropic, Ollama, or Custom)
- Model name specification (with links to provider documentation)
- Source language confirmation (defaults to English)
- Target language selection
- Batch settings (optional)
Note on Model Selection: The wizard does not pre-select specific models. You will need to enter the model name manually. The wizard provides links to each provider's model documentation to help you choose.
The wizard creates:
.env- API credentialsi18n.config.json- Full configuration
The validate command now provides:
- Line numbers for each issue
- Severity levels (ERROR, WARNING, INFO)
- Suggested fixes
- Context snippets for interpolation errors
Example output:
Line 42: [submit.btn] ERROR Missing interpolation: {{count}}
Suggestion: Add {{count}} to the translation
Context: Source: "Submit {{count}} items" | Translation: "Enviar"
# Force re-translation of already translated batches
npm run translate:run -- --force
# Clean only CSV files
npm run clean -- --csv-only
# Clean only batch directories
npm run clean -- --batches-only
# Clean only output directory
npm run clean -- --output-only
# Clean batches and output, keep CSV
npm run clean -- --keep-csvNote: When passing flags to commands via npm run, use
--before the flag:npm run translate:run -- --force npm run clean -- --csv-only
# From your Angular project
ng extract-i18n --output-path src/locale --out-file messages.xlf
# Or for Angular 17+ with esbuild
ng extract-i18n --format xlf2 --output-path src/localeCopy the generated messages.xlf file to this tool's directory.
npm run xlf-to-csvThis generates messages.csv with columns for each target language.
npm run translate:allThis command automatically executes:
- XLF to CSV
- Batch splitting
- LLM translation (languages processed sequentially, batches in parallel)
- Batch merging
- CSV to XLF
Important Note: Languages are processed sequentially (one after another) to avoid conflicts in translated batch files. Within each language, batches are processed in parallel according to the
concurrencyparameter.
# 1. Split into batches
npm run translate:split
# 2. Run translation
npm run translate:run
# 3. Merge results
npm run translate:merge
# 4. Generate final XLF files
npm run csv-to-xlfnpm run validateThe translated XLF files are in dist-i18n/. Copy them to your Angular project:
cp dist-i18n/*.xlf your-angular-project/src/locale/The tool is compatible with any API following the OpenAI format:
{
"llm": {
"baseURL": "https://api.deepseek.com",
"model": "your-model-name",
"apiKey": "${LLM_API_KEY}"
}
}See: https://api-docs.deepseek.com/
{
"llm": {
"baseURL": "https://api.openai.com/v1",
"model": "your-model-name",
"apiKey": "${LLM_API_KEY}"
}
}See: https://platform.openai.com/docs/models
{
"llm": {
"baseURL": "https://api.anthropic.com/v1",
"model": "your-model-name",
"apiKey": "${LLM_API_KEY}"
}
}See: https://docs.anthropic.com/en/docs/about-claude/models
For local development with Ollama:
{
"llm": {
"baseURL": "http://localhost:11434/v1",
"model": "your-model-name",
"apiKey": "ollama"
}
}See: https://ollama.com/library
You can use any provider that implements the OpenAI API format:
- Groq:
https://api.groq.com/openai/v1 - OpenRouter:
https://openrouter.ai/api/v1 - LocalAI:
http://localhost:8080/v1 - Any other OpenAI-compatible endpoint
When using the init wizard, select "Custom Provider" and enter your endpoint URL.
NEVER commit the
.envfile or any file containing API keys.The
.envfile is excluded from Git via.gitignore, but you must verify it is never accidentally included.
NEVER hardcode API keys directly in
i18n.config.jsonif you plan to version that file.
-
Use environment variables: The
.envfile is excluded from Git via.gitignore -
Reference variables in config: Use the
${LLM_API_KEY}syntax ini18n.config.json:{ "llm": { "apiKey": "${LLM_API_KEY}" } } -
Verify before commit: Make sure
.envis not included:git status # .env should NOT appear in the list of files to commit -
Rotate compromised keys: If you accidentally commit a key, rotate it immediately from the provider's dashboard.
-
Use keys with minimum permissions: Limit API keys to only the necessary permissions.
-
Review .gitignore: Make sure your
.gitignoreincludes:# Environment variables (SECURITY - Never commit these!) .env .env.local .env.*.local
Cause: i18n.config.json does not exist in the root directory.
Solution: Create the configuration file:
# Make sure you are in the correct directory
ls i18n.config.jsonCause: The environment variable is not set or the .env file does not exist.
Solution:
# Verify that .env exists and contains the key
cat .env
# Should show: LLM_API_KEY=your-key-here
# If it doesn't exist, create it:
cp .env.example .env
# Then edit .env with your real keyCause: Invalid or expired API key.
Solution: Verify that the API key is correct and has funds/credit.
Cause: Too many requests in a short time.
Solution:
- Reduce
concurrencyin the configuration - Increase
batchSizeto make fewer calls - Wait a few minutes before retrying
Cause: The API takes too long to respond.
Solution:
- Timeout is set to 60 seconds
- Check your internet connection
- Try with a faster model
Cause: The LLM does not follow instructions correctly.
Solution:
- Verify that the
systemPromptincludes instructions about interpolations - Try with a more capable model (check your provider's available models)
- Check the validation report:
npm run validate
Cause: The translated CSV has incorrect format.
Solution:
- Run validation:
npm run validate - Manually review
messages.translated.csv - Make sure all language columns have content
Cause: Need to run xlf-to-csv before other commands.
Solution:
npm run xlf-to-csv
npm run translate:allCause: No pending batches or they are already translated.
Solution:
# Force re-processing
npm run translate:run -- --force
# Or clean and start over
npm run clean
npm run translate:allCause: The messages.xlf file does not exist in the directory.
Solution:
# Verify the file exists
ls messages.xlf
# If it doesn't exist, extract it from your Angular project
ng extract-i18n --output-path . --out-file messages.xlfCause: The i18n.config.json file has JSON syntax errors.
Solution:
- Validate the JSON in an online linter
- Make sure all commas and quotes are correct
- Verify there are no comments (JSON does not support comments)
| Package | Version | Purpose |
|---|---|---|
commander |
^14.0.0 | CLI argument parsing |
chalk |
^5.6.0 | Terminal colors and styling |
ora |
^9.0.0 | Spinners for long operations |
cli-progress |
^3.12.0 | Progress bars |
inquirer |
^13.0.0 | Interactive prompts |
zod |
^4.0.0 | Schema validation |
@xmldom/xmldom |
^0.8.10 | XML parser for XLF files |
csv-parse |
^5.5.6 | CSV file reading |
csv-stringify |
^6.5.1 | CSV file writing |
dotenv |
^16.6.1 | Environment variable loading |
MIT License - Free for personal and commercial projects.
Contributions are welcome. Please open an issue or pull request to suggest improvements or report bugs.