i18n-keyless-react 1.10.5 → 1.10.7
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/dist/core/api.d.ts +5 -0
- package/dist/core/api.js +23 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/core/my-pqueue.d.ts +24 -0
- package/dist/core/my-pqueue.js +108 -0
- package/dist/core/package.json +22 -0
- package/dist/core/service.d.ts +27 -0
- package/dist/core/service.js +163 -0
- package/dist/core/types.d.ts +94 -0
- package/dist/{__tests__ → react/__tests__}/I18nKeylessText.test.js +9 -2
- package/dist/{__tests__ → react/__tests__}/__mocks__/store.d.ts +1 -1
- package/dist/{__tests__ → react/__tests__}/__mocks__/store.js +10 -2
- package/dist/react/__tests__/store.test.d.ts +1 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +4 -0
- package/dist/{package.json → react/package.json} +3 -2
- package/dist/{store.d.ts → react/store.d.ts} +3 -2
- package/dist/{store.js → react/store.js} +27 -15
- package/dist/react/types.d.ts +154 -0
- package/dist/react/types.js +1 -0
- package/dist/{utils.d.ts → react/utils.d.ts} +9 -1
- package/dist/{utils.js → react/utils.js} +16 -0
- package/package.json +3 -2
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -4
- /package/dist/{__tests__/I18nKeylessText.test.d.ts → core/types.js} +0 -0
- /package/dist/{I18nKeylessText.d.ts → react/I18nKeylessText.d.ts} +0 -0
- /package/dist/{I18nKeylessText.js → react/I18nKeylessText.js} +0 -0
- /package/dist/{__tests__/store.test.d.ts → react/__tests__/I18nKeylessText.test.d.ts} +0 -0
- /package/dist/{__tests__ → react/__tests__}/__mocks__/zustand.d.ts +0 -0
- /package/dist/{__tests__ → react/__tests__}/__mocks__/zustand.js +0 -0
- /package/dist/{__tests__ → react/__tests__}/setup.d.ts +0 -0
- /package/dist/{__tests__ → react/__tests__}/setup.js +0 -0
- /package/dist/{__tests__ → react/__tests__}/store.test.js +0 -0
- /package/dist/{vitest.config.d.ts → react/vitest.config.d.ts} +0 -0
- /package/dist/{vitest.config.js → react/vitest.config.js} +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const api: {
|
|
2
|
+
fetchTranslation: (url: string, options: RequestInit) => Promise<any>;
|
|
3
|
+
fetchTranslationsForOneLanguage: (url: string, options: RequestInit) => Promise<any>;
|
|
4
|
+
fetchAllTranslationsForAllLanguages: (url: string, options: RequestInit) => Promise<any>;
|
|
5
|
+
};
|
package/dist/core/api.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const api = {
|
|
2
|
+
fetchTranslation: async (url, options) => {
|
|
3
|
+
return fetch(url, options)
|
|
4
|
+
.then((res) => res.json())
|
|
5
|
+
.catch((err) => {
|
|
6
|
+
return { ok: false, error: err.message };
|
|
7
|
+
});
|
|
8
|
+
},
|
|
9
|
+
fetchTranslationsForOneLanguage: async (url, options) => {
|
|
10
|
+
return fetch(url, options)
|
|
11
|
+
.then((res) => res.json())
|
|
12
|
+
.catch((err) => {
|
|
13
|
+
return { ok: false, error: err.message };
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
fetchAllTranslationsForAllLanguages: async (url, options) => {
|
|
17
|
+
return fetch(url, options)
|
|
18
|
+
.then((res) => res.json())
|
|
19
|
+
.catch((err) => {
|
|
20
|
+
return { ok: false, error: err.message };
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { Lang, PrimaryLang, Translations, HandleTranslateFunction, GetAllTranslationsFunction, GetAllTranslationsForAllLanguagesFunction, LanguagesConfig, LastRefresh, UniqueId, I18nKeylessRequestBody, I18nKeylessResponse, I18nKeylessAllTranslationsResponse, FetchTranslationParams, TranslationOptions, } from "./types";
|
|
2
|
+
export { getTranslationCore, getAllTranslationsFromLanguage, queue } from "./service";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getTranslationCore, getAllTranslationsFromLanguage, queue } from "./service";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type Task<T> = () => Promise<T>;
|
|
2
|
+
declare class EventEmitter {
|
|
3
|
+
private events;
|
|
4
|
+
on(event: string, callback: () => void): void;
|
|
5
|
+
off(event: string, callback: () => void): void;
|
|
6
|
+
emit(event: string): void;
|
|
7
|
+
}
|
|
8
|
+
export default class MyPQueue extends EventEmitter {
|
|
9
|
+
private queue;
|
|
10
|
+
private pending;
|
|
11
|
+
private readonly concurrency;
|
|
12
|
+
private processing;
|
|
13
|
+
constructor(options?: {
|
|
14
|
+
concurrency?: number;
|
|
15
|
+
});
|
|
16
|
+
add<T>(task: Task<T>, options?: {
|
|
17
|
+
priority?: number;
|
|
18
|
+
id?: string;
|
|
19
|
+
}): Promise<T>;
|
|
20
|
+
private processNext;
|
|
21
|
+
get size(): number;
|
|
22
|
+
get isPaused(): boolean;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// https://github.com/sindresorhus/p-queue/issues/145#issuecomment-882068004
|
|
2
|
+
// p-queu import is broken, so here is the smalle implementation of it
|
|
3
|
+
class EventEmitter {
|
|
4
|
+
constructor() {
|
|
5
|
+
Object.defineProperty(this, "events", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: {}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
on(event, callback) {
|
|
13
|
+
if (!this.events[event]) {
|
|
14
|
+
this.events[event] = [];
|
|
15
|
+
}
|
|
16
|
+
this.events[event].push(callback);
|
|
17
|
+
}
|
|
18
|
+
off(event, callback) {
|
|
19
|
+
if (!this.events[event])
|
|
20
|
+
return;
|
|
21
|
+
this.events[event] = this.events[event].filter((cb) => cb !== callback);
|
|
22
|
+
}
|
|
23
|
+
emit(event) {
|
|
24
|
+
if (!this.events[event])
|
|
25
|
+
return;
|
|
26
|
+
this.events[event].forEach((callback) => callback());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export default class MyPQueue extends EventEmitter {
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
super();
|
|
32
|
+
Object.defineProperty(this, "queue", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: []
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "pending", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "concurrency", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: void 0
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "processing", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: false
|
|
55
|
+
});
|
|
56
|
+
this.concurrency = options.concurrency ?? Infinity;
|
|
57
|
+
}
|
|
58
|
+
add(task, options = {}) {
|
|
59
|
+
const { priority = 0, id = String(Date.now()) } = options;
|
|
60
|
+
// If task with same ID exists, return its promise
|
|
61
|
+
const existingTask = this.queue.find((item) => item.id === id);
|
|
62
|
+
if (existingTask) {
|
|
63
|
+
return existingTask.task();
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const wrappedTask = async () => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await task();
|
|
69
|
+
resolve(result);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
reject(error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.pending--;
|
|
78
|
+
this.processNext();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
this.queue.push({ task: wrappedTask, priority, id });
|
|
82
|
+
this.queue.sort((a, b) => b.priority - a.priority);
|
|
83
|
+
this.processNext();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async processNext() {
|
|
87
|
+
if (this.processing)
|
|
88
|
+
return;
|
|
89
|
+
this.processing = true;
|
|
90
|
+
while (this.queue.length > 0 && this.pending < this.concurrency) {
|
|
91
|
+
const task = this.queue.shift();
|
|
92
|
+
if (task) {
|
|
93
|
+
this.pending++;
|
|
94
|
+
task.task().catch(() => { });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.processing = false;
|
|
98
|
+
if (this.queue.length === 0 && this.pending === 0) {
|
|
99
|
+
this.emit("empty");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
get size() {
|
|
103
|
+
return this.queue.length;
|
|
104
|
+
}
|
|
105
|
+
get isPaused() {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "i18n-keyless-core",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "1.10.7",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"prepublishOnly": "rm -rf ./dist && tsc --project tsconfig.json && npm pack",
|
|
14
|
+
"test": "echo 'no test for core'",
|
|
15
|
+
"postpublish": "rm -rf ./dist && rm *.tgz"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.5.5",
|
|
19
|
+
"@types/react": "^19.0.8",
|
|
20
|
+
"@types/react-dom": "^19.0.3"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Lang, TranslationOptions, I18nKeylessResponse, FetchTranslationParams } from "./types";
|
|
2
|
+
import MyPQueue from "./my-pqueue";
|
|
3
|
+
export declare const queue: MyPQueue;
|
|
4
|
+
/**
|
|
5
|
+
* Gets a translation for the specified key from the store
|
|
6
|
+
* @param key - The translation key (text in primary language)
|
|
7
|
+
* @param store - The translation store containing translations and config
|
|
8
|
+
* @param options - Optional parameters for translation retrieval
|
|
9
|
+
* @returns The translated text or the original key if not found
|
|
10
|
+
* @throws Error if config is not initialized
|
|
11
|
+
*/
|
|
12
|
+
export declare function getTranslationCore(key: string, store: FetchTranslationParams, options?: TranslationOptions): string;
|
|
13
|
+
/**
|
|
14
|
+
* Queues a key for translation if not already translated
|
|
15
|
+
* @param key - The text to translate
|
|
16
|
+
* @param store - The translation store
|
|
17
|
+
* @param options - Optional parameters for the translation process
|
|
18
|
+
* @throws Error if config is not initialized
|
|
19
|
+
*/
|
|
20
|
+
export declare function translateKey(key: string, store: FetchTranslationParams, options?: TranslationOptions): void;
|
|
21
|
+
/**
|
|
22
|
+
* Fetches all translations for a target language
|
|
23
|
+
* @param targetLanguage - The language code to fetch translations for
|
|
24
|
+
* @param store - The translation store
|
|
25
|
+
* @returns Promise resolving to the translation response or void if failed
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAllTranslationsFromLanguage(targetLanguage: Lang, store: FetchTranslationParams): Promise<I18nKeylessResponse | void>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import MyPQueue from "./my-pqueue";
|
|
2
|
+
import packageJson from "./package.json";
|
|
3
|
+
import { api } from "./api";
|
|
4
|
+
export const queue = new MyPQueue({ concurrency: 5 });
|
|
5
|
+
/**
|
|
6
|
+
* Gets a translation for the specified key from the store
|
|
7
|
+
* @param key - The translation key (text in primary language)
|
|
8
|
+
* @param store - The translation store containing translations and config
|
|
9
|
+
* @param options - Optional parameters for translation retrieval
|
|
10
|
+
* @returns The translated text or the original key if not found
|
|
11
|
+
* @throws Error if config is not initialized
|
|
12
|
+
*/
|
|
13
|
+
export function getTranslationCore(key, store, options) {
|
|
14
|
+
const currentLanguage = store.currentLanguage;
|
|
15
|
+
const config = store.config;
|
|
16
|
+
const translations = store.translations;
|
|
17
|
+
if (!config.API_KEY) {
|
|
18
|
+
throw new Error("i18n-keyless: config is not initialized");
|
|
19
|
+
}
|
|
20
|
+
if (currentLanguage === config.languages.primary) {
|
|
21
|
+
return key;
|
|
22
|
+
}
|
|
23
|
+
if (options?.forceTemporary?.[currentLanguage]) {
|
|
24
|
+
translateKey(key, store, options);
|
|
25
|
+
}
|
|
26
|
+
const context = options?.context;
|
|
27
|
+
const translation = context ? translations[`${key}__${context}`] : translations[key];
|
|
28
|
+
if (!translation) {
|
|
29
|
+
translateKey(key, store, options);
|
|
30
|
+
}
|
|
31
|
+
return translation || key;
|
|
32
|
+
}
|
|
33
|
+
const translating = {};
|
|
34
|
+
/**
|
|
35
|
+
* Queues a key for translation if not already translated
|
|
36
|
+
* @param key - The text to translate
|
|
37
|
+
* @param store - The translation store
|
|
38
|
+
* @param options - Optional parameters for the translation process
|
|
39
|
+
* @throws Error if config is not initialized
|
|
40
|
+
*/
|
|
41
|
+
export function translateKey(key, store, options) {
|
|
42
|
+
const currentLanguage = store.currentLanguage;
|
|
43
|
+
const config = store.config;
|
|
44
|
+
const translations = store.translations;
|
|
45
|
+
const uniqueId = store.uniqueId;
|
|
46
|
+
if (!config.API_KEY) {
|
|
47
|
+
throw new Error("i18n-keyless: config is not initialized");
|
|
48
|
+
}
|
|
49
|
+
const context = options?.context;
|
|
50
|
+
const debug = options?.debug;
|
|
51
|
+
// if (key.length > 280) {
|
|
52
|
+
// console.error("i18n-keyless: Key length exceeds 280 characters limit:", key);
|
|
53
|
+
// return;
|
|
54
|
+
// }
|
|
55
|
+
if (!key) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (debug) {
|
|
59
|
+
console.log("translateKey", key, context, debug);
|
|
60
|
+
}
|
|
61
|
+
const forceTemporaryLang = options?.forceTemporary?.[currentLanguage];
|
|
62
|
+
const translation = context ? translations[`${key}__${context}`] : translations[key];
|
|
63
|
+
if (translation && !forceTemporaryLang) {
|
|
64
|
+
if (debug) {
|
|
65
|
+
console.log("translation exists", `${key}__${context}`);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
queue.add(async () => {
|
|
70
|
+
try {
|
|
71
|
+
if (translating[key]) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
translating[key] = true;
|
|
76
|
+
}
|
|
77
|
+
if (config.handleTranslate) {
|
|
78
|
+
await config.handleTranslate?.(key);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const body = {
|
|
82
|
+
key,
|
|
83
|
+
context,
|
|
84
|
+
forceTemporary: options?.forceTemporary,
|
|
85
|
+
languages: config.languages.supported,
|
|
86
|
+
primaryLanguage: config.languages.primary,
|
|
87
|
+
};
|
|
88
|
+
const apiUrl = config.API_URL || "https://api.i18n-keyless.com";
|
|
89
|
+
const url = `${apiUrl}/translate`;
|
|
90
|
+
if (debug) {
|
|
91
|
+
console.log("fetching translation", url, body);
|
|
92
|
+
}
|
|
93
|
+
const response = await api
|
|
94
|
+
.fetchTranslation(url, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
Authorization: `Bearer ${config.API_KEY}`,
|
|
99
|
+
unique_id: uniqueId || "",
|
|
100
|
+
Version: packageJson.version,
|
|
101
|
+
},
|
|
102
|
+
body: JSON.stringify(body),
|
|
103
|
+
})
|
|
104
|
+
.then((res) => res);
|
|
105
|
+
if (debug) {
|
|
106
|
+
console.log("response", response);
|
|
107
|
+
}
|
|
108
|
+
if (response.message) {
|
|
109
|
+
console.warn("i18n-keyless: ", response.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
translating[key] = false;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error("i18n-keyless: Error translating key:", error);
|
|
117
|
+
translating[key] = false;
|
|
118
|
+
}
|
|
119
|
+
}, { priority: 1, id: key });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fetches all translations for a target language
|
|
123
|
+
* @param targetLanguage - The language code to fetch translations for
|
|
124
|
+
* @param store - The translation store
|
|
125
|
+
* @returns Promise resolving to the translation response or void if failed
|
|
126
|
+
*/
|
|
127
|
+
export async function getAllTranslationsFromLanguage(targetLanguage, store) {
|
|
128
|
+
const config = store.config;
|
|
129
|
+
const lastRefresh = store.lastRefresh;
|
|
130
|
+
const uniqueId = store.uniqueId;
|
|
131
|
+
if (!config.API_KEY) {
|
|
132
|
+
console.error("i18n-keyless: No config found");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// if (config.languages.primary === targetLanguage) {
|
|
136
|
+
// return;
|
|
137
|
+
// }
|
|
138
|
+
try {
|
|
139
|
+
const response = config.getAllTranslations
|
|
140
|
+
? await config.getAllTranslations()
|
|
141
|
+
: await api
|
|
142
|
+
.fetchTranslationsForOneLanguage(`${config.API_URL || "https://api.i18n-keyless.com"}/translate/${targetLanguage}?last_refresh=${lastRefresh}`, {
|
|
143
|
+
method: "GET",
|
|
144
|
+
headers: {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
Authorization: `Bearer ${config.API_KEY}`,
|
|
147
|
+
Version: packageJson.version,
|
|
148
|
+
unique_id: uniqueId || "",
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
.then((res) => res);
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new Error(response.error);
|
|
154
|
+
}
|
|
155
|
+
if (response.message) {
|
|
156
|
+
console.warn("i18n-keyless: ", response.message);
|
|
157
|
+
}
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
console.error("i18n-keyless: fetch all translations error:", error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export type PrimaryLang = "fr" | "en";
|
|
2
|
+
export type Lang = "fr" | "en" | "nl" | "it" | "de" | "es" | "pl" | "pt" | "ro" | "sv" | "tr" | "ja" | "cn" | "ru" | "ko" | "ar";
|
|
3
|
+
export type Translations = Record<string, string>;
|
|
4
|
+
export type HandleTranslateFunction = (key: string) => Promise<{
|
|
5
|
+
ok: boolean;
|
|
6
|
+
message: string;
|
|
7
|
+
}>;
|
|
8
|
+
export type GetAllTranslationsFunction = () => Promise<I18nKeylessResponse>;
|
|
9
|
+
export type GetAllTranslationsForAllLanguagesFunction = () => Promise<I18nKeylessAllTranslationsResponse>;
|
|
10
|
+
export type LastRefresh = string | null;
|
|
11
|
+
export type UniqueId = string | null;
|
|
12
|
+
export type LanguagesConfig = {
|
|
13
|
+
/**
|
|
14
|
+
* the language used by the developer
|
|
15
|
+
*/
|
|
16
|
+
primary: PrimaryLang;
|
|
17
|
+
/**
|
|
18
|
+
* the languages supported for the user.
|
|
19
|
+
* For now we support:
|
|
20
|
+
* fr, nl, it, de, es, pl, pt, ro, sv, tr, ja, cn, ru, ko, ar
|
|
21
|
+
*
|
|
22
|
+
* If you need more, please reach out to @ambroselli_io on X/Twitter or by mail at arnaud.ambroselli.io@gmail.com
|
|
23
|
+
*/
|
|
24
|
+
supported: Lang[];
|
|
25
|
+
/**
|
|
26
|
+
* if the user's langauge is not supported, the fallback language will be used
|
|
27
|
+
*/
|
|
28
|
+
fallback?: Lang;
|
|
29
|
+
/**
|
|
30
|
+
* the language to use when the app is initialized
|
|
31
|
+
*/
|
|
32
|
+
initWithDefault?: Lang;
|
|
33
|
+
};
|
|
34
|
+
export type TranslationOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* The context of the translation.
|
|
37
|
+
* Useful for ambiguous translations, like "8 heures" in French could be "8 AM" or "8 hours".
|
|
38
|
+
* You'll find it useful when it occurs to you, don't worry :)
|
|
39
|
+
*/
|
|
40
|
+
context?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Could be helpful if something weird happens with this particular key.
|
|
43
|
+
*/
|
|
44
|
+
debug?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* If the proposed translation from AI is not satisfactory,
|
|
47
|
+
* you can use this field to setup your own translation.
|
|
48
|
+
* You can leave it there forever, or remove it once your translation is saved.
|
|
49
|
+
*/
|
|
50
|
+
forceTemporary?: Partial<Record<Lang, string>>;
|
|
51
|
+
};
|
|
52
|
+
export interface I18nKeylessRequestBody {
|
|
53
|
+
key: string;
|
|
54
|
+
context?: string;
|
|
55
|
+
forceTemporary?: TranslationOptions["forceTemporary"];
|
|
56
|
+
languages: LanguagesConfig["supported"];
|
|
57
|
+
primaryLanguage: LanguagesConfig["primary"];
|
|
58
|
+
}
|
|
59
|
+
export interface I18nKeylessResponse {
|
|
60
|
+
ok: boolean;
|
|
61
|
+
data: {
|
|
62
|
+
translations: Translations;
|
|
63
|
+
uniqueId: UniqueId;
|
|
64
|
+
lastRefresh: LastRefresh;
|
|
65
|
+
};
|
|
66
|
+
error: string;
|
|
67
|
+
message: string;
|
|
68
|
+
}
|
|
69
|
+
export interface I18nKeylessAllTranslationsResponse {
|
|
70
|
+
ok: boolean;
|
|
71
|
+
data: {
|
|
72
|
+
translations: Record<Lang, Translations>;
|
|
73
|
+
uniqueId: UniqueId;
|
|
74
|
+
lastRefresh: LastRefresh;
|
|
75
|
+
};
|
|
76
|
+
error: string;
|
|
77
|
+
message: string;
|
|
78
|
+
}
|
|
79
|
+
export type FetchTranslationParams = {
|
|
80
|
+
uniqueId: UniqueId;
|
|
81
|
+
lastRefresh: LastRefresh;
|
|
82
|
+
currentLanguage: Lang;
|
|
83
|
+
config: {
|
|
84
|
+
API_KEY: string;
|
|
85
|
+
API_URL?: string;
|
|
86
|
+
languages: LanguagesConfig;
|
|
87
|
+
addMissingTranslations?: boolean;
|
|
88
|
+
debug?: boolean;
|
|
89
|
+
handleTranslate?: HandleTranslateFunction;
|
|
90
|
+
getAllTranslations?: GetAllTranslationsFunction;
|
|
91
|
+
getAllTranslationsForAllLanguages?: GetAllTranslationsForAllLanguagesFunction;
|
|
92
|
+
};
|
|
93
|
+
translations: Translations;
|
|
94
|
+
};
|
|
@@ -6,7 +6,13 @@ import { getTranslationCore } from "i18n-keyless-core";
|
|
|
6
6
|
// Create a mock store before vi.mock call using vi.hoisted
|
|
7
7
|
const mockStore = vi.hoisted(() => {
|
|
8
8
|
const store = {
|
|
9
|
-
config:
|
|
9
|
+
config: {
|
|
10
|
+
API_KEY: "any-fucking-key",
|
|
11
|
+
languages: {
|
|
12
|
+
primary: "en",
|
|
13
|
+
supported: ["en"],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
10
16
|
currentLanguage: "en",
|
|
11
17
|
translations: {},
|
|
12
18
|
uniqueId: null,
|
|
@@ -57,9 +63,10 @@ describe("I18nKeylessText", () => {
|
|
|
57
63
|
translations: {},
|
|
58
64
|
currentLanguage: "en",
|
|
59
65
|
config: {
|
|
66
|
+
API_KEY: "any-fucking-key",
|
|
60
67
|
languages: {
|
|
61
68
|
primary: "en",
|
|
62
|
-
supported: ["en"
|
|
69
|
+
supported: ["en"],
|
|
63
70
|
},
|
|
64
71
|
},
|
|
65
72
|
});
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { validateLanguage } from "i18n-keyless-core";
|
|
2
1
|
import { vi } from "vitest";
|
|
2
|
+
import { validateLanguage } from "../../utils";
|
|
3
3
|
export const store = {
|
|
4
|
-
config:
|
|
4
|
+
config: {
|
|
5
|
+
API_KEY: "any-fucking-keyk",
|
|
6
|
+
languages: {
|
|
7
|
+
primary: "fr",
|
|
8
|
+
supported: ["fr"],
|
|
9
|
+
},
|
|
10
|
+
},
|
|
5
11
|
currentLanguage: "fr",
|
|
6
12
|
translations: {},
|
|
7
13
|
uniqueId: null,
|
|
@@ -13,6 +19,7 @@ export const store = {
|
|
|
13
19
|
}),
|
|
14
20
|
};
|
|
15
21
|
// Create a function that supports the selector pattern
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
23
|
const useI18nKeylessMock = ((selectorOrStore) => {
|
|
17
24
|
// If it's a function (selector), call it with the store
|
|
18
25
|
if (typeof selectorOrStore === "function") {
|
|
@@ -20,6 +27,7 @@ const useI18nKeylessMock = ((selectorOrStore) => {
|
|
|
20
27
|
}
|
|
21
28
|
// Otherwise return the store
|
|
22
29
|
return store;
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
31
|
});
|
|
24
32
|
// Add the getState and setState methods to the mock function
|
|
25
33
|
useI18nKeylessMock.getState = vi.fn(() => store);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { I18nKeylessText } from "./I18nKeylessText";
|
|
2
|
+
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation } from "./store";
|
|
3
|
+
export { clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
4
|
+
export type { I18nKeylessTextProps } from "./I18nKeylessText";
|
|
5
|
+
export { type I18nConfig, type TranslationStoreState, type TranslationOptions, type TranslationStore } from "./types";
|
|
6
|
+
export { type Lang, type PrimaryLang, type Translations, type I18nKeylessRequestBody, type I18nKeylessResponse, getAllTranslationsFromLanguage, queue, } from "i18n-keyless-core";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { I18nKeylessText } from "./I18nKeylessText";
|
|
2
|
+
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation } from "./store";
|
|
3
|
+
export { clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
4
|
+
export { getAllTranslationsFromLanguage, queue, } from "i18n-keyless-core";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18n-keyless-react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.10.
|
|
4
|
+
"version": "1.10.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"prepublishOnly": "rm -rf ./dist && tsc --project tsconfig.json && npm run test && npm pack",
|
|
14
14
|
"test": "vitest run",
|
|
15
15
|
"test:watch": "vitest",
|
|
16
|
-
"test:coverage": "vitest run --coverage"
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"postpublish": "rm -rf ./dist && rm *.tgz"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"i18n-keyless-core": "latest",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Lang, type TranslationOptions } from "i18n-keyless-core";
|
|
2
|
+
import { type I18nConfig, type TranslationStore } from "./types";
|
|
2
3
|
export declare const useI18nKeyless: import("zustand").UseBoundStore<import("zustand").StoreApi<TranslationStore>>;
|
|
3
4
|
/**
|
|
4
5
|
* Initializes the i18n configuration with defaults and validation
|
|
@@ -6,7 +7,7 @@ export declare const useI18nKeyless: import("zustand").UseBoundStore<import("zus
|
|
|
6
7
|
* @returns The validated and completed configuration
|
|
7
8
|
* @throws Error if required configuration properties are missing
|
|
8
9
|
*/
|
|
9
|
-
export declare function init(newConfig:
|
|
10
|
+
export declare function init(newConfig: I18nConfig): Promise<void>;
|
|
10
11
|
export declare function useCurrentLanguage(): Lang | null;
|
|
11
12
|
export declare function getTranslation(key: string, options?: TranslationOptions): string;
|
|
12
13
|
export declare function setCurrentLanguage(lang: I18nConfig["languages"]["supported"][number]): void;
|
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
import { queue, getAllTranslationsFromLanguage,
|
|
1
|
+
import { queue, getAllTranslationsFromLanguage, getTranslationCore, } from "i18n-keyless-core";
|
|
2
2
|
import { create } from "zustand";
|
|
3
|
-
import { storeKeys, setItem, getItem, clearI18nKeylessStorage } from "./utils";
|
|
3
|
+
import { storeKeys, setItem, getItem, clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
4
4
|
queue.on("empty", () => {
|
|
5
5
|
// when each word is translated, fetch the translations for the current language
|
|
6
6
|
const store = useI18nKeyless.getState();
|
|
7
|
-
|
|
7
|
+
if (store.config) {
|
|
8
|
+
getAllTranslationsFromLanguage(store.currentLanguage, store).then(store.setTranslations);
|
|
9
|
+
}
|
|
8
10
|
});
|
|
9
11
|
export const useI18nKeyless = create((set, get) => ({
|
|
10
12
|
uniqueId: null,
|
|
11
13
|
lastRefresh: null,
|
|
12
14
|
translations: {},
|
|
13
15
|
currentLanguage: "fr",
|
|
14
|
-
config:
|
|
16
|
+
config: {
|
|
17
|
+
API_KEY: "",
|
|
18
|
+
languages: {
|
|
19
|
+
primary: "fr",
|
|
20
|
+
supported: ["fr"],
|
|
21
|
+
},
|
|
22
|
+
storage: undefined,
|
|
23
|
+
},
|
|
15
24
|
setTranslations: (response) => {
|
|
16
25
|
if (!response?.ok) {
|
|
17
26
|
return;
|
|
18
27
|
}
|
|
19
28
|
const config = get().config;
|
|
20
|
-
if (!config) {
|
|
29
|
+
if (!config.API_KEY) {
|
|
21
30
|
throw new Error(`i18n-keyless: config is not initialized setting translations`);
|
|
22
31
|
}
|
|
23
32
|
const newTranslations = response.data.translations;
|
|
@@ -38,12 +47,12 @@ export const useI18nKeyless = create((set, get) => ({
|
|
|
38
47
|
}
|
|
39
48
|
},
|
|
40
49
|
setLanguage: async (lang) => {
|
|
41
|
-
const
|
|
42
|
-
if (!config) {
|
|
50
|
+
const store = get();
|
|
51
|
+
if (!store.config) {
|
|
43
52
|
throw new Error(`i18n-keyless: config is not initialized setting translations`);
|
|
44
53
|
}
|
|
45
|
-
const debug = config.debug;
|
|
46
|
-
const validatedLang = validateLanguage(lang, config);
|
|
54
|
+
const debug = store.config.debug;
|
|
55
|
+
const validatedLang = validateLanguage(lang, store.config);
|
|
47
56
|
if (validatedLang !== lang) {
|
|
48
57
|
if (debug)
|
|
49
58
|
console.log("i18n-keyless: language", lang, "is not supported, fallback to", validatedLang);
|
|
@@ -53,18 +62,18 @@ export const useI18nKeyless = create((set, get) => ({
|
|
|
53
62
|
console.log("i18n-keyless: setLanguage", lang);
|
|
54
63
|
}
|
|
55
64
|
set({ currentLanguage: validatedLang });
|
|
56
|
-
if (config.storage) {
|
|
57
|
-
setItem(storeKeys.currentLanguage, validatedLang, config.storage);
|
|
65
|
+
if (store.config.storage) {
|
|
66
|
+
setItem(storeKeys.currentLanguage, validatedLang, store.config.storage);
|
|
58
67
|
}
|
|
59
68
|
// Only fetch translations if the new language is not the primary language
|
|
60
|
-
if (lang !== config.languages.primary) {
|
|
61
|
-
await getAllTranslationsFromLanguage(lang,
|
|
69
|
+
if (lang !== store.config.languages.primary) {
|
|
70
|
+
await getAllTranslationsFromLanguage(lang, store).then(store.setTranslations);
|
|
62
71
|
}
|
|
63
72
|
},
|
|
64
73
|
}));
|
|
65
74
|
async function hydrate() {
|
|
66
75
|
const config = useI18nKeyless.getState().config;
|
|
67
|
-
if (!config) {
|
|
76
|
+
if (!config.API_KEY) {
|
|
68
77
|
throw new Error(`i18n-keyless: config is not initialized hydrating`);
|
|
69
78
|
}
|
|
70
79
|
const storage = config.storage;
|
|
@@ -139,6 +148,9 @@ export async function init(newConfig) {
|
|
|
139
148
|
// default to true
|
|
140
149
|
newConfig.addMissingTranslations = true;
|
|
141
150
|
}
|
|
151
|
+
if (!newConfig.API_KEY) {
|
|
152
|
+
throw new Error(`i18n-keyless: API_KEY is required`);
|
|
153
|
+
}
|
|
142
154
|
useI18nKeyless.setState({ config: newConfig });
|
|
143
155
|
await hydrate();
|
|
144
156
|
const currentLanguage = useI18nKeyless.getState().currentLanguage;
|
|
@@ -159,7 +171,7 @@ export async function clearI18nKeylessStorageAndStore() {
|
|
|
159
171
|
useI18nKeyless.setState({
|
|
160
172
|
translations: {},
|
|
161
173
|
currentLanguage: "fr",
|
|
162
|
-
config:
|
|
174
|
+
config: undefined,
|
|
163
175
|
});
|
|
164
176
|
const config = useI18nKeyless.getState().config;
|
|
165
177
|
if (config?.storage) {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { I18nKeylessResponse, Lang, Translations, HandleTranslateFunction, GetAllTranslationsFunction, UniqueId, LastRefresh, LanguagesConfig } from "i18n-keyless-core";
|
|
2
|
+
type GetStorageFunction = (key: string) => string | null | undefined | Promise<string | null | undefined>;
|
|
3
|
+
type SetStorageFunction = (key: string, value: string) => void | Promise<void>;
|
|
4
|
+
type RemoveStorageFunction = (key: string) => void | Promise<void>;
|
|
5
|
+
type ClearStorageFunction = () => void | Promise<void>;
|
|
6
|
+
export interface I18nConfig {
|
|
7
|
+
/**
|
|
8
|
+
* The API key for the i18n-keyless API
|
|
9
|
+
*
|
|
10
|
+
* contact @ambroselli_io on X/Twitter or by mail at arnaud.ambroselli.io@gmail.com for getting one
|
|
11
|
+
*/
|
|
12
|
+
API_KEY: string;
|
|
13
|
+
/**
|
|
14
|
+
* Your own API URL for the i18n-keyless API
|
|
15
|
+
*
|
|
16
|
+
* You'll need to implement two routes on your server
|
|
17
|
+
* - GET /translate/:lang
|
|
18
|
+
* - POST /translate -- with a body of { key: string }
|
|
19
|
+
*/
|
|
20
|
+
API_URL?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The languages config
|
|
23
|
+
*
|
|
24
|
+
* primary: the language used by the developer
|
|
25
|
+
* supported: the languages supported for the user
|
|
26
|
+
* fallback: if the user's langauge is not supported, the fallback language will be used
|
|
27
|
+
* initWithDefault: the language to use when the app is initialized for the first time
|
|
28
|
+
*/
|
|
29
|
+
languages: LanguagesConfig;
|
|
30
|
+
/**
|
|
31
|
+
* if true, everytime a primary key is not found
|
|
32
|
+
* there will be a call to POST /translate -- with a body of { key: string }
|
|
33
|
+
* which should handle adding the key to the translations and, if needed,
|
|
34
|
+
* translate the key to all the languages supported by the user
|
|
35
|
+
*
|
|
36
|
+
* Two scenarios
|
|
37
|
+
* 1. Enable it in dev mode only: you'll may add some useless key, but you are 100% sure all the translations are up to date
|
|
38
|
+
* 2. Enable it in prod mode only: you take a risk taht the translations is not available when required for the first user demanding
|
|
39
|
+
* Best take: enable it all the time
|
|
40
|
+
*/
|
|
41
|
+
addMissingTranslations?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* called right after the store is initialized, maybe to hide screensplash. or init specific default langauge for dayjs, or whatever
|
|
44
|
+
*/
|
|
45
|
+
onInit?: (lang: Lang) => void;
|
|
46
|
+
/**
|
|
47
|
+
* if true, all the logs will be displayed in the console
|
|
48
|
+
*/
|
|
49
|
+
debug?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* called everytime the language is set, maybe to set also the locale to dayjs or whatever
|
|
52
|
+
*/
|
|
53
|
+
onSetLanguage?: (lang: Lang) => void;
|
|
54
|
+
/**
|
|
55
|
+
* if this function exists, it will be called instead of the API call
|
|
56
|
+
* if this function doesn't exist, the default behavior is to call the API
|
|
57
|
+
* therefore you would need either to
|
|
58
|
+
* - use this `handleTranslate` function to handle the translation with your own API
|
|
59
|
+
* - not use this `handleTranslate` function, and use the built in API call with API_KEY filled
|
|
60
|
+
* - not use this `handleTranslate` function nor API_KEY key, and provide your own API_URL
|
|
61
|
+
*/
|
|
62
|
+
handleTranslate?: HandleTranslateFunction;
|
|
63
|
+
/**
|
|
64
|
+
* if this function exists, it will be called instead of the API call
|
|
65
|
+
* if this function doesn't exist, the default behavior is to call the API, with the API_KEY
|
|
66
|
+
* therefore you need either to
|
|
67
|
+
* - use this `getAllTranslations` function to handle the translation with your own API
|
|
68
|
+
* - not use this `getAllTranslations` function, and use the built in API call with the API_KEY filled
|
|
69
|
+
* - not use this `getAllTranslations` function nor API_KEY key, and provide your own API_URL
|
|
70
|
+
*/
|
|
71
|
+
getAllTranslations?: GetAllTranslationsFunction;
|
|
72
|
+
/**
|
|
73
|
+
* the storage to use for the translations
|
|
74
|
+
*
|
|
75
|
+
* you can use react-native-mmkv, @react-native-async-storage/async-storage, or window.localStorage, or idb-keyval for IndexedDB, or any storage that has a getItem, setItem, removeItem, or get, set, and remove method
|
|
76
|
+
*/
|
|
77
|
+
storage?: {
|
|
78
|
+
getItem?: GetStorageFunction;
|
|
79
|
+
get?: GetStorageFunction;
|
|
80
|
+
getString?: GetStorageFunction;
|
|
81
|
+
setItem?: SetStorageFunction;
|
|
82
|
+
set?: SetStorageFunction;
|
|
83
|
+
removeItem?: RemoveStorageFunction;
|
|
84
|
+
remove?: RemoveStorageFunction;
|
|
85
|
+
delete?: RemoveStorageFunction;
|
|
86
|
+
del?: RemoveStorageFunction;
|
|
87
|
+
clear?: ClearStorageFunction;
|
|
88
|
+
clearAll?: ClearStorageFunction;
|
|
89
|
+
} & {
|
|
90
|
+
getString?: GetStorageFunction;
|
|
91
|
+
get?: GetStorageFunction;
|
|
92
|
+
getItem?: GetStorageFunction;
|
|
93
|
+
} & {
|
|
94
|
+
setItem?: SetStorageFunction;
|
|
95
|
+
set?: SetStorageFunction;
|
|
96
|
+
} & {
|
|
97
|
+
removeItem?: RemoveStorageFunction;
|
|
98
|
+
remove?: RemoveStorageFunction;
|
|
99
|
+
delete?: RemoveStorageFunction;
|
|
100
|
+
del?: RemoveStorageFunction;
|
|
101
|
+
} & ({
|
|
102
|
+
clear: ClearStorageFunction;
|
|
103
|
+
clearAll?: never;
|
|
104
|
+
} | {
|
|
105
|
+
clearAll: ClearStorageFunction;
|
|
106
|
+
clear?: never;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export interface TranslationStoreState {
|
|
110
|
+
/**
|
|
111
|
+
* the unique id of the consumer of i18n-keyless API, to help identify the usage API side
|
|
112
|
+
*/
|
|
113
|
+
uniqueId: UniqueId;
|
|
114
|
+
/**
|
|
115
|
+
* the last refresh of the translations, to only fetch the new ones if any
|
|
116
|
+
*/
|
|
117
|
+
lastRefresh: LastRefresh;
|
|
118
|
+
/**
|
|
119
|
+
* the translations fetched from i18n-keyless' API
|
|
120
|
+
*/
|
|
121
|
+
translations: Translations;
|
|
122
|
+
/**
|
|
123
|
+
* the current language of the user
|
|
124
|
+
*/
|
|
125
|
+
currentLanguage: Lang;
|
|
126
|
+
/**
|
|
127
|
+
* i18n-keyless' config
|
|
128
|
+
*/
|
|
129
|
+
config: I18nConfig;
|
|
130
|
+
}
|
|
131
|
+
export type TranslationOptions = {
|
|
132
|
+
/**
|
|
133
|
+
* The context of the translation.
|
|
134
|
+
* Useful for ambiguous translations, like "8 heures" in French could be "8 AM" or "8 hours".
|
|
135
|
+
* You'll find it useful when it occurs to you, don't worry :)
|
|
136
|
+
*/
|
|
137
|
+
context?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Could be helpful if something weird happens with this particular key.
|
|
140
|
+
*/
|
|
141
|
+
debug?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* If the proposed translation from AI is not satisfactory,
|
|
144
|
+
* you can use this field to setup your own translation.
|
|
145
|
+
* You can leave it there forever, or remove it once your translation is saved.
|
|
146
|
+
*/
|
|
147
|
+
forceTemporary?: Partial<Record<Lang, string>>;
|
|
148
|
+
};
|
|
149
|
+
interface TranslationStoreActions {
|
|
150
|
+
setTranslations: (translations: I18nKeylessResponse | void) => void;
|
|
151
|
+
setLanguage: (lang: Lang) => void;
|
|
152
|
+
}
|
|
153
|
+
export type TranslationStore = TranslationStoreState & TranslationStoreActions;
|
|
154
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { I18nConfig } from "
|
|
1
|
+
import type { I18nConfig } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* The keys used to store i18n-keyless data in storage
|
|
4
4
|
*/
|
|
@@ -36,3 +36,11 @@ export declare function deleteItem(key: string, storage: I18nConfig["storage"]):
|
|
|
36
36
|
* @param storage - The storage implementation to clear
|
|
37
37
|
*/
|
|
38
38
|
export declare function clearI18nKeylessStorage(storage: I18nConfig["storage"]): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Validates the language against the supported languages
|
|
41
|
+
* @param lang - The language to validate
|
|
42
|
+
* @param config - The configuration object
|
|
43
|
+
* @returns The validated language or the fallback language if not supported
|
|
44
|
+
* @throws Error if config is not initialized
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateLanguage(lang: I18nConfig["languages"]["supported"][number], config: I18nConfig): import("i18n-keyless-core").Lang | undefined;
|
|
@@ -99,3 +99,19 @@ export async function clearI18nKeylessStorage(storage) {
|
|
|
99
99
|
deleteItem(key, storage);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Validates the language against the supported languages
|
|
104
|
+
* @param lang - The language to validate
|
|
105
|
+
* @param config - The configuration object
|
|
106
|
+
* @returns The validated language or the fallback language if not supported
|
|
107
|
+
* @throws Error if config is not initialized
|
|
108
|
+
*/
|
|
109
|
+
export function validateLanguage(lang, config) {
|
|
110
|
+
if (!config.API_KEY) {
|
|
111
|
+
throw new Error(`i18n-keyless: config is not initialized validating language`);
|
|
112
|
+
}
|
|
113
|
+
if (!config.languages.supported.includes(lang)) {
|
|
114
|
+
return config.languages.fallback;
|
|
115
|
+
}
|
|
116
|
+
return lang;
|
|
117
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18n-keyless-react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.10.
|
|
4
|
+
"version": "1.10.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"prepublishOnly": "rm -rf ./dist && tsc --project tsconfig.json && npm run test && npm pack",
|
|
14
14
|
"test": "vitest run",
|
|
15
15
|
"test:watch": "vitest",
|
|
16
|
-
"test:coverage": "vitest run --coverage"
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"postpublish": "rm -rf ./dist && rm *.tgz"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"i18n-keyless-core": "latest",
|
package/dist/index.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { I18nKeylessText } from "./I18nKeylessText";
|
|
2
|
-
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation } from "./store";
|
|
3
|
-
export { clearI18nKeylessStorage } from "./utils";
|
|
4
|
-
export type { I18nKeylessTextProps } from "./I18nKeylessText";
|
|
5
|
-
export { type I18nConfig, type Lang, type PrimaryLang, type Translations, type TranslationStore, type TranslationStoreState, type I18nKeylessRequestBody, type I18nKeylessResponse, type TranslationOptions, getAllTranslationsFromLanguage, validateLanguage, queue, } from "i18n-keyless-core";
|
package/dist/index.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { I18nKeylessText } from "./I18nKeylessText";
|
|
2
|
-
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation } from "./store";
|
|
3
|
-
export { clearI18nKeylessStorage } from "./utils";
|
|
4
|
-
export { getAllTranslationsFromLanguage, validateLanguage, queue, } from "i18n-keyless-core";
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|