# i18n-keyless
> Ultimate DX for i18n. A keyless internationalization SaaS: write strings in your source language, get AI-powered translations at runtime — no JSON files, no translation keys, no manual upkeep.
i18n-keyless lets you ship multi-language apps without managing translation keys or files. You write text in your primary language (French or English), wrap it in a component or call, and the SDK fetches AI-generated translations on the fly, caches them locally, and lets you override them from a dashboard or directly in code.
Official SDKs: React, React Native (`i18n-keyless-react`), and Node.js (`i18n-keyless-node`).
This file is a single-page Markdown summary of the documentation, intended to be pasted into an LLM context window so the assistant has full coverage of the API surface and conventions.
## Core concepts
- **Keyless**: the source string itself is the translation key — no IDs to manage.
- **Primary language**: `fr` or `en` only. This is the language you write your code in.
- **Supported target languages**: `fr`, `en`, `nl`, `it`, `de`, `es`, `pl`, `pt`, `ro`, `hu`, `sv`, `tr`, `ja`, `cn`, `cz`, `ru`, `ko`, `ar`. Also exported as the runtime const `AVAILABLE_LANGS` from `i18n-keyless-react` and `i18n-keyless-node` (see Type reference below).
- **Storage** (React / React Native only): required, used to cache translations locally. Must implement `getItem`, `setItem`, `removeItem` (sync or async).
- **AI translations**: on cache miss, the server generates a translation with Mistral, stores it server-side and pushes it to the client cache.
- **Overrides**: edit a translation in the dashboard at https://i18n-keyless.com/dashboard, or use `forceTemporary` from code.
- **API key**: get one at https://i18n-keyless.com/#get-api-key.
## Installation
### React (web)
```bash
npm install i18n-keyless-react
```
Initialize once in `main.tsx` / `index.tsx` before rendering the app:
```ts
import * as I18nKeyless from "i18n-keyless-react";
I18nKeyless.init({
API_KEY: "YOUR_API_KEY",
storage: window.localStorage, // or any get/set/del-compatible adapter
languages: {
primary: "fr", // "fr" | "en"
supported: ["en", "fr"],
fallback: "en", // optional
initWithDefault: "fr", // optional
},
});
```
For IndexedDB, wrap `idb-keyval` in a `{ getItem, setItem, removeItem }` adapter.
### React Native
```bash
npx expo install i18n-keyless-react react-native-mmkv
npx expo prebuild
```
```ts
import * as I18nKeyless from "i18n-keyless-react";
import { MMKV } from "react-native-mmkv";
I18nKeyless.init({
API_KEY: "YOUR_API_KEY",
storage: new MMKV(),
languages: { primary: "fr", supported: ["en", "fr"] },
});
```
`AsyncStorage` (`@react-native-async-storage/async-storage`) is also supported.
### Node.js
```bash
npm install i18n-keyless-node
```
```ts
import * as I18nKeyless from "i18n-keyless-node";
I18nKeyless.init({
API_KEY: "YOUR_API_KEY",
languages: { primary: "fr", supported: ["en", "fr"] },
});
```
No storage parameter on Node.
## React / React Native API (`i18n-keyless-react`)
### `I18nKeylessText` — translation component
Use it inline to wrap any text. The text content is also the translation key.
```tsx
import { I18nKeylessText } from "i18n-keyless-react";
Bonjour le monde
```
Props:
- `children: ReactNode` — the source text.
- `context?: string` — disambiguation hint (e.g. `"This is a back button"`).
- `replace?: Record` — placeholder substitution (e.g. `{ "{name}": user.name }`).
- `forceTemporary?: Record` — override per language, e.g. `{ en: "Back" }`.
- `debug?: boolean` — log internals for this call.
### `getTranslation(text, options?)` — string-returning hook
Use when you need a translated string in a prop (placeholder, `aria-label`, tab label, etc.) instead of as JSX.
```tsx
import { getTranslation } from "i18n-keyless-react";
```
Same options as `I18nKeylessText`. Returns `string`. Must be called inside a React component (it's a hook).
### `useCurrentLanguage()` — current language hook
Returns `Lang | null` (null until set).
```ts
const current = useCurrentLanguage(); // "fr" | "en" | ... | null
```
### `setCurrentLanguage(lang)` — switch language
```ts
import { setCurrentLanguage } from "i18n-keyless-react";
setCurrentLanguage("es");
```
Triggers re-render of all consumers. Can be called from anywhere (not a hook).
### `getSupportedLanguages()` — configured languages
Returns the `Lang[]` you passed to `init`. Useful for building a language selector.
### `useI18nKeyless(selector)` — Zustand store accessor
Low-level access to the store. Common selectors:
```ts
const primary = useI18nKeyless((s) => s.config.languages.primary);
const supported = useI18nKeyless((s) => s.config.languages.supported);
const lastRefresh = useI18nKeyless((s) => s.lastRefresh); // for forcing re-renders
```
## Node.js API (`i18n-keyless-node`)
### `awaitForTranslation(text, language, options?)`
Returns a `Promise`. **Must be awaited** — calling without `await` will trigger 429 rate limit errors.
```ts
import { awaitForTranslation, type Lang } from "i18n-keyless-node";
const title = await awaitForTranslation("Viens voir l'application", user.lang as Lang);
const body = await awaitForTranslation(
"Bonjour {name} !",
user.lang as Lang,
{ replace: { "{name}": user.name }, context: "push notification" }
);
```
Options:
- `context?: string`
- `replace?: Record`
- `forceTemporary?: string` — note: a single string here, not a record (target language is already known).
- `debug?: boolean`
### `getSupportedLanguages()`
Same as in the React SDK.
## Common patterns
### Disambiguate ambiguous words
```tsx
RetourProche
```
### Replace dynamic values
Any pattern can be a placeholder: `{name}`, `{{name}}`, `[name]`, `%name%`, ``, plain `name`, `{user_name}`, etc.
```tsx
Hello {name} !
```
### Force a translation from code
When the AI gets it wrong and you can't (or won't) fix it from the dashboard:
```tsx
Retour
```
### Build a language selector
```tsx
import { useCurrentLanguage, setCurrentLanguage, useI18nKeyless } from "i18n-keyless-react";
const current = useCurrentLanguage();
const primary = useI18nKeyless((s) => s.config.languages.primary);
```
### Translate Markdown
`i18n-keyless` only translates strings, so use Markdown for inline styling. With `react-markdown`, force re-render with `lastRefresh`:
```tsx
import { useI18nKeyless, getTranslation } from "i18n-keyless-react";
import ReactMarkdown from "react-markdown";
const lastRefresh = useI18nKeyless((s) => s.lastRefresh);
{getTranslation(children)}
```
### Wrap in a `MyText` component
Recommended in React Native (for fonts/styles) and useful in React too. Forward `context`, `replace`, `forceTemporary`, `debug` through an `i18nProps` prop, and add a `skipTranslation` escape hatch for already-translated content.
### Debug a single call
```tsx
Hello world
getTranslation("Hello world", { debug: true });
await awaitForTranslation("Hello world", "fr", { debug: true });
```
## Type reference
```ts
// Importable runtime const — exported from i18n-keyless-react and i18n-keyless-node
// (re-exported from i18n-keyless-core). Use it to support every available language
// without hardcoding the list, or to build language selectors / validation.
const AVAILABLE_LANGS = [
"fr", "en", "nl", "it", "de", "es", "pl", "pt", "ro",
"hu", "sv", "tr", "ja", "cn", "cz", "ru", "ko", "ar",
] as const;
type Lang = (typeof AVAILABLE_LANGS)[number];
// "fr" | "en" | "nl" | "it" | "de" | "es" | "pl" | "pt" | "ro"
// | "hu" | "sv" | "tr" | "ja" | "cn" | "cz" | "ru" | "ko" | "ar"
type PrimaryLang = "fr" | "en";
type LanguagesConfig = {
primary: PrimaryLang;
supported: Lang[];
fallback?: Lang;
initWithDefault?: Lang;
};
type TranslationOptions = {
context?: string;
debug?: boolean;
forceTemporary?: Partial>; // Node: string
replace?: Record;
};
```
### Using `AVAILABLE_LANGS`
```ts
import { AVAILABLE_LANGS } from "i18n-keyless-react"; // or "i18n-keyless-node"
// Support every available language without hardcoding the list
I18nKeyless.init({
API_KEY: "YOUR_API_KEY",
storage: window.localStorage,
languages: {
primary: "fr",
supported: [...AVAILABLE_LANGS],
},
});
// Validate a user-provided language code at runtime
const isLang = (l: string): l is Lang => (AVAILABLE_LANGS as readonly string[]).includes(l);
```
## Gotchas
- `awaitForTranslation` **must** be awaited. Fire-and-forget calls will hit rate limits.
- `init` must run before any translation call; in React, do it in your entry file before rendering.
- Storage adapter methods can be sync or async, but must all be present (`getItem`, `setItem`, `removeItem`).
- The `primary` language can only be `fr` or `en`. Source strings must be written in that language.
- Translations are cached on-device — if you change a translation in the dashboard, cached clients pick it up via the next refresh, not instantly.
- For `react-markdown` (and similar memoized renderers), pass `key={lastRefresh}` so the tree re-renders when translations refresh.
## Resources
- Website: https://i18n-keyless.com
- Docs: https://i18n-keyless.com/docs/quick-setup
- Dashboard: https://i18n-keyless.com/dashboard
- Get an API key: https://i18n-keyless.com/#get-api-key
- GitHub: https://github.com/arnaudambro/i18n-keyless