@adminforth/i18n 1.0.9 → 1.0.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/Changelog.md +13 -0
- package/custom/langCommon.ts +1 -0
- package/dist/custom/langCommon.ts +1 -0
- package/dist/index.js +38 -15
- package/index.ts +47 -14
- package/package.json +1 -1
package/Changelog.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [v1.0.10]
|
|
9
|
+
|
|
10
|
+
## Fixed
|
|
11
|
+
|
|
12
|
+
- fix automatic translations for duplicate strings
|
|
13
|
+
- improve slavik pluralization generations by splitting the requests
|
package/custom/langCommon.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -205,29 +205,38 @@ export default class I18N extends AdminForthPlugin {
|
|
|
205
205
|
// clear frontend cache for all lan
|
|
206
206
|
return { ok: true };
|
|
207
207
|
}));
|
|
208
|
+
// add hook on delete of any translation to reset cache
|
|
209
|
+
resourceConfig.hooks.delete.afterSave.push((_d) => __awaiter(this, [_d], void 0, function* ({ record }) {
|
|
210
|
+
for (const lang of this.options.supportedLanguages) {
|
|
211
|
+
this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
212
|
+
this.cache.clear(`${this.resourceConfig.resourceId}:${record[this.options.categoryFieldName]}:${lang}:${record[this.enFieldName]}`);
|
|
213
|
+
}
|
|
214
|
+
this.updateUntranslatedMenuBadge();
|
|
215
|
+
return { ok: true };
|
|
216
|
+
}));
|
|
208
217
|
if (this.options.completedFieldName) {
|
|
209
218
|
// on show and list add a list hook which will add incomplete field to record if translation is missing for at least one language
|
|
210
219
|
const addIncompleteField = (record) => {
|
|
211
220
|
// form list of all langs, sorted by alphabet, without en, to get 'al|ro|uk'
|
|
212
221
|
record.fully_translated = this.fullCompleatedFieldValue === record[this.options.completedFieldName];
|
|
213
222
|
};
|
|
214
|
-
resourceConfig.hooks.list.afterDatasourceResponse.push((
|
|
223
|
+
resourceConfig.hooks.list.afterDatasourceResponse.push((_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
|
|
215
224
|
response.forEach(addIncompleteField);
|
|
216
225
|
return { ok: true };
|
|
217
226
|
}));
|
|
218
|
-
resourceConfig.hooks.show.afterDatasourceResponse.push((
|
|
227
|
+
resourceConfig.hooks.show.afterDatasourceResponse.push((_f) => __awaiter(this, [_f], void 0, function* ({ response }) {
|
|
219
228
|
addIncompleteField(response.length && response[0]);
|
|
220
229
|
return { ok: true };
|
|
221
230
|
}));
|
|
222
231
|
// also add edit hook beforeSave to update completedFieldName
|
|
223
|
-
resourceConfig.hooks.edit.beforeSave.push((
|
|
232
|
+
resourceConfig.hooks.edit.beforeSave.push((_g) => __awaiter(this, [_g], void 0, function* ({ record, oldRecord }) {
|
|
224
233
|
const futureRecord = Object.assign(Object.assign({}, oldRecord), record);
|
|
225
234
|
const futureCompletedFieldValue = yield this.computeCompletedFieldValue(futureRecord);
|
|
226
235
|
record[this.options.completedFieldName] = futureCompletedFieldValue;
|
|
227
236
|
return { ok: true };
|
|
228
237
|
}));
|
|
229
238
|
// add list hook to support filtering by fully_translated virtual field
|
|
230
|
-
resourceConfig.hooks.list.beforeDatasourceRequest.push((
|
|
239
|
+
resourceConfig.hooks.list.beforeDatasourceRequest.push((_h) => __awaiter(this, [_h], void 0, function* ({ query }) {
|
|
231
240
|
if (!query.filters || query.filters.length === 0) {
|
|
232
241
|
query.filters = [];
|
|
233
242
|
}
|
|
@@ -265,7 +274,7 @@ export default class I18N extends AdminForthPlugin {
|
|
|
265
274
|
// if optional `confirm` is provided, user will be asked to confirm action
|
|
266
275
|
confirm: 'Are you sure you want to translate selected items?',
|
|
267
276
|
state: 'selected',
|
|
268
|
-
action: (
|
|
277
|
+
action: (_j) => __awaiter(this, [_j], void 0, function* ({ selectedIds, tr }) {
|
|
269
278
|
try {
|
|
270
279
|
yield this.bulkTranslate({ selectedIds });
|
|
271
280
|
}
|
|
@@ -308,6 +317,7 @@ export default class I18N extends AdminForthPlugin {
|
|
|
308
317
|
});
|
|
309
318
|
});
|
|
310
319
|
}
|
|
320
|
+
// returns translated count
|
|
311
321
|
bulkTranslate(_a) {
|
|
312
322
|
return __awaiter(this, arguments, void 0, function* ({ selectedIds }) {
|
|
313
323
|
const needToTranslateByLang = {};
|
|
@@ -331,19 +341,24 @@ export default class I18N extends AdminForthPlugin {
|
|
|
331
341
|
}
|
|
332
342
|
const maxKeysInOneReq = 10;
|
|
333
343
|
const updateStrings = {};
|
|
334
|
-
const translateToLang = (
|
|
344
|
+
const translateToLang = (langIsoCode_1, strings_1, ...args_1) => __awaiter(this, [langIsoCode_1, strings_1, ...args_1], void 0, function* (langIsoCode, strings, plurals = false) {
|
|
345
|
+
if (strings.length === 0) {
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
335
348
|
if (strings.length > maxKeysInOneReq) {
|
|
349
|
+
let totalTranslated = 0;
|
|
336
350
|
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
337
351
|
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
338
|
-
|
|
352
|
+
console.log('🪲🔪slicedStrings ', slicedStrings);
|
|
353
|
+
totalTranslated += yield translateToLang(langIsoCode, slicedStrings, plurals);
|
|
339
354
|
}
|
|
340
|
-
return;
|
|
355
|
+
return totalTranslated;
|
|
341
356
|
}
|
|
342
357
|
const lang = langIsoCode;
|
|
343
|
-
const
|
|
358
|
+
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
344
359
|
const prompt = `
|
|
345
360
|
I need to translate strings in JSON to ${lang} language from English for my web app.
|
|
346
|
-
${
|
|
361
|
+
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
347
362
|
Keep keys, as is, write translation into values! Here are the strings:
|
|
348
363
|
|
|
349
364
|
\`\`\`json
|
|
@@ -378,22 +393,29 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
378
393
|
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
379
394
|
// might be several with same en_string
|
|
380
395
|
for (const translation of translationsTargeted) {
|
|
381
|
-
translation[this.trFieldNames[lang]] = translatedStr;
|
|
396
|
+
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
382
397
|
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
383
|
-
if (!updateStrings[
|
|
384
|
-
updateStrings[
|
|
398
|
+
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
399
|
+
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
385
400
|
updates: {},
|
|
401
|
+
translatedStr,
|
|
386
402
|
category: translation[this.options.categoryFieldName],
|
|
387
403
|
strId: translation[this.primaryKeyFieldName],
|
|
388
404
|
};
|
|
389
405
|
}
|
|
390
|
-
updateStrings[
|
|
406
|
+
updateStrings[translation[this.primaryKeyFieldName]].updates[this.trFieldNames[lang]] = translatedStr;
|
|
391
407
|
}
|
|
392
408
|
}
|
|
409
|
+
return res.length;
|
|
393
410
|
});
|
|
394
411
|
const langsInvolved = new Set(Object.keys(needToTranslateByLang));
|
|
412
|
+
let totalTranslated = 0;
|
|
395
413
|
yield Promise.all(Object.entries(needToTranslateByLang).map((_b) => __awaiter(this, [_b], void 0, function* ([lang, strings]) {
|
|
396
|
-
|
|
414
|
+
// first translate without plurals
|
|
415
|
+
const stringsWithoutPlurals = strings.filter(s => !s.en_string.includes('|'));
|
|
416
|
+
totalTranslated += yield translateToLang(lang, stringsWithoutPlurals, false);
|
|
417
|
+
const stringsWithPlurals = strings.filter(s => s.en_string.includes('|'));
|
|
418
|
+
totalTranslated += yield translateToLang(lang, stringsWithPlurals, true);
|
|
397
419
|
})));
|
|
398
420
|
yield Promise.all(Object.entries(updateStrings).map((_c) => __awaiter(this, [_c], void 0, function* ([_, { updates, strId }]) {
|
|
399
421
|
// because this will translate all languages, we can set completedLangs to all languages
|
|
@@ -406,6 +428,7 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
406
428
|
this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
|
|
407
429
|
}
|
|
408
430
|
}
|
|
431
|
+
return totalTranslated;
|
|
409
432
|
});
|
|
410
433
|
}
|
|
411
434
|
processExtractedMessages(adminforth, filePath) {
|
package/index.ts
CHANGED
|
@@ -234,6 +234,16 @@ export default class I18N extends AdminForthPlugin {
|
|
|
234
234
|
return { ok: true };
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
// add hook on delete of any translation to reset cache
|
|
238
|
+
resourceConfig.hooks.delete.afterSave.push(async ({ record }: { record: any }): Promise<{ ok: boolean, error?: string }> => {
|
|
239
|
+
for (const lang of this.options.supportedLanguages) {
|
|
240
|
+
this.cache.clear(`${this.resourceConfig.resourceId}:frontend:${lang}`);
|
|
241
|
+
this.cache.clear(`${this.resourceConfig.resourceId}:${record[this.options.categoryFieldName]}:${lang}:${record[this.enFieldName]}`);
|
|
242
|
+
}
|
|
243
|
+
this.updateUntranslatedMenuBadge();
|
|
244
|
+
return { ok: true };
|
|
245
|
+
});
|
|
246
|
+
|
|
237
247
|
if (this.options.completedFieldName) {
|
|
238
248
|
// on show and list add a list hook which will add incomplete field to record if translation is missing for at least one language
|
|
239
249
|
const addIncompleteField = (record: any) => {
|
|
@@ -347,7 +357,8 @@ export default class I18N extends AdminForthPlugin {
|
|
|
347
357
|
});
|
|
348
358
|
}
|
|
349
359
|
|
|
350
|
-
|
|
360
|
+
// returns translated count
|
|
361
|
+
async bulkTranslate({ selectedIds }: { selectedIds: string[] }): Promise<number> {
|
|
351
362
|
|
|
352
363
|
const needToTranslateByLang : Partial<
|
|
353
364
|
Record<
|
|
@@ -382,26 +393,33 @@ export default class I18N extends AdminForthPlugin {
|
|
|
382
393
|
const maxKeysInOneReq = 10;
|
|
383
394
|
|
|
384
395
|
const updateStrings: Record<string, {
|
|
385
|
-
updates: any,
|
|
396
|
+
updates: any,
|
|
397
|
+
category: string,
|
|
398
|
+
strId: string,
|
|
399
|
+
translatedStr: string
|
|
386
400
|
}> = {};
|
|
387
401
|
|
|
388
|
-
const translateToLang = async (langIsoCode: LanguageCode, strings: { en_string: string, category: string }[]) => {
|
|
389
|
-
|
|
402
|
+
const translateToLang = async (langIsoCode: LanguageCode, strings: { en_string: string, category: string }[], plurals=false): Promise<number> => {
|
|
403
|
+
if (strings.length === 0) {
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
390
406
|
|
|
391
407
|
if (strings.length > maxKeysInOneReq) {
|
|
408
|
+
let totalTranslated = 0;
|
|
392
409
|
for (let i = 0; i < strings.length; i += maxKeysInOneReq) {
|
|
393
410
|
const slicedStrings = strings.slice(i, i + maxKeysInOneReq);
|
|
394
|
-
|
|
411
|
+
console.log('🪲🔪slicedStrings ', slicedStrings);
|
|
412
|
+
totalTranslated += await translateToLang(langIsoCode, slicedStrings, plurals);
|
|
395
413
|
}
|
|
396
|
-
return;
|
|
414
|
+
return totalTranslated;
|
|
397
415
|
}
|
|
398
416
|
const lang = langIsoCode;
|
|
399
417
|
|
|
400
|
-
const
|
|
418
|
+
const requestSlavicPlurals = Object.keys(SLAVIC_PLURAL_EXAMPLES).includes(lang) && plurals;
|
|
401
419
|
|
|
402
420
|
const prompt = `
|
|
403
421
|
I need to translate strings in JSON to ${lang} language from English for my web app.
|
|
404
|
-
${
|
|
422
|
+
${requestSlavicPlurals ? `You should provide 4 translations (in format zero | singular | 2-4 | 5+) e.g. ${SLAVIC_PLURAL_EXAMPLES[lang]}` : ''}
|
|
405
423
|
Keep keys, as is, write translation into values! Here are the strings:
|
|
406
424
|
|
|
407
425
|
\`\`\`json
|
|
@@ -442,29 +460,42 @@ ${
|
|
|
442
460
|
return;
|
|
443
461
|
}
|
|
444
462
|
res = JSON.parse(res);
|
|
445
|
-
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
for (const [enStr, translatedStr] of Object.entries(res) as [string, string][]) {
|
|
446
466
|
const translationsTargeted = translations.filter(t => t[this.enFieldName] === enStr);
|
|
447
467
|
// might be several with same en_string
|
|
448
468
|
for (const translation of translationsTargeted) {
|
|
449
|
-
translation[this.trFieldNames[lang]] = translatedStr;
|
|
469
|
+
//translation[this.trFieldNames[lang]] = translatedStr;
|
|
450
470
|
// process.env.HEAVY_DEBUG && console.log(`🪲translated to ${lang} ${translation.en_string}, ${translatedStr}`)
|
|
451
|
-
if (!updateStrings[
|
|
452
|
-
|
|
471
|
+
if (!updateStrings[translation[this.primaryKeyFieldName]]) {
|
|
472
|
+
|
|
473
|
+
updateStrings[translation[this.primaryKeyFieldName]] = {
|
|
453
474
|
updates: {},
|
|
475
|
+
translatedStr,
|
|
454
476
|
category: translation[this.options.categoryFieldName],
|
|
455
477
|
strId: translation[this.primaryKeyFieldName],
|
|
456
478
|
};
|
|
457
479
|
}
|
|
458
|
-
updateStrings[
|
|
480
|
+
updateStrings[
|
|
481
|
+
translation[this.primaryKeyFieldName]
|
|
482
|
+
].updates[this.trFieldNames[lang]] = translatedStr;
|
|
459
483
|
}
|
|
460
484
|
}
|
|
461
485
|
|
|
486
|
+
return res.length;
|
|
462
487
|
}
|
|
463
488
|
|
|
464
489
|
const langsInvolved = new Set(Object.keys(needToTranslateByLang));
|
|
465
490
|
|
|
491
|
+
let totalTranslated = 0;
|
|
466
492
|
await Promise.all(Object.entries(needToTranslateByLang).map(async ([lang, strings]: [LanguageCode, { en_string: string, category: string }[]]) => {
|
|
467
|
-
|
|
493
|
+
// first translate without plurals
|
|
494
|
+
const stringsWithoutPlurals = strings.filter(s => !s.en_string.includes('|'));
|
|
495
|
+
totalTranslated += await translateToLang(lang, stringsWithoutPlurals, false);
|
|
496
|
+
|
|
497
|
+
const stringsWithPlurals = strings.filter(s => s.en_string.includes('|'));
|
|
498
|
+
totalTranslated += await translateToLang(lang, stringsWithPlurals, true);
|
|
468
499
|
}));
|
|
469
500
|
|
|
470
501
|
await Promise.all(
|
|
@@ -488,6 +519,8 @@ ${
|
|
|
488
519
|
}
|
|
489
520
|
}
|
|
490
521
|
|
|
522
|
+
return totalTranslated;
|
|
523
|
+
|
|
491
524
|
}
|
|
492
525
|
|
|
493
526
|
async processExtractedMessages(adminforth: IAdminForth, filePath: string) {
|