npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rio-cloud/i18n

v1.0.1

Published

Deterministic CLI workflow for AI-assisted Web, JVM, iOS, and KMP translations

Readme

I18n

This package provides the reusable, deterministic i18n-flow CLI for AI-assisted Web, JVM, iOS, and Kotlin Multiplatform translations. It initializes projects, creates or updates translations, validates the results, and offers supporting catalog and maintenance tools.

Consumer projects keep their app-specific configuration in a .i18nrc.json file at the project root.

Backend projects using Spring MessageSource or Java ResourceBundle should start with the JVM backend guide.

iOS apps using locale-specific Localizable.strings resources should start with the iOS guide.

Compose Multiplatform apps using composeResources should start with the Kotlin Multiplatform guide.

Table of contents

Main idea

The workflow groups its commands by purpose. Most developers primarily use the translation commands.

Translation

  1. translate
    • creates missing translations only
    • does not overwrite existing target translations
    • after adding source keys, simply run i18n-flow translate; locale or key parameters are not required
    • automatically uses locale-oriented requests for large backfills and multi-locale requests for small deltas
  2. improve
    • reviews existing target translations against the corresponding messages from the configured source locale
    • gives the AI both the source text and the current target translation as its basis
    • leaves the source locale unchanged unless --include-source-locale is passed; with that option, the source is improved first and its updated text is then used to improve the selected target locales
    • runs in conservative mode by default
    • changes a translation only when an objective correction is required
    • use --mode revise to actively improve wording, clarity, consistency, and style
  3. retranslate
    • regenerates existing translations from the current English source text
    • use this when the source label in en-GB changed and targets need to be updated accordingly
  4. validate
    • checks all existing target translations against the mandatory translation policy
    • reports missing translations without treating them as invalid
    • does not call AI or change translation files

Catalog and setup

  • init configures a project and installs the repository-scoped i18n skill.
  • select-ai-model updates the model and reasoning of the current AI runner in an initialized project.
  • install-skill
    • installs the packaged workflow instructions at .agents/skills/i18n/SKILL.md
    • preserves an existing installed skill unless --update is passed
  • inspect-ui-components
    • finds UI component usages described by an external i18n catalog
    • maps localized props to canonical catalog keys and messages
    • produces a human-readable or JSON task without changing project files
  • sync-shared-messages
    • finds shared catalog message keys that are referenced in project source files
    • copies their source text and available reviewed translations into the configured locale files
    • is run automatically by translate; the explicit command is useful for previews and CI checks
  • migrate-to-catalog-keys
    • migrates explicit shared-message and UI-component legacyKeys in source and locale files
    • removes migrated legacy locale entries and supports --dry-run

Maintenance

  • remove-unused-keys
    • removes messages that are not statically referenced in project code from every locale JSON file found in the directory configured by targetPathPattern
    • conservatively preserves key families constructed with template literals or string concatenation
    • use --keep for indirect dynamic keys that cannot be inferred statically
    • use --unsafe to opt into aggressive exact-reference-only cleanup
  • harmonize-keys
    • adds the configured prefix to unprefixed translation keys and normalizes supported legacy key forms
    • updates exact JavaScript/TypeScript references and unambiguous static prefixes of trailing dynamic keys
    • detects conflicting normalized keys before changing files and supports --dry-run
  • cleanup-phrase
    • removes obsolete root-level Phrase YAML configuration files after migration
    • removes pull-translation(s) and push-translation(s) scripts from package.json
    • supports an interactive confirmation, --yes, --dry-run, and JSON output

Diagnostics

  • test-catalog-connection verifies access to the configured i18n catalog.
  • test-ai-connection verifies the configured AI runner.

translate, improve, and retranslate run end-to-end:

  • build a job manifest
  • call the configured Codex CLI model
  • validate the AI output
  • write the updated target locale files

Platform guides

  • Web/JSON projects: continue with the setup and configuration reference in this README.
  • JVM/Java-properties projects: see the JVM backend guide.
  • iOS/Apple-strings projects: see the iOS app guide.
  • Kotlin Multiplatform/Compose-XML projects: see the KMP guide.

Installation and entry points

The folder is packaged as @rio-cloud/i18n and exposes the brand-neutral i18n-flow executable. Use the package runner for your project:

| Package manager | Initialize | Run the package script | Run the binary directly | | --- | --- | --- | --- | | npm | npx @rio-cloud/i18n init | npm run i18n-flow -- <command> | npm exec -- i18n-flow <command> | | pnpm | pnpm dlx @rio-cloud/i18n init | pnpm run i18n-flow -- <command> | pnpm exec i18n-flow <command> | | Yarn | yarn dlx @rio-cloud/i18n init | yarn run i18n-flow <command> | yarn i18n-flow <command> | | Bun | bunx @rio-cloud/i18n init | bun run i18n-flow -- <command> | bunx i18n-flow <command> |

With npm or Bun, put every i18n-flow argument after the -- separator. For example, use npm run i18n-flow -- help or npm run i18n-flow -- --help; otherwise npm handles --help itself.

The remaining examples use the package-manager-neutral form i18n-flow <command>.

Built-in help

The executable has built-in documentation, so the most relevant guidance is available directly in the terminal:

i18n-flow help
i18n-flow translate --help
i18n-flow improve --help
i18n-flow help web
i18n-flow help examples
i18n-flow help config

Interactive init flow

In an interactive terminal, the Inquirer wizard discovers locale files and guides the developer through source, targets, locales, the catalog Git URL or local path, AI runner, and Codex model and reasoning settings. Chalk highlights the summary and result.

Run the wizard with:

i18n-flow init

The CLI first detects the likely platform from project files. Gradle or Maven builds and Java properties message bundles indicate JVM; Xcode projects and .lproj/Localizable.strings resources indicate iOS; commonMain/composeResources/values/strings.xml indicates Kotlin Multiplatform; package.json and JSON locale files indicate Web. An unambiguous interactive result is shown without exposing the underlying file paths and must be confirmed. Rejecting it, or conflicting signals, opens the platform selection with Web as the default. Non-interactive setup uses an unambiguous detection and otherwise falls back to Web. Use --platform web, --platform jvm, --platform ios, or --platform kmp to bypass detection. The Web setup then follows this flow. Values supplied as CLI options skip the corresponding question.

  1. Source translation file
    • The wizard searches the project for en-GB.json, en-US.json, or en.json source candidates.
    • One matching source file is selected automatically.
    • If multiple candidates exist, choose Select the source translation file.
    • If no candidate can be inferred, pass --source-file <path>.
  2. Target locale path pattern
    • Enter the target file pattern.
    • Default: {locale}.json next to the selected source file, for example src/features/translations/{locale}.json.
    • If {locale} is omitted, the wizard normalizes the value to a locale pattern.
  3. Source locale
    • Enter the locale represented by the source file.
    • Default: en-GB.
  4. Translation key prefix
    • intl-msg: — recommended and selected by default.
    • Custom prefix — prompts for a complete project-specific prefix including its separator.
    • No prefix — preserves unprefixed translation keys.
  5. Target locales
    • Existing locale files matching the target path pattern are detected and proposed as the target locales.
    • Confirm the detected locales to continue, or decline to customize them in the checklist. Detected locales are preselected when customizing.
    • If none are detected, choose one or more locales from the checklist: bg-BG, cs-CZ, da-DK, de-DE, el-GR, es-ES, et-EE, fi-FI, fr-FR, hr-HR, hu-HU, it-IT, lt-LT, lv-LV, nb-NO, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, and sv-SE. All are initially selected.
  6. Shared translation rules (i18n-catalog)
    • Enter the Git URL of the shared catalog. A local path remains supported for catalog development.
    • Default: ssh://[email protected]:7999/riofront/i18n-catalog.git.
    • --catalog <source>, I18N_CATALOG_URL, or I18N_CATALOG_PATH can provide the value non-interactively.
    • --catalog-ref <ref> or I18N_CATALOG_REF selects a branch or tag; otherwise the remote default branch is used.
    • Before writing setup files, initialization verifies that the selected remote and ref are readable with the current Git/Bitbucket credentials. --dry-run does not access the network.
  7. AI runner
    • Codex CLI — selected by default. Continue with:
      • Initialization reads the available model and reasoning-effort catalog from the signed-in Codex CLI. The interactive model recommendation prefers the newest available Terra or Luna generation, with Terra preferred when both families have the same generation. It recommends medium reasoning when the selected model supports it, otherwise the model's own default effort.
      • Custom model remains available for models outside the discovered catalog.
      • Explicit --model and --reasoning-effort options take precedence over automatic selections.
      • Before writing setup files, initialization performs a small AI request to verify that the configured Codex CLI can reach the selected model. If catalog discovery fails, setup warns and uses gpt-5.6-terra with medium reasoning; the connection check still runs and must succeed before setup files are written. Failure of that connection check stops setup with an update/sign-in hint instead of exposing the raw CLI error. --dry-run still performs the local Codex catalog discovery, but skips the generation connection check. If the Codex executable cannot be started for discovery, initialization stops with a concise command-path error.
    • AWS Bedrock — confirm or replace the default model eu.anthropic.claude-sonnet-4-6 and Region eu-central-1, then optionally enter an AWS profile. The runner uses the AWS SDK credential chain, including credentials refreshed with aws sso login. Initialization verifies the Bedrock connection before writing setup files; --dry-run skips this network check. Bedrock projects install the optional runtime once with npm install @aws-sdk/client-bedrock-runtime; other projects do not receive it.
    • Custom AI — continue with one adapter template:
      • Provider SDK — selected by default
      • Direct API (API key)
  8. Package script
    • Confirm Add an "i18n-flow" script to this project's package.json? (recommended) to add the single script "i18n-flow": "i18n-flow".
    • Decline to leave the existing scripts section unchanged and run the binary directly.
  9. Configuration summary and confirmation
    • Review source, target pattern, prefix, locales, catalog, runner, model, and optional adapter.
    • Confirm Create this configuration? before files are written.
    • When .i18nrc.json already exists, confirm its replacement. Existing project-context files are preserved unless their individual replacement prompts are confirmed.

After confirmation, initialization creates or updates:

  • .i18nrc.json
  • .i18n/context/project.md
  • .agents/skills/i18n/SKILL.md
  • an ignored .i18n/cache/ checkout when a catalog Git URL is configured
  • the selected Custom AI adapter template, when applicable
  • the optional i18n-flow package script and the pinned @rio-cloud/i18n development dependency in package.json
  • .gitignore rules for local state and job artifacts

With --yes or --json, the interactive questions are skipped. The automatic Codex model and reasoning effort come from the signed-in CLI's default model entry and its default effort. If discovery fails or the CLI reports no default model, initialization warns and uses gpt-5.6-terra with medium reasoning; the usual connection check still has to succeed. Explicit --model or --reasoning-effort values override the corresponding automatic choice. Other automatic defaults are Codex CLI, intl-msg:, and the shared Bitbucket catalog URL. Detected target locales are used; otherwise the default locale list above is selected. The i18n-flow package script is added unless --no-package-script is passed.

For automated setup, use --yes; use --dry-run or --json for a machine-readable plan/result. Initialization writes .i18nrc.json, project context templates, the repository-scoped skill, the optional i18n-flow package script, the pinned development dependency, and local artifact ignore rules. Shared domain data remains in the catalog checkout. The generated .i18n/context/project.md is the extension point for project-specific product and audience knowledge, tone and form of address, required terminology, and terms that must not be translated. Project rules extend the shared catalog and take precedence when they conflict with a catalog default. This supports deliberate exceptions such as a driver-facing app using informal German du while the shared catalog defaults to formal Sie. Add any additional project context files explicitly to contextPaths.

There are two supported ways to start the workflow after initialization:

  • run the optional package script with the syntax shown in the table above
  • run the i18n-flow <command> binary directly through the package manager

Both entry points use the same deterministic CLI implementation. The CLI owns discovery, selection, batching, manifests, JSON schema creation, validation, and target-file writes. The configured AI runner only receives a bounded prompt and schema and returns structured translation data.

JVM projects

For Spring MessageSource and Java ResourceBundle setup, bundle strategies, locale mappings, configuration, and supported commands, see the JVM backend guide. The explicit quick start is i18n-flow init --platform jvm.

iOS projects

For .lproj/Localizable.strings discovery, encoding preservation, Phrase migration, configuration, and supported commands, see the iOS app guide. The explicit quick start is i18n-flow init --platform ios.

Change the configured AI model

Run i18n-flow select-ai-model in an initialized Web, JVM, iOS, or KMP project to update ai.model and ai.reasoningEffort. The runner and all other configuration settings are preserved. Use --config <path> to update an alternative configuration file.

--model and --reasoning-effort take precedence over interactive selection. If either value is missing, the CLI asks for it. Codex discovers its model catalog and offers the same model and reasoning recommendations as init; custom models also allow manual reasoning input. Other runners ask for missing values as text.

i18n-flow select-ai-model
i18n-flow select-ai-model --model gpt-6-luna --reasoning-effort medium
i18n-flow select-ai-model --model gpt-6-luna --yes

--yes is allowed only with the Codex runner and at least one missing value. It automatically selects the catalog's default model and/or the selected model's default reasoning, as during init. If a custom model has no catalog reasoning default, it uses medium. Passing both values together with --yes, or using --yes with another runner, is an error. Without a terminal, pass both values or use the permitted Codex --yes mode.

Before saving, the CLI tests the selected settings with the same minimal AI request as init. All runners, including custom commands, must pass this test. Codex discovery failures stop the command before the final test. A discovery or connection failure leaves the configuration unchanged.

AI runner boundary

The actual AI translation call is made through .i18nrc.json > ai. With runner: "codex-cli", execution depends on the local codex binary and its authentication state. With runner: "bedrock", the AWS SDK invokes the configured model through Bedrock's Converse API and resolves authentication through the standard AWS credential chain. Runners do not discover or write translation files.

To integrate another executable, use the command runner. It receives a versioned JSON request over stdin and must return { "output": ..., "metadata": ... } over stdout. Provider-specific behavior remains isolated in runners/. When Custom AI is selected during initialization, the CLI asks whether to use a provider SDK or a direct HTTP API with an API key. It then creates and configures .i18n/ai-adapter-sdk.mjs or .i18n/ai-adapter-api.mjs and asks the developer to complete the provider-specific parts. An existing adapter is not overwritten. For automated setup, select the template with --custom-ai-mode sdk or --custom-ai-mode api-key.

Additional API or internal agent runners can implement the same interface without changing the deterministic workflow. Built-in runners and their initialization behavior are registered centrally in src/cli/runners/configuration.mjs, so new providers do not require separate configuration branches in every platform initializer.

Typical developer flows

1. New locale or missing translations

  1. Add or confirm the target locale in .i18nrc.json
  2. Make sure you are logged into Codex locally
  3. Run i18n-flow translate
  4. Let the AI fill only missing keys

2. New UI feature with new English keys

  1. Add new keys to the English source file
  2. Run i18n-flow translate
  3. No locale or key parameters are needed; only the missing translations are selected automatically
  4. Multiple locales are translated in one AI call when they fit within the configured batch limits
  5. Existing manual translations stay untouched

3. Improve existing translations

  1. Run i18n-flow improve and select the keys and locales interactively, or pass them as CLI options
  2. For every selected target translation, the AI compares its current text with the corresponding text from the configured source locale, for example en-GB
  3. For a focused request, use for example i18n-flow improve --locale de-DE --keys 'intl-msg:app.*'
  4. The default conservative mode changes only translations that require an objective correction
  5. Pass --mode revise when the AI should also actively improve wording, clarity, consistency, and style
  6. Add --include-source-locale to improve the source locale first and use that updated source text as the reference for the selected target locales

4. English source text changed

  1. Update the label in the configured English source file
  2. Select keys with --keys or use --all
  3. Run, for example, i18n-flow retranslate --locale de-DE --keys 'intl-msg:app.*'
  4. Existing target translations for those keys are regenerated from the updated source text

5. Harmonize existing translation keys

i18n-flow harmonize-keys migrates keys in the configured source locale and every existing locale listed in targetLocales to the configured prefix. A key without a prefix receives it directly, for example home.title becomes intl-msg:home.title. Supported legacy forms such as intl-msg.home.title are normalized as well. An unknown prefix is preserved as a key namespace rather than interpreted as a typo: with intl-msg: configured, app:save becomes intl-msg:app.save.

The command also scans .js, .jsx, .ts, .tsx, .mjs, .cjs, .mts, and .cts files below the project root. It updates exact references to migrated keys. If the dynamic part is only a trailing suffix and the static prefix can be derived unambiguously from migrated locale keys, that prefix is updated too:

formatMessage({ id: 'home.title' }); // -> 'intl-msg:home.title'
const key = `home.${section}`; // -> `intl-msg:home.${section}`
const otherKey = 'app:' + action; // -> 'intl-msg:app.' + action

The migration is intentionally text-based rather than syntax- or usage-aware. Its main limitations are:

  • Unknown or misspelled prefixes are not corrected. For example, intl-mesg:home is treated as a namespace instead of a typo for intl-msg:home.
  • Dynamic prefixes are changed only when matching migrated locale keys imply one unambiguous replacement. Fully indirect keys, a dynamic beginning, or template literals with additional static text after the interpolation are left unchanged.
  • Source references outside the supported JavaScript/TypeScript extensions are not changed. Configuration entries, generated .i18n files, HTML, Vue, Svelte, Markdown, and other formats remain untouched.
  • Because replacement is text-based, matching text in comments or unrelated strings can also be changed. Review the preview before writing.
  • Only the configured source file and existing targetLocales files participate. A source reference with no matching old key in those locale files cannot be migrated.

Run i18n-flow harmonize-keys --dry-run first to review the key mapping and affected files. The command detects conflicting normalized keys with different values before it writes any files.

Why this approach

The workflow is intentionally built around a small local CLI instead of sending whole translation files to an AI manually.

  • The root .i18nrc.json is this repository's single source of truth for source files, target files, locales, AI runtime settings, batching, formatting, and checks similar to what we are known from Phrase.
  • AI calls are scoped to explicit jobs. A run translates, improves, or retranslates selected keys instead of rewriting entire locale files.
  • Existing target translations are preserved by translate, so manual fixes and reviewed translations are not overwritten accidentally.
  • Prompts include catalog and project context, UI style rules, output constraints, and protected terminology.
  • The AI response is constrained with a JSON schema and validated before it is written back.
  • Placeholder checks catch common breaking changes such as missing {name}, %s, or ${value} placeholders.
  • Protected terminology can be checked against the source and target text.
  • Each run writes a manifest and state summary so the requested keys, locales, model, duration, validation findings, and written files stay inspectable.

The main tradeoff is that the AI can preserve structure and follow context, but it cannot guarantee perfect wording, domain nuance, or consistency across all future changes without review and good catalog data.

Source of truth

The root .i18nrc.json is the app-specific configuration for:

  • the project platform
  • translation key prefix
  • source locale and source file
  • target locales
  • target file pattern
  • state file location
  • which catalog checkout and project context files should be given to the AI
  • which Codex CLI settings should be used
  • JSON formatting rules
  • translation checks
  • globally ignored keys

The current source locale is:

"sourceLocale": "en-GB"

That means:

  • translate reads missing source keys from the configured sourceFile
  • retranslate always uses the current English source text from that same source file

Important config sections

Core paths

{
  "platform": "web",
  "prefix": "intl-msg:",
  "sourceLocale": "en-GB",
  "sourceFile": "src/features/translations/codex/en-GB.json",
  "targetLocales": ["de-DE", "fr-FR"],
  "targetPathPattern": "src/features/translations/codex/{locale}.json",
  "stateFile": ".i18n/state.json"
}

Meaning:

  • platform selects the platform-specific command, initialization, help, and resource adapters. New Web configurations use web; existing configurations without this property remain compatible and are treated as Web projects.
  • prefix is the complete text prepended by harmonize-keys, including any separator. The default intl-msg: produces keys such as intl-msg:home.title. For example, my-app. produces my-app.home.title.
  • sourceLocale is the locale of the source messages. The current workflow expects en-GB.
  • sourceFile is the JSON file that contains the source messages.
  • targetLocales is the default locale scope for commands when no CLI locale override is provided. Locale files matching targetPathPattern but missing from this list are ignored. Before each task, the CLI prints a warning asking the user to add those locales to the configuration manually.
  • targetPathPattern is the path to target locale files. {locale} is replaced with each target locale.
  • stateFile stores the latest run summary. Job manifests are written next to it under jobs/.

Relative paths are resolved against the config file directory and project root. For non-src/ paths, the CLI also tries resolving under src/.

AI context

{
  "catalog": {
    "url": "[email protected]:company/i18n-catalog.git"
  },
  "contextPaths": [".i18n/context/project.md"]
}

Meaning:

  • catalog.url identifies the shared GitLab or Bitbucket repository. catalog.ref optionally selects its branch or tag; when omitted, the remote default branch is used. The CLI clones it into the ignored .i18n/cache/catalog/ directory and fetches updates before use. Initialization requires successful remote access even if a cache already exists. During later commands, a failed update falls back to an existing valid cache with a warning.
  • catalog.path remains available for a checked-out local catalog. A CLI --catalog source has highest precedence, followed by I18N_CATALOG_PATH, I18N_CATALOG_URL, and the configured source.
  • Technical output and integrity policies for JSON, keys, placeholders, ICU structures, and markup are built into the workflow and do not depend on the catalog.
  • contextPaths accepts project-specific Markdown files or directories. New configurations reference .i18n/context/project.md directly. Add further project files explicitly when needed. When a directory is configured, every direct .md file in it is loaded in alphabetical order.
  • Catalog guidance, terminology, protected terms, writing style, approved corrections, and shared messages are loaded first; project-local Markdown files are loaded afterwards. Project context extends the shared defaults and wins on conflicts involving terminology, protected terms, product names, audience, form of address, tone, or preferred wording. A project protected-term definition overrides an identically named catalog term. Neither catalog nor project context can override the built-in output-integrity rules for JSON, keys, placeholders, ICU structures, and markup.

The catalog also contains UI component contracts used by inspect-ui-components and shared messages synchronized into client translation files. Other catalog artifacts are translation context rather than client translation output.

Use i18n-flow test-catalog-connection to diagnose the configured catalog independently. For remote sources, the command checks the selected Git ref in a temporary checkout, validates catalog.json, and leaves both project files and the managed catalog cache unchanged. Pass --json for machine-readable CI output, or use --catalog and --catalog-ref to test an override.

AI runtime

{
  "ai": {
    "runner": "codex-cli",
    "command": "codex",
    "model": "gpt-5.6-terra",
    "reasoningEffort": "medium",
    "sandbox": "read-only",
    "timeoutMs": 300000
  }
}

AWS Bedrock example:

{
  "ai": {
    "runner": "bedrock",
    "model": "eu.anthropic.claude-sonnet-4-6",
    "region": "eu-central-1",
    "profile": "rio-development",
    "maxTokens": 8192,
    "timeoutMs": 600000
  }
}

region defaults to eu-central-1; --aws-region, AWS_REGION, and AWS_DEFAULT_REGION override it in that order. profile is optional and otherwise resolved through the standard AWS credential chain. Authenticate named IAM Identity Center profiles with aws sso login --profile <profile>. Do not store AWS access keys or session tokens in .i18nrc.json. The Bedrock SDK is an optional peer dependency and is loaded only when the Bedrock runner executes. The selected model must support Converse tool use with a forced named tool (toolChoice.tool). The runner submits the output schema as the submit_result tool and reads its structured input as the result, avoiding free-text JSON formatting errors. Property names with unsupported characters (such as :) or more than 64 characters receive temporary aliases in the tool schema and are restored in the response. Locale files keep their original keys.

Meaning:

  • runner selects codex-cli, bedrock, or command as the execution backend for AI translation
  • command points to the local Codex binary or custom adapter executable when that runner needs one
  • model selects the Codex model or Bedrock model/inference-profile ID. New Codex projects use the signed-in CLI's default model in automatic setup; Bedrock defaults to eu.anthropic.claude-sonnet-4-6.
  • reasoningEffort selects the Codex reasoning level. Model choices and model default efforts come from the signed-in CLI catalog during initialization.
  • region configures AWS Bedrock and defaults to eu-central-1; profile and maxTokens are optional
  • sandbox limits what codex exec may do while generating translations
  • timeoutMs sets the request timeout per locale call

For typical product UI translations, prefer the newest available Terra or Luna model and medium reasoning when that effort is supported. Use a Luna model for large, simple backfills where cost is the priority, and a Sol model for particularly important or terminology-heavy content. Compare the available models with a representative set of 50–100 keys before using a cheaper model for a large translation run.

Translation

{
  "translation": {
    "batchSize": 80,
    "deltaLocalesPerCall": 6,
    "maxParallelLocales": 4
  }
}

Meaning:

  • batchSize limits the number of translations in an AI request. For a large backfill it is applied per locale. For an automatically bundled multi-locale request it limits the total key-locale combinations in that request.
  • deltaLocalesPerCall limits how many locales translate, retranslate, or improve may bundle into one automatic multi-locale request. Its default is 6. Setting it to 1 disables multi-locale batching and creates a separate AI request for every locale. For example, one missing source key across 24 locales normally produces four chunks at the default value, provided batchSize allows all six key-locale combinations per chunk.
  • maxParallelLocales controls how many locale or locale-chunk AI calls can run at the same time.

Recommended starting point:

| Situation | batchSize | deltaLocalesPerCall | maxParallelLocales | | --- | ---: | ---: | ---: | | Normal project usage | 80 | 6 | 4 | | Rate limits or unstable runner | 40 | 3 | 1–2 | | Fast runner and small labels | 100 | 6–8 | 4–6 |

Change one setting at a time. Reduce batchSize when requests become too large or hard to retry, reduce deltaLocalesPerCall when multi-locale requests time out, and reduce maxParallelLocales when the runner reports rate limits. Higher values can reduce wall-clock time but do not improve translation quality by themselves.

Batching

Batching applies to all AI-backed commands.

  • It is controlled by translation.batchSize.
  • translate inspects missing keys, while retranslate and improve inspect the selected existing keys per locale. All three choose locale-oriented requests for large workloads and multi-locale requests when multiple locales fit safely into one request.
  • A multi-locale request never exceeds translation.batchSize key-locale combinations or translation.deltaLocalesPerCall locales.
  • Large locale workloads are processed in locale-first order. Once the command advances to the next locale, the previous locale is complete.
  • improve and retranslate continue automatically and bundle small selected-key workloads across locales. Large workloads stay locale-first. Passing --limit intentionally stops after one bounded batch.

This locale-first order makes progress predictable: once the command moves to the next locale, the previous locale is complete. A one-off run with --limit keeps the requested multi-locale scope and stops after one bounded batch.

Batching is used for practical reliability and cost control:

  • It keeps prompts small enough to stay within model context limits.
  • It avoids asking the model to reason about too many unrelated labels at once.
  • It limits the blast radius of a failed AI call. A failed batch can be retried without redoing all locales.
  • It makes long backfills resumable because each batch writes a manifest and then updates only the requested missing keys.
  • It keeps runtime predictable. batchSize caps how many missing keys a locale can process in one batch.
  • Automatic multi-locale chunks can run in parallel through translation.maxParallelLocales.

There is no strategy option. If three new keys are missing in 24 locales, translate groups locales automatically to reduce startup and prompt overhead. If many keys are missing, it keeps locale requests separate to preserve a focused prompt and predictable retries. The chosen execution strategy is recorded in the job manifest and shown in the output.

Parallelisation is controlled by translation.maxParallelLocales.

  • It lets independent locale or locale-chunk requests run at the same time.
  • It reduces wall-clock time for large translation jobs without increasing the size of each individual prompt.
  • It keeps failures isolated because each parallel worker still handles a bounded batch or locale chunk.
  • It is useful when many locales need the same kind of update and the Codex/API runtime can handle multiple requests concurrently.

The value should stay conservative. Higher parallelism can make a run faster, but it can also increase local CPU and memory usage, hit rate limits sooner, or make failures harder to read in the console output.

JSON formatting

{
  "jsonIndent": 4,
  "sortKeys": false,
  "newlineAtEnd": true
}

Meaning:

  • jsonIndent controls JSON indentation for files written by the CLI.
  • sortKeys sorts object keys deeply before writing when set to true.
  • newlineAtEnd controls whether written JSON files end with a trailing newline.

These settings are used for target translation files, job manifests, and state files written by the CLI.

Checks

{
  "checks": {
    "protectedTerms": "contextual"
  }
}

Meaning:

  • protectedTerms accepts "contextual" or "strict" and defaults to "contextual".
    • contextual supplies protected-term context to the AI and reports deterministic findings without blocking the job.
    • strict additionally fails when a protected term appears in the source but is missing verbatim from a target translation.

Protected-term findings are always recorded in the manifest. Placeholder mismatches are violations of the mandatory translation policy and always block the affected job. The deterministic protected-term check is lexical and cannot decide whether a potentially ambiguous name such as Perform or Exceptions was used as a product name or as a common word; use strict only when that tradeoff is acceptable.

Ignored keys

Keep ignoredKeys near the end of .i18nrc.json, where a growing list does not interrupt the core path and runtime settings:

{
  "ignoredKeys": [
    "intl-msg:legal.notice",
    "intl-msg:glossary.serviceDescription.*"
  ]
}

Exact keys and * patterns are excluded globally from translate, improve, and retranslate. harmonize-keys leaves them unchanged, and remove-unused-keys preserves them. For example, intl-msg:glossary.serviceDescription.* ignores every key below that prefix.

Job manifests and generated output

Short answer

The generated files under .i18n/jobs/ are records of individual command runs. .i18n/state.json is only a small pointer to the most recently written record. It is a state file, not a statistics file.

They are not a translation memory, do not track which source texts changed, and are not read to decide what a later command should translate. Every command recalculates its work from the current source and target translation files.

The potentially confusing part is that the CLI also builds a job manifest in memory before calling AI. That in-memory plan supplies the selected texts, keys, locales, context, and settings for the current AI request. Once the run finishes, the CLI writes the completed manifest to disk as an audit and debugging record. The saved JSON file is not fed back into the AI on the next run.

.i18nrc + CLI options + current translation files + catalog
                            │
                            ▼
                 job plan created in memory
                            │
                            ▼
              current AI request and validation
                            │
                            ▼
             translations written to locale files
                            │
                            ▼
       completed job manifest saved for diagnostics
                            │
                            ▼
       state.json points to that most recent manifest

Generated job artifacts

The generated job manifests and state file serve different purposes:

| Concept | Purpose | Created by | | --- | --- | --- | | In-memory job manifest | Concrete plan used to build and execute the current AI request | CLI, during the current run | | .i18n/jobs/<mode>-<timestamp>.json | Saved plan and result record for diagnostics and auditing | CLI, after a batch is prepared or completed | | .i18n/state.json (stateFile) | Pointer and compact summary for the most recently saved manifest | CLI, when a manifest is saved |

The resolved CLI options become part of the manifest. For example, --locale, --keys, --context, --model, --reasoning-effort, and the improve-specific --mode determine the concrete locale scope, key scope, instructions, improve mode, and AI settings recorded for that batch.

A manifest contains the information needed to understand what the CLI selected and what happened, including:

  • command mode, resolved improve mode when applicable, timestamp, locales, and keys
  • source and target file references
  • source texts, current target texts, and placeholders for the selected entries
  • catalog and project context files
  • resolved AI settings and the automatic batching strategy
  • validation findings
  • after a successful AI run: request count, duration, provider metadata, and updated-key counts

The lifecycle of a manifest is:

  1. The CLI combines .i18nrc.json and CLI options.
  2. It reads the current translation files, discovers the applicable keys and locales, and creates one bounded batch plan in memory.
  3. With --dry-run, it writes that plan without updating stateFile and stops before calling AI.
  4. During a normal run, the CLI uses the in-memory plan to build the current AI request. It validates and writes the returned translations, adds aiRun result metadata, and saves the completed manifest.
  5. A long command can create multiple manifests because every processed batch has its own plan and result record. stateFile points to the most recently written one.

By default, manifests are written to .i18n/jobs/<mode>-<timestamp>.json. When stateFile uses another directory, the jobs/ directory is created next to that state file.

The saved manifest is useful for reviewing the selected scope, diagnosing unexpected output, understanding which model and context were used, and retaining CI/debugging evidence. It is not consumed by the client application, is not read by later translation commands, and is not the source of truth for translations.

How later commands determine their work

  • translate compares the current source file with the current target locale files and selects missing string values.
  • improve uses the source text and current target translation for each selected key. With --include-source-locale, it improves the source locale first and uses the updated source text for the selected target locales.
  • retranslate uses the currently selected keys and current source and target texts. It does not automatically detect source-text changes from previous manifests. Existing placeholder or protected-term violations in the selected target texts do not block retranslation because those values are about to be replaced. Newly generated translations still pass the complete mandatory policy before any locale file is written.
  • terminology and approved wording come from the current catalog and project context, not from previous jobs.

Deleting .i18n/jobs/ or .i18n/state.json therefore does not remove translations and does not prevent future runs. It only removes the diagnostic records and the pointer to the most recent record.

--dry-run does not create a queued or paused job. There is currently no command that executes an existing manifest later. To continue or retry work, run the original translation command again; it recalculates the remaining scope from the current source and target files. --out <path> only changes where the generated manifest is written.

Should manifests be committed?

Normally, no. Job manifests and stateFile are transient runtime artifacts. They can change on every run, contain source and target text plus execution metadata, and create noisy repository history. i18n-flow init therefore adds .i18n/state.json and .i18n/jobs/ to .gitignore automatically. An explicit --out path should also remain untracked unless a team deliberately wants to preserve one diagnostic artifact. If stateFile is moved from its default location, add the custom state file and its adjacent jobs/ directory to .gitignore manually.

Commit these project inputs and outputs instead:

  • .i18nrc.json
  • project context files under .i18n/context/
  • custom AI adapter source code, without credentials
  • source and target translation files

For CI, prefer uploading manifests as temporary build artifacts when they are useful for troubleshooting instead of committing them to the repository.

Removing unused keys

remove-unused-keys reviews keys without exact source references before changing locale files. In an interactive terminal, it opens a checkbox list. Reliable dynamic-prefix matches and plural families with a statically referenced base key are treated as used and do not appear. Keys with no inferred reference are preselected; ambiguous dynamic matches and unreferenced plural keys remain unselected and are annotated as uncertain dynamic reference or plural key. Press a to toggle all candidates, or use Space to select individual keys.

Use --dry-run to print the complete annotated candidate list without changing files. Non-interactive write mode requires --yes and removes only the safely preselected candidates. Use --all --yes to explicitly remove every candidate, including dynamically referenced and plural-family keys. --keep <a,b> and configured ignoredKeys exclude keys from the review entirely.

CLI options

Most commands support the same selection and execution options:

  • --config <path> uses a different config file instead of .i18nrc.json.
  • --locale <locale> or --locales <locale1,locale2> overrides the configured locale scope.
  • --keys <key1,key2> overrides the configured key scope, including when selecting only one key.
  • --context <text> adds one-off instructions for the AI, for example a preferred tone. The instructions are stored in the job manifest and apply to every AI request in that run. They do not override translation policy or mandatory terminology context.
  • --model <model> overrides the configured AI model for one translation run. The selected model is stored in the job manifest and passed to the configured runner.
  • --reasoning-effort <level> overrides the configured reasoning effort for one translation run. Codex CLI accepts the level names supported by the installed CLI and model; the value is passed to Codex for validation. Bedrock and custom-command runners accept none, low, medium, high, xhigh, or max. The selected value is stored in the job manifest.
  • improve --mode <conservative|revise> selects the review behavior. Conservative mode changes text only when an objective correction is required. Revise mode actively improves wording, clarity, consistency, and style while keeping the existing translation as its basis.
  • When improve runs interactively without a CLI key and locale scope, it asks which keys and target locales to improve before showing the plan and overwrite confirmation.
  • --all selects every source key. This is especially useful with improve and retranslate; --limit can still constrain a one-off run.
  • --limit <number> overrides translation.batchSize for the current run.
  • --keys <a,b> accepts exact keys and anchored * patterns such as intl-msg:app.*.
  • improve --include-source-locale improves the configured source locale first. Target locales in the same batch are then improved using the updated source texts. Other commands reject this modifier.
  • --out <path> writes the generated manifest to an explicit path.
  • --dry-run builds and writes the manifest without calling AI, changing translations, or updating stateFile.
  • improve and retranslate ask for confirmation before overwriting translations. In non-interactive environments, pass --yes to confirm the run. --dry-run does not require confirmation.

Shared-message sync

shared-messages catalog artifacts define reusable application messages that are not tied to a UIKIT component, such as the generic error fallback. Catalog keys do not contain the project prefix. For a project configured with "prefix": "intl-msg:", the catalog key common-message.error.generic.headline therefore becomes intl-msg:common-message.error.generic.headline.

Before planning a translate job, i18n-flow scans JavaScript and TypeScript source files for exact references to catalog shared-message keys. For every used key it:

  1. copies the catalog source into the configured source-locale file;
  2. copies a target value only when the catalog contains a reviewed translation for that locale;
  3. leaves all other target locales missing so the normal translation job generates them;
  4. preserves unrelated project messages and ignores shared catalog messages that the project does not reference.

This makes a template reference such as the following sufficient; developers do not need to copy catalog messages or know which locale files contain them:

<FormattedMessage id='intl-msg:common-message.error.generic.headline' />

Run the sync explicitly to apply catalog values without starting an AI translation, or preview it without writing:

i18n-flow sync-shared-messages
i18n-flow sync-shared-messages --dry-run
i18n-flow sync-shared-messages --dry-run --json

The sync treats catalog values as canonical and updates a differing project value. translate --dry-run also previews the automatic sync in memory but does not change locale files.

Catalog-key migration

Shared messages and UI-component message definitions can declare proven predecessor keys as legacyKeys. Migrate Phrase-era locale entries and their JavaScript or TypeScript references together with:

i18n-flow migrate-to-catalog-keys --dry-run
i18n-flow migrate-to-catalog-keys

The migration replaces exact quoted legacy references in source files, writes the canonical catalog value when one is available (otherwise preserving the existing legacy locale value), and removes migrated legacy locale entries. It only uses explicit catalog mappings and is idempotent. sync-shared-messages and translate do not perform this source-code migration implicitly.

UI component inspection

inspect-ui-components reads ui-component-contract artifacts from an external i18n catalog and scans JavaScript and TypeScript client source files. Component and prop knowledge comes from the catalog; the CLI contains no hard-coded UIKIT component list.

The catalog source is read from .i18nrc.json. Override it for a single run through --catalog <source>. A local checkout can also be supplied through I18N_CATALOG_PATH; remote deployment configuration can use I18N_CATALOG_URL and I18N_CATALOG_REF:

i18n-flow inspect-ui-components --catalog ../i18n-catalog
i18n-flow inspect-ui-components --catalog [email protected]:company/i18n-catalog.git --json
i18n-flow inspect-ui-components --fix

The result reports hard-coded, missing, already aligned, and custom-expression props. Catalog keys are prefix-free; the configured project prefix is applied to every reported expectedKey and when looking up project messages. The result also includes canonical catalog messages and their current values in the configured project locale files. The command is read-only by default and does not call AI; its JSON output is intended as a structured task for a client AI agent.

A component contract can define multiple semantic variants for the same physical prop. The inspector resolves a variant from an already canonical key, a referenced project message with the same catalog value, or a hard-coded catalog value:

{
    "TreeOption": {
        "label": {
            "variants": {
                "showEmptyGroups": "treeOption.label.showEmptyGroups",
                "showGroups": "treeOption.label.showGroups"
            }
        }
    }
}

When exactly one variant matches, the result contains its canonical expectedKey and variant name. When the intended meaning cannot be inferred safely, the prop is reported as ambiguous-variant with all candidate keys and remains unchanged by --fix.

With --fix, the command copies every available canonical catalog message to its configured project locale file. It also normalizes a component prop expression when that expression contains exactly one non-canonical message key with the configured prefix. Hard-coded values, missing props, and ambiguous expressions remain findings because the CLI cannot safely infer or introduce the consuming application's i18n access pattern. Use --fix --dry-run to preview the counts without changing source or translation files.

Example commands

Available commands:

i18n-flow init
i18n-flow install-skill
i18n-flow inspect-ui-components
i18n-flow sync-shared-messages
i18n-flow migrate-to-catalog-keys
i18n-flow validate
i18n-flow translate
i18n-flow improve
i18n-flow retranslate
i18n-flow remove-unused-keys
i18n-flow harmonize-keys
i18n-flow cleanup-phrase
i18n-flow test-catalog-connection
i18n-flow test-ai-connection
i18n-flow help

Examples:

# Create a structured localization task for catalog-supported UI components
i18n-flow inspect-ui-components --catalog ../i18n-catalog --json

# Apply available catalog messages and safely replace non-canonical keys
i18n-flow inspect-ui-components --fix

# Verify Bitbucket/Git read access and the catalog manifest without changing the cache
i18n-flow test-catalog-connection

# Replace the installed repository skill with the version shipped by this package
i18n-flow install-skill --update

# Validate every existing target translation without changing files or calling AI
i18n-flow validate

# Validate one configured locale
i18n-flow validate --locale de-DE

# Normal feature workflow: translate every newly added or otherwise missing key
i18n-flow translate

# Build the manifest only and skip the AI call
i18n-flow translate --dry-run

# Translate selected labels with one-off AI instructions
i18n-flow translate --locale de-DE --keys 'intl-msg:batteryAnalyticsSurvey.*' --context 'The text should be informal'

# Improve existing translations for selected keys
i18n-flow improve --locale de-DE --keys 'intl-msg:app.*'

# Actively revise wording, clarity, consistency, and style
i18n-flow improve --locale de-DE --keys 'intl-msg:app.*' --mode revise

# Improve selected German translations once with the quality-focused model
i18n-flow improve --locale de-DE --keys 'intl-msg:slider.batteryAnalyticsSurvey.*' --model gpt-5.6-sol

# Use additional reasoning for a difficult translation run
i18n-flow improve --locale de-DE --keys 'intl-msg:slider.batteryAnalyticsSurvey.*' --reasoning-effort high

# Improve the complete German locale
i18n-flow improve --locale de-DE --all

# Retranslate existing translations because the English source changed
i18n-flow retranslate --keys intl-msg:foo.bar --locale de-DE

# Retranslate the complete Brazilian Portuguese locale from the current English source
i18n-flow retranslate --locale pt-BR --all

# Preview key normalization without changing files
i18n-flow harmonize-keys --dry-run

# Preview obsolete Phrase configuration and package scripts after migration
i18n-flow cleanup-phrase --dry-run

# Remove the reviewed Phrase artifacts without an interactive prompt
i18n-flow cleanup-phrase --yes

Prefer a focused locale and key selection for improve. --all selects every source key in the selected locale scope; it can take a long time, make many AI requests, and consume substantially more AI tokens. Use it only when a complete review is intended.

Repository structure

Production code under src/ has three top-level areas:

  • core/
    • platform-independent jobs, prompts, policies, validation, ports, and workflow orchestration
    • imports neither cli nor a concrete platform
  • cli/
    • the unified i18n-flow binary, argument parsing, command handlers, terminal UI, and replaceable AI runners
  • platforms/
    • platform registries and concrete resource, discovery, initialization, help, and asset integrations
    • the Web platform owns the JSON resource adapter and repository-scoped frontend skill
    • the JVM platform owns Java-properties bundles, explicit locale/file mappings, and default-bundle strategies
    • the iOS platform owns Apple .strings parsing, encoding preservation, and .lproj locale mappings

Tests and fixtures live separately under the root-level test/ directory.

Repository skill

i18n-flow init installs the packaged skill at .agents/skills/i18n/SKILL.md without replacing an existing file. This keeps the AI instructions versioned with the client repository and makes catalog-backed UI component conventions available while code is written. Run i18n-flow install-skill to install it separately, or i18n-flow install-skill --update to explicitly replace an installed copy with the version from the package. The installed metadata.version is generated from the @rio-cloud/i18n package version. Without --update, the installer reports whether the checked-in skill is current, outdated, or from an older unversioned installation.

Publishing

This package is intended for RIO projects and is published under the public @rio-cloud scope on npmjs.org. Publishing requires an npm account with permission to publish packages in that scope and the configured npm two-factor authentication or publishing token.

Before publishing, update the version in package.json and package-lock.json, add the release notes to CHANGELOG.md, and commit those changes. Then run the release from the repository root:

npm login
npm whoami
npm publish

The package's publishConfig selects npmjs.org and public access, so no additional registry or access flags are needed. npm publish runs npm run test-all through prepublishOnly and stops before publishing if type checking, formatting, or tests fail. Each version can be published only once. After a successful release, verify it with:

npm view @rio-cloud/i18n version

License

Licensed under the Apache License 2.0.