@adminforth/i18n 2.4.0 → 2.5.0

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.
@@ -41,7 +41,7 @@
41
41
  <div class="group hover:bg-gray-100 dark:hover:bg-gray-600 px-2 py-2 flex items-center cursor-pointer" v-for="(index, lang) in checkedLanguages" :key="lang" @click="toggleLanguage(lang)">
42
42
  <Checkbox v-model="checkedLanguages[lang]" />
43
43
  <span class="flag-icon mr-2"
44
- :class="`flag-icon-${getCountryCodeFromLangCode(lang)}`"
44
+ :class="`flag-icon-${props.meta.countryCodes[lang]}`"
45
45
  ></span>
46
46
  <span class="group-hover:text-gray-900 text-gray-600 dark:text-white dark:group-hover:text-gray-200">{{ ISO6391.getName(lang.slice(0,2)) }} ({{ lang }})</span>
47
47
  </div>
@@ -58,7 +58,6 @@
58
58
  import { computed, onMounted, ref, onUnmounted } from 'vue';
59
59
  import { callAdminForthApi } from '@/utils';
60
60
  import { useAdminforth } from '@/adminforth';
61
- import { getCountryCodeFromLangCode } from './langCommon';
62
61
  import { getName, overwrite } from 'country-list';
63
62
  import ISO6391 from 'iso-639-1';
64
63
  import 'flag-icon-css/css/flag-icons.min.css';
@@ -82,6 +81,7 @@
82
81
  adminUser: Record<string, any>;
83
82
  meta: {
84
83
  supportedLanguages: string[];
84
+ countryCodes: Record<string, string>;
85
85
  pluginInstanceId: string;
86
86
  };
87
87
  clearCheckboxes: () => void;
@@ -10,7 +10,7 @@
10
10
  <div>
11
11
  <span class="mr-1">
12
12
  <span class="flag-icon"
13
- :class="`flag-icon-${getCountryCodeFromLangCode(selectedOption.value)}`"
13
+ :class="`flag-icon-${selectedOption.countryCode}`"
14
14
  ></span>
15
15
  </span>
16
16
  <span>{{ selectedOption.label }}</span>
@@ -32,7 +32,7 @@
32
32
  >
33
33
  <span class="mr-1">
34
34
  <span class="flag-icon"
35
- :class="`flag-icon-${getCountryCodeFromLangCode(option.value)}`"
35
+ :class="`flag-icon-${option.countryCode}`"
36
36
  ></span>
37
37
  </span>
38
38
  <span>{{ option.label }}</span>
@@ -48,7 +48,7 @@
48
48
  import 'flag-icon-css/css/flag-icons.min.css';
49
49
  import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
50
50
 
51
- import { setLang, getCountryCodeFromLangCode, getLocalLang, setLocalLang } from './langCommon';
51
+ import { setLang, getLocalLang, setLocalLang, getLanguageOptions } from './langCommon';
52
52
 
53
53
  import { computed, ref, onMounted, watch } from 'vue';
54
54
  import { useI18n } from 'vue-i18n';
@@ -68,15 +68,7 @@ function doChangeLang(lang) {
68
68
  }
69
69
 
70
70
 
71
- const options = computed(() => {
72
- return props.meta.supportedLanguages.map((lang) => {
73
- const region = String(lang.code).split('-')[1]?.toUpperCase();
74
- return {
75
- value: lang.code,
76
- label: region ? `${lang.name} (${region})` : lang.name,
77
- };
78
- });
79
- });
71
+ const options = computed(() => getLanguageOptions(props.meta.supportedLanguages));
80
72
 
81
73
  const selectedOption = computed(() => {
82
74
  const val = options.value.find((option) => option.value === selectedLanguage.value);
@@ -9,7 +9,7 @@
9
9
  <template #item="{ option }">
10
10
  <span class="mr-1">
11
11
  <span class="flag-icon"
12
- :class="`flag-icon-${getCountryCodeFromLangCode(option.value)}`"
12
+ :class="`flag-icon-${option.countryCode}`"
13
13
  ></span>
14
14
 
15
15
  </span>
@@ -19,7 +19,7 @@
19
19
  <template #selected-item="{option}">
20
20
  <span class="mr-1">
21
21
  <span class="flag-icon"
22
- :class="`flag-icon-${getCountryCodeFromLangCode(option.value)}`"
22
+ :class="`flag-icon-${option.countryCode}`"
23
23
  ></span>
24
24
  </span>
25
25
  <span>{{ option.label }}</span>
@@ -31,7 +31,7 @@
31
31
  <script setup>
32
32
  import Select from '@/afcl/Select.vue';
33
33
  import 'flag-icon-css/css/flag-icons.min.css';
34
- import { setLang, getCountryCodeFromLangCode, getLocalLang } from './langCommon';
34
+ import { setLang, getLocalLang, getLanguageOptions } from './langCommon';
35
35
  import { useCoreStore } from '@/stores/core';
36
36
 
37
37
  import { computed, ref, onMounted, watch } from 'vue';
@@ -52,15 +52,7 @@ watch(() => selectedLanguage.value, async (newVal) => {
52
52
  });
53
53
 
54
54
 
55
- const options = computed(() => {
56
- return props.meta.supportedLanguages.map((lang) => {
57
- const region = String(lang.code).split('-')[1]?.toUpperCase();
58
- return {
59
- value: lang.code,
60
- label: region ? `${lang.name} (${region})` : lang.name,
61
- };
62
- });
63
- });
55
+ const options = computed(() => getLanguageOptions(props.meta.supportedLanguages));
64
56
 
65
57
  onMounted(() => {
66
58
  selectedLanguage.value = getLocalLang(props.meta.supportedLanguages, props.meta.primaryLanguage);
@@ -22,7 +22,7 @@
22
22
  </span>
23
23
  <div class="flex items-center gap-2 shrink-0">
24
24
  <span class="flag-icon"
25
- :class="`flag-icon-${getCountryCodeFromLangCode(task.state?.lang)}`"
25
+ :class="`flag-icon-${task.state?.countryCode}`"
26
26
  ></span>
27
27
  <component
28
28
  :is="getCustomComponent({file: '@@/plugins/BackgroundJobsPlugin/StateToIcon.vue'})"
@@ -61,7 +61,6 @@
61
61
  import { useI18n } from 'vue-i18n'
62
62
  import { computed, onMounted, onUnmounted, ref } from 'vue';
63
63
  import websocket from '@/websocket';
64
- import { getCountryCodeFromLangCode } from './langCommon';
65
64
  import { getCustomComponent } from '@/utils';
66
65
  import { Button, Spinner } from '@/afcl';
67
66
  import { IconAngleLeftOutline, IconAngleRightOutline } from '@iconify-prerendered/vue-flowbite';
@@ -106,7 +105,7 @@ async function loadPage(targetPage: number) {
106
105
  try {
107
106
  for (let retry = 0; retry < retries; retry++) {
108
107
  try {
109
- const res = await props.getJobTasks(PAGE_SIZE, targetPage * PAGE_SIZE, ['lang', 'error', 'taskName', 'failedToTranslate']);
108
+ const res = await props.getJobTasks(PAGE_SIZE, targetPage * PAGE_SIZE, ['lang', 'countryCode', 'error', 'taskName', 'failedToTranslate']);
110
109
  if (res && res.length > 0) {
111
110
  translationTasks.value = res;
112
111
  page.value = targetPage;
@@ -65,32 +65,20 @@ export async function setLang({ setLocaleMessage, locale }: any, pluginInstanceI
65
65
  setLocalLang(langIso);
66
66
  }
67
67
 
68
- // only remap the country code for the languages where language code is different from the country code
69
- // don't include es: es, fr: fr, etc, only include the ones where language code is different from the country code
70
- const countryISO31661ByLangISO6391 = {
71
- en: 'us', // English → United States
72
- zh: 'cn', // Chinese → China
73
- hi: 'in', // Hindi → India
74
- ar: 'sa', // Arabic → Saudi Arabia
75
- ko: 'kr', // Korean → South Korea
76
- ja: 'jp', // Japanese → Japan
77
- uk: 'ua', // Ukrainian → Ukraine
78
- ur: 'pk', // Urdu → Pakistan
79
- sr: 'rs', // Serbian → Serbia
80
- da: 'dk' // Danish → Denmark
81
- };
82
-
83
- export function getCountryCodeFromLangCode(langCode) {
84
- const [primary, region] = String(langCode).split('-');
85
- if (region && /^[A-Za-z]{2}$/.test(region)) {
86
- return region.toLowerCase();
87
- }
88
- return countryISO31661ByLangISO6391[primary] || primary;
89
- }
90
-
91
-
92
68
  const LS_LANG_KEY = `afLanguage`;
93
69
 
70
+ // options for language selectors, region of BCP-47 tag is added to native language name, e.g. "Português (BR)"
71
+ export function getLanguageOptions(supportedLanguages: { code: string, name: string, countryCode: string }[]) {
72
+ return supportedLanguages.map((lang) => {
73
+ const region = String(lang.code).split('-')[1]?.toUpperCase();
74
+ return {
75
+ value: lang.code,
76
+ countryCode: lang.countryCode,
77
+ label: region ? `${lang.name} (${region})` : lang.name,
78
+ };
79
+ });
80
+ }
81
+
94
82
  export function getLocalLang(supportedLanguages: {code}[], primaryLanguage?: SupportedLanguage): string {
95
83
  let lsLang = localStorage.getItem(LS_LANG_KEY);
96
84
  // if someone screwed up the local storage or we stopped language support, lets check if it is in supported languages
package/dist/index.d.ts CHANGED
@@ -24,7 +24,11 @@ export default class I18nPlugin extends I18nBase<PluginOptions> {
24
24
  externalAppOnly: boolean;
25
25
  fullCompleatedFieldValue: string;
26
26
  constructor(options: PluginOptions);
27
- computeCompletedFieldValue(record: any): Promise<string>;
27
+ completedLangsValue(isTranslated: (lang: SupportedLanguage) => boolean): string;
28
+ computeCompletedFieldValue(record: any): string;
29
+ categoryCacheKey(category: string, lang: string): string;
30
+ messageCacheKey(category: string, lang: string, enString: string): string;
31
+ clearTranslationCache(category: string, lang: string, enString: string): void;
28
32
  updateUntranslatedMenuBadge(): Promise<void>;
29
33
  modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
30
34
  generateAndSaveBunch(prompt: string, strings: {
@@ -67,5 +71,5 @@ export default class I18nPlugin extends I18nBase<PluginOptions> {
67
71
  }[], category: string): Promise<void>;
68
72
  setupEndpoints(server: IHttpServer): void;
69
73
  }
70
- export { I18nBase, getCountryCodeFromLangCode, getPrimaryLanguageCode, isValidSupportedLanguageTag, requiresSlavicPlurals, SOURCE_LANGUAGE, PLURAL_SEPARATOR, SLAVIC_PLURAL_FORMS } from './base.js';
74
+ export { I18nBase, getCountryCodeFromLangCode, getPrimaryLanguageCode, isValidSupportedLanguageTag, requiresSlavicPlurals, SOURCE_LANGUAGE, PLURAL_SEPARATOR, SLAVIC_PLURAL_FORMS, SLAVIC_PLURAL_FORMS_HINT } from './base.js';
71
75
  export type { I18nBaseOptions, SupportedLanguage } from './types.js';
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { Filters, suggestIfTypo, AdminForthDataTypes, RAMLock, filtersTools, AdminForthFilterOperators } from "adminforth";
11
- import { I18nBase, getPrimaryLanguageCode, requiresSlavicPlurals, SLAVIC_PLURAL_EXAMPLES } from './base.js';
11
+ import { I18nBase, getCountryCodeFromLangCode, getPrimaryLanguageCode, requiresSlavicPlurals, SLAVIC_PLURAL_EXAMPLES, SLAVIC_PLURAL_FORMS, SLAVIC_PLURAL_FORMS_HINT, SOURCE_LANGUAGE } from './base.js';
12
12
  import { z } from "zod";
13
13
  import iso6391 from 'iso-639-1';
14
14
  const updateFieldBodySchema = z.object({
@@ -77,21 +77,24 @@ export default class I18nPlugin extends I18nBase {
77
77
  this.cache = new CachingAdapterMemory();
78
78
  this.trFieldNames = {};
79
79
  }
80
+ // translated languages joined by '|', e.g. 'uk|ja', stored in completedFieldName to filter not fully translated strings
81
+ completedLangsValue(isTranslated) {
82
+ return this.targetLanguages().filter(isTranslated).join('|');
83
+ }
80
84
  computeCompletedFieldValue(record) {
81
- return __awaiter(this, void 0, void 0, function* () {
82
- return this.options.supportedLanguages.reduce((acc, lang) => {
83
- if (lang === 'en') {
84
- return acc;
85
- }
86
- if (record[this.trFieldNames[lang]]) {
87
- if (acc !== '') {
88
- acc += '|';
89
- }
90
- acc += lang;
91
- }
92
- return acc;
93
- }, '');
94
- });
85
+ return this.completedLangsValue((lang) => Boolean(record[this.trFieldNames[lang]]));
86
+ }
87
+ // all translations of category for frontend_messages
88
+ categoryCacheKey(category, lang) {
89
+ return `${this.resourceConfig.resourceId}:${category}:${lang}`;
90
+ }
91
+ // one string translated by backend tr
92
+ messageCacheKey(category, lang, enString) {
93
+ return `${this.categoryCacheKey(category, lang)}:${enString}`;
94
+ }
95
+ clearTranslationCache(category, lang, enString) {
96
+ this.cache.clear(this.categoryCacheKey(category, lang));
97
+ this.cache.clear(this.messageCacheKey(category, lang, enString));
95
98
  }
96
99
  updateUntranslatedMenuBadge() {
97
100
  return __awaiter(this, void 0, void 0, function* () {
@@ -131,10 +134,7 @@ export default class I18nPlugin extends I18nBase {
131
134
  throw new Error(`Primary key field not found in resource ${resourceConfig.resourceId}`);
132
135
  }
133
136
  // parse trFieldNames
134
- for (const lang of this.options.supportedLanguages) {
135
- if (lang === 'en') {
136
- continue;
137
- }
137
+ for (const lang of this.targetLanguages()) {
138
138
  if ((_b = this.options.translationFieldNames) === null || _b === void 0 ? void 0 : _b[lang]) {
139
139
  this.trFieldNames[lang] = this.options.translationFieldNames[lang];
140
140
  }
@@ -166,17 +166,8 @@ export default class I18nPlugin extends I18nBase {
166
166
  },
167
167
  };
168
168
  }
169
- this.enFieldName = this.trFieldNames['en'] || 'en_string';
170
- this.fullCompleatedFieldValue = this.options.supportedLanguages.reduce((acc, lang) => {
171
- if (lang === 'en') {
172
- return acc;
173
- }
174
- if (acc !== '') {
175
- acc += '|';
176
- }
177
- acc += lang;
178
- return acc;
179
- }, '');
169
+ this.enFieldName = this.trFieldNames[SOURCE_LANGUAGE] || `${SOURCE_LANGUAGE}_string`;
170
+ this.fullCompleatedFieldValue = this.completedLangsValue(() => true);
180
171
  // if not enFieldName column is not found, throw error
181
172
  const enColumn = resourceConfig.columns.find(c => c.name === this.enFieldName);
182
173
  if (!enColumn) {
@@ -243,10 +234,7 @@ export default class I18nPlugin extends I18nBase {
243
234
  resourceConfig.options.allowedActions.create = false;
244
235
  // add hook to validate user did not screw up with template params
245
236
  resourceConfig.hooks.edit.beforeSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ updates, oldRecord }) {
246
- for (const lang of this.options.supportedLanguages) {
247
- if (lang === 'en') {
248
- continue;
249
- }
237
+ for (const lang of this.targetLanguages()) {
250
238
  if (updates[this.trFieldNames[lang]]) { // if user set '', it will have '' in updates, then it is fine, we shoudl nto check it
251
239
  if (!ensureTemplateHasAllParams(oldRecord[this.enFieldName], updates[this.trFieldNames[lang]])) {
252
240
  return { ok: false, error: `Template params mismatch for ${updates[this.enFieldName]}. Template param names should be the same as in original string. E. g. 'Hello {name}', should be 'Hola {name}' and not 'Hola {nombre}'!` };
@@ -259,19 +247,10 @@ export default class I18nPlugin extends I18nBase {
259
247
  resourceConfig.hooks.edit.afterSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ updates, oldRecord }) {
260
248
  if (oldRecord) {
261
249
  // find lang which changed
262
- let langsChanged = [];
263
- for (const lang of this.options.supportedLanguages) {
264
- if (lang === 'en') {
265
- continue;
266
- }
267
- if (updates[this.trFieldNames[lang]] !== undefined) {
268
- langsChanged.push(lang);
269
- }
270
- }
250
+ const langsChanged = this.targetLanguages().filter((lang) => updates[this.trFieldNames[lang]] !== undefined);
271
251
  // clear frontend cache for all langsChanged
272
252
  for (const lang of langsChanged) {
273
- this.cache.clear(`${this.resourceConfig.resourceId}:${oldRecord[this.options.categoryFieldName]}:${lang}`);
274
- this.cache.clear(`${this.resourceConfig.resourceId}:${oldRecord[this.options.categoryFieldName]}:${lang}:${oldRecord[this.enFieldName]}`);
253
+ this.clearTranslationCache(oldRecord[this.options.categoryFieldName], lang, oldRecord[this.enFieldName]);
275
254
  }
276
255
  this.updateUntranslatedMenuBadge();
277
256
  }
@@ -281,9 +260,7 @@ export default class I18nPlugin extends I18nBase {
281
260
  // add hook on delete of any translation to reset cache
282
261
  resourceConfig.hooks.delete.afterSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ record }) {
283
262
  for (const lang of this.options.supportedLanguages) {
284
- // if frontend, clear frontend cache
285
- this.cache.clear(`${this.resourceConfig.resourceId}:${record[this.options.categoryFieldName]}:${lang}`);
286
- this.cache.clear(`${this.resourceConfig.resourceId}:${record[this.options.categoryFieldName]}:${lang}:${record[this.enFieldName]}`);
263
+ this.clearTranslationCache(record[this.options.categoryFieldName], lang, record[this.enFieldName]);
287
264
  }
288
265
  this.updateUntranslatedMenuBadge();
289
266
  return { ok: true };
@@ -342,6 +319,7 @@ export default class I18nPlugin extends I18nBase {
342
319
  file: this.componentPath('BulkActionButton.vue'),
343
320
  meta: {
344
321
  supportedLanguages: this.options.supportedLanguages,
322
+ countryCodes: Object.fromEntries(this.options.supportedLanguages.map((lang) => [lang, getCountryCodeFromLangCode(lang)])),
345
323
  pluginInstanceId: this.pluginInstanceId,
346
324
  }
347
325
  };
@@ -488,13 +466,8 @@ export default class I18nPlugin extends I18nBase {
488
466
  yield this.adminforth.resource(this.resourceConfig.resourceId).update(strId, Object.assign(Object.assign({}, updates), { [this.options.completedFieldName]: futureCompletedFieldValue }));
489
467
  })));
490
468
  for (const lang of langsInvolved) {
491
- const categoriesInvolved = new Set();
492
469
  for (const { enStr, category } of Object.values(updateStrings)) {
493
- categoriesInvolved.add(category);
494
- yield this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}:${enStr}`);
495
- }
496
- for (const category of categoriesInvolved) {
497
- yield this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}`);
470
+ this.clearTranslationCache(category, lang, enStr);
498
471
  }
499
472
  }
500
473
  return { ok: true };
@@ -555,7 +528,7 @@ export default class I18nPlugin extends I18nBase {
555
528
 
556
529
 
557
530
  ${region ? `Use the regional conventions for ${langCode} (region ${region}), including spelling, punctuation, and formatting.` : ''}
558
- ${requestSlavicPlurals ? `You should provide 4 slavic forms (in format "zero count | singular count | 2-4 | 5+") e.g. "apple | apples" should become "${SLAVIC_PLURAL_EXAMPLES[primaryLang]}"` : ''}
531
+ ${requestSlavicPlurals ? `You should provide ${SLAVIC_PLURAL_FORMS} slavic forms (in format "${SLAVIC_PLURAL_FORMS_HINT}") e.g. "apple | apples" should become "${SLAVIC_PLURAL_EXAMPLES[primaryLang]}"` : ''}
559
532
  JSON keys are internal identifiers. Keep keys as is and write translations into values. If values have variables (in curly brackets), then translated strings should have them as well (variables itself should not be translated). Here are the strings:
560
533
  \`\`\`json
561
534
  {
@@ -622,6 +595,7 @@ export default class I18nPlugin extends I18nBase {
622
595
  translations: translations.filter(t => stringBanchCopy.includes(t.en_string)),
623
596
  updateStrings,
624
597
  lang,
598
+ countryCode: getCountryCodeFromLangCode(lang),
625
599
  failedToTranslate,
626
600
  needToTranslateByLang,
627
601
  promptCost: basePromptTokenLength + banchTokens,
@@ -638,8 +612,8 @@ export default class I18nPlugin extends I18nBase {
638
612
  const translations = yield this.adminforth.resource(this.resourceConfig.resourceId).list(Filters.IN(this.primaryKeyFieldName, selectedIds));
639
613
  const languagesToProcess = selectedLanguages || this.options.supportedLanguages;
640
614
  for (const lang of languagesToProcess) {
641
- if (lang === 'en') {
642
- // all strings are in English, no need to translate
615
+ if (lang === SOURCE_LANGUAGE) {
616
+ // all strings are in source language, no need to translate
643
617
  continue;
644
618
  }
645
619
  for (const translation of translations) {
@@ -769,10 +743,7 @@ export default class I18nPlugin extends I18nBase {
769
743
  if (this.options.completeAdapter) {
770
744
  this.options.completeAdapter.validate();
771
745
  }
772
- for (const lang of this.options.supportedLanguages) {
773
- if (lang === 'en') {
774
- continue;
775
- }
746
+ for (const lang of this.targetLanguages()) {
776
747
  const column = resourceConfig.columns.find(c => c.name === this.trFieldNames[lang]);
777
748
  if (!column) {
778
749
  throw new Error(`Field ${this.trFieldNames[lang]} not found for storing translation for language ${lang}
@@ -807,66 +778,43 @@ export default class I18nPlugin extends I18nBase {
807
778
  // in this plugin we will use plugin to fill the database with missing language messages
808
779
  if (!this.externalAppOnly) {
809
780
  this.tryProcessAndWatch(adminforth);
810
- adminforth.tr = (msg, category, lang, params, pluralizationNumber) => __awaiter(this, void 0, void 0, function* () {
811
- if (!msg) {
812
- return msg;
813
- }
781
+ this.installBackendTranslator(adminforth, (msg, category, lang) => __awaiter(this, void 0, void 0, function* () {
814
782
  if (category === 'frontend') {
815
783
  throw new Error(`Category 'frontend' is reserved for frontend messages, use any other category for backend messages`);
816
784
  }
817
- // console.log('🪲tr', msg, category, lang);
818
- lang = this.resolveLanguage(lang);
819
- let result;
820
785
  // try to get translation from cache
821
- const cacheKey = `${resourceConfig.resourceId}:${category}:${lang}:${msg}`;
786
+ const cacheKey = this.messageCacheKey(category, lang, msg);
822
787
  const cached = yield this.cache.get(cacheKey);
823
788
  if (cached) {
824
- result = cached;
825
- }
826
- if (!result) {
827
- const resource = adminforth.resource(resourceConfig.resourceId);
828
- const translation = yield resource.get([Filters.EQ(this.enFieldName, msg), Filters.EQ(this.options.categoryFieldName, category)]);
829
- if (!translation) {
830
- yield resource.create({
831
- [this.enFieldName]: msg,
832
- [this.options.categoryFieldName]: category,
833
- });
834
- this.updateUntranslatedMenuBadge();
835
- }
836
- // do this check here, to faster register missing translations
837
- // also not cache it - no sense to cache english strings
838
- if (lang === 'en') {
839
- // set to cache to return faster next time
840
- result = msg;
841
- }
842
- else {
843
- result = translation === null || translation === void 0 ? void 0 : translation[this.trFieldNames[lang]];
844
- if (!result) {
845
- // return english
846
- result = msg;
847
- }
848
- }
849
- // cache so even if key does not exist, we will not hit database
850
- yield this.cache.set(cacheKey, result);
789
+ return cached;
790
+ }
791
+ const resource = adminforth.resource(resourceConfig.resourceId);
792
+ const translation = yield resource.get([Filters.EQ(this.enFieldName, msg), Filters.EQ(this.options.categoryFieldName, category)]);
793
+ if (!translation) {
794
+ yield resource.create({
795
+ [this.enFieldName]: msg,
796
+ [this.options.categoryFieldName]: category,
797
+ });
798
+ this.updateUntranslatedMenuBadge();
851
799
  }
852
- return this.formatTranslation(msg, result, lang, params, pluralizationNumber);
853
- });
800
+ // do this check here, to faster register missing translations
801
+ const result = lang === SOURCE_LANGUAGE ? msg : (translation === null || translation === void 0 ? void 0 : translation[this.trFieldNames[lang]]) || msg;
802
+ // cache so even if key does not exist, we will not hit database
803
+ yield this.cache.set(cacheKey, result);
804
+ return result;
805
+ }));
854
806
  }
855
807
  }
856
808
  getCategoryTranslations(category, lang) {
857
809
  return __awaiter(this, void 0, void 0, function* () {
858
810
  const resource = this.adminforth.resource(this.resourceConfig.resourceId);
859
- const cacheKey = `${this.resourceConfig.resourceId}:${category}:${lang}`;
811
+ const cacheKey = this.categoryCacheKey(category, lang);
860
812
  const cached = yield this.cache.get(cacheKey);
861
813
  if (cached) {
862
814
  return cached;
863
815
  }
864
- const translations = {};
865
816
  const allTranslations = yield resource.list([Filters.EQ(this.options.categoryFieldName, category)]);
866
- for (const tr of allTranslations) {
867
- const translatedValue = tr[this.trFieldNames[lang]];
868
- translations[tr[this.enFieldName]] = translatedValue || tr[this.enFieldName];
869
- }
817
+ const translations = this.fallbackToOriginal(Object.fromEntries(allTranslations.map((tr) => [tr[this.enFieldName], tr[this.trFieldNames[lang]]])));
870
818
  yield this.cache.set(cacheKey, translations);
871
819
  return translations;
872
820
  });
@@ -1049,4 +997,4 @@ export default class I18nPlugin extends I18nBase {
1049
997
  });
1050
998
  }
1051
999
  }
1052
- export { I18nBase, getCountryCodeFromLangCode, getPrimaryLanguageCode, isValidSupportedLanguageTag, requiresSlavicPlurals, SOURCE_LANGUAGE, PLURAL_SEPARATOR, SLAVIC_PLURAL_FORMS } from './base.js';
1000
+ export { I18nBase, getCountryCodeFromLangCode, getPrimaryLanguageCode, isValidSupportedLanguageTag, requiresSlavicPlurals, SOURCE_LANGUAGE, PLURAL_SEPARATOR, SLAVIC_PLURAL_FORMS, SLAVIC_PLURAL_FORMS_HINT } from './base.js';