--- url: /guide/getting-started.md description: >- Set up intl-ai in minutes. Install the plugin, configure your AI model, and translate. --- # Getting Started This guide will help you set up `@intl-ai/unplugin` in your project. ## Installation Pick the channel that matches your workflow. The bundler plugin and the CLI binary are independent: use either or both. ### Bundler plugin Install into your project. Works with Vite, Webpack, Rollup, esbuild, Rspack, Rolldown, and Farm. ::: code-group ```sh [npm] npm install -D @intl-ai/unplugin ``` ```sh [pnpm] pnpm add -D @intl-ai/unplugin ``` ```sh [yarn] yarn add -D @intl-ai/unplugin ``` ```sh [bun] bun add -D @intl-ai/unplugin ``` ::: For Next.js, swap to `@intl-ai/next` and follow the [Next.js setup](/guide/build-systems/next-js). ### CLI binary Install the `intl-ai` command globally. ::: code-group ```sh [Homebrew] brew install sigilco/tap-intl-ai/intl-ai ``` ```sh [mise] mise use npm:intl-ai@latest ``` ```sh [install.sh] curl -fsSL https://intl-ai.pages.dev/install.sh | bash ``` ::: Override the install path with `INTL_AI_INSTALL_DIR` and pin a version with `INTL_AI_VERSION` before running `install.sh`. ### No install Run the CLI through `npx` if you don't want to install globally. ```bash npx @intl-ai/cli fill ``` Requires Node.js 22+ and an [AI model provider](/guide/ai-model). Verify with `intl-ai --help`. ## Quick Start ### 1. Create Configuration File Create an `intl-ai.config.ts` file in your project root. If you do not have a local model or cloud API key, you can use OpenRouter's free tier with no account setup beyond an API key. ### Local model (LM Studio) ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "qwen3.5-4b-instruct", apiKey: "lm-studio", baseURL: "http://127.0.0.1:1234/v1", defaultLocale: "en", locales: ["en", "de", "es", "fr"], localeDir: "./locales", }; ``` ### Cloud model (OpenRouter free tier) ```typescript import { resolveProvider } from "@intl-ai/api/internal"; const openrouter = resolveProvider("openai"); export default { provider: openrouter, model: "google/gemini-2.0-flash-exp:free", apiKey: "${OPENROUTER_API_KEY}", baseURL: "https://openrouter.ai/api/v1", defaultLocale: "en", locales: ["en", "de", "es", "fr"], localeDir: "./locales", }; ``` See [AI model setup](/guide/ai-model) for all provider options. **Key Configuration:** * `provider`: Provider ID or AIProvider instance (e.g. `"openai"`, `"anthropic"`, or a custom provider) * `model`: Model name passed to the provider * `apiKey`: Your API key (use `${ENV_VAR}` for environment variables) * `baseURL`: Provider endpoint URL * `defaultLocale`: The primary language for your application ### 2. Set Up Your Bundler ::: code-group ```typescript [Vite] import { defineConfig } from "vite"; import intlAi from "@intl-ai/unplugin/vite"; export default defineConfig({ plugins: [intlAi()], }); ``` ```javascript [Webpack] const IntlAiPlugin = require("@intl-ai/unplugin/webpack"); module.exports = { plugins: [new IntlAiPlugin()], }; ``` ::: See [Build systems](/guide/build-systems/) for Next.js, Rollup, esbuild, Rspack, Rolldown, Farm, and more. ### 3. Create Directory and Translation Files Create the directory specified in your config (default: `./locales`), then add your first translation file for the default locale: **locales/en.json:** ```json { "greeting": "Hello, {name}!", "welcome": "Welcome to our application", "description": "This is a sample translation" } ``` ### 4. Run Translation Run the CLI to fill in translations for your target locales: ```bash intl-ai fill ``` Or use `runFill` programmatically from `@intl-ai/api`: ```typescript import { runFill } from "@intl-ai/api"; import type { IntlAiConfig } from "@intl-ai/api"; const config: IntlAiConfig = { defaultLocale: "en", locales: ["en", "de", "es", "fr"], localeDir: "./locales", model: "openai", apiKey: "${OPENAI_API_KEY}", baseURL: "https://api.openai.com/v1", }; const result = await runFill(config); // { locales: ["de", "es", "fr"], translated: 12, skipped: 0, errors: 0 } ``` This produces `locales/de.json`, `locales/es.json`, and `locales/fr.json` with AI-generated translations, plus a `locales/intl-ai.lock.json` lockfile that tracks which keys were translated and their source hashes. ### 5. Optional: Check Translation Quality After filling, run `check` to detect missing keys, stale translations (keys whose source changed), and extra keys with no source: ```bash intl-ai check ``` Or use `runCheck` programmatically from `@intl-ai/api`: ```typescript import { runCheck } from "@intl-ai/api"; const result = await runCheck(config, { locale: "es" }); // { results: [{ locale: "es", missing: [...], stale: [...], extra: [...] }], hasIssues: true } ``` `runCheck` is read-only. It writes nothing to disk and does not call hooks (hooks fire only during `runFill`). Use it in CI to enforce translation completeness before deploying. ## Supported Bundlers `@intl-ai/unplugin` works with all major bundlers. See [Build systems](/guide/build-systems/) for dedicated setup guides: * [Vite](/guide/build-systems/vite) - Modern, fast build tool * [Webpack](/guide/build-systems/webpack) - Industry standard bundler * [Rollup](/guide/build-systems/rollup) - Flexible module bundler * [esbuild](/guide/build-systems/esbuild) - Extremely fast JavaScript bundler * [Rspack](/guide/build-systems/rspack) - Rust-based, webpack-compatible bundler * [Rolldown](/guide/build-systems/rolldown) - Rust-powered Rollup-compatible bundler * [Farm](/guide/build-systems/farm) - Rust-based web build tool * [Next.js](/guide/build-systems/next-js) - React framework with Turbopack bridge ## Verify Installation To verify everything is working: 1. Start your development server: `npm run dev`, `pnpm dev`, or `yarn dev` 2. Check that your bundler loads without errors and translation files are being processed 3. Verify translations render correctly in your application If you encounter issues, check the [AI model setup](/guide/ai-model) guide to ensure your model provider is configured correctly. --- --- url: /guide/ai-model.md description: >- Configure any AI model for intl-ai. OpenAI, Anthropic, Ollama, or bring your own compatible API. --- # AI model setup Translation is structured and instruction-following. Budget models handle it well, so you do not need a flagship model. ## Pick a provider Three paths work: local, cloud, or an aggregator. ### Local: LM Studio [LM Studio](https://lmstudio.ai) runs models locally. This is ideal for development, testing, and privacy-sensitive work. Download LM Studio, load any model that fits your hardware, and start the local server. ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "qwen3.5-4b-instruct", apiKey: "lm-studio", baseURL: "http://127.0.0.1:1234/v1", defaultLocale: "en", locales: ["en", "de", "es", "fr"], localeDir: "./locales", }; ``` ### Cloud: OpenAI-compatible providers Any provider with an OpenAI-compatible endpoint works the same way: set the provider ID, model name, API key, and base URL. * **OpenAI**: set `OPENAI_API_KEY`. See [platform.openai.com](https://platform.openai.com). * **Anthropic**: set `ANTHROPIC_API_KEY`. See [console.anthropic.com](https://console.anthropic.com). * **Google**: set `GOOGLE_GENERATIVE_AI_API_KEY`. See [aistudio.google.com](https://aistudio.google.com). * **Azure OpenAI**, **Cohere**, **Mistral**, and others: use the matching base URL and a custom AIProvider if the API shape differs from OpenAI. Example with OpenAI: ```typescript export default { provider: "openai", model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", baseURL: "https://api.openai.com/v1", }; ``` Example with Anthropic: ```typescript export default { provider: "anthropic", model: "claude-3-5-haiku-latest", apiKey: "${ANTHROPIC_API_KEY}", baseURL: "https://api.anthropic.com/v1", modelParams: { max_tokens: 1024 }, }; ``` ### Aggregator: OpenRouter [OpenRouter](https://openrouter.ai) gives you one API key for many providers and a free tier. A stable free model at the time of writing is `google/gemini-2.0-flash-exp:free`. If it stops working, check OpenRouter's free model list and update this single reference. ```typescript import { resolveProvider } from "@intl-ai/api/internal"; const openrouter = resolveProvider("openai"); export default { provider: openrouter, model: "google/gemini-2.0-flash-exp:free", apiKey: "${OPENROUTER_API_KEY}", baseURL: "https://openrouter.ai/api/v1", }; ``` ## Context window Use a model with a minimum context window of 16,000 tokens. Every provider listed above exceeds this. ## Troubleshooting If translation fails, check: * The API key environment variable is set. * The provider ID in your config matches a supported provider. * The provider's server is reachable from your machine. * Your model identifier matches the provider's documentation. --- --- url: /guide/configuration.md description: >- intl-ai config file reference. Single JSON or TypeScript file, validated against a published schema. --- # Configuration intl-ai reads a single config file. The JSON format is validated against a published JSON Schema and works in any runtime. ## Config file discovery The CLI and bundler plugins look for one of these files in your project root: * `intl-ai.config.json` (recommended for runtime-agnostic setups and non-Node consumers) * `intl-ai.config.ts` (when you need a custom AIProvider instance) ## JSON config ```json { "$schema": "https://www.schemastore.org/intl-ai.json", "defaultLocale": "en", "locales": ["en", "es", "fr"], "localeDir": "./locales", "provider": "openai", "model": "gpt-4o-mini", "apiKey": "${OPENAI_API_KEY}", "baseURL": "https://api.openai.com/v1", "maxRetries": 3 } ``` ## Required options ### `defaultLocale` Source language for translations. ```json "defaultLocale": "en" ``` ### `locales` All supported locale codes. ```json "locales": ["en", "es", "fr"] ``` ### `localeDir` Directory containing locale files. The structure depends on the chosen `format`: * JSON (default): `${localeDir}/${locale}.json` * YAML: `${localeDir}/${locale}.yaml` ```json "localeDir": "./locales" ``` ### `provider` Provider ID. Built-in providers: `openai`, `anthropic`. Custom providers can pass an `AIProvider` instance directly. ```json "provider": "openai" ``` For a custom provider, use `resolveProvider` from `@intl-ai/api/internal`: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", defaultLocale: "en", locales: ["en", "es"], localeDir: "./locales", }; ``` ### `apiKey` API key for your provider. We recommend reading it from an environment variable. ```json "apiKey": "${OPENAI_API_KEY}" ``` ### `model` Model name passed to the provider. The model name format depends on your provider. ```json "model": "gpt-4o-mini" ``` ## Optional options ### `baseURL` Provider endpoint. Defaults to `https://api.openai.com/v1`. ```json "baseURL": "https://api.openai.com/v1" ``` ### `glossary` Terms to preserve during translation. ```json "glossary": { "React": "React", "TypeScript": "TypeScript" } ``` ### `maxRetries` Maximum retry attempts for failed translations. Default is `3`. ```json "maxRetries": 3 ``` ### `processor` Syntax processor. Use `icu` for ICU MessageFormat or omit for passthrough. ```json "processor": "icu" ``` Built-in processors: `passthrough`, `icu`. ## Locale formats intl-ai supports JSON and YAML locale files out of the box. ### JSON (default) JSON is the default format. No configuration needed: ```json { "greeting": "Hello", "farewell": "Goodbye" } ``` ### YAML YAML supports nested keys and is useful for larger projects: ```yaml greeting: Hello farewell: Goodbye nested: welcome: Welcome back ``` To use YAML, set `format` in your config: ```json { "format": "yaml" } ``` ### Other formats For custom locale formats (CSV, TOML, or custom file formats), intl-ai does not include a built-in adapter. Use one of these approaches: **Interactive translation (recommended for most cases):** Use the `intl-ai-translate-fill` skill in an opencode agent session. The skill is format-agnostic and works with any file format. **Batch CI translation:** Build a custom `LocaleFormat` adapter using the `@intl-ai/api` package. See `intl-ai-format-strategy` for guidance on when to build an adapter vs. use a skill. **Rule of thumb:** Start with the skill. Build an adapter only when you need batch CI on a format the CLI does not support natively. ### Batch size `batchSize` controls how many keys are sent in a single translation request. Default is unlimited (all keys in one batch). Reduce for models with lower context windows or to get more granular per-key quality control. ```json { "batchSize": 50 } ``` For most JSON and YAML files, the default (unlimited batch) works well. ## Editor intellisense Add `"$schema": "https://www.schemastore.org/intl-ai.json"` to your JSON config for autocomplete and validation in VS Code, JetBrains, and other editors. ## CI validation Validate a config file in CI with any JSON Schema tool: ```bash # check-jsonschema pip install check-jsonschema check-jsonschema --schemafile https://www.schemastore.org/intl-ai.json intl-ai.config.json ``` ## TypeScript config When you need to pass a custom AIProvider instance, use a TypeScript config: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", baseURL: "https://api.openai.com/v1", defaultLocale: "en", locales: ["en", "es"], localeDir: "./locales", }; ``` See [Providers](/guide/providers) for how the provider system works, and [AI model setup](/guide/ai-model) for provider options. ### Parallel locale processing (CLI) When using the CLI (`intl-ai fill`), `--concurrency` controls how many locales are processed in parallel. Default is 4. ```bash intl-ai fill --concurrency 8 ``` Range: 1 to 16. --- --- url: /guide/build-systems.md description: >- intl-ai supports Vite, Webpack, Rollup, esbuild, Rspack, Rolldown, and Farm via unplugin. Pick your bundler. --- # Build systems intl-ai runs at build time via `@intl-ai/unplugin`. It supports every major bundler through [unjs/unplugin](https://github.com/unjs/unplugin). Choose your bundler below. * [Vite](/guide/build-systems/vite) - Modern, fast build tool * [Webpack](/guide/build-systems/webpack) - Industry standard bundler * [Rollup](/guide/build-systems/rollup) - Flexible module bundler * [esbuild](/guide/build-systems/esbuild) - Extremely fast JavaScript bundler * [Rspack](/guide/build-systems/rspack) - Rust-based, webpack-compatible bundler * [Rolldown](/guide/build-systems/rolldown) - Rust-powered Rollup-compatible bundler * [Farm](/guide/build-systems/farm) - Rust-based web build tool * [Next.js](/guide/build-systems/next-js) - React framework with Turbopack bridge If you use a framework, wire up the bundler here and pair it with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/vite.md description: 'AI-translate Vite i18n locale files at build time. Zero runtime, any AI model.' --- # Vite ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```typescript import { defineConfig } from "vite"; import IntlAi from "@intl-ai/unplugin/vite"; export default defineConfig({ plugins: [IntlAi()], }); ``` Works with any Vite-based framework (Vue, React, Svelte, Solid). Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/webpack.md description: >- AI-translate Webpack i18n locale files at build time. Zero runtime, any AI model. --- # Webpack ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript const IntlAi = require("@intl-ai/unplugin/webpack"); module.exports = { plugins: [new IntlAi()], }; ``` For Next.js, see [Next.js](/guide/build-systems/next-js). Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/rollup.md description: >- AI-translate Rollup i18n locale files at build time. Zero runtime, any AI model. --- # Rollup ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript import IntlAi from "@intl-ai/unplugin/rollup"; export default { plugins: [IntlAi()], }; ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/esbuild.md description: >- AI-translate esbuild i18n locale files at build time. Zero runtime, any AI model. --- # esbuild ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript import IntlAi from "@intl-ai/unplugin/esbuild"; import { build } from "esbuild"; build({ plugins: [IntlAi()], }); ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/rspack.md description: >- AI-translate Rspack i18n locale files at build time. Zero runtime, any AI model. --- # Rspack ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript const IntlAi = require("@intl-ai/unplugin/rspack"); module.exports = { plugins: [new IntlAi()], }; ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/rolldown.md description: >- AI-translate Rolldown i18n locale files at build time. Zero runtime, any AI model. --- # Rolldown ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript import IntlAi from "@intl-ai/unplugin/rolldown"; export default { plugins: [IntlAi()], }; ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/farm.md description: 'AI-translate Farm i18n locale files at build time. Zero runtime, any AI model.' --- # Farm ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin ``` ```sh [pnpm] pnpm add @intl-ai/unplugin ``` ```sh [yarn] yarn add @intl-ai/unplugin ``` ```sh [bun] bun add @intl-ai/unplugin ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```javascript import IntlAi from "@intl-ai/unplugin/farm"; export default { plugins: [IntlAi()], }; ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/build-systems/next-js.md description: >- Build-time AI translation for Next.js i18n. Zero runtime overhead, any AI model. --- # Next.js Next.js 15+ defaults to Turbopack. Use `@intl-ai/next` to register the Turbopack loader. For Next.js 14 with webpack only, use `@intl-ai/unplugin/webpack` directly. ## Installation ::: code-group ```sh [npm] npm install @intl-ai/next ``` ```sh [pnpm] pnpm add @intl-ai/next ``` ```sh [yarn] yarn add @intl-ai/next ``` ```sh [bun] bun add @intl-ai/next ``` ::: ## Configuration Create an `intl-ai.config.ts` at your project root. See [Configuration](/guide/configuration) for the full schema. ```typescript import withIntlAi from "@intl-ai/next"; export default withIntlAi({ reactStrictMode: true, }); ``` No changes to your app code required. Translations are generated at build time with zero runtime overhead. ## Next.js 14 (webpack only) If you are on Next.js 14 with webpack, use `@intl-ai/unplugin/webpack` directly: ```typescript import intlAiWebpackPlugin from "@intl-ai/unplugin/webpack"; export default { webpack(config) { config.plugins = config.plugins || []; config.plugins.push(intlAiWebpackPlugin()); return config; }, }; ``` Pair with an i18n library from [i18n libraries](/guide/i18n-libraries/). --- --- url: /guide/i18n-libraries.md description: >- intl-ai generates locale files at build time. Use with vue-i18n, i18next, or any i18n library. --- # i18n libraries intl-ai generates translation files at build time. You choose the runtime i18n library. This page lists the supported options and their ICU compatibility. ## ICU MessageFormat Most modern i18n libraries speak ICU MessageFormat. Set `processor: "icu"` in your config to use it. The main exception is i18next, which uses `{{var}}` interpolation and plural suffixes instead of ICU syntax. ## Library compatibility | Library | Native ICU | Setup | Best for | | ----------------------------- | ------------------------- | ------------------------ | -------------------------------------- | | react-intl | Yes | None | React apps that need ICU | | @formatjs/intl | Yes | None | Framework-agnostic ICU runtime | | lingui | Yes | Build macro | Compile-time safety, any framework | | i18next-icu | Yes (via plugin) | Install `i18next-icu` | Existing i18next apps migrating to ICU | | vue-i18n + intl-messageformat | Yes (via custom compiler) | Custom `messageCompiler` | Vue apps that need ICU | | svelte-i18n | Partial | Manual formatter calls | Svelte apps | | @cookbook/solid-intl | Yes | None | Solid apps that need ICU | | @lit/localize | Partial | Community workaround | Web components | | typesafe-i18n | No | N/A | Type-safe i18n without ICU | | rosetta | No | N/A | Tiny footprint (298 bytes) | | @solid-primitives/i18n | No | N/A | Minimal Solid i18n | ## Configuration Set `processor: "icu"` in your `intl-ai.config.ts` when your target library uses ICU MessageFormat. Most modern libraries do. If you use i18next, omit the processor setting or set `processor: "passthrough"` to preserve its `{{var}}` style. ```ts // intl-ai.config.ts export default { // ... your provider setup defaultLocale: "en", locales: ["en", "es", "fr"], localeDir: "./locales", processor: "icu", // recommended for most libraries }; ``` ## Per-library guides * [Vue (vue-i18n)](/guide/i18n-libraries/vue-i18n) * [i18next](/guide/i18n-libraries/i18next) ## Choosing a library | Need | Use | | ------------------------ | --------------------------------------------- | | React + ICU | `react-intl` | | Vue + ICU | `vue-i18n` with `intl-messageformat` compiler | | i18next syntax (no ICU) | `i18next` + `react-i18next` | | Compile-time type safety | `lingui` or `typesafe-i18n` | | Smallest bundle | `rosetta` | --- --- url: /guide/i18n-libraries/vue-i18n.md description: >- AI-translate vue-i18n locale files at build time. Works with any AI model, zero runtime cost. --- # Vue (vue-i18n) This guide covers vue-i18n consumption. For bundler setup, see [Build systems](/guide/build-systems/). ## Overview intl-ai generates translation JSON files at build time. vue-i18n consumes these files at runtime in your Vue application. This guide shows how to set up both tools together. ## Installation Install intl-ai and vue-i18n: ::: code-group ```sh [npm] npm install @intl-ai/unplugin vue-i18n ``` ```sh [pnpm] pnpm add @intl-ai/unplugin vue-i18n ``` ```sh [yarn] yarn add @intl-ai/unplugin vue-i18n ``` ```sh [bun] bun add @intl-ai/unplugin vue-i18n ``` ::: You only need `@intl-ai/unplugin`. The translation engine lives in `@intl-ai/api` and is bundled automatically. ## Configuration Create an `intl-ai.config.ts` (or `.json`) at your project root. See [Configuration](/guide/configuration) for the full schema. For a custom AIProvider instance: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", baseURL: "https://api.openai.com/v1", defaultLocale: "en", locales: ["en", "es", "fr"], localeDir: "./locales", processor: "icu", }; ``` ## Vue App Setup ### Locale File Structure intl-ai generates JSON files like: ```json // locales/en.json { "greeting": "Hello, {name}!", "items": { "one": "You have {count} item", "other": "You have {count} items" } } ``` ### Create i18n Instance ```typescript import { createI18n } from "vue-i18n"; import en from "./locales/en.json"; import es from "./locales/es.json"; const i18n = createI18n({ locale: "en", fallbackLocale: "en", messages: { en, es }, }); app.use(i18n); ``` ### Using Translations In Vue templates: ```vue ``` In Composition API: ```vue ``` ## Processor Note vue-i18n supports ICU MessageFormat. Set `processor: "icu"` in your config so AI-generated translations preserve ICU placeholders correctly. --- --- url: /guide/i18n-libraries/i18next.md description: >- AI-translate i18next locale files at build time. Works with any AI model, zero runtime cost. --- # i18next This guide covers i18next consumption. For bundler setup, see [Build systems](/guide/build-systems/). ## Overview intl-ai generates translation JSON files at build time. i18next consumes these files at runtime. This guide shows how to integrate both tools. ## Installation ::: code-group ```sh [npm] npm install @intl-ai/unplugin i18next react-i18next ``` ```sh [pnpm] pnpm add @intl-ai/unplugin i18next react-i18next ``` ```sh [yarn] yarn add @intl-ai/unplugin i18next react-i18next ``` ```sh [bun] bun add @intl-ai/unplugin i18next react-i18next ``` ::: You only need `@intl-ai/unplugin`. The translation engine lives in `@intl-ai/api` and is bundled automatically. ## i18next Syntax Note i18next uses `{{variable}}` for interpolation (not ICU `{variable}`). For example: ```json { "greeting": "Hello, {{name}}!", "items": "You have {{count}} items" } ``` ## Configuration Create an `intl-ai.config.ts` (or `.json`) at your project root. See [Configuration](/guide/configuration) for the full schema. For a custom AIProvider instance: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider("openai"), model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", baseURL: "https://api.openai.com/v1", defaultLocale: "en", locales: ["en", "es", "de"], localeDir: "./public/locales", }; ``` ## React App Usage ```typescript import i18next from "i18next"; import { initReactI18next } from "react-i18next"; import en from "./locales/en.json"; import es from "./locales/es.json"; i18next.use(initReactI18next).init({ lng: "en", fallbackLng: "en", resources: { en: { translation: en }, es: { translation: es }, }, }); ``` ### Using Translations ```tsx import { useTranslation } from "react-i18next"; function App() { const { t } = useTranslation(); return (

{t("greeting", { name: "World" })}

{t("items", { count: 5 })}

); } ``` --- --- url: /guide/mobile/expo.md description: >- Build-time AI translation for Expo i18n. Translations happen during prebuild, zero runtime. --- # Expo The Expo integration is provided as a self-contained config plugin in [`examples/expo/plugin`](https://github.com/sigilco/intl-ai/tree/main/examples/expo/plugin). It runs `intl-ai fill` during `expo prebuild` and has zero runtime overhead because all translations are written to disk before Metro bundles your app. ## Copy the plugin Copy `examples/expo/plugin/` into your Expo project (for example, to `./plugins/intl-ai/`). The plugin is not published as an npm package, so you own and can customize the code. ## Configure `app.json` Reference the local plugin in your `app.json`: ```json { "expo": { "plugins": [["./plugins/intl-ai", { "configPath": "intl-ai.config.json" }]] } } ``` ## Create `intl-ai.config.json` ```json { "$schema": "https://www.schemastore.org/intl-ai.json", "defaultLocale": "en", "locales": ["en", "es"], "localeDir": "locales", "model": "your-provider/your-model", "apiKey": "${OPENAI_API_KEY}", "baseURL": "https://api.openai.com/v1", "maxRetries": 3 } ``` ## Run prebuild ```bash expo prebuild ``` The plugin invokes: ```bash npx intl-ai fill --config intl-ai.config.json ``` ## Plugin options | Option | Type | Default | Description | | ----------------- | --------- | --------------------- | ------------------------------------------------------- | | `configPath` | `string` | `intl-ai.config.json` | Path to your JSON config, relative to the project root. | | `verbose` | `boolean` | `false` | Forward CLI output to the parent process. | | `continueOnError` | `boolean` | `false` | Allow prebuild to continue if translation fails. | ## Runtime usage Load the generated JSON files directly with your preferred i18n library (`i18next`, `react-intl`, etc.). The plugin only writes translations; it does not impose a runtime API. ## Example See [`examples/expo`](https://github.com/sigilco/intl-ai/tree/main/examples/expo) for a complete working app. --- --- url: /guide/mobile/flutter.md description: >- Build-time AI translation for Flutter i18n. Translations happen during build, zero runtime. --- # Flutter The Flutter integration is provided as a self-contained [`build_runner`](https://pub.dev/packages/build_runner) builder in [`examples/flutter/plugin`](https://github.com/sigilco/intl-ai/tree/main/examples/flutter/plugin). It runs `intl-ai fill` during `flutter pub run build_runner build` and has zero runtime overhead because all translations are written to disk before the Flutter app is bundled. ## Copy the builder Copy `examples/flutter/plugin/` into your Flutter project (for example, to `./tools/intl_ai_builder/`). The builder is not published as a package, so you own and can customize the code. ## Configure `build.yaml` Add the builder to your app's `build.yaml`: ```yaml targets: $default: builders: intl_ai_flutter|intl_ai: enabled: true options: configPath: "intl-ai.config.json" executable: "intl-ai" verbose: false ``` Update the `import` path in `build.yaml` to match where you copied the builder, for example: ```yaml builders: intl_ai: import: "package:my_app/tools/intl_ai_builder/lib/builder.dart" ``` ## Create `intl-ai.config.json` ```json { "$schema": "https://www.schemastore.org/intl-ai.json", "defaultLocale": "en", "locales": ["en", "es"], "localeDir": "assets/locales", "model": "your-provider/your-model", "apiKey": "${OPENAI_API_KEY}", "baseURL": "https://api.openai.com/v1", "maxRetries": 3 } ``` ## Run build\_runner ```bash flutter pub get flutter pub run build_runner build --delete-conflicting-outputs ``` The builder invokes: ```bash intl-ai fill --config intl-ai.config.json ``` ## Builder options | Option | Type | Default | Description | | ------------ | -------- | --------------------- | -------------------------------------------------------------------------------------------------------- | | `configPath` | `String` | `intl-ai.config.json` | Path to your JSON config, relative to the package root. | | `executable` | `String` | `intl-ai` | Command used to invoke the CLI. Use `npx intl-ai` or `bunx intl-ai` if you do not want a global install. | | `verbose` | `bool` | `false` | Forward CLI output to the build runner log. | ## How it works The builder is registered on the `$package$` asset and writes an `intl-ai.done` marker file. When `build_runner` runs, it invokes the `intl-ai` CLI once per package. Add `intl-ai.done` to your `.gitignore`; it is only used to track build completion. ## Runtime usage Load the generated JSON files with your preferred i18n library (`easy_localization`, `i18n_extension`, `flutter_gen`, etc.). The builder only writes translations; it does not impose a runtime API. ## Requirements * Dart SDK 3.0+ * `intl-ai` CLI on `PATH` * An `intl-ai.config.json` file and locale directory ## Example See [`examples/flutter`](https://github.com/sigilco/intl-ai/tree/main/examples/flutter) for a complete working app. --- --- url: /guide/mobile/swiftui.md description: >- Build-time AI translation for SwiftUI i18n via Xcode build phases. Zero runtime overhead. --- # SwiftUI You can integrate `intl-ai` into a SwiftUI project by running the CLI as an Xcode build script phase. All translations happen at build time, so there is zero runtime overhead. ## Project layout ``` MyApp/ ├── intl-ai.config.json ├── locales/ │ ├── en.json │ └── es.json ├── MyApp/ │ ├── MyApp.swift │ └── Resources/ │ └── locales/ │ ├── en.json │ └── es.json └── MyApp.xcodeproj/ ``` Store source locale files in a project directory, then copy the generated translations into your app bundle as a build step. ## Add a build script phase 1. Select your app target in Xcode. 2. Open **Build Phases** and add a new **Run Script** phase named **"Translate Locales"**. 3. Paste the following script: ```bash set -e # Run intl-ai fill to generate missing translations. if command -v intl-ai &> /dev/null; then intl-ai fill --config "$SRCROOT/intl-ai.config.json" else echo "warning: intl-ai not found in PATH. Skipping translation." fi # Copy generated locale files into the app bundle. LOCALES_DIR="$SRCROOT/locales" DEST_DIR="$BUNDLE_RESOURCE_PATH/locales" if [ -d "$LOCALES_DIR" ]; then mkdir -p "$DEST_DIR" cp -R "$LOCALES_DIR"/*.json "$DEST_DIR/" fi ``` 4. Drag the **Translate Locales** phase before **Copy Bundle Resources**. ## Load translations at runtime Use `Bundle.main.url(forResource:withExtension:)` or `Bundle.main.decode(_:)` helpers to load JSON files from the bundle: ```swift import Foundation extension Bundle { func decode(_ file: String, as type: T.Type = T.self) -> T { guard let url = self.url(forResource: file, withExtension: nil) else { fatalError("Failed to locate \(file) in bundle.") } guard let data = try? Data(contentsOf: url) else { fatalError("Failed to load \(file) from bundle.") } let decoder = JSONDecoder() guard let loaded = try? decoder.decode(T.self, from: data) else { fatalError("Failed to decode \(file) from bundle.") } return loaded } } struct Localizations: Decodable { let hello: String let goodbye: String } let en = Bundle.main.decode("en.json", as: Localizations.self) ``` ## Requirements * `intl-ai` installed on your `PATH` (see [Installation](/guide/getting-started#installation)). * `intl-ai.config.json` at project root. ## Example See the project layout above and adapt it to your own SwiftUI app. `intl-ai` only writes translations; it does not impose a runtime API. --- --- url: /guide/mobile/jetpack.md --- # Android Jetpack You can integrate `intl-ai` into an Android Jetpack project by adding a Gradle task that runs the CLI before the build. All translations happen at build time, so there is zero runtime overhead. ## Project layout ``` app/ ├── build.gradle.kts ├── intl-ai.config.json └── src/main/assets/locales/ ├── en.json └── es.json ``` Store source locale files in `src/main/assets/locales/`, run `intl-ai fill` as a Gradle task, and load them from assets at runtime. ## Add a Gradle task In your `app/build.gradle.kts`, register a task that invokes the CLI before resources are merged: ```kotlin import java.io.ByteArrayOutputStream plugins { alias(libs.plugins.android.application) } android { /* ... */ } tasks.register("intlAiFill") { group = "intl-ai" description = "Translate missing locale keys with intl-ai" commandLine("intl-ai", "fill", "--config", "${projectDir}/intl-ai.config.json") // Only run when source locale files change. inputs.dir("${projectDir}/src/main/assets/locales") outputs.dir("${projectDir}/src/main/assets/locales") doFirst { if (org.gradle.internal.os.OperatingSystem.current().isWindows) { commandLine("cmd", "/c", "intl-ai", "fill", "--config", "${projectDir}/intl-ai.config.json") } } } tasks.named("preBuild").configure { dependsOn("intlAiFill") } ``` For Windows, the task falls back to `cmd /c` so the binary can be found on `PATH`. ## Load translations at runtime Read JSON files from assets and parse them with your JSON library of choice: ```kotlin import android.content.Context import kotlinx.serialization.json.Json import kotlinx.serialization.Serializable @Serializable data class LocaleMessages( val hello: String, val goodbye: String ) fun loadLocale(context: Context, locale: String): LocaleMessages { val json = context.assets.open("locales/$locale.json").bufferedReader().use { it.readText() } return Json.decodeFromString(LocaleMessages.serializer(), json) } ``` ## Requirements * `intl-ai` installed on your `PATH` (see [Installation](/guide/getting-started#installation)). * `intl-ai.config.json` in `app/`. Adjust `localeDir` to point to `src/main/assets/locales`. ## Example Use the project layout above and adapt it to your own Jetpack Compose or View-based app. `intl-ai` only writes translations; it does not impose a runtime API. --- --- url: /guide/desktop/dotnet.md description: >- Build-time AI translation for .NET i18n via MSBuild. Translations at build time, zero runtime. --- # .NET / C\# You can integrate `intl-ai` into a .NET project by adding an MSBuild target that runs the CLI before the build. All translations happen at build time, so there is zero runtime overhead. ## Project layout ``` MyApp/ ├── MyApp.csproj ├── intl-ai.config.json └── Resources/ ├── en.json └── es.json ``` Store source locale files in `Resources/`, run `intl-ai fill` as an MSBuild target, and embed or copy them into the output directory. ## Add an MSBuild target Add the following target to your `.csproj` file: ```xml Exe net8.0 PreserveNewest ``` The `IntlAiFill` target runs before every build, invoking the CLI to fill missing translations. Because the `Resources\*.json` items use `CopyToOutputDirectory=PreserveNewest`, the generated files are copied to the output folder automatically. ## Load translations at runtime Use `System.Text.Json` to load locale files from the output directory: ```csharp using System.Text.Json; public record LocaleMessages(string Hello, string Goodbye); public static class Translations { public static LocaleMessages Load(string locale) { var path = Path.Combine(AppContext.BaseDirectory, "Resources", $"{locale}.json"); var json = File.ReadAllText(path); return JsonSerializer.Deserialize(json)!; } } ``` ## Requirements * `intl-ai` installed on your `PATH` (see [Installation](/guide/getting-started#installation)). * `intl-ai.config.json` next to your `.csproj` file. Adjust `localeDir` to point to `Resources`. ## Example Use the project layout above and adapt it to your own WPF, WinUI, ASP.NET, or console app. `intl-ai` only writes translations; it does not impose a runtime API. --- --- url: /guide/api.md description: >- @intl-ai/api reference. Run fill and check operations directly in any JavaScript or TypeScript project. --- # API reference `@intl-ai/api` is the runtime-agnostic core of intl-ai. It exposes one function and one config type. ## `runFill(config, options?)` ```typescript import { runFill } from "@intl-ai/api"; import type { IntlAiConfig, RunFillOptions, RunFillResult } from "@intl-ai/api"; const result: RunFillResult = await runFill(config, { locale: "es", force: false, dryRun: false, }); ``` The function walks your locale files, translates missing keys, updates the lockfile, and writes the new translations to disk. ### Options | Option | Type | Description | | -------- | --------- | -------------------------------------- | | `locale` | `string` | Translate only this locale. | | `force` | `boolean` | Re-translate human-edited entries. | | `dryRun` | `boolean` | Preview changes without writing files. | ### Result | Field | Type | Description | | ------------ | ---------- | ---------------------------------- | | `translated` | `number` | Number of keys translated. | | `skipped` | `number` | Number of keys already up to date. | | `errors` | `number` | Number of translation errors. | | `locales` | `string[]` | Locales that were processed. | ## `runCheck(config, options?)` ```typescript import { runCheck } from "@intl-ai/api"; import type { IntlAiConfig, RunCheckOptions, RunCheckResult } from "@intl-ai/api"; const result: RunCheckResult = await runCheck(config, { locale: "es", }); ``` Reads locale files and the lockfile, then reports missing keys, stale translations, and extra keys. Read-only: writes nothing to disk. ### Options | Option | Type | Description | | -------- | -------- | ----------------------- | | `locale` | `string` | Check only this locale. | ### Result | Field | Type | Description | | ----------- | ------------------- | -------------------------------------------------- | | `hasIssues` | `boolean` | True if any locale has missing or stale entries. | | `results` | `CheckLocaleResult` | Per-locale breakdown of missing, stale, and extra. | Each `CheckLocaleResult`: | Field | Type | Description | | --------- | --------------------------- | ------------------------------------------------------- | | `locale` | `string` | Locale code. | | `missing` | `MissingTranslationEntry[]` | Keys in source but absent or empty in target. | | `stale` | `StaleEntry[]` | Keys whose source content changed since last translate. | | `extra` | `string[]` | Keys in target with no corresponding source entry. | ### CLI exit codes When called via the CLI (`intl-ai check`): * `0` — all translations are complete and up to date * `10` — one or more locales have missing or stale entries ## `IntlAiConfig` ```typescript interface IntlAiConfig { defaultLocale: string; locales: string[]; localeDir: string; model: AIProvider | string; // provider ID string or AIProvider instance apiKey: ApiKeyValue; // supports $VAR and ${VAR} env interpolation baseURL: string; modelParams?: Record; // passthrough to provider hook?: TranslationHook; processor?: IntlAiProcessor; glossary?: Record; maxRetries?: number; } ``` For JSON config files, use `IntlAiJsonConfigSchema` and `jsonConfigToIntlAiConfig` from `@intl-ai/api/internal`. ## Advanced: batched fill (internal) `batchedFill` is available via `@intl-ai/api/internal` for consumers who need parallel locale processing and the failure report artifact. ```typescript import { batchedFill } from "@intl-ai/api/internal"; import type { BatchedFillOptions, BatchedFillResult } from "@intl-ai/api/internal"; const result = await batchedFill(config, { concurrency: 4, // max parallel locales, default 4 // ...runFill options (locale, force, dryRun, onProgress, hook) }); ``` This is the API used by the CLI. The failure report is written to `${localeDir}/.intl-ai/report-.json` when any translation fails and `dryRun` is false. ## JSON Schema A JSON Schema for `intl-ai.config.json` is published at: ```text https://www.schemastore.org/intl-ai.json ``` Add it to your config for editor intellisense and CI validation: ```json { "$schema": "https://www.schemastore.org/intl-ai.json", "defaultLocale": "en", "locales": ["en", "es"], "localeDir": "./locales", "provider": "openai", "model": "gpt-4o-mini", "apiKey": "${OPENAI_API_KEY}", "baseURL": "https://api.openai.com/v1" } ``` --- --- url: /guide/contributing.md description: >- Contribute to intl-ai documentation. Edit any doc page on GitHub and open a pull request. --- # Contributing to the documentation Thank you for your interest in improving the intl-ai documentation. This guide covers everything you need to know about contributing to the docs. For general contribution guidelines, branch workflows, commit conventions, and the full development process, see our [main Contributing Guide](https://github.com/sigilco/intl-ai/blob/main/CONTRIBUTING.md). ## Documentation setup The docs are built with [VitePress](https://vitepress.dev/), a static site generator optimized for technical documentation. ### Prerequisites * Node.js 22+ and pnpm 11+ * Basic familiarity with Markdown * A text editor (VS Code recommended) ### Installation ```bash pnpm install ``` ## Running docs locally Start the development server to preview your changes in real-time: ```bash pnpm docs:dev ``` This starts a local dev server (typically at `http://localhost:5173`) with hot module replacement for instant preview updates. ## Building for production ```bash pnpm docs:build ``` This generates optimized static files in the `docs/.vitepress/dist/` directory. ## Previewing the production build After building, preview the production version locally: ```bash pnpm docs:preview ``` ## Documentation writing guidelines ### File structure ``` docs/ .vitepress/ config.ts guide/ getting-started.md ai-model.md configuration.md api.md migration.md contributing.md next-js.md vue-i18n.md i18next.md mobile/ expo.md flutter.md swiftui.md jetpack.md desktop/ dotnet.md public/ logo.svg index.md ``` ### Markdown conventions * **Headings:** Use `#` for page title, `##` for sections, `###` for subsections * **Code blocks:** Specify language for syntax highlighting (` ```bash `, ` ```typescript `, etc.) * **Links:** Use relative paths for internal links: `[link text](/guide/page-name)` * **Line length:** Keep lines under 100 characters ### Frontmatter Every documentation page must include YAML frontmatter at the top: ```yaml --- title: Page title --- ``` ### Internal links ```markdown [Getting started guide](/guide/getting-started) ``` ### External links External links open in a new tab automatically. ## Adding new guide pages 1. Create a new `.md` file in `docs/guide/`. 2. Add the required YAML frontmatter with a `title` field. 3. Add the page to the sidebar in `docs/.vitepress/config.ts`. 4. Run `pnpm docs:dev` and verify your page appears and renders correctly. 5. Submit a pull request following the [main Contributing Guide](https://github.com/sigilco/intl-ai/blob/main/CONTRIBUTING.md). ## Documentation commands reference | Command | Purpose | | ------------------- | ---------------------------------------------- | | `pnpm docs:dev` | Start local development server with hot reload | | `pnpm docs:build` | Build production-ready static files | | `pnpm docs:preview` | Preview the production build locally | ## Deployment Documentation is deployed to Cloudflare Pages at `https://intl-ai.pages.dev/` when changes are merged to the `main` branch. The deployment process: 1. Detects changes to the `docs/` directory 2. Runs `pnpm docs:build` to generate static files 3. Deploys the output to Cloudflare Pages No manual deployment steps are required. ## Before submitting documentation changes * Run `pnpm docs:dev` and verify all pages render correctly * Check that internal links work (no 404s) * Verify code examples are accurate and runnable * Ensure frontmatter is present on all new pages * Update the sidebar navigation if adding new pages * Follow Markdown conventions and style guidelines * Proofread for spelling and grammar ## Code of conduct All contributors must adhere to our [Code of Conduct](https://github.com/sigilco/intl-ai/blob/main/CODE_OF_CONDUCT.md). We are committed to providing a welcoming and inclusive environment for all contributors. ## Questions or issues? * Documentation questions: Open a GitHub Discussion * Found a typo or error: Open an issue or submit a PR * Feature suggestions: Open an issue to discuss before implementing --- --- url: /guide/internals.md description: >- intl-ai internals for contributors. Hexagonal architecture, package layout, and design decisions. --- # Internals This page is for contributors who want to understand how intl-ai is built. User-facing documentation lives under [Guide](/guide/getting-started). ## Package layout | Package | Purpose | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `@intl-ai/api` | Runtime-agnostic translation core. Public surface: `runFill`, `IntlAiConfig`, `RunFillOptions`, `RunFillResult`, `IntlAiConfigSchema`. | | `@intl-ai/cli` | `intl-ai fill` and `intl-ai check` commands. Loads `intl-ai.config.ts` or `intl-ai.config.json`. | | `@intl-ai/unplugin` | Universal bundler plugin adapters. Loads config and calls `runFill` at `buildStart`. | | `@intl-ai/next` | Next.js wrapper around the webpack plugin and Turbopack loader. Loads config at startup and on the webpack `emit` hook. | ## Internal subpath Modules that SDK consumers do not need are exposed through `@intl-ai/api/internal`. Sibling packages in this monorepo may import them, but external SDKs and plugins should stay on the public surface. The internal barrel includes engine, lockfile, processor, formats, utilities, and JSON config schemas. ## JSON Schema generation `packages/api/src/schema/intl-ai.schema.json` is generated from `IntlAiJsonConfigSchema` in `packages/api/src/schema/json-config.ts`. Run: ```bash pnpm --filter @intl-ai/api schema:build ``` This writes both the package-local schema and `docs/public/schema/v1.json`, which GitHub Pages serves at `/intl-ai/schema/v1.json`. ## SchemaStore The schema is submitted to SchemaStore so editors discover it automatically for files matching `intl-ai.config.ts` and `intl-ai.config.json`. See the plan at `.agents/plans/2026-06-22-runtime-agnostic-rethink.md` for the catalog entry format. ## Release pipeline * Binaries are built with `bun build --compile` for `bun-darwin-arm64`, `bun-linux-x64`, and `bun-linux-arm64`. * npm packages are published with changesets. * Docs are built with VitePress and deployed to GitHub Pages. See `.github/workflows/release.yml` for details. --- --- url: /guide/observability.md description: >- Monitor the intl-ai translation pipeline with hooks. Track batch progress, retries, and failures. --- # Observability with hooks TranslationHook is an optional callback interface on the config object. It gives you visibility into every step of the AI translation pipeline: when a batch is sent, when it succeeds, and when it fails. Hooks are available only in TypeScript config files (`intl-ai.config.ts`). JSON config does not support function values. ## The three callbacks Each callback receives a single info object. All three are optional; implement only the ones you need. ### `onRequest` Fires before each batch is sent to the AI provider. ```typescript onRequest?: (info: { provider: string; // e.g. "openai", "anthropic" model: string; // e.g. "gpt-4o-mini" locale: string; // target locale code entryCount: number; // number of keys in this batch }) => void; ``` Use this to log which locales and batch sizes are being processed. ### `onSuccess` Fires after a batch completes successfully. ```typescript onSuccess?: (info: { provider: string; model: string; locale: string; results: TranslationResult[]; // per-key results durationMs: number; // wall-clock time for this request }) => void; ``` Each `TranslationResult` has: | Field | Type | Description | | ------------ | --------- | ---------------------------------------------- | | `key` | `string` | The locale key being translated. | | `translated` | `string?` | The translated text, if translation succeeded. | | `success` | `boolean` | Whether this individual key was translated. | | `error` | `string?` | Error message if this key failed validation. | ### `onError` Fires when a batch exhausts all retry attempts without a successful response. ```typescript onError?: (info: { provider: string; model: string; locale: string; error: string; // human-readable error from the last attempt attempt: number; // the retry attempt that failed (equals maxRetries) }) => void; ``` Note that `onError` fires only after all retries are exhausted. Individual retry attempts within a batch are handled internally and do not trigger `onError`. ## Setting up a hook Pass the `hook` property on your config object: ```typescript import type { IntlAiConfig } from "@intl-ai/api"; import type { TranslationResult } from "@intl-ai/api/internal"; const hook = { onRequest(info) { console.log( `[${info.locale}] sending ${info.entryCount} keys to ${info.provider}/${info.model}`, ); }, onSuccess(info) { const succeeded = info.results.filter((r) => r.success).length; console.log( `[${info.locale}] translated ${succeeded}/${info.results.length} keys in ${info.durationMs.toFixed(0)}ms`, ); }, onError(info) { console.error(`[${info.locale}] failed after ${info.attempt} attempts: ${info.error}`); }, }; const config: IntlAiConfig = { provider: "openai", model: "gpt-4o-mini", apiKey: "${OPENAI_API_KEY}", defaultLocale: "en", locales: ["en", "es", "fr"], localeDir: "./locales", hook, }; export default config; ``` This produces output like: ``` [es] sending 24 keys to openai/gpt-4o-mini [es] translated 24/24 keys in 1823ms [fr] sending 24 keys to openai/gpt-4o-mini [fr] translated 23/24 keys in 2104ms ``` ## Use cases ### Progress logging The simplest use case is logging translation progress to stdout during builds. This helps you confirm which locales are being processed and how long each takes: ```typescript onRequest(info) { console.log(`Translating ${info.locale} (${info.entryCount} keys)...`); }, onSuccess(info) { console.log(`Done: ${info.locale} (${info.durationMs.toFixed(0)}ms)`); }, ``` ### Error tracking Route translation failures to an error monitoring service. Because `onError` receives the locale and error message, you can tag and group issues: ```typescript import * as Sentry from "@sentry/node"; onError(info) { Sentry.captureMessage("Translation batch failed", { level: "warning", tags: { locale: info.locale, provider: info.provider, model: info.model }, extra: { error: info.error, attempt: info.attempt }, }); }, ``` ### Duration tracking Use `onSuccess` to track how long each locale takes. This helps you identify slow locales or provider issues: ```typescript const timings: Record = {}; onSuccess(info) { timings[info.locale] = (timings[info.locale] ?? 0) + info.durationMs; }, ``` ### Per-key validation reporting The `results` array in `onSuccess` includes per-key status. Use this to surface validation failures that do not bubble up to `onError`: ```typescript onSuccess(info) { const failed = info.results.filter((r) => !r.success); for (const r of failed) { console.warn(`[${info.locale}] key "${r.key}": ${r.error}`); } }, ``` ## Sync-only callbacks All three callbacks are synchronous. They must return `void` and cannot be async. The translation engine calls them inline during the batch loop, so blocking or async work would stall the pipeline. If you need to perform async work like sending telemetry or writing to a remote service, queue the data in the callback and process it outside the translation pipeline: ```typescript const telemetryQueue: Array> = []; const hook = { onSuccess(info) { telemetryQueue.push({ locale: info.locale, provider: info.provider, durationMs: info.durationMs, keyCount: info.results.length, }); }, }; // After translation completes await runFill(config); await flushTelemetry(telemetryQueue); ``` --- --- url: /guide/providers.md description: >- AI provider reference for intl-ai. Use OpenAI, Anthropic, Ollama, or implement your own provider. --- # Providers intl-ai translates locale keys by calling an AI model over HTTP. Every provider encapsulates how requests are shaped for a specific API and how responses are parsed back into translations. You can use a built-in provider, or write your own for any compatible API. ## How it works Each provider implements the `AIProvider` interface from `@intl-ai/api`: ```typescript interface AIProvider { readonly id: string; buildRequest(opts: { model: string; systemPrompt: string; userPrompt: string; temperature: number; modelParams?: Record; }): { url: string; headers: Record; body: Record; }; parseResponse(data: unknown): { content: string }; } ``` At build time, intl-ai: 1. Passes the system prompt (translation instructions) and user prompt (keys + source text) into `buildRequest`. 2. Sends the resulting HTTP request to `baseURL + url`. 3. Feeds the parsed JSON response through `parseResponse` to extract the translated string. This means the translation logic itself is decoupled from any specific vendor. Swap providers without changing your locale files or config structure. ## Built-in providers intl-ai ships with two providers: `openai` and `anthropic`. ### OpenAI Provider ID: `openai` * Uses the `/chat/completions` endpoint. * Passes `Authorization: Bearer ${apiKey}` in the request header. * Sends the system prompt and user prompt as separate message roles. * Enables JSON Schema structured output to guarantee the response shape. * The `apiKey` placeholder in the header is resolved from environment variables at runtime via `resolveApiKey`. ### Anthropic Provider ID: `anthropic` * Uses the `/messages` endpoint. * Passes `X-Api-Key: ${apiKey}` and `anthropic-version: 2023-06-01` in the request headers. * Sends the system prompt as a `system` array (Anthropic's preferred format) and the user prompt as a `user` message. * Defaults `max_tokens` to `1024` when no `modelParams.max_tokens` is provided. ## Registry resolution Use `resolveProvider()` from `@intl-ai/api/internal` to turn a config value into a concrete provider instance: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; // By ID string — returns the matching built-in provider const openai = resolveProvider("openai"); const anthropic = resolveProvider("anthropic"); // By object — passes through directly (custom provider) const custom = resolveProvider(myProvider); ``` When you pass a string, `resolveProvider` looks it up in the built-in registry. If the ID is not recognized, it throws an error listing the available providers. When you pass an `AIProvider` object, it returns it unchanged. In your config, the `provider` field accepts either form: ```typescript // intl-ai.config.ts — string form export default { provider: "anthropic", model: "claude-sonnet-4-20250514", apiKey: "${ANTHROPIC_API_KEY}", baseURL: "https://api.anthropic.com/v1", defaultLocale: "en", locales: ["en", "es"], localeDir: "./locales", }; ``` ```typescript // intl-ai.config.ts — custom provider form import { resolveProvider } from "@intl-ai/api/internal"; export default { provider: resolveProvider(myProvider), model: "my-model", apiKey: "${MY_API_KEY}", baseURL: "https://my-api.example.com/v1", defaultLocale: "en", locales: ["en", "es"], localeDir: "./locales", }; ``` ## Writing a custom provider You only need to implement `id`, `buildRequest`, and `parseResponse`. Here is a minimal provider for an OpenAI-compatible API (for example, a self-hosted vLLM or LiteLLM instance): ```typescript import type { AIProvider } from "@intl-ai/api"; const myProvider: AIProvider = { id: "my-provider", buildRequest({ model, systemPrompt, userPrompt, temperature, modelParams }) { return { url: "/chat/completions", headers: { "Content-Type": "application/json", Authorization: "Bearer ${apiKey}", }, body: { model, messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt }, ], temperature, ...modelParams, }, }; }, parseResponse(data: unknown) { // Adapt this to your API's response shape const body = data as { choices?: Array<{ message?: { content?: string } }> }; return { content: body.choices?.[0]?.message?.content ?? "", }; }, }; ``` Then pass it to your config: ```typescript import { resolveProvider } from "@intl-ai/api/internal"; import { myProvider } from "./my-provider"; export default { provider: resolveProvider(myProvider), model: "my-model-name", apiKey: "${MY_PROVIDER_API_KEY}", baseURL: "https://my-provider.example.com/v1", defaultLocale: "en", locales: ["en", "es"], localeDir: "./locales", }; ``` ### The buildRequest contract `buildRequest` receives an object with: * `model` — the model name from config. * `systemPrompt` — translation instructions including glossary and locale context. * `userPrompt` — the actual keys and source strings to translate. * `temperature` — sampling temperature from config. * `modelParams` — optional extra parameters forwarded from config. It must return: * `url` — the path appended to `baseURL`. * `headers` — HTTP headers. Use `"${apiKey}"` as a placeholder and intl-ai resolves it from environment variables. * `body` — the JSON request payload. ### The parseResponse contract `parseResponse` receives the parsed JSON body of the API response. It must return `{ content: string }` where `content` is the raw translation text (typically a JSON string that intl-ai parses further). ## Provider quirks A few things to keep in mind when configuring providers: **Anthropic requires `max_tokens`.** The Anthropic API rejects requests without a `max_tokens` field. The built-in provider defaults to `1024`, but you can override it with `modelParams`: ```json { "provider": "anthropic", "modelParams": { "max_tokens": 4096 } } ``` **Base URLs differ per provider.** OpenAI uses `https://api.openai.com/v1`. Anthropic uses `https://api.anthropic.com/v1`. If you are using a proxy or self-hosted endpoint, set `baseURL` in your config to match your endpoint. **API key header format.** OpenAI expects `Authorization: Bearer `. Anthropic expects `X-Api-Key: `. Custom providers should follow the format their API expects. The `${apiKey}` placeholder in the header value is automatically replaced by intl-ai at runtime. **Structured output.** The OpenAI provider enables JSON Schema structured output in `response_format`. This guarantees the model returns valid JSON matching the translation schema. If your custom provider's API does not support structured output, rely on prompt engineering instead, and ensure `parseResponse` handles the response shape accordingly. **modelParams are spread into the body.** Anything you put in `modelParams` is merged into the request body via the spread operator. This means provider-specific fields like `top_p`, `frequency_penalty`, or `max_tokens` go there. Be careful not to override fields the provider already sets (like `messages` or `model`).