@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
package/base.ts
CHANGED
|
@@ -11,19 +11,21 @@ const REGION_CODE_RE = /^[A-Z]{2}$/;
|
|
|
11
11
|
export const SOURCE_LANGUAGE = 'en';
|
|
12
12
|
export const PLURAL_SEPARATOR = '|';
|
|
13
13
|
export const SLAVIC_PLURAL_FORMS = 4;
|
|
14
|
+
export const SLAVIC_PLURAL_FORMS_HINT = 'zero | singular | 2-4 | 5+';
|
|
14
15
|
|
|
16
|
+
// forms in SLAVIC_PLURAL_FORMS_HINT order
|
|
15
17
|
export const SLAVIC_PLURAL_EXAMPLES = {
|
|
16
|
-
uk: 'яблук | яблуко | яблука | яблук',
|
|
17
|
-
bg: 'ябълки | ябълка | ябълки | ябълки',
|
|
18
|
-
cs: 'jablek | jablko | jablka | jablek',
|
|
19
|
-
hr: 'jabuka | jabuka | jabuke | jabuka',
|
|
20
|
-
mk: 'јаболка | јаболко | јаболка | јаболка',
|
|
21
|
-
pl: 'jabłek | jabłko | jabłka | jabłek',
|
|
22
|
-
sk: 'jabĺk | jablko | jablká | jabĺk',
|
|
23
|
-
sl: 'jabolk | jabolko | jabolka | jabolk',
|
|
24
|
-
sr: 'јабука | јабука | јабуке | јабука',
|
|
25
|
-
be: 'яблыкаў | яблык | яблыкі | яблыкаў',
|
|
26
|
-
ru: 'яблок | яблоко | яблока | яблок',
|
|
18
|
+
uk: 'яблук | яблуко | яблука | яблук',
|
|
19
|
+
bg: 'ябълки | ябълка | ябълки | ябълки',
|
|
20
|
+
cs: 'jablek | jablko | jablka | jablek',
|
|
21
|
+
hr: 'jabuka | jabuka | jabuke | jabuka',
|
|
22
|
+
mk: 'јаболка | јаболко | јаболка | јаболка',
|
|
23
|
+
pl: 'jabłek | jabłko | jabłka | jabłek',
|
|
24
|
+
sk: 'jabĺk | jablko | jablká | jabĺk',
|
|
25
|
+
sl: 'jabolk | jabolko | jabolka | jabolk',
|
|
26
|
+
sr: 'јабука | јабука | јабуке | јабука',
|
|
27
|
+
be: 'яблыкаў | яблык | яблыкі | яблыкаў',
|
|
28
|
+
ru: 'яблок | яблоко | яблока | яблок',
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const countryISO31661ByLangISO6391 = {
|
|
@@ -36,7 +38,142 @@ const countryISO31661ByLangISO6391 = {
|
|
|
36
38
|
uk: 'ua', // Ukrainian → Ukraine
|
|
37
39
|
ur: 'pk', // Urdu → Pakistan
|
|
38
40
|
sr: 'rs', // Serbian → Serbia
|
|
39
|
-
da: 'dk' // Danish → Denmark
|
|
41
|
+
da: 'dk', // Danish → Denmark
|
|
42
|
+
sv: 'se', // Swedish → Sweden
|
|
43
|
+
he: 'il', // Hebrew → Israel
|
|
44
|
+
el: 'gr', // Greek → Greece
|
|
45
|
+
cs: 'cz', // Czech → Czech Republic
|
|
46
|
+
vi: 'vn', // Vietnamese → Vietnam
|
|
47
|
+
aa: 'et', // Afar → Ethiopia
|
|
48
|
+
ab: 'ge', // Abkhaz → Georgia
|
|
49
|
+
ae: 'ir', // Avestan → Iran
|
|
50
|
+
af: 'za', // Afrikaans → South Africa
|
|
51
|
+
ak: 'gh', // Akan → Ghana
|
|
52
|
+
am: 'et', // Amharic → Ethiopia
|
|
53
|
+
an: 'es', // Aragonese → Spain
|
|
54
|
+
as: 'in', // Assamese → India
|
|
55
|
+
av: 'ru', // Avaric → Russia
|
|
56
|
+
ay: 'bo', // Aymara → Bolivia
|
|
57
|
+
ba: 'ru', // Bashkir → Russia
|
|
58
|
+
be: 'by', // Belarusian → Belarus
|
|
59
|
+
bi: 'vu', // Bislama → Vanuatu
|
|
60
|
+
bm: 'ml', // Bambara → Mali
|
|
61
|
+
bn: 'bd', // Bengali → Bangladesh
|
|
62
|
+
bo: 'cn', // Tibetan → China
|
|
63
|
+
br: 'fr', // Breton → France
|
|
64
|
+
bs: 'ba', // Bosnian → Bosnia & Herzegovina
|
|
65
|
+
ca: 'es', // Catalan → Spain
|
|
66
|
+
ce: 'ru', // Chechen → Russia
|
|
67
|
+
ch: 'gu', // Chamorro → Guam
|
|
68
|
+
co: 'fr', // Corsican → France
|
|
69
|
+
cr: 'ca', // Cree → Canada
|
|
70
|
+
cu: 'ru', // Old Church Slavonic → Russia
|
|
71
|
+
cv: 'ru', // Chuvash → Russia
|
|
72
|
+
cy: 'gb', // Welsh → United Kingdom
|
|
73
|
+
dv: 'mv', // Divehi → Maldives
|
|
74
|
+
dz: 'bt', // Dzongkha → Bhutan
|
|
75
|
+
ee: 'gh', // Ewe → Ghana
|
|
76
|
+
et: 'ee', // Estonian → Estonia
|
|
77
|
+
eu: 'es', // Basque → Spain
|
|
78
|
+
fa: 'ir', // Persian → Iran
|
|
79
|
+
ff: 'sn', // Fula → Senegal
|
|
80
|
+
fy: 'nl', // Western Frisian → Netherlands
|
|
81
|
+
ga: 'ie', // Irish → Ireland
|
|
82
|
+
gd: 'gb', // Scottish Gaelic → United Kingdom
|
|
83
|
+
gl: 'es', // Galician → Spain
|
|
84
|
+
gn: 'py', // Guaraní → Paraguay
|
|
85
|
+
gu: 'in', // Gujarati → India
|
|
86
|
+
gv: 'im', // Manx → Isle of Man
|
|
87
|
+
ha: 'ng', // Hausa → Nigeria
|
|
88
|
+
ho: 'pg', // Hiri Motu → Papua New Guinea
|
|
89
|
+
hy: 'am', // Armenian → Armenia
|
|
90
|
+
hz: 'na', // Herero → Namibia
|
|
91
|
+
ie: 'ee', // Interlingue → Estonia
|
|
92
|
+
ig: 'ng', // Igbo → Nigeria
|
|
93
|
+
ii: 'cn', // Nuosu → China
|
|
94
|
+
ik: 'us', // Inupiaq → United States
|
|
95
|
+
iu: 'ca', // Inuktitut → Canada
|
|
96
|
+
jv: 'id', // Javanese → Indonesia
|
|
97
|
+
ka: 'ge', // Georgian → Georgia
|
|
98
|
+
kg: 'cd', // Kongo → Congo - Kinshasa
|
|
99
|
+
ki: 'ke', // Kikuyu → Kenya
|
|
100
|
+
kj: 'na', // Kwanyama → Namibia
|
|
101
|
+
kk: 'kz', // Kazakh → Kazakhstan
|
|
102
|
+
kl: 'gl', // Kalaallisut → Greenland
|
|
103
|
+
km: 'kh', // Khmer → Cambodia
|
|
104
|
+
kn: 'in', // Kannada → India
|
|
105
|
+
kr: 'ng', // Kanuri → Nigeria
|
|
106
|
+
ks: 'in', // Kashmiri → India
|
|
107
|
+
ku: 'tr', // Kurdish → Türkiye
|
|
108
|
+
kv: 'ru', // Komi → Russia
|
|
109
|
+
kw: 'gb', // Cornish → United Kingdom
|
|
110
|
+
ky: 'kg', // Kyrgyz → Kyrgyzstan
|
|
111
|
+
la: 'va', // Latin → Vatican City
|
|
112
|
+
lb: 'lu', // Luxembourgish → Luxembourg
|
|
113
|
+
lg: 'ug', // Ganda → Uganda
|
|
114
|
+
li: 'nl', // Limburgish → Netherlands
|
|
115
|
+
ln: 'cd', // Lingala → Congo - Kinshasa
|
|
116
|
+
lo: 'la', // Lao → Laos
|
|
117
|
+
lu: 'cd', // Luba-Katanga → Congo - Kinshasa
|
|
118
|
+
mi: 'nz', // Māori → New Zealand
|
|
119
|
+
ml: 'in', // Malayalam → India
|
|
120
|
+
mr: 'in', // Marathi → India
|
|
121
|
+
ms: 'my', // Malay → Malaysia
|
|
122
|
+
my: 'mm', // Burmese → Myanmar (Burma)
|
|
123
|
+
na: 'nr', // Nauru → Nauru
|
|
124
|
+
nb: 'no', // Norwegian Bokmål → Norway
|
|
125
|
+
nd: 'zw', // Northern Ndebele → Zimbabwe
|
|
126
|
+
ne: 'np', // Nepali → Nepal
|
|
127
|
+
ng: 'na', // Ndonga → Namibia
|
|
128
|
+
nn: 'no', // Norwegian Nynorsk → Norway
|
|
129
|
+
nr: 'za', // Southern Ndebele → South Africa
|
|
130
|
+
nv: 'us', // Navajo → United States
|
|
131
|
+
ny: 'mw', // Chichewa → Malawi
|
|
132
|
+
oc: 'fr', // Occitan → France
|
|
133
|
+
oj: 'ca', // Ojibwe → Canada
|
|
134
|
+
om: 'et', // Oromo → Ethiopia
|
|
135
|
+
or: 'in', // Oriya → India
|
|
136
|
+
os: 'ge', // Ossetian → Georgia
|
|
137
|
+
pa: 'in', // Panjabi → India
|
|
138
|
+
pi: 'gb', // Pāli → United Kingdom
|
|
139
|
+
ps: 'af', // Pashto → Afghanistan
|
|
140
|
+
qu: 'pe', // Quechua → Peru
|
|
141
|
+
rm: 'ch', // Romansh → Switzerland
|
|
142
|
+
rn: 'bi', // Kirundi → Burundi
|
|
143
|
+
sa: 'in', // Sanskrit → India
|
|
144
|
+
sc: 'it', // Sardinian → Italy
|
|
145
|
+
sd: 'pk', // Sindhi → Pakistan
|
|
146
|
+
se: 'no', // Northern Sami → Norway
|
|
147
|
+
sg: 'cf', // Sango → Central African Republic
|
|
148
|
+
si: 'lk', // Sinhala → Sri Lanka
|
|
149
|
+
sl: 'si', // Slovenian → Slovenia
|
|
150
|
+
sm: 'ws', // Samoan → Samoa
|
|
151
|
+
sn: 'zw', // Shona → Zimbabwe
|
|
152
|
+
sq: 'al', // Albanian → Albania
|
|
153
|
+
ss: 'za', // Swati → South Africa
|
|
154
|
+
st: 'za', // Southern Sotho → South Africa
|
|
155
|
+
su: 'id', // Sundanese → Indonesia
|
|
156
|
+
sw: 'tz', // Swahili → Tanzania
|
|
157
|
+
ta: 'in', // Tamil → India
|
|
158
|
+
te: 'in', // Telugu → India
|
|
159
|
+
tg: 'tj', // Tajik → Tajikistan
|
|
160
|
+
ti: 'et', // Tigrinya → Ethiopia
|
|
161
|
+
tk: 'tm', // Turkmen → Turkmenistan
|
|
162
|
+
tl: 'ph', // Tagalog → Philippines
|
|
163
|
+
tn: 'za', // Tswana → South Africa
|
|
164
|
+
ts: 'za', // Tsonga → South Africa
|
|
165
|
+
tt: 'ru', // Tatar → Russia
|
|
166
|
+
tw: 'gh', // Twi → Ghana
|
|
167
|
+
ty: 'pf', // Tahitian → French Polynesia
|
|
168
|
+
ug: 'cn', // Uyghur → China
|
|
169
|
+
ve: 'za', // Venda → South Africa
|
|
170
|
+
wa: 'be', // Walloon → Belgium
|
|
171
|
+
wo: 'sn', // Wolof → Senegal
|
|
172
|
+
xh: 'za', // Xhosa → South Africa
|
|
173
|
+
yi: 'ua', // Yiddish → Ukraine
|
|
174
|
+
yo: 'ng', // Yoruba → Nigeria
|
|
175
|
+
za: 'cn', // Zhuang → China
|
|
176
|
+
zu: 'za', // Zulu → South Africa
|
|
40
177
|
};
|
|
41
178
|
|
|
42
179
|
export function getCountryCodeFromLangCode(lang: SupportedLanguage) {
|
|
@@ -98,6 +235,31 @@ export abstract class I18nBase<O extends I18nBaseOptions = I18nBaseOptions> exte
|
|
|
98
235
|
|
|
99
236
|
abstract getCategoryTranslations(category: string, lang: string): Promise<Record<string, string>>;
|
|
100
237
|
|
|
238
|
+
// languages translations are written for: strings in source language are the keys themselves
|
|
239
|
+
targetLanguages(): SupportedLanguage[] {
|
|
240
|
+
return this.options.supportedLanguages.filter((lang) => lang !== SOURCE_LANGUAGE);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// empty translation means "not translated yet", original string is shown instead
|
|
244
|
+
fallbackToOriginal(messages: Record<string, string>): Record<string, string> {
|
|
245
|
+
return Object.fromEntries(Object.entries(messages).map(([key, value]) => [key, value || key]));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Translates backend strings (menu, resource labels, errors) with adminforth.tr,
|
|
250
|
+
* subclass provides only translation lookup in its storage for already resolved language
|
|
251
|
+
*/
|
|
252
|
+
installBackendTranslator(adminforth: IAdminForth, lookup: (msg: string, category: string, lang: string) => Promise<string | undefined>) {
|
|
253
|
+
adminforth.tr = async (msg, category, lang, params, pluralizationNumber) => {
|
|
254
|
+
if (!msg) {
|
|
255
|
+
return msg;
|
|
256
|
+
}
|
|
257
|
+
const resolvedLang = this.resolveLanguage(lang);
|
|
258
|
+
const translation = await lookup(msg, category, resolvedLang);
|
|
259
|
+
return this.formatTranslation(msg, translation || msg, resolvedLang, params, pluralizationNumber);
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
101
263
|
// validate each supported language: ISO 639-1 or BCP-47 with region (e.g., en-GB)
|
|
102
264
|
validateSupportedLanguages() {
|
|
103
265
|
this.options.supportedLanguages.forEach((lang) => {
|
|
@@ -118,6 +280,7 @@ export abstract class I18nBase<O extends I18nBaseOptions = I18nBaseOptions> exte
|
|
|
118
280
|
code: lang,
|
|
119
281
|
// lang name on on language native name
|
|
120
282
|
name: iso6391.getNativeName(getPrimaryLanguageCode(lang)),
|
|
283
|
+
countryCode: getCountryCodeFromLangCode(lang),
|
|
121
284
|
}
|
|
122
285
|
))
|
|
123
286
|
};
|
package/build.log
CHANGED
|
@@ -20,5 +20,5 @@ custom/package.json
|
|
|
20
20
|
custom/pnpm-lock.yaml
|
|
21
21
|
custom/tsconfig.json
|
|
22
22
|
|
|
23
|
-
sent
|
|
24
|
-
total size is
|
|
23
|
+
sent 47,235 bytes received 305 bytes 95,080.00 bytes/sec
|
|
24
|
+
total size is 46,083 speedup is 0.97
|
|
@@ -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;
|
package/custom/langCommon.ts
CHANGED
|
@@ -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/base.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { I18nBaseOptions, SupportedLanguage } from './types.js';
|
|
|
4
4
|
export declare const SOURCE_LANGUAGE = "en";
|
|
5
5
|
export declare const PLURAL_SEPARATOR = "|";
|
|
6
6
|
export declare const SLAVIC_PLURAL_FORMS = 4;
|
|
7
|
+
export declare const SLAVIC_PLURAL_FORMS_HINT = "zero | singular | 2-4 | 5+";
|
|
7
8
|
export declare const SLAVIC_PLURAL_EXAMPLES: {
|
|
8
9
|
uk: string;
|
|
9
10
|
bg: string;
|
|
@@ -34,6 +35,13 @@ export declare abstract class I18nBase<O extends I18nBaseOptions = I18nBaseOptio
|
|
|
34
35
|
constructor(options: O);
|
|
35
36
|
instanceUniqueRepresentation(): string;
|
|
36
37
|
abstract getCategoryTranslations(category: string, lang: string): Promise<Record<string, string>>;
|
|
38
|
+
targetLanguages(): SupportedLanguage[];
|
|
39
|
+
fallbackToOriginal(messages: Record<string, string>): Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* Translates backend strings (menu, resource labels, errors) with adminforth.tr,
|
|
42
|
+
* subclass provides only translation lookup in its storage for already resolved language
|
|
43
|
+
*/
|
|
44
|
+
installBackendTranslator(adminforth: IAdminForth, lookup: (msg: string, category: string, lang: string) => Promise<string | undefined>): void;
|
|
37
45
|
validateSupportedLanguages(): void;
|
|
38
46
|
injectLanguageSelectors(adminforth: IAdminForth): void;
|
|
39
47
|
resolveLanguage(lang: string): string;
|
package/dist/base.js
CHANGED
|
@@ -16,18 +16,20 @@ const REGION_CODE_RE = /^[A-Z]{2}$/;
|
|
|
16
16
|
export const SOURCE_LANGUAGE = 'en';
|
|
17
17
|
export const PLURAL_SEPARATOR = '|';
|
|
18
18
|
export const SLAVIC_PLURAL_FORMS = 4;
|
|
19
|
+
export const SLAVIC_PLURAL_FORMS_HINT = 'zero | singular | 2-4 | 5+';
|
|
20
|
+
// forms in SLAVIC_PLURAL_FORMS_HINT order
|
|
19
21
|
export const SLAVIC_PLURAL_EXAMPLES = {
|
|
20
|
-
uk: 'яблук | яблуко | яблука | яблук',
|
|
21
|
-
bg: 'ябълки | ябълка | ябълки | ябълки',
|
|
22
|
-
cs: 'jablek | jablko | jablka | jablek',
|
|
23
|
-
hr: 'jabuka | jabuka | jabuke | jabuka',
|
|
24
|
-
mk: 'јаболка | јаболко | јаболка | јаболка',
|
|
25
|
-
pl: 'jabłek | jabłko | jabłka | jabłek',
|
|
26
|
-
sk: 'jabĺk | jablko | jablká | jabĺk',
|
|
27
|
-
sl: 'jabolk | jabolko | jabolka | jabolk',
|
|
28
|
-
sr: 'јабука | јабука | јабуке | јабука',
|
|
29
|
-
be: 'яблыкаў | яблык | яблыкі | яблыкаў',
|
|
30
|
-
ru: 'яблок | яблоко | яблока | яблок',
|
|
22
|
+
uk: 'яблук | яблуко | яблука | яблук',
|
|
23
|
+
bg: 'ябълки | ябълка | ябълки | ябълки',
|
|
24
|
+
cs: 'jablek | jablko | jablka | jablek',
|
|
25
|
+
hr: 'jabuka | jabuka | jabuke | jabuka',
|
|
26
|
+
mk: 'јаболка | јаболко | јаболка | јаболка',
|
|
27
|
+
pl: 'jabłek | jabłko | jabłka | jabłek',
|
|
28
|
+
sk: 'jabĺk | jablko | jablká | jabĺk',
|
|
29
|
+
sl: 'jabolk | jabolko | jabolka | jabolk',
|
|
30
|
+
sr: 'јабука | јабука | јабуке | јабука',
|
|
31
|
+
be: 'яблыкаў | яблык | яблыкі | яблыкаў',
|
|
32
|
+
ru: 'яблок | яблоко | яблока | яблок',
|
|
31
33
|
};
|
|
32
34
|
const countryISO31661ByLangISO6391 = {
|
|
33
35
|
en: 'us', // English → United States
|
|
@@ -39,7 +41,142 @@ const countryISO31661ByLangISO6391 = {
|
|
|
39
41
|
uk: 'ua', // Ukrainian → Ukraine
|
|
40
42
|
ur: 'pk', // Urdu → Pakistan
|
|
41
43
|
sr: 'rs', // Serbian → Serbia
|
|
42
|
-
da: 'dk' // Danish → Denmark
|
|
44
|
+
da: 'dk', // Danish → Denmark
|
|
45
|
+
sv: 'se', // Swedish → Sweden
|
|
46
|
+
he: 'il', // Hebrew → Israel
|
|
47
|
+
el: 'gr', // Greek → Greece
|
|
48
|
+
cs: 'cz', // Czech → Czech Republic
|
|
49
|
+
vi: 'vn', // Vietnamese → Vietnam
|
|
50
|
+
aa: 'et', // Afar → Ethiopia
|
|
51
|
+
ab: 'ge', // Abkhaz → Georgia
|
|
52
|
+
ae: 'ir', // Avestan → Iran
|
|
53
|
+
af: 'za', // Afrikaans → South Africa
|
|
54
|
+
ak: 'gh', // Akan → Ghana
|
|
55
|
+
am: 'et', // Amharic → Ethiopia
|
|
56
|
+
an: 'es', // Aragonese → Spain
|
|
57
|
+
as: 'in', // Assamese → India
|
|
58
|
+
av: 'ru', // Avaric → Russia
|
|
59
|
+
ay: 'bo', // Aymara → Bolivia
|
|
60
|
+
ba: 'ru', // Bashkir → Russia
|
|
61
|
+
be: 'by', // Belarusian → Belarus
|
|
62
|
+
bi: 'vu', // Bislama → Vanuatu
|
|
63
|
+
bm: 'ml', // Bambara → Mali
|
|
64
|
+
bn: 'bd', // Bengali → Bangladesh
|
|
65
|
+
bo: 'cn', // Tibetan → China
|
|
66
|
+
br: 'fr', // Breton → France
|
|
67
|
+
bs: 'ba', // Bosnian → Bosnia & Herzegovina
|
|
68
|
+
ca: 'es', // Catalan → Spain
|
|
69
|
+
ce: 'ru', // Chechen → Russia
|
|
70
|
+
ch: 'gu', // Chamorro → Guam
|
|
71
|
+
co: 'fr', // Corsican → France
|
|
72
|
+
cr: 'ca', // Cree → Canada
|
|
73
|
+
cu: 'ru', // Old Church Slavonic → Russia
|
|
74
|
+
cv: 'ru', // Chuvash → Russia
|
|
75
|
+
cy: 'gb', // Welsh → United Kingdom
|
|
76
|
+
dv: 'mv', // Divehi → Maldives
|
|
77
|
+
dz: 'bt', // Dzongkha → Bhutan
|
|
78
|
+
ee: 'gh', // Ewe → Ghana
|
|
79
|
+
et: 'ee', // Estonian → Estonia
|
|
80
|
+
eu: 'es', // Basque → Spain
|
|
81
|
+
fa: 'ir', // Persian → Iran
|
|
82
|
+
ff: 'sn', // Fula → Senegal
|
|
83
|
+
fy: 'nl', // Western Frisian → Netherlands
|
|
84
|
+
ga: 'ie', // Irish → Ireland
|
|
85
|
+
gd: 'gb', // Scottish Gaelic → United Kingdom
|
|
86
|
+
gl: 'es', // Galician → Spain
|
|
87
|
+
gn: 'py', // Guaraní → Paraguay
|
|
88
|
+
gu: 'in', // Gujarati → India
|
|
89
|
+
gv: 'im', // Manx → Isle of Man
|
|
90
|
+
ha: 'ng', // Hausa → Nigeria
|
|
91
|
+
ho: 'pg', // Hiri Motu → Papua New Guinea
|
|
92
|
+
hy: 'am', // Armenian → Armenia
|
|
93
|
+
hz: 'na', // Herero → Namibia
|
|
94
|
+
ie: 'ee', // Interlingue → Estonia
|
|
95
|
+
ig: 'ng', // Igbo → Nigeria
|
|
96
|
+
ii: 'cn', // Nuosu → China
|
|
97
|
+
ik: 'us', // Inupiaq → United States
|
|
98
|
+
iu: 'ca', // Inuktitut → Canada
|
|
99
|
+
jv: 'id', // Javanese → Indonesia
|
|
100
|
+
ka: 'ge', // Georgian → Georgia
|
|
101
|
+
kg: 'cd', // Kongo → Congo - Kinshasa
|
|
102
|
+
ki: 'ke', // Kikuyu → Kenya
|
|
103
|
+
kj: 'na', // Kwanyama → Namibia
|
|
104
|
+
kk: 'kz', // Kazakh → Kazakhstan
|
|
105
|
+
kl: 'gl', // Kalaallisut → Greenland
|
|
106
|
+
km: 'kh', // Khmer → Cambodia
|
|
107
|
+
kn: 'in', // Kannada → India
|
|
108
|
+
kr: 'ng', // Kanuri → Nigeria
|
|
109
|
+
ks: 'in', // Kashmiri → India
|
|
110
|
+
ku: 'tr', // Kurdish → Türkiye
|
|
111
|
+
kv: 'ru', // Komi → Russia
|
|
112
|
+
kw: 'gb', // Cornish → United Kingdom
|
|
113
|
+
ky: 'kg', // Kyrgyz → Kyrgyzstan
|
|
114
|
+
la: 'va', // Latin → Vatican City
|
|
115
|
+
lb: 'lu', // Luxembourgish → Luxembourg
|
|
116
|
+
lg: 'ug', // Ganda → Uganda
|
|
117
|
+
li: 'nl', // Limburgish → Netherlands
|
|
118
|
+
ln: 'cd', // Lingala → Congo - Kinshasa
|
|
119
|
+
lo: 'la', // Lao → Laos
|
|
120
|
+
lu: 'cd', // Luba-Katanga → Congo - Kinshasa
|
|
121
|
+
mi: 'nz', // Māori → New Zealand
|
|
122
|
+
ml: 'in', // Malayalam → India
|
|
123
|
+
mr: 'in', // Marathi → India
|
|
124
|
+
ms: 'my', // Malay → Malaysia
|
|
125
|
+
my: 'mm', // Burmese → Myanmar (Burma)
|
|
126
|
+
na: 'nr', // Nauru → Nauru
|
|
127
|
+
nb: 'no', // Norwegian Bokmål → Norway
|
|
128
|
+
nd: 'zw', // Northern Ndebele → Zimbabwe
|
|
129
|
+
ne: 'np', // Nepali → Nepal
|
|
130
|
+
ng: 'na', // Ndonga → Namibia
|
|
131
|
+
nn: 'no', // Norwegian Nynorsk → Norway
|
|
132
|
+
nr: 'za', // Southern Ndebele → South Africa
|
|
133
|
+
nv: 'us', // Navajo → United States
|
|
134
|
+
ny: 'mw', // Chichewa → Malawi
|
|
135
|
+
oc: 'fr', // Occitan → France
|
|
136
|
+
oj: 'ca', // Ojibwe → Canada
|
|
137
|
+
om: 'et', // Oromo → Ethiopia
|
|
138
|
+
or: 'in', // Oriya → India
|
|
139
|
+
os: 'ge', // Ossetian → Georgia
|
|
140
|
+
pa: 'in', // Panjabi → India
|
|
141
|
+
pi: 'gb', // Pāli → United Kingdom
|
|
142
|
+
ps: 'af', // Pashto → Afghanistan
|
|
143
|
+
qu: 'pe', // Quechua → Peru
|
|
144
|
+
rm: 'ch', // Romansh → Switzerland
|
|
145
|
+
rn: 'bi', // Kirundi → Burundi
|
|
146
|
+
sa: 'in', // Sanskrit → India
|
|
147
|
+
sc: 'it', // Sardinian → Italy
|
|
148
|
+
sd: 'pk', // Sindhi → Pakistan
|
|
149
|
+
se: 'no', // Northern Sami → Norway
|
|
150
|
+
sg: 'cf', // Sango → Central African Republic
|
|
151
|
+
si: 'lk', // Sinhala → Sri Lanka
|
|
152
|
+
sl: 'si', // Slovenian → Slovenia
|
|
153
|
+
sm: 'ws', // Samoan → Samoa
|
|
154
|
+
sn: 'zw', // Shona → Zimbabwe
|
|
155
|
+
sq: 'al', // Albanian → Albania
|
|
156
|
+
ss: 'za', // Swati → South Africa
|
|
157
|
+
st: 'za', // Southern Sotho → South Africa
|
|
158
|
+
su: 'id', // Sundanese → Indonesia
|
|
159
|
+
sw: 'tz', // Swahili → Tanzania
|
|
160
|
+
ta: 'in', // Tamil → India
|
|
161
|
+
te: 'in', // Telugu → India
|
|
162
|
+
tg: 'tj', // Tajik → Tajikistan
|
|
163
|
+
ti: 'et', // Tigrinya → Ethiopia
|
|
164
|
+
tk: 'tm', // Turkmen → Turkmenistan
|
|
165
|
+
tl: 'ph', // Tagalog → Philippines
|
|
166
|
+
tn: 'za', // Tswana → South Africa
|
|
167
|
+
ts: 'za', // Tsonga → South Africa
|
|
168
|
+
tt: 'ru', // Tatar → Russia
|
|
169
|
+
tw: 'gh', // Twi → Ghana
|
|
170
|
+
ty: 'pf', // Tahitian → French Polynesia
|
|
171
|
+
ug: 'cn', // Uyghur → China
|
|
172
|
+
ve: 'za', // Venda → South Africa
|
|
173
|
+
wa: 'be', // Walloon → Belgium
|
|
174
|
+
wo: 'sn', // Wolof → Senegal
|
|
175
|
+
xh: 'za', // Xhosa → South Africa
|
|
176
|
+
yi: 'ua', // Yiddish → Ukraine
|
|
177
|
+
yo: 'ng', // Yoruba → Nigeria
|
|
178
|
+
za: 'cn', // Zhuang → China
|
|
179
|
+
zu: 'za', // Zulu → South Africa
|
|
43
180
|
};
|
|
44
181
|
export function getCountryCodeFromLangCode(lang) {
|
|
45
182
|
const [langCode, region] = String(lang).split('-');
|
|
@@ -87,6 +224,28 @@ export class I18nBase extends AdminForthPlugin {
|
|
|
87
224
|
instanceUniqueRepresentation() {
|
|
88
225
|
return 'single';
|
|
89
226
|
}
|
|
227
|
+
// languages translations are written for: strings in source language are the keys themselves
|
|
228
|
+
targetLanguages() {
|
|
229
|
+
return this.options.supportedLanguages.filter((lang) => lang !== SOURCE_LANGUAGE);
|
|
230
|
+
}
|
|
231
|
+
// empty translation means "not translated yet", original string is shown instead
|
|
232
|
+
fallbackToOriginal(messages) {
|
|
233
|
+
return Object.fromEntries(Object.entries(messages).map(([key, value]) => [key, value || key]));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Translates backend strings (menu, resource labels, errors) with adminforth.tr,
|
|
237
|
+
* subclass provides only translation lookup in its storage for already resolved language
|
|
238
|
+
*/
|
|
239
|
+
installBackendTranslator(adminforth, lookup) {
|
|
240
|
+
adminforth.tr = (msg, category, lang, params, pluralizationNumber) => __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
if (!msg) {
|
|
242
|
+
return msg;
|
|
243
|
+
}
|
|
244
|
+
const resolvedLang = this.resolveLanguage(lang);
|
|
245
|
+
const translation = yield lookup(msg, category, resolvedLang);
|
|
246
|
+
return this.formatTranslation(msg, translation || msg, resolvedLang, params, pluralizationNumber);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
90
249
|
// validate each supported language: ISO 639-1 or BCP-47 with region (e.g., en-GB)
|
|
91
250
|
validateSupportedLanguages() {
|
|
92
251
|
this.options.supportedLanguages.forEach((lang) => {
|
|
@@ -105,6 +264,7 @@ export class I18nBase extends AdminForthPlugin {
|
|
|
105
264
|
code: lang,
|
|
106
265
|
// lang name on on language native name
|
|
107
266
|
name: iso6391.getNativeName(getPrimaryLanguageCode(lang)),
|
|
267
|
+
countryCode: getCountryCodeFromLangCode(lang),
|
|
108
268
|
}))
|
|
109
269
|
};
|
|
110
270
|
adminforth.config.customization.loginPageInjections.underLoginButton.push({
|