{
  "markdown": "# i1n\n\n**Your app in every language. One command.**\n\n[![npm](https://img.shields.io/npm/v/i1n)](https://www.npmjs.com/package/i1n) [![license](https://img.shields.io/npm/l/i1n)](https://github.com/Pakvothe/i1n-cli/blob/main/LICENSE) [![MCP](https://img.shields.io/badge/MCP-9_tools-amber)](https://i1n.ai) [![Listed on MCP Servers](https://img.shields.io/badge/mcpservers.org-listed-blue)](https://mcpservers.org/servers/pakvothe/i1n-cli) [![Security: DeepSec](https://img.shields.io/badge/security-DeepSec_audited-success)](https://github.com/vercel-labs/deepsec) [![SaaSHub](https://img.shields.io/badge/SaaSHub-approved-blue)](https://www.saashub.com/i1n?utm_source=badge&utm_campaign=badge&utm_content=i1n)\n\n![demo](./demo.gif)\n\nLocalization as code. Push your translation keys, AI translates to 182 languages, pull type-safe TypeScript definitions. Built for developers, AI agents, and product teams.\n\n**Free forever** · No credit card · [i1n.ai](https://i1n.ai)\n\n---\n\n## Why i1n?\n\nTraditional i18n means dozens of JSON files, zero type safety, hours of copy-pasting, and deploys that break at 2 AM. Existing tools charge $144+/mo and require browser-based workflows.\n\ni1n is different:\n\n- **One command** — `i1n push --translate es,fr,ja` and you're done\n- **Type-safe** — auto-generated `i1n.d.ts` with full IDE autocomplete\n- **AI-native** — MCP server for Cursor, Claude Code, Windsurf. Your agent handles i18n for you\n- **Zero migration** — Bridge Mode wraps your existing i18next/next-intl/vue-i18n\n- **5x cheaper** — Free tier included. Pro at $29/mo vs Lokalise at $144/mo\n\n---\n\n## 📦 Install\n\n```bash\n# To use the CLI (global)\nnpm install -g i1n\n\n# To use the SDK + types (in your app)\nnpm install i1n\n\n# Local CLI usage (optional)\nnpm install -D i1n\n```\n\n_Supports `npm`, `pnpm`, `yarn`, and `bun`._\n\n---\n\n## 🏁 Quick Start\n\n```bash\n# 1. Initialize (auth + auto-detect setup)\ni1n init\n\n# 2. Push your translation keys\ni1n push\n\n# 3. Pull translations + auto-generated TypeScript types\ni1n pull\n```\n\n---\n\n## ✨ Key Features & Commands\n\n### 🛠️ `i1n init`\n\n**Interactive setup that prepares your workspace.**\n\n- Authenticates via API key.\n- **New?** If you don't have a key yet, the CLI provides clear guidance on how to get started.\n- Auto-detects frameworks (Next.js, Vite, Expo, Flutter, Rails, etc.).\n- Saves configuration to `i1n.config.json` (automatically ignored via `.gitignore`).\n- **AI Orchestration**: Optionally sets up rules for your AI coding tools.\n\n### ⬆️ `i1n push`\n\n**Syncs your local translations to i1n.**\n\n- Detects new keys and source changes.\n- **Smart Translate**: Offers to translate missing keys with a cost estimate before proceeding.\n- Efficient caching layer — repeated translations cost a fraction of fresh ones.\n- **Three-way diff** — push only sends the (key, lang) pairs you actually changed, never overwriting edits made via the dashboard or by other teammates. See [Team workflow](#-team-workflow) for the full conflict model.\n\n**Flags:**\n\n- `--translate [langs]` — trigger AI translation after push (e.g. `--translate es,fr,ja`)\n- `--strategy <mode>` — how to handle real conflicts: `interactive` (default in TTY), `ours`, `theirs`, `abort`\n- `--force` — shorthand for `--strategy ours` (overwrite the server with your local values; destructive)\n\n### ⬇️ `i1n pull`\n\n**Downloads translations and generates type-safe IDs.**\n\n- Updates local locale files in your configured format.\n- Generates `i1n.d.ts` for full IDE autocomplete.\n\n### 📊 `i1n limits`\n\n**Real-time usage tracking.**\n\n- View your current plan and credit usage.\n- Monitor active language slots and available capacity.\n\n### ✅ `i1n check`\n\n**Catch broken translations before they ship. Built for CI.**\n\n- Detects missing keys per language, **broken interpolation placeholders** (`{{count}}` lost in translation), empty values, and malformed files.\n- `--min-coverage 95` fails the build when translation coverage drops below your threshold.\n- `--json` for tooling. Exit codes: `0` clean, `1` errors found, `2` config problem.\n- 100% offline — no API calls, no secrets needed in CI.\n\n```yaml\n# .github/workflows/ci.yml\n- name: Validate translations\n  run: npx i1n check --min-coverage 95\n```\n\n### 🧠 `i1n setup-ai`\n\n**Turns your IDE into a localization expert.**\n\n- Generates project-specific rules for **Cursor (`.mdc`)**, **Claude Code (`CLAUDE.md`)**, **Windsurf**, and more.\n- Ensures AI agents follow your naming conventions, file structure, and brand voice.\n\n### 🔌 `i1n mcp`\n\n**MCP server for AI coding assistants.**\n\nStarts a [Model Context Protocol](https://modelcontextprotocol.io) server that lets Cursor, Claude Code, Windsurf, and other AI assistants execute i1n commands directly from your IDE.\n\n```bash\n# Add to Claude Code\nclaude mcp add i1n -- npx i1n mcp\n\n# Or add to .mcp.json / cursor config\n```\n\n```json\n{\n  \"mcpServers\": {\n    \"i1n\": {\n      \"command\": \"npx\",\n      \"args\": [\"i1n\", \"mcp\"]\n    }\n  }\n}\n```\n\n**9 tools available:**\n\n| Tool | Description |\n| ---- | ----------- |\n| `i1n_status` | Get project status, plan, limits, and active languages |\n| `i1n_check` | Validate locale files offline: missing keys, broken placeholders, coverage |\n| `i1n_push` | Push local translation files with three-way diff (preserves server-side edits, aborts on conflict so the agent can resolve) |\n| `i1n_pull` | Pull translations and generate type-safe TypeScript definitions |\n| `i1n_translate` | Translate keys to specified languages using AI |\n| `i1n_add_language` | Add new languages with optional auto-translation |\n| `i1n_extract_and_translate` | Extract strings from code, push as keys, translate to all languages |\n| `i1n_search` | Search existing translation keys by name or value |\n| `i1n_setup_bridge` | Detect your i18n library (i18next, vue-i18n, next-intl, etc.) and wire up i1n bridge mode end-to-end |\n\n**The killer workflow** — tell your AI agent \"internationalize this component\":\n\n1. The agent reads your file and identifies hardcoded strings\n2. It calls `i1n_extract_and_translate` with the extracted strings\n3. i1n pushes the keys, translates to all active languages, generates types\n4. The agent rewrites your component with `t('key')` calls\n\nA 60-minute task in 30 seconds.\n\n---\n\n## 👥 Team workflow\n\ni1n is designed for teams where multiple people edit translations in parallel — devs in different branches, copywriters in the dashboard, AI agents via MCP. `i1n push` is safe to run without worrying that your local working tree might pulverize someone else's edits.\n\n### How push decides what to send\n\nBefore every push, the CLI:\n\n1. Reads your local locale files (`L`).\n2. Asks the server which keys exist and when each was last modified (cheap metadata-only call, ~50× smaller than a full pull).\n3. If anything moved on the server since your last sync, fetches the full server state (`S`).\n4. Computes a three-way diff per `(namespace, key, lang)` against the last baseline you synced (`P`, stored in `locales/.i1n-push-state.json`).\n\nFor each `(key, lang)` the diff places it in one of these buckets:\n\n| Local | Server | Baseline | Action |\n| --- | --- | --- | --- |\n| `==` server | — | — | unchanged, skip |\n| `==` baseline | changed | — | **server-only** — auto-pull into your locale files |\n| changed | `==` baseline | — | **local edit** — push it |\n| changed | changed | both moved | **conflict** — resolve interactively |\n| missing | present | present in baseline | warn, don't propagate (no delete verb) |\n\nOnly the languages that genuinely changed locally are sent. Languages you didn't touch are not in the payload, so the server's per-language merge preserves them. No more \"my push silently overwrote yield_rate that I never even opened\".\n\n### When there's a real conflict\n\nA real conflict means **you and someone else both edited the same `(key, lang)` to different values** since the last sync. The CLI shows each one and asks you to pick:\n\n```\nConflict 1/3: common.greeting [en_us]\n\n  › Keep local: \"Hello there\"\n    Accept server: \"Hi\"\n    Abort push\n```\n\n- **Local** → push your value, overwrite the server.\n- **Server** → discard your local, auto-pull the server's value into your file.\n- **Abort** → exit; nothing is pushed.\n\nFor batch / CI / non-interactive environments, pass a strategy:\n\n```bash\ni1n push --strategy theirs   # accept all server values, push nothing for conflicts\ni1n push --strategy ours     # local wins (alias: --force)\ni1n push --strategy abort    # exit on any conflict\n```\n\nIn non-TTY contexts (e.g. CI without a strategy flag), push aborts with a diff of the conflicts so you can resolve in code.\n\n### Auto-pulling server-only changes\n\nIf a teammate or someone in the dashboard updated a key you never touched, the server's value is automatically written into your local file at push time and your `i1n.d.ts` is regenerated if needed. Your working tree ends up reflecting reality — your `git diff` will show the bring-in so you can commit it alongside your own changes.\n\n### MCP push (AI agents)\n\nThe MCP `i1n_push` tool runs the same diff but defaults to **abort on conflict** because an AI agent should not silently pick a winner. Conflicts are reported in the response so the agent can decide to pull, ask you, or resolve manually before retrying.\n\n### Fresh checkouts\n\n`locales/.i1n-push-state.json` is gitignored by design — it's working-tree state, like `.git/index`. On a fresh clone or new branch where the file doesn't exist, the baseline is synthesized from the server. Any local divergence from the server is then treated as a conflict (the CLI can't tell whether you edited locally or have stale data). Run `i1n pull` first if you just cloned and want to bring everything in cleanly.\n\n---\n\n## 📁 Supported Formats\n\n| Format            | Frameworks                   | File Sample           |\n| ----------------- | ---------------------------- | --------------------- |\n| **Nested JSON**   | i18next, next-intl, vue-i18n | `en/common.json`      |\n| **Flat JSON**     | React Native, Generic        | `locales/en.json`     |\n| **ARB**           | Flutter / Dart               | `app_en.arb`          |\n| **YAML**          | Ruby on Rails                | `en.yml`              |\n| **Android XML**   | Native Android               | `strings.xml`         |\n| **Apple Strings** | iOS / macOS                  | `Localizable.strings` |\n| **TypeScript**    | Type-safe JSON               | `locales/en.ts`       |\n\n---\n\n## 🧩 SDK Usage\n\nThe `i1n` package includes a runtime SDK for web and mobile JS/TS projects. You can use it in two ways:\n\n### Standalone Mode — Replace your i18n library\n\nUse the i1n native engine directly. No external dependencies needed.\n\n```typescript\nimport { init, t, setLocale } from \"i1n\";\n\n// Load your translation resources\ninit({\n  locale: \"en_us\",\n  resources: {\n    en_us: {\n      auth: { login: \"Login\", title: \"Welcome back, {user}\" },\n      items_one: \"One item\",\n      items_other: \"{count} items\",\n    },\n    es_es: {\n      auth: { login: \"Entrar\", title: \"Bienvenido de nuevo, {user}\" },\n      items_one: \"Un elemento\",\n      items_other: \"{count} elementos\",\n    },\n  },\n});\n\n// Autocomplete and type-safety work out of the box after 'i1n pull'\nt(\"auth.login\"); // \"Login\"\n\n// Support for default values (useful during development)\nt(\"new.key\", { defaultValue: \"Coming soon...\" }); // \"Coming soon...\"\n\n// Variables & Plurals\nt(\"auth.title\", { user: \"Fran\" }); // \"Welcome back, Fran\"\nt(\"items\", { count: 5 }); // \"5 items\"\n\n// Switch language at runtime\nsetLocale(\"es_es\");\nt(\"auth.login\"); // \"Entrar\"\n```\n\n**Key resolution** works with both nested and flat structures automatically — use whatever format your project prefers.\n\n### Bridge Mode — Keep your library, add type safety\n\nAlready using i18next, vue-i18n, or react-intl? Connect it to i1n with one line and get full autocompletion.\n\n```typescript\nimport i18next from \"i18next\";\nimport { registerI1n, t } from \"i1n\";\n\n// Set up i18next as usual\nawait i18next.init({\n  lng: \"en\",\n  resources: {\n    /* ... */\n  },\n});\n\n// Connect to i1n — one line\nregisterI1n((key, params) => i18next.t(key, params));\n\n// Now t() uses i18next under the hood, but with strict type checking\nt(\"common.greeting\", { name: \"World\" }); // Powered by i18next, typed by i1n\n```\n\nWorks with any library:\n\n- **vue-i18n**: `registerI1n((key, params) => i18n.global.t(key, params))`\n- **react-intl**: `registerI1n((key, params) => intl.formatMessage({ id: key }, params))`\n- **Custom**: `registerI1n((key) => myLookup(key))`\n\n### Pluralization\n\nDefine plural variants with `_zero`, `_one`, `_other` suffixes:\n\n```typescript\n// In your translation files:\n// \"items_zero\": \"No items\"\n// \"items_one\": \"One item\"\n// \"items_other\": \"{count} items\"\n\nt(\"items\", { count: 0 }); // \"No items\"\nt(\"items\", { count: 1 }); // \"One item\"\nt(\"items\", { count: 5 }); // \"5 items\"\n```\n\n### Interpolation\n\nThree syntaxes supported universally: `{var}`, `{{var}}`, `%{var}`\n\n### JavaScript (without TypeScript)\n\nThe SDK works in plain JS — you just don't get autocompletion:\n\n```javascript\nimport { init, t } from \"i1n\";\ninit({ locale: \"en_us\", resources: { en_us: { greeting: \"Hello {name}\" } } });\nt(\"greeting\", { name: \"World\" }); // \"Hello World\"\n```\n\n### ⚛️ React / Preact Integration\n\nFor a \"plug and play\" experience, use this minimalist provider pattern.\n\n```tsx\nimport { createContext, useContext, useState, useEffect } from \"react\";\nimport { init, t, getLocale, setLocale as sdkSetLocale } from \"i1n\";\n\n// 1. Initialize with wordings\n// (In a real app, you'd probably import these from your locales folder)\ninit({\n  locale: \"en_us\",\n  resources: {\n    /* ... */\n  },\n});\n\nconst STORAGE_KEY = \"i1n-locale\";\nconst I1nContext = createContext({\n  locale: \"en_us\",\n  setLocale: (l: string) => {},\n});\n\n// 2. Persistent Provider\nexport function I1nProvider({ children, defaultLocale = \"en_us\" }) {\n  const [locale, setLocaleState] = useState(() => {\n    return localStorage.getItem(STORAGE_KEY) || defaultLocale;\n  });\n\n  // Keep SDK in sync\n  useEffect(() => {\n    sdkSetLocale(locale);\n  }, [locale]);\n\n  const setLocale = (newLocale: string) => {\n    localStorage.setItem(STORAGE_KEY, newLocale);\n    setLocaleState(newLocale);\n  };\n\n  return (\n    <I1nContext.Provider value={{ locale, setLocale }}>\n      {children}\n    </I1nContext.Provider>\n  );\n}\n\n// 3. Simple Hook\nexport const useI1n = () => ({ t, ...useContext(I1nContext) });\n```\n\nUsage:\n\n```tsx\nconst { t, setLocale } = useI1n();\n\nreturn (\n  <div>\n    <h1>{t(\"auth.title\", { user: \"Fran\" })}</h1>\n    <button onClick={() => setLocale(\"es_es\")}>Español</button>\n  </div>\n);\n```\n\n### Non-JS Platforms\n\nFlutter, Android, and iOS projects don't use the SDK. They use the translation files (`.arb`, `.xml`, `.strings`) generated by `i1n pull` with their native localization systems.\n\n---\n\n## 🛡️ Developer Experience\n\n### 🔒 Privacy & Security\n\n- **Auto-Ignore**: `i1n init` automatically adds sensitive config files to your `.gitignore`.\n- **Secret Management**: API keys are only stored locally and never committed to version control.\n- **Encrypted Transmission**: All sync operations happen over secure HTTPS channels.\n\n### 🔒 Zero-Config Type Safety (TypeScript)\n\nThe CLI generates a lightweight declaration file (`i1n.d.ts`) that automatically augments the `i1n` package with your project's specific keys.\n\n1. **Pull**: Run `i1n pull`. The CLI generates `locales/i1n.d.ts` and **automatically updates** your `tsconfig.json` so your IDE finds them immediately.\n2. **Usage**: Import `t` from `i1n` and get full autocomplete + compile-time checking. No manual path mapping required.\n\n```typescript\nimport { t } from \"i1n\";\n\n// Full autocomplete & compile-time checking\nt(\"auth.login.title\");\n\n// ERROR: Argument of type '\"auth.login.titlse\"' is not assignable...\nt(\"auth.login.titlse\");\n\n// Dynamic strings still pass through — useful for `t(item.name)`,\n// runtime-built keys, etc.\ndeclare const dynamicKey: string;\nt(dynamicKey);\n```\n\n> Strict literal checking landed in `1.3.0`: passing a hard-coded string that\n> isn't in `I1nKeys` is now a TypeScript error (no more silent\n> `[i1n] Missing translation` warnings at runtime). Variables typed as\n> `string` keep working without casts.\n\n---\n\n## 💳 Pricing\n\n| Plan | Price | Keys (shared pool) | Languages | AI translations/mo |\n|------|-------|--------------------|-----------|--------------------|\n| **Starter** | $0 | 200 | 2 | 2,000 |\n| **Pro** | $29/mo | 2,000 | 3 | 10,000 |\n| **Business** | $99/mo | 8,000 | 6 | 30,000 |\n| **Enterprise** | From $399/mo | Custom (25k+) | Unlimited | Custom |\n\nCLI, SDK, and MCP server are free on every plan. No credit card required for Starter.\n\nPro lifetime from $199 — [only for the first 200 users](https://i1n.ai/pricing).\n\n---\n\n## 📄 License\n\nMIT — © 2026 i1n.ai\n",
  "bytes": 16768,
  "sha": "a82d4c7b59aa3273cd90fa78fc2015f385f73c9926102b6bdcebd2e9ff799999",
  "repo_slug": "pakvothe/i1n-cli",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pakvothe_i1n_8c4c7b0f/readme"
}