@better-auth/i18n 1.7.0-beta.2 → 1.7.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -1
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/index-CYmtmQve.d.mts +196 -0
- package/dist/{index-Dfuc66o9.d.mts → index-DjO1TPAZ.d.mts} +3 -60
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -2
- package/dist/locales/index.d.mts +2 -0
- package/dist/locales/index.mjs +2 -0
- package/dist/locales-CSRpG5Mc.mjs +930 -0
- package/dist/{version-BVHIqaH7.mjs → version-BnvpTmd9.mjs} +1 -1
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Better Auth i18n Plugin
|
|
2
2
|
|
|
3
|
-
Internationalization plugin for [Better Auth](https://www.better-auth.com) — translate error messages
|
|
3
|
+
Internationalization plugin for [Better Auth](https://www.better-auth.com) — translate error messages based on the detected locale.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,6 +8,109 @@ Internationalization plugin for [Better Auth](https://www.better-auth.com) — t
|
|
|
8
8
|
npm install @better-auth/i18n
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Built-in translations
|
|
12
|
+
|
|
13
|
+
The package ships with ready-to-use translations for 22 languages:
|
|
14
|
+
|
|
15
|
+
| Code | Language |
|
|
16
|
+
|------|-----------|
|
|
17
|
+
| `ar` | Arabic |
|
|
18
|
+
| `bn` | Bengali |
|
|
19
|
+
| `de` | German |
|
|
20
|
+
| `en` | English |
|
|
21
|
+
| `es` | Spanish |
|
|
22
|
+
| `fa` | Persian (Farsi) |
|
|
23
|
+
| `fr` | French |
|
|
24
|
+
| `hi` | Hindi |
|
|
25
|
+
| `id` | Indonesian |
|
|
26
|
+
| `it` | Italian |
|
|
27
|
+
| `ja` | Japanese |
|
|
28
|
+
| `ko` | Korean |
|
|
29
|
+
| `nl` | Dutch |
|
|
30
|
+
| `pl` | Polish |
|
|
31
|
+
| `pt` | Portuguese |
|
|
32
|
+
| `ru` | Russian |
|
|
33
|
+
| `sv` | Swedish |
|
|
34
|
+
| `th` | Thai |
|
|
35
|
+
| `tr` | Turkish |
|
|
36
|
+
| `uk` | Ukrainian |
|
|
37
|
+
| `vi` | Vietnamese |
|
|
38
|
+
| `zh` | Chinese (Simplified) |
|
|
39
|
+
|
|
40
|
+
### Use all built-in locales
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { betterAuth } from "better-auth";
|
|
44
|
+
import { i18n, locales } from "@better-auth/i18n";
|
|
45
|
+
|
|
46
|
+
export const auth = betterAuth({
|
|
47
|
+
plugins: [
|
|
48
|
+
i18n({ translations: locales }),
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Use a subset of locales
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { i18n, locales } from "@better-auth/i18n";
|
|
57
|
+
|
|
58
|
+
export const auth = betterAuth({
|
|
59
|
+
plugins: [
|
|
60
|
+
i18n({
|
|
61
|
+
translations: {
|
|
62
|
+
en: locales.en,
|
|
63
|
+
fr: locales.fr,
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Override specific messages
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { i18n, locales } from "@better-auth/i18n";
|
|
74
|
+
|
|
75
|
+
export const auth = betterAuth({
|
|
76
|
+
plugins: [
|
|
77
|
+
i18n({
|
|
78
|
+
translations: {
|
|
79
|
+
...locales,
|
|
80
|
+
fr: {
|
|
81
|
+
...locales.fr,
|
|
82
|
+
USER_NOT_FOUND: "Membre introuvable",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Add a custom locale
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { i18n, locales } from "@better-auth/i18n";
|
|
94
|
+
import type { TranslationDictionary } from "@better-auth/i18n";
|
|
95
|
+
|
|
96
|
+
const myLocale: TranslationDictionary = {
|
|
97
|
+
USER_NOT_FOUND: "...",
|
|
98
|
+
INVALID_EMAIL_OR_PASSWORD: "...",
|
|
99
|
+
// ... other error codes
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const auth = betterAuth({
|
|
103
|
+
plugins: [
|
|
104
|
+
i18n({
|
|
105
|
+
translations: {
|
|
106
|
+
...locales,
|
|
107
|
+
xx: myLocale,
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
11
114
|
## Documentation
|
|
12
115
|
|
|
13
116
|
For full documentation, visit [better-auth.com/docs/plugins/i18n](https://www.better-auth.com/docs/plugins/i18n).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
package/dist/client.d.mts
CHANGED
package/dist/client.mjs
CHANGED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, GenericEndpointContext, UnionToIntersection } from "@better-auth/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type ALL_PLUGIN_ERROR_CODE_KEYS = keyof UnionToIntersection<{ [Key in Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]: BetterAuthPluginRegistry<unknown, unknown>[Key] extends {
|
|
5
|
+
creator: infer C;
|
|
6
|
+
} ? C extends ((...args: any[]) => infer P) ? P extends {
|
|
7
|
+
$ERROR_CODES: infer E;
|
|
8
|
+
} ? E : {} : {} : {} }[Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]>;
|
|
9
|
+
type InternalTranslationDictionary = Partial<{ [Key in ALL_PLUGIN_ERROR_CODE_KEYS]: string }>;
|
|
10
|
+
/**
|
|
11
|
+
* Translation dictionary mapping error codes to translated messages
|
|
12
|
+
*/
|
|
13
|
+
type TranslationDictionary = InternalTranslationDictionary & Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Locale detection strategy
|
|
16
|
+
*/
|
|
17
|
+
type LocaleDetectionStrategy = "header" | "cookie" | "session" | "callback";
|
|
18
|
+
/**
|
|
19
|
+
* Options for the i18n plugin
|
|
20
|
+
*/
|
|
21
|
+
interface I18nOptions<Locales extends string[]> {
|
|
22
|
+
/**
|
|
23
|
+
* Translation dictionaries keyed by locale code
|
|
24
|
+
* @example
|
|
25
|
+
* {
|
|
26
|
+
* en: { USER_NOT_FOUND: "User not found" },
|
|
27
|
+
* fr: { USER_NOT_FOUND: "Utilisateur non trouvé" }
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
translations: { [Locale in Locales[number]]: TranslationDictionary };
|
|
31
|
+
/**
|
|
32
|
+
* Default/fallback locale when detection fails
|
|
33
|
+
* @default "en"
|
|
34
|
+
*/
|
|
35
|
+
defaultLocale?: Locales[number] | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Locale detection strategies in priority order
|
|
38
|
+
* @default ["header"]
|
|
39
|
+
*/
|
|
40
|
+
detection?: LocaleDetectionStrategy[] | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Cookie name for locale detection (when "cookie" strategy is used)
|
|
43
|
+
* @default "locale"
|
|
44
|
+
*/
|
|
45
|
+
localeCookie?: string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* User field name for stored locale preference (when "session" strategy is used)
|
|
48
|
+
* @default "locale"
|
|
49
|
+
*/
|
|
50
|
+
userLocaleField?: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Custom locale detection function (when "callback" strategy is used).
|
|
53
|
+
* @example
|
|
54
|
+
* getLocale: (ctx) => {
|
|
55
|
+
* return ctx.headers?.get("X-Custom-Locale") ?? null;
|
|
56
|
+
* }
|
|
57
|
+
*/
|
|
58
|
+
getLocale?: undefined | ((ctx: GenericEndpointContext) => Promise<Locales[number] | null> | Locales[number] | null);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/locales/ar.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* Arabic translations
|
|
64
|
+
*/
|
|
65
|
+
declare const ar: TranslationDictionary;
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/locales/bn.d.ts
|
|
68
|
+
/**
|
|
69
|
+
* Bengali translations
|
|
70
|
+
*/
|
|
71
|
+
declare const bn: TranslationDictionary;
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/locales/de.d.ts
|
|
74
|
+
/**
|
|
75
|
+
* German translations
|
|
76
|
+
*/
|
|
77
|
+
declare const de: TranslationDictionary;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/locales/en.d.ts
|
|
80
|
+
/**
|
|
81
|
+
* English translations (default)
|
|
82
|
+
*/
|
|
83
|
+
declare const en: TranslationDictionary;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/locales/es.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* Spanish translations
|
|
88
|
+
*/
|
|
89
|
+
declare const es: TranslationDictionary;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/locales/fa.d.ts
|
|
92
|
+
/**
|
|
93
|
+
* Persian (Farsi) translations
|
|
94
|
+
*/
|
|
95
|
+
declare const fa: TranslationDictionary;
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/locales/fr.d.ts
|
|
98
|
+
/**
|
|
99
|
+
* French translations
|
|
100
|
+
*/
|
|
101
|
+
declare const fr: TranslationDictionary;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/locales/hi.d.ts
|
|
104
|
+
/**
|
|
105
|
+
* Hindi translations
|
|
106
|
+
*/
|
|
107
|
+
declare const hi: TranslationDictionary;
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/locales/id.d.ts
|
|
110
|
+
/**
|
|
111
|
+
* Indonesian translations
|
|
112
|
+
*/
|
|
113
|
+
declare const id: TranslationDictionary;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/locales/it.d.ts
|
|
116
|
+
/**
|
|
117
|
+
* Italian translations
|
|
118
|
+
*/
|
|
119
|
+
declare const it: TranslationDictionary;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/locales/ja.d.ts
|
|
122
|
+
/**
|
|
123
|
+
* Japanese translations
|
|
124
|
+
*/
|
|
125
|
+
declare const ja: TranslationDictionary;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/locales/ko.d.ts
|
|
128
|
+
/**
|
|
129
|
+
* Korean translations
|
|
130
|
+
*/
|
|
131
|
+
declare const ko: TranslationDictionary;
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/locales/nl.d.ts
|
|
134
|
+
/**
|
|
135
|
+
* Dutch translations
|
|
136
|
+
*/
|
|
137
|
+
declare const nl: TranslationDictionary;
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/locales/pl.d.ts
|
|
140
|
+
/**
|
|
141
|
+
* Polish translations
|
|
142
|
+
*/
|
|
143
|
+
declare const pl: TranslationDictionary;
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/locales/pt.d.ts
|
|
146
|
+
/**
|
|
147
|
+
* Portuguese translations
|
|
148
|
+
*/
|
|
149
|
+
declare const pt: TranslationDictionary;
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/locales/ru.d.ts
|
|
152
|
+
/**
|
|
153
|
+
* Russian translations
|
|
154
|
+
*/
|
|
155
|
+
declare const ru: TranslationDictionary;
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/locales/sv.d.ts
|
|
158
|
+
/**
|
|
159
|
+
* Swedish translations
|
|
160
|
+
*/
|
|
161
|
+
declare const sv: TranslationDictionary;
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/locales/th.d.ts
|
|
164
|
+
/**
|
|
165
|
+
* Thai translations
|
|
166
|
+
*/
|
|
167
|
+
declare const th: TranslationDictionary;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/locales/tr.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* Turkish translations
|
|
172
|
+
*/
|
|
173
|
+
declare const tr: TranslationDictionary;
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/locales/uk.d.ts
|
|
176
|
+
/**
|
|
177
|
+
* Ukrainian translations
|
|
178
|
+
*/
|
|
179
|
+
declare const uk: TranslationDictionary;
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/locales/vi.d.ts
|
|
182
|
+
/**
|
|
183
|
+
* Vietnamese translations
|
|
184
|
+
*/
|
|
185
|
+
declare const vi: TranslationDictionary;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/locales/zh.d.ts
|
|
188
|
+
/**
|
|
189
|
+
* Simplified Chinese translations
|
|
190
|
+
*/
|
|
191
|
+
declare const zh: TranslationDictionary;
|
|
192
|
+
declare namespace index_d_exports {
|
|
193
|
+
export { ar, bn, de, en, es, fa, fr, hi, id, it, ja, ko, nl, pl, pt, ru, sv, th, tr, uk, vi, zh };
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
export { ar as C, TranslationDictionary as E, bn as S, LocaleDetectionStrategy as T, fr as _, tr as a, en as b, ru as c, nl as d, ko as f, hi as g, id as h, uk as i, pt as l, it as m, zh as n, th as o, ja as p, vi as r, sv as s, index_d_exports as t, pl as u, fa as v, I18nOptions as w, de as x, es as y };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as TranslationDictionary, T as LocaleDetectionStrategy, w as I18nOptions } from "./index-CYmtmQve.mjs";
|
|
2
|
+
import { GenericEndpointContext } from "@better-auth/core";
|
|
2
3
|
|
|
3
4
|
//#region ../../node_modules/.pnpm/better-call@1.3.5_zod@4.3.6/node_modules/better-call/dist/standard-schema.d.mts
|
|
4
5
|
//#region src/standard-schema.d.ts
|
|
@@ -417,64 +418,6 @@ type InferHeadersInput<Option extends EndpointOptions | MiddlewareOptions> = Opt
|
|
|
417
418
|
headers?: HeadersInit;
|
|
418
419
|
};
|
|
419
420
|
//#endregion
|
|
420
|
-
//#region src/types.d.ts
|
|
421
|
-
type ALL_PLUGIN_ERROR_CODE_KEYS = keyof UnionToIntersection<{ [Key in Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]: BetterAuthPluginRegistry<unknown, unknown>[Key] extends {
|
|
422
|
-
creator: infer C;
|
|
423
|
-
} ? C extends ((...args: any[]) => infer P) ? P extends {
|
|
424
|
-
$ERROR_CODES: infer E;
|
|
425
|
-
} ? E : {} : {} : {} }[Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]>;
|
|
426
|
-
type InternalTranslationDictionary = Partial<{ [Key in ALL_PLUGIN_ERROR_CODE_KEYS]: string }>;
|
|
427
|
-
/**
|
|
428
|
-
* Translation dictionary mapping error codes to translated messages
|
|
429
|
-
*/
|
|
430
|
-
type TranslationDictionary = InternalTranslationDictionary & Record<string, string>;
|
|
431
|
-
/**
|
|
432
|
-
* Locale detection strategy
|
|
433
|
-
*/
|
|
434
|
-
type LocaleDetectionStrategy = "header" | "cookie" | "session" | "callback";
|
|
435
|
-
/**
|
|
436
|
-
* Options for the i18n plugin
|
|
437
|
-
*/
|
|
438
|
-
interface I18nOptions<Locales extends string[]> {
|
|
439
|
-
/**
|
|
440
|
-
* Translation dictionaries keyed by locale code
|
|
441
|
-
* @example
|
|
442
|
-
* {
|
|
443
|
-
* en: { USER_NOT_FOUND: "User not found" },
|
|
444
|
-
* fr: { USER_NOT_FOUND: "Utilisateur non trouvé" }
|
|
445
|
-
* }
|
|
446
|
-
*/
|
|
447
|
-
translations: { [Locale in Locales[number]]: TranslationDictionary };
|
|
448
|
-
/**
|
|
449
|
-
* Default/fallback locale when detection fails
|
|
450
|
-
* @default "en"
|
|
451
|
-
*/
|
|
452
|
-
defaultLocale?: Locales[number] | undefined;
|
|
453
|
-
/**
|
|
454
|
-
* Locale detection strategies in priority order
|
|
455
|
-
* @default ["header"]
|
|
456
|
-
*/
|
|
457
|
-
detection?: LocaleDetectionStrategy[] | undefined;
|
|
458
|
-
/**
|
|
459
|
-
* Cookie name for locale detection (when "cookie" strategy is used)
|
|
460
|
-
* @default "locale"
|
|
461
|
-
*/
|
|
462
|
-
localeCookie?: string | undefined;
|
|
463
|
-
/**
|
|
464
|
-
* User field name for stored locale preference (when "session" strategy is used)
|
|
465
|
-
* @default "locale"
|
|
466
|
-
*/
|
|
467
|
-
userLocaleField?: string | undefined;
|
|
468
|
-
/**
|
|
469
|
-
* Custom locale detection function (when "callback" strategy is used).
|
|
470
|
-
* @example
|
|
471
|
-
* getLocale: (ctx) => {
|
|
472
|
-
* return ctx.headers?.get("X-Custom-Locale") ?? null;
|
|
473
|
-
* }
|
|
474
|
-
*/
|
|
475
|
-
getLocale?: undefined | ((ctx: GenericEndpointContext) => Promise<Locales[number] | null> | Locales[number] | null);
|
|
476
|
-
}
|
|
477
|
-
//#endregion
|
|
478
421
|
//#region src/index.d.ts
|
|
479
422
|
declare module "@better-auth/core" {
|
|
480
423
|
interface BetterAuthPluginRegistry<AuthOptions, Options> {
|
|
@@ -525,4 +468,4 @@ declare const i18n: <Locales extends string[]>(options: I18nOptions<Locales>) =>
|
|
|
525
468
|
};
|
|
526
469
|
};
|
|
527
470
|
//#endregion
|
|
528
|
-
export {
|
|
471
|
+
export { i18n as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { t as i18n } from "./index-DjO1TPAZ.mjs";
|
|
2
|
+
import { E as TranslationDictionary, T as LocaleDetectionStrategy, t as index_d_exports, w as I18nOptions } from "./index-CYmtmQve.mjs";
|
|
3
|
+
export { I18nOptions, LocaleDetectionStrategy, TranslationDictionary, i18n, index_d_exports as locales };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-BnvpTmd9.mjs";
|
|
2
|
+
import { t as locales_exports } from "./locales-CSRpG5Mc.mjs";
|
|
2
3
|
import { APIError, createAuthMiddleware, isAPIError } from "better-auth/api";
|
|
3
4
|
import { parseCookies } from "better-auth/cookies";
|
|
4
5
|
//#region src/index.ts
|
|
@@ -109,4 +110,4 @@ const i18n = (options) => {
|
|
|
109
110
|
};
|
|
110
111
|
};
|
|
111
112
|
//#endregion
|
|
112
|
-
export { i18n };
|
|
113
|
+
export { i18n, locales_exports as locales };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as ar, S as bn, _ as fr, a as tr, b as en, c as ru, d as nl, f as ko, g as hi, h as id, i as uk, l as pt, m as it, n as zh, o as th, p as ja, r as vi, s as sv, u as pl, v as fa, x as de, y as es } from "../index-CYmtmQve.mjs";
|
|
2
|
+
export { ar, bn, de, en, es, fa, fr, hi, id, it, ja, ko, nl, pl, pt, ru, sv, th, tr, uk, vi, zh };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as ar, S as bn, _ as fr, a as tr, b as en, c as ru, d as nl, f as ko, g as hi, h as id, i as uk, l as pt, m as it, n as zh, o as th, p as ja, r as vi, s as sv, u as pl, v as fa, x as de, y as es } from "../locales-CSRpG5Mc.mjs";
|
|
2
|
+
export { ar, bn, de, en, es, fa, fr, hi, id, it, ja, ko, nl, pl, pt, ru, sv, th, tr, uk, vi, zh };
|