itube-specs 0.0.946 → 0.0.947
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/components/card/category.vue +2 -1
- package/components/card/model-mini.vue +5 -3
- package/components/category/list.vue +2 -1
- package/components/info/grid.vue +2 -1
- package/components/info/main.vue +19 -3
- package/components/layout/language.vue +9 -1
- package/composables/__tests__/use-client-country.test.ts +27 -27
- package/composables/use-client-country.ts +22 -20
- package/lib/countries/__tests__/countries.test.ts +60 -0
- package/lib/countries/de.json +251 -0
- package/lib/countries/en.json +251 -0
- package/lib/countries/es.json +251 -0
- package/lib/countries/fr.json +251 -0
- package/lib/countries/index.ts +51 -0
- package/lib/countries/it.json +251 -0
- package/lib/countries/ja.json +251 -0
- package/lib/countries/ko.json +251 -0
- package/lib/countries/ru.json +251 -0
- package/lib/countries/zh.json +251 -0
- package/package.json +1 -1
- package/pages/index.vue +6 -4
- package/plugins/countries.server.ts +13 -47
- package/runtime/index.ts +1 -0
- package/runtime/utils/server/__tests__/get-path-locale.test.ts +27 -0
- package/runtime/utils/server/get-path-locale.ts +10 -0
- package/utils/__tests__/iso-code-to-country-name.test.ts +30 -0
- package/utils/iso-code-to-country-name.ts +22 -0
|
@@ -21,14 +21,15 @@
|
|
|
21
21
|
<div class="card-category__wrapper-header">
|
|
22
22
|
<span class="card-category__title">
|
|
23
23
|
{{ card.title }}
|
|
24
|
+
<!-- Флаг декоративный: страна уже названа в заголовке рядом -->
|
|
24
25
|
<img
|
|
25
26
|
v-if="card.icon"
|
|
26
27
|
:src="`/icons/flags/${card.icon}.svg`"
|
|
27
|
-
:alt="card.icon"
|
|
28
28
|
class="card-category__icon"
|
|
29
29
|
width="17"
|
|
30
30
|
height="17"
|
|
31
31
|
loading="lazy"
|
|
32
|
+
alt=""
|
|
32
33
|
>
|
|
33
34
|
</span>
|
|
34
35
|
<span
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
<div class="card-model-mini__wrapper">
|
|
14
14
|
<p class="card-model-mini__name">
|
|
15
15
|
{{ card.title }}
|
|
16
|
+
<!-- Название страны текстом рядом не продублировано, поэтому оно в `alt`.
|
|
17
|
+
Форма как в CardModel: `name` — код для файла флага, `title` — читаемое имя. -->
|
|
16
18
|
<img
|
|
17
|
-
v-if="card.country"
|
|
18
|
-
:alt="card.country"
|
|
19
|
-
:src="`/icons/flags/${card.country}.svg`"
|
|
19
|
+
v-if="card.country?.name"
|
|
20
|
+
:alt="card.country.title"
|
|
21
|
+
:src="`/icons/flags/${card.country.name}.svg`"
|
|
20
22
|
class="card-model-mini__flag"
|
|
21
23
|
width="16"
|
|
22
24
|
height="12"
|
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
class="category-list__link"
|
|
10
10
|
>
|
|
11
11
|
{{ category.title }}
|
|
12
|
+
<!-- Флаг декоративный: страна уже названа в тексте ссылки -->
|
|
12
13
|
<img
|
|
13
14
|
v-if="category.icon"
|
|
14
15
|
:src="`/icons/flags/${category.icon}.svg`"
|
|
15
|
-
:alt="category.icon"
|
|
16
16
|
class="category-list__link-image"
|
|
17
17
|
width="17"
|
|
18
18
|
height="17"
|
|
19
19
|
loading="lazy"
|
|
20
|
+
alt=""
|
|
20
21
|
>
|
|
21
22
|
</NuxtLink>
|
|
22
23
|
<NuxtLink
|
package/components/info/grid.vue
CHANGED
|
@@ -41,13 +41,14 @@
|
|
|
41
41
|
]"
|
|
42
42
|
class="info-grid__item-value"
|
|
43
43
|
>
|
|
44
|
+
<!-- Флаг декоративный: страна названа в `value.title` ниже -->
|
|
44
45
|
<img
|
|
45
46
|
v-if="item.name === 'nationality_iso_codes' && isoCodes"
|
|
46
47
|
:src="`/icons/flags/${isoCodes[vIndex]}.svg`"
|
|
47
|
-
:alt="isoCodes[vIndex]"
|
|
48
48
|
class="info-grid__item-flag"
|
|
49
49
|
width="20"
|
|
50
50
|
height="14"
|
|
51
|
+
alt=""
|
|
51
52
|
>
|
|
52
53
|
<UiIcon
|
|
53
54
|
v-else-if="item.icon"
|
package/components/info/main.vue
CHANGED
|
@@ -77,11 +77,13 @@
|
|
|
77
77
|
size="14"
|
|
78
78
|
/>
|
|
79
79
|
<span class="info-main__born-text">{{ card.birthSummary }}</span>
|
|
80
|
+
<!-- Национальность нигде больше не подписана — флаг единственный её носитель,
|
|
81
|
+
поэтому в `alt` идёт название страны, а не ISO-код. -->
|
|
80
82
|
<img
|
|
81
83
|
v-for="(item, index) in card.nationalityIsoCodes"
|
|
82
84
|
:key="`nationalityIsoCodes-${index}`"
|
|
83
85
|
:src="`/icons/flags/${item}.svg`"
|
|
84
|
-
:alt="item"
|
|
86
|
+
:alt="isoCodeToCountryName(item, locale)"
|
|
85
87
|
class="info-main__born-flag"
|
|
86
88
|
width="20"
|
|
87
89
|
height="14"
|
|
@@ -157,8 +159,9 @@ import type {
|
|
|
157
159
|
} from '../../types';
|
|
158
160
|
import { AuthorizationApiService } from '../../services/api/authorization.service';
|
|
159
161
|
import { groupParametersByGroup } from '../../utils/info-main/group-parameters-by-group';
|
|
162
|
+
import { isoCodeToCountryName } from '../../utils/iso-code-to-country-name';
|
|
160
163
|
|
|
161
|
-
const { t } = useI18n();
|
|
164
|
+
const { t, locale } = useI18n();
|
|
162
165
|
|
|
163
166
|
const props = defineProps<{
|
|
164
167
|
card: ICardInfo
|
|
@@ -211,13 +214,26 @@ function getParameter(name: string) {
|
|
|
211
214
|
return props.card.parameters?.find(p => p.name === name)?.values?.[ 0 ]?.name;
|
|
212
215
|
}
|
|
213
216
|
|
|
217
|
+
/**
|
|
218
|
+
* `IModelCard.country` — пара «код файла флага + читаемое имя», как её отдаёт
|
|
219
|
+
* cleanModelCard. Раньше сюда уезжал целиком массив `nationalityIsoCodes`: у модели с
|
|
220
|
+
* двумя национальностями путь к флагу выходил `/icons/flags/us,gb.svg`, а у модели без
|
|
221
|
+
* национальностей пустой массив проходил `v-if` и давал битую картинку. Берём первый код.
|
|
222
|
+
*/
|
|
223
|
+
const shareCountry = computed(() => {
|
|
224
|
+
const iso = props.card.nationalityIsoCodes?.[0] || '';
|
|
225
|
+
if (!iso) return undefined;
|
|
226
|
+
|
|
227
|
+
return { name: iso, title: isoCodeToCountryName(iso, locale.value) };
|
|
228
|
+
});
|
|
229
|
+
|
|
214
230
|
const shareModelCard = computed<IModelCard>(() => ({
|
|
215
231
|
title: props.card.title,
|
|
216
232
|
videosCount: props.card.videosCount,
|
|
217
233
|
thumbUrl: props.card.img || '',
|
|
218
234
|
guid: props.card.guid || '',
|
|
219
235
|
parameters: [],
|
|
220
|
-
country:
|
|
236
|
+
country: shareCountry.value,
|
|
221
237
|
age: getParameter('age'),
|
|
222
238
|
}));
|
|
223
239
|
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
|
|
133
133
|
<script setup lang="ts">
|
|
134
134
|
import type { ISelectItem } from '../../types';
|
|
135
|
+
import { isoCodeToCountryName } from '../../utils/iso-code-to-country-name';
|
|
135
136
|
|
|
136
137
|
const { locale, locales } = useI18n();
|
|
137
138
|
const switchLocalePath = useSwitchLocalePath();
|
|
@@ -143,14 +144,21 @@ defineProps<{
|
|
|
143
144
|
}>();
|
|
144
145
|
|
|
145
146
|
const {
|
|
147
|
+
code: clientCountryCode,
|
|
146
148
|
icon: clientCountryIcon,
|
|
147
|
-
|
|
149
|
+
place: clientCountryPlace,
|
|
148
150
|
flagSrc: clientCountryFlag,
|
|
149
151
|
countriesItems,
|
|
150
152
|
setCountry,
|
|
151
153
|
loadCountries,
|
|
152
154
|
} = useClientCountry();
|
|
153
155
|
|
|
156
|
+
// Именительный падеж считает ICU, своей таблицы имён нет. Пустой `place` = служебный код
|
|
157
|
+
// гео-заголовка (`XX`, `T1`, `AP`), такую «страну» не подписываем.
|
|
158
|
+
const clientCountryName = computed(() => (
|
|
159
|
+
clientCountryPlace.value ? isoCodeToCountryName(clientCountryCode.value, locale.value) : ''
|
|
160
|
+
));
|
|
161
|
+
|
|
154
162
|
const isCountryOpen = ref(false);
|
|
155
163
|
const countryValue = ref('');
|
|
156
164
|
const filteredCountries = computed(() => {
|
|
@@ -9,7 +9,8 @@ vi.mock('../../services/api/dictionaries.service', () => ({
|
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
11
|
const setCode = (code: string) => useState<string>('clientCountryCode', () => '').value = code;
|
|
12
|
-
|
|
12
|
+
/** Признак реальной страны: ставит SSR-плагин из lib/countries */
|
|
13
|
+
const setPlace = (place: string) => useState<string>('clientCountryPlace', () => '').value = place;
|
|
13
14
|
|
|
14
15
|
describe('useClientCountry', () => {
|
|
15
16
|
beforeEach(() => {
|
|
@@ -17,17 +18,17 @@ describe('useClientCountry', () => {
|
|
|
17
18
|
getCountries.mockReset();
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
it('без кода не отдаёт ни флаг, ни
|
|
21
|
-
const { icon,
|
|
21
|
+
it('без кода не отдаёт ни флаг, ни обстоятельство места', () => {
|
|
22
|
+
const { icon, place, flagSrc } = useClientCountry();
|
|
22
23
|
|
|
23
24
|
expect(icon.value).toBe('');
|
|
24
|
-
expect(
|
|
25
|
+
expect(place.value).toBe('');
|
|
25
26
|
expect(flagSrc.value).toBe('');
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
it('строит иконку и путь к флагу из кода', () => {
|
|
29
30
|
setCode('ad');
|
|
30
|
-
|
|
31
|
+
setPlace('в Андорре');
|
|
31
32
|
const { icon, flagSrc } = useClientCountry();
|
|
32
33
|
|
|
33
34
|
expect(icon.value).toBe('ad');
|
|
@@ -36,7 +37,7 @@ describe('useClientCountry', () => {
|
|
|
36
37
|
|
|
37
38
|
it('нормализует код: регистр и пробелы', () => {
|
|
38
39
|
setCode(' AD ');
|
|
39
|
-
|
|
40
|
+
setPlace('в Андорре');
|
|
40
41
|
const { icon, flagSrc } = useClientCountry();
|
|
41
42
|
|
|
42
43
|
expect(icon.value).toBe('ad');
|
|
@@ -45,7 +46,7 @@ describe('useClientCountry', () => {
|
|
|
45
46
|
|
|
46
47
|
it('учитывает страны, где код флага отличается от ISO-кода', () => {
|
|
47
48
|
setCode('cn');
|
|
48
|
-
|
|
49
|
+
setPlace('в Китае');
|
|
49
50
|
const { icon, flagSrc } = useClientCountry();
|
|
50
51
|
|
|
51
52
|
expect(icon.value).toBe('zh');
|
|
@@ -53,19 +54,28 @@ describe('useClientCountry', () => {
|
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
it('не показывает флаг для служебного кода, которого нет в словаре', () => {
|
|
56
|
-
// Cloudflare отдаёт XX для неизвестного IP и T1 для Tor — файла такого флага
|
|
57
|
+
// Cloudflare отдаёт XX для неизвестного IP и T1 для Tor — файла такого флага нет.
|
|
58
|
+
// Реальность страны определяет непустой `place`: по Intl.DisplayNames не проверить,
|
|
59
|
+
// она вернёт `XX` как `XX`, а `US` в en-локали — как `US`
|
|
57
60
|
setCode('xx');
|
|
58
|
-
const { icon,
|
|
61
|
+
const { icon, place, flagSrc } = useClientCountry();
|
|
59
62
|
|
|
60
63
|
expect(icon.value).toBe('xx');
|
|
61
|
-
expect(
|
|
64
|
+
expect(place.value).toBe('');
|
|
62
65
|
expect(flagSrc.value).toBe('');
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
it('
|
|
68
|
+
it('обстоятельство места берёт из состояния, поставленного SSR', () => {
|
|
69
|
+
setCode('cz');
|
|
70
|
+
setPlace('в Чехии');
|
|
71
|
+
const { place } = useClientCountry();
|
|
72
|
+
|
|
73
|
+
expect(place.value).toBe('в Чехии');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('загруженный список стран флаг не ломает', async () => {
|
|
66
77
|
setCode('ad');
|
|
67
|
-
|
|
68
|
-
setResolvedName('USA');
|
|
78
|
+
setPlace('в Андорре');
|
|
69
79
|
getCountries.mockResolvedValue({
|
|
70
80
|
countries: [
|
|
71
81
|
{ title: 'Andorra', isoCodes: ['AD', 'AND'] },
|
|
@@ -73,30 +83,20 @@ describe('useClientCountry', () => {
|
|
|
73
83
|
],
|
|
74
84
|
});
|
|
75
85
|
|
|
76
|
-
const {
|
|
86
|
+
const { flagSrc, loadCountries } = useClientCountry();
|
|
77
87
|
await loadCountries();
|
|
78
88
|
|
|
79
|
-
expect(name.value).toBe('Andorra');
|
|
80
89
|
expect(flagSrc.value).toBe('/icons/flags/ad.svg');
|
|
81
90
|
});
|
|
82
91
|
|
|
83
|
-
it('
|
|
84
|
-
setCode('ad');
|
|
85
|
-
setResolvedName('Andorra');
|
|
86
|
-
const { name } = useClientCountry();
|
|
87
|
-
|
|
88
|
-
expect(name.value).toBe('Andorra');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('выбор страны меняет и код, и имя', () => {
|
|
92
|
+
it('выбор страны меняет код и флаг', () => {
|
|
92
93
|
setCode('ad');
|
|
93
|
-
|
|
94
|
-
const { code,
|
|
94
|
+
setPlace('в Андорре');
|
|
95
|
+
const { code, flagSrc, setCountry } = useClientCountry();
|
|
95
96
|
|
|
96
97
|
setCountry({ title: 'USA', value: 'USA', icon: 'us', key: 'us' });
|
|
97
98
|
|
|
98
99
|
expect(code.value).toBe('us');
|
|
99
|
-
expect(name.value).toBe('USA');
|
|
100
100
|
expect(flagSrc.value).toBe('/icons/flags/us.svg');
|
|
101
101
|
});
|
|
102
102
|
|
|
@@ -7,17 +7,23 @@ const COOKIE_OPTS = { maxAge: 60 * 60 * 24 * 365, path: '/', secure: true, sameS
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Единый источник страны клиента: ISO-код (гео-заголовок прокси или ручной выбор
|
|
10
|
-
* в селекторе), иконка флага и
|
|
10
|
+
* в селекторе), иконка флага и обстоятельство места для заголовков.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* Всё резолвится ОТ ТЕКУЩЕГО кода — своего кеша нет. Раньше имя страны лежало в отдельной
|
|
13
|
+
* куке `clientCountryName` рядом с парной `clientCountryNameCode`, и пара могла разъехаться:
|
|
14
|
+
* заголовок писал одну страну, флаг и селектор показывали другую (реальный кейс на проде:
|
|
15
|
+
* `clientCountryName=USA` при коде `ad`).
|
|
16
|
+
*
|
|
17
|
+
* Имени тут нет намеренно: именительный падеж один в один отдаёт `Intl.DisplayNames`, см.
|
|
18
|
+
* utils/iso-code-to-country-name.ts — потребитель зовёт её со своей локалью.
|
|
16
19
|
*/
|
|
17
20
|
export const useClientCountry = () => {
|
|
18
21
|
const code = useState<string>('clientCountryCode', () => '');
|
|
19
|
-
const resolvedName = useState<string>('clientCountryName', () => '');
|
|
20
22
|
const countriesItems = useState<ISelectItem[]>('countriesItems', () => []);
|
|
23
|
+
// Обстоятельство места («в Чехии») ставит только SSR-плагин из lib/countries: косвенных
|
|
24
|
+
// падежей и артиклей в ECMA-402 нет. Ручной выбор страны в селекторе перезагружает
|
|
25
|
+
// страницу, поэтому клиенту словарь не нужен.
|
|
26
|
+
const place = useState<string>('clientCountryPlace', () => '');
|
|
21
27
|
const codeCookie = useCookie<string | null>('clientCountryCode', COOKIE_OPTS);
|
|
22
28
|
|
|
23
29
|
/** ISO-код → имя файла флага (у части стран код локали отличается от кода страны) */
|
|
@@ -28,22 +34,18 @@ export const useClientCountry = () => {
|
|
|
28
34
|
|
|
29
35
|
const icon = computed(() => toIcon(code.value));
|
|
30
36
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Флаг только для страны, которую смогли назвать: гео-заголовок умеет отдавать
|
|
39
|
-
// служебные коды (Cloudflare `XX` для неизвестного IP и `T1` для Tor, MaxMind `AP`),
|
|
40
|
-
// их нет ни в словаре стран, ни среди файлов флагов — вышла бы битая картинка.
|
|
41
|
-
const flagSrc = computed(() => (icon.value && name.value ? `/icons/flags/${icon.value}.svg` : ''));
|
|
37
|
+
// Флаг только для страны, которая есть в словаре: гео-заголовок умеет присылать служебные
|
|
38
|
+
// коды (Cloudflare `XX` для неизвестного IP и `T1` для Tor, MaxMind `AP`) — файла такого
|
|
39
|
+
// флага нет, вышла бы битая картинка. Непустой `place` и есть этот признак: по
|
|
40
|
+
// `Intl.DisplayNames` не проверить, она вернёт `XX` как `XX`, а `US` в en-локали — как `US`.
|
|
41
|
+
const flagSrc = computed(() => (icon.value && place.value ? `/icons/flags/${icon.value}.svg` : ''));
|
|
42
42
|
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Ручной выбор страны в селекторе: наружу переживает перезагрузку только код. `place`
|
|
45
|
+
* пересчитает следующий SSR — layout/language.vue тут же уходит в `location.href`.
|
|
46
|
+
*/
|
|
44
47
|
const setCountry = (item: ISelectItem) => {
|
|
45
48
|
code.value = item.key || '';
|
|
46
|
-
resolvedName.value = item.title;
|
|
47
49
|
codeCookie.value = code.value || null;
|
|
48
50
|
};
|
|
49
51
|
|
|
@@ -69,5 +71,5 @@ export const useClientCountry = () => {
|
|
|
69
71
|
}
|
|
70
72
|
};
|
|
71
73
|
|
|
72
|
-
return { code, icon,
|
|
74
|
+
return { code, icon, place, flagSrc, countriesItems, setCountry, loadCountries };
|
|
73
75
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getCountryPlace } from '../index';
|
|
3
|
+
import en from '../en.json';
|
|
4
|
+
|
|
5
|
+
describe('getCountryPlace', () => {
|
|
6
|
+
it('отдаёт обстоятельство места с предлогом и падежом', async () => {
|
|
7
|
+
expect(await getCountryPlace('cz', 'ru')).toBe('в Чехии');
|
|
8
|
+
expect(await getCountryPlace('cu', 'ru')).toBe('на Кубе');
|
|
9
|
+
expect(await getCountryPlace('fr', 'ru')).toBe('во Франции');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('учитывает артикли и сочленённые предлоги других локалей', async () => {
|
|
13
|
+
expect(await getCountryPlace('us', 'en')).toBe('in the United States');
|
|
14
|
+
expect(await getCountryPlace('ph', 'de')).toBe('auf den Philippinen');
|
|
15
|
+
expect(await getCountryPlace('us', 'it')).toBe('negli Stati Uniti');
|
|
16
|
+
expect(await getCountryPlace('us', 'fr')).toBe('aux États-Unis');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('для локалей без предлога отдаёт именительный падеж — частица живёт в шаблоне', async () => {
|
|
20
|
+
expect(await getCountryPlace('cz', 'ja')).toBe('チェコ');
|
|
21
|
+
expect(await getCountryPlace('cz', 'ko')).toBe('체코');
|
|
22
|
+
expect(await getCountryPlace('cz', 'zh')).toBe('捷克');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('нормализует код: регистр и пробелы', async () => {
|
|
26
|
+
expect(await getCountryPlace(' CZ ', 'ru')).toBe('в Чехии');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('на пустом и служебном коде отдаёт пустую строку', async () => {
|
|
30
|
+
expect(await getCountryPlace('', 'ru')).toBe('');
|
|
31
|
+
// Cloudflare XX (неизвестный IP) и T1 (Tor), MaxMind AP — не ISO-страны
|
|
32
|
+
expect(await getCountryPlace('xx', 'ru')).toBe('');
|
|
33
|
+
expect(await getCountryPlace('t1', 'ru')).toBe('');
|
|
34
|
+
expect(await getCountryPlace('ap', 'ru')).toBe('');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('неизвестную локаль резолвит английским словарём', async () => {
|
|
38
|
+
expect(await getCountryPlace('cz', 'pt')).toBe('in Czechia');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('словари покрывают все локали одним набором кодов', async () => {
|
|
42
|
+
const codes = Object.keys(en).sort();
|
|
43
|
+
|
|
44
|
+
for (const locale of ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh']) {
|
|
45
|
+
const { default: places } = await import(`../${locale}.json`);
|
|
46
|
+
|
|
47
|
+
expect(Object.keys(places).sort(), locale).toEqual(codes);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('именительного падежа не держит — его отдаёт ICU один в один', async () => {
|
|
52
|
+
for (const locale of ['de', 'en', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh']) {
|
|
53
|
+
const { default: places } = await import(`../${locale}.json`);
|
|
54
|
+
const displayNames = new Intl.DisplayNames([locale], { type: 'region' });
|
|
55
|
+
const missing = Object.keys(places).filter(code => !displayNames.of(code.toUpperCase()));
|
|
56
|
+
|
|
57
|
+
expect(missing, locale).toEqual([]);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ad": "in Andorra",
|
|
3
|
+
"ae": "in den Vereinigten Arabischen Emiraten",
|
|
4
|
+
"af": "in Afghanistan",
|
|
5
|
+
"ag": "auf Antigua und Barbuda",
|
|
6
|
+
"ai": "auf Anguilla",
|
|
7
|
+
"al": "in Albanien",
|
|
8
|
+
"am": "in Armenien",
|
|
9
|
+
"ao": "in Angola",
|
|
10
|
+
"aq": "in der Antarktis",
|
|
11
|
+
"ar": "in Argentinien",
|
|
12
|
+
"as": "in Amerikanisch-Samoa",
|
|
13
|
+
"at": "in Österreich",
|
|
14
|
+
"au": "in Australien",
|
|
15
|
+
"aw": "auf Aruba",
|
|
16
|
+
"ax": "auf den Ålandinseln",
|
|
17
|
+
"az": "in Aserbaidschan",
|
|
18
|
+
"ba": "in Bosnien und Herzegowina",
|
|
19
|
+
"bb": "auf Barbados",
|
|
20
|
+
"bd": "in Bangladesch",
|
|
21
|
+
"be": "in Belgien",
|
|
22
|
+
"bf": "in Burkina Faso",
|
|
23
|
+
"bg": "in Bulgarien",
|
|
24
|
+
"bh": "in Bahrain",
|
|
25
|
+
"bi": "in Burundi",
|
|
26
|
+
"bj": "in Benin",
|
|
27
|
+
"bl": "auf St. Barthélemy",
|
|
28
|
+
"bm": "auf Bermuda",
|
|
29
|
+
"bn": "in Brunei Darussalam",
|
|
30
|
+
"bo": "in Bolivien",
|
|
31
|
+
"bq": "in den Karibischen Niederlanden",
|
|
32
|
+
"br": "in Brasilien",
|
|
33
|
+
"bs": "auf den Bahamas",
|
|
34
|
+
"bt": "in Bhutan",
|
|
35
|
+
"bv": "auf der Bouvetinsel",
|
|
36
|
+
"bw": "in Botsuana",
|
|
37
|
+
"by": "in Belarus",
|
|
38
|
+
"bz": "in Belize",
|
|
39
|
+
"ca": "in Kanada",
|
|
40
|
+
"cc": "auf den Kokosinseln",
|
|
41
|
+
"cd": "in Kongo-Kinshasa",
|
|
42
|
+
"cf": "in der Zentralafrikanischen Republik",
|
|
43
|
+
"cg": "in Kongo-Brazzaville",
|
|
44
|
+
"ch": "in der Schweiz",
|
|
45
|
+
"ci": "in Côte d’Ivoire",
|
|
46
|
+
"ck": "auf den Cookinseln",
|
|
47
|
+
"cl": "in Chile",
|
|
48
|
+
"cm": "in Kamerun",
|
|
49
|
+
"cn": "in China",
|
|
50
|
+
"co": "in Kolumbien",
|
|
51
|
+
"cr": "in Costa Rica",
|
|
52
|
+
"cu": "auf Kuba",
|
|
53
|
+
"cv": "auf Cabo Verde",
|
|
54
|
+
"cw": "auf Curaçao",
|
|
55
|
+
"cx": "auf der Weihnachtsinsel",
|
|
56
|
+
"cy": "auf Zypern",
|
|
57
|
+
"cz": "in Tschechien",
|
|
58
|
+
"de": "in Deutschland",
|
|
59
|
+
"dj": "in Dschibuti",
|
|
60
|
+
"dk": "in Dänemark",
|
|
61
|
+
"dm": "auf Dominica",
|
|
62
|
+
"do": "in der Dominikanischen Republik",
|
|
63
|
+
"dz": "in Algerien",
|
|
64
|
+
"ec": "in Ecuador",
|
|
65
|
+
"ee": "in Estland",
|
|
66
|
+
"eg": "in Ägypten",
|
|
67
|
+
"eh": "in der Westsahara",
|
|
68
|
+
"er": "in Eritrea",
|
|
69
|
+
"es": "in Spanien",
|
|
70
|
+
"et": "in Äthiopien",
|
|
71
|
+
"fi": "in Finnland",
|
|
72
|
+
"fj": "auf Fidschi",
|
|
73
|
+
"fk": "auf den Falklandinseln",
|
|
74
|
+
"fm": "in Mikronesien",
|
|
75
|
+
"fo": "auf den Färöern",
|
|
76
|
+
"fr": "in Frankreich",
|
|
77
|
+
"ga": "in Gabun",
|
|
78
|
+
"gb": "im Vereinigten Königreich",
|
|
79
|
+
"gd": "auf Grenada",
|
|
80
|
+
"ge": "in Georgien",
|
|
81
|
+
"gf": "in Französisch-Guayana",
|
|
82
|
+
"gg": "auf Guernsey",
|
|
83
|
+
"gh": "in Ghana",
|
|
84
|
+
"gi": "in Gibraltar",
|
|
85
|
+
"gl": "in Grönland",
|
|
86
|
+
"gm": "in Gambia",
|
|
87
|
+
"gn": "in Guinea",
|
|
88
|
+
"gp": "auf Guadeloupe",
|
|
89
|
+
"gq": "in Äquatorialguinea",
|
|
90
|
+
"gr": "in Griechenland",
|
|
91
|
+
"gs": "auf Südgeorgien und den Südlichen Sandwichinseln",
|
|
92
|
+
"gt": "in Guatemala",
|
|
93
|
+
"gu": "auf Guam",
|
|
94
|
+
"gw": "in Guinea-Bissau",
|
|
95
|
+
"gy": "in Guyana",
|
|
96
|
+
"hk": "in Hongkong",
|
|
97
|
+
"hm": "auf den Heard und McDonaldinseln",
|
|
98
|
+
"hn": "in Honduras",
|
|
99
|
+
"hr": "in Kroatien",
|
|
100
|
+
"ht": "auf Haiti",
|
|
101
|
+
"hu": "in Ungarn",
|
|
102
|
+
"id": "in Indonesien",
|
|
103
|
+
"ie": "in Irland",
|
|
104
|
+
"il": "in Israel",
|
|
105
|
+
"im": "auf der Isle of Man",
|
|
106
|
+
"in": "in Indien",
|
|
107
|
+
"io": "im Britischen Territorium im Indischen Ozean",
|
|
108
|
+
"iq": "im Irak",
|
|
109
|
+
"ir": "im Iran",
|
|
110
|
+
"is": "in Island",
|
|
111
|
+
"it": "in Italien",
|
|
112
|
+
"je": "auf Jersey",
|
|
113
|
+
"jm": "auf Jamaika",
|
|
114
|
+
"jo": "in Jordanien",
|
|
115
|
+
"jp": "in Japan",
|
|
116
|
+
"ke": "in Kenia",
|
|
117
|
+
"kg": "in Kirgisistan",
|
|
118
|
+
"kh": "in Kambodscha",
|
|
119
|
+
"ki": "in Kiribati",
|
|
120
|
+
"km": "auf den Komoren",
|
|
121
|
+
"kn": "auf St. Kitts und Nevis",
|
|
122
|
+
"kp": "in Nordkorea",
|
|
123
|
+
"kr": "in Südkorea",
|
|
124
|
+
"kw": "in Kuwait",
|
|
125
|
+
"ky": "auf den Kaimaninseln",
|
|
126
|
+
"kz": "in Kasachstan",
|
|
127
|
+
"la": "in Laos",
|
|
128
|
+
"lb": "im Libanon",
|
|
129
|
+
"lc": "auf St. Lucia",
|
|
130
|
+
"li": "in Liechtenstein",
|
|
131
|
+
"lk": "in Sri Lanka",
|
|
132
|
+
"lr": "in Liberia",
|
|
133
|
+
"ls": "in Lesotho",
|
|
134
|
+
"lt": "in Litauen",
|
|
135
|
+
"lu": "in Luxemburg",
|
|
136
|
+
"lv": "in Lettland",
|
|
137
|
+
"ly": "in Libyen",
|
|
138
|
+
"ma": "in Marokko",
|
|
139
|
+
"mc": "in Monaco",
|
|
140
|
+
"md": "in der Republik Moldau",
|
|
141
|
+
"me": "in Montenegro",
|
|
142
|
+
"mf": "auf St. Martin",
|
|
143
|
+
"mg": "auf Madagaskar",
|
|
144
|
+
"mh": "auf den Marshallinseln",
|
|
145
|
+
"mk": "in Nordmazedonien",
|
|
146
|
+
"ml": "in Mali",
|
|
147
|
+
"mm": "in Myanmar",
|
|
148
|
+
"mn": "in der Mongolei",
|
|
149
|
+
"mo": "in Macau",
|
|
150
|
+
"mp": "auf den Nördlichen Marianen",
|
|
151
|
+
"mq": "auf Martinique",
|
|
152
|
+
"mr": "in Mauretanien",
|
|
153
|
+
"ms": "auf Montserrat",
|
|
154
|
+
"mt": "auf Malta",
|
|
155
|
+
"mu": "auf Mauritius",
|
|
156
|
+
"mv": "auf den Malediven",
|
|
157
|
+
"mw": "in Malawi",
|
|
158
|
+
"mx": "in Mexiko",
|
|
159
|
+
"my": "in Malaysia",
|
|
160
|
+
"mz": "in Mosambik",
|
|
161
|
+
"na": "in Namibia",
|
|
162
|
+
"nc": "in Neukaledonien",
|
|
163
|
+
"ne": "im Niger",
|
|
164
|
+
"nf": "auf der Norfolkinsel",
|
|
165
|
+
"ng": "in Nigeria",
|
|
166
|
+
"ni": "in Nicaragua",
|
|
167
|
+
"nl": "in den Niederlanden",
|
|
168
|
+
"no": "in Norwegen",
|
|
169
|
+
"np": "in Nepal",
|
|
170
|
+
"nr": "auf Nauru",
|
|
171
|
+
"nu": "auf Niue",
|
|
172
|
+
"nz": "in Neuseeland",
|
|
173
|
+
"om": "im Oman",
|
|
174
|
+
"pa": "in Panama",
|
|
175
|
+
"pe": "in Peru",
|
|
176
|
+
"pf": "in Französisch-Polynesien",
|
|
177
|
+
"pg": "in Papua-Neuguinea",
|
|
178
|
+
"ph": "auf den Philippinen",
|
|
179
|
+
"pk": "in Pakistan",
|
|
180
|
+
"pl": "in Polen",
|
|
181
|
+
"pm": "auf St. Pierre und Miquelon",
|
|
182
|
+
"pn": "auf den Pitcairninseln",
|
|
183
|
+
"pr": "in Puerto Rico",
|
|
184
|
+
"ps": "in den Palästinensischen Autonomiegebieten",
|
|
185
|
+
"pt": "in Portugal",
|
|
186
|
+
"pw": "in Palau",
|
|
187
|
+
"py": "in Paraguay",
|
|
188
|
+
"qa": "in Katar",
|
|
189
|
+
"re": "auf Réunion",
|
|
190
|
+
"ro": "in Rumänien",
|
|
191
|
+
"rs": "in Serbien",
|
|
192
|
+
"ru": "in Russland",
|
|
193
|
+
"rw": "in Ruanda",
|
|
194
|
+
"sa": "in Saudi-Arabien",
|
|
195
|
+
"sb": "auf den Salomonen",
|
|
196
|
+
"sc": "auf den Seychellen",
|
|
197
|
+
"sd": "im Sudan",
|
|
198
|
+
"se": "in Schweden",
|
|
199
|
+
"sg": "in Singapur",
|
|
200
|
+
"sh": "auf St. Helena",
|
|
201
|
+
"si": "in Slowenien",
|
|
202
|
+
"sj": "auf Spitzbergen und Jan Mayen",
|
|
203
|
+
"sk": "in der Slowakei",
|
|
204
|
+
"sl": "in Sierra Leone",
|
|
205
|
+
"sm": "in San Marino",
|
|
206
|
+
"sn": "im Senegal",
|
|
207
|
+
"so": "in Somalia",
|
|
208
|
+
"sr": "in Suriname",
|
|
209
|
+
"ss": "im Südsudan",
|
|
210
|
+
"st": "auf São Tomé und Príncipe",
|
|
211
|
+
"sv": "in El Salvador",
|
|
212
|
+
"sx": "auf Sint Maarten",
|
|
213
|
+
"sy": "in Syrien",
|
|
214
|
+
"sz": "in Eswatini",
|
|
215
|
+
"tc": "auf den Turks- und Caicosinseln",
|
|
216
|
+
"td": "im Tschad",
|
|
217
|
+
"tf": "in den Französischen Süd- und Antarktisgebieten",
|
|
218
|
+
"tg": "in Togo",
|
|
219
|
+
"th": "in Thailand",
|
|
220
|
+
"tj": "in Tadschikistan",
|
|
221
|
+
"tk": "auf Tokelau",
|
|
222
|
+
"tl": "in Timor-Leste",
|
|
223
|
+
"tm": "in Turkmenistan",
|
|
224
|
+
"tn": "in Tunesien",
|
|
225
|
+
"to": "auf Tonga",
|
|
226
|
+
"tr": "in der Türkei",
|
|
227
|
+
"tt": "auf Trinidad und Tobago",
|
|
228
|
+
"tv": "auf Tuvalu",
|
|
229
|
+
"tw": "in Taiwan",
|
|
230
|
+
"tz": "in Tansania",
|
|
231
|
+
"ua": "in der Ukraine",
|
|
232
|
+
"ug": "in Uganda",
|
|
233
|
+
"um": "auf den Amerikanischen Überseeinseln",
|
|
234
|
+
"us": "in den Vereinigten Staaten",
|
|
235
|
+
"uy": "in Uruguay",
|
|
236
|
+
"uz": "in Usbekistan",
|
|
237
|
+
"va": "in der Vatikanstadt",
|
|
238
|
+
"vc": "auf St. Vincent und den Grenadinen",
|
|
239
|
+
"ve": "in Venezuela",
|
|
240
|
+
"vg": "auf den Britischen Jungferninseln",
|
|
241
|
+
"vi": "auf den Amerikanischen Jungferninseln",
|
|
242
|
+
"vn": "in Vietnam",
|
|
243
|
+
"vu": "in Vanuatu",
|
|
244
|
+
"wf": "auf Wallis und Futuna",
|
|
245
|
+
"ws": "auf Samoa",
|
|
246
|
+
"ye": "im Jemen",
|
|
247
|
+
"yt": "auf Mayotte",
|
|
248
|
+
"za": "in Südafrika",
|
|
249
|
+
"zm": "in Sambia",
|
|
250
|
+
"zw": "in Simbabwe"
|
|
251
|
+
}
|