@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.
- package/base.ts +175 -12
- package/build.log +2 -2
- package/custom/BulkActionButton.vue +2 -2
- package/custom/LanguageInUserMenu.vue +4 -12
- package/custom/LanguageUnderLogin.vue +4 -12
- package/custom/TranslationJobViewComponent.vue +2 -3
- package/custom/langCommon.ts +12 -24
- package/dist/base.d.ts +8 -0
- package/dist/base.js +172 -12
- package/dist/custom/BulkActionButton.vue +2 -2
- package/dist/custom/LanguageInUserMenu.vue +4 -12
- package/dist/custom/LanguageUnderLogin.vue +4 -12
- package/dist/custom/TranslationJobViewComponent.vue +2 -3
- package/dist/custom/langCommon.ts +12 -24
- package/dist/index.d.ts +6 -2
- package/dist/index.js +53 -105
- package/index.ts +61 -106
- package/package.json +1 -1
|
@@ -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-${
|
|
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-${
|
|
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-${
|
|
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,
|
|
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-${
|
|
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-${
|
|
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,
|
|
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-${
|
|
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
|
-
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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.
|
|
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[
|
|
170
|
-
this.fullCompleatedFieldValue = this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ===
|
|
642
|
-
// all strings are in
|
|
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.
|
|
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
|
|
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 =
|
|
786
|
+
const cacheKey = this.messageCacheKey(category, lang, msg);
|
|
822
787
|
const cached = yield this.cache.get(cacheKey);
|
|
823
788
|
if (cached) {
|
|
824
|
-
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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';
|