@whiteslove/parsing-lexicon 0.2.5 → 0.2.7
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/index.d.ts +5 -0
- package/package.json +6 -1
- package/src/housing-features.d.ts +8 -0
- package/src/housing-intent.js +12 -1
- package/src/housing-language.d.ts +4 -0
- package/src/housing-language.js +73 -0
- package/src/housing-listing-fields.d.ts +49 -0
- package/src/housing-safety.d.ts +7 -0
- package/src/housing-safety.js +75 -0
- package/src/housing-title.d.ts +2 -0
- package/src/housing-title.js +70 -0
- package/src/housing.js +2 -2
- package/src/index.js +3 -0
package/index.d.ts
CHANGED
|
@@ -373,6 +373,11 @@ export * from './src/housing-context.js';
|
|
|
373
373
|
export * from './src/hiring-context.js';
|
|
374
374
|
export * from './src/housing-intent.js';
|
|
375
375
|
export * from './src/housing-structured.js';
|
|
376
|
+
export * from './src/housing-safety.js';
|
|
377
|
+
export * from './src/housing-title.js';
|
|
378
|
+
export * from './src/housing-language.js';
|
|
379
|
+
export * from './src/housing-features.js';
|
|
380
|
+
export * from './src/housing-listing-fields.js';
|
|
376
381
|
|
|
377
382
|
// housing semantic helper declarations added in 0.2.5
|
|
378
383
|
export function resolveHousingOccupancy(value: unknown): 'wholeProperty' | 'room' | 'sharedRoom' | 'bedSpace' | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whiteslove/parsing-lexicon",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Shared deterministic multilingual parsing lexicon for Whiteslove housing and hiring services",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"./housing-context": { "types": "./src/housing-context.d.ts", "import": "./src/housing-context.js" },
|
|
42
42
|
"./housing-address": { "types": "./src/housing-address.d.ts", "import": "./src/housing-address.js" },
|
|
43
43
|
"./housing-structured": { "types": "./src/housing-structured.d.ts", "import": "./src/housing-structured.js" },
|
|
44
|
+
"./housing-safety": { "types": "./src/housing-safety.d.ts", "import": "./src/housing-safety.js" },
|
|
45
|
+
"./housing-title": { "types": "./src/housing-title.d.ts", "import": "./src/housing-title.js" },
|
|
46
|
+
"./housing-language": { "types": "./src/housing-language.d.ts", "import": "./src/housing-language.js" },
|
|
47
|
+
"./housing-features": { "types": "./src/housing-features.d.ts", "import": "./src/housing-features.js" },
|
|
48
|
+
"./housing-listing-fields": { "types": "./src/housing-listing-fields.d.ts", "import": "./src/housing-listing-fields.js" },
|
|
44
49
|
"./housing-text": { "types": "./src/housing-text.d.ts", "import": "./src/housing-text.js" },
|
|
45
50
|
"./housing-source-aliases": { "types": "./src/housing-source-aliases.d.ts", "import": "./src/housing-source-aliases.js" },
|
|
46
51
|
"./hiring": "./src/hiring.js",
|
package/src/housing-intent.js
CHANGED
|
@@ -132,6 +132,17 @@ export function resolveHousingIntent(value) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
export function classifyHousingDealType(value) {
|
|
135
|
-
|
|
135
|
+
const resolved = resolveHousingIntent(value)?.dealType;
|
|
136
|
+
if (resolved) return resolved;
|
|
137
|
+
|
|
138
|
+
// Some housing feeds omit an explicit rent verb but still use established
|
|
139
|
+
// Uzbek offer/share shorthand. Keep this fallback package-owned so consumers
|
|
140
|
+
// do not carry their own multilingual regexes.
|
|
141
|
+
const text = String(value || '');
|
|
142
|
+
if (!text) return null;
|
|
143
|
+
if (/(?:sherik(?:ka|lik)|шерик(?:ка|лик)|(?:1|bir)\s+ta\s+qiz\s+sherik|(?:1|бир)\s*та\s*(?:бола|киши|қиз|киз)\s*керак|1\s*хонага[^\r\n]{0,40}(?:киши|одам)\s*турилади|oila(?:ga)?\s+qo['’`]?yiladi|oila(?:ga)?\s+quyiladi|(?:хонали|квартир)[^\r\n]{0,100}турибди[^\r\n]{0,30}\d+\s*\$|квартира\s+бор)/iu.test(text)) {
|
|
144
|
+
return 'longRent';
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
136
147
|
}
|
|
137
148
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { normalizeUnicode } from './normalization.js';
|
|
2
|
+
|
|
3
|
+
// Uzbek and Kazakh Cyrillic carry letters no Slavic alphabet uses. Their
|
|
4
|
+
// presence rules out "this text is already Russian/Ukrainian" even though the
|
|
5
|
+
// housing vocabulary overlaps heavily. `і` is deliberately absent: it is an
|
|
6
|
+
// ordinary Ukrainian letter.
|
|
7
|
+
const NON_SLAVIC_CYRILLIC = /[ўқғҳәөүұңһ]/iu;
|
|
8
|
+
const SLAVIC_LANGUAGES = new Set(['ru', 'uk']);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Housing vocabulary that reliably indicates the language a listing body is
|
|
12
|
+
* written in. Deliberately common words: the goal is deciding whether a reader
|
|
13
|
+
* needs a translation, not identifying rare dialects.
|
|
14
|
+
*/
|
|
15
|
+
const HOUSING_LANGUAGE_SIGNALS = Object.freeze({
|
|
16
|
+
ru: {
|
|
17
|
+
pattern: /(?:квартир\p{L}*|комнат\p{L}*|этаж\p{L}*|дом\p{L}*|цен\p{L}*|сда[её]тся|прода[её]тся|аренд\p{L}*|рядом|метро|семейн\p{L}*|коммунальн\p{L}*|ремонт\p{L}*|мебел\p{L}*|балкон\p{L}*|район\p{L}*)/giu,
|
|
18
|
+
minimum: 2,
|
|
19
|
+
},
|
|
20
|
+
uk: {
|
|
21
|
+
pattern: /(?:квартир\p{L}*|кімнат\p{L}*|поверх\p{L}*|будинк\p{L}*|цін\p{L}*|здається|продається|оренд\p{L}*|поруч|метро|сімейн\p{L}*|комунальн\p{L}*|ремонт\p{L}*|меблі\p{L}*|балкон\p{L}*|район\p{L}*)/giu,
|
|
22
|
+
minimum: 2,
|
|
23
|
+
},
|
|
24
|
+
en: {
|
|
25
|
+
pattern: /(?:^|[^\p{L}])(?:apartment|flat|house|room|bedroom|floor|price|rent|rental|sale|family|utilities|near|available|furnished|balcony|district|deposit)(?=$|[^\p{L}])/giu,
|
|
26
|
+
minimum: 3,
|
|
27
|
+
},
|
|
28
|
+
uz: {
|
|
29
|
+
pattern: /(?:kvartira\p{L}*|xona\p{L}*|qavat\p{L}*|uy\p{L}*|narx\p{L}*|ijara\p{L}*|beriladi|sotiladi|yaqin|metro|mebel\p{L}*|balkon\p{L}*|tuman\p{L}*)/giu,
|
|
30
|
+
minimum: 2,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function signalCount(text, language) {
|
|
35
|
+
const signal = HOUSING_LANGUAGE_SIGNALS[language];
|
|
36
|
+
if (!signal) return 0;
|
|
37
|
+
return (text.match(signal.pattern) || []).length;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True when the housing text already reads as the given language, i.e. a reader
|
|
42
|
+
* of that language does not need it translated.
|
|
43
|
+
*
|
|
44
|
+
* Uses a per-language evidence threshold rather than a single hit, because one
|
|
45
|
+
* shared word ("metro", "balkon") appears across all of these languages.
|
|
46
|
+
*/
|
|
47
|
+
export function housingTextIsInLanguage(value, language) {
|
|
48
|
+
const text = normalizeUnicode(value ?? '').toLocaleLowerCase();
|
|
49
|
+
if (!text.trim()) return false;
|
|
50
|
+
const signal = HOUSING_LANGUAGE_SIGNALS[language];
|
|
51
|
+
if (!signal) return false;
|
|
52
|
+
// Uzbek/Kazakh Cyrillic text reuses Slavic housing words but is neither.
|
|
53
|
+
if (SLAVIC_LANGUAGES.has(language) && NON_SLAVIC_CYRILLIC.test(text)) return false;
|
|
54
|
+
return signalCount(text, language) >= signal.minimum;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Best-guess language of a housing text, or null when no language reaches its
|
|
59
|
+
* evidence threshold.
|
|
60
|
+
*/
|
|
61
|
+
export function detectHousingTextLanguage(value) {
|
|
62
|
+
const text = normalizeUnicode(value ?? '').toLocaleLowerCase();
|
|
63
|
+
if (!text.trim()) return null;
|
|
64
|
+
|
|
65
|
+
let best = null;
|
|
66
|
+
for (const language of Object.keys(HOUSING_LANGUAGE_SIGNALS)) {
|
|
67
|
+
if (SLAVIC_LANGUAGES.has(language) && NON_SLAVIC_CYRILLIC.test(text)) continue;
|
|
68
|
+
const count = signalCount(text, language);
|
|
69
|
+
if (count < HOUSING_LANGUAGE_SIGNALS[language].minimum) continue;
|
|
70
|
+
if (!best || count > best.count) best = { language, count };
|
|
71
|
+
}
|
|
72
|
+
return best?.language ?? null;
|
|
73
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type HousingMinRentTerm = Readonly<{
|
|
2
|
+
value: number;
|
|
3
|
+
unit: 'day' | 'week' | 'month' | 'year';
|
|
4
|
+
}>;
|
|
5
|
+
|
|
6
|
+
export type HousingUtilitiesAmount = Readonly<{
|
|
7
|
+
amount: number;
|
|
8
|
+
currency: string | null;
|
|
9
|
+
approximate: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
|
|
12
|
+
export type HousingListingFields = Readonly<{
|
|
13
|
+
bedrooms: number | null;
|
|
14
|
+
bathrooms: number | null;
|
|
15
|
+
buildingYear: number | null;
|
|
16
|
+
balcony: boolean | null;
|
|
17
|
+
terrace: boolean | null;
|
|
18
|
+
privateYard: boolean | null;
|
|
19
|
+
courtyard: boolean | null;
|
|
20
|
+
gazebo: boolean | null;
|
|
21
|
+
dishwasher: boolean | null;
|
|
22
|
+
airConditioner: boolean | null;
|
|
23
|
+
gas: boolean | null;
|
|
24
|
+
newBuilding: boolean | null;
|
|
25
|
+
communalSeparated: boolean | null;
|
|
26
|
+
parking: boolean | null;
|
|
27
|
+
elevator: boolean | null;
|
|
28
|
+
heating: boolean | null;
|
|
29
|
+
hotWater: boolean | null;
|
|
30
|
+
internet: boolean | null;
|
|
31
|
+
petsAllowed: boolean | null;
|
|
32
|
+
childrenAllowed: boolean | null;
|
|
33
|
+
smokingAllowed: boolean | null;
|
|
34
|
+
negotiable: boolean | null;
|
|
35
|
+
furnished: boolean | null;
|
|
36
|
+
depositRequired: boolean | null;
|
|
37
|
+
firstRent: boolean | null;
|
|
38
|
+
minRentTerm: HousingMinRentTerm | null;
|
|
39
|
+
availableFrom: string | null;
|
|
40
|
+
utilitiesAmount: HousingUtilitiesAmount | null;
|
|
41
|
+
}>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Empty input yields an empty object, so every field is optional on the result.
|
|
45
|
+
*/
|
|
46
|
+
export function parseHousingListingFields(
|
|
47
|
+
value: unknown,
|
|
48
|
+
options?: { country?: string },
|
|
49
|
+
): Readonly<Partial<HousingListingFields>>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { deepFreeze } from './lexicon-core.js';
|
|
2
|
+
import { normalizeUnicode } from './normalization.js';
|
|
3
|
+
import { isRoomOnlyHousing } from './housing-source-aliases.js';
|
|
4
|
+
|
|
5
|
+
// Clause-scoped gap: the demand and the person must sit in the same sentence.
|
|
6
|
+
// Crossing `.`/`!`/`?`/newline let "Ищу жильё. Одна девушка уже живёт" read as a
|
|
7
|
+
// demand for one woman, which it is not.
|
|
8
|
+
const CLAUSE = String.raw`[^\r\n.!?]`;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wording that seeks exactly one female tenant, as opposed to the generic
|
|
12
|
+
* "women only" audience wording that ordinary women-only listings use.
|
|
13
|
+
* The count matters: a landlord addressing one specific woman is the signal,
|
|
14
|
+
* not a flat that happens to prefer female tenants.
|
|
15
|
+
*/
|
|
16
|
+
const SINGLE_FEMALE_TENANT_PATTERNS = Object.freeze([
|
|
17
|
+
// ru: "только одна девушка", "нужна 1 девушка", "ищу одну женщину", "подселю одну девушку"
|
|
18
|
+
new RegExp(
|
|
19
|
+
String.raw`(?:только|лише|нужн\p{L}*|потрібн\p{L}*|ищ[еуy]\p{L}*|шука\p{L}*|подсел\p{L}*|підсел\p{L}*)`
|
|
20
|
+
+ CLAUSE + `{0,24}`
|
|
21
|
+
+ String.raw`(?:^|[^\p{L}\p{N}_])(?:одн(?:а|ої|ой|у|ту)|1)\s+(?:девушк\p{L}*|дівчин\p{L}*|женщин\p{L}*|жінк\p{L}*)`,
|
|
22
|
+
'iu',
|
|
23
|
+
),
|
|
24
|
+
// uk/ru reversed order: "одна девушка нужна"
|
|
25
|
+
new RegExp(
|
|
26
|
+
String.raw`(?:^|[^\p{L}\p{N}_])(?:одн(?:а|ої|ой|у)|1)\s+(?:девушк\p{L}*|дівчин\p{L}*|женщин\p{L}*|жінк\p{L}*)`
|
|
27
|
+
+ CLAUSE + `{0,18}`
|
|
28
|
+
+ String.raw`(?:нужн\p{L}*|потрібн\p{L}*|треба|ищ[еуy]\p{L}*|шука\p{L}*)`,
|
|
29
|
+
'iu',
|
|
30
|
+
),
|
|
31
|
+
// uzLatn: "faqat 1 ta qiz kerak", "bitta ayol ijarachi kerak"
|
|
32
|
+
new RegExp(
|
|
33
|
+
String.raw`(?:faqat\s+)?(?:^|[^\p{L}\p{N}_])(?:1|bitta)\s*(?:ta\s*)?(?:qiz|ayol)`
|
|
34
|
+
+ CLAUSE + `{0,18}`
|
|
35
|
+
+ String.raw`(?:ijarachi\s*)?(?:kerak|kere|kerakli)`,
|
|
36
|
+
'iu',
|
|
37
|
+
),
|
|
38
|
+
// uzCyrl: "фақат 1 та қиз керак"
|
|
39
|
+
new RegExp(
|
|
40
|
+
String.raw`(?:фақат\s+)?(?:^|[^\p{L}\p{N}_])(?:1|битта)\s*(?:та\s*)?(?:қиз|аёл)`
|
|
41
|
+
+ CLAUSE + `{0,18}`
|
|
42
|
+
+ String.raw`(?:ижарачи\s*)?(?:керак|керакли)`,
|
|
43
|
+
'iu',
|
|
44
|
+
),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True when the text asks for exactly one female tenant.
|
|
49
|
+
*
|
|
50
|
+
* Deliberately narrower than the `women` audience: "только для девушек" is an
|
|
51
|
+
* ordinary preference and must not match, while "нужна одна девушка" does.
|
|
52
|
+
*/
|
|
53
|
+
export function seeksSingleFemaleTenant(value) {
|
|
54
|
+
const text = normalizeUnicode(value ?? '');
|
|
55
|
+
if (!text.trim()) return false;
|
|
56
|
+
return SINGLE_FEMALE_TENANT_PATTERNS.some((pattern) => pattern.test(text));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Linguistic safety signals for a housing listing.
|
|
61
|
+
*
|
|
62
|
+
* This reports only what the wording says. Whether a given combination is
|
|
63
|
+
* treated as a risk — and any price policy applied on top — belongs to the
|
|
64
|
+
* consuming service, not to the lexicon.
|
|
65
|
+
*/
|
|
66
|
+
export function parseHousingSafetySignals(value) {
|
|
67
|
+
const text = normalizeUnicode(value ?? '');
|
|
68
|
+
if (!text.trim()) {
|
|
69
|
+
return deepFreeze({ roomOnly: false, singleFemaleTenantSought: false });
|
|
70
|
+
}
|
|
71
|
+
return deepFreeze({
|
|
72
|
+
roomOnly: isRoomOnlyHousing(text),
|
|
73
|
+
singleFemaleTenantSought: seeksSingleFemaleTenant(text),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { normalizeUnicode } from './normalization.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Marketplace category headings that some sources (notably OLX) hand back in
|
|
5
|
+
* place of a real listing title. They name a whole search category rather than
|
|
6
|
+
* one property, so a consumer must not present them as the listing's own title.
|
|
7
|
+
*
|
|
8
|
+
* The English forms already embed the subject ("long-term apartment rentals"),
|
|
9
|
+
* so the subject is matched as an optional trailing word rather than required.
|
|
10
|
+
*/
|
|
11
|
+
const CATEGORY = String.raw`(?:`
|
|
12
|
+
+ String.raw`(?:довгостроков\p{L}*|долгосрочн\p{L}*|короткостроков\p{L}*|краткосрочн\p{L}*|подобов\p{L}*|посуточн\p{L}*)\s+(?:оренда|аренда|найм)`
|
|
13
|
+
+ String.raw`|(?:оренда|аренда|найм|продаж|продажа)`
|
|
14
|
+
+ String.raw`|(?:long|short)[- ]?term\s+(?:apartment\s+|flat\s+|house\s+|room\s+)?rentals?`
|
|
15
|
+
+ String.raw`|(?:apartments?|flats?|houses?|rooms?)\s+for\s+(?:rent|sale)`
|
|
16
|
+
+ String.raw`|rentals?|daily\s+rentals?`
|
|
17
|
+
+ String.raw`|ijaraga\s+berish|ijara|sotuvi`
|
|
18
|
+
+ String.raw`)`;
|
|
19
|
+
|
|
20
|
+
const SUBJECT = String.raw`(?:`
|
|
21
|
+
+ String.raw`квартир\p{L}*|кімнат\p{L}*|комнат\p{L}*|будинк\p{L}*|будинків|дом\p{L}*|житл\p{L}*|нерухомост\p{L}*|недвижимост\p{L}*`
|
|
22
|
+
+ String.raw`|apartments?|flats?|houses?|rooms?|property|real\s+estate`
|
|
23
|
+
+ String.raw`|kvartira\p{L}*|uylar|xona\p{L}*`
|
|
24
|
+
+ String.raw`)`;
|
|
25
|
+
|
|
26
|
+
// Only a single trailing locality clause counts as part of the heading — one
|
|
27
|
+
// short comma- or dash-separated fragment such as ", Подільський район".
|
|
28
|
+
// Anything richer means the title is saying something about this property.
|
|
29
|
+
const LOCALITY_TAIL = String.raw`(?:\s*[,–—-]\s*[^\r\n,]{1,40})?`;
|
|
30
|
+
|
|
31
|
+
const GENERIC_TITLE_PATTERN = new RegExp(
|
|
32
|
+
`^\\s*${CATEGORY}(?:\\s+${SUBJECT})?${LOCALITY_TAIL}\\s*$`,
|
|
33
|
+
'iu',
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Digits are the cheapest proof a title says something specific: a room count,
|
|
37
|
+
// an area, a price or a street number. "2-к квартира, 54 м²" is a real title
|
|
38
|
+
// even though it opens with the same words as the category heading.
|
|
39
|
+
const SPECIFIC_DETAIL = /\d/u;
|
|
40
|
+
|
|
41
|
+
function titleContent(value) {
|
|
42
|
+
return normalizeUnicode(value ?? '')
|
|
43
|
+
.replace(
|
|
44
|
+
/[\p{Extended_Pictographic}\p{Emoji_Presentation}\p{Emoji_Modifier}\p{Variation_Selector}\p{Join_Control}]/gu,
|
|
45
|
+
'',
|
|
46
|
+
)
|
|
47
|
+
.replace(/[^\p{L}\p{N}]+/gu, '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* True when the title is a marketplace category heading rather than a title
|
|
52
|
+
* describing this specific property.
|
|
53
|
+
*/
|
|
54
|
+
export function isGenericHousingTitle(value) {
|
|
55
|
+
const text = normalizeUnicode(value ?? '').replace(/\s+/g, ' ').trim();
|
|
56
|
+
if (!text) return false;
|
|
57
|
+
if (SPECIFIC_DETAIL.test(text)) return false;
|
|
58
|
+
return GENERIC_TITLE_PATTERN.test(text);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* True when the title can stand on its own in a listing card or popup.
|
|
63
|
+
*
|
|
64
|
+
* Rejects titles carrying almost no letters or digits (emoji- or
|
|
65
|
+
* punctuation-only) as well as marketplace category headings.
|
|
66
|
+
*/
|
|
67
|
+
export function hasMeaningfulHousingTitle(value) {
|
|
68
|
+
if (titleContent(value).length < 3) return false;
|
|
69
|
+
return !isGenericHousingTitle(value);
|
|
70
|
+
}
|
package/src/housing.js
CHANGED
|
@@ -120,8 +120,8 @@ export const CURRENCIES = Object.freeze([
|
|
|
120
120
|
|
|
121
121
|
export const SELLER_TERMS = Object.freeze({
|
|
122
122
|
owner: group('owner', {
|
|
123
|
-
ru: ['собственник', 'хозяин', 'от хозяина', 'без посредников'], en: ['owner', 'direct owner', 'no agent'], uk: ['власник', 'власниця', 'від власника', 'без посередників'], ro: ['proprietar', 'direct proprietar', 'fără agenție', 'fara agentie', 'fără intermediari'],
|
|
124
|
-
uzLatn: ['egasi', 'uy egasi', 'mulkdor', 'maklersiz', 'vositachisiz'], uzCyrl: ['эгаси', 'уй эгаси', 'мулкдор', 'маклерсиз', 'воситачисиз'], kk: ['иесі', 'үй иесі', 'меншік иесі', 'делдалсыз'],
|
|
123
|
+
ru: ['собственник', 'хозяин', 'от хозяина', 'без посредников', 'без посредника', 'без риелтора', 'без риэлтора', 'без маклера', 'без маклер', 'без агента'], en: ['owner', 'direct owner', 'no agent', 'no broker', 'no realtor'], uk: ['власник', 'власниця', 'від власника', 'без посередників'], ro: ['proprietar', 'direct proprietar', 'fără agenție', 'fara agentie', 'fără intermediari'],
|
|
124
|
+
uzLatn: ['egasi', 'uy egasi', 'mulkdor', 'maklersiz', 'vositachisiz'], uzCyrl: ['эгаси', 'уй эгаси', 'мулкдор', 'маклерсиз', 'воситачисиз', 'без маклер'], kk: ['иесі', 'үй иесі', 'меншік иесі', 'делдалсыз'],
|
|
125
125
|
}),
|
|
126
126
|
agency: group('agency', {
|
|
127
127
|
ru: ['агентство', 'агент', 'риелтор', 'риэлтор', 'маклер', 'посредник'], en: ['agency', 'agent', 'realtor', 'broker'], uk: ['агентство', 'агент', 'рієлтор', 'риелтор', 'посередник'], ro: ['agenție', 'agentie', 'agent imobiliar', 'agenție imobiliară', 'agentie imobiliara', 'broker'],
|
package/src/index.js
CHANGED
|
@@ -36,6 +36,9 @@ export * from './housing-context.js';
|
|
|
36
36
|
export * from './housing-address.js';
|
|
37
37
|
export * from './housing-features.js';
|
|
38
38
|
export * from './housing-listing-fields.js';
|
|
39
|
+
export * from './housing-safety.js';
|
|
40
|
+
export * from './housing-title.js';
|
|
41
|
+
export * from './housing-language.js';
|
|
39
42
|
export * from './housing-structured.js';
|
|
40
43
|
export * from './hiring.js';
|
|
41
44
|
export * from './hiring-languages.js';
|