@whiteslove/parsing-lexicon 0.5.7 → 0.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whiteslove/parsing-lexicon",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Shared deterministic multilingual parsing lexicon for WhitesLove housing and hiring services",
5
5
  "repository": {
6
6
  "type": "git",
package/src/locations.js CHANGED
@@ -15,6 +15,7 @@ import { UA_REGIONAL_LOCATION_EXTENSIONS } from './ua-location-extensions-region
15
15
  import { UA_SECONDARY_LOCATION_EXTENSIONS } from './ua-secondary-cities.js';
16
16
  import { UA_METRO_LOCATION_EXTENSIONS } from './ua-location-extensions-metro.js';
17
17
  import { UA_KHARKIV_LOCATION_TRANSLATIONS } from './ua-kharkiv-location-translations.js';
18
+ import { UA_CITY_LOCATION_EXPANSIONS } from './ua-city-location-expansions.js';
18
19
 
19
20
  export const UA_EXTRA_LOCATION_DICTIONARIES = Object.freeze({
20
21
  ...RAW_UA_EXTRA_LOCATION_DICTIONARIES,
@@ -37,6 +38,10 @@ const TASHKENT_UNSUPPORTED_SEED_MICRODISTRICTS = new Set([
37
38
  'Yunusabad-22',
38
39
  ]);
39
40
 
41
+ const LEGACY_SAMARKAND_SILK_ROAD_RESIDENCE = (
42
+ BASE_LOCATION_DICTIONARIES.UZ?.Samarkand?.residentialComplexes || []
43
+ ).find(({ name }) => name === 'Silk Road Residence');
44
+
40
45
  // The UZ seed is compatibility storage. In 0.3 semantic corrections are
41
46
  // applied before merging it with the canonical Uzbekistan extension layer.
42
47
  const UZ_BASE_LOCATION_DICTIONARIES = Object.freeze({
@@ -47,9 +52,17 @@ const UZ_BASE_LOCATION_DICTIONARIES = Object.freeze({
47
52
  (BASE_LOCATION_DICTIONARIES.UZ?.Tashkent?.microdistricts || [])
48
53
  .filter(({ name }) => !TASHKENT_UNSUPPORTED_SEED_MICRODISTRICTS.has(name)),
49
54
  ),
55
+ residentialComplexes: Object.freeze([
56
+ ...(BASE_LOCATION_DICTIONARIES.UZ?.Tashkent?.residentialComplexes || [])
57
+ .filter(({ name }) => name !== 'Tashkent City' && name !== 'Silk Road Residence'),
58
+ ...(LEGACY_SAMARKAND_SILK_ROAD_RESIDENCE ? [LEGACY_SAMARKAND_SILK_ROAD_RESIDENCE] : []),
59
+ ]),
60
+ }),
61
+ Samarkand: Object.freeze({
62
+ ...(BASE_LOCATION_DICTIONARIES.UZ?.Samarkand || {}),
50
63
  residentialComplexes: Object.freeze(
51
- (BASE_LOCATION_DICTIONARIES.UZ?.Tashkent?.residentialComplexes || [])
52
- .filter(({ name }) => name !== 'Tashkent City'),
64
+ (BASE_LOCATION_DICTIONARIES.UZ?.Samarkand?.residentialComplexes || [])
65
+ .filter(({ name }) => name !== 'Silk Road Residence'),
53
66
  ),
54
67
  }),
55
68
  });
@@ -125,6 +138,9 @@ function normalizeUzSemanticLocations(country) {
125
138
 
126
139
  const samarkand = normalized?.Samarkand;
127
140
  if (samarkand) {
141
+ const microdistricts = samarkand.microdistricts || [];
142
+ const mahallas = samarkand.mahallas || [];
143
+ const settlements = samarkand.settlements || [];
128
144
  const landmarks = samarkand.landmarks || [];
129
145
  const localAreas = samarkand.localAreas || [];
130
146
  const streets = samarkand.streets || [];
@@ -168,14 +184,150 @@ function normalizeUzSemanticLocations(country) {
168
184
  ? semanticEntry(canonicalUniversity, 'University Boulevard', universityAliases, 'street')
169
185
  : null;
170
186
 
187
+ const sogdianaMicrodistrict = microdistricts.find(({ name }) => name === 'Sogdiana');
188
+ const sogdianaLocalArea = localAreas.find(({ name }) => name === 'Sugdiyona');
189
+ const sogdianaMahalla = mahallas.find(({ name }) => name === 'Sogdiana');
190
+ const normalizedSogdiana = semanticEntry(
191
+ sogdianaMahalla || sogdianaLocalArea || sogdianaMicrodistrict || {},
192
+ 'Sogdiana',
193
+ [
194
+ ...(sogdianaMahalla?.aliases || []),
195
+ ...(sogdianaLocalArea?.aliases || []),
196
+ ...(sogdianaMicrodistrict?.aliases || []),
197
+ 'Sugdiyona',
198
+ "Sug'diyona",
199
+ 'Sug‘diyona',
200
+ "So'g'diyona",
201
+ 'So‘g‘diyona',
202
+ 'Согдиана',
203
+ 'Согдиёна',
204
+ 'Сўғдиёна',
205
+ ],
206
+ 'mahalla',
207
+ );
208
+
209
+ const kimyogarlarMicrodistrict = microdistricts.find(({ name }) => name === 'Kimyogarlar');
210
+ const kimyogarlarLocalArea = localAreas.find(({ name }) => name === 'Kimyogarlar');
211
+ const kimyogarlarSettlement = settlements.find(({ name }) => name === 'Kimyogarlar');
212
+ const normalizedKimyogarlar = semanticEntry(
213
+ kimyogarlarSettlement || kimyogarlarLocalArea || kimyogarlarMicrodistrict || {},
214
+ 'Kimyogarlar',
215
+ [
216
+ ...(kimyogarlarSettlement?.aliases || []),
217
+ ...(kimyogarlarLocalArea?.aliases || []),
218
+ ...(kimyogarlarMicrodistrict?.aliases || []),
219
+ 'Кимёгарлар',
220
+ 'Химики',
221
+ 'Химгородок',
222
+ 'посёлок Химиков',
223
+ 'поселок Химиков',
224
+ ],
225
+ 'settlement',
226
+ );
227
+
228
+ const chilquduqSource = mahallas.find(({ name }) => ['Chilquduq', 'Chilkuduq', 'Chilkuduk'].includes(name));
229
+ const navrozSource = mahallas.find(({ name }) => name === "Navro'z");
230
+ const shirinSource = mahallas.find(({ name }) => name === 'Shirin');
231
+ const choponOtaSource = mahallas.find(({ name }) => name === "Cho'pon ota");
232
+ const normalizedChilquduq = semanticEntry(
233
+ { ...(chilquduqSource || {}), parent: 'Xishrov' },
234
+ 'Chilquduq',
235
+ [
236
+ ...(chilquduqSource?.aliases || []),
237
+ 'Chilkuduq',
238
+ 'Chilkuduk',
239
+ 'Chilquduq MFY',
240
+ 'Chilquduq mahallasi',
241
+ 'Чилқудуқ',
242
+ 'Чилкудук',
243
+ ],
244
+ 'mahalla',
245
+ );
246
+ const normalizedNavroz = navrozSource
247
+ ? semanticEntry({ ...navrozSource, parent: 'Xishrov' }, "Navro'z", navrozSource.aliases || [], 'mahalla')
248
+ : null;
249
+ const normalizedShirin = shirinSource
250
+ ? semanticEntry({ ...shirinSource, parent: 'Farhod' }, 'Shirin', shirinSource.aliases || [], 'mahalla')
251
+ : null;
252
+ const normalizedChoponOta = choponOtaSource
253
+ ? semanticEntry({ ...choponOtaSource, parent: 'Farhod' }, "Cho'pon ota", choponOtaSource.aliases || [], 'mahalla')
254
+ : null;
255
+
256
+ const existingXishrov = settlements.find(({ name }) => ['Xishrov', 'Xishrav', 'Khishrav'].includes(name));
257
+ const normalizedXishrov = semanticEntry(
258
+ existingXishrov || {},
259
+ 'Xishrov',
260
+ [
261
+ ...(existingXishrov?.aliases || []),
262
+ 'Xishrav',
263
+ 'Khishrav',
264
+ 'Хишрав',
265
+ "So'lim shaharchasi",
266
+ 'So‘lim shaharchasi',
267
+ 'Solim shaharchasi',
268
+ 'Сўлим шаҳарчаси',
269
+ 'Сулим шахарчаси',
270
+ ],
271
+ 'settlement',
272
+ );
273
+ const existingFarhod = settlements.find(({ name }) => ['Farhod', 'Farxod'].includes(name));
274
+ const normalizedFarhod = semanticEntry(
275
+ existingFarhod || {},
276
+ 'Farhod',
277
+ [
278
+ ...(existingFarhod?.aliases || []),
279
+ 'Farhod shaharchasi',
280
+ 'Farxod shaharchasi',
281
+ 'Фарход шаҳарчаси',
282
+ 'Фарход шахарчаси',
283
+ ],
284
+ 'settlement',
285
+ );
286
+
287
+ const verifiedSamarkandStreetRows = Object.freeze([
288
+ ['Siyob Street', ['Siyob ko‘chasi', "Siyob ko'chasi", 'Сиёб кўчаси', 'Сиабская улица']],
289
+ ["Cho'pon-Ota Street", ["Cho'pon-Ota ko'chasi", 'Cho‘pon-Ota ko‘chasi', 'Чўпон Ота кўчаси', 'улица Чупон-Ота']],
290
+ ['Academician Vohid Abdullayev Street', ['Akademik Vohid Abdullayev ko‘chasi', "Akademik Vohid Abdullayev ko'chasi", 'Vohid Abdullayev ko‘chasi', 'улица Академика Вохида Абдуллаева']],
291
+ ]);
292
+ const verifiedSamarkandStreetNames = new Set(verifiedSamarkandStreetRows.map(([name]) => name));
293
+ const verifiedSamarkandStreets = verifiedSamarkandStreetRows.map(([name, aliases]) => semanticEntry({}, name, aliases, 'street'));
294
+
171
295
  normalized = Object.freeze({
172
296
  ...normalized,
173
297
  Samarkand: Object.freeze({
174
298
  ...samarkand,
175
- localAreas: Object.freeze(localAreas.filter(({ name }) => name !== 'University Boulevard')),
299
+ mahallas: Object.freeze([
300
+ ...mahallas.filter(({ name }) => ![
301
+ 'Sogdiana',
302
+ 'Chilquduq',
303
+ 'Chilkuduq',
304
+ 'Chilkuduk',
305
+ "Navro'z",
306
+ 'Shirin',
307
+ "Cho'pon ota",
308
+ ].includes(name)),
309
+ normalizedSogdiana,
310
+ normalizedChilquduq,
311
+ ...(normalizedNavroz ? [normalizedNavroz] : []),
312
+ ...(normalizedShirin ? [normalizedShirin] : []),
313
+ ...(normalizedChoponOta ? [normalizedChoponOta] : []),
314
+ ]),
315
+ microdistricts: Object.freeze(
316
+ microdistricts.filter(({ name }) => !['Sogdiana', 'Kimyogarlar'].includes(name)),
317
+ ),
318
+ localAreas: Object.freeze(
319
+ localAreas.filter(({ name }) => !['University Boulevard', 'Sugdiyona', 'Kimyogarlar'].includes(name)),
320
+ ),
321
+ settlements: Object.freeze([
322
+ ...settlements.filter(({ name }) => !['Kimyogarlar', 'Xishrov', 'Xishrav', 'Khishrav', 'Farhod', 'Farxod'].includes(name)),
323
+ normalizedKimyogarlar,
324
+ normalizedXishrov,
325
+ normalizedFarhod,
326
+ ]),
176
327
  streets: Object.freeze([
177
- ...streets.filter(({ name }) => name !== 'University Boulevard'),
328
+ ...streets.filter(({ name }) => name !== 'University Boulevard' && !verifiedSamarkandStreetNames.has(name)),
178
329
  ...(normalizedUniversity ? [normalizedUniversity] : []),
330
+ ...verifiedSamarkandStreets,
179
331
  ]),
180
332
  landmarks: Object.freeze([
181
333
  ...landmarks.filter(({ name }) => ![
@@ -359,6 +511,7 @@ const COUNTRY_LOCATION_DICTIONARIES = Object.freeze({
359
511
  UA_SECONDARY_LOCATION_EXTENSIONS,
360
512
  UA_METRO_LOCATION_EXTENSIONS,
361
513
  UA_KHARKIV_TRANSLATION_EXTENSIONS,
514
+ UA_CITY_LOCATION_EXPANSIONS,
362
515
  ),
363
516
  });
364
517
 
@@ -0,0 +1,76 @@
1
+ import { locationEntries } from './location-merge.js';
2
+
3
+ const microdistricts = (rows) => Object.freeze({ microdistricts: locationEntries(rows) });
4
+
5
+ // Listing-facing aliases that enrich the major Ukraine dictionaries without
6
+ // changing their established parser canonicals. Keep physical coordinates and
7
+ // source ownership in geo-catalog.
8
+ export const UA_CITY_LOCATION_EXPANSIONS = Object.freeze({
9
+ Kharkiv: microdistricts([
10
+ ['Zhukovskoho', 'Селище ім. Жуковського', 'селище Жуковського', 'Жуковського', 'посёлок Жуковского', 'поселок Жуковского', 'Жуковского', 'Zhukovskoho settlement'],
11
+ ['Kulynychi', 'Кулиничі', 'Кулиничи', 'селище Кулиничі', 'посёлок Кулиничи', 'поселок Кулиничи', 'Kulynichi'],
12
+ ['Saltivka', 'Saltovka', 'Салтовка'],
13
+ ['Pavlove Pole', 'Pavlovo Pole', 'Pavlovo Polye'],
14
+ ['Oleksiivka', 'Alekseevka', 'Alekseyevka'],
15
+ ['Kholodna Hora', 'Kholodnaya Gora', 'Holodnaya Gora'],
16
+ ['Novi Budynky', 'Novye Doma', 'Novyye Doma'],
17
+ ['Piatykhatky', 'Pyatikhatki', 'Pyatihatki'],
18
+ ['Zaliutyne', 'Zalyutino', 'Zalutino'],
19
+ ['Rohan', 'Rogan'],
20
+ ['Zhuravlivka', 'Zhuravlevka'],
21
+ ['Velyka Danylivka', 'Bolshaya Danilovka', 'Velika Danylivka'],
22
+ ['Sokolnyky', 'Sokolniki'],
23
+ ]),
24
+
25
+ Odesa: microdistricts([
26
+ ['Vuzivskyi', 'Вузівський', 'Вузовський', 'Вузовский', 'Vuzovskyi', 'Вузівський район', 'Вузовский район'],
27
+ ['Chubaivka', 'Чубаївка', 'Чубаевка', 'Chubayevka'],
28
+ ['Arkadia', 'Arcadia', 'район Аркадії', 'район Аркадии', 'Аркадія район', 'Аркадия район'],
29
+ ['Tairova', 'Tairovo', 'район Таїрова', 'район Таирова', 'ж/м Таїрова', 'ж/м Таирова', 'жилмассив Таирова'],
30
+ ['Cheryomushky', 'Cheremushki', 'Cheryomushki', 'Черемушки Одеса', 'Черёмушки Одесса', 'Черемушки Одесса', 'Черемушки район', 'Черёмушки район'],
31
+ ['Kotivskoho', 'Котовка', 'Поскот', 'ПосКот', 'ж/м Котовського', 'ж/м Котовского', 'жилмассив Котовского'],
32
+ ['Luzanivka', 'Luzanovka', 'Лузановский район', 'Лузанівський район', 'район Лузановки', 'район Лузанівки'],
33
+ ['Malyi Fontan', 'Maly Fontan', 'Малого Фонтана', 'Малого Фонтану', 'на Малом Фонтане', 'на Малому Фонтані'],
34
+ ['Moldavanka', 'Moldovanka', 'район Молдаванки', 'на Молдаванке', 'на Молдаванці'],
35
+ ['Peresyp', 'Peresip', 'район Пересыпи', 'район Пересипу', 'на Пересыпи', 'на Пересипі'],
36
+ ['Chornomorka', 'Chernomorka', 'Черноморка район', 'Чорноморка район', 'Люстдорф', 'Lustdorf'],
37
+ ['Dacha Kovalevskoho', 'Dacha Kovalevskogo', 'Дача Ковалевского район', 'район Дачи Ковалевского', 'район Дачі Ковалевського'],
38
+ ['Zastava-1', 'Zastava 1', 'Первая Застава', 'Перша Застава', 'Застава I'],
39
+ ['Zastava-2', 'Zastava 2', 'Вторая Застава', 'Друга Застава', 'Застава II'],
40
+ ]),
41
+
42
+ Kyiv: microdistricts([
43
+ ['Troyeshchyna', 'Troieshchyna', 'Троєщина масив', 'жилмассив Троещина', 'ж/м Троєщина', 'ж/м Троещина'],
44
+ ['Lipky', 'Lypky'],
45
+ ['Lukianivka', 'Lukyanivka'],
46
+ ['Zvirynets', 'Zverinets'],
47
+ ['Holosiiv', 'Holosiyiv', 'Goloseevo'],
48
+ ['Demiivka', 'Demiyivka', 'Demeevka'],
49
+ ['Shuliavka', 'Shulyavka'],
50
+ ['Solomianka', 'Solomyanka', 'Solomenka'],
51
+ ['Chokolivka', 'Chokolovka'],
52
+ ['Vidradnyi', 'Otradnyi', 'Otradny'],
53
+ ['Karavaievi Dachi', 'Karavayevi Dachi', 'Karavaevy Dachi'],
54
+ ['Kurenivka', 'Kurenevka'],
55
+ ['Rusanivka', 'Rusanovka'],
56
+ ['Berezniaky', 'Bereznyaky'],
57
+ ['Darnytsia', 'Darnitsa'],
58
+ ['Nova Darnytsia', 'Novaya Darnitsa'],
59
+ ['Stara Darnytsia', 'Staraya Darnitsa'],
60
+ ['Pozniaky', 'Poznyaki'],
61
+ ['Bortnychi', 'Bortnichi'],
62
+ ['Feofaniia', 'Feofaniya'],
63
+ ['Vydubychi', 'Vydubichi'],
64
+ ['Lisovyi Masyv', 'Lisovyi Massif', 'Lesnoy Massiv'],
65
+ ['Minskyi Masyv', 'Minskyi Massif', 'Minskiy Massiv'],
66
+ ['Kharkivskyi', 'Kharkivskyi Masyv', 'Kharkivskyi massif', 'Харківський житловий масив', 'Харьковский жилмассив'],
67
+ ['Borshchahivka', 'Borshchagivka', 'Borshchagovka'],
68
+ ['Pivdenna Borshchahivka', 'Yuzhnaya Borshchagovka'],
69
+ ['Mykilska Borshchahivka', 'Nikolskaya Borshchagovka'],
70
+ ['Sviatoshyn', 'Svyatoshyn'],
71
+ ['Akademmistechko', 'Akademhorodok', 'Akademgorodok'],
72
+ ['Pushcha-Vodytsia', 'Pushcha Vodytsia', 'Pushcha-Voditsa'],
73
+ ['Vynohradar', 'Vinogradar'],
74
+ ['Nyvky', 'Nivki'],
75
+ ]),
76
+ });