@whiteslove/parsing-lexicon 0.2.10 → 0.3.1

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.
Files changed (46) hide show
  1. package/index.d.ts +2 -1
  2. package/package.json +4 -1
  3. package/src/central-asia-locations.js +111 -45
  4. package/src/central-asia.js +14 -160
  5. package/src/geo.js +10 -9
  6. package/src/geography-central-asia.js +182 -2
  7. package/src/geography-detection.d.ts +1 -0
  8. package/src/geography-detection.js +42 -2
  9. package/src/geography-display.js +37 -9
  10. package/src/hiring-salary-context.d.ts +4 -0
  11. package/src/hiring-salary-context.js +46 -12
  12. package/src/hiring.js +2 -2
  13. package/src/housing-address.d.ts +10 -1
  14. package/src/housing-address.js +112 -10
  15. package/src/housing-context.js +6 -4
  16. package/src/housing-display.d.ts +2 -0
  17. package/src/housing-display.js +65 -0
  18. package/src/housing-features.js +19 -3
  19. package/src/housing-intent.js +6 -4
  20. package/src/housing-listing-enrichment.d.ts +65 -0
  21. package/src/housing-listing-enrichment.js +253 -0
  22. package/src/housing-listing-fields.js +57 -16
  23. package/src/housing-money.d.ts +2 -1
  24. package/src/housing-money.js +31 -3
  25. package/src/housing-poi-extensions.js +134 -0
  26. package/src/housing-source-aliases.d.ts +12 -0
  27. package/src/housing-source-aliases.js +94 -0
  28. package/src/housing-structured.d.ts +46 -2
  29. package/src/housing-structured.js +200 -22
  30. package/src/housing-text.d.ts +20 -1
  31. package/src/housing-text.js +42 -17
  32. package/src/housing.js +9 -7
  33. package/src/index.js +7 -0
  34. package/src/landmarks.js +10 -3
  35. package/src/location-data.js +410 -0
  36. package/src/location-merge.js +2 -1
  37. package/src/locations.js +110 -423
  38. package/src/tashkent-colloquial.js +14 -12
  39. package/src/tashkent-housing-geography.d.ts +10 -0
  40. package/src/tashkent-housing-geography.js +96 -7
  41. package/src/tashkent-pois.js +19 -19
  42. package/src/ua-location-extensions-major.js +30 -18
  43. package/src/ua-location-extensions-metro.js +31 -0
  44. package/src/ua-location-extensions-regional.js +11 -2
  45. package/src/ua-secondary-cities.js +1 -1
  46. package/src/uz-location-extensions.js +38 -1
package/index.d.ts CHANGED
@@ -80,7 +80,7 @@ export type SearchCluster = Readonly<{
80
80
  export type AreaEntry = Readonly<{
81
81
  canonical: string;
82
82
  name: string;
83
- type: 'local_area';
83
+ type: 'local_area' | 'microdistrict';
84
84
  country: 'UZ';
85
85
  city: 'Tashkent';
86
86
  aliases: readonly string[];
@@ -92,6 +92,7 @@ export type LocationCityDictionary = Readonly<{
92
92
  localAreas?: readonly LocationEntry[];
93
93
  suburbs?: readonly LocationEntry[];
94
94
  settlements?: readonly LocationEntry[];
95
+ developmentAreas?: readonly LocationEntry[];
95
96
  metro?: readonly LocationEntry[] | readonly MetroStation[];
96
97
  residentialComplexes?: readonly LocationEntry[];
97
98
  streets?: readonly LocationEntry[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whiteslove/parsing-lexicon",
3
- "version": "0.2.10",
3
+ "version": "0.3.1",
4
4
  "description": "Shared deterministic multilingual parsing lexicon for Whiteslove housing and hiring services",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,6 +48,9 @@
48
48
  "./housing-listing-fields": { "types": "./src/housing-listing-fields.d.ts", "import": "./src/housing-listing-fields.js" },
49
49
  "./housing-text": { "types": "./src/housing-text.d.ts", "import": "./src/housing-text.js" },
50
50
  "./housing-source-aliases": { "types": "./src/housing-source-aliases.d.ts", "import": "./src/housing-source-aliases.js" },
51
+ "./housing-poi-extensions": "./src/housing-poi-extensions.js",
52
+ "./housing-display": { "types": "./src/housing-display.d.ts", "import": "./src/housing-display.js" },
53
+ "./housing-listing-enrichment": { "types": "./src/housing-listing-enrichment.d.ts", "import": "./src/housing-listing-enrichment.js" },
51
54
  "./hiring": "./src/hiring.js",
52
55
  "./hiring-languages": "./src/hiring-languages.js",
53
56
  "./hiring-language-extensions": { "types": "./src/hiring-language-extensions.d.ts", "import": "./src/hiring-language-extensions.js" },
@@ -1,24 +1,14 @@
1
1
  import { LOCATION_DICTIONARIES } from './locations.js';
2
- import { LOCATION_LIST_KEYS, mergeLocationCountries } from './location-merge.js';
2
+ import { LOCATION_LIST_KEYS } from './location-merge.js';
3
+ import { CITIES_BY_COUNTRY, canonicalCity } from './geography.js';
3
4
  import { aliasesOf, aliasesToRegex, normalizeForMatch } from './normalization.js';
4
- import {
5
- KZ_CITY_CATALOG,
6
- UZ_CITY_CATALOG,
7
- canonicalKazakhstanCity,
8
- canonicalUzbekistanCity,
9
- } from './central-asia.js';
10
- import { KZ_AMBIGUOUS_LOCAL_NAMES, KZ_LOCATION_EXTENSIONS, KZ_SEARCH_CLUSTERS } from './kz-location-extensions.js';
11
- import { UZ_AMBIGUOUS_LOCAL_NAMES, UZ_LOCATION_EXTENSIONS } from './uz-location-extensions.js';
12
-
13
- export const KZ_EXPANDED_LOCATION_DICTIONARIES = mergeLocationCountries(
14
- LOCATION_DICTIONARIES.KZ || {},
15
- KZ_LOCATION_EXTENSIONS,
16
- );
17
-
18
- export const UZ_EXPANDED_LOCATION_DICTIONARIES = mergeLocationCountries(
19
- LOCATION_DICTIONARIES.UZ || {},
20
- UZ_LOCATION_EXTENSIONS,
21
- );
5
+ import { KZ_AMBIGUOUS_LOCAL_NAMES, KZ_SEARCH_CLUSTERS } from './kz-location-extensions.js';
6
+ import { UZ_AMBIGUOUS_LOCAL_NAMES } from './uz-location-extensions.js';
7
+
8
+ // Compatibility names retained for consumers. The canonical dictionaries are
9
+ // assembled once in locations.js; matcher layers must consume, not re-merge.
10
+ export const KZ_EXPANDED_LOCATION_DICTIONARIES = LOCATION_DICTIONARIES.KZ || Object.freeze({});
11
+ export const UZ_EXPANDED_LOCATION_DICTIONARIES = LOCATION_DICTIONARIES.UZ || Object.freeze({});
22
12
 
23
13
  export const CENTRAL_ASIA_LOCATION_DICTIONARIES = Object.freeze({
24
14
  KZ: KZ_EXPANDED_LOCATION_DICTIONARIES,
@@ -32,6 +22,7 @@ const TYPE_BY_KEY = Object.freeze({
32
22
  localAreas: 'local_area',
33
23
  suburbs: 'suburb',
34
24
  settlements: 'settlement',
25
+ developmentAreas: 'development_area',
35
26
  metro: 'metro',
36
27
  residentialComplexes: 'residential_complex',
37
28
  streets: 'street',
@@ -49,21 +40,13 @@ const KZ_AMBIGUOUS = new Set(KZ_AMBIGUOUS_LOCAL_NAMES.map(normalizeForMatch));
49
40
  const UZ_AMBIGUOUS = new Set(UZ_AMBIGUOUS_LOCAL_NAMES.map(normalizeForMatch));
50
41
 
51
42
  function cityCatalog(countryCode) {
52
- return countryCode === 'KZ' ? KZ_CITY_CATALOG : countryCode === 'UZ' ? UZ_CITY_CATALOG : [];
43
+ return CITIES_BY_COUNTRY[countryCode] || [];
53
44
  }
54
45
 
55
46
  function dictionaries(countryCode) {
56
47
  return CENTRAL_ASIA_LOCATION_DICTIONARIES[countryCode] || {};
57
48
  }
58
49
 
59
- function canonicalCity(value, countryCode) {
60
- return countryCode === 'KZ'
61
- ? canonicalKazakhstanCity(value)
62
- : countryCode === 'UZ'
63
- ? canonicalUzbekistanCity(value)
64
- : null;
65
- }
66
-
67
50
  const cityRegexCache = new Map();
68
51
  function cityRegex(item) {
69
52
  const key = `${item.country}:${item.canonical}`;
@@ -91,31 +74,85 @@ function explicitCityFromText(text, countryCode) {
91
74
  return matches[0]?.item?.canonical || null;
92
75
  }
93
76
 
94
- function isExplicitMetroContext(value, match) {
95
- const start = match.index ?? 0;
96
- const end = start + match[0].length;
97
- const before = value.slice(Math.max(0, start - 36), start);
98
- const after = value.slice(end, end + 36);
99
- return /(?:метро|metro|станц(?:ия|ии)?|station|ст\.?\s*м\.?|м\.)\s*[:\-–—]?\s*$/iu.test(before)
100
- || /^\s*(?:метро|metro|station|станц(?:ия|ии)?)(?=$|[^\p{L}\p{N}_])/iu.test(after);
77
+ const CONTEXT_PATTERNS = Object.freeze({
78
+ metro: Object.freeze({
79
+ before: /(?:метро|metro|станц(?:ия|ии)?|station|ст\.?\s*м\.?)\s*[:\-–—]?\s*$/iu,
80
+ after: /^\s*[:\-–—]?\s*(?:метро|metro|станц(?:ия|ии)?|station)(?=$|[^\p{L}\p{N}_])/iu,
81
+ inside: /(?:метро|metro|станц(?:ия|ии)?|station)/iu,
82
+ }),
83
+ district: Object.freeze({
84
+ before: /(?:район|р-н|рн|туман\p{L}{0,4}|tumani|district)\s*[:\-–—]?\s*$/iu,
85
+ after: /^\s*[:\-–—]?\s*(?:район|р-н|рн|туман\p{L}{0,4}|tumani|district)(?=$|[^\p{L}\p{N}_])/iu,
86
+ }),
87
+ mahalla: Object.freeze({
88
+ before: /(?:махалл(?:а|я)|маҳалла(?:си)?|mahalla(?:si)?|mfy|мфй)\s*[:\-–—]?\s*$/iu,
89
+ after: /^\s*[:\-–—]?\s*(?:махалл(?:а|я)|маҳалла(?:си)?|mahalla(?:si)?|mfy|мфй)(?=$|[^\p{L}\p{N}_])/iu,
90
+ }),
91
+ microdistrict: Object.freeze({
92
+ before: /(?:массив(?:и)?|massiv(?:i)?|жилмассив|ж\/м|микрорайон|мкр\.?|mavze(?:si)?|мавзе(?:си)?|квартал|kvartal|daha|даха|даҳа)\s*[:\-–—]?\s*$/iu,
93
+ after: /^\s*(?:\d{1,2}[aа]?\s*)?[:\-–—]?\s*(?:массив(?:и)?|massiv(?:i)?|жилмассив|ж\/м|микрорайон|мкр\.?|mavze(?:si)?|мавзе(?:си)?|квартал|kvartal|daha|даха|даҳа)(?=$|[^\p{L}\p{N}_])/iu,
94
+ }),
95
+ local_area: Object.freeze({
96
+ before: /(?:массив(?:и)?|massiv(?:i)?|жилмассив|ж\/м|микрорайон|мкр\.?|mavze(?:si)?|мавзе(?:си)?|квартал|kvartal|daha|даха|даҳа|зона|area)\s*[:\-–—]?\s*$/iu,
97
+ after: /^\s*(?:\d{1,2}[aа]?\s*)?[:\-–—]?\s*(?:массив(?:и)?|massiv(?:i)?|жилмассив|ж\/м|микрорайон|мкр\.?|mavze(?:si)?|мавзе(?:си)?|квартал|kvartal|daha|даха|даҳа|зона|area)(?=$|[^\p{L}\p{N}_])/iu,
98
+ }),
99
+ development_area: Object.freeze({
100
+ before: /(?:(?:международн\p{L}*|international)\s+)?(?:бизнес[-\s]?центр|делов\p{L}*\s+центр|business\s+cent(?:er|re)|development\s+area|mixed[-\s]?use\s+(?:district|development))\s*[:\-–—]?\s*$/iu,
101
+ after: /^\s*[:\-–—]?\s*(?:(?:международн\p{L}*|international)\s+)?(?:бизнес[-\s]?центр|делов\p{L}*\s+центр|business\s+cent(?:er|re)|development\s+area|mixed[-\s]?use\s+(?:district|development))(?=$|[^\p{L}\p{N}_])/iu,
102
+ }),
103
+ residential_complex: Object.freeze({
104
+ before: /(?:жк|жилой\s+комплекс|residential\s+complex|residence|turar\s+joy\s+majmuasi|tjm)\s*[:\-–—]?\s*$/iu,
105
+ after: /^\s*[:\-–—]?\s*(?:жк|жилой\s+комплекс|residential\s+complex|residence|turar\s+joy\s+majmuasi|tjm)(?=$|[^\p{L}\p{N}_])/iu,
106
+ }),
107
+ street: Object.freeze({
108
+ before: /(?:улица|ул\.?|street|st\.?|ko['’ʻ]?cha(?:si)?|кўча(?:си)?)\s*[:\-–—]?\s*$/iu,
109
+ after: /^\s*[:\-–—]?\s*(?:улица|ул\.?|street|st\.?|ko['’ʻ]?cha(?:si)?|кўча(?:си)?)(?=$|[^\p{L}\p{N}_])/iu,
110
+ }),
111
+ poi: Object.freeze({
112
+ before: /(?:базар|рынок|bozor(?:i)?|мечет\p{L}*|масжид|masjid|mosque|парк|park|mall|молл|трц|тц|вокзал|аэропорт|airport|университет|university)\s*[:\-–—]?\s*$/iu,
113
+ after: /^\s*[:\-–—]?\s*(?:базар|рынок|bozor(?:i)?|мечет\p{L}*|масжид|masjid|mosque|парк|park|mall|молл|трц|тц|вокзал|аэропорт|airport|университет|university)(?=$|[^\p{L}\p{N}_])/iu,
114
+ }),
115
+ });
116
+
117
+ function hasExplicitContext(value, candidate) {
118
+ const pattern = CONTEXT_PATTERNS[candidate.type];
119
+ if (!pattern) return false;
120
+ const before = value.slice(Math.max(0, candidate.start - 40), candidate.start);
121
+ const after = value.slice(candidate.end, Math.min(value.length, candidate.end + 40));
122
+ return pattern.before.test(before) || pattern.after.test(after) || Boolean(pattern.inside?.test(candidate.matchedText));
123
+ }
124
+
125
+ function overlaps(a, b) {
126
+ return a.start < b.end && b.start < a.end;
127
+ }
128
+
129
+ function containsSpan(outer, inner) {
130
+ return outer.start <= inner.start && outer.end >= inner.end;
101
131
  }
102
132
 
103
- function metroOverlapsArea(item, data) {
104
- const itemKeys = new Set([item.name, ...(item.aliases || [])].map(normalizeForMatch).filter(Boolean));
105
- return ['microdistricts', 'localAreas'].some((key) => (data?.[key] || []).some((area) =>
106
- [area.name, ...(area.aliases || [])].map(normalizeForMatch).some((value) => itemKeys.has(value)),
107
- ));
133
+ const SEMANTIC_CHAR_RE = /[\p{L}\p{N}_]/u;
134
+ function semanticBounds(value, match) {
135
+ let start = match.index ?? 0;
136
+ let end = start + match[0].length;
137
+ while (start < end && !SEMANTIC_CHAR_RE.test(value[start])) start += 1;
138
+ while (end > start && !SEMANTIC_CHAR_RE.test(value[end - 1])) end -= 1;
139
+ return { start, end };
140
+ }
141
+
142
+ function publicMatch(candidate) {
143
+ const { start, end, matchedText, explicitContext, ...result } = candidate;
144
+ return Object.freeze(result);
108
145
  }
109
146
 
110
147
  function findEntryMatches(text, cityName, data) {
111
148
  const value = String(text || '');
112
- const matches = [];
149
+ const raw = [];
113
150
  for (const key of LOCATION_LIST_KEYS) {
114
151
  for (const item of data?.[key] || []) {
115
152
  const match = value.match(item?.re);
116
153
  if (!match) continue;
117
- if (key === 'metro' && metroOverlapsArea(item, data) && !isExplicitMetroContext(value, match)) continue;
118
- matches.push(Object.freeze({
154
+ const { start, end } = semanticBounds(value, match);
155
+ const candidate = {
119
156
  country: item.country || null,
120
157
  city: cityName,
121
158
  type: item.entityType || TYPE_BY_KEY[key] || key,
@@ -126,10 +163,39 @@ function findEntryMatches(text, cityName, data) {
126
163
  parent: item.parent || item.district || null,
127
164
  confidence: item.confidence || null,
128
165
  language: item.language || null,
129
- }));
166
+ start,
167
+ end,
168
+ matchedText: value.slice(start, end),
169
+ explicitContext: false,
170
+ };
171
+ candidate.explicitContext = hasExplicitContext(value, candidate);
172
+ raw.push(candidate);
130
173
  }
131
174
  }
132
- return matches;
175
+
176
+ const filtered = raw.filter((candidate) => {
177
+ const peers = raw.filter((other) => other !== candidate && overlaps(candidate, other));
178
+ if (candidate.explicitContext) return true;
179
+
180
+ // An explicit type marker is stronger than a same-surface alias of another type:
181
+ // "метро Минор" is a station, while "Минор махалла" is the mahalla.
182
+ if (peers.some((peer) => peer.explicitContext && peer.type !== candidate.type)) return false;
183
+
184
+ // Prefer the semantically longer phrase when one alias contains another, e.g.
185
+ // "Сергели машинный базар" over the bare district/metro token "Сергели".
186
+ const length = candidate.end - candidate.start;
187
+ if (peers.some((peer) => containsSpan(peer, candidate) && (peer.end - peer.start) > length)) return false;
188
+
189
+ // Bare station names that are also real neighborhoods/mahallas/districts are
190
+ // not enough to assert metro semantics. Explicit metro context restores them.
191
+ if (candidate.type === 'metro' && peers.some((peer) =>
192
+ ['district', 'microdistrict', 'mahalla', 'local_area'].includes(peer.type)
193
+ )) return false;
194
+
195
+ return true;
196
+ });
197
+
198
+ return filtered.map(publicMatch);
133
199
  }
134
200
 
135
201
  function isAmbiguousMatch(match, countryCode) {
@@ -1,5 +1,11 @@
1
- import { KZ_CITIES, UZ_CITIES } from './geo.js';
2
- import { aliasesOf, findCanonical, normalizeForMatch } from './normalization.js';
1
+ import {
2
+ KZ_CITY_ADDITIONS,
3
+ UZ_CITY_ADDITIONS,
4
+ } from './geography-central-asia.js';
5
+ import { CITIES_BY_COUNTRY, canonicalCity } from './geography.js';
6
+ import { aliasesOf, normalizeForMatch } from './normalization.js';
7
+
8
+ export { KZ_CITY_ADDITIONS, UZ_CITY_ADDITIONS };
3
9
 
4
10
  const freezeAliases = (aliases = {}) => Object.freeze(Object.fromEntries(
5
11
  Object.entries(aliases).map(([lang, values]) => {
@@ -20,117 +26,8 @@ const city = (canonical, aliases, extra = {}) => Object.freeze({
20
26
  aliases: freezeAliases(aliases),
21
27
  });
22
28
 
23
- function mergeCatalog(base, additions) {
24
- const order = [];
25
- const byCanonical = new Map();
26
- for (const item of [...base, ...additions]) {
27
- if (!item?.canonical) continue;
28
- if (!byCanonical.has(item.canonical)) order.push(item.canonical);
29
- const previous = byCanonical.get(item.canonical);
30
- if (!previous) {
31
- byCanonical.set(item.canonical, item);
32
- continue;
33
- }
34
- const languages = [...new Set([...Object.keys(previous.aliases || {}), ...Object.keys(item.aliases || {})])];
35
- const aliases = Object.fromEntries(languages.map((lang) => {
36
- const seen = new Set();
37
- const values = [...(previous.aliases?.[lang] || []), ...(item.aliases?.[lang] || [])].filter((alias) => {
38
- const key = normalizeForMatch(alias);
39
- if (!key || seen.has(key)) return false;
40
- seen.add(key);
41
- return true;
42
- });
43
- return [lang, values];
44
- }));
45
- const { aliases: _previousAliases, ...previousMeta } = previous;
46
- const { aliases: _itemAliases, ...itemMeta } = item;
47
- byCanonical.set(item.canonical, city(item.canonical, aliases, { ...previousMeta, ...itemMeta }));
48
- }
49
- return Object.freeze(order.map((name) => byCanonical.get(name)));
50
- }
51
-
52
- export const KZ_CITY_ADDITIONS = Object.freeze([
53
- city('Almaty', { kk: ['Алматы'], ru: ['Алма-Ата', 'Алма Ата', 'Верный'], en: ['Alma-Ata', 'Alma Ata', 'Verny'] }, { country: 'KZ', priority: 'P0' }),
54
- city('Astana', { kk: ['Астана', 'Нұр-Сұлтан', 'Нұр Сұлтан', 'Ақмола'], ru: ['Астана', 'Нур-Султан', 'Нур Султан', 'Акмолинск', 'Акмола', 'Целиноград'], en: ['Astana', 'Nur-Sultan', 'Nur Sultan', 'Akmolinsk', 'Aqmola', 'Tselinograd'] }, { country: 'KZ', priority: 'P0' }),
55
- city('Shymkent', { kk: ['Шымкент'], ru: ['Шымкент', 'Чимкент', 'Чымкент'], en: ['Shymkent', 'Chimkent'] }, { country: 'KZ', priority: 'P0' }),
56
- city('Karaganda', { kk: ['Қарағанды'], ru: ['Караганда', 'Караганды'], en: ['Karaganda', 'Qaragandy', 'Karagandy'] }, { country: 'KZ', priority: 'P1' }),
57
- city('Aktobe', { kk: ['Ақтөбе'], ru: ['Актобе', 'Актюбинск'], en: ['Aktobe', 'Aqtobe', 'Aktyubinsk'] }, { country: 'KZ', priority: 'P1' }),
58
- city('Taraz', { kk: ['Тараз', 'Жамбыл', 'Әулие-Ата'], ru: ['Тараз', 'Джамбул', 'Жамбыл', 'Аулие-Ата'], en: ['Taraz', 'Dzhambul', 'Jambyl', 'Aulie-Ata'] }, { country: 'KZ', priority: 'P1' }),
59
- city('Pavlodar', { kk: ['Павлодар'], ru: ['Павлодар'], en: ['Pavlodar'] }, { country: 'KZ', priority: 'P1' }),
60
- city('Oskemen', { kk: ['Өскемен'], ru: ['Усть-Каменогорск', 'Усть Каменогорск', 'Оскемен'], en: ['Oskemen', 'Ust-Kamenogorsk', 'Ust Kamenogorsk'] }, { country: 'KZ', priority: 'P1' }),
61
- city('Semey', { kk: ['Семей'], ru: ['Семей', 'Семипалатинск'], en: ['Semey', 'Semipalatinsk'] }, { country: 'KZ', priority: 'P1' }),
62
- city('Atyrau', { kk: ['Атырау'], ru: ['Атырау', 'Гурьев'], en: ['Atyrau', 'Guryev', 'Guriev'] }, { country: 'KZ', priority: 'P1' }),
63
- city('Aktau', { kk: ['Ақтау'], ru: ['Актау', 'Шевченко'], en: ['Aktau', 'Aqtau', 'Shevchenko'] }, { country: 'KZ', priority: 'P1' }),
64
- city('Kostanay', { kk: ['Қостанай'], ru: ['Костанай', 'Кустанай'], en: ['Kostanay', 'Qostanay', 'Kustanay'] }, { country: 'KZ', priority: 'P2' }),
65
- city('Kyzylorda', { kk: ['Қызылорда', 'Ақмешіт'], ru: ['Кызылорда', 'Ак-Мечеть', 'Перовск'], en: ['Kyzylorda', 'Qyzylorda', 'Kzyl-Orda', 'Ak-Mechet', 'Perovsk'] }, { country: 'KZ', priority: 'P2' }),
66
- city('Oral', { kk: ['Орал'], ru: ['Орал', 'Уральск'], en: ['Oral', 'Uralsk'] }, { country: 'KZ', priority: 'P2' }),
67
- city('Petropavl', { kk: ['Петропавл'], ru: ['Петропавловск', 'Петропавл'], en: ['Petropavl', 'Petropavlovsk'] }, { country: 'KZ', priority: 'P2' }),
68
- city('Taldykorgan', { kk: ['Талдықорған'], ru: ['Талдыкорган', 'Талды-Курган'], en: ['Taldykorgan', 'Taldyqorgan', 'Taldy-Kurgan'] }, { country: 'KZ', priority: 'P2' }),
69
- city('Turkistan', { kk: ['Түркістан'], ru: ['Туркестан'], en: ['Turkistan', 'Turkestan'] }, { country: 'KZ', priority: 'P2' }),
70
- city('Kokshetau', { kk: ['Көкшетау'], ru: ['Кокшетау', 'Кокчетав'], en: ['Kokshetau', 'Kokchetav'] }, { country: 'KZ', priority: 'P2' }),
71
- city('Temirtau', { kk: ['Теміртау'], ru: ['Темиртау'], en: ['Temirtau'] }, { country: 'KZ', priority: 'P3' }),
72
- city('Ekibastuz', { kk: ['Екібастұз'], ru: ['Экибастуз'], en: ['Ekibastuz'] }, { country: 'KZ', priority: 'P3' }),
73
- city('Rudny', { kk: ['Рудный'], ru: ['Рудный'], en: ['Rudny', 'Rudnyi'] }, { country: 'KZ', priority: 'P3' }),
74
- city('Zhezkazgan', { kk: ['Жезқазған'], ru: ['Жезказган'], en: ['Zhezkazgan', 'Jezkazgan', 'Dzhezkazgan'] }, { country: 'KZ', priority: 'P3' }),
75
- city('Balkhash', { kk: ['Балқаш'], ru: ['Балхаш'], en: ['Balkhash', 'Balqash'] }, { country: 'KZ', priority: 'P3' }),
76
- city('Konaev', { kk: ['Қонаев', 'Қапшағай'], ru: ['Конаев', 'Капчагай'], en: ['Konaev', 'Qonaev', 'Kapshagay', 'Kapchagai', 'Qapshagai'] }, { country: 'KZ', priority: 'P3' }),
77
- city('Zhanaozen', { kk: ['Жаңаөзен'], ru: ['Жанаозен'], en: ['Zhanaozen', 'Zhanaözen', 'New Uzen'] }, { country: 'KZ', priority: 'P3' }),
78
- city('Satbayev', { kk: ['Сәтбаев'], ru: ['Сатпаев', 'Никольский'], en: ['Satbayev', 'Satpaev', 'Nikolsky'] }, { country: 'KZ', priority: 'P3' }),
79
- city('Kosshy', { kk: ['Қосшы'], ru: ['Косшы'], en: ['Kosshy', 'Qosshy', 'Koschi'] }, { country: 'KZ', priority: 'P3' }),
80
- city('Arys', { kk: ['Арыс'], ru: ['Арыс'], en: ['Arys'] }, { country: 'KZ', priority: 'P3' }),
81
- city('Kentau', { kk: ['Кентау'], ru: ['Кентау'], en: ['Kentau'] }, { country: 'KZ', priority: 'P3' }),
82
- city('Saryagash', { kk: ['Сарыағаш'], ru: ['Сарыагаш'], en: ['Saryagash'] }, { country: 'KZ', priority: 'P3' }),
83
- city('Stepnogorsk', { kk: ['Степногорск'], ru: ['Степногорск'], en: ['Stepnogorsk'] }, { country: 'KZ', priority: 'P4' }),
84
- city('Atbasar', { kk: ['Атбасар'], ru: ['Атбасар'], en: ['Atbasar'] }, { country: 'KZ', priority: 'P4' }),
85
- city('Shchuchinsk', { kk: ['Щучинск'], ru: ['Щучинск'], en: ['Shchuchinsk'] }, { country: 'KZ', priority: 'P4' }),
86
- city('Makinsk', { kk: ['Макинск'], ru: ['Макинск'], en: ['Makinsk'] }, { country: 'KZ', priority: 'P4' }),
87
- city('Ereymentau', { kk: ['Ерейментау'], ru: ['Ерейментау'], en: ['Ereymentau'] }, { country: 'KZ', priority: 'P4' }),
88
- city('Akkol', { kk: ['Ақкөл'], ru: ['Акколь'], en: ['Akkol', 'Aqkol'] }, { country: 'KZ', priority: 'P4' }),
89
- city('Kaskelen', { kk: ['Қаскелең'], ru: ['Каскелен'], en: ['Kaskelen'] }, { country: 'KZ', priority: 'P4' }),
90
- city('Talgar', { kk: ['Талғар'], ru: ['Талгар'], en: ['Talgar'] }, { country: 'KZ', priority: 'P4' }),
91
- city('Esik', { kk: ['Есік'], ru: ['Есик'], en: ['Esik', 'Issyk'] }, { country: 'KZ', priority: 'P4' }),
92
- city('Shelek', { kk: ['Шелек'], ru: ['Шелек'], en: ['Shelek'] }, { country: 'KZ', priority: 'P4' }),
93
- city('Uzynagash', { kk: ['Ұзынағаш'], ru: ['Узынагаш'], en: ['Uzynagash'] }, { country: 'KZ', priority: 'P4' }),
94
- city('Zharkent', { kk: ['Жаркент'], ru: ['Жаркент'], en: ['Zharkent'] }, { country: 'KZ', priority: 'P4' }),
95
- city('Tekeli', { kk: ['Текелі'], ru: ['Текели'], en: ['Tekeli'] }, { country: 'KZ', priority: 'P4' }),
96
- city('Usharal', { kk: ['Үшарал'], ru: ['Ушарал'], en: ['Usharal'] }, { country: 'KZ', priority: 'P4' }),
97
- city('Saran', { kk: ['Саран'], ru: ['Сарань'], en: ['Saran'] }, { country: 'KZ', priority: 'P4' }),
98
- city('Shakhtinsk', { kk: ['Шахтинск'], ru: ['Шахтинск'], en: ['Shakhtinsk'] }, { country: 'KZ', priority: 'P4' }),
99
- city('Abai', { kk: ['Абай'], ru: ['Абай'], en: ['Abai'] }, { country: 'KZ', priority: 'P4' }),
100
- city('Priozersk', { kk: ['Приозерск'], ru: ['Приозёрск'], en: ['Priozersk'] }, { country: 'KZ', priority: 'P4' }),
101
- city('Karkaralinsk', { kk: ['Қарқаралы'], ru: ['Каркаралинск'], en: ['Karkaralinsk', 'Karkaraly'] }, { country: 'KZ', priority: 'P4' }),
102
- city('Aksu', { kk: ['Ақсу'], ru: ['Аксу'], en: ['Aksu', 'Aqsu'] }, { country: 'KZ', priority: 'P4' }),
103
- city('Ridder', { kk: ['Риддер'], ru: ['Риддер'], en: ['Ridder'] }, { country: 'KZ', priority: 'P4' }),
104
- city('Altai', { kk: ['Алтай'], ru: ['Алтай', 'Зыряновск'], en: ['Altai', 'Zyryanovsk'] }, { country: 'KZ', priority: 'P4' }),
105
- city('Serebryansk', { kk: ['Серебрянск'], ru: ['Серебрянск'], en: ['Serebryansk'] }, { country: 'KZ', priority: 'P4' }),
106
- city('Shemonaikha', { kk: ['Шемонаиха'], ru: ['Шемонаиха'], en: ['Shemonaikha'] }, { country: 'KZ', priority: 'P4' }),
107
- city('Kurchatov', { kk: ['Курчатов'], ru: ['Курчатов'], en: ['Kurchatov'] }, { country: 'KZ', priority: 'P4' }),
108
- city('Ayagoz', { kk: ['Аягөз'], ru: ['Аягоз'], en: ['Ayagoz'] }, { country: 'KZ', priority: 'P4' }),
109
- city('Khromtau', { kk: ['Хромтау'], ru: ['Хромтау'], en: ['Khromtau'] }, { country: 'KZ', priority: 'P4' }),
110
- city('Alga', { kk: ['Алға'], ru: ['Алга'], en: ['Alga'] }, { country: 'KZ', priority: 'P4' }),
111
- city('Kandyagash', { kk: ['Қандыағаш'], ru: ['Кандыагаш'], en: ['Kandyagash'] }, { country: 'KZ', priority: 'P4' }),
112
- city('Shalkar', { kk: ['Шалқар'], ru: ['Шалкар'], en: ['Shalkar'] }, { country: 'KZ', priority: 'P4' }),
113
- city('Kulsary', { kk: ['Құлсары'], ru: ['Кульсары'], en: ['Kulsary'] }, { country: 'KZ', priority: 'P4' }),
114
- city('Dossor', { kk: ['Доссор'], ru: ['Доссор'], en: ['Dossor'] }, { country: 'KZ', priority: 'P4' }),
115
- city('Fort-Shevchenko', { kk: ['Форт-Шевченко'], ru: ['Форт-Шевченко'], en: ['Fort-Shevchenko'] }, { country: 'KZ', priority: 'P4' }),
116
- city('Lisakovsk', { kk: ['Лисаковск'], ru: ['Лисаковск'], en: ['Lisakovsk'] }, { country: 'KZ', priority: 'P4' }),
117
- city('Arkalyk', { kk: ['Арқалық'], ru: ['Аркалык'], en: ['Arkalyk'] }, { country: 'KZ', priority: 'P4' }),
118
- city('Tobyl', { kk: ['Тобыл'], ru: ['Тобыл'], en: ['Tobyl'] }, { country: 'KZ', priority: 'P4' }),
119
- city('Zhitikara', { kk: ['Жітіқара'], ru: ['Житикара'], en: ['Zhitikara'] }, { country: 'KZ', priority: 'P4' }),
120
- city('Aksai', { kk: ['Ақсай'], ru: ['Аксай'], en: ['Aksai'] }, { country: 'KZ', priority: 'P4' }),
121
- city('Baikonur', { kk: ['Байқоңыр'], ru: ['Байконур'], en: ['Baikonur'] }, { country: 'KZ', priority: 'P4', type: 'special_status_city' }),
122
- city('Aral', { kk: ['Арал'], ru: ['Аральск', 'Арал'], en: ['Aral', 'Aralsk'] }, { country: 'KZ', priority: 'P4' }),
123
- city('Kazaly', { kk: ['Қазалы'], ru: ['Казалинск', 'Казалы'], en: ['Kazaly', 'Kazalinsk'] }, { country: 'KZ', priority: 'P4' }),
124
- city('Shu', { kk: ['Шу'], ru: ['Шу'], en: ['Shu'] }, { country: 'KZ', priority: 'P4' }),
125
- city('Karatau', { kk: ['Қаратау'], ru: ['Каратау'], en: ['Karatau'] }, { country: 'KZ', priority: 'P4' }),
126
- city('Zhanatas', { kk: ['Жаңатас'], ru: ['Жанатас'], en: ['Zhanatas'] }, { country: 'KZ', priority: 'P4' }),
127
- city('Merke', { kk: ['Мерке'], ru: ['Мерке'], en: ['Merke'] }, { country: 'KZ', priority: 'P4' }),
128
- city('Zhetysai', { kk: ['Жетісай'], ru: ['Жетысай'], en: ['Zhetysai'] }, { country: 'KZ', priority: 'P4' }),
129
- city('Lenger', { kk: ['Леңгір'], ru: ['Ленгер'], en: ['Lenger'] }, { country: 'KZ', priority: 'P4' }),
130
- city('Shardara', { kk: ['Шардара'], ru: ['Шардара'], en: ['Shardara'] }, { country: 'KZ', priority: 'P4' }),
131
- ]);
132
-
133
- export const KZ_CITY_CATALOG = mergeCatalog(KZ_CITIES, KZ_CITY_ADDITIONS);
29
+ /** @deprecated Use CITIES_BY_COUNTRY.KZ / canonicalCity(). */
30
+ export const KZ_CITY_CATALOG = CITIES_BY_COUNTRY.KZ;
134
31
 
135
32
  export const KZ_SEARCH_TARGETS = Object.freeze([
136
33
  city('Burabay', { kk: ['Бурабай'], ru: ['Бурабай', 'Боровое'], en: ['Burabay', 'Borovoe'] }, { country: 'KZ', type: 'resort', priority: 'P4' }),
@@ -138,51 +35,8 @@ export const KZ_SEARCH_TARGETS = Object.freeze([
138
35
  city('Bayanaul', { kk: ['Баянауыл'], ru: ['Баянаул'], en: ['Bayanaul'] }, { country: 'KZ', type: 'resort', priority: 'P4' }),
139
36
  ]);
140
37
 
141
- export const UZ_CITY_ADDITIONS = Object.freeze([
142
- city('Tashkent', { uzLatn: ['Toshkent', 'Toshkent shahri'], uzCyrl: ['Тошкент', 'Тошкент шаҳри'], ru: ['Ташкент', 'город Ташкент'], en: ['Tashkent'] }, { country: 'UZ', priority: 'P0' }),
143
- city('Samarkand', { uzLatn: ['Samarqand', 'Samarqand shahri'], uzCyrl: ['Самарқанд'], ru: ['Самарканд', 'г. Самарканд'], en: ['Samarkand', 'Samarqand'] }, { country: 'UZ', priority: 'P0', localCanonical: 'Samarqand' }),
144
- city('Namangan', { uzLatn: ['Namangan', 'Namangan shahri'], uzCyrl: ['Наманган шаҳри'], ru: ['Наманган'], en: ['Namangan'] }, { country: 'UZ', priority: 'P0' }),
145
- city('Andijan', { uzLatn: ['Andijon', 'Andijon shahri', 'Andijan', 'Andizhan'], uzCyrl: ['Андижон'], ru: ['Андижан'], en: ['Andijan', 'Andizhan'] }, { country: 'UZ', priority: 'P0', localCanonical: 'Andijon' }),
146
- city('Fergana', { uzLatn: ["Farg'ona", 'Fargona', "Farg'ona shahri"], uzCyrl: ['Фарғона', 'Фаргона'], ru: ['Фергана'], en: ['Fergana', 'Ferghana'] }, { country: 'UZ', priority: 'P0', localCanonical: "Farg'ona" }),
147
- city('Bukhara', { uzLatn: ['Buxoro', 'Buxoro shahri'], uzCyrl: ['Бухоро'], ru: ['Бухара'], en: ['Bukhara', 'Buchara'] }, { country: 'UZ', priority: 'P0', localCanonical: 'Buxoro' }),
148
- city('Qarshi', { uzLatn: ['Qarshi', 'Qarshi shahri', 'Karshi'], uzCyrl: ['Қарши'], ru: ['Карши'], en: ['Qarshi', 'Karshi'] }, { country: 'UZ', priority: 'P0' }),
149
- city('Nukus', { uzLatn: ['Nukus', 'Nukus shahri'], uzCyrl: ['Нукус'], kaaLat: ['Nókis'], kaaCyrl: ['Нөкис', 'Нөкис қаласы'], ru: ['Нукус'], en: ['Nukus'] }, { country: 'UZ', priority: 'P0', region: "Qoraqalpog'iston" }),
150
- city('Urgench', { uzLatn: ['Urganch', 'Urganch shahri', 'Urgench'], uzCyrl: ['Урганч'], ru: ['Ургенч'], en: ['Urgench', 'Urganch'] }, { country: 'UZ', priority: 'P0', localCanonical: 'Urganch' }),
151
- city('Kokand', { uzLatn: ["Qo'qon", 'Qo‘qon', 'Qoqon'], uzCyrl: ['Қўқон'], ru: ['Коканд'], en: ['Kokand', 'Kokon'] }, { country: 'UZ', priority: 'P1', localCanonical: "Qo'qon" }),
152
- city('Margilan', { uzLatn: ["Marg'ilon", 'Marg‘ilon', 'Margilon'], uzCyrl: ['Марғилон'], ru: ['Маргилан'], en: ['Margilan', 'Marghelan'] }, { country: 'UZ', priority: 'P1', localCanonical: "Marg'ilon" }),
153
- city('Jizzakh', { uzLatn: ['Jizzax', 'Jizzakh'], uzCyrl: ['Жиззах'], ru: ['Джизак'], en: ['Jizzakh', 'Djizak'] }, { country: 'UZ', priority: 'P1', localCanonical: 'Jizzax' }),
154
- city('Navoiy', { uzLatn: ['Navoiy', 'Navoi'], uzCyrl: ['Навоий'], ru: ['Навои'], en: ['Navoiy', 'Navoi'] }, { country: 'UZ', priority: 'P1' }),
155
- city('Termez', { uzLatn: ['Termiz', 'Termiz shahri'], uzCyrl: ['Термиз'], ru: ['Термез'], en: ['Termez', 'Termiz'] }, { country: 'UZ', priority: 'P1', localCanonical: 'Termiz' }),
156
- city('Gulistan', { uzLatn: ['Guliston', 'Gulistan'], uzCyrl: ['Гулистон'], ru: ['Гулистан'], en: ['Gulistan', 'Guliston'] }, { country: 'UZ', priority: 'P1', localCanonical: 'Guliston' }),
157
- city('Chirchiq', { uzLatn: ['Chirchiq', 'Chirchik'], uzCyrl: ['Чирчиқ'], ru: ['Чирчик'], en: ['Chirchiq', 'Chirchik'] }, { country: 'UZ', priority: 'P1' }),
158
- city('Almalyk', { uzLatn: ['Olmaliq', 'Almalyk'], uzCyrl: ['Олмалиқ'], ru: ['Алмалык'], en: ['Almalyk', 'Olmaliq'] }, { country: 'UZ', priority: 'P1', localCanonical: 'Olmaliq' }),
159
- city('Angren', { uzLatn: ['Angren'], uzCyrl: ['Ангрен'], ru: ['Ангрен'], en: ['Angren'] }, { country: 'UZ', priority: 'P1' }),
160
- city('Bekabad', { uzLatn: ['Bekobod', 'Bekabad'], uzCyrl: ['Бекобод'], ru: ['Бекабад', 'Бекобод'], en: ['Bekabad', 'Bekobod'] }, { country: 'UZ', priority: 'P1', localCanonical: 'Bekobod' }),
161
- city('Shakhrisabz', { uzLatn: ['Shahrisabz', 'Shakhrisabz', 'Shahr-i Sabz'], uzCyrl: ['Шаҳрисабз'], ru: ['Шахрисабз'], en: ['Shakhrisabz', 'Shahrisabz'] }, { country: 'UZ', priority: 'P2', localCanonical: 'Shahrisabz' }),
162
- city('Khiva', { uzLatn: ['Xiva', 'Khiva'], uzCyrl: ['Хива'], ru: ['Хива'], en: ['Khiva', 'Chiwa'] }, { country: 'UZ', priority: 'P2', localCanonical: 'Xiva' }),
163
- city('Denov', { uzLatn: ['Denov', 'Denau'], uzCyrl: ['Денов'], ru: ['Денау', 'Денов'], en: ['Denov', 'Denau'] }, { country: 'UZ', priority: 'P2' }),
164
- city('Asaka', { uzLatn: ['Asaka', 'Assaka'], uzCyrl: ['Асака'], ru: ['Асака'], en: ['Asaka'] }, { country: 'UZ', priority: 'P2' }),
165
- city('Kogon', { uzLatn: ['Kogon', 'Yangi Buxoro'], uzCyrl: ['Когон'], ru: ['Каган', 'Когон', 'Новая Бухара'], en: ['Kogon', 'Kagan'] }, { country: 'UZ', priority: 'P2' }),
166
- city('Kattakurgan', { uzLatn: ["Kattaqo'rg'on", 'Kattaqo‘rg‘on', 'Kattakurgan'], uzCyrl: ['Каттақўрғон'], ru: ['Каттакурган'], en: ['Kattakurgan'] }, { country: 'UZ', priority: 'P2', localCanonical: "Kattaqo'rg'on" }),
167
- city('Urgut', { uzLatn: ['Urgut', 'Urgut shahri'], uzCyrl: ['Ургут'], ru: ['Ургут'], en: ['Urgut'] }, { country: 'UZ', priority: 'P2' }),
168
- city('Yangiyol', { uzLatn: ["Yangiyo'l", 'Yangiyo‘l', 'Yangiyul'], uzCyrl: ['Янги йўл', 'Янгийўл'], ru: ['Янгиюль'], en: ['Yangiyol', 'Yangiyul'] }, { country: 'UZ', priority: 'P2', localCanonical: "Yangiyo'l" }),
169
- city('Chust', { uzLatn: ['Chust'], uzCyrl: ['Чуст'], ru: ['Чуст'], en: ['Chust'] }, { country: 'UZ', priority: 'P3' }),
170
- city('Chartak', { uzLatn: ['Chortoq', 'Chartak'], uzCyrl: ['Чортоқ'], ru: ['Чартак'], en: ['Chartak', 'Chortoq'] }, { country: 'UZ', priority: 'P3', localCanonical: 'Chortoq' }),
171
- city('Kosonsoy', { uzLatn: ['Kosonsoy', 'Kasansay'], uzCyrl: ['Косонсой'], ru: ['Касансай', 'Косонсой'], en: ['Kosonsoy', 'Kasansay'] }, { country: 'UZ', priority: 'P3' }),
172
- city('Shahrixon', { uzLatn: ['Shahrixon', 'Shahrikhan'], uzCyrl: ['Шаҳрихон'], ru: ['Шахрихан'], en: ['Shahrixon', 'Shahrikhan'] }, { country: 'UZ', priority: 'P3' }),
173
- city('Xonobod', { uzLatn: ['Xonobod', 'Xonobod shahri'], uzCyrl: ['Хонобод'], ru: ['Ханабад', 'Хонобод'], en: ['Khanabad', 'Xonobod'] }, { country: 'UZ', priority: 'P3', contextRequired: true }),
174
- city('Khojeyli', { uzLatn: ["Xo'jayli", 'Xojayli'], uzCyrl: ['Хўжайли'], kaaLat: ['Xojeli'], ru: ['Ходжейли'], en: ['Khojeyli', 'Khodzheyli'] }, { country: 'UZ', priority: 'P3', region: "Qoraqalpog'iston" }),
175
- city('Takhiatash', { uzLatn: ['Taxiatosh'], uzCyrl: ['Тахиатош'], kaaLat: ['Taxiatas'], ru: ['Тахиаташ'], en: ['Takhiatash', 'Tahiatash'] }, { country: 'UZ', priority: 'P3', region: "Qoraqalpog'iston" }),
176
- city('Kungrad', { uzLatn: ["Qo'ng'irot", 'Qo‘ng‘irot'], uzCyrl: ['Қўнғирот'], kaaLat: ['Qońırat'], ru: ['Кунград'], en: ['Kungrad', 'Kongirat'] }, { country: 'UZ', priority: 'P3', region: "Qoraqalpog'iston" }),
177
- city('Beruniy', { uzLatn: ['Beruniy'], uzCyrl: ['Беруний'], ru: ['Беруни'], en: ['Beruniy', 'Biruni'] }, { country: 'UZ', priority: 'P3', region: "Qoraqalpog'iston" }),
178
- city('Turtkul', { uzLatn: ["To'rtko'l", 'To‘rtko‘l'], uzCyrl: ['Тўрткўл'], kaaLat: ['Tórtkúl'], ru: ['Турткуль'], en: ['Turtkul', 'Tortkul'] }, { country: 'UZ', priority: 'P3', region: "Qoraqalpog'iston" }),
179
- city('Yangiyer', { uzLatn: ['Yangiyer', 'Yangi Yer'], uzCyrl: ['Янгиер'], ru: ['Янгиер'], en: ['Yangiyer'] }, { country: 'UZ', priority: 'P3' }),
180
- city('Shirin', { uzLatn: ['Shirin'], uzCyrl: ['Ширин'], ru: ['Ширин'], en: ['Shirin'] }, { country: 'UZ', priority: 'P3' }),
181
- city('Gazalkent', { uzLatn: ["G'azalkent", 'Gazalkent'], uzCyrl: ['Ғазалкент'], ru: ['Газалкент'], en: ['Gazalkent'] }, { country: 'UZ', priority: 'P3', localCanonical: "G'azalkent" }),
182
- city('Muynak', { uzLatn: ["Mo'ynoq", 'Moynaq'], uzCyrl: ['Мўйноқ'], kaaLat: ['Moynaq'], ru: ['Муйнак'], en: ['Muynak', 'Moynak'] }, { country: 'UZ', priority: 'P4', region: "Qoraqalpog'iston" }),
183
- ]);
184
-
185
- export const UZ_CITY_CATALOG = mergeCatalog(UZ_CITIES, UZ_CITY_ADDITIONS);
38
+ /** @deprecated Use CITIES_BY_COUNTRY.UZ / canonicalCity(). */
39
+ export const UZ_CITY_CATALOG = CITIES_BY_COUNTRY.UZ;
186
40
 
187
41
  export const UZ_SEARCH_TARGETS = Object.freeze([
188
42
  city('Chorvoq', { uzLatn: ['Chorvoq', 'Charvak'], ru: ['Чарвак'], en: ['Chorvoq', 'Charvak'] }, { country: 'UZ', type: 'tourism_target', priority: 'P4' }),
@@ -213,11 +67,11 @@ export const UZ_LOCATION_TERMS = Object.freeze({
213
67
  });
214
68
 
215
69
  export function canonicalKazakhstanCity(value) {
216
- return findCanonical(value, KZ_CITY_CATALOG)?.canonical || null;
70
+ return canonicalCity(value, 'KZ');
217
71
  }
218
72
 
219
73
  export function canonicalUzbekistanCity(value) {
220
- return findCanonical(value, UZ_CITY_CATALOG)?.canonical || null;
74
+ return canonicalCity(value, 'UZ');
221
75
  }
222
76
 
223
77
  export function canonicalCentralAsiaCity(value, country = null) {
package/src/geo.js CHANGED
@@ -112,16 +112,17 @@ export function tashkentMetroLabels() {
112
112
  return Object.fromEntries(TASHKENT_METRO.map((item) => [item.name, { ru: item.labels.ru, en: item.labels.en, line: item.line }]));
113
113
  }
114
114
 
115
- const area = (name, aliases) => deepFreeze({ canonical: name, name, type: 'local_area', country: 'UZ', city: 'Tashkent', aliases });
115
+ // Historical consumer name retained, but entries now carry their actual semantic type.
116
+ const area = (name, aliases, type = 'local_area') => deepFreeze({ canonical: name, name, type, country: 'UZ', city: 'Tashkent', aliases });
116
117
 
117
118
  export const TASHKENT_AREAS = Object.freeze({
118
119
  Almazar: Object.freeze([
119
- area('Sebzar', ['себзар', 'sebzar', 'ц 17', 'ц 18', 'c 17', 'c 18']),
120
+ area('Sebzar', ['себзар', 'sebzar', 'ц 17', 'ц 18', 'c 17', 'c 18'], 'microdistrict'),
120
121
  area('Karakamysh-1/2', ['каракамыш 1 2', 'қорақамиш 1 2', 'qoraqamish 1 2', 'karakamish 1 2']),
121
122
  area('Karakamysh-2/3', ['каракамыш 2 3', 'қорақамиш 2 3', 'qoraqamish 2 3', 'karakamish 2 3']),
122
123
  area('Karakamysh-2/4', ['каракамыш 2 4', 'қорақамиш 2 4', 'qoraqamish 2 4', 'karakamish 2 4']),
123
124
  area('Karakamysh-2/5', ['каракамыш 2 5', 'қорақамиш 2 5', 'qoraqamish 2 5', 'karakamish 2 5']),
124
- area('Olympia', ['олимпия', 'olimpiya', 'olympia']), area('Vuzgorodok', ['вузгородок', 'вуз городок', 'vuzgorodok']),
125
+ area('Olympia', ['олимпия', 'olimpiya', 'olympia'], 'microdistrict'), area('Vuzgorodok', ['вузгородок', 'вуз городок', 'vuzgorodok']),
125
126
  area('Medgorodok', ['медгородок', 'мед городок', 'medgorodok']), area('Takhtapul', ['тахтапуль', 'тахтапул', 'taxtapul', 'takhtapul']),
126
127
  area('Chimbay', ['чимбай', 'chimboy', 'chimbay']),
127
128
  ]),
@@ -132,12 +133,12 @@ export const TASHKENT_AREAS = Object.freeze({
132
133
  'Mirzo Ulugbek': Object.freeze([
133
134
  area('Buyuk Ipak Yuli', ['буюк ипак йули', 'буюк ипак йўли', 'buyuk ipak yuli', "buyuk ipak yo'li", 'ц 1', 'c 1']),
134
135
  area('Alay', ['олой', 'алайский', 'алайск', 'alay', 'ц 2', 'c 2']),
135
- ...[1, 2, 3, 4, 6].map((n) => area(`Karasu-${n}`, [`карасу ${n}`, `қорасув ${n}`, `qorasuv ${n}`, `karasu ${n}`])),
136
- ...[1, 2, 3, 4].map((n) => area(`TTZ-${n}`, [`ттз ${n}`, `ttz ${n}`])),
136
+ ...[1, 2, 3, 4].map((n) => area(`Karasu-${n}`, [`карасу ${n}`, `қорасув ${n}`, `qorasuv ${n}`, `karasu ${n}`], 'microdistrict')),
137
+ area('Karasu-6', ['карасу 6', 'қорасув 6', 'qorasuv 6', 'karasu 6'], 'microdistrict'),
138
+ ...[1, 2, 3, 4].map((n) => area(`TTZ-${n}`, [`ттз ${n}`, `ttz ${n}`], 'microdistrict')),
137
139
  area('Yalangach', ['ялангач', 'yalangach', "yalang'och"]), area('Feruza', ['феруза', 'feruza']), area('Geofizika', ['геофизика', 'поселок геофизиков', 'geofizika']),
138
140
  ]),
139
141
  Sergeli: Object.freeze([
140
- area('Sergeli Car Bazaar', ['сергели машинный базар', 'сергели машина бозор', 'sergeli moshina bozor', 'sergile moshena bozor', 'sergele moshina bozor']),
141
142
  area('Yangi Sergeli', ['янги сергели', 'yangi sergeli']), area('Stroygorod', ['стройгород', 'stroygorod']),
142
143
  ]),
143
144
  Chilanzar: Object.freeze([
@@ -157,13 +158,13 @@ export const TASHKENT_AREAS = Object.freeze({
157
158
  area('Rakat', ['ракат', 'rakat']), area('Rakatboshi', ['ракатбоши', 'rakatboshi']),
158
159
  ]),
159
160
  Yangihayot: Object.freeze([
160
- area('Uzgarish', ['узгарыш', 'ўзгариш', "o'zgarish", 'uzgarish']), area('Dustlik-1', ['дустлик 1', 'дўстлик 1', "do'stlik 1", 'dustlik 1']),
161
- area('Dustlik-2', ['дустлик 2', 'дўстлик 2', "do'stlik 2", 'dustlik 2']), area('Yangi Choshtepa', ['янги чоштепа', 'yangi choshtepa']), area('Sputnik', ['спутник', 'йўлдош', "yo'ldosh", 'yoldosh']),
161
+ area('Uzgarish', ['узгарыш', 'ўзгариш', "o'zgarish", 'uzgarish']), area('Dustlik-1', ['дустлик 1', 'дўстлик 1', "do'stlik 1", 'dustlik 1'], 'microdistrict'),
162
+ area('Dustlik-2', ['дустлик 2', 'дўстлик 2', "do'stlik 2", 'dustlik 2'], 'microdistrict'), area('Yangi Choshtepa', ['янги чоштепа', 'yangi choshtepa'], 'microdistrict'), area('Sputnik', ['спутник', 'йўлдош', "yo'ldosh", 'yoldosh'], 'microdistrict'),
162
163
  ]),
163
164
  Yashnobod: Object.freeze([
164
165
  area('Kuylyuk Center', ['куйлюк центр', 'куйлик центр', "qo'yliq markaz", 'kuylyuk center']),
165
166
  ...[1, 2, 3, 4].map((n) => area(`Aviasozlar-${n}`, [`авиасозлар ${n}`, `городок авиастроителей ${n}`, `aviasozlar ${n}`])),
166
167
  ...[1, 2, 3, 4].map((n) => area(`Tuzel-${n}`, [`тузель ${n}`, `tuzel ${n}`])),
167
- area('Tashselmash', ['ташсельмаш', 'tashselmash']), area('Alimkent', ['алимкент', 'olimkent', 'alimkent']), area('Shohimardon', ['шохимардон', 'shohimardon']),
168
+ area('Tashselmash', ['ташсельмаш', 'tashselmash'], 'microdistrict'), area('Alimkent', ['алимкент', 'olimkent', 'alimkent']), area('Shohimardon', ['шохимардон', 'shohimardon']),
168
169
  ]),
169
170
  });