@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 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
- ## Fixed
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
- setLang({ setLocaleMessage, locale }, props.meta.pluginInstanceId, lang);
59
+ setLocalLang(lang);
60
60
  // unfortunately, we need this to recall all APIs
61
61
  document.location.reload();
62
62
 
@@ -54,6 +54,7 @@ const countryISO31661ByLangISO6391 = {
54
54
  ko: 'kr', // Korean → South Korea
55
55
  ja: 'jp', // Japanese → Japan
56
56
  uk: 'ua', // Ukrainian → Ukraine
57
+ ur: 'pk', // Urdu → Pakistan
57
58
  };
58
59
 
59
60
  export function getCountryCodeFromLangCode(langCode) {
@@ -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
- setLang({ setLocaleMessage, locale }, props.meta.pluginInstanceId, lang);
59
+ setLocalLang(lang);
60
60
  // unfortunately, we need this to recall all APIs
61
61
  document.location.reload();
62
62
 
@@ -54,6 +54,7 @@ const countryISO31661ByLangISO6391 = {
54
54
  ko: 'kr', // Korean → South Korea
55
55
  ja: 'jp', // Japanese → Japan
56
56
  uk: 'ua', // Ukrainian → Ukraine
57
+ ur: 'pk', // Urdu → Pakistan
57
58
  };
58
59
 
59
60
  export function getCountryCodeFromLangCode(langCode) {
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((_d) => __awaiter(this, [_d], void 0, function* ({ response }) {
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((_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
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((_f) => __awaiter(this, [_f], void 0, function* ({ record, oldRecord }) {
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((_g) => __awaiter(this, [_g], void 0, function* ({ query }) {
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: (_h) => __awaiter(this, [_h], void 0, function* ({ selectedIds, tr }) {
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`, 'frontend', { count: selectedIds.length }),
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 spaDir = adminforth.codeInjector.spaTmpPath();
472
+ const serveDir = adminforth.codeInjector.getServeDir();
464
473
  // messages file is in i18n-messages.json
465
- const messagesFile = path.join(spaDir, '..', 'spa_tmp', 'i18n-messages.json');
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
- throw new Error(`Language ${lang} is not entered to be supported by requested by browser in request headers accept-language`);
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`, 'frontend', {count: selectedIds.length}),
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 spaDir = adminforth.codeInjector.spaTmpPath();
567
+ const serveDir = adminforth.codeInjector.getServeDir();
560
568
  // messages file is in i18n-messages.json
561
- const messagesFile = path.join(spaDir, '..', 'spa_tmp', 'i18n-messages.json');
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
- throw new Error(`Language ${lang} is not entered to be supported by requested by browser in request headers accept-language`);
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/i18n",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",