translatronx
v2.1.0
Published
Deterministic, incremental, build-time translation compiler using LLMs
Maintainers
Readme
translatronx
Deterministic, incremental, build-time translation compiler using LLMs
Why translatronx?
Most translation tools are either:
- Runtime systems with performance and cost overhead
- SaaS platforms that own your data and workflow
- Non-deterministic LLM wrappers that retranslate everything
translatronx treats translations like source code:
- Compile once at build time
- Only retranslate what changed
- Preserve manual edits permanently
- Ship static files with zero runtime cost
translatronx is not another translation management system. It is a translation compiler — it treats language like code: build once → ship everywhere → zero runtime cost.
🌟 Features
- 🔄 Incremental & Deterministic - Only translates new/changed strings, same input = same output
- 💰 Cost-Efficient - Deduplication and intelligent batching minimize LLM API costs
- 🔒 Developer Authority - Manual edits are detected and protected forever (unless forced)
- 🎯 Provider-Agnostic - OpenAI, Anthropic, Groq, Azure OpenAI, OpenRouter, or local models
- ⚡ CI/CD Ready - Integrates seamlessly into GitHub Actions, GitLab CI, CircleCI, etc.
- 📊 State-Aware - SQLite ledger tracks changes without storing actual translations
- 🛡️ Production-Ready - Atomic writes, transaction safety, zero data loss on interruption
- 🎨 Customizable Prompts - Fine-tune translation quality with formatting, glossaries, and brand voice
- 📝 Context Files - Provide LLMs with context for each translation key to improve quality
- 📈 Comprehensive Reporting - Detailed statistics, cost estimates, and audit trails
📋 Table of Contents
- Installation
- Quick Start
- Tutorial: Step-by-Step Guide
- Configuration Reference
- CLI Commands
- Context Files
- Advanced Usage
- Best Practices
- API Reference
📦 Installation
Prerequisites
- Node.js >= 20.0.0
- npm, yarn, or pnpm
Install as Dev Dependency
npm install --save-dev translatronx
# or
yarn add --dev translatronx
# or
pnpm add -D translatronxInstall Globally (Optional)
npm install -g translatronx🚀 Quick Start
1. Initialize Configuration
npx translatronx initThis creates a translatronx.config.ts file in your project root.
2. Set API Key
# For OpenAI
export OPENAI_API_KEY=your-api-key
# For Anthropic
export ANTHROPIC_API_KEY=your-api-key
# For Groq
export GROQ_API_KEY=your-api-key3. Create Source Translation File
Create ./locales/en.json:
{
"welcome": "Welcome to our app!",
"greeting": "Hello, {name}!",
"auth": {
"login": "Log in",
"logout": "Log out",
"signup": "Sign up"
}
}4. Run Translation Sync
npx translatronx syncOutput:
🔄 Syncing translations...
✅ Translation sync complete!
Statistics:
Total strings: 5
Translated: 15
Failed: 0
Skipped: 0
Tokens used: 245 (input) + 312 (output)
Duration: 3.42sThis generates:
./locales/fr.json./locales/de.json./locales/es.json
📚 Tutorial: Step-by-Step Guide
1. Basic Translation Workflow
Step 1.1: Initialize Your Project
# Create a new directory for your translations
mkdir my-app-i18n
cd my-app-i18n
# Initialize npm project
npm init -y
# Install translatronx
npm install --save-dev translatronx
# Initialize configuration
npx translatronx initStep 1.2: Configure Your Project
Edit translatronx.config.ts:
import { defineConfig } from 'translatronx';
export default defineConfig({
sourceLanguage: 'en',
targetLanguages: [
{ language: 'French', shortCode: 'fr' },
{ language: 'German', shortCode: 'de' },
{ language: 'Spanish', shortCode: 'es' }
],
extractors: [
{
type: 'json',
pattern: './locales/en.json'
}
],
providers: [
{
name: 'openai',
type: 'openai',
model: 'gpt-4o-mini',
temperature: 0.3
}
],
output: {
dir: './locales',
format: 'json',
fileNaming: '{shortCode}.json'
}
});Step 1.3: Create Your Source Translations
Create ./locales/en.json:
{
"app": {
"title": "My Awesome App",
"description": "The best app you'll ever use"
},
"navigation": {
"home": "Home",
"about": "About",
"contact": "Contact Us"
},
"auth": {
"login": "Log in",
"logout": "Log out",
"signup": "Sign up",
"forgotPassword": "Forgot Password?"
},
"messages": {
"welcome": "Welcome, {username}!",
"goodbye": "See you soon, {username}!",
"error": "An error occurred. Please try again."
}
}Step 1.4: Run Your First Translation
# Set your API key
export OPENAI_API_KEY=your-api-key-here
# Run translation sync
npx translatronx syncWhat happens:
- translatronx reads your source file (
en.json) - Extracts all translatable strings
- Batches them efficiently for the LLM
- Generates translations for all target languages
- Writes output files (
fr.json,de.json,es.json) - Stores state in
.translatronx/ledger.sqlite
Step 1.5: Check Translation Status
npx translatronx statusOutput:
📊 Translation Coverage Report
┌─────────────┬────────┬───────────┬──────────┬──────────┐
│ Language │ Total │ Clean │ Dirty │ Coverage │
├─────────────┼────────┼───────────┼──────────┼──────────┤
│ French (fr) │ 12 │ 12 │ 0 │ 100.0% │
│ German (de) │ 12 │ 12 │ 0 │ 100.0% │
│ Spanish (es)│ 12 │ 12 │ 0 │ 100.0% │
└─────────────┴────────┴───────────┴──────────┴──────────┘Step 1.6: Update Source and Re-sync
Add a new string to en.json:
{
"app": {
"title": "My Awesome App",
"description": "The best app you'll ever use",
"tagline": "Built with love ❤️" // NEW!
},
// ... rest of the file
}Run sync again:
npx translatronx syncOutput:
✅ Translation sync complete!
Statistics:
Total strings: 13
Translated: 3 ← Only the new string!
Failed: 0
Skipped: 10 ← Existing translations skippedThis is the power of incremental translation! Only new/changed strings are translated, saving you time and money.
2. Using Context Files for Better Translations
Context files allow you to provide additional information to the LLM about each translation key, resulting in more accurate and contextually appropriate translations.
Step 2.1: Generate a Context File Template
npx translatronx context generate --source ./locales/en.jsonOutput:
📝 Generating context file template...
Source: /path/to/locales/en.json
Output: /path/to/locales/en.context.json
Merge: No
✅ Context file generated successfully!
💡 Tip: Edit locales/en.context.json to add context for each translation keyThis creates ./locales/en.context.json:
{
"app": {
"title": {
"value": "My Awesome App",
"context": ""
},
"description": {
"value": "The best app you'll ever use",
"context": ""
},
"tagline": {
"value": "Built with love ❤️",
"context": ""
}
},
"navigation": {
"home": {
"value": "Home",
"context": ""
},
// ... etc
}
}Step 2.2: Add Context to Your Keys
Edit en.context.json to add helpful context:
{
"app": {
"title": {
"value": "My Awesome App",
"context": "Main application title shown in the header and browser tab"
},
"description": {
"value": "The best app you'll ever use",
"context": "Marketing tagline shown on the landing page. Should be enthusiastic and engaging."
},
"tagline": {
"value": "Built with love ❤️",
"context": "Footer tagline. Keep the heart emoji in all translations."
}
},
"auth": {
"login": {
"value": "Log in",
"context": "Button text for user authentication. Should be concise and action-oriented."
},
"logout": {
"value": "Log out",
"context": "Button text for ending user session."
},
"forgotPassword": {
"value": "Forgot Password?",
"context": "Link text for password recovery. Should be phrased as a question."
}
},
"messages": {
"welcome": {
"value": "Welcome, {username}!",
"context": "Greeting shown when user logs in. {username} is the user's display name."
}
}
}Step 2.3: Validate Your Context File
npx translatronx context validateOutput:
🔍 Validating context file...
Source: /path/to/locales/en.json
Context: /path/to/locales/en.context.json
✅ Context file is valid!Step 2.4: Enable Context in Configuration
Update translatronx.config.ts:
export default defineConfig({
// ... other config
extractors: [
{
type: 'json',
pattern: './locales/en.json',
contextFile: {
enabled: true,
pattern: './locales/en.context.json',
autoGenerate: false,
autoUpdate: false
}
}
],
// ... rest of config
});Step 2.5: Re-translate with Context
To see the improvement, let's force a retranslation:
# Delete existing translations
rm ./locales/fr.json ./locales/de.json ./locales/es.json
# Clear the ledger to force retranslation
rm -rf .translatronx
# Run sync with context
npx translatronx syncThe LLM now receives context for each string, resulting in better translations!
For example, without context:
- "Log in" might be translated as "Connexion" (noun) in French
With context ("Button text for user authentication"):
- "Log in" is translated as "Se connecter" (verb/action) in French
3. Managing Existing Translations
If you already have translations and want to add context files without affecting them:
Step 3.1: Import Context from Source
npx translatronx context import --source ./locales/en.jsonOutput:
📥 Importing context from source...
Source: /path/to/locales/en.json
Output: /path/to/locales/en.context.json
Merge: Yes
✅ Context imported from source successfully!
• Existing context preserved
• New keys from source added
• Source values updated to match current source
• Your existing translations are safe
💡 Tip: Your existing translations will NOT be affected.
Context files only improve future translations.Key Points:
- ✅ Your existing translation files (
fr.json,de.json, etc.) are never touched - ✅ Existing context you've written is preserved
- ✅ New keys from source are added with empty context
- ✅ Only future translations will benefit from context
Step 3.2: Sync Context When Source Changes
When you add/remove keys in your source file:
npx translatronx context syncOutput:
🔄 Syncing context file with source...
Source: /path/to/locales/en.json
Context: /path/to/locales/en.context.json
✅ Context file synced successfully!
• New keys added
• Deleted keys removed
• Existing context preserved4. Customizing Prompts
Improve translation quality by customizing the prompts sent to the LLM.
Step 4.1: Add Custom Context
export default defineConfig({
// ... other config
prompts: {
customContext: 'This is a mobile banking app. Use financial terminology and maintain a professional tone.',
}
});Step 4.2: Add Glossary
export default defineConfig({
// ... other config
prompts: {
glossary: {
'Dashboard': 'Tableau de bord',
'Account': 'Compte',
'Transaction': 'Transaction',
'Balance': 'Solde'
}
}
});Step 4.3: Set Formatting Style
export default defineConfig({
// ... other config
prompts: {
formatting: 'formal', // 'formal' | 'casual' | 'technical'
brandVoice: 'Professional, trustworthy, and user-friendly'
}
});Step 4.4: Custom User Prompt
export default defineConfig({
// ... other config
prompts: {
userPrompt: [
'Translate the following strings for a mobile banking application.',
'Maintain consistency with previous translations.',
'Use formal language and financial terminology.',
'Preserve all placeholders like {username} exactly as they appear.'
]
}
});5. CI/CD Integration
Step 5.1: GitHub Actions
Create .github/workflows/translations.yml:
name: Update Translations
on:
push:
paths:
- 'locales/en.json'
- 'locales/en.context.json'
branches:
- main
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run translations
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npx translatronx sync
- name: Commit translations
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
git add locales/*.json
git diff --quiet && git diff --staged --quiet || git commit -m "chore: update translations"
git pushStep 5.2: GitLab CI
Create .gitlab-ci.yml:
translate:
image: node:20
stage: build
only:
changes:
- locales/en.json
- locales/en.context.json
script:
- npm ci
- npx translatronx sync
- git config user.email "[email protected]"
- git config user.name "GitLab CI"
- git add locales/*.json
- git diff --quiet && git diff --staged --quiet || (git commit -m "chore: update translations" && git push)
variables:
OPENAI_API_KEY: $OPENAI_API_KEY⚙️ Configuration Reference
Complete Configuration Example
import { defineConfig } from 'translatronx';
export default defineConfig({
// Source language code (ISO 639-1)
sourceLanguage: 'en',
// Target languages with full names and short codes
targetLanguages: [
{ language: 'French', shortCode: 'fr' },
{ language: 'German (Formal)', shortCode: 'de-formal' },
{ language: 'Simplified Chinese', shortCode: 'zh-Hans' }
],
// Extractors define how to find translatable strings
extractors: [
{
type: 'json', // 'json' | 'typescript' | 'custom'
pattern: './locales/en.json', // Glob pattern(s)
keyPrefix: 'app', // Optional: prefix for all keys
exclude: ['**/node_modules/**'], // Optional: exclude patterns
contextFile: { // Optional: context file configuration
enabled: true,
pattern: './locales/en.context.json',
autoGenerate: false,
autoUpdate: false
}
}
],
// LLM providers configuration
providers: [
{
name: 'openai-primary',
type: 'openai',
model: 'gpt-4o-mini',
temperature: 0.3,
maxRetries: 3,
apiKey: process.env.OPENAI_API_KEY, // Optional: defaults to env var
fallback: 'anthropic-backup' // Optional: fallback provider
},
{
name: 'anthropic-backup',
type: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
temperature: 0.3,
maxRetries: 2
}
],
// Validation rules
validation: {
preservePlaceholders: true, // Ensure {var} placeholders are preserved
maxLengthRatio: 3, // Max target/source length ratio
preventSourceLeakage: true, // Prevent untranslated source text
brandNames: ['Acme', 'Widget'], // Protected brand names
customRules: [] // Custom validation functions
},
// Output configuration
output: {
dir: './locales',
format: 'json', // 'json' | 'yaml' | 'typescript'
flat: false, // Flatten nested objects
indent: 2, // JSON indentation
fileNaming: '{shortCode}.json', // File naming pattern
allowSameFolder: false // Allow source & target in same dir
},
// Prompt customization
prompts: {
customContext: 'This is a mobile banking app. Use financial terminology.',
formatting: 'formal', // 'formal' | 'casual' | 'technical'
glossary: {
'Dashboard': 'Tableau de bord',
'Settings': 'Paramètres'
},
brandVoice: 'Professional, trustworthy, and user-friendly',
userPrompt: [ // Custom user prompt (optional)
'Translate the following strings for a mobile app.',
'Maintain consistency with previous translations.'
]
},
// Advanced settings
advanced: {
batchSize: 20, // Strings per LLM call
concurrency: 3, // Parallel LLM requests
cacheDir: './.translatronx', // State directory
ledgerPath: './.translatronx/ledger.sqlite',
verbose: false // Enable verbose logging
}
});Configuration Options
sourceLanguage
- Type:
string - Required: Yes
- Description: ISO 639-1 language code for source language
targetLanguages
- Type:
Array<{ language: string, shortCode: string }> - Required: Yes
- Description: Array of target languages with full names and codes
extractors
- Type:
Array<ExtractorConfig> - Required: Yes
- Description: Configuration for extracting translatable strings
Extractor Context File Options
enabled: Enable context file supportpattern: Path to context file (defaults to{source}.context.json)autoGenerate: Auto-generate context file if missingautoUpdate: Auto-update context file when source changes
providers
- Type:
Array<ProviderConfig> - Required: Yes
- Description: LLM provider configuration
Supported providers:
openai- OpenAI (GPT-4, GPT-3.5, etc.)anthropic- Anthropic (Claude)groq- Groqazure-openai- Azure OpenAIopenrouter- OpenRouter
validation
- Type:
ValidationConfig - Required: No
- Description: Translation validation rules
output
- Type:
OutputConfig - Required: Yes
- Description: Output file configuration
prompts
- Type:
PromptConfig - Required: No
- Description: Prompt customization options
advanced
- Type:
AdvancedConfig - Required: No
- Description: Advanced performance and behavior settings
🖥️ CLI Commands
translatronx sync
Synchronize translations (incremental processing).
npx translatronx sync [options]Options:
-f, --force- Force regeneration of manual overrides-v, --verbose- Enable verbose output
Example:
npx translatronx sync --verbosetranslatronx status
Display coverage statistics and system state.
npx translatronx statusOutput:
📊 Translation Coverage Report
┌─────────────┬────────┬───────────┬──────────┬──────────┐
│ Language │ Total │ Clean │ Dirty │ Coverage │
├─────────────┼────────┼───────────┼──────────┼──────────┤
│ French (fr) │ 12 │ 12 │ 0 │ 100.0% │
│ German (de) │ 12 │ 11 │ 1 │ 91.7% │
└─────────────┴────────┴───────────┴──────────┴──────────┘translatronx retry
Retry failed translation batches.
npx translatronx retry [options]Options:
--batch <id>- Specific batch ID to retry--lang <code>- Specific language to retry--dry-run- Show what would be retried without making changes
Example:
npx translatronx retry --lang fr --dry-runtranslatronx init
Initialize translatronx configuration.
npx translatronx initCreates a translatronx.config.ts file in your project root.
translatronx context generate
Generate context file template from source file.
npx translatronx context generate [options]Options:
--source <path>- Source file path (overrides config)--output <path>- Context file output path--merge- Merge with existing context file--dry-run- Preview changes without writing
Example:
npx translatronx context generate --source ./locales/en.jsontranslatronx context validate
Validate context file matches source file.
npx translatronx context validate [options]Options:
--source <path>- Source file path (overrides config)--context <path>- Context file path (overrides config)
Example:
npx translatronx context validatetranslatronx context sync
Sync context file with source (add new keys, remove deleted keys).
npx translatronx context sync [options]Options:
--source <path>- Source file path (overrides config)--context <path>- Context file path (overrides config)--dry-run- Preview changes without writing
Example:
npx translatronx context sync --dry-runtranslatronx context import
Import/generate context file from source JSON (preserves existing translations).
npx translatronx context import [options]Options:
--source <path>- Source JSON file to generate context from--output <path>- Output context file path--merge- Merge with existing context file (default: true)--dry-run- Preview changes without writing
Example:
npx translatronx context import --source ./locales/en.jsonUse Case: Perfect for users who already have translations and want to add context files without affecting existing translations.
📝 Context Files
Context files provide additional information to the LLM about each translation key, resulting in more accurate and contextually appropriate translations.
Context File Structure
Context files mirror your source file structure:
Source File (en.json):
{
"greeting": "Hello, {name}!",
"auth": {
"login": "Sign in",
"logout": "Sign out"
}
}Context File (en.context.json):
{
"greeting": {
"value": "Hello, {name}!",
"context": "Greeting shown when user logs in. {name} is the user's display name.",
"notes": "Keep it friendly and welcoming",
"maxLength": 50,
"tone": "casual"
},
"auth": {
"login": {
"value": "Sign in",
"context": "Button text for user authentication. Should be concise and action-oriented."
},
"logout": {
"value": "Sign out",
"context": "Button text for ending user session."
}
}
}Context Metadata Fields
Each key in a context file can have:
value(required): The source text (for validation)context(optional): Descriptive text for the LLMnotes(optional): Additional notesmaxLength(optional): Maximum character lengthtone(optional): Desired tone (e.g., "formal", "casual", "technical")
Context File Workflow
Generate template:
npx translatronx context generateAdd context to keys: Edit the generated
en.context.jsonfileValidate:
npx translatronx context validateEnable in config:
extractors: [{ type: 'json', pattern: './locales/en.json', contextFile: { enabled: true, pattern: './locales/en.context.json' } }]Run sync:
npx translatronx sync
Benefits of Context Files
- Better Translation Quality - LLMs understand the purpose and usage of each string
- Consistency - Maintain consistent terminology across your app
- Tone Control - Specify formal vs. casual tone per string
- Length Constraints - Ensure translations fit UI constraints
- Optional - Works with or without context files
- Backward Compatible - No breaking changes to existing workflows
🔧 Advanced Usage
Manual Edits Protection
translatronx automatically detects and protects manual edits:
Translate your strings:
npx translatronx syncManually edit a translation in
fr.json:{ "welcome": "Bienvenue chez nous!" // Manual edit }Run sync again:
npx translatronx syncYour manual edit is preserved! The ledger marks it as
MANUALstatus.
To force retranslation of manual edits:
npx translatronx sync --forceMultiple Providers with Fallback
export default defineConfig({
providers: [
{
name: 'primary',
type: 'openai',
model: 'gpt-4o-mini',
fallback: 'backup'
},
{
name: 'backup',
type: 'anthropic',
model: 'claude-3-5-sonnet-20241022'
}
]
});If the primary provider fails, translatronx automatically uses the backup.
Custom Validation Rules
export default defineConfig({
validation: {
customRules: [
(source, target) => {
// Ensure translations don't exceed source length by more than 50%
if (target.length > source.length * 1.5) {
return { isValid: false, error: 'Translation too long' };
}
return { isValid: true };
}
]
}
});Importing Existing Translations
If you have existing translations you want to import:
npx translatronx import --source ./old-translations/fr.json --target frThis imports your existing translations and marks them as MANUAL in the ledger, protecting them from being overwritten.
💡 Best Practices
1. Use Context Files for Important Strings
Add context to strings where:
- The meaning is ambiguous (e.g., "Bank" - financial institution or river bank?)
- Tone matters (e.g., error messages should be helpful, not scary)
- Length constraints exist (e.g., button text must be short)
- Cultural nuances matter (e.g., greetings, politeness levels)
2. Keep Source Files Clean
- Use clear, descriptive keys:
auth.loginButtonnotbtn1 - Avoid abbreviations in source text
- Use consistent placeholder format:
{variable}not{{variable}}or$variable
3. Leverage Glossaries
Create a glossary for:
- Brand names
- Product names
- Technical terms
- Domain-specific terminology
4. Run Translations in CI/CD
Automate translations on source file changes:
- Ensures translations are always up-to-date
- Catches issues early
- Reduces manual work
5. Review Generated Translations
While LLMs are good, they're not perfect:
- Review critical strings (legal, security, payments)
- Have native speakers spot-check
- Use the
statuscommand to track coverage
6. Use Incremental Workflow
Don't retranslate everything:
- Let translatronx track changes
- Only force retranslation when necessary
- Trust the ledger system
📖 API Reference
Programmatic Usage
import { TranslationCompiler, loadConfig } from 'translatronx';
async function main() {
// Load configuration
const config = await loadConfig();
// Create compiler
const compiler = new TranslationCompiler(config);
// Run sync
const stats = await compiler.sync({ verbose: true });
console.log(`Translated ${stats.translatedUnits} strings`);
console.log(`Cost: $${stats.costEstimateUsd.toFixed(4)}`);
// Close compiler
compiler.close();
}
main();Configuration API
import { defineConfig } from 'translatronx';
export default defineConfig({
// Type-safe configuration
sourceLanguage: 'en',
targetLanguages: [
{ language: 'French', shortCode: 'fr' }
],
// ... rest of config
});Custom Extractors
import { type Extractor, type SourceUnit } from 'translatronx';
class CustomExtractor implements Extractor {
async extract(sourceFiles: string[], config: ExtractorConfig): Promise<SourceUnit[]> {
// Your custom extraction logic
return [];
}
}🤝 Contributing
We welcome contributions! Please see CONTRIBUTING.md for details.
Development Setup
# Clone repository
git clone https://github.com/msalways/translatronx.git
cd translatronx
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run build
# Lint
npm run lint📄 License
MIT © Shanthosh
🙏 Acknowledgments
- Inspired by the TypeScript compiler philosophy
- Built with OpenAI, Anthropic, and Groq
- Uses better-sqlite3 for state management
📞 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: [email protected]
Made with ❤️ by developers, for developers
