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.
@@ -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
@@ -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"
@@ -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: props.card.nationalityIsoCodes || '',
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
- name: clientCountryName,
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
- const setResolvedName = (name: string) => useState<string>('clientCountryName', () => '').value = name;
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, name, flagSrc } = useClientCountry();
21
+ it('без кода не отдаёт ни флаг, ни обстоятельство места', () => {
22
+ const { icon, place, flagSrc } = useClientCountry();
22
23
 
23
24
  expect(icon.value).toBe('');
24
- expect(name.value).toBe('');
25
+ expect(place.value).toBe('');
25
26
  expect(flagSrc.value).toBe('');
26
27
  });
27
28
 
28
29
  it('строит иконку и путь к флагу из кода', () => {
29
30
  setCode('ad');
30
- setResolvedName('Andorra');
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
- setResolvedName('Andorra');
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
- setResolvedName('China');
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, name, flagSrc } = useClientCountry();
61
+ const { icon, place, flagSrc } = useClientCountry();
59
62
 
60
63
  expect(icon.value).toBe('xx');
61
- expect(name.value).toBe('');
64
+ expect(place.value).toBe('');
62
65
  expect(flagSrc.value).toBe('');
63
66
  });
64
67
 
65
- it('имя следует за кодом и не может остаться от другой страны', async () => {
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 { name, flagSrc, loadCountries } = useClientCountry();
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
- setResolvedName('Andorra');
94
- const { code, name, flagSrc, setCountry } = useClientCountry();
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
- * лежало в отдельной куке `clientCountryName` рядом с парной `clientCountryNameCode`,
14
- * и пара могла разъехаться: заголовок писал одну страну, флаг и селектор показывали
15
- * другую (реальный кейс на проде: `clientCountryName=USA` при коде `ad`).
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
- // Список стран догружается лениво на клиенте (см. layout/language.vue) — если он
32
- // уже есть, имя берём из него: там оно точно совпадает с текущим кодом.
33
- const name = computed(() => {
34
- if (!icon.value) return '';
35
- return countriesItems.value.find(item => item.icon === icon.value)?.title || resolvedName.value;
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, name, flagSrc, countriesItems, setCountry, loadCountries };
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
+ }