-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathllms.txt
More file actions
801 lines (626 loc) · 47 KB
/
Copy pathllms.txt
File metadata and controls
801 lines (626 loc) · 47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
# 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, 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`), Node.js (`i18n-keyless-node`), Vue 3 / Nuxt (`i18n-keyless-vue`), Angular (`i18n-keyless-angular`), plain browser with a script tag and the `<i18n-t>` web component (`i18n-keyless-browser`), Laravel (`i18n-keyless/laravel` on Packagist), Ruby on Rails (`i18n-keyless-rails` on RubyGems) and Flutter (`i18n_keyless` on pub.dev). One protocol, one dashboard, one API key. This file documents the React and Node SDKs in depth; the other packages are listed under "Other packages and ports" with a link to their own reference.
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**: the language you write your code in. Since v3 it can be **any** of the supported languages; before v3 it had to be `fr` or `en`.
- **Supported target languages** (48, the App Store localizations):
<!-- fact:langs-inline -->
`ar`, `bn`, `ca`, `zh-Hans`, `zh-Hant`, `hr`, `cs`, `da`, `nl`, `en`, `en-GB`, `fi`, `fr`,
`fr-CA`, `de`, `el`, `gu`, `he`, `hi`, `hu`, `id`, `it`, `ja`, `kn`, `ko`, `ms`, `ml`, `mr`,
`no`, `or`, `pl`, `pt`, `pt-BR`, `pa`, `ro`, `ru`, `sk`, `sl`, `es`, `es-MX`, `sv`, `ta`, `te`,
`th`, `tr`, `uk`, `ur`, `vi`
<!-- /fact -->
Also exported as the runtime const `AVAILABLE_LANGS` from `i18n-keyless-react` and `i18n-keyless-node` (see Type reference below).
- **Version**: this file describes **v3** (`i18n-keyless-*@3.x`). v3 renamed two codes — `cn` became `zh-Hans` and `cz` became `cs` — and lifted the restriction on the primary language. See the upgrade guide: https://docs.i18n-keyless.com/docs/guides/upgrade-v3.
- **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.
- **Namespaces**: an optional string per translation (e.g. `"checkout"`) that partitions a project's translations so a client fetches and stores only the slice it renders — the fix for the browser storage quota error. It's a fetch/storage partition, **not** a semantic key (use `context` to disambiguate meaning). The reserved default namespace is the literal string `"default"`; with no namespace set anywhere, behavior is identical to legacy. See "Partition translations with namespaces" below.
- **User generated content (UGC)**: text written by users in *their* language (a comment, a review, a bio). Pass `originLanguage` to tell i18n-keyless what language it's written in — the text itself is still the key. See "Translate user generated content (UGC)" below (`>= 2.5.0`).
- **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", // any supported language
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.
### Other packages and ports
Same protocol, same options (`context`, `replace`, `namespace`, `unpersistedNamespace`, `forceTemporary`, `originLanguage`, `debug`), same storage keys, same dashboard. Each README is the complete reference for its package.
- **Vue 3 / Nuxt / Vite SSR** (`npm install i18n-keyless-vue`): `init()` then `createApp(App).use(I18nKeyless)` registers `<T>`; `t()` from `useI18nKeyless()` for attributes, `useTranslation()` for a computed string; SSR through the plugin options `{ lang, translations }`. README: https://github.com/arnaudambro/i18n-keyless/blob/main/packages/vue/README.md
- **Angular >= 17.1, standalone, signals, Angular SSR** (`npm install i18n-keyless-angular`): `provideI18nKeyless(config)` in the root providers; the `<i18n-t>` component, the impure `t` pipe and `I18nKeylessService.translation()` signals; `provideI18nKeylessServer()` for localized SSR; published AOT-ready (built with `ngc`). README: https://github.com/arnaudambro/i18n-keyless/blob/main/packages/angular/README.md
- **Plain browser: Svelte, Alpine, htmx, jQuery, legacy sites** (`npm install i18n-keyless-browser`, or one `<script async type="module" src="https://esm.sh/i18n-keyless-browser/auto" data-api-key data-primary data-supported>` tag): the `data-i18n` attribute, the `<i18n-t>` web component, and `getTranslation` / `watchTranslation` / `translateDom` from JS (`window.i18nKeyless` in script-tag mode). README: https://github.com/arnaudambro/i18n-keyless/blob/main/packages/browser/README.md
- **Laravel 11, 12, 13** (`composer require i18n-keyless/laravel`, then `I18N_KEYLESS_API_KEY` and `I18N_KEYLESS_LANGUAGES` in `.env`): the existing `__('...')` calls (JSON keyless mode) resolve through the API, cached in Laravel's cache; `:name` placeholders stay Laravel's; `i18nk('text', context: '...')` for a context. README: https://github.com/arnaudambro/i18n-keyless/blob/main/ports/laravel/README.md
- **Ruby on Rails 7, 8** (`bundle add i18n-keyless-rails`, then `I18N_KEYLESS_API_KEY` and `I18N_KEYLESS_LANGUAGES` in the environment): an I18n backend chained after the app's, so `t('Welcome to our app')` with a source string resolves through the API, cached in `Rails.cache`, while YAML keys (`t('users.index.title')`, a Symbol, a lowercase identifier) keep working and win; `%{name}` placeholders stay I18n's; `t('text', context: '...')` or `i18nk('text', context: '...')` for a context. README: https://github.com/arnaudambro/i18n-keyless/blob/main/ports/rails/README.md
- **Flutter / Dart** (`flutter pub add i18n_keyless`): one `I18nKeylessClient`, `init` before `runApp`, `I18nKeylessScope` around the app, the `T('...')` widget and `context.t('...')`; `Lang` is an enum; no ARB files, no `gen-l10n`. README: https://github.com/arnaudambro/i18n-keyless/blob/main/ports/flutter/README.md
- **Protocol and ports**: the language-neutral wire protocol every SDK implements (https://github.com/arnaudambro/i18n-keyless/blob/main/docs/PROTOCOL.md), the JSON conformance vectors every port replays (https://github.com/arnaudambro/i18n-keyless/tree/main/conformance) and the checklist for a new port (https://github.com/arnaudambro/i18n-keyless/blob/main/docs/PORT_CHECKLIST.md).
## 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";
<h1><I18nKeylessText>Bonjour le monde</I18nKeylessText></h1>
```
Props:
- `children: ReactNode` — the source text.
- `context?: string` — disambiguation hint (e.g. `"This is a back button"`).
- `namespace?: string` — fetch/storage partition for this translation. Defaults to `defaultNamespace` from `init()`, or `"default"`.
- `unpersistedNamespace?: boolean` — when `true`, the `namespace`'s translations live in memory only (never written to storage, never reloaded at boot). For high-cardinality/transient namespaces.
- `replace?: Record<string, string>` — placeholder substitution (e.g. `{ "{name}": user.name }`).
- `forceTemporary?: Record<string, string>` — override per language, e.g. `{ en: "Back" }`.
- `originLanguage?: Lang` — the language the text is written in when it differs from your primary language (user generated content). See "Translate user generated content (UGC)" below.
- `debug?: boolean` — log internals for this call.
### `getTranslation(text, options?)` — string-returning function
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";
<input placeholder={getTranslation("Votre email")} />
<Tab label={getTranslation("Premier onglet", { context: "navigation" })} />
```
Same options as `I18nKeylessText`. Returns `string`. It is a **plain function, not a hook** — it reads the store at call time, so it also works outside components (route loaders, utilities) once `init` has run. The flip side: it is **not reactive**. A component that only calls `getTranslation` won't re-render when the translation arrives or the language changes — subscribe in that component with `useCurrentLanguage()` and/or `useI18nKeyless((s) => s.translations)`.
### `useTranslation(text, options?)` — translated string hook (≥ 3.3.0)
```tsx
import { useTranslation } from "i18n-keyless-react";
function SearchBox() {
const placeholder = useTranslation("Votre email");
const title = useTranslation("8 heures", { context: "durée" });
return <input placeholder={placeholder} title={title} />;
}
```
Same options as `I18nKeylessText`; it is the hook behind that component. Returns `string`. **Inside a component, this is the string API**: it is reactive, and under SSR it reads `<I18nKeylessProvider>` like `<T>` does — so it is correct in TanStack Start, where `getTranslation()` in a component body renders the primary language. Keep `getTranslation()` for code outside components (loaders, `head()`, utilities).
### `useCurrentLanguage()` — current language hook
Returns `Lang | null` (null until set). Under `<I18nKeylessProvider>` (SSR) it is the provider's language, on the server and the client alike (≥ 3.3.0).
```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 translations = useI18nKeyless((s) => s.translations); // subscribe → re-render whenever translations arrive/refresh
```
To make a component that calls the non-reactive `getTranslation` re-render on refresh, subscribe to `s.translations` (a fresh object on every merge — including boot hydration and multi-namespace refreshes, which `s.lastRefresh` can miss).
## Node.js API (`i18n-keyless-node`)
Two functions fetch/retrieve a translation. Both **must be awaited** — calling without
`await` will trigger 429 rate limit errors (this package POSTs directly, with no queue).
### `awaitForTranslationOrFallbackToOriginal(text, language, options?)`
Returns a `Promise<string>` that **never rejects**. On a failed request it resolves to the
original text (`replace` still applied) and logs the failure with `console.error`. Use it in
request-time code: HTTP route handlers, server components, `generateMetadata`, API
responses, emails, push notifications — anywhere one failed translation must not fail the
whole request.
```ts
import { awaitForTranslationOrFallbackToOriginal, type Lang } from "i18n-keyless-node";
const title = await awaitForTranslationOrFallbackToOriginal("Viens voir l'application", user.lang as Lang);
const body = await awaitForTranslationOrFallbackToOriginal(
"Bonjour {name} !",
user.lang as Lang,
{ replace: { "{name}": user.name }, context: "push notification" }
);
```
### `awaitForTranslationOrThrow(text, language, options?)`
Same lookup, same options. Returns a `Promise<string>` that **rejects** on a failed request
with an `Error` naming the key (the original error as `cause`). An ignored rejection crashes
the Node process by design; a `try/catch` or `.catch()` is honoured. Use it in scripts,
build steps, and migrations, where a failed translation should stop the run instead of
shipping source text.
```ts
import { awaitForTranslationOrThrow, type Lang } from "i18n-keyless-node";
const title = await awaitForTranslationOrThrow("Viens voir l'application", "fr" as Lang);
```
> **Deprecated:** `awaitForTranslation` is an alias of `awaitForTranslationOrThrow` (same
> behaviour), kept since 3.5.0 for backward compatibility. Removed in 4.0.0.
Options (identical for both functions):
- `context?: string`
- `namespace?: string` — fetch partition; the backend returns only the requested namespace. Defaults to `defaultNamespace` from `init()`, or `"default"`.
- `unpersistedNamespace?: boolean` — marks the namespace as transient (mainly a client-storage flag; harmless on Node).
- `replace?: Record<string, string>`
- `forceTemporary?: Partial<Record<Lang, string>>` — keyed by target language, e.g. `{ forceTemporary: { en: "Go Back" } }`.
- `originLanguage?: Lang` — the language the text is written in when it differs from your primary language (user generated content). See "Translate user generated content (UGC)" below.
- `debug?: boolean`
### `getSupportedLanguages()`
Same as in the React SDK.
## Common patterns
### Disambiguate ambiguous words
```tsx
<I18nKeylessText context="back button in navigation">Retour</I18nKeylessText>
<I18nKeylessText context="distance / proximity">Proche</I18nKeylessText>
```
### Replace dynamic values
Any pattern can be a placeholder: `{name}`, `{{name}}`, `[name]`, `%name%`, `<name>`, plain `name`, `{user_name}`, etc.
```tsx
<I18nKeylessText replace={{ "{name}": user.name }}>Hello {name} !</I18nKeylessText>
```
### Partition translations with namespaces
Attach a `namespace` per call so each screen fetches/stores only its own slice (fixes the
`Setting the value of 'i18n-keyless-translations' exceeded the quota` storage error):
```tsx
<I18nKeylessText namespace="checkout">Pay now</I18nKeylessText>
getTranslation("Pay now", { namespace: "checkout" });
await awaitForTranslationOrFallbackToOriginal("Pay now", user.lang as Lang, { namespace: "checkout" });
```
Set a global default in `init` (per-call `namespace` always overrides it):
```ts
I18nKeyless.init({
API_KEY: "YOUR_API_KEY",
storage: window.localStorage,
defaultNamespace: "app-ui", // optional; falls back to the reserved "default"
languages: { primary: "en", supported: ["en", "fr", "es"] },
});
```
For high-cardinality or short-lived namespaces (e.g. one per discussion or document), add
`unpersistedNamespace` so the namespace's translations stay in memory only — never written
to storage, never reloaded at boot:
```tsx
<I18nKeylessText namespace={`discussion-${id}`} unpersistedNamespace>{message}</I18nKeylessText>
getTranslation(message, { namespace: `discussion-${id}`, unpersistedNamespace: true });
```
Notes:
- Namespaces and `context` are independent: namespace = fetch/storage partition, context = meaning disambiguation. The same source text can live in several namespaces with identical translations.
- Reusing a source text across namespaces never costs an extra AI translation — the backend copies the existing translation into the new namespace.
### Translate user generated content (UGC) — `>= 2.5.0`
Your UI strings are written in your primary language, but users write in *their* language. Pass `originLanguage` to say what language the text is written in — everything else stays the same, the text itself is the key:
```tsx
<I18nKeylessText originLanguage={comment.lang}>{comment.text}</I18nKeylessText>
getTranslation(comment.text, { originLanguage: comment.lang });
await awaitForTranslationOrFallbackToOriginal(comment.text, viewer.lang as Lang, { originLanguage: comment.lang as Lang });
```
What happens (say your primary language is `en` and a user posts `"Hola mundo"` with `originLanguage="es"`):
1. The backend AI-translates it once into the primary language (`"Hello world"`), which becomes the translation key server-side.
2. The original text is kept **verbatim** for viewers in the origin language — Spanish readers always see exactly what the author wrote, never an AI rewrite. When the viewer's language **is** the origin language, the text renders as-is with no API call.
3. Every other supported language is AI-translated from the primary version. Lookups by the original text resolve in every viewer language — including the primary one.
Notes:
- `originLanguage` omitted or equal to the primary language → the regular flow, unchanged.
- Combine with a namespace — ideally an unpersisted one (e.g. one per discussion) — so high-cardinality user content never bloats local storage or the UI-strings bundle.
- Billing: a never-seen UGC string costs one extra AI call (origin → primary) on top of the regular translation call. Re-posting the same text is free.
### Force a translation from code
When the AI gets it wrong and you can't (or won't) fix it from the dashboard:
```tsx
<I18nKeylessText forceTemporary={{ en: "Back" }}>Retour</I18nKeylessText>
```
### Build a language selector
```tsx
import { useCurrentLanguage, setCurrentLanguage, useI18nKeyless } from "i18n-keyless-react";
const current = useCurrentLanguage();
const primary = useI18nKeyless((s) => s.config.languages.primary);
<select value={current || primary} onChange={(e) => setCurrentLanguage(e.target.value as Lang)}>
{/* ...options... */}
</select>
```
### Translate Markdown
`i18n-keyless` only translates strings, so use Markdown for inline styling. `getTranslation` is not reactive, so subscribe to `s.translations` to re-render when translations arrive, and key `react-markdown` by the resolved text (it prevents re-rendering otherwise):
```tsx
import { useI18nKeyless, getTranslation } from "i18n-keyless-react";
import ReactMarkdown from "react-markdown";
useI18nKeyless((s) => s.translations); // subscribe → re-render on refresh
const text = getTranslation(children);
<ReactMarkdown key={text}>{text}</ReactMarkdown>
```
### Translate a blog post or long content
A post is **not one translation, it is one translation per block**. The server refuses a source string longer than **2000 characters** (`context` and `namespace` are capped at 200), so the whole post is never a key. That cap is a quality rule, not a storage one — a row is keyed by a hash of its source text. Blocks are the better unit anyway: an edit re-translates one paragraph instead of the whole article, and a model asked for one paragraph does not drift, drop a list or reformat a table.
The recipe:
1. Split the document into Markdown blocks — a heading, a paragraph, a whole list, a quote, a table row. Never split inside a sentence. Target **1000 characters** per block; cut a longer paragraph at a sentence break, but keep a table or a list whole whatever its length.
2. Keep the Markdown **inside** each block (`**bold**`, `[link](url)`, `` `code` ``, list markers). The translation keeps the syntax and the renderer restores the styling.
3. Pass the **same `context` to every block: one very short summary of the post**, one sentence, 200 characters maximum. A lone paragraph does not say what the article is about; the summary does, and it is what makes that paragraph translate correctly.
4. Pass one `namespace` per post (`blog:<slug>`), so a reader downloads only the post they open.
5. Never send a code fence, a URL or a front-matter key to be translated.
6. Keep the summary stable: `context` is part of the row identity `(api key, source language, source text, context, namespace)`. Rewriting it makes every block a new row, translated and billed again.
The title, the description, the excerpt and every image `alt` are blocks too — same `context`, same `namespace`.
```ts
import { awaitForTranslationOrFallbackToOriginal, type Lang } from "i18n-keyless-node";
const SUMMARY = "Guide: cache API responses at the edge with a Cloudflare Worker.";
async function translatePost(blocks: string[], slug: string, lang: Lang): Promise<string> {
const out: string[] = [];
for (const block of blocks) {
out.push(
await awaitForTranslationOrFallbackToOriginal(block, lang, {
context: SUMMARY,
namespace: `blog:${slug}`,
}),
);
}
return out.join("\n\n");
}
```
In React, the same shape per block: `useTranslation(block, { context: SUMMARY, namespace })`, each rendered through `react-markdown`. Full guide: https://docs.i18n-keyless.com/docs/guides/long-form-content
### 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
<I18nKeylessText debug>Hello world</I18nKeylessText>
getTranslation("Hello world", { debug: true });
await awaitForTranslationOrFallbackToOriginal("Hello world", "fr", { debug: true });
```
## Server-Side Rendering (SSR)
Works under SSR (TanStack Start, Next, Remix, Astro, Expo Router server output, and any
Node/modern-edge runtime) since `i18n-keyless-react >= 2.0.0` (TanStack Start / Vite SSR
needs `>= 2.3.2`). v2 is a **drop-in for SPAs** — no code changes. The server is **read-only**: usage analytics are never sent
from the server, so SSR adds no API traffic (on a long-lived server it's reported once
per process boot, not once per user — fewer calls than a SPA). Translate-on-miss is
unchanged: a key with no translation yet is still requested once.
`storage` is **optional on the server** (defaults to an in-memory store, also exported as
`createMemoryStorage()`) and still required in the browser. Pass `ssr: true` to `init` to
force read-only mode explicitly (useful on serverless cold starts). Runtime: Node ≥ 20.10
or a modern bundler (Vite, esbuild, webpack 5, Rollup 3+). `runWithI18nKeyless` needs
`AsyncLocalStorage` (Node and most edge runtimes; Cloudflare Workers needs a flag — where
unavailable, use `<I18nKeylessProvider>` for `<T>` and the scope degrades to a no-op).
The SSR additions are inert in the browser: `node:async_hooks` is loaded via a guarded
dynamic import, so it never enters browser/React Native bundles and the browser path is a
no-op. There are two modes.
### The two translation paths (the part most people get wrong)
Two paths resolve the language through two different mechanisms:
- **Component path**: `<I18nKeylessText>` / `<T>` → `<I18nKeylessProvider lang translations>`
(React context). Reads context first, then the ALS, then the store.
- **Function path**: imperative `getTranslation(key)` → the ALS scope set by
`runWithI18nKeyless`. `getTranslation` is a plain function and **cannot read React
context**, so it depends on the ALS.
How much wiring you need depends on whether the component tree renders **inside** or
**outside** the ALS scope — which is framework-specific:
- **Remix / React Router 7** — `entry.server` renders the tree *inside* `runWithI18nKeyless`,
so on the server both paths work from the ALS alone — **no Provider needed**. For the
client, serialize a snapshot (`getUsedTranslationsSnapshot()` in a `<script>` after the
app) and call `hydrateFromServer` before `hydrateRoot`.
- **TanStack Start** (requires `>= 2.3.2`) — the tree renders *outside* the ALS; only
`head()` and route loaders run inside it. Need **both**: `<I18nKeylessProvider>` via the
root loader (component path) AND `runWithI18nKeyless` wrapping the WHOLE handler (function
path). Call `getTranslation()` only in loaders / `head()`, never in a component body.
- **Next.js App Router / Astro islands** — no render hook to wrap; use `<I18nKeylessProvider>`
for `<T>` (SSR-correct). Imperative `getTranslation()` in server output renders the
primary language until the client effect — prefer `<T>` for zero flash.
### Primary-language SSR (default, no extra code)
The server renders your **primary** language and the client's first render matches it
exactly (no hydration mismatch); after hydration the client fills translations from cache
and re-renders into the user's language. Just call `init` the same way, omitting
`storage` on the server:
```ts
import { init } from "i18n-keyless-react";
await init({
languages: { primary: "fr", supported: ["fr", "en"] },
API_KEY: process.env.I18N_KEYLESS_API_KEY,
storage: typeof window === "undefined" ? undefined : window.localStorage,
});
```
### Localized SSR (server-render any language, for SEO)
To serve translated, indexable HTML for `/{lang}/...` or `?lang=xx` URLs:
1. **`getServerTranslations(lang)`** — returns the translations map for `lang`, cached
per server process (each language fetched at most once per boot). Requires `init` to
have run first. Returns `{}` for the primary language. Evict with
`clearServerTranslationsCache(lang?)`.
2. **`runWithI18nKeyless({ lang, translations }, fn)`** — wrap the server render so both
`getTranslation(...)` (a plain function — reads the request scope via
`AsyncLocalStorage`) and `<T>` / `<I18nKeylessText>` (components — read the Provider)
resolve in `lang`. Wrap once; no per-call-site changes.
3. **`<I18nKeylessProvider lang translations>`** — per-request React context for `<T>`,
and what makes client hydration flash-free. In localized SSR the language is the `lang`
you pass (drive it from the URL); `setCurrentLanguage` is for SPA mode only.
```tsx
import { runWithI18nKeyless, getServerTranslations, I18nKeylessProvider } from "i18n-keyless-react";
import { renderToString } from "react-dom/server"; // or your framework's renderer
const lang = langFromUrlOrHeader(request); // e.g. "en"
const translations = await getServerTranslations(lang);
const html = await runWithI18nKeyless({ lang, translations }, () =>
renderToString(
<I18nKeylessProvider lang={lang} translations={translations}>
<App /> {/* every <T>…</T> and getTranslation(...) inside renders in `lang` */}
</I18nKeylessProvider>
)
);
```
For flash-free hydration, inline the fetched `translations` into the HTML and pass the
**same** object to the `<I18nKeylessProvider>` on the client. The Provider covers the `<T>`
/ `<I18nKeylessText>` **component** form on the first client render (it reads React
context).
```tsx
// server: embed the data
<script id="i18n-data" type="application/json"
dangerouslySetInnerHTML={{ __html: JSON.stringify({ lang, translations }) }} />;
// client: read it back and feed the same provider
const { lang, translations } = JSON.parse(document.getElementById("i18n-data").textContent);
<I18nKeylessProvider lang={lang} translations={translations}><App /></I18nKeylessProvider>;
```
#### `getTranslation` function form — synchronous client seed (`>= 2.2.0`)
`getTranslation(key)` is a plain function that reads the store, not React context. The
Provider seeds the store on **mount** (after the first render), so on a cold cache the
function form renders the primary language first and re-renders → mismatch + blink. Fix:
seed the store **synchronously before `hydrateRoot`** with `hydrateFromServer`, from a
snapshot the server reads via `getRequestScope()`:
```tsx
// server — embed snapshot from inside the scoped render
const html = await runWithI18nKeyless({ lang, translations }, () => {
const body = renderToString(
<I18nKeylessProvider lang={lang} translations={translations}><App /></I18nKeylessProvider>
);
return `${body}<script id="i18n-keyless" type="application/json">${JSON.stringify(getRequestScope())}</script>`;
});
// client — seed synchronously, then init, then hydrate
import { hydrateFromServer, init } from "i18n-keyless-react";
const el = document.getElementById("i18n-keyless");
if (el) hydrateFromServer(JSON.parse(el.textContent)); // { lang, translations } — BEFORE first render
init({ languages: { primary: "fr", supported: ["fr", "en"] }, API_KEY, storage: window.localStorage });
hydrateRoot(document, <App />);
```
`init`'s async hydrate treats the seed as authoritative (won't reset to primary on a cold
cache). `hydrateFromServer` is SSR-only and opt-in — SPA/non-SSR apps never call it.
Separately (`>= 2.2.0`), `getTranslation` defers its usage-analytics write to a
`queueMicrotask`, so calling it during render no longer triggers React's "Cannot update a
component while rendering a different component" warning; the server records nothing.
**Large translation sets (`>= 2.3.0`).** By default the snapshot embeds the full language
set in every page's HTML — fine for small/medium sets (it doubles as a warm cache for
instant client navigation). For thousands of keys, serialize only the keys the page
rendered: swap `getRequestScope()` for `getUsedTranslationsSnapshot()` at the
serialization site. During render, each `getTranslation(...)` / `<I18nKeylessText>` adds
its key to a per-request `Set` (no store write, no re-render); the snapshot returns just
those keys (intersected with available). The full set stays in scope so any key still
resolves mid-render — only the serialized payload narrows; concurrent requests are
isolated. Keep calling `init()` on the client: its background fetch loads the full set so
post-hydration navigation has every key (until then, misses resolve via translate-on-miss).
Measure first: `JSON.stringify(getRequestScope().translations).length`. **Not compatible
with TanStack Start** (or any setup whose body renders outside the ALS / uses code-split
routes): keys aren't recorded, so the subset misses body keys → mismatch. Use the full map
there.
### SSR exports
- `getServerTranslations(lang) => Promise<Translations>` — fetch a language's
translations on the server, cached per process.
- `clearServerTranslationsCache(lang?) => void` — evict one/all languages from that cache.
- `runWithI18nKeyless(scope, fn) => Promise<R>` — run a server render with
`{ lang, translations }` active so `getTranslation` and `<T>` resolve in `lang`.
- `getRequestScope() => { lang, translations } | undefined` — read the active request
scope (advanced) — serialize for the client (full set).
- `getUsedTranslationsSnapshot() => { lang, translations } | undefined` (`>= 2.3.0`) — like
`getRequestScope` but `translations` is narrowed to only the keys this render touched —
for large sets. **Render-inside-ALS only** — incompatible with TanStack Start (and any
setup whose body renders outside the ALS or uses code-split routes): keys aren't recorded,
so the subset misses body keys → hydration mismatch; serialize the full map there.
`undefined` outside a `runWithI18nKeyless` render.
- `I18nKeylessProvider({ lang, translations, children })` — per-request context for `<T>`;
seeds the store on the client for flash-free hydration of the component form.
- `hydrateFromServer({ lang, translations }) => void` (`>= 2.2.0`) — client-only; seed the
store *synchronously* before `hydrateRoot` so the `getTranslation` function form is
correct on first render. No-op without `lang`.
- `createMemoryStorage() => Storage` — in-memory storage adapter (server default).
- `init({ …, ssr? })` — `storage` optional on the server; `ssr: true` forces read-only.
### Migrating from an SSR workaround
- Remove `ssr: { noExternal: ['i18n-keyless-*'] }` from your bundler config — v2's build
is valid native Node ESM and externalizes cleanly.
- Remove any no-op server storage — omit `storage` on the server; it defaults to
in-memory.
- To render non-primary languages, adopt the localized-SSR flow above
(`getServerTranslations` + `runWithI18nKeyless` + `<I18nKeylessProvider>`).
### Framework recipes
Copy-paste integration guides per meta-framework live at:
- TanStack Start (**requires `>= 2.3.2`**) — https://i18n-keyless.com/docs/ssr/tanstack-start
(in `src/server.ts`: `createStartHandler(defaultStreamHandler)` + wrap the WHOLE `fetch`
in `runWithI18nKeyless`, export via `createServerEntry({ fetch })` — not just
`defaultStreamHandler`; `<I18nKeylessProvider>` from the root-route loader; call
`getTranslation` only in loaders/`head()`, never in a component body; client just
`initI18nClient()` — no `hydrateFromServer`; `?lang=` is the single source of truth;
serialize the full map, not `getUsedTranslationsSnapshot()`).
- Remix / React Router 7 framework mode — https://i18n-keyless.com/docs/ssr/remix (wrap
`renderToPipeableStream` in `runWithI18nKeyless` in `app/entry.server.tsx`; tree renders
inside the ALS so both paths resolve from it — **no Provider**; serialize via a tiny
`<I18nKeylessSnapshot>` (uses `getUsedTranslationsSnapshot()`) rendered after the app in
`root.tsx`; `entry.client.tsx` calls `hydrateFromServer(snapshot)` before `hydrateRoot`,
then `initI18nClient()`).
- Next.js App Router — https://i18n-keyless.com/docs/ssr/nextjs (no render hook to wrap, so
no `runWithI18nKeyless`: `app/[lang]/layout.tsx` (server) fetches `getServerTranslations`
→ a `"use client"` `Providers` boundary rendering `<I18nKeylessProvider>` and calling
`hydrateFromServer` + `initI18nClient` in an effect; imperative `getTranslation` renders
the primary language until that effect — prefer `<T>`).
- Astro (React islands) — https://i18n-keyless.com/docs/ssr/astro (`.astro` page fetches
`getServerTranslations`, passes `lang` + `translations` as island props; island renders
`<I18nKeylessProvider>` and runs `hydrateFromServer` + `initI18nClient` in an effect;
prefer `<T>` over imperative `getTranslation` for zero flash).
### Example apps
Runnable examples (one per framework), each with a passing test suite, running offline
against a bundled mock backend (no API key needed):
https://github.com/arnaudambro/i18n-keyless/tree/main/examples — docs at
https://i18n-keyless.com/docs/examples. Covers `vite-react` (SPA), `tanstack-start`
(SSR+SPA, needs `>= 2.3.2`), `remix-rr7` (SSR), `nextjs` (App Router SSR), `astro` (SSR
islands), `node` (`i18n-keyless-node` + `awaitForTranslationOrThrow` /
`awaitForTranslationOrFallbackToOriginal`), `react-native` (MMKV /
AsyncStorage), and `expo`. Run: `cd examples/<name> && cp .env.example .env && npm install
&& npm run dev` (leave the API key empty + run `examples/_mock-server` to try offline).
### Changelog
- `2.5.0` — `originLanguage` (UGC): translate user-written text arriving in any supported
language; the raw text is kept verbatim for origin-language viewers, everything else is
AI-translated via the primary language.
- `2.4.0` — namespaces (`namespace`, `unpersistedNamespace`, init's `defaultNamespace`):
per-slice fetch/storage partitioning (fixes the browser storage quota error).
- `2.3.2` — fix SSR request scope under TanStack Start / Vite: the per-request
AsyncLocalStorage is now pinned to a single `globalThis` slot shared across V8 realms /
module graphs (was duplicated, so `?lang=en` rendered the primary language with a
hydration mismatch). SPA, Next.js, Remix, Astro, and Node were unaffected. No API change.
- `2.3.0` — `getUsedTranslationsSnapshot()` for per-page key subsets on large translation
sets.
- `2.2.0` — `hydrateFromServer()` synchronous client seed for the `getTranslation` function
form; `getTranslation` defers usage recording to a microtask (no setState-during-render
warning).
- `2.1.0` — AsyncLocalStorage request scope (`runWithI18nKeyless`, `getRequestScope`).
- `2.0.0` — SSR support (`getServerTranslations`, `<I18nKeylessProvider>`, read-only server,
optional server `storage`).
## 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 = [
// fact:langs-array
"ar", "bn", "ca", "zh-Hans", "zh-Hant", "hr", "cs", "da", "nl", "en", "en-GB", "fi", "fr",
"fr-CA", "de", "el", "gu", "he", "hi", "hu", "id", "it", "ja", "kn", "ko", "ms", "ml", "mr",
"no", "or", "pl", "pt", "pt-BR", "pa", "ro", "ru", "sk", "sl", "es", "es-MX", "sv", "ta",
"te", "th", "tr", "uk", "ur", "vi",
// /fact
] as const;
type Lang = (typeof AVAILABLE_LANGS)[number];
// fact:langs-union
// "ar" | "bn" | "ca" | "zh-Hans" | "zh-Hant" | "hr" | "cs" | "da" | "nl" | "en"
// | "en-GB" | "fi" | "fr" | "fr-CA" | "de" | "el" | "gu" | "he" | "hi" | "hu" | "id"
// | "it" | "ja" | "kn" | "ko" | "ms" | "ml" | "mr" | "no" | "or" | "pl" | "pt" | "pt-BR"
// | "pa" | "ro" | "ru" | "sk" | "sl" | "es" | "es-MX" | "sv" | "ta" | "te" | "th" | "tr"
// | "uk" | "ur" | "vi"
// /fact
// Any supported language can be the one you write your app in (v3; it was "fr" | "en" before).
type PrimaryLang = Lang;
type LanguagesConfig = {
primary: PrimaryLang;
supported: Lang[];
fallback?: Lang;
initWithDefault?: Lang;
};
type TranslationOptions = {
context?: string;
debug?: boolean;
forceTemporary?: Partial<Record<Lang, string>>;
namespace?: string; // defaults to init()'s defaultNamespace, else "default"
originLanguage?: Lang; // UGC: the language the text is written in, when ≠ primary
replace?: Record<string, string>;
unpersistedNamespace?: boolean; // memory-only namespace, never persisted
};
// init() also accepts:
// defaultNamespace?: string — namespace applied to every call that doesn't set its own
```
### 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
- `awaitForTranslationOrThrow` / `awaitForTranslationOrFallbackToOriginal` **must** be awaited, always — even the fallback variant, which never rejects, still needs it for rate limiting. 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`).
- Source strings must be written in the `primary` language. Since v3 that can be any supported 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.
- `getTranslation` is a plain function, not a hook — components that rely on it must subscribe to the store (`useCurrentLanguage()` / `useI18nKeyless((s) => s.translations)`) to re-render when translations refresh.
- For `react-markdown` (and similar renderers that block re-renders), key the renderer by the resolved translated text (see "Translate Markdown" above).
- A source string is capped at 2000 characters (`context` and `namespace` at 200); longer is a `400`. The cap on the source text is a quality rule, not a storage one: the unique index holds a sha256 of the text, so only `context` and `namespace` sit in its size budget. Long-form content is allowed, but it must be sent block by block, around 1000 characters each — see "Translate a blog post or long content" above.
## Operate it from your agent (MCP)
i18n-keyless ships an MCP server. Anything a human can do in the dashboard, an agent can do with a prompt — the same URL for every client, OAuth instead of a pasted key. Guide: https://docs.i18n-keyless.com/docs/guides/mcp
Claude Code:
```bash
claude mcp add --transport http i18n-keyless https://api.i18n-keyless.com/mcp
```
Cursor, Windsurf, any client reading an `mcpServers` block:
```json
{ "mcpServers": { "i18n-keyless": { "type": "http", "url": "https://api.i18n-keyless.com/mcp" } } }
```
The flow, once per project: the agent calls the server and gets a `401` with OAuth metadata; the browser opens **once** on `i18n-keyless.com/oauth/consent`, where you sign in or sign up (plan and Stripe payment included), pick the project and approve; the agent then holds a token bound to you and that one project — refreshed automatically, revocable from the agent. One token, one project: add the server once per app.
Tools, by the scope you grant on the consent page (a tool outside the granted scopes is not offered):
- `i18n:read` — `get_started` (install + `init` with the project's public key and languages filled in), `search_docs`, `list_languages`, `get_project`, `list_projects`, `list_translations` (paginated, `missingOnly` for rows a language is still empty in), `get_translation`.
- `i18n:write` — `translate`, `override_translation`, `delete_translation` (needs `confirm: true`), `set_project_languages` (primary only while the project has no translation).
- `i18n:account` — `create_project` (instance admins only; on i18n-keyless.com projects are created from the website), `invite_member` (returns the invitation to pass on; no email is sent), `remove_member` — owner or admin, need `confirm: true`.
Not tools, on purpose: code scanning (the agent already has the code), the free trial, plan changes (not in the dashboard yet). Stateless HTTP, no sessions; language codes are v3 only; MCP traffic never counts as a monthly active user.
## Resources
- Website: https://i18n-keyless.com
- Docs: https://docs.i18n-keyless.com
- Dashboard: https://i18n-keyless.com/dashboard
- Get an API key: https://i18n-keyless.com/#get-api-key
- GitHub: https://github.com/arnaudambro/i18n-keyless
- Agent Skill (`SKILL.md`, for Claude Code and any tool that reads skills): https://github.com/arnaudambro/i18n-keyless/blob/main/skills/i18n-keyless/SKILL.md
- Per-package skills: Vue https://github.com/arnaudambro/i18n-keyless/blob/main/packages/vue/SKILL.md, Angular https://github.com/arnaudambro/i18n-keyless/blob/main/packages/angular/SKILL.md, browser https://github.com/arnaudambro/i18n-keyless/blob/main/packages/browser/SKILL.md, Laravel https://github.com/arnaudambro/i18n-keyless/blob/main/ports/laravel/SKILL.md, Rails https://github.com/arnaudambro/i18n-keyless/blob/main/ports/rails/SKILL.md, Flutter https://github.com/arnaudambro/i18n-keyless/blob/main/ports/flutter/SKILL.md
- Protocol and ports: https://github.com/arnaudambro/i18n-keyless/blob/main/docs/PROTOCOL.md, conformance vectors https://github.com/arnaudambro/i18n-keyless/tree/main/conformance, port checklist https://github.com/arnaudambro/i18n-keyless/blob/main/docs/PORT_CHECKLIST.md
- MCP server (HTTP, OAuth): https://api.i18n-keyless.com/mcp — guide: https://docs.i18n-keyless.com/docs/guides/mcp
- Self-hosted (€30 once): the same Docker image as i18n-keyless.com, on your server, with ONCE (once.com) or docker compose, SQLite or Postgres, any AI provider (`AI_PROVIDER`, `AI_MODEL`, `AI_API_KEY`): https://i18n-keyless.com/self-hosted — guide: https://docs.i18n-keyless.com/docs/guides/self-hosting. Point every SDK at it with the `API_URL` init option.
### Every page of the documentation
Generated from the site — run `npm run docs:sync` in `docs/` after adding a page.
<!-- fact:docs-index -->
- Add context — https://docs.i18n-keyless.com/docs/guides/add-context
- Astro — https://docs.i18n-keyless.com/docs/ssr/astro
- Blog posts & long content — https://docs.i18n-keyless.com/docs/guides/long-form-content
- Change language — https://docs.i18n-keyless.com/docs/guides/react/change-language
- Debug — https://docs.i18n-keyless.com/docs/guides/debug
- Examples — https://docs.i18n-keyless.com/docs/examples
- Force temporary translation — https://docs.i18n-keyless.com/docs/guides/force-temporary
- I18nKeyless.init() — https://docs.i18n-keyless.com/docs/api/node/I18nKeyless.init
- I18nKeyless.init() — https://docs.i18n-keyless.com/docs/api/react/I18nKeyless.init
- I18nKeylessText — https://docs.i18n-keyless.com/docs/api/react/I18nKeylessText
- Namespaces — https://docs.i18n-keyless.com/docs/guides/namespaces
- Next.js — https://docs.i18n-keyless.com/docs/ssr/nextjs
- Operate it from your agent (MCP) — https://docs.i18n-keyless.com/docs/guides/mcp
- Proxy through your backend — https://docs.i18n-keyless.com/docs/guides/proxy-mode
- Quick setup — https://docs.i18n-keyless.com/docs/quick-setup
- Reliability & scale — https://docs.i18n-keyless.com/docs/guides/reliability-and-scale
- Remix / React Router 7 — https://docs.i18n-keyless.com/docs/ssr/remix
- Replace values — https://docs.i18n-keyless.com/docs/guides/replace-values
- Self-host it (€30, once) — https://docs.i18n-keyless.com/docs/guides/self-hosting
- Server-Side Rendering — https://docs.i18n-keyless.com/docs/ssr
- TanStack Start — https://docs.i18n-keyless.com/docs/ssr/tanstack-start
- Translate user content (UGC) — https://docs.i18n-keyless.com/docs/guides/user-generated-content
- Types — https://docs.i18n-keyless.com/docs/api/types
- Upgrading to v3 — https://docs.i18n-keyless.com/docs/guides/upgrade-v3
- Use a custom component — https://docs.i18n-keyless.com/docs/guides/react/my-text
- Utilities — https://docs.i18n-keyless.com/docs/api/react/utilities
- With markdown — https://docs.i18n-keyless.com/docs/guides/react/with-markdown
- awaitForTranslation() (deprecated) — https://docs.i18n-keyless.com/docs/api/node/awaitForTranslation
- awaitForTranslationOrFallbackToOriginal() — https://docs.i18n-keyless.com/docs/api/node/awaitForTranslationOrFallbackToOriginal
- awaitForTranslationOrThrow() — https://docs.i18n-keyless.com/docs/api/node/awaitForTranslationOrThrow
- getAllTranslationsForAllLanguages — https://docs.i18n-keyless.com/docs/api/node/getAllTranslationsForAllLanguages
- getSupportedLanguages() — https://docs.i18n-keyless.com/docs/api/node/getSupportedLanguages
- getSupportedLanguages() — https://docs.i18n-keyless.com/docs/api/react/getSupportedLanguages
- getTranslation() — https://docs.i18n-keyless.com/docs/api/react/getTranslation
- setCurrentLanguage() — https://docs.i18n-keyless.com/docs/api/react/setCurrentLanguage
- useCurrentLanguage() — https://docs.i18n-keyless.com/docs/api/react/useCurrentLanguage
- useI18nKeyless() — https://docs.i18n-keyless.com/docs/api/react/useI18nKeyless
- useTranslation() — https://docs.i18n-keyless.com/docs/api/react/useTranslation
<!-- /fact -->