@adminforth/i18n 1.0.10 → 1.0.12
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 -1
- package/custom/LanguageInUserMenu.vue +2 -2
- package/custom/langCommon.ts +1 -0
- package/dist/custom/LanguageInUserMenu.vue +2 -2
- package/dist/custom/langCommon.ts +1 -0
- package/dist/index.js +23 -9
- package/index.ts +21 -6
- package/package.json +1 -1
package/Changelog.md
CHANGED
|
@@ -5,9 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.12]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- live mode frontend translations loading when tmp dir is nopt preserver (e.g. docker cached /tmp pipeline)
|
|
13
|
+
|
|
14
|
+
## [1.0.11]
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- cache invalidations on delete
|
|
19
|
+
|
|
8
20
|
## [v1.0.10]
|
|
9
21
|
|
|
10
|
-
|
|
22
|
+
### Fixed
|
|
11
23
|
|
|
12
24
|
- fix automatic translations for duplicate strings
|
|
13
25
|
- improve slavik pluralization generations by splitting the requests
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
import 'flag-icon-css/css/flag-icons.min.css';
|
|
44
44
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
45
45
|
|
|
46
|
-
import { setLang, getCountryCodeFromLangCode, getLocalLang } from './langCommon';
|
|
46
|
+
import { setLang, getCountryCodeFromLangCode, getLocalLang, setLocalLang } from './langCommon';
|
|
47
47
|
|
|
48
48
|
import { computed, ref, onMounted, watch } from 'vue';
|
|
49
49
|
import { useI18n } from 'vue-i18n';
|
|
@@ -56,7 +56,7 @@ const props = defineProps(['meta', 'resource']);
|
|
|
56
56
|
const selectedLanguage = ref('');
|
|
57
57
|
|
|
58
58
|
function doChangeLang(lang) {
|
|
59
|
-
|
|
59
|
+
setLocalLang(lang);
|
|
60
60
|
// unfortunately, we need this to recall all APIs
|
|
61
61
|
document.location.reload();
|
|
62
62
|
|
package/custom/langCommon.ts
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
import 'flag-icon-css/css/flag-icons.min.css';
|
|
44
44
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
45
45
|
|
|
46
|
-
import { setLang, getCountryCodeFromLangCode, getLocalLang } from './langCommon';
|
|
46
|
+
import { setLang, getCountryCodeFromLangCode, getLocalLang, setLocalLang } from './langCommon';
|
|
47
47
|
|
|
48
48
|
import { computed, ref, onMounted, watch } from 'vue';
|
|
49
49
|
import { useI18n } from 'vue-i18n';
|
|
@@ -56,7 +56,7 @@ const props = defineProps(['meta', 'resource']);
|
|
|
56
56
|
const selectedLanguage = ref('');
|
|
57
57
|
|
|
58
58
|
function doChangeLang(lang) {
|
|
59
|
-
|
|
59
|
+
setLocalLang(lang);
|
|
60
60
|
// unfortunately, we need this to recall all APIs
|
|
61
61
|
document.location.reload();
|
|
62
62
|
|
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
|
}
|
|
@@ -278,7 +287,7 @@ export default class I18N extends AdminForthPlugin {
|
|
|
278
287
|
this.updateUntranslatedMenuBadge();
|
|
279
288
|
return {
|
|
280
289
|
ok: true, error: undefined,
|
|
281
|
-
successMessage: yield tr(`Translated {count} items`, '
|
|
290
|
+
successMessage: yield tr(`Translated {count} items`, 'backend', { count: selectedIds.length }),
|
|
282
291
|
};
|
|
283
292
|
})
|
|
284
293
|
});
|
|
@@ -460,9 +469,9 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
460
469
|
}
|
|
461
470
|
tryProcessAndWatch(adminforth) {
|
|
462
471
|
return __awaiter(this, void 0, void 0, function* () {
|
|
463
|
-
const
|
|
472
|
+
const serveDir = adminforth.codeInjector.getServeDir();
|
|
464
473
|
// messages file is in i18n-messages.json
|
|
465
|
-
const messagesFile = path.join(
|
|
474
|
+
const messagesFile = path.join(serveDir, 'i18n-messages.json');
|
|
466
475
|
console.log('🪲messagesFile', messagesFile);
|
|
467
476
|
this.processExtractedMessages(adminforth, messagesFile);
|
|
468
477
|
// we use watcher because file can't be yet created when we start - bundleNow can be done in build time or can be done now
|
|
@@ -509,10 +518,15 @@ ${JSON.stringify(strings.reduce((acc, s) => {
|
|
|
509
518
|
if (!msg) {
|
|
510
519
|
return msg;
|
|
511
520
|
}
|
|
521
|
+
if (category === 'frontend') {
|
|
522
|
+
throw new Error(`Category 'frontend' is reserved for frontend messages, use any other category for backend messages`);
|
|
523
|
+
}
|
|
512
524
|
// console.log('🪲tr', msg, category, lang);
|
|
513
525
|
// if lang is not supported , throw
|
|
514
526
|
if (!this.options.supportedLanguages.includes(lang)) {
|
|
515
|
-
|
|
527
|
+
lang = 'en'; // for now simply fallback to english
|
|
528
|
+
// throwing like line below might be too strict, e.g. for custom apis made with fetch which don't pass accept-language
|
|
529
|
+
// throw new Error(`Language ${lang} is not entered to be supported by requested by browser in request headers accept-language`);
|
|
516
530
|
}
|
|
517
531
|
let result;
|
|
518
532
|
// try to get translation from cache
|
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) => {
|
|
@@ -316,7 +326,7 @@ export default class I18N extends AdminForthPlugin {
|
|
|
316
326
|
this.updateUntranslatedMenuBadge();
|
|
317
327
|
return {
|
|
318
328
|
ok: true, error: undefined,
|
|
319
|
-
successMessage: await tr(`Translated {count} items`, '
|
|
329
|
+
successMessage: await tr(`Translated {count} items`, 'backend', {count: selectedIds.length}),
|
|
320
330
|
};
|
|
321
331
|
}
|
|
322
332
|
}
|
|
@@ -550,15 +560,13 @@ ${
|
|
|
550
560
|
|
|
551
561
|
// updateBadge
|
|
552
562
|
this.updateUntranslatedMenuBadge()
|
|
553
|
-
|
|
554
|
-
|
|
555
563
|
}
|
|
556
564
|
|
|
557
565
|
|
|
558
566
|
async tryProcessAndWatch(adminforth: IAdminForth) {
|
|
559
|
-
const
|
|
567
|
+
const serveDir = adminforth.codeInjector.getServeDir();
|
|
560
568
|
// messages file is in i18n-messages.json
|
|
561
|
-
const messagesFile = path.join(
|
|
569
|
+
const messagesFile = path.join(serveDir, 'i18n-messages.json');
|
|
562
570
|
console.log('🪲messagesFile', messagesFile);
|
|
563
571
|
this.processExtractedMessages(adminforth, messagesFile);
|
|
564
572
|
// we use watcher because file can't be yet created when we start - bundleNow can be done in build time or can be done now
|
|
@@ -609,11 +617,18 @@ ${
|
|
|
609
617
|
if (!msg) {
|
|
610
618
|
return msg;
|
|
611
619
|
}
|
|
620
|
+
|
|
621
|
+
if (category === 'frontend') {
|
|
622
|
+
throw new Error(`Category 'frontend' is reserved for frontend messages, use any other category for backend messages`);
|
|
623
|
+
}
|
|
612
624
|
// console.log('🪲tr', msg, category, lang);
|
|
613
625
|
|
|
614
626
|
// if lang is not supported , throw
|
|
615
627
|
if (!this.options.supportedLanguages.includes(lang as LanguageCode)) {
|
|
616
|
-
|
|
628
|
+
lang = 'en'; // for now simply fallback to english
|
|
629
|
+
|
|
630
|
+
// throwing like line below might be too strict, e.g. for custom apis made with fetch which don't pass accept-language
|
|
631
|
+
// throw new Error(`Language ${lang} is not entered to be supported by requested by browser in request headers accept-language`);
|
|
617
632
|
}
|
|
618
633
|
|
|
619
634
|
let result;
|