Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.internationalizer/
dist/
/*.tgz
/.env
4 changes: 0 additions & 4 deletions .internationalizer.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY # env var containing your API key
# base_url: https://api.openai.com # for OpenAI-compatible endpoints
locale_overrides: # optional provider/model settings by target locale
ja:
provider: openrouter
model: sakana/sakana-namazu
api_key_env: OPENROUTER_API_KEY
yue:
provider: openrouter
model: deepseek/deepseek-v4-flash-0731
Expand Down
4 changes: 0 additions & 4 deletions .internationalizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ llm:
model: gemini-3.8-flash
api_key_env: GOOGLE_AI_STUDIO_API_KEY
locale_overrides:
ja:
provider: openrouter
model: sakana/sakana-namazu
api_key_env: OPENROUTER_API_KEY
yue:
provider: openrouter
model: deepseek/deepseek-v4-flash-0731
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Internationalizer is different. It's a **CLI pipeline** that combines LLM transl
- **Per-language glossaries** — enforce consistent terminology across your app
- **Per-language style guides** — control tone, formality, pluralization, and typography
- **Translation memory** — skip unchanged strings, save money on API calls
- **Key validation** — catch missing translations and interpolation mismatches before they ship
- **Deterministic validation** — catch missing or extra keys, protected-structure drift, glossary issues, and plural or ICU errors before they ship

## Installation

Expand Down Expand Up @@ -115,7 +115,7 @@ internationalizer validate

### `translate`

Find missing keys and translate them via an LLM.
Find missing or stale keys and translate them via an LLM.

```bash
internationalizer translate # translate all locales
Expand Down Expand Up @@ -262,10 +262,6 @@ llm:
# global provider inherits unspecified global settings. A different provider
# uses that provider's defaults for unspecified settings.
locale_overrides:
ja:
provider: openrouter
model: sakana/sakana-namazu
api_key_env: OPENROUTER_API_KEY
yue:
provider: openrouter
model: deepseek/deepseek-v4-flash-0731
Expand Down Expand Up @@ -306,6 +302,8 @@ validation:
Locale identifiers must be well-formed BCP 47 tags such as `fr`, `pt-BR`, or
`sr-Latn-RS`. Canonical-equivalent target locales are rejected as duplicates,
and locale-specific provider overrides match canonical-equivalent spelling.
In the example above, locales without an override—including Japanese—inherit
the global Gemini configuration.

ICU MessageFormat values are parsed structurally. Simple arguments, `select`,
`plural`, `selectordinal`, `number`, `date`, and `time` are supported, including
Expand Down Expand Up @@ -371,12 +369,16 @@ findings; it does not exempt a longer value merely containing `API`.

Translation memory is stored as a JSONL file (one JSON record per line). Each record contains:

- The source key and value
- The translated value
- A SHA-256 hash of the source value
- The bundle, key, source value, translated value, and canonical target locale
- Source and translation-policy hashes
- The provider and model that produced the translation
- A timestamp

On subsequent runs, unchanged strings are served from the TM cache without calling the LLM, saving both time and API costs. The TM file is git-friendly and can be committed alongside your locale files.
On subsequent runs, strings with the same source and policy hashes are served
from the cache without calling the LLM. The default path is under the ignored
`.internationalizer/` directory, so it remains a local cache. Set `tm_path` to a
tracked location if your project intentionally shares translation memory. The
reviewable `.internationalizer.lock` manifest is versioned separately.

## Supported Formats

Expand Down Expand Up @@ -410,6 +412,8 @@ internal/
OpenRouter uses openai.go with custom base_url
locale/ BCP 47 identity and CLDR plural categories
message/ ICU MessageFormat parser and structural comparison
policy/ Stable translation-policy hashing
state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
Expand Down
Loading