@adminforth/i18n 1.0.18-next.1 → 1.0.18-next.11
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/index.js +94 -77
- package/index.ts +129 -101
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import path from 'path';
|
|
|
13
13
|
import fs from 'fs-extra';
|
|
14
14
|
import chokidar from 'chokidar';
|
|
15
15
|
import { AsyncQueue } from '@sapphire/async-queue';
|
|
16
|
+
console.log = (...args) => {
|
|
17
|
+
process.stdout.write(args.join(" ") + "\n");
|
|
18
|
+
};
|
|
16
19
|
const processFrontendMessagesQueue = new AsyncQueue();
|
|
17
20
|
const SLAVIC_PLURAL_EXAMPLES = {
|
|
18
21
|
uk: 'яблук | Яблуко | Яблука | Яблук', // zero | singular | 2-4 | 5+
|
|
@@ -141,6 +144,7 @@ export default class I18N extends AdminForthPlugin {
|
|
|
141
144
|
if (!enColumn) {
|
|
142
145
|
throw new Error(`Field ${this.enFieldName} not found column to store english original string in resource ${resourceConfig.resourceId}`);
|
|
143
146
|
}
|
|
147
|
+
enColumn.editReadonly = true;
|
|
144
148
|
// if sourceFieldName defined, check it exists
|
|
145
149
|
if (this.options.sourceFieldName) {
|
|
146
150
|
if (!resourceConfig.columns.find(c => c.name === this.options.sourceFieldName)) {
|
|
@@ -282,15 +286,16 @@ export default class I18N extends AdminForthPlugin {
|
|
|
282
286
|
translatedCount = yield this.bulkTranslate({ selectedIds });
|
|
283
287
|
}
|
|
284
288
|
catch (e) {
|
|
289
|
+
process.env.HEAVY_DEBUG && console.error('🪲⛔ bulkTranslate error', e);
|
|
285
290
|
if (e instanceof AiTranslateError) {
|
|
286
|
-
process.env.HEAVY_DEBUG && console.error('🪲⛔ bulkTranslate error', e);
|
|
287
291
|
return { ok: false, error: e.message };
|
|
288
292
|
}
|
|
293
|
+
throw e;
|
|
289
294
|
}
|
|
290
|
-
process.env.HEAVY_DEBUG && console.log('🪲bulkTranslate done', selectedIds);
|
|
291
295
|
this.updateUntranslatedMenuBadge();
|
|
292
296
|
return {
|
|
293
|
-
ok: true,
|
|
297
|
+
ok: true,
|
|
298
|
+
error: undefined,
|
|
294
299
|
successMessage: yield tr(`Translated {count} items`, 'backend', {
|
|
295
300
|
count: translatedCount,
|
|
296
301
|
}),
|
|
@@ -323,6 +328,82 @@ export default class I18N extends AdminForthPlugin {
|
|
|
323
328
|
});
|
|
324
329
|
});
|
|
325
330
|
}
|
|
331
|
+
translateToLang(langIsoCode_1, strings_1) {
|
|
332
|
+
return __awaiter(this, arguments, void 0, function* (langIsoCode, strings, plurals = false, translations, updateStrings = {}) {
|
|
333
|
+
const maxKeysInOneReq = 10;
|
|
334
|
+
if (strings.length === 0) {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
if (strings.length > maxKeysInOneReq) {
|
|
338
|
+
let totalTranslated = [];
|
|
339
|
+
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
340
|
+
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
341
|
+
process.env.HEAVY_DEBUG && console.log('🪲🔪slicedStrings len', slicedStrings.length);
|
|
342
|
+
const madeKeys = yield this.translateToLang(langIsoCode, slicedStrings, plurals, translations, updateStrings);
|
|
343
|
+
totalTranslated = totalTranslated.concat(madeKeys);
|
|
344
|
+
}
|
|
345
|
+
return totalTranslated;
|
|
346
|
+
}
|
|
347
|
+
const lang = langIsoCode;
|
|
348
|
+
const langName = iso6391.getName(lang);
|
|
349
|
+
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
350
|
+
const prompt = `
|
|
351
|
+
I need to translate strings in JSON to ${lang} (${langName}) language from English for my web app.
|
|
352
|
+
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
353
|
+
Keep keys, as is, write translation into values! Here are the strings:
|
|
354
|
+
|
|
355
|
+
\`\`\`json
|
|
356
|
+
${JSON.stringify(strings.reduce((acc, s) => {
|
|
357
|
+
acc[s.en_string] = '';
|
|
358
|
+
return acc;
|
|
359
|
+
}, {}), null, 2)}
|
|
360
|
+
\`\`\`
|
|
361
|
+
`;
|
|
362
|
+
// call OpenAI
|
|
363
|
+
const resp = yield this.options.completeAdapter.complete(prompt, [], 300);
|
|
364
|
+
if (resp.error) {
|
|
365
|
+
throw new AiTranslateError(resp.error);
|
|
366
|
+
}
|
|
367
|
+
// parse response like
|
|
368
|
+
// Here are the translations for the strings you provided:
|
|
369
|
+
// ```json
|
|
370
|
+
// [{"live": "canlı"}, {"Table Games": "Masa Oyunları"}]
|
|
371
|
+
// ```
|
|
372
|
+
let res;
|
|
373
|
+
try {
|
|
374
|
+
res = resp.content.split("```json")[1].split("```")[0];
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
console.error(`Error in parsing LLM resp: ${resp}\n Prompt was: ${prompt}\n Resp was: ${JSON.stringify(resp)}`);
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
res = JSON.parse(res);
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
console.error(`Error in parsing LLM resp json: ${resp}\n Prompt was: ${prompt}\n Resp was: ${JSON.stringify(resp)}`);
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
for (const [enStr, translatedStr] of Object.entries(res)) {
|
|
388
|
+
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
389
|
+
// might be several with same en_string
|
|
390
|
+
for (const translation of translationsTargeted) {
|
|
391
|
+
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
392
|
+
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
393
|
+
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
394
|
+
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
395
|
+
updates: {},
|
|
396
|
+
translatedStr,
|
|
397
|
+
category: translation[this.options.categoryFieldName],
|
|
398
|
+
strId: translation[this.primaryKeyFieldName],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
updateStrings[translation[this.primaryKeyFieldName]].updates[this.trFieldNames[lang]] = translatedStr;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return Object.keys(updateStrings);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
326
407
|
// returns translated count
|
|
327
408
|
bulkTranslate(_a) {
|
|
328
409
|
return __awaiter(this, arguments, void 0, function* ({ selectedIds }) {
|
|
@@ -345,83 +426,16 @@ export default class I18N extends AdminForthPlugin {
|
|
|
345
426
|
}
|
|
346
427
|
}
|
|
347
428
|
}
|
|
348
|
-
const maxKeysInOneReq = 10;
|
|
349
429
|
const updateStrings = {};
|
|
350
|
-
const translateToLang = (langIsoCode_1, strings_1, ...args_1) => __awaiter(this, [langIsoCode_1, strings_1, ...args_1], void 0, function* (langIsoCode, strings, plurals = false) {
|
|
351
|
-
if (strings.length === 0) {
|
|
352
|
-
return 0;
|
|
353
|
-
}
|
|
354
|
-
if (strings.length > maxKeysInOneReq) {
|
|
355
|
-
let totalTranslated = 0;
|
|
356
|
-
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
357
|
-
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
358
|
-
process.env.HEAVY_DEBUG && console.log('🪲🔪slicedStrings len', slicedStrings.length);
|
|
359
|
-
totalTranslated += yield translateToLang(langIsoCode, slicedStrings, plurals);
|
|
360
|
-
}
|
|
361
|
-
return totalTranslated;
|
|
362
|
-
}
|
|
363
|
-
const lang = langIsoCode;
|
|
364
|
-
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
365
|
-
const prompt = `
|
|
366
|
-
I need to translate strings in JSON to ${lang} language from English for my web app.
|
|
367
|
-
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
368
|
-
Keep keys, as is, write translation into values! Here are the strings:
|
|
369
|
-
|
|
370
|
-
\`\`\`json
|
|
371
|
-
${JSON.stringify(strings.reduce((acc, s) => {
|
|
372
|
-
acc[s.en_string] = '';
|
|
373
|
-
return acc;
|
|
374
|
-
}, {}), null, 2)}
|
|
375
|
-
\`\`\`
|
|
376
|
-
`;
|
|
377
|
-
process.env.HEAVY_DEBUG && console.log('🧠 llm prompt', prompt);
|
|
378
|
-
// call OpenAI
|
|
379
|
-
const resp = yield this.options.completeAdapter.complete(prompt, [], 300);
|
|
380
|
-
process.env.HEAVY_DEBUG && console.log('🧠 llm resp', resp);
|
|
381
|
-
if (resp.error) {
|
|
382
|
-
throw new AiTranslateError(resp.error);
|
|
383
|
-
}
|
|
384
|
-
// parse response like
|
|
385
|
-
// Here are the translations for the strings you provided:
|
|
386
|
-
// ```json
|
|
387
|
-
// [{"live": "canlı"}, {"Table Games": "Masa Oyunları"}]
|
|
388
|
-
// ```
|
|
389
|
-
let res;
|
|
390
|
-
try {
|
|
391
|
-
res = resp.content.split("```json")[1].split("```")[0];
|
|
392
|
-
}
|
|
393
|
-
catch (e) {
|
|
394
|
-
console.error('error in parsing OpenAI', resp);
|
|
395
|
-
throw new AiTranslateError('Error in parsing OpenAI response');
|
|
396
|
-
}
|
|
397
|
-
res = JSON.parse(res);
|
|
398
|
-
for (const [enStr, translatedStr] of Object.entries(res)) {
|
|
399
|
-
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
400
|
-
// might be several with same en_string
|
|
401
|
-
for (const translation of translationsTargeted) {
|
|
402
|
-
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
403
|
-
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
404
|
-
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
405
|
-
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
406
|
-
updates: {},
|
|
407
|
-
translatedStr,
|
|
408
|
-
category: translation[this.options.categoryFieldName],
|
|
409
|
-
strId: translation[this.primaryKeyFieldName],
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
updateStrings[translation[this.primaryKeyFieldName]].updates[this.trFieldNames[lang]] = translatedStr;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
return Object.keys(updateStrings).length;
|
|
416
|
-
});
|
|
417
430
|
const langsInvolved = new Set(Object.keys(needToTranslateByLang));
|
|
418
|
-
let totalTranslated =
|
|
431
|
+
let totalTranslated = [];
|
|
419
432
|
yield Promise.all(Object.entries(needToTranslateByLang).map((_b) => __awaiter(this, [_b], void 0, function* ([lang, strings]) {
|
|
420
433
|
// first translate without plurals
|
|
421
434
|
const stringsWithoutPlurals = strings.filter(s => !s.en_string.includes('|'));
|
|
422
|
-
|
|
435
|
+
const noPluralKeys = yield this.translateToLang(lang, stringsWithoutPlurals, false, translations, updateStrings);
|
|
423
436
|
const stringsWithPlurals = strings.filter(s => s.en_string.includes('|'));
|
|
424
|
-
|
|
437
|
+
const pluralKeys = yield this.translateToLang(lang, stringsWithPlurals, true, translations, updateStrings);
|
|
438
|
+
totalTranslated = totalTranslated.concat(noPluralKeys, pluralKeys);
|
|
425
439
|
})));
|
|
426
440
|
yield Promise.all(Object.entries(updateStrings).map((_c) => __awaiter(this, [_c], void 0, function* ([_, { updates, strId }]) {
|
|
427
441
|
// because this will translate all languages, we can set completedLangs to all languages
|
|
@@ -429,12 +443,12 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
429
443
|
yield this.adminforth.resource(this.resourceConfig.resourceId).update(strId, Object.assign(Object.assign({}, updates), { [this.options.completedFieldName]: futureCompletedFieldValue }));
|
|
430
444
|
})));
|
|
431
445
|
for (const lang of langsInvolved) {
|
|
432
|
-
this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
446
|
+
yield this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
433
447
|
for (const [enStr, { category }] of Object.entries(updateStrings)) {
|
|
434
|
-
this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
|
|
448
|
+
yield this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
|
|
435
449
|
}
|
|
436
450
|
}
|
|
437
|
-
return totalTranslated;
|
|
451
|
+
return new Set(totalTranslated).size;
|
|
438
452
|
});
|
|
439
453
|
}
|
|
440
454
|
processExtractedMessages(adminforth, filePath) {
|
|
@@ -506,6 +520,9 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
506
520
|
validateConfigAfterDiscover(adminforth, resourceConfig) {
|
|
507
521
|
// optional method where you can safely check field types after database discovery was performed
|
|
508
522
|
// ensure each trFieldName (apart from enFieldName) is nullable column of type string
|
|
523
|
+
if (this.options.completeAdapter) {
|
|
524
|
+
this.options.completeAdapter.validate();
|
|
525
|
+
}
|
|
509
526
|
for (const lang of this.options.supportedLanguages) {
|
|
510
527
|
if (lang === 'en') {
|
|
511
528
|
continue;
|
package/index.ts
CHANGED
|
@@ -7,6 +7,11 @@ import fs from 'fs-extra';
|
|
|
7
7
|
import chokidar from 'chokidar';
|
|
8
8
|
import { AsyncQueue } from '@sapphire/async-queue';
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
console.log = (...args) => {
|
|
12
|
+
process.stdout.write(args.join(" ") + "\n");
|
|
13
|
+
};
|
|
14
|
+
|
|
10
15
|
const processFrontendMessagesQueue = new AsyncQueue();
|
|
11
16
|
|
|
12
17
|
const SLAVIC_PLURAL_EXAMPLES = {
|
|
@@ -156,6 +161,8 @@ export default class I18N extends AdminForthPlugin {
|
|
|
156
161
|
throw new Error(`Field ${this.enFieldName} not found column to store english original string in resource ${resourceConfig.resourceId}`);
|
|
157
162
|
}
|
|
158
163
|
|
|
164
|
+
enColumn.editReadonly = true;
|
|
165
|
+
|
|
159
166
|
// if sourceFieldName defined, check it exists
|
|
160
167
|
if (this.options.sourceFieldName) {
|
|
161
168
|
if (!resourceConfig.columns.find(c => c.name === this.options.sourceFieldName)) {
|
|
@@ -322,15 +329,16 @@ export default class I18N extends AdminForthPlugin {
|
|
|
322
329
|
try {
|
|
323
330
|
translatedCount = await this.bulkTranslate({ selectedIds });
|
|
324
331
|
} catch (e) {
|
|
332
|
+
process.env.HEAVY_DEBUG && console.error('🪲⛔ bulkTranslate error', e);
|
|
325
333
|
if (e instanceof AiTranslateError) {
|
|
326
|
-
process.env.HEAVY_DEBUG && console.error('🪲⛔ bulkTranslate error', e);
|
|
327
334
|
return { ok: false, error: e.message };
|
|
328
|
-
}
|
|
335
|
+
}
|
|
336
|
+
throw e;
|
|
329
337
|
}
|
|
330
|
-
process.env.HEAVY_DEBUG && console.log('🪲bulkTranslate done', selectedIds);
|
|
331
338
|
this.updateUntranslatedMenuBadge();
|
|
332
339
|
return {
|
|
333
|
-
ok: true,
|
|
340
|
+
ok: true,
|
|
341
|
+
error: undefined,
|
|
334
342
|
successMessage: await tr(`Translated {count} items`, 'backend', {
|
|
335
343
|
count: translatedCount,
|
|
336
344
|
}),
|
|
@@ -364,6 +372,103 @@ export default class I18N extends AdminForthPlugin {
|
|
|
364
372
|
});
|
|
365
373
|
}
|
|
366
374
|
|
|
375
|
+
async translateToLang (
|
|
376
|
+
langIsoCode: LanguageCode,
|
|
377
|
+
strings: { en_string: string, category: string }[],
|
|
378
|
+
plurals=false,
|
|
379
|
+
translations: any[],
|
|
380
|
+
updateStrings: Record<string, { updates: any, category: string, strId: string, translatedStr: string }> = {}
|
|
381
|
+
): Promise<string[]> {
|
|
382
|
+
const maxKeysInOneReq = 10;
|
|
383
|
+
if (strings.length === 0) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (strings.length > maxKeysInOneReq) {
|
|
388
|
+
let totalTranslated = [];
|
|
389
|
+
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
390
|
+
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
391
|
+
process.env.HEAVY_DEBUG && console.log('🪲🔪slicedStrings len', slicedStrings.length);
|
|
392
|
+
const madeKeys = await this.translateToLang(langIsoCode, slicedStrings, plurals, translations, updateStrings);
|
|
393
|
+
totalTranslated = totalTranslated.concat(madeKeys);
|
|
394
|
+
}
|
|
395
|
+
return totalTranslated;
|
|
396
|
+
}
|
|
397
|
+
const lang = langIsoCode;
|
|
398
|
+
const langName = iso6391.getName(lang);
|
|
399
|
+
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
400
|
+
|
|
401
|
+
const prompt = `
|
|
402
|
+
I need to translate strings in JSON to ${lang} (${langName}) language from English for my web app.
|
|
403
|
+
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
404
|
+
Keep keys, as is, write translation into values! Here are the strings:
|
|
405
|
+
|
|
406
|
+
\`\`\`json
|
|
407
|
+
${
|
|
408
|
+
JSON.stringify(strings.reduce((acc: object, s: { en_string: string }): object => {
|
|
409
|
+
acc[s.en_string] = '';
|
|
410
|
+
return acc;
|
|
411
|
+
}, {}), null, 2)
|
|
412
|
+
}
|
|
413
|
+
\`\`\`
|
|
414
|
+
`;
|
|
415
|
+
|
|
416
|
+
// call OpenAI
|
|
417
|
+
const resp = await this.options.completeAdapter.complete(
|
|
418
|
+
prompt,
|
|
419
|
+
[],
|
|
420
|
+
300,
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
if (resp.error) {
|
|
424
|
+
throw new AiTranslateError(resp.error);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// parse response like
|
|
428
|
+
// Here are the translations for the strings you provided:
|
|
429
|
+
// ```json
|
|
430
|
+
// [{"live": "canlı"}, {"Table Games": "Masa Oyunları"}]
|
|
431
|
+
// ```
|
|
432
|
+
let res;
|
|
433
|
+
try {
|
|
434
|
+
res = resp.content.split("```json")[1].split("```")[0];
|
|
435
|
+
} catch (e) {
|
|
436
|
+
console.error(`Error in parsing LLM resp: ${resp}\n Prompt was: ${prompt}\n Resp was: ${JSON.stringify(resp)}`, );
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
res = JSON.parse(res);
|
|
442
|
+
} catch (e) {
|
|
443
|
+
console.error(`Error in parsing LLM resp json: ${resp}\n Prompt was: ${prompt}\n Resp was: ${JSON.stringify(resp)}`, );
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
for (const [enStr, translatedStr] of Object.entries(res) as [string, string][]) {
|
|
449
|
+
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
450
|
+
// might be several with same en_string
|
|
451
|
+
for (const translation of translationsTargeted) {
|
|
452
|
+
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
453
|
+
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
454
|
+
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
455
|
+
|
|
456
|
+
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
457
|
+
updates: {},
|
|
458
|
+
translatedStr,
|
|
459
|
+
category: translation[this.options.categoryFieldName],
|
|
460
|
+
strId: translation[this.primaryKeyFieldName],
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
updateStrings[
|
|
464
|
+
translation[this.primaryKeyFieldName]
|
|
465
|
+
].updates[this.trFieldNames[lang]] = translatedStr;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return Object.keys(updateStrings);
|
|
470
|
+
}
|
|
471
|
+
|
|
367
472
|
// returns translated count
|
|
368
473
|
async bulkTranslate({ selectedIds }: { selectedIds: string[] }): Promise<number> {
|
|
369
474
|
|
|
@@ -397,8 +502,6 @@ export default class I18N extends AdminForthPlugin {
|
|
|
397
502
|
}
|
|
398
503
|
}
|
|
399
504
|
|
|
400
|
-
const maxKeysInOneReq = 10;
|
|
401
|
-
|
|
402
505
|
const updateStrings: Record<string, {
|
|
403
506
|
updates: any,
|
|
404
507
|
category: string,
|
|
@@ -406,104 +509,27 @@ export default class I18N extends AdminForthPlugin {
|
|
|
406
509
|
translatedStr: string
|
|
407
510
|
}> = {};
|
|
408
511
|
|
|
409
|
-
const translateToLang = async (langIsoCode: LanguageCode, strings: { en_string: string, category: string }[], plurals=false): Promise<number> => {
|
|
410
|
-
if (strings.length === 0) {
|
|
411
|
-
return 0;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (strings.length > maxKeysInOneReq) {
|
|
415
|
-
let totalTranslated = 0;
|
|
416
|
-
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
417
|
-
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
418
|
-
process.env.HEAVY_DEBUG && console.log('🪲🔪slicedStrings len', slicedStrings.length);
|
|
419
|
-
totalTranslated += await translateToLang(langIsoCode, slicedStrings, plurals);
|
|
420
|
-
}
|
|
421
|
-
return totalTranslated;
|
|
422
|
-
}
|
|
423
|
-
const lang = langIsoCode;
|
|
424
|
-
|
|
425
|
-
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
426
512
|
|
|
427
|
-
|
|
428
|
-
I need to translate strings in JSON to ${lang} language from English for my web app.
|
|
429
|
-
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
430
|
-
Keep keys, as is, write translation into values! Here are the strings:
|
|
513
|
+
const langsInvolved = new Set(Object.keys(needToTranslateByLang));
|
|
431
514
|
|
|
432
|
-
|
|
433
|
-
${
|
|
434
|
-
JSON.stringify(strings.reduce((acc: object, s: { en_string: string }): object => {
|
|
435
|
-
acc[s.en_string] = '';
|
|
436
|
-
return acc;
|
|
437
|
-
}, {}), null, 2)
|
|
438
|
-
}
|
|
439
|
-
\`\`\`
|
|
440
|
-
`;
|
|
515
|
+
let totalTranslated = [];
|
|
441
516
|
|
|
442
|
-
|
|
517
|
+
await Promise.all(
|
|
518
|
+
Object.entries(needToTranslateByLang).map(
|
|
519
|
+
async ([lang, strings]: [LanguageCode, { en_string: string, category: string }[]]) => {
|
|
520
|
+
// first translate without plurals
|
|
521
|
+
const stringsWithoutPlurals = strings.filter(s => !s.en_string.includes('|'));
|
|
522
|
+
const noPluralKeys = await this.translateToLang(lang, stringsWithoutPlurals, false, translations, updateStrings);
|
|
443
523
|
|
|
444
|
-
// call OpenAI
|
|
445
|
-
const resp = await this.options.completeAdapter.complete(
|
|
446
|
-
prompt,
|
|
447
|
-
[],
|
|
448
|
-
300,
|
|
449
|
-
);
|
|
450
524
|
|
|
451
|
-
|
|
525
|
+
const stringsWithPlurals = strings.filter(s => s.en_string.includes('|'));
|
|
452
526
|
|
|
453
|
-
|
|
454
|
-
throw new AiTranslateError(resp.error);
|
|
455
|
-
}
|
|
527
|
+
const pluralKeys = await this.translateToLang(lang, stringsWithPlurals, true, translations, updateStrings);
|
|
456
528
|
|
|
457
|
-
|
|
458
|
-
// Here are the translations for the strings you provided:
|
|
459
|
-
// ```json
|
|
460
|
-
// [{"live": "canlı"}, {"Table Games": "Masa Oyunları"}]
|
|
461
|
-
// ```
|
|
462
|
-
let res;
|
|
463
|
-
try {
|
|
464
|
-
res = resp.content.split("```json")[1].split("```")[0];
|
|
465
|
-
} catch (e) {
|
|
466
|
-
console.error('error in parsing OpenAI', resp);
|
|
467
|
-
throw new AiTranslateError('Error in parsing OpenAI response');
|
|
468
|
-
}
|
|
469
|
-
res = JSON.parse(res);
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
for (const [enStr, translatedStr] of Object.entries(res) as [string, string][]) {
|
|
473
|
-
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
474
|
-
// might be several with same en_string
|
|
475
|
-
for (const translation of translationsTargeted) {
|
|
476
|
-
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
477
|
-
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
478
|
-
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
479
|
-
|
|
480
|
-
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
481
|
-
updates: {},
|
|
482
|
-
translatedStr,
|
|
483
|
-
category: translation[this.options.categoryFieldName],
|
|
484
|
-
strId: translation[this.primaryKeyFieldName],
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
updateStrings[
|
|
488
|
-
translation[this.primaryKeyFieldName]
|
|
489
|
-
].updates[this.trFieldNames[lang]] = translatedStr;
|
|
529
|
+
totalTranslated = totalTranslated.concat(noPluralKeys, pluralKeys);
|
|
490
530
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return Object.keys(updateStrings).length;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const langsInvolved = new Set(Object.keys(needToTranslateByLang));
|
|
497
|
-
|
|
498
|
-
let totalTranslated = 0;
|
|
499
|
-
await Promise.all(Object.entries(needToTranslateByLang).map(async ([lang, strings]: [LanguageCode, { en_string: string, category: string }[]]) => {
|
|
500
|
-
// first translate without plurals
|
|
501
|
-
const stringsWithoutPlurals = strings.filter(s => !s.en_string.includes('|'));
|
|
502
|
-
totalTranslated += await translateToLang(lang, stringsWithoutPlurals, false);
|
|
503
|
-
|
|
504
|
-
const stringsWithPlurals = strings.filter(s => s.en_string.includes('|'));
|
|
505
|
-
totalTranslated += await translateToLang(lang, stringsWithPlurals, true);
|
|
506
|
-
}));
|
|
531
|
+
)
|
|
532
|
+
);
|
|
507
533
|
|
|
508
534
|
await Promise.all(
|
|
509
535
|
Object.entries(updateStrings).map(
|
|
@@ -520,13 +546,13 @@ ${
|
|
|
520
546
|
);
|
|
521
547
|
|
|
522
548
|
for (const lang of langsInvolved) {
|
|
523
|
-
this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
549
|
+
await this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
524
550
|
for (const [enStr, { category }] of Object.entries(updateStrings)) {
|
|
525
|
-
this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
|
|
551
|
+
await this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
|
|
526
552
|
}
|
|
527
553
|
}
|
|
528
554
|
|
|
529
|
-
return totalTranslated;
|
|
555
|
+
return new Set(totalTranslated).size;
|
|
530
556
|
|
|
531
557
|
}
|
|
532
558
|
|
|
@@ -593,12 +619,10 @@ ${
|
|
|
593
619
|
});
|
|
594
620
|
w.on('change', () => {
|
|
595
621
|
process.env.HEAVY_DEBUG && console.log('🪲🔔messagesFile change', messagesFile);
|
|
596
|
-
|
|
597
622
|
this.processExtractedMessages(adminforth, messagesFile);
|
|
598
623
|
});
|
|
599
624
|
w.on('add', () => {
|
|
600
625
|
process.env.HEAVY_DEBUG && console.log('🪲🔔messagesFile add', messagesFile);
|
|
601
|
-
|
|
602
626
|
this.processExtractedMessages(adminforth, messagesFile);
|
|
603
627
|
});
|
|
604
628
|
|
|
@@ -607,6 +631,10 @@ ${
|
|
|
607
631
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
|
|
608
632
|
// optional method where you can safely check field types after database discovery was performed
|
|
609
633
|
// ensure each trFieldName (apart from enFieldName) is nullable column of type string
|
|
634
|
+
if (this.options.completeAdapter) {
|
|
635
|
+
this.options.completeAdapter.validate();
|
|
636
|
+
}
|
|
637
|
+
|
|
610
638
|
for (const lang of this.options.supportedLanguages) {
|
|
611
639
|
if (lang === 'en') {
|
|
612
640
|
continue;
|