@treeviz/gedcom-parser 2.0.8 → 2.0.10

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 (37) hide show
  1. package/dist/classes/index.js +627 -159
  2. package/dist/classes/indi.d.ts.map +1 -1
  3. package/dist/cli/index.js +627 -159
  4. package/dist/constants/index.js +626 -158
  5. package/dist/factories/index.js +626 -158
  6. package/dist/index.js +628 -160
  7. package/dist/kinship-translator/index.d.ts +1 -0
  8. package/dist/kinship-translator/index.d.ts.map +1 -1
  9. package/dist/kinship-translator/index.js +627 -159
  10. package/dist/kinship-translator/kinship-translator.basic.d.ts +11 -0
  11. package/dist/kinship-translator/kinship-translator.basic.d.ts.map +1 -1
  12. package/dist/kinship-translator/kinship-translator.d.ts.map +1 -1
  13. package/dist/kinship-translator/kinship-translator.de.d.ts +2 -1
  14. package/dist/kinship-translator/kinship-translator.de.d.ts.map +1 -1
  15. package/dist/kinship-translator/kinship-translator.en.d.ts +2 -1
  16. package/dist/kinship-translator/kinship-translator.en.d.ts.map +1 -1
  17. package/dist/kinship-translator/kinship-translator.es.d.ts +2 -1
  18. package/dist/kinship-translator/kinship-translator.es.d.ts.map +1 -1
  19. package/dist/kinship-translator/kinship-translator.fr.d.ts +2 -1
  20. package/dist/kinship-translator/kinship-translator.fr.d.ts.map +1 -1
  21. package/dist/kinship-translator/kinship-translator.hu.d.ts +8 -1
  22. package/dist/kinship-translator/kinship-translator.hu.d.ts.map +1 -1
  23. package/dist/kinship-translator/kinship-translator.interface.d.ts +1 -0
  24. package/dist/kinship-translator/kinship-translator.interface.d.ts.map +1 -1
  25. package/dist/kinship-translator/kinship-translator.ru.d.ts +18 -0
  26. package/dist/kinship-translator/kinship-translator.ru.d.ts.map +1 -0
  27. package/dist/kinship-translator/patterns.es.d.ts.map +1 -1
  28. package/dist/kinship-translator/patterns.ru.d.ts +2 -0
  29. package/dist/kinship-translator/patterns.ru.d.ts.map +1 -0
  30. package/dist/kinship-translator/translators.d.ts +1 -0
  31. package/dist/kinship-translator/translators.d.ts.map +1 -1
  32. package/dist/kinship-translator/types.d.ts +1 -1
  33. package/dist/kinship-translator/types.d.ts.map +1 -1
  34. package/dist/utils/cache.d.ts +14 -1
  35. package/dist/utils/cache.d.ts.map +1 -1
  36. package/dist/utils/index.js +628 -160
  37. package/package.json +1 -1
@@ -95,7 +95,8 @@ __export(translators_exports, {
95
95
  en: () => KinshipTranslatorEn,
96
96
  es: () => KinshipTranslatorEs,
97
97
  fr: () => KinshipTranslatorFr,
98
- hu: () => KinshipTranslatorHU
98
+ hu: () => KinshipTranslatorHU,
99
+ ru: () => KinshipTranslatorRu
99
100
  });
100
101
  var getGedcomId = (gedcom) => {
101
102
  if (!gedcom) {
@@ -217,6 +218,135 @@ var profilePictureCache = (gedcom, key, value) => {
217
218
  const cached = caches.profilePictureCache?.[fullKey];
218
219
  return cached;
219
220
  };
221
+ var isNonBiologicalPath = (path) => path.some(
222
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
223
+ );
224
+ var invertHop = (kinship) => {
225
+ if (kinship === "parent") {
226
+ return "child";
227
+ }
228
+ if (kinship === "child") {
229
+ return "parent";
230
+ }
231
+ return kinship;
232
+ };
233
+ var hopRelation = (from, toIndi, kinship, inherited) => {
234
+ if (kinship === "parent" || kinship === "child") {
235
+ const parent = kinship === "parent" ? toIndi : from.indi;
236
+ const child = kinship === "parent" ? from.indi : toIndi;
237
+ const currentRelation = child.getParentType(parent);
238
+ if (currentRelation && currentRelation !== "biological" /* BIOLOGICAL */) {
239
+ return currentRelation;
240
+ }
241
+ }
242
+ if (inherited && inherited !== "biological" /* BIOLOGICAL */) {
243
+ return inherited;
244
+ }
245
+ return void 0;
246
+ };
247
+ var rebuildPathFromHops = (hops, skipRelation = false) => {
248
+ if (hops.length === 0) {
249
+ return [];
250
+ }
251
+ const path = [
252
+ {
253
+ indi: hops[0].indi,
254
+ kinship: "self",
255
+ level: 0,
256
+ levelUp: 0,
257
+ levelDown: 0,
258
+ degree: 0
259
+ }
260
+ ];
261
+ for (let i = 1; i < hops.length; i++) {
262
+ const prev = path[i - 1];
263
+ const { indi, kinship } = hops[i];
264
+ const relation = skipRelation ? void 0 : hopRelation(prev, indi, kinship, prev.relation);
265
+ if (kinship === "parent") {
266
+ path.push({
267
+ indi,
268
+ kinship,
269
+ relation,
270
+ level: prev.level + 1,
271
+ levelUp: prev.levelUp + 1,
272
+ levelDown: prev.levelDown,
273
+ degree: prev.degree
274
+ });
275
+ continue;
276
+ }
277
+ if (kinship === "child") {
278
+ path.push({
279
+ indi,
280
+ kinship,
281
+ relation,
282
+ level: prev.level - 1,
283
+ levelUp: prev.levelUp,
284
+ levelDown: prev.levelDown + 1,
285
+ degree: prev.levelUp ? prev.level > 0 ? prev.levelUp - prev.level + 1 : prev.levelDown - Math.abs(prev.level) : 0
286
+ });
287
+ continue;
288
+ }
289
+ path.push({
290
+ indi,
291
+ kinship,
292
+ relation,
293
+ level: prev.level,
294
+ levelUp: prev.levelUp,
295
+ levelDown: prev.levelDown,
296
+ degree: prev.degree
297
+ });
298
+ }
299
+ return path;
300
+ };
301
+ var storePathIfBetter = (gedcom, fromId, toId, path) => {
302
+ const key = `${fromId}|${toId}`;
303
+ const existing = pathCache(gedcom, key);
304
+ if (existing && (!isNonBiologicalPath(existing) || isNonBiologicalPath(path))) {
305
+ return;
306
+ }
307
+ pathCache(gedcom, key, path);
308
+ };
309
+ var cacheDiscoveredPath = (gedcom, path) => {
310
+ const n = path.length;
311
+ if (n < 2) {
312
+ return;
313
+ }
314
+ for (let i = 0; i < n; i++) {
315
+ const fromId = path[i].indi.id;
316
+ if (!fromId) {
317
+ continue;
318
+ }
319
+ for (let j = i + 1; j < n; j++) {
320
+ const toId = path[j].indi.id;
321
+ if (!toId) {
322
+ continue;
323
+ }
324
+ const isOriginalPair = i === 0 && j === n - 1;
325
+ const isDirectHop = j === i + 1;
326
+ const slice = path.slice(i, j + 1);
327
+ const sliceMixed = isNonBiologicalPath(slice);
328
+ if (!isOriginalPair && !isDirectHop && sliceMixed) {
329
+ continue;
330
+ }
331
+ const forward = i === 0 ? slice : rebuildPathFromHops(slice, !sliceMixed);
332
+ const reverseHops = [
333
+ {
334
+ indi: slice[slice.length - 1].indi,
335
+ kinship: "self"
336
+ }
337
+ ];
338
+ for (let k = slice.length - 2; k >= 0; k--) {
339
+ reverseHops.push({
340
+ indi: slice[k].indi,
341
+ kinship: invertHop(slice[k + 1].kinship)
342
+ });
343
+ }
344
+ const reverse = rebuildPathFromHops(reverseHops, !sliceMixed);
345
+ storePathIfBetter(gedcom, fromId, toId, forward);
346
+ storePathIfBetter(gedcom, toId, fromId, reverse);
347
+ }
348
+ }
349
+ };
220
350
 
221
351
  // src/utils/get-all-prop.ts
222
352
  var getAllProp = (indi, prop) => {
@@ -3478,7 +3608,6 @@ var Indi = class extends Common {
3478
3608
  if (cache) {
3479
3609
  return cache;
3480
3610
  }
3481
- const visited = new Individuals().append(this);
3482
3611
  const mainItem = {
3483
3612
  indi: this,
3484
3613
  level: 0,
@@ -3487,129 +3616,155 @@ var Indi = class extends Common {
3487
3616
  degree: 0,
3488
3617
  kinship: "self"
3489
3618
  };
3490
- const path = [mainItem];
3619
+ const mainPath = [mainItem];
3491
3620
  if (this.id === usedIndi.id) {
3492
- return path;
3621
+ return mainPath;
3493
3622
  }
3494
- const queue = [
3495
- {
3496
- ...mainItem,
3497
- path
3498
- }
3499
- ];
3500
- let helper = 0;
3501
- while (queue.length > 0) {
3502
- if (helper++ > 1e6) {
3503
- break;
3504
- }
3505
- const {
3506
- indi,
3507
- path: path2,
3508
- kinship,
3509
- relation,
3510
- level,
3511
- levelUp,
3512
- levelDown,
3513
- degree,
3514
- breakOnNext,
3515
- breakAfterNext,
3516
- inLaw
3517
- } = queue.shift();
3518
- if (usedIndi.id === indi.id) {
3519
- if (breakOnNext) {
3520
- return void 0;
3623
+ const bfs = (biologicalOnly) => {
3624
+ const visited = new Individuals().append(this);
3625
+ const queue = [
3626
+ {
3627
+ ...mainItem,
3628
+ path: mainPath
3521
3629
  }
3522
- pathCache(this._gedcom, cacheKey, path2);
3523
- return path2;
3524
- }
3525
- visited.append(indi);
3526
- const additional = {};
3527
- if (breakOnNext || breakAfterNext) {
3528
- additional.breakOnNext = breakOnNext || breakAfterNext;
3529
- }
3530
- if (inLaw) {
3531
- additional.inLaw = inLaw;
3532
- }
3533
- if (kinship === "spouse" && breakAfterSpouse) {
3534
- if (path2.length <= 2) {
3535
- additional.inLaw = true;
3536
- } else {
3537
- additional.breakOnNext = true;
3630
+ ];
3631
+ let helper = 0;
3632
+ while (queue.length > 0) {
3633
+ if (helper++ > 1e6) {
3634
+ break;
3538
3635
  }
3539
- }
3540
- if (kinship !== "child" || !breakAfterSpouse) {
3541
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3636
+ const {
3637
+ indi,
3638
+ path,
3639
+ kinship,
3640
+ relation,
3641
+ level,
3642
+ levelUp,
3643
+ levelDown,
3644
+ degree,
3645
+ breakOnNext,
3646
+ breakAfterNext,
3647
+ inLaw
3648
+ } = queue.shift();
3649
+ if (usedIndi.id === indi.id) {
3650
+ if (breakOnNext) {
3651
+ return void 0;
3652
+ }
3653
+ return path;
3654
+ }
3655
+ visited.append(indi);
3656
+ const additional = {};
3657
+ if (breakOnNext || breakAfterNext) {
3658
+ additional.breakOnNext = breakOnNext || breakAfterNext;
3659
+ }
3660
+ if (inLaw) {
3661
+ additional.inLaw = inLaw;
3662
+ }
3663
+ if (!biologicalOnly && kinship === "spouse" && breakAfterSpouse) {
3664
+ if (path.length <= 2) {
3665
+ additional.inLaw = true;
3666
+ } else {
3667
+ additional.breakOnNext = true;
3668
+ }
3669
+ }
3670
+ if (kinship !== "child" || !breakAfterSpouse) {
3671
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3672
+ if (!visited.has(relative)) {
3673
+ const currentRelation = indi.getParentType(relative);
3674
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3675
+ if (biologicalOnly && isNonBiological) {
3676
+ return;
3677
+ }
3678
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
3679
+ additional.breakAfterNext = true;
3680
+ }
3681
+ const newItem = {
3682
+ indi: relative,
3683
+ kinship: "parent",
3684
+ relation: isNonBiological ? currentRelation : relation,
3685
+ level: level + 1,
3686
+ levelUp: levelUp + 1,
3687
+ levelDown,
3688
+ degree,
3689
+ ...additional
3690
+ };
3691
+ queue.push({
3692
+ ...newItem,
3693
+ path: [...path, newItem]
3694
+ });
3695
+ }
3696
+ });
3697
+ }
3698
+ indi.getChildren().forEach((relative) => {
3542
3699
  if (!visited.has(relative)) {
3543
- const currentRelation = indi.getParentType(relative);
3544
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
3700
+ const currentRelation = relative.getParentType(indi);
3701
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3702
+ if (biologicalOnly && isNonBiological) {
3703
+ return;
3704
+ }
3705
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
3545
3706
  additional.breakAfterNext = true;
3546
3707
  }
3547
3708
  const newItem = {
3548
3709
  indi: relative,
3549
- kinship: "parent",
3550
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
3551
- level: level + 1,
3552
- levelUp: levelUp + 1,
3553
- levelDown,
3554
- degree,
3710
+ kinship: "child",
3711
+ relation: isNonBiological ? currentRelation : relation,
3712
+ level: level - 1,
3713
+ levelUp,
3714
+ levelDown: levelDown + 1,
3715
+ degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
3555
3716
  ...additional
3556
3717
  };
3557
3718
  queue.push({
3558
3719
  ...newItem,
3559
- path: [...path2, newItem]
3720
+ path: [...path, newItem]
3560
3721
  });
3561
3722
  }
3562
3723
  });
3563
- }
3564
- indi.getChildren().forEach((relative) => {
3565
- if (!visited.has(relative)) {
3566
- const currentRelation = relative.getParentType(indi);
3567
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
3568
- additional.breakAfterNext = true;
3569
- }
3570
- const newItem = {
3571
- indi: relative,
3572
- kinship: "child",
3573
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
3574
- level: level - 1,
3575
- levelUp,
3576
- levelDown: levelDown + 1,
3577
- degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
3578
- ...additional
3579
- };
3580
- queue.push({
3581
- ...newItem,
3582
- path: [...path2, newItem]
3583
- });
3724
+ if (biologicalOnly) {
3725
+ continue;
3584
3726
  }
3585
- });
3586
- indi.getCoParents().forEach((relative) => {
3587
- if (!visited.has(relative)) {
3588
- const currentAddition = {};
3589
- if (relation && relation !== "biological" /* BIOLOGICAL */) {
3590
- currentAddition.relation = relation;
3591
- }
3592
- if (inLaw) {
3593
- currentAddition.breakOnNext = true;
3727
+ indi.getCoParents().forEach((relative) => {
3728
+ if (!visited.has(relative)) {
3729
+ const currentAddition = {};
3730
+ if (relation && relation !== "biological" /* BIOLOGICAL */) {
3731
+ currentAddition.relation = relation;
3732
+ }
3733
+ if (inLaw) {
3734
+ currentAddition.breakOnNext = true;
3735
+ }
3736
+ const newItem = {
3737
+ indi: relative,
3738
+ kinship: "spouse",
3739
+ level,
3740
+ levelUp,
3741
+ levelDown,
3742
+ degree,
3743
+ ...additional,
3744
+ ...currentAddition
3745
+ };
3746
+ queue.push({
3747
+ ...newItem,
3748
+ path: [...path, newItem]
3749
+ });
3594
3750
  }
3595
- const newItem = {
3596
- indi: relative,
3597
- kinship: "spouse",
3598
- level,
3599
- levelUp,
3600
- levelDown,
3601
- degree,
3602
- ...additional,
3603
- ...currentAddition
3604
- };
3605
- queue.push({
3606
- ...newItem,
3607
- path: [...path2, newItem]
3608
- });
3609
- }
3610
- });
3751
+ });
3752
+ }
3753
+ return void 0;
3754
+ };
3755
+ const foundPath = bfs(false);
3756
+ const lastItem = foundPath?.[foundPath.length - 1];
3757
+ if (lastItem?.relation && lastItem.relation !== "biological" /* BIOLOGICAL */) {
3758
+ const biologicalPath = bfs(true);
3759
+ if (biologicalPath) {
3760
+ cacheDiscoveredPath(this._gedcom, biologicalPath);
3761
+ return biologicalPath;
3762
+ }
3611
3763
  }
3612
- return void 0;
3764
+ if (foundPath) {
3765
+ cacheDiscoveredPath(this._gedcom, foundPath);
3766
+ }
3767
+ return foundPath;
3613
3768
  }
3614
3769
  kinship(other, showMainPerson, lang = "en", entirePath, displayName = "givenname") {
3615
3770
  const KinshipTranslatorClass2 = getKinshipTranslatorClass();
@@ -4581,6 +4736,44 @@ var KinshipTranslatorBasic = class {
4581
4736
  }
4582
4737
  return this.pathN?.indi;
4583
4738
  }
4739
+ /**
4740
+ * True when the path goes through a common ancestor whose two children
4741
+ * (the person we came from and the person we go to) share fewer than two
4742
+ * biological parents — i.e. they are half-siblings, so every collateral
4743
+ * relative on this path is a half-aunt/half-cousin/... too.
4744
+ *
4745
+ * Direct ancestors/descendants and paths that already use a
4746
+ * step/adopted/foster edge are never half-blood.
4747
+ */
4748
+ get isHalfBlood() {
4749
+ const path = this.path;
4750
+ if (!path || path.length < 3) {
4751
+ return false;
4752
+ }
4753
+ if (path.some(
4754
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
4755
+ )) {
4756
+ return false;
4757
+ }
4758
+ let peakIndex = -1;
4759
+ for (let i = 1; i < path.length; i++) {
4760
+ if (path[i].kinship === "parent") {
4761
+ peakIndex = i;
4762
+ }
4763
+ }
4764
+ if (peakIndex <= 0 || peakIndex >= path.length - 1) {
4765
+ return false;
4766
+ }
4767
+ if (path[peakIndex + 1].kinship !== "child") {
4768
+ return false;
4769
+ }
4770
+ const siblingA = path[peakIndex - 1].indi;
4771
+ const siblingB = path[peakIndex + 1].indi;
4772
+ const parentsA = siblingA.getBiologicalParents();
4773
+ const parentsB = siblingB.getBiologicalParents();
4774
+ const inter = parentsA.intersection(parentsB);
4775
+ return inter.length === 1 && (parentsA.length > 1 || parentsB.length > 1);
4776
+ }
4584
4777
  indirect() {
4585
4778
  return "";
4586
4779
  }
@@ -4608,6 +4801,9 @@ var KinshipTranslatorBasic = class {
4608
4801
  relationType(relation) {
4609
4802
  return relation ?? "";
4610
4803
  }
4804
+ halfBlood(relation) {
4805
+ return relation ?? "";
4806
+ }
4611
4807
  of(relation) {
4612
4808
  return relation ?? "";
4613
4809
  }
@@ -4846,14 +5042,34 @@ var KinshipTranslatorHU = class extends KinshipTranslatorBasic {
4846
5042
  return `${prefix.replace(/nagy$/, "")}unoka`;
4847
5043
  }
4848
5044
  sibling() {
4849
- const parents1 = this.person1?.getBiologicalParents();
4850
- const parentsN = this.personN?.getBiologicalParents();
4851
- const inter = parents1?.intersection(parentsN);
4852
- if (!inter || inter.length < 2) {
4853
- return "f\xE9ltestv\xE9r";
4854
- }
4855
5045
  return "testv\xE9r";
4856
5046
  }
5047
+ halfBlood(relation) {
5048
+ if (!relation || !this.isHalfBlood) {
5049
+ return relation ?? "";
5050
+ }
5051
+ const [head, ...rest] = relation.split(" ");
5052
+ if (!head) {
5053
+ return relation;
5054
+ }
5055
+ const prefixed = this.prefixHalf(head);
5056
+ return [prefixed, ...rest].join(" ");
5057
+ }
5058
+ /**
5059
+ * AkH. 139: compounds of more than two members stay closed up to 6
5060
+ * syllables; longer ones are hyphenated at the main boundary. A head
5061
+ * that is already hyphenated also takes a hyphenated half- prefix.
5062
+ */
5063
+ prefixHalf(head) {
5064
+ if (head.includes("-") || !/[aáeéiíoóöőuúüű]/i.test(head)) {
5065
+ return `f\xE9l-${head}`;
5066
+ }
5067
+ const syllables = (head.match(/[aáeéiíoóöőuúüű]/gi) ?? []).length + 1;
5068
+ if (syllables > 6) {
5069
+ return `f\xE9l-${head}`;
5070
+ }
5071
+ return `f\xE9l${head}`;
5072
+ }
4857
5073
  spouse() {
4858
5074
  if (this.personN?.isMale()) {
4859
5075
  return "f\xE9rj";
@@ -5052,20 +5268,22 @@ var KinshipTranslatorEn = class extends KinshipTranslatorBasic {
5052
5268
  return `${prefix}child`;
5053
5269
  }
5054
5270
  sibling() {
5055
- const parents1 = this.person1?.getBiologicalParents();
5056
- const parentsN = this.personN?.getBiologicalParents();
5057
- const inter = parents1?.intersection(parentsN);
5058
- let prefix = "";
5059
- if (!inter || inter.length < 2) {
5060
- prefix = "half-";
5061
- }
5062
5271
  if (this.personN?.isMale()) {
5063
- return `${prefix}brother`;
5272
+ return "brother";
5064
5273
  }
5065
5274
  if (this.personN?.isFemale()) {
5066
- return `${prefix}sister`;
5275
+ return "sister";
5276
+ }
5277
+ return "sibling";
5278
+ }
5279
+ halfBlood(relation) {
5280
+ if (!relation || !this.isHalfBlood) {
5281
+ return relation ?? "";
5282
+ }
5283
+ if (relation.includes(" ")) {
5284
+ return `half ${relation}`;
5067
5285
  }
5068
- return `${prefix}sibling`;
5286
+ return `half-${relation}`;
5069
5287
  }
5070
5288
  spouse() {
5071
5289
  if (this.personN?.isMale()) {
@@ -5247,20 +5465,22 @@ var KinshipTranslatorDe = class extends KinshipTranslatorBasic {
5247
5465
  return `${prefix}kind`;
5248
5466
  }
5249
5467
  sibling() {
5250
- const parents1 = this.person1?.getBiologicalParents();
5251
- const parentsN = this.personN?.getBiologicalParents();
5252
- const inter = parents1?.intersection(parentsN);
5253
- let prefix = "";
5254
- if (!inter || inter.length < 2) {
5255
- prefix = "Halb";
5256
- }
5257
5468
  if (this.personN?.isMale()) {
5258
- return `${prefix}bruder`;
5469
+ return "bruder";
5259
5470
  }
5260
5471
  if (this.personN?.isFemale()) {
5261
- return `${prefix}schwester`;
5472
+ return "schwester";
5473
+ }
5474
+ return "geschwister";
5475
+ }
5476
+ halfBlood(relation) {
5477
+ if (!relation || !this.isHalfBlood) {
5478
+ return relation ?? "";
5479
+ }
5480
+ if (/^[A-ZÄÖÜ]/.test(relation) || /[-\s]/.test(relation)) {
5481
+ return `Halb-${relation}`;
5262
5482
  }
5263
- return `${prefix}geschwister`;
5483
+ return `Halb${relation}`;
5264
5484
  }
5265
5485
  spouse() {
5266
5486
  if (this.personN?.isMale()) {
@@ -5442,20 +5662,19 @@ var KinshipTranslatorFr = class extends KinshipTranslatorBasic {
5442
5662
  return `${prefix}enfant`;
5443
5663
  }
5444
5664
  sibling() {
5445
- const parents1 = this.person1?.getBiologicalParents();
5446
- const parentsN = this.personN?.getBiologicalParents();
5447
- const inter = parents1?.intersection(parentsN);
5448
- let prefix = "";
5449
- if (!inter || inter.length < 2) {
5450
- prefix = "demi-";
5451
- }
5452
5665
  if (this.personN?.isMale()) {
5453
- return `${prefix}fr\xE8re`;
5666
+ return "fr\xE8re";
5454
5667
  }
5455
5668
  if (this.personN?.isFemale()) {
5456
- return `${prefix}s\u0153ur`;
5669
+ return "s\u0153ur";
5670
+ }
5671
+ return "fr\xE8re/s\u0153ur";
5672
+ }
5673
+ halfBlood(relation) {
5674
+ if (!relation || !this.isHalfBlood) {
5675
+ return relation ?? "";
5457
5676
  }
5458
- return `${prefix}fr\xE8re/s\u0153ur`;
5677
+ return `demi-${relation}`;
5459
5678
  }
5460
5679
  spouse() {
5461
5680
  if (this.personN?.isMale()) {
@@ -5552,12 +5771,12 @@ var InLawsEs = {
5552
5771
  "madre del (c\xF3nyuge|esposa|esposo)": "suegra",
5553
5772
  "padre del (c\xF3nyuge|esposa|esposo)": "suegro",
5554
5773
  "padre/madre del (c\xF3nyuge|esposa|esposo)": "suegro/suegra",
5555
- "esposa de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
5556
- "esposo de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
5557
- "c\xF3nyuge de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
5558
- "(medio )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
5559
- "(medio )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
5560
- "(medio )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
5774
+ "esposa de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
5775
+ "esposo de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
5776
+ "c\xF3nyuge de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
5777
+ "(medio |media |medio/a )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
5778
+ "(medio |media |medio/a )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
5779
+ "(medio |media |medio/a )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
5561
5780
  };
5562
5781
 
5563
5782
  // src/kinship-translator/kinship-translator.es.ts
@@ -5637,20 +5856,25 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
5637
5856
  return `${prefix}hijo/hija`;
5638
5857
  }
5639
5858
  sibling() {
5640
- const parents1 = this.person1?.getBiologicalParents();
5641
- const parentsN = this.personN?.getBiologicalParents();
5642
- const inter = parents1?.intersection(parentsN);
5643
- let prefix = "";
5644
- if (!inter || inter.length < 2) {
5645
- prefix = "medio ";
5646
- }
5647
5859
  if (this.personN?.isMale()) {
5648
- return `${prefix}hermano`;
5860
+ return "hermano";
5861
+ }
5862
+ if (this.personN?.isFemale()) {
5863
+ return "hermana";
5864
+ }
5865
+ return "hermano/hermana";
5866
+ }
5867
+ halfBlood(relation) {
5868
+ if (!relation || !this.isHalfBlood) {
5869
+ return relation ?? "";
5649
5870
  }
5650
5871
  if (this.personN?.isFemale()) {
5651
- return `${prefix}hermana`;
5872
+ return `media ${relation}`;
5873
+ }
5874
+ if (this.personN?.isMale()) {
5875
+ return `medio ${relation}`;
5652
5876
  }
5653
- return `${prefix}hermano/hermana`;
5877
+ return `medio/a ${relation}`;
5654
5878
  }
5655
5879
  spouse() {
5656
5880
  if (this.personN?.isMale()) {
@@ -5739,6 +5963,249 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
5739
5963
  }
5740
5964
  };
5741
5965
 
5966
+ // src/kinship-translator/patterns.ru.ts
5967
+ var InLawsRu = {
5968
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
5969
+ "\u0436\u0435\u043D\u0430 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
5970
+ "\u043C\u0443\u0436 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C",
5971
+ "\u043C\u0430\u0442\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0451\u0449\u0430",
5972
+ "\u043E\u0442\u0435\u0446 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C",
5973
+ "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C/\u0442\u0451\u0449\u0430",
5974
+ "\u0436\u0435\u043D\u0430 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
5975
+ "\u043C\u0443\u0436 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C",
5976
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
5977
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0435\u0441\u0442\u0440\u0430 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0437\u043E\u043B\u043E\u0432\u043A\u0430",
5978
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0431\u0440\u0430\u0442 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D",
5979
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0438\u0431\u043B\u0438\u043D\u0433 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D/\u0437\u043E\u043B\u043E\u0432\u043A\u0430"
5980
+ };
5981
+
5982
+ // src/kinship-translator/kinship-translator.ru.ts
5983
+ var GENITIVE = {
5984
+ \u0441\u044B\u043D: "\u0441\u044B\u043D\u0430",
5985
+ \u0434\u043E\u0447\u044C: "\u0434\u043E\u0447\u0435\u0440\u0438",
5986
+ \u0440\u0435\u0431\u0451\u043D\u043E\u043A: "\u0440\u0435\u0431\u0451\u043D\u043A\u0430",
5987
+ \u0431\u0440\u0430\u0442: "\u0431\u0440\u0430\u0442\u0430",
5988
+ \u0441\u0435\u0441\u0442\u0440\u0430: "\u0441\u0435\u0441\u0442\u0440\u044B",
5989
+ \u0441\u0438\u0431\u043B\u0438\u043D\u0433: "\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430",
5990
+ \u043E\u0442\u0435\u0446: "\u043E\u0442\u0446\u0430",
5991
+ \u043C\u0430\u0442\u044C: "\u043C\u0430\u0442\u0435\u0440\u0438",
5992
+ \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C: "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044F",
5993
+ \u043C\u0443\u0436: "\u043C\u0443\u0436\u0430",
5994
+ \u0436\u0435\u043D\u0430: "\u0436\u0435\u043D\u044B",
5995
+ \u0441\u0443\u043F\u0440\u0443\u0433: "\u0441\u0443\u043F\u0440\u0443\u0433\u0430",
5996
+ \u0441\u0443\u043F\u0440\u0443\u0433\u0430: "\u0441\u0443\u043F\u0440\u0443\u0433\u0438"
5997
+ };
5998
+ var toGenitive = (word) => GENITIVE[word] ?? word;
5999
+ var cousinWord = (degree, sex) => {
6000
+ const stem = degree <= 1 ? "\u0434\u0432\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 2 ? "\u0442\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 3 ? "\u0447\u0435\u0442\u0432\u0435\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : `${degree + 1}-\u044E\u0440\u043E\u0434\u043D`;
6001
+ if (sex === "m") {
6002
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442`;
6003
+ }
6004
+ if (sex === "f") {
6005
+ return `${stem}\u0430\u044F \u0441\u0435\u0441\u0442\u0440\u0430`;
6006
+ }
6007
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442/\u0441\u0435\u0441\u0442\u0440\u0430`;
6008
+ };
6009
+ var praRepeat = (count) => count > 0 ? "\u043F\u0440\u0430".repeat(count) : "";
6010
+ var sexOf = (person) => {
6011
+ if (person?.isMale?.()) {
6012
+ return "m";
6013
+ }
6014
+ if (person?.isFemale?.()) {
6015
+ return "f";
6016
+ }
6017
+ return "n";
6018
+ };
6019
+ var KinshipTranslatorRu = class extends KinshipTranslatorBasic {
6020
+ indirect() {
6021
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6022
+ if (degree > 0) {
6023
+ degree = degree - 1;
6024
+ }
6025
+ return cousinWord(degree, sexOf(this.personN));
6026
+ }
6027
+ removal() {
6028
+ const level = this.pathN?.level ?? 0;
6029
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6030
+ if (degree > 0) {
6031
+ degree = degree - 1;
6032
+ }
6033
+ if (!degree) {
6034
+ return level < 0 ? this.nibling() : this.auncle();
6035
+ }
6036
+ const removed = Math.abs(level);
6037
+ return `${cousinWord(degree, sexOf(this.personN))} (${removed}\xD7 \u0447\u0435\u0440\u0435\u0437 \u043F\u043E\u043A\u043E\u043B\u0435\u043D\u0438\u0435)`;
6038
+ }
6039
+ auncle() {
6040
+ const level = Math.abs(this.pathN?.level ?? 0);
6041
+ const pra = praRepeat(Math.max(0, level - 2));
6042
+ const sex = sexOf(this.personN);
6043
+ if (sex === "m") {
6044
+ return `${pra}\u0434\u044F\u0434\u044F`;
6045
+ }
6046
+ if (sex === "f") {
6047
+ return `${pra}\u0442\u0451\u0442\u044F`;
6048
+ }
6049
+ return `${pra}\u0434\u044F\u0434\u044F/\u0442\u0451\u0442\u044F`;
6050
+ }
6051
+ nibling() {
6052
+ const level = Math.abs(this.pathN?.level ?? 0);
6053
+ const pra = praRepeat(Math.max(0, level - 2));
6054
+ const sex = sexOf(this.personN);
6055
+ if (sex === "m") {
6056
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A`;
6057
+ }
6058
+ if (sex === "f") {
6059
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6060
+ }
6061
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A/\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6062
+ }
6063
+ parent() {
6064
+ const level = Math.abs(this.pathN?.level ?? 0);
6065
+ const sex = sexOf(this.personN);
6066
+ if (level <= 1) {
6067
+ if (sex === "m") {
6068
+ return "\u043E\u0442\u0435\u0446";
6069
+ }
6070
+ if (sex === "f") {
6071
+ return "\u043C\u0430\u0442\u044C";
6072
+ }
6073
+ return "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C";
6074
+ }
6075
+ if (level === 2) {
6076
+ if (sex === "m") {
6077
+ return "\u0434\u0435\u0434";
6078
+ }
6079
+ if (sex === "f") {
6080
+ return "\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6081
+ }
6082
+ return "\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6083
+ }
6084
+ const pra = praRepeat(level - 2);
6085
+ if (sex === "m") {
6086
+ return `${pra}\u0434\u0435\u0434`;
6087
+ }
6088
+ if (sex === "f") {
6089
+ return `${pra}\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6090
+ }
6091
+ return `${pra}\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6092
+ }
6093
+ child() {
6094
+ const level = Math.abs(this.pathN?.level ?? 0);
6095
+ const sex = sexOf(this.personN);
6096
+ if (level <= 1) {
6097
+ if (sex === "m") {
6098
+ return "\u0441\u044B\u043D";
6099
+ }
6100
+ if (sex === "f") {
6101
+ return "\u0434\u043E\u0447\u044C";
6102
+ }
6103
+ return "\u0440\u0435\u0431\u0451\u043D\u043E\u043A";
6104
+ }
6105
+ if (level === 2) {
6106
+ if (sex === "m") {
6107
+ return "\u0432\u043D\u0443\u043A";
6108
+ }
6109
+ if (sex === "f") {
6110
+ return "\u0432\u043D\u0443\u0447\u043A\u0430";
6111
+ }
6112
+ return "\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430";
6113
+ }
6114
+ const pra = praRepeat(level - 2);
6115
+ if (sex === "m") {
6116
+ return `${pra}\u0432\u043D\u0443\u043A`;
6117
+ }
6118
+ if (sex === "f") {
6119
+ return `${pra}\u0432\u043D\u0443\u0447\u043A\u0430`;
6120
+ }
6121
+ return `${pra}\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430`;
6122
+ }
6123
+ sibling() {
6124
+ const sex = sexOf(this.personN);
6125
+ if (sex === "m") {
6126
+ return "\u0431\u0440\u0430\u0442";
6127
+ }
6128
+ if (sex === "f") {
6129
+ return "\u0441\u0435\u0441\u0442\u0440\u0430";
6130
+ }
6131
+ return "\u0441\u0438\u0431\u043B\u0438\u043D\u0433";
6132
+ }
6133
+ halfBlood(relation) {
6134
+ if (!relation || !this.isHalfBlood) {
6135
+ return relation ?? "";
6136
+ }
6137
+ if (/сестра|дочь|мать|тётя|бабушка|жена|племянница|внучка/.test(
6138
+ relation
6139
+ )) {
6140
+ return `\u0441\u0432\u043E\u0434\u043D\u0430\u044F ${relation}`;
6141
+ }
6142
+ return `\u0441\u0432\u043E\u0434\u043D\u044B\u0439 ${relation}`;
6143
+ }
6144
+ spouse() {
6145
+ const sex = sexOf(this.personN);
6146
+ if (sex === "m") {
6147
+ return "\u043C\u0443\u0436";
6148
+ }
6149
+ if (sex === "f") {
6150
+ return "\u0436\u0435\u043D\u0430";
6151
+ }
6152
+ return "\u0441\u0443\u043F\u0440\u0443\u0433";
6153
+ }
6154
+ ofSpouse(relation) {
6155
+ if (!relation) {
6156
+ return "";
6157
+ }
6158
+ const spouse = this.path?.[1].indi;
6159
+ const sex = sexOf(spouse);
6160
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6161
+ return `${relation} ${toGenitive(spouseType)}`;
6162
+ }
6163
+ spouseOf(relation) {
6164
+ if (!relation) {
6165
+ return "";
6166
+ }
6167
+ const spouse = this.path?.[this.path.length - 1].indi;
6168
+ const sex = sexOf(spouse);
6169
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6170
+ return `${spouseType} ${toGenitive(relation)}`;
6171
+ }
6172
+ of(relation) {
6173
+ const formattedName = nameFormatter(this.path?.[0]?.indi, {
6174
+ nameOrder: "first-last"
6175
+ });
6176
+ let name = formattedName.inOrder.filter(Boolean).join(" ");
6177
+ if (this.displayName === "none") {
6178
+ name = "";
6179
+ } else if (this.displayName === "givenname") {
6180
+ name = formattedName.givenname;
6181
+ } else if (this.displayName === "surname") {
6182
+ name = `${formattedName.surname}`;
6183
+ }
6184
+ if (!name || !relation) {
6185
+ return relation ?? "";
6186
+ }
6187
+ return `${name}: ${relation}`;
6188
+ }
6189
+ inLaw(relation) {
6190
+ let inLawRelation = relation;
6191
+ Object.keys(InLawsRu).find((pattern) => {
6192
+ const regex = new RegExp(`^${pattern}$`);
6193
+ if (relation?.match(regex)) {
6194
+ inLawRelation = InLawsRu[pattern];
6195
+ return true;
6196
+ }
6197
+ return false;
6198
+ });
6199
+ return inLawRelation ?? "";
6200
+ }
6201
+ relationType(relation) {
6202
+ if (!relation || !this.pathN?.relation) {
6203
+ return relation ?? "";
6204
+ }
6205
+ return `${this.pathN.relation} ${relation}`;
6206
+ }
6207
+ };
6208
+
5742
6209
  // src/kinship-translator/kinship-translator.ts
5743
6210
  var KinshipTranslator = class {
5744
6211
  constructor(person1, person2, lang = "en", entirePath, displayName = "givenname") {
@@ -5832,6 +6299,7 @@ var KinshipTranslator = class {
5832
6299
  } else if (personN.level !== 0) {
5833
6300
  relation = this.removal();
5834
6301
  }
6302
+ relation = this.translator?.halfBlood(relation);
5835
6303
  relation = this.translator?.relationType(relation);
5836
6304
  if (this.translator?.isOfSpouse) {
5837
6305
  relation = this.translator?.ofSpouse(relation);