RelationshipService::legacyCousinName2()   D
last analyzed

Complexity

Conditions 18

Size

Total Lines 67
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 41
nop 3
dl 0
loc 67
rs 4.8666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Services;
21
22
use Fisharebest\Webtrees\Auth;
23
use Fisharebest\Webtrees\Fact;
24
use Fisharebest\Webtrees\Family;
25
use Fisharebest\Webtrees\I18N;
26
use Fisharebest\Webtrees\Individual;
27
use Fisharebest\Webtrees\Module\ModuleLanguageInterface;
28
use Fisharebest\Webtrees\Relationship;
29
30
use function abs;
31
use function array_key_exists;
32
use function array_merge;
33
use function array_reduce;
34
use function array_slice;
35
use function count;
36
use function implode;
37
use function intdiv;
38
use function min;
39
use function preg_match;
40
use function sprintf;
41
use function strlen;
42
use function substr;
43
44
/**
45
 * Names for relationships.
46
 */
47
class RelationshipService
48
{
49
    private const COMPONENTS = [
50
        'CHIL' => [
51
            'CHIL' => Relationship::SIBLINGS,
52
            'HUSB' => Relationship::PARENTS,
53
            'WIFE' => Relationship::PARENTS,
54
        ],
55
        'HUSB' => [
56
            'CHIL' => Relationship::CHILDREN,
57
            'HUSB' => Relationship::SPOUSES,
58
            'WIFE' => Relationship::SPOUSES,
59
        ],
60
        'WIFE' => [
61
            'CHIL' => Relationship::CHILDREN,
62
            'HUSB' => Relationship::SPOUSES,
63
            'WIFE' => Relationship::SPOUSES,
64
        ],
65
    ];
66
67
    /**
68
     * For close family relationships, such as the families tab, associates, and the family navigator.
69
     *
70
     * @param Individual $individual1
71
     * @param Individual $individual2
72
     *
73
     * @return string
74
     */
75
    public function getCloseRelationshipName(Individual $individual1, Individual $individual2): string
76
    {
77
        $language = app(ModuleService::class)
78
            ->findByInterface(ModuleLanguageInterface::class, true)
79
            ->first(fn (ModuleLanguageInterface $language): bool => $language->locale()->languageTag() === I18N::languageTag());
80
81
        $path = $this->getCloseRelationship($individual1, $individual2);
82
83
        // No relationship found?
84
        if ($path === []) {
85
            return '';
86
        }
87
88
        return $this->nameFromPath($path, $language);
89
    }
90
91
    /**
92
     * Get relationship between two individuals in the gedcom.  This function
93
     * takes account of pending changes, so we can display names of newly added
94
     * relations.
95
     *
96
     * @param Individual $individual1
97
     * @param Individual $individual2
98
     * @param int        $maxlength
99
     *
100
     * @return array<Individual|Family> An array of nodes on the relationship path
101
     */
102
    private function getCloseRelationship(Individual $individual1, Individual $individual2, int $maxlength = 4): array
103
    {
104
        if ($individual1 === $individual2) {
105
            return [$individual1];
106
        }
107
108
        // Only examine each individual once
109
        $visited = [
110
            $individual1->xref() => true,
111
        ];
112
113
        // Build paths out from the first individual
114
        $paths = [
115
            [$individual1],
116
        ];
117
118
        // Loop over paths of length 1, 2, 3, ...
119
        while ($maxlength >= 0) {
120
            $maxlength--;
121
122
            foreach ($paths as $i => $path) {
123
                // Try each new relation from the end of the path
124
                $indi = $path[count($path) - 1];
125
126
                // Parents and siblings
127
                foreach ($indi->childFamilies(Auth::PRIV_HIDE) as $family) {
128
                    $visited[$family->xref()] = true;
129
                    foreach ($family->spouses(Auth::PRIV_HIDE) as $spouse) {
130
                        if (!isset($visited[$spouse->xref()])) {
131
                            $new_path   = $path;
132
                            $new_path[] = $family;
133
                            $new_path[] = $spouse;
134
                            if ($spouse === $individual2) {
135
                                return $new_path;
136
                            }
137
138
                            $paths[]                  = $new_path;
139
                            $visited[$spouse->xref()] = true;
140
                        }
141
                    }
142
                    foreach ($family->children(Auth::PRIV_HIDE) as $child) {
143
                        if (!isset($visited[$child->xref()])) {
144
                            $new_path   = $path;
145
                            $new_path[] = $family;
146
                            $new_path[] = $child;
147
                            if ($child === $individual2) {
148
                                return $new_path;
149
                            }
150
151
                            $paths[]                 = $new_path;
152
                            $visited[$child->xref()] = true;
153
                        }
154
                    }
155
                }
156
157
                // Spouses and children
158
                foreach ($indi->spouseFamilies(Auth::PRIV_HIDE) as $family) {
159
                    $visited[$family->xref()] = true;
160
                    foreach ($family->spouses(Auth::PRIV_HIDE) as $spouse) {
161
                        if (!isset($visited[$spouse->xref()])) {
162
                            $new_path   = $path;
163
                            $new_path[] = $family;
164
                            $new_path[] = $spouse;
165
                            if ($spouse === $individual2) {
166
                                return $new_path;
167
                            }
168
169
                            $paths[]                  = $new_path;
170
                            $visited[$spouse->xref()] = true;
171
                        }
172
                    }
173
                    foreach ($family->children(Auth::PRIV_HIDE) as $child) {
174
                        if (!isset($visited[$child->xref()])) {
175
                            $new_path   = $path;
176
                            $new_path[] = $family;
177
                            $new_path[] = $child;
178
                            if ($child === $individual2) {
179
                                return $new_path;
180
                            }
181
182
                            $paths[]                 = $new_path;
183
                            $visited[$child->xref()] = true;
184
                        }
185
                    }
186
                }
187
                unset($paths[$i]);
188
            }
189
        }
190
191
        return [];
192
    }
193
194
    /**
195
     * @param array<Individual|Family> $nodes
196
     * @param ModuleLanguageInterface  $language
197
     *
198
     * @return string
199
     */
200
    public function nameFromPath(array $nodes, ModuleLanguageInterface $language): string
201
    {
202
        // The relationship matching algorithm could be used for this, but it is more efficient to check it here.
203
        if (count($nodes) === 1) {
204
            return $this->reflexivePronoun($nodes[0]);
205
        }
206
207
        // The relationship definitions for the language.
208
        $relationships = $language->relationships();
209
210
        // We don't strictly need this, as all the information is contained in the nodes.
211
        // But it gives us simpler code and better performance.
212
        // It is also needed for the legacy algorithm.
213
        $pattern = $this->components($nodes);
214
215
        // No definitions for this language?  Use the legacy algorithm.
216
        if ($relationships === []) {
217
            return $this->legacyNameAlgorithm(implode('', $pattern), $nodes[0], $nodes[count($nodes) - 1]);
218
        }
219
220
        // Match the relationship, using a longest-substring algorithm.
221
        $relationships = $this->matchRelationships($nodes, $pattern, $relationships);
222
223
        // Reduce the genitive-nominative chain to a single string.
224
        return array_reduce($relationships, static function (array $carry, array $item): array {
225
            return [sprintf($carry[1], $item[0]), sprintf($carry[1], $item[1])];
226
        }, [0 => '', 1 => '%s'])[0];
227
    }
228
229
    /**
230
     * Generate a reflexive pronoun for an individual
231
     *
232
     * @param Individual $individual
233
     *
234
     * @return string
235
     */
236
    protected function reflexivePronoun(Individual $individual): string
237
    {
238
        switch ($individual->sex()) {
239
            case 'M':
240
                /* I18N: reflexive pronoun */
241
                return I18N::translate('himself');
242
            case 'F':
243
                /* I18N: reflexive pronoun */
244
                return I18N::translate('herself');
245
            default:
246
                /* I18N: reflexive pronoun - gender neutral version of himself/herself */
247
                return I18N::translate('themself');
248
        }
249
    }
250
251
    /**
252
     * Convert a relationship path into its component pieces; brother, wife, mother, daughter, etc.
253
     *
254
     * @param array<Individual|Family> $nodes Alternating list of Individual and Family objects
255
     *
256
     * @return array<string>
257
     */
258
    private function components(array $nodes): array
259
    {
260
        $pattern = [];
261
262
        $count = count($nodes);
263
264
        for ($i = 1; $i < $count; $i += 2) {
265
            $prev   = $nodes[$i - 1];
266
            $family = $nodes[$i];
267
            $next   = $nodes[$i + 1];
268
269
            preg_match('/\n1 (HUSB|WIFE|CHIL) @' . $prev->xref() . '@/', $family->gedcom(), $match);
270
            $rel1 = $match[1] ?? 'xxx';
271
272
            preg_match('/\n1 (HUSB|WIFE|CHIL) @' . $next->xref() . '@/', $family->gedcom(), $match);
273
            $rel2 = $match[1] ?? 'xxx';
274
275
            $pattern[] = self::COMPONENTS[$rel1][$rel2][$next->sex()] ?? 'xxx';
276
        }
277
278
        return $pattern;
279
    }
280
281
    /**
282
     * @param array<Individual|Family> $nodes
283
     * @param array<string>            $pattern
284
     * @param array<Relationship>      $relationships
285
     *
286
     * @return array<Relationship>
287
     */
288
    protected function matchRelationships(array $nodes, array $pattern, array $relationships): array
289
    {
290
        $count = count($pattern);
291
292
        // Look for the longest matchable series of components
293
        for ($length = $count; $length > 0; $length--) {
294
            for ($start = $count - $length; $start >= 0; $start--) {
295
                foreach ($relationships as $relationship) {
296
                    $path_slice    = array_slice($nodes, $start * 2, $length * 2 + 1);
297
                    $pattern_slice = array_slice($pattern, $start, $length);
298
                    $result        = $relationship->match($path_slice, $pattern_slice);
299
300
                    if ($result !== null) {
301
                        $nodes_before   = array_slice($nodes, 0, $start * 2 + 1);
302
                        $pattern_before = array_slice($pattern, 0, $start);
303
                        $result_before  = $this->matchRelationships($nodes_before, $pattern_before, $relationships);
304
305
                        $nodes_after   = array_slice($nodes, ($start + $length) * 2);
306
                        $pattern_after = array_slice($pattern, $start + $length);
307
                        $result_after  = $this->matchRelationships($nodes_after, $pattern_after, $relationships);
308
309
                        return array_merge($result_before, [$result], $result_after);
310
                    }
311
                }
312
            }
313
        }
314
315
        return [];
316
    }
317
318
    /**
319
     * @param string          $path
320
     * @param Individual|null $person1
321
     * @param Individual|null $person2
322
     *
323
     * @return string
324
     *
325
     * @deprecated This code was originally Functions::getRelationshipNameFromPath
326
     */
327
    public function legacyNameAlgorithm(string $path, ?Individual $person1 = null, ?Individual $person2 = null): string
328
    {
329
        // The path does not include the starting person. In some languages, the
330
        // translation for a man’s (relative) is different from a woman’s (relative),
331
        // due to inflection.
332
        $sex1 = $person1 ? $person1->sex() : 'U';
333
334
        // The sex of the last person in the relationship determines the name in
335
        // many cases. e.g. great-aunt / great-uncle
336
        if (preg_match('/(fat|hus|son|bro)$/', $path) === 1) {
337
            $sex2 = 'M';
338
        } elseif (preg_match('/(mot|wif|dau|sis)$/', $path) === 1) {
339
            $sex2 = 'F';
340
        } else {
341
            $sex2 = 'U';
342
        }
343
344
        switch ($path) {
345
            case '':
346
                return I18N::translate('self');
347
                //  Level One relationships
348
            case 'mot':
349
                return I18N::translate('mother');
350
            case 'fat':
351
                return I18N::translate('father');
352
            case 'par':
353
                return I18N::translate('parent');
354
            case 'hus':
355
                if ($person1 instanceof Individual && $person2 instanceof Individual) {
356
                    // We had the linking family earlier, but lost it.  Find it again.
357
                    foreach ($person1->spouseFamilies(Auth::PRIV_HIDE) as $family) {
358
                        if ($person2 === $family->spouse($person1)) {
359
                            $event = $family->facts(['ANUL', 'DIV', 'ENGA', 'MARR'], true, Auth::PRIV_HIDE, true)->last();
360
361
                            if ($event instanceof Fact) {
362
                                switch ($event->tag()) {
363
                                    case 'FAM:ANUL':
364
                                    case 'FAM:DIV':
365
                                        return I18N::translate('ex-husband');
366
                                    case 'FAM:MARR':
367
                                        return I18N::translate('husband');
368
                                    case 'FAM:ENGA':
369
                                        return I18N::translate('fiancé');
370
                                }
371
                            }
372
                        }
373
                    }
374
                }
375
376
                return I18N::translateContext('MALE', 'partner');
377
378
            case 'wif':
379
                if ($person1 instanceof Individual && $person2 instanceof Individual) {
380
                    // We had the linking family earlier, but lost it.  Find it again.
381
                    foreach ($person1->spouseFamilies(Auth::PRIV_HIDE) as $family) {
382
                        if ($person2 === $family->spouse($person1)) {
383
                            $event = $family->facts(['ANUL', 'DIV', 'ENGA', 'MARR'], true, Auth::PRIV_HIDE, true)->last();
384
385
                            if ($event instanceof Fact) {
386
                                switch ($event->tag()) {
387
                                    case 'FAM:ANUL':
388
                                    case 'FAM:DIV':
389
                                        return I18N::translate('ex-wife');
390
                                    case 'FAM:MARR':
391
                                        return I18N::translate('wife');
392
                                    case 'FAM:ENGA':
393
                                        return I18N::translate('fiancée');
394
                                }
395
                            }
396
                        }
397
                    }
398
                }
399
400
                return I18N::translateContext('FEMALE', 'partner');
401
            case 'spo':
402
                if ($person1 instanceof Individual && $person2 instanceof Individual) {
403
                    // We had the linking family earlier, but lost it.  Find it again.
404
                    foreach ($person1->spouseFamilies(Auth::PRIV_HIDE) as $family) {
405
                        if ($person2 === $family->spouse($person1)) {
406
                            $event = $family->facts(['ANUL', 'DIV', 'ENGA', 'MARR'], true, Auth::PRIV_HIDE, true)->last();
407
408
                            if ($event instanceof Fact) {
409
                                switch ($event->tag()) {
410
                                    case 'FAM:ANUL':
411
                                    case 'FAM:DIV':
412
                                        return I18N::translate('ex-spouse');
413
                                    case 'FAM:MARR':
414
                                        return I18N::translate('spouse');
415
                                    case 'FAM:ENGA':
416
                                        return I18N::translate('fiancé(e)');
417
                                }
418
                            }
419
                        }
420
                    }
421
                }
422
423
                return I18N::translate('partner');
424
425
            case 'son':
426
                return I18N::translate('son');
427
            case 'dau':
428
                return I18N::translate('daughter');
429
            case 'chi':
430
                return I18N::translate('child');
431
            case 'bro':
432
                if ($person1 && $person2) {
433
                    $dob1 = $person1->getBirthDate();
434
                    $dob2 = $person2->getBirthDate();
435
                    if ($dob1->isOK() && $dob2->isOK()) {
436
                        if (abs($dob1->julianDay() - $dob2->julianDay()) < 2 && $dob1->minimumDate()->day > 0 && $dob2->minimumDate()->day > 0) {
437
                            // Exclude BEF, AFT, etc.
438
                            return I18N::translate('twin brother');
439
                        }
440
441
                        if ($dob1->maximumJulianDay() < $dob2->minimumJulianDay()) {
442
                            return I18N::translate('younger brother');
443
                        }
444
445
                        if ($dob1->minimumJulianDay() > $dob2->maximumJulianDay()) {
446
                            return I18N::translate('elder brother');
447
                        }
448
                    }
449
                }
450
451
                return I18N::translate('brother');
452
            case 'sis':
453
                if ($person1 && $person2) {
454
                    $dob1 = $person1->getBirthDate();
455
                    $dob2 = $person2->getBirthDate();
456
                    if ($dob1->isOK() && $dob2->isOK()) {
457
                        if (abs($dob1->julianDay() - $dob2->julianDay()) < 2 && $dob1->minimumDate()->day > 0 && $dob2->minimumDate()->day > 0) {
458
                            // Exclude BEF, AFT, etc.
459
                            return I18N::translate('twin sister');
460
                        }
461
462
                        if ($dob1->maximumJulianDay() < $dob2->minimumJulianDay()) {
463
                            return I18N::translate('younger sister');
464
                        }
465
466
                        if ($dob1->minimumJulianDay() > $dob2->maximumJulianDay()) {
467
                            return I18N::translate('elder sister');
468
                        }
469
                    }
470
                }
471
472
                return I18N::translate('sister');
473
            case 'sib':
474
                if ($person1 && $person2) {
475
                    $dob1 = $person1->getBirthDate();
476
                    $dob2 = $person2->getBirthDate();
477
                    if ($dob1->isOK() && $dob2->isOK()) {
478
                        if (abs($dob1->julianDay() - $dob2->julianDay()) < 2 && $dob1->minimumDate()->day > 0 && $dob2->minimumDate()->day > 0) {
479
                            // Exclude BEF, AFT, etc.
480
                            return I18N::translate('twin sibling');
481
                        }
482
483
                        if ($dob1->maximumJulianDay() < $dob2->minimumJulianDay()) {
484
                            return I18N::translate('younger sibling');
485
                        }
486
487
                        if ($dob1->minimumJulianDay() > $dob2->maximumJulianDay()) {
488
                            return I18N::translate('elder sibling');
489
                        }
490
                    }
491
                }
492
493
                return I18N::translate('sibling');
494
495
                // Level Two relationships
496
            case 'brochi':
497
                return I18N::translateContext('brother’s child', 'nephew/niece');
498
            case 'brodau':
499
                return I18N::translateContext('brother’s daughter', 'niece');
500
            case 'broson':
501
                return I18N::translateContext('brother’s son', 'nephew');
502
            case 'browif':
503
                return I18N::translateContext('brother’s wife', 'sister-in-law');
504
            case 'chichi':
505
                return I18N::translateContext('child’s child', 'grandchild');
506
            case 'chidau':
507
                return I18N::translateContext('child’s daughter', 'granddaughter');
508
            case 'chihus':
509
                return I18N::translateContext('child’s husband', 'son-in-law');
510
            case 'chison':
511
                return I18N::translateContext('child’s son', 'grandson');
512
            case 'chispo':
513
                return I18N::translateContext('child’s spouse', 'son/daughter-in-law');
514
            case 'chiwif':
515
                return I18N::translateContext('child’s wife', 'daughter-in-law');
516
            case 'dauchi':
517
                return I18N::translateContext('daughter’s child', 'grandchild');
518
            case 'daudau':
519
                return I18N::translateContext('daughter’s daughter', 'granddaughter');
520
            case 'dauhus':
521
                return I18N::translateContext('daughter’s husband', 'son-in-law');
522
            case 'dauson':
523
                return I18N::translateContext('daughter’s son', 'grandson');
524
            case 'fatbro':
525
                return I18N::translateContext('father’s brother', 'uncle');
526
            case 'fatchi':
527
                return I18N::translateContext('father’s child', 'half-sibling');
528
            case 'fatdau':
529
                return I18N::translateContext('father’s daughter', 'half-sister');
530
            case 'fatfat':
531
                return I18N::translateContext('father’s father', 'paternal grandfather');
532
            case 'fatmot':
533
                return I18N::translateContext('father’s mother', 'paternal grandmother');
534
            case 'fatpar':
535
                return I18N::translateContext('father’s parent', 'paternal grandparent');
536
            case 'fatsib':
537
                return I18N::translateContext('father’s sibling', 'aunt/uncle');
538
            case 'fatsis':
539
                return I18N::translateContext('father’s sister', 'aunt');
540
            case 'fatson':
541
                return I18N::translateContext('father’s son', 'half-brother');
542
            case 'fatwif':
543
                return I18N::translateContext('father’s wife', 'step-mother');
544
            case 'husbro':
545
                return I18N::translateContext('husband’s brother', 'brother-in-law');
546
            case 'huschi':
547
                return I18N::translateContext('husband’s child', 'step-child');
548
            case 'husdau':
549
                return I18N::translateContext('husband’s daughter', 'step-daughter');
550
            case 'husfat':
551
                return I18N::translateContext('husband’s father', 'father-in-law');
552
            case 'husmot':
553
                return I18N::translateContext('husband’s mother', 'mother-in-law');
554
            case 'hussib':
555
                return I18N::translateContext('husband’s sibling', 'brother/sister-in-law');
556
            case 'hussis':
557
                return I18N::translateContext('husband’s sister', 'sister-in-law');
558
            case 'husson':
559
                return I18N::translateContext('husband’s son', 'step-son');
560
            case 'motbro':
561
                return I18N::translateContext('mother’s brother', 'uncle');
562
            case 'motchi':
563
                return I18N::translateContext('mother’s child', 'half-sibling');
564
            case 'motdau':
565
                return I18N::translateContext('mother’s daughter', 'half-sister');
566
            case 'motfat':
567
                return I18N::translateContext('mother’s father', 'maternal grandfather');
568
            case 'mothus':
569
                return I18N::translateContext('mother’s husband', 'step-father');
570
            case 'motmot':
571
                return I18N::translateContext('mother’s mother', 'maternal grandmother');
572
            case 'motpar':
573
                return I18N::translateContext('mother’s parent', 'maternal grandparent');
574
            case 'motsib':
575
                return I18N::translateContext('mother’s sibling', 'aunt/uncle');
576
            case 'motsis':
577
                return I18N::translateContext('mother’s sister', 'aunt');
578
            case 'motson':
579
                return I18N::translateContext('mother’s son', 'half-brother');
580
            case 'parbro':
581
                return I18N::translateContext('parent’s brother', 'uncle');
582
            case 'parchi':
583
                return I18N::translateContext('parent’s child', 'half-sibling');
584
            case 'pardau':
585
                return I18N::translateContext('parent’s daughter', 'half-sister');
586
            case 'parfat':
587
                return I18N::translateContext('parent’s father', 'grandfather');
588
            case 'parmot':
589
                return I18N::translateContext('parent’s mother', 'grandmother');
590
            case 'parpar':
591
                return I18N::translateContext('parent’s parent', 'grandparent');
592
            case 'parsib':
593
                return I18N::translateContext('parent’s sibling', 'aunt/uncle');
594
            case 'parsis':
595
                return I18N::translateContext('parent’s sister', 'aunt');
596
            case 'parson':
597
                return I18N::translateContext('parent’s son', 'half-brother');
598
            case 'parspo':
599
                return I18N::translateContext('parent’s spouse', 'step-parent');
600
            case 'sibchi':
601
                return I18N::translateContext('sibling’s child', 'nephew/niece');
602
            case 'sibdau':
603
                return I18N::translateContext('sibling’s daughter', 'niece');
604
            case 'sibson':
605
                return I18N::translateContext('sibling’s son', 'nephew');
606
            case 'sibspo':
607
                return I18N::translateContext('sibling’s spouse', 'brother/sister-in-law');
608
            case 'sischi':
609
                return I18N::translateContext('sister’s child', 'nephew/niece');
610
            case 'sisdau':
611
                return I18N::translateContext('sister’s daughter', 'niece');
612
            case 'sishus':
613
                return I18N::translateContext('sister’s husband', 'brother-in-law');
614
            case 'sisson':
615
                return I18N::translateContext('sister’s son', 'nephew');
616
            case 'sonchi':
617
                return I18N::translateContext('son’s child', 'grandchild');
618
            case 'sondau':
619
                return I18N::translateContext('son’s daughter', 'granddaughter');
620
            case 'sonson':
621
                return I18N::translateContext('son’s son', 'grandson');
622
            case 'sonwif':
623
                return I18N::translateContext('son’s wife', 'daughter-in-law');
624
            case 'spobro':
625
                return I18N::translateContext('spouse’s brother', 'brother-in-law');
626
            case 'spochi':
627
                return I18N::translateContext('spouse’s child', 'step-child');
628
            case 'spodau':
629
                return I18N::translateContext('spouse’s daughter', 'step-daughter');
630
            case 'spofat':
631
                return I18N::translateContext('spouse’s father', 'father-in-law');
632
            case 'spomot':
633
                return I18N::translateContext('spouse’s mother', 'mother-in-law');
634
            case 'sposis':
635
                return I18N::translateContext('spouse’s sister', 'sister-in-law');
636
            case 'sposon':
637
                return I18N::translateContext('spouse’s son', 'step-son');
638
            case 'spopar':
639
                return I18N::translateContext('spouse’s parent', 'mother/father-in-law');
640
            case 'sposib':
641
                return I18N::translateContext('spouse’s sibling', 'brother/sister-in-law');
642
            case 'wifbro':
643
                return I18N::translateContext('wife’s brother', 'brother-in-law');
644
            case 'wifchi':
645
                return I18N::translateContext('wife’s child', 'step-child');
646
            case 'wifdau':
647
                return I18N::translateContext('wife’s daughter', 'step-daughter');
648
            case 'wiffat':
649
                return I18N::translateContext('wife’s father', 'father-in-law');
650
            case 'wifmot':
651
                return I18N::translateContext('wife’s mother', 'mother-in-law');
652
            case 'wifsib':
653
                return I18N::translateContext('wife’s sibling', 'brother/sister-in-law');
654
            case 'wifsis':
655
                return I18N::translateContext('wife’s sister', 'sister-in-law');
656
            case 'wifson':
657
                return I18N::translateContext('wife’s son', 'step-son');
658
659
                // Level Three relationships
660
            case 'brochichi':
661
                if ($sex1 === 'M') {
662
                    return I18N::translateContext('(a man’s) brother’s child’s child', 'great-nephew/niece');
663
                }
664
665
                return I18N::translateContext('(a woman’s) brother’s child’s child', 'great-nephew/niece');
666
            case 'brochidau':
667
                if ($sex1 === 'M') {
668
                    return I18N::translateContext('(a man’s) brother’s child’s daughter', 'great-niece');
669
                }
670
671
                return I18N::translateContext('(a woman’s) brother’s child’s daughter', 'great-niece');
672
            case 'brochison':
673
                if ($sex1 === 'M') {
674
                    return I18N::translateContext('(a man’s) brother’s child’s son', 'great-nephew');
675
                }
676
677
                return I18N::translateContext('(a woman’s) brother’s child’s son', 'great-nephew');
678
            case 'brodauchi':
679
                if ($sex1 === 'M') {
680
                    return I18N::translateContext('(a man’s) brother’s daughter’s child', 'great-nephew/niece');
681
                }
682
683
                return I18N::translateContext('(a woman’s) brother’s daughter’s child', 'great-nephew/niece');
684
            case 'brodaudau':
685
                if ($sex1 === 'M') {
686
                    return I18N::translateContext('(a man’s) brother’s daughter’s daughter', 'great-niece');
687
                }
688
689
                return I18N::translateContext('(a woman’s) brother’s daughter’s daughter', 'great-niece');
690
            case 'brodauhus':
691
                return I18N::translateContext('brother’s daughter’s husband', 'nephew-in-law');
692
            case 'brodauson':
693
                if ($sex1 === 'M') {
694
                    return I18N::translateContext('(a man’s) brother’s daughter’s son', 'great-nephew');
695
                }
696
697
                return I18N::translateContext('(a woman’s) brother’s daughter’s son', 'great-nephew');
698
            case 'brosonchi':
699
                if ($sex1 === 'M') {
700
                    return I18N::translateContext('(a man’s) brother’s son’s child', 'great-nephew/niece');
701
                }
702
703
                return I18N::translateContext('(a woman’s) brother’s son’s child', 'great-nephew/niece');
704
            case 'brosondau':
705
                if ($sex1 === 'M') {
706
                    return I18N::translateContext('(a man’s) brother’s son’s daughter', 'great-niece');
707
                }
708
709
                return I18N::translateContext('(a woman’s) brother’s son’s daughter', 'great-niece');
710
            case 'brosonson':
711
                if ($sex1 === 'M') {
712
                    return I18N::translateContext('(a man’s) brother’s son’s son', 'great-nephew');
713
                }
714
715
                return I18N::translateContext('(a woman’s) brother’s son’s son', 'great-nephew');
716
            case 'brosonwif':
717
                return I18N::translateContext('brother’s son’s wife', 'niece-in-law');
718
            case 'browifbro':
719
                return I18N::translateContext('brother’s wife’s brother', 'brother-in-law');
720
            case 'browifsib':
721
                return I18N::translateContext('brother’s wife’s sibling', 'brother/sister-in-law');
722
            case 'browifsis':
723
                return I18N::translateContext('brother’s wife’s sister', 'sister-in-law');
724
            case 'chichichi':
725
                return I18N::translateContext('child’s child’s child', 'great-grandchild');
726
            case 'chichidau':
727
                return I18N::translateContext('child’s child’s daughter', 'great-granddaughter');
728
            case 'chichison':
729
                return I18N::translateContext('child’s child’s son', 'great-grandson');
730
            case 'chidauchi':
731
                return I18N::translateContext('child’s daughter’s child', 'great-grandchild');
732
            case 'chidaudau':
733
                return I18N::translateContext('child’s daughter’s daughter', 'great-granddaughter');
734
            case 'chidauhus':
735
                return I18N::translateContext('child’s daughter’s husband', 'granddaughter’s husband');
736
            case 'chidauson':
737
                return I18N::translateContext('child’s daughter’s son', 'great-grandson');
738
            case 'chisonchi':
739
                return I18N::translateContext('child’s son’s child', 'great-grandchild');
740
            case 'chisondau':
741
                return I18N::translateContext('child’s son’s daughter', 'great-granddaughter');
742
            case 'chisonson':
743
                return I18N::translateContext('child’s son’s son', 'great-grandson');
744
            case 'chisonwif':
745
                return I18N::translateContext('child’s son’s wife', 'grandson’s wife');
746
            case 'dauchichi':
747
                return I18N::translateContext('daughter’s child’s child', 'great-grandchild');
748
            case 'dauchidau':
749
                return I18N::translateContext('daughter’s child’s daughter', 'great-granddaughter');
750
            case 'dauchison':
751
                return I18N::translateContext('daughter’s child’s son', 'great-grandson');
752
            case 'daudauchi':
753
                return I18N::translateContext('daughter’s daughter’s child', 'great-grandchild');
754
            case 'daudaudau':
755
                return I18N::translateContext('daughter’s daughter’s daughter', 'great-granddaughter');
756
            case 'daudauhus':
757
                return I18N::translateContext('daughter’s daughter’s husband', 'granddaughter’s husband');
758
            case 'daudauson':
759
                return I18N::translateContext('daughter’s daughter’s son', 'great-grandson');
760
            case 'dauhusfat':
761
                return I18N::translateContext('daughter’s husband’s father', 'son-in-law’s father');
762
            case 'dauhusmot':
763
                return I18N::translateContext('daughter’s husband’s mother', 'son-in-law’s mother');
764
            case 'dauhuspar':
765
                return I18N::translateContext('daughter’s husband’s parent', 'son-in-law’s parent');
766
            case 'dausonchi':
767
                return I18N::translateContext('daughter’s son’s child', 'great-grandchild');
768
            case 'dausondau':
769
                return I18N::translateContext('daughter’s son’s daughter', 'great-granddaughter');
770
            case 'dausonson':
771
                return I18N::translateContext('daughter’s son’s son', 'great-grandson');
772
            case 'dausonwif':
773
                return I18N::translateContext('daughter’s son’s wife', 'grandson’s wife');
774
            case 'fatbrochi':
775
                return I18N::translateContext('father’s brother’s child', 'first cousin');
776
            case 'fatbrodau':
777
                return I18N::translateContext('father’s brother’s daughter', 'first cousin');
778
            case 'fatbroson':
779
                return I18N::translateContext('father’s brother’s son', 'first cousin');
780
            case 'fatbrowif':
781
                return I18N::translateContext('father’s brother’s wife', 'aunt');
782
            case 'fatfatbro':
783
                return I18N::translateContext('father’s father’s brother', 'great-uncle');
784
            case 'fatfatfat':
785
                return I18N::translateContext('father’s father’s father', 'great-grandfather');
786
            case 'fatfatmot':
787
                return I18N::translateContext('father’s father’s mother', 'great-grandmother');
788
            case 'fatfatpar':
789
                return I18N::translateContext('father’s father’s parent', 'great-grandparent');
790
            case 'fatfatsib':
791
                return I18N::translateContext('father’s father’s sibling', 'great-aunt/uncle');
792
            case 'fatfatsis':
793
                return I18N::translateContext('father’s father’s sister', 'great-aunt');
794
            case 'fatmotbro':
795
                return I18N::translateContext('father’s mother’s brother', 'great-uncle');
796
            case 'fatmotfat':
797
                return I18N::translateContext('father’s mother’s father', 'great-grandfather');
798
            case 'fatmotmot':
799
                return I18N::translateContext('father’s mother’s mother', 'great-grandmother');
800
            case 'fatmotpar':
801
                return I18N::translateContext('father’s mother’s parent', 'great-grandparent');
802
            case 'fatmotsib':
803
                return I18N::translateContext('father’s mother’s sibling', 'great-aunt/uncle');
804
            case 'fatmotsis':
805
                return I18N::translateContext('father’s mother’s sister', 'great-aunt');
806
            case 'fatparbro':
807
                return I18N::translateContext('father’s parent’s brother', 'great-uncle');
808
            case 'fatparfat':
809
                return I18N::translateContext('father’s parent’s father', 'great-grandfather');
810
            case 'fatparmot':
811
                return I18N::translateContext('father’s parent’s mother', 'great-grandmother');
812
            case 'fatparpar':
813
                return I18N::translateContext('father’s parent’s parent', 'great-grandparent');
814
            case 'fatparsib':
815
                return I18N::translateContext('father’s parent’s sibling', 'great-aunt/uncle');
816
            case 'fatparsis':
817
                return I18N::translateContext('father’s parent’s sister', 'great-aunt');
818
            case 'fatsischi':
819
                return I18N::translateContext('father’s sister’s child', 'first cousin');
820
            case 'fatsisdau':
821
                return I18N::translateContext('father’s sister’s daughter', 'first cousin');
822
            case 'fatsishus':
823
                return I18N::translateContext('father’s sister’s husband', 'uncle');
824
            case 'fatsisson':
825
                return I18N::translateContext('father’s sister’s son', 'first cousin');
826
            case 'fatwifchi':
827
                return I18N::translateContext('father’s wife’s child', 'step-sibling');
828
            case 'fatwifdau':
829
                return I18N::translateContext('father’s wife’s daughter', 'step-sister');
830
            case 'fatwifson':
831
                return I18N::translateContext('father’s wife’s son', 'step-brother');
832
            case 'husbrowif':
833
                return I18N::translateContext('husband’s brother’s wife', 'sister-in-law');
834
            case 'hussishus':
835
                return I18N::translateContext('husband’s sister’s husband', 'brother-in-law');
836
            case 'hussibchi':
837
                return I18N::translateContext('husband’s sibling’s child', 'nephew/niece');
838
            case 'hussischi':
839
                return I18N::translateContext('husband’s sister’s child', 'nephew/niece');
840
            case 'husbrochi':
841
                return I18N::translateContext('husband’s brother’s child', 'nephew/niece');
842
            case 'hussibdau':
843
                return I18N::translateContext('husband’s sibling’s daughter', 'niece');
844
            case 'hussisdau':
845
                return I18N::translateContext('husband’s sister’s daughter', 'niece');
846
            case 'husbrodau':
847
                return I18N::translateContext('husband’s brother’s daughter', 'niece');
848
            case 'hussibson':
849
                return I18N::translateContext('husband’s sibling’s son', 'nephew');
850
            case 'hussisson':
851
                return I18N::translateContext('husband’s sister’s son', 'nephew');
852
            case 'husbroson':
853
                return I18N::translateContext('husband’s brother’s son', 'nephew');
854
            case 'motbrochi':
855
                return I18N::translateContext('mother’s brother’s child', 'first cousin');
856
            case 'motbrodau':
857
                return I18N::translateContext('mother’s brother’s daughter', 'first cousin');
858
            case 'motbroson':
859
                return I18N::translateContext('mother’s brother’s son', 'first cousin');
860
            case 'motbrowif':
861
                return I18N::translateContext('mother’s brother’s wife', 'aunt');
862
            case 'motfatbro':
863
                return I18N::translateContext('mother’s father’s brother', 'great-uncle');
864
            case 'motfatfat':
865
                return I18N::translateContext('mother’s father’s father', 'great-grandfather');
866
            case 'motfatmot':
867
                return I18N::translateContext('mother’s father’s mother', 'great-grandmother');
868
            case 'motfatpar':
869
                return I18N::translateContext('mother’s father’s parent', 'great-grandparent');
870
            case 'motfatsib':
871
                return I18N::translateContext('mother’s father’s sibling', 'great-aunt/uncle');
872
            case 'motfatsis':
873
                return I18N::translateContext('mother’s father’s sister', 'great-aunt');
874
            case 'mothuschi':
875
                return I18N::translateContext('mother’s husband’s child', 'step-sibling');
876
            case 'mothusdau':
877
                return I18N::translateContext('mother’s husband’s daughter', 'step-sister');
878
            case 'mothusson':
879
                return I18N::translateContext('mother’s husband’s son', 'step-brother');
880
            case 'motmotbro':
881
                return I18N::translateContext('mother’s mother’s brother', 'great-uncle');
882
            case 'motmotfat':
883
                return I18N::translateContext('mother’s mother’s father', 'great-grandfather');
884
            case 'motmotmot':
885
                return I18N::translateContext('mother’s mother’s mother', 'great-grandmother');
886
            case 'motmotpar':
887
                return I18N::translateContext('mother’s mother’s parent', 'great-grandparent');
888
            case 'motmotsib':
889
                return I18N::translateContext('mother’s mother’s sibling', 'great-aunt/uncle');
890
            case 'motmotsis':
891
                return I18N::translateContext('mother’s mother’s sister', 'great-aunt');
892
            case 'motparbro':
893
                return I18N::translateContext('mother’s parent’s brother', 'great-uncle');
894
            case 'motparfat':
895
                return I18N::translateContext('mother’s parent’s father', 'great-grandfather');
896
            case 'motparmot':
897
                return I18N::translateContext('mother’s parent’s mother', 'great-grandmother');
898
            case 'motparpar':
899
                return I18N::translateContext('mother’s parent’s parent', 'great-grandparent');
900
            case 'motparsib':
901
                return I18N::translateContext('mother’s parent’s sibling', 'great-aunt/uncle');
902
            case 'motparsis':
903
                return I18N::translateContext('mother’s parent’s sister', 'great-aunt');
904
            case 'motsischi':
905
                return I18N::translateContext('mother’s sister’s child', 'first cousin');
906
            case 'motsisdau':
907
                return I18N::translateContext('mother’s sister’s daughter', 'first cousin');
908
            case 'motsishus':
909
                return I18N::translateContext('mother’s sister’s husband', 'uncle');
910
            case 'motsisson':
911
                return I18N::translateContext('mother’s sister’s son', 'first cousin');
912
            case 'parbrowif':
913
                return I18N::translateContext('parent’s brother’s wife', 'aunt');
914
            case 'parfatbro':
915
                return I18N::translateContext('parent’s father’s brother', 'great-uncle');
916
            case 'parfatfat':
917
                return I18N::translateContext('parent’s father’s father', 'great-grandfather');
918
            case 'parfatmot':
919
                return I18N::translateContext('parent’s father’s mother', 'great-grandmother');
920
            case 'parfatpar':
921
                return I18N::translateContext('parent’s father’s parent', 'great-grandparent');
922
            case 'parfatsib':
923
                return I18N::translateContext('parent’s father’s sibling', 'great-aunt/uncle');
924
            case 'parfatsis':
925
                return I18N::translateContext('parent’s father’s sister', 'great-aunt');
926
            case 'parmotbro':
927
                return I18N::translateContext('parent’s mother’s brother', 'great-uncle');
928
            case 'parmotfat':
929
                return I18N::translateContext('parent’s mother’s father', 'great-grandfather');
930
            case 'parmotmot':
931
                return I18N::translateContext('parent’s mother’s mother', 'great-grandmother');
932
            case 'parmotpar':
933
                return I18N::translateContext('parent’s mother’s parent', 'great-grandparent');
934
            case 'parmotsib':
935
                return I18N::translateContext('parent’s mother’s sibling', 'great-aunt/uncle');
936
            case 'parmotsis':
937
                return I18N::translateContext('parent’s mother’s sister', 'great-aunt');
938
            case 'parparbro':
939
                return I18N::translateContext('parent’s parent’s brother', 'great-uncle');
940
            case 'parparfat':
941
                return I18N::translateContext('parent’s parent’s father', 'great-grandfather');
942
            case 'parparmot':
943
                return I18N::translateContext('parent’s parent’s mother', 'great-grandmother');
944
            case 'parparpar':
945
                return I18N::translateContext('parent’s parent’s parent', 'great-grandparent');
946
            case 'parparsib':
947
                return I18N::translateContext('parent’s parent’s sibling', 'great-aunt/uncle');
948
            case 'parparsis':
949
                return I18N::translateContext('parent’s parent’s sister', 'great-aunt');
950
            case 'parsishus':
951
                return I18N::translateContext('parent’s sister’s husband', 'uncle');
952
            case 'parspochi':
953
                return I18N::translateContext('parent’s spouse’s child', 'step-sibling');
954
            case 'parspodau':
955
                return I18N::translateContext('parent’s spouse’s daughter', 'step-sister');
956
            case 'parsposon':
957
                return I18N::translateContext('parent’s spouse’s son', 'step-brother');
958
            case 'sibchichi':
959
                return I18N::translateContext('sibling’s child’s child', 'great-nephew/niece');
960
            case 'sibchidau':
961
                return I18N::translateContext('sibling’s child’s daughter', 'great-niece');
962
            case 'sibchison':
963
                return I18N::translateContext('sibling’s child’s son', 'great-nephew');
964
            case 'sibdauchi':
965
                return I18N::translateContext('sibling’s daughter’s child', 'great-nephew/niece');
966
            case 'sibdaudau':
967
                return I18N::translateContext('sibling’s daughter’s daughter', 'great-niece');
968
            case 'sibdauhus':
969
                return I18N::translateContext('sibling’s daughter’s husband', 'nephew-in-law');
970
            case 'sibdauson':
971
                return I18N::translateContext('sibling’s daughter’s son', 'great-nephew');
972
            case 'sibsonchi':
973
                return I18N::translateContext('sibling’s son’s child', 'great-nephew/niece');
974
            case 'sibsondau':
975
                return I18N::translateContext('sibling’s son’s daughter', 'great-niece');
976
            case 'sibsonson':
977
                return I18N::translateContext('sibling’s son’s son', 'great-nephew');
978
            case 'sibsonwif':
979
                return I18N::translateContext('sibling’s son’s wife', 'niece-in-law');
980
            case 'sischichi':
981
                if ($sex1 === 'M') {
982
                    return I18N::translateContext('(a man’s) sister’s child’s child', 'great-nephew/niece');
983
                }
984
985
                return I18N::translateContext('(a woman’s) sister’s child’s child', 'great-nephew/niece');
986
            case 'sischidau':
987
                if ($sex1 === 'M') {
988
                    return I18N::translateContext('(a man’s) sister’s child’s daughter', 'great-niece');
989
                }
990
991
                return I18N::translateContext('(a woman’s) sister’s child’s daughter', 'great-niece');
992
            case 'sischison':
993
                if ($sex1 === 'M') {
994
                    return I18N::translateContext('(a man’s) sister’s child’s son', 'great-nephew');
995
                }
996
997
                return I18N::translateContext('(a woman’s) sister’s child’s son', 'great-nephew');
998
            case 'sisdauchi':
999
                if ($sex1 === 'M') {
1000
                    return I18N::translateContext('(a man’s) sister’s daughter’s child', 'great-nephew/niece');
1001
                }
1002
1003
                return I18N::translateContext('(a woman’s) sister’s daughter’s child', 'great-nephew/niece');
1004
            case 'sisdaudau':
1005
                if ($sex1 === 'M') {
1006
                    return I18N::translateContext('(a man’s) sister’s daughter’s daughter', 'great-niece');
1007
                }
1008
1009
                return I18N::translateContext('(a woman’s) sister’s daughter’s daughter', 'great-niece');
1010
            case 'sisdauhus':
1011
                return I18N::translateContext('sisters’s daughter’s husband', 'nephew-in-law');
1012
            case 'sisdauson':
1013
                if ($sex1 === 'M') {
1014
                    return I18N::translateContext('(a man’s) sister’s daughter’s son', 'great-nephew');
1015
                }
1016
1017
                return I18N::translateContext('(a woman’s) sister’s daughter’s son', 'great-nephew');
1018
            case 'sishusbro':
1019
                return I18N::translateContext('sister’s husband’s brother', 'brother-in-law');
1020
            case 'sishussib':
1021
                return I18N::translateContext('sister’s husband’s sibling', 'brother/sister-in-law');
1022
            case 'sishussis':
1023
                return I18N::translateContext('sister’s husband’s sister', 'sister-in-law');
1024
            case 'sissonchi':
1025
                if ($sex1 === 'M') {
1026
                    return I18N::translateContext('(a man’s) sister’s son’s child', 'great-nephew/niece');
1027
                }
1028
1029
                return I18N::translateContext('(a woman’s) sister’s son’s child', 'great-nephew/niece');
1030
            case 'sissondau':
1031
                if ($sex1 === 'M') {
1032
                    return I18N::translateContext('(a man’s) sister’s son’s daughter', 'great-niece');
1033
                }
1034
1035
                return I18N::translateContext('(a woman’s) sister’s son’s daughter', 'great-niece');
1036
            case 'sissonson':
1037
                if ($sex1 === 'M') {
1038
                    return I18N::translateContext('(a man’s) sister’s son’s son', 'great-nephew');
1039
                }
1040
1041
                return I18N::translateContext('(a woman’s) sister’s son’s son', 'great-nephew');
1042
            case 'sissonwif':
1043
                return I18N::translateContext('sisters’s son’s wife', 'niece-in-law');
1044
            case 'sonchichi':
1045
                return I18N::translateContext('son’s child’s child', 'great-grandchild');
1046
            case 'sonchidau':
1047
                return I18N::translateContext('son’s child’s daughter', 'great-granddaughter');
1048
            case 'sonchison':
1049
                return I18N::translateContext('son’s child’s son', 'great-grandson');
1050
            case 'sondauchi':
1051
                return I18N::translateContext('son’s daughter’s child', 'great-grandchild');
1052
            case 'sondaudau':
1053
                return I18N::translateContext('son’s daughter’s daughter', 'great-granddaughter');
1054
            case 'sondauhus':
1055
                return I18N::translateContext('son’s daughter’s husband', 'granddaughter’s husband');
1056
            case 'sondauson':
1057
                return I18N::translateContext('son’s daughter’s son', 'great-grandson');
1058
            case 'sonsonchi':
1059
                return I18N::translateContext('son’s son’s child', 'great-grandchild');
1060
            case 'sonsondau':
1061
                return I18N::translateContext('son’s son’s daughter', 'great-granddaughter');
1062
            case 'sonsonson':
1063
                return I18N::translateContext('son’s son’s son', 'great-grandson');
1064
            case 'sonsonwif':
1065
                return I18N::translateContext('son’s son’s wife', 'grandson’s wife');
1066
            case 'sonwiffat':
1067
                return I18N::translateContext('son’s wife’s father', 'daughter-in-law’s father');
1068
            case 'sonwifmot':
1069
                return I18N::translateContext('son’s wife’s mother', 'daughter-in-law’s mother');
1070
            case 'sonwifpar':
1071
                return I18N::translateContext('son’s wife’s parent', 'daughter-in-law’s parent');
1072
            case 'wifbrowif':
1073
                return I18N::translateContext('wife’s brother’s wife', 'sister-in-law');
1074
            case 'wifsishus':
1075
                return I18N::translateContext('wife’s sister’s husband', 'brother-in-law');
1076
            case 'wifsibchi':
1077
                return I18N::translateContext('wife’s sibling’s child', 'nephew/niece');
1078
            case 'wifsischi':
1079
                return I18N::translateContext('wife’s sister’s child', 'nephew/niece');
1080
            case 'wifbrochi':
1081
                return I18N::translateContext('wife’s brother’s child', 'nephew/niece');
1082
            case 'wifsibdau':
1083
                return I18N::translateContext('wife’s sibling’s daughter', 'niece');
1084
            case 'wifsisdau':
1085
                return I18N::translateContext('wife’s sister’s daughter', 'niece');
1086
            case 'wifbrodau':
1087
                return I18N::translateContext('wife’s brother’s daughter', 'niece');
1088
            case 'wifsibson':
1089
                return I18N::translateContext('wife’s sibling’s son', 'nephew');
1090
            case 'wifsisson':
1091
                return I18N::translateContext('wife’s sister’s son', 'nephew');
1092
            case 'wifbroson':
1093
                return I18N::translateContext('wife’s brother’s son', 'nephew');
1094
1095
                // Some “special case” level four relationships that have specific names in certain languages
1096
            case 'fatfatbrowif':
1097
                return I18N::translateContext('father’s father’s brother’s wife', 'great-aunt');
1098
            case 'fatfatsibspo':
1099
                return I18N::translateContext('father’s father’s sibling’s spouse', 'great-aunt/uncle');
1100
            case 'fatfatsishus':
1101
                return I18N::translateContext('father’s father’s sister’s husband', 'great-uncle');
1102
            case 'fatmotbrowif':
1103
                return I18N::translateContext('father’s mother’s brother’s wife', 'great-aunt');
1104
            case 'fatmotsibspo':
1105
                return I18N::translateContext('father’s mother’s sibling’s spouse', 'great-aunt/uncle');
1106
            case 'fatmotsishus':
1107
                return I18N::translateContext('father’s mother’s sister’s husband', 'great-uncle');
1108
            case 'fatparbrowif':
1109
                return I18N::translateContext('father’s parent’s brother’s wife', 'great-aunt');
1110
            case 'fatparsibspo':
1111
                return I18N::translateContext('father’s parent’s sibling’s spouse', 'great-aunt/uncle');
1112
            case 'fatparsishus':
1113
                return I18N::translateContext('father’s parent’s sister’s husband', 'great-uncle');
1114
            case 'motfatbrowif':
1115
                return I18N::translateContext('mother’s father’s brother’s wife', 'great-aunt');
1116
            case 'motfatsibspo':
1117
                return I18N::translateContext('mother’s father’s sibling’s spouse', 'great-aunt/uncle');
1118
            case 'motfatsishus':
1119
                return I18N::translateContext('mother’s father’s sister’s husband', 'great-uncle');
1120
            case 'motmotbrowif':
1121
                return I18N::translateContext('mother’s mother’s brother’s wife', 'great-aunt');
1122
            case 'motmotsibspo':
1123
                return I18N::translateContext('mother’s mother’s sibling’s spouse', 'great-aunt/uncle');
1124
            case 'motmotsishus':
1125
                return I18N::translateContext('mother’s mother’s sister’s husband', 'great-uncle');
1126
            case 'motparbrowif':
1127
                return I18N::translateContext('mother’s parent’s brother’s wife', 'great-aunt');
1128
            case 'motparsibspo':
1129
                return I18N::translateContext('mother’s parent’s sibling’s spouse', 'great-aunt/uncle');
1130
            case 'motparsishus':
1131
                return I18N::translateContext('mother’s parent’s sister’s husband', 'great-uncle');
1132
            case 'parfatbrowif':
1133
                return I18N::translateContext('parent’s father’s brother’s wife', 'great-aunt');
1134
            case 'parfatsibspo':
1135
                return I18N::translateContext('parent’s father’s sibling’s spouse', 'great-aunt/uncle');
1136
            case 'parfatsishus':
1137
                return I18N::translateContext('parent’s father’s sister’s husband', 'great-uncle');
1138
            case 'parmotbrowif':
1139
                return I18N::translateContext('parent’s mother’s brother’s wife', 'great-aunt');
1140
            case 'parmotsibspo':
1141
                return I18N::translateContext('parent’s mother’s sibling’s spouse', 'great-aunt/uncle');
1142
            case 'parmotsishus':
1143
                return I18N::translateContext('parent’s mother’s sister’s husband', 'great-uncle');
1144
            case 'parparbrowif':
1145
                return I18N::translateContext('parent’s parent’s brother’s wife', 'great-aunt');
1146
            case 'parparsibspo':
1147
                return I18N::translateContext('parent’s parent’s sibling’s spouse', 'great-aunt/uncle');
1148
            case 'parparsishus':
1149
                return I18N::translateContext('parent’s parent’s sister’s husband', 'great-uncle');
1150
            case 'fatfatbrodau':
1151
                return I18N::translateContext('father’s father’s brother’s daughter', 'first cousin once removed ascending');
1152
            case 'fatfatbroson':
1153
                return I18N::translateContext('father’s father’s brother’s son', 'first cousin once removed ascending');
1154
            case 'fatfatbrochi':
1155
                return I18N::translateContext('father’s father’s brother’s child', 'first cousin once removed ascending');
1156
            case 'fatfatsisdau':
1157
                return I18N::translateContext('father’s father’s sister’s daughter', 'first cousin once removed ascending');
1158
            case 'fatfatsisson':
1159
                return I18N::translateContext('father’s father’s sister’s son', 'first cousin once removed ascending');
1160
            case 'fatfatsischi':
1161
                return I18N::translateContext('father’s father’s sister’s child', 'first cousin once removed ascending');
1162
            case 'fatmotbrodau':
1163
                return I18N::translateContext('father’s mother’s brother’s daughter', 'first cousin once removed ascending');
1164
            case 'fatmotbroson':
1165
                return I18N::translateContext('father’s mother’s brother’s son', 'first cousin once removed ascending');
1166
            case 'fatmotbrochi':
1167
                return I18N::translateContext('father’s mother’s brother’s child', 'first cousin once removed ascending');
1168
            case 'fatmotsisdau':
1169
                return I18N::translateContext('father’s mother’s sister’s daughter', 'first cousin once removed ascending');
1170
            case 'fatmotsisson':
1171
                return I18N::translateContext('father’s mother’s sister’s son', 'first cousin once removed ascending');
1172
            case 'fatmotsischi':
1173
                return I18N::translateContext('father’s mother’s sister’s child', 'first cousin once removed ascending');
1174
            case 'motfatbrodau':
1175
                return I18N::translateContext('mother’s father’s brother’s daughter', 'first cousin once removed ascending');
1176
            case 'motfatbroson':
1177
                return I18N::translateContext('mother’s father’s brother’s son', 'first cousin once removed ascending');
1178
            case 'motfatbrochi':
1179
                return I18N::translateContext('mother’s father’s brother’s child', 'first cousin once removed ascending');
1180
            case 'motfatsisdau':
1181
                return I18N::translateContext('mother’s father’s sister’s daughter', 'first cousin once removed ascending');
1182
            case 'motfatsisson':
1183
                return I18N::translateContext('mother’s father’s sister’s son', 'first cousin once removed ascending');
1184
            case 'motfatsischi':
1185
                return I18N::translateContext('mother’s father’s sister’s child', 'first cousin once removed ascending');
1186
            case 'motmotbrodau':
1187
                return I18N::translateContext('mother’s mother’s brother’s daughter', 'first cousin once removed ascending');
1188
            case 'motmotbroson':
1189
                return I18N::translateContext('mother’s mother’s brother’s son', 'first cousin once removed ascending');
1190
            case 'motmotbrochi':
1191
                return I18N::translateContext('mother’s mother’s brother’s child', 'first cousin once removed ascending');
1192
            case 'motmotsisdau':
1193
                return I18N::translateContext('mother’s mother’s sister’s daughter', 'first cousin once removed ascending');
1194
            case 'motmotsisson':
1195
                return I18N::translateContext('mother’s mother’s sister’s son', 'first cousin once removed ascending');
1196
            case 'motmotsischi':
1197
                return I18N::translateContext('mother’s mother’s sister’s child', 'first cousin once removed ascending');
1198
        }
1199
1200
        // Some “special case” level five relationships that have specific names in certain languages
1201
        if (preg_match('/^(mot|fat|par)fatbro(son|dau|chi)dau$/', $path) === 1) {
1202
            return I18N::translateContext('grandfather’s brother’s granddaughter', 'second cousin');
1203
        }
1204
1205
        if (preg_match('/^(mot|fat|par)fatbro(son|dau|chi)son$/', $path) === 1) {
1206
            return I18N::translateContext('grandfather’s brother’s grandson', 'second cousin');
1207
        }
1208
1209
        if (preg_match('/^(mot|fat|par)fatbro(son|dau|chi)chi$/', $path) === 1) {
1210
            return I18N::translateContext('grandfather’s brother’s grandchild', 'second cousin');
1211
        }
1212
1213
        if (preg_match('/^(mot|fat|par)fatsis(son|dau|chi)dau$/', $path) === 1) {
1214
            return I18N::translateContext('grandfather’s sister’s granddaughter', 'second cousin');
1215
        }
1216
1217
        if (preg_match('/^(mot|fat|par)fatsis(son|dau|chi)son$/', $path) === 1) {
1218
            return I18N::translateContext('grandfather’s sister’s grandson', 'second cousin');
1219
        }
1220
1221
        if (preg_match('/^(mot|fat|par)fatsis(son|dau|chi)chi$/', $path) === 1) {
1222
            return I18N::translateContext('grandfather’s sister’s grandchild', 'second cousin');
1223
        }
1224
1225
        if (preg_match('/^(mot|fat|par)fatsib(son|dau|chi)dau$/', $path) === 1) {
1226
            return I18N::translateContext('grandfather’s sibling’s granddaughter', 'second cousin');
1227
        }
1228
1229
        if (preg_match('/^(mot|fat|par)fatsib(son|dau|chi)son$/', $path) === 1) {
1230
            return I18N::translateContext('grandfather’s sibling’s grandson', 'second cousin');
1231
        }
1232
1233
        if (preg_match('/^(mot|fat|par)fatsib(son|dau|chi)chi$/', $path) === 1) {
1234
            return I18N::translateContext('grandfather’s sibling’s grandchild', 'second cousin');
1235
        }
1236
1237
        if (preg_match('/^(mot|fat|par)motbro(son|dau|chi)dau$/', $path) === 1) {
1238
            return I18N::translateContext('grandmother’s brother’s granddaughter', 'second cousin');
1239
        }
1240
1241
        if (preg_match('/^(mot|fat|par)motbro(son|dau|chi)son$/', $path) === 1) {
1242
            return I18N::translateContext('grandmother’s brother’s grandson', 'second cousin');
1243
        }
1244
1245
        if (preg_match('/^(mot|fat|par)motbro(son|dau|chi)chi$/', $path) === 1) {
1246
            return I18N::translateContext('grandmother’s brother’s grandchild', 'second cousin');
1247
        }
1248
1249
        if (preg_match('/^(mot|fat|par)motsis(son|dau|chi)dau$/', $path) === 1) {
1250
            return I18N::translateContext('grandmother’s sister’s granddaughter', 'second cousin');
1251
        }
1252
1253
        if (preg_match('/^(mot|fat|par)motsis(son|dau|chi)son$/', $path) === 1) {
1254
            return I18N::translateContext('grandmother’s sister’s grandson', 'second cousin');
1255
        }
1256
1257
        if (preg_match('/^(mot|fat|par)motsis(son|dau|chi)chi$/', $path) === 1) {
1258
            return I18N::translateContext('grandmother’s sister’s grandchild', 'second cousin');
1259
        }
1260
1261
        if (preg_match('/^(mot|fat|par)motsib(son|dau|chi)dau$/', $path) === 1) {
1262
            return I18N::translateContext('grandmother’s sibling’s granddaughter', 'second cousin');
1263
        }
1264
1265
        if (preg_match('/^(mot|fat|par)motsib(son|dau|chi)son$/', $path) === 1) {
1266
            return I18N::translateContext('grandmother’s sibling’s grandson', 'second cousin');
1267
        }
1268
1269
        if (preg_match('/^(mot|fat|par)motsib(son|dau|chi)chi$/', $path) === 1) {
1270
            return I18N::translateContext('grandmother’s sibling’s grandchild', 'second cousin');
1271
        }
1272
1273
        if (preg_match('/^(mot|fat|par)parbro(son|dau|chi)dau$/', $path) === 1) {
1274
            return I18N::translateContext('grandparent’s brother’s granddaughter', 'second cousin');
1275
        }
1276
1277
        if (preg_match('/^(mot|fat|par)parbro(son|dau|chi)son$/', $path) === 1) {
1278
            return I18N::translateContext('grandparent’s brother’s grandson', 'second cousin');
1279
        }
1280
1281
        if (preg_match('/^(mot|fat|par)parbro(son|dau|chi)chi$/', $path) === 1) {
1282
            return I18N::translateContext('grandparent’s brother’s grandchild', 'second cousin');
1283
        }
1284
1285
        if (preg_match('/^(mot|fat|par)parsis(son|dau|chi)dau$/', $path) === 1) {
1286
            return I18N::translateContext('grandparent’s sister’s granddaughter', 'second cousin');
1287
        }
1288
1289
        if (preg_match('/^(mot|fat|par)parsis(son|dau|chi)son$/', $path) === 1) {
1290
            return I18N::translateContext('grandparent’s sister’s grandson', 'second cousin');
1291
        }
1292
1293
        if (preg_match('/^(mot|fat|par)parsis(son|dau|chi)chi$/', $path) === 1) {
1294
            return I18N::translateContext('grandparent’s sister’s grandchild', 'second cousin');
1295
        }
1296
1297
        if (preg_match('/^(mot|fat|par)parsib(son|dau|chi)dau$/', $path) === 1) {
1298
            return I18N::translateContext('grandparent’s sibling’s granddaughter', 'second cousin');
1299
        }
1300
1301
        if (preg_match('/^(mot|fat|par)parsib(son|dau|chi)son$/', $path) === 1) {
1302
            return I18N::translateContext('grandparent’s sibling’s grandson', 'second cousin');
1303
        }
1304
1305
        if (preg_match('/^(mot|fat|par)parsib(son|dau|chi)chi$/', $path) === 1) {
1306
            return I18N::translateContext('grandparent’s sibling’s grandchild', 'second cousin');
1307
        }
1308
1309
        // Look for generic/pattern relationships.
1310
        if (preg_match('/^((?:mot|fat|par)+)(bro|sis|sib)$/', $path, $match) === 1) {
1311
            // siblings of direct ancestors
1312
            $up       = intdiv(strlen($match[1]), 3);
1313
            $bef_last = substr($path, -6, 3);
1314
            switch ($up) {
1315
                case 3:
1316
                    if ($sex2 === 'M') {
1317
                        if ($bef_last === 'fat') {
1318
                            return I18N::translateContext('great-grandfather’s brother', 'great-great-uncle');
1319
                        }
1320
1321
                        if ($bef_last === 'mot') {
1322
                            return I18N::translateContext('great-grandmother’s brother', 'great-great-uncle');
1323
                        }
1324
1325
                        return I18N::translateContext('great-grandparent’s brother', 'great-great-uncle');
1326
                    }
1327
1328
                    if ($sex2 === 'F') {
1329
                        return I18N::translate('great-great-aunt');
1330
                    }
1331
1332
                    return I18N::translate('great-great-aunt/uncle');
1333
1334
                case 4:
1335
                    if ($sex2 === 'M') {
1336
                        if ($bef_last === 'fat') {
1337
                            return I18N::translateContext('great-great-grandfather’s brother', 'great-great-great-uncle');
1338
                        }
1339
1340
                        if ($bef_last === 'mot') {
1341
                            return I18N::translateContext('great-great-grandmother’s brother', 'great-great-great-uncle');
1342
                        }
1343
1344
                        return I18N::translateContext('great-great-grandparent’s brother', 'great-great-great-uncle');
1345
                    }
1346
1347
                    if ($sex2 === 'F') {
1348
                        return I18N::translate('great-great-great-aunt');
1349
                    }
1350
1351
                    return I18N::translate('great-great-great-aunt/uncle');
1352
1353
                case 5:
1354
                    if ($sex2 === 'M') {
1355
                        if ($bef_last === 'fat') {
1356
                            return I18N::translateContext('great-great-great-grandfather’s brother', 'great ×4 uncle');
1357
                        }
1358
1359
                        if ($bef_last === 'mot') {
1360
                            return I18N::translateContext('great-great-great-grandmother’s brother', 'great ×4 uncle');
1361
                        }
1362
1363
                        return I18N::translateContext('great-great-great-grandparent’s brother', 'great ×4 uncle');
1364
                    }
1365
1366
                    if ($sex2 === 'F') {
1367
                        return I18N::translate('great ×4 aunt');
1368
                    }
1369
1370
                    return I18N::translate('great ×4 aunt/uncle');
1371
1372
                case 6:
1373
                    if ($sex2 === 'M') {
1374
                        if ($bef_last === 'fat') {
1375
                            return I18N::translateContext('great ×4 grandfather’s brother', 'great ×5 uncle');
1376
                        }
1377
1378
                        if ($bef_last === 'mot') {
1379
                            return I18N::translateContext('great ×4 grandmother’s brother', 'great ×5 uncle');
1380
                        }
1381
1382
                        return I18N::translateContext('great ×4 grandparent’s brother', 'great ×5 uncle');
1383
                    }
1384
1385
                    if ($sex2 === 'F') {
1386
                        return I18N::translate('great ×5 aunt');
1387
                    }
1388
1389
                    return I18N::translate('great ×5 aunt/uncle');
1390
1391
                case 7:
1392
                    if ($sex2 === 'M') {
1393
                        if ($bef_last === 'fat') {
1394
                            return I18N::translateContext('great ×5 grandfather’s brother', 'great ×6 uncle');
1395
                        }
1396
1397
                        if ($bef_last === 'mot') {
1398
                            return I18N::translateContext('great ×5 grandmother’s brother', 'great ×6 uncle');
1399
                        }
1400
1401
                        return I18N::translateContext('great ×5 grandparent’s brother', 'great ×6 uncle');
1402
                    }
1403
1404
                    if ($sex2 === 'F') {
1405
                        return I18N::translate('great ×6 aunt');
1406
                    }
1407
1408
                    return I18N::translate('great ×6 aunt/uncle');
1409
1410
                case 8:
1411
                    if ($sex2 === 'M') {
1412
                        if ($bef_last === 'fat') {
1413
                            return I18N::translateContext('great ×6 grandfather’s brother', 'great ×7 uncle');
1414
                        }
1415
1416
                        if ($bef_last === 'mot') {
1417
                            return I18N::translateContext('great ×6 grandmother’s brother', 'great ×7 uncle');
1418
                        }
1419
1420
                        return I18N::translateContext('great ×6 grandparent’s brother', 'great ×7 uncle');
1421
                    }
1422
1423
                    if ($sex2 === 'F') {
1424
                        return I18N::translate('great ×7 aunt');
1425
                    }
1426
1427
                    return I18N::translate('great ×7 aunt/uncle');
1428
1429
                default:
1430
                    // Different languages have different rules for naming generations.
1431
                    // An English great ×12 uncle is a Danish great ×10 uncle.
1432
                    //
1433
                    // Need to find out which languages use which rules.
1434
                    switch (I18N::languageTag()) {
1435
                        case 'da':
1436
                            if ($sex2 === 'M') {
1437
                                return I18N::translate('great ×%s uncle', I18N::number($up - 4));
1438
                            }
1439
1440
                            if ($sex2 === 'F') {
1441
                                return I18N::translate('great ×%s aunt', I18N::number($up - 4));
1442
                            }
1443
1444
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up - 4));
1445
1446
                        case 'pl':
1447
                            if ($sex2 === 'M') {
1448
                                if ($bef_last === 'fat') {
1449
                                    return I18N::translateContext('great ×(%s-1) grandfather’s brother', 'great ×%s uncle', I18N::number($up - 2));
1450
                                }
1451
1452
                                if ($bef_last === 'mot') {
1453
                                    return I18N::translateContext('great ×(%s-1) grandmother’s brother', 'great ×%s uncle', I18N::number($up - 2));
1454
                                }
1455
1456
                                return I18N::translateContext('great ×(%s-1) grandparent’s brother', 'great ×%s uncle', I18N::number($up - 2));
1457
                            }
1458
1459
                            if ($sex2 === 'F') {
1460
                                return I18N::translate('great ×%s aunt', I18N::number($up - 2));
1461
                            }
1462
1463
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up - 2));
1464
1465
                        case 'ko': // Source : Jeongwan Nam ([email protected])
1466
                            if ($sex2 === 'M') {
1467
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1468
                                return I18N::translate('great ×%s uncle', I18N::number($up + 1));
1469
                            }
1470
1471
                            if ($sex2 === 'F') {
1472
                                return I18N::translate('great ×%s aunt', I18N::number($up + 1));
1473
                            }
1474
1475
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up + 1));
1476
1477
                        case 'hi': // Source: MrQD
1478
                            if ($sex2 === 'M') {
1479
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1480
                                return I18N::translate('great ×%s uncle', I18N::number($up - 2));
1481
                            }
1482
1483
                            if ($sex2 === 'F') {
1484
                                return I18N::translate('great ×%s aunt', I18N::number($up - 2));
1485
                            }
1486
1487
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up - 2));
1488
1489
                        case 'zh-Hans': // Source: xmlf
1490
                        case 'zh-Hant':
1491
                            if ($sex2 === 'M') {
1492
                                return I18N::translate('great ×%s uncle', I18N::number($up));
1493
                            }
1494
                            if ($sex2 === 'F') {
1495
                                return I18N::translate('great ×%s aunt', I18N::number($up));
1496
                            }
1497
1498
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up));
1499
1500
                        case 'it': // Source: Michele Locati
1501
                        case 'en_AU':
1502
                        case 'en_GB':
1503
                        case 'en_US':
1504
                        default:
1505
                            if ($sex2 === 'M') {
1506
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1507
                                return I18N::translate('great ×%s uncle', I18N::number($up - 1));
1508
                            }
1509
1510
                            if ($sex2 === 'F') {
1511
                                return I18N::translate('great ×%s aunt', I18N::number($up - 1));
1512
                            }
1513
1514
                            return I18N::translate('great ×%s aunt/uncle', I18N::number($up - 1));
1515
                    }
1516
            }
1517
        }
1518
        if (preg_match('/^(?:bro|sis|sib)((?:son|dau|chi)+)$/', $path, $match) === 1) {
1519
            // direct descendants of siblings
1520
            $down  = intdiv(strlen($match[1]), 3) + 1; // Add one, as we count generations from the common ancestor
1521
            $first = substr($path, 0, 3);
1522
            switch ($down) {
1523
                case 4:
1524
                    if ($sex2 === 'M') {
1525
                        if ($first === 'bro' && $sex1 === 'M') {
1526
                            return I18N::translateContext('(a man’s) brother’s great-grandson', 'great-great-nephew');
1527
                        }
1528
1529
                        if ($first === 'sis' && $sex1 === 'M') {
1530
                            return I18N::translateContext('(a man’s) sister’s great-grandson', 'great-great-nephew');
1531
                        }
1532
1533
                        return I18N::translateContext('(a woman’s) great-great-nephew', 'great-great-nephew');
1534
                    }
1535
1536
                    if ($sex2 === 'F') {
1537
                        if ($first === 'bro' && $sex1 === 'M') {
1538
                            return I18N::translateContext('(a man’s) brother’s great-granddaughter', 'great-great-niece');
1539
                        }
1540
1541
                        if ($first === 'sis' && $sex1 === 'M') {
1542
                            return I18N::translateContext('(a man’s) sister’s great-granddaughter', 'great-great-niece');
1543
                        }
1544
1545
                        return I18N::translateContext('(a woman’s) great-great-niece', 'great-great-niece');
1546
                    }
1547
1548
                    if ($first === 'bro' && $sex1 === 'M') {
1549
                        return I18N::translateContext('(a man’s) brother’s great-grandchild', 'great-great-nephew/niece');
1550
                    }
1551
1552
                    if ($first === 'sis' && $sex1 === 'M') {
1553
                        return I18N::translateContext('(a man’s) sister’s great-grandchild', 'great-great-nephew/niece');
1554
                    }
1555
1556
                    return I18N::translateContext('(a woman’s) great-great-nephew/niece', 'great-great-nephew/niece');
1557
1558
                case 5:
1559
                    if ($sex2 === 'M') {
1560
                        if ($first === 'bro' && $sex1 === 'M') {
1561
                            return I18N::translateContext('(a man’s) brother’s great-great-grandson', 'great-great-great-nephew');
1562
                        }
1563
1564
                        if ($first === 'sis' && $sex1 === 'M') {
1565
                            return I18N::translateContext('(a man’s) sister’s great-great-grandson', 'great-great-great-nephew');
1566
                        }
1567
1568
                        return I18N::translateContext('(a woman’s) great-great-great-nephew', 'great-great-great-nephew');
1569
                    }
1570
1571
                    if ($sex2 === 'F') {
1572
                        if ($first === 'bro' && $sex1 === 'M') {
1573
                            return I18N::translateContext('(a man’s) brother’s great-great-granddaughter', 'great-great-great-niece');
1574
                        }
1575
1576
                        if ($first === 'sis' && $sex1 === 'M') {
1577
                            return I18N::translateContext('(a man’s) sister’s great-great-granddaughter', 'great-great-great-niece');
1578
                        }
1579
1580
                        return I18N::translateContext('(a woman’s) great-great-great-niece', 'great-great-great-niece');
1581
                    }
1582
1583
                    if ($first === 'bro' && $sex1 === 'M') {
1584
                        return I18N::translateContext('(a man’s) brother’s great-great-grandchild', 'great-great-great-nephew/niece');
1585
                    }
1586
1587
                    if ($first === 'sis' && $sex1 === 'M') {
1588
                        return I18N::translateContext('(a man’s) sister’s great-great-grandchild', 'great-great-great-nephew/niece');
1589
                    }
1590
1591
                    return I18N::translateContext('(a woman’s) great-great-great-nephew/niece', 'great-great-great-nephew/niece');
1592
1593
                case 6:
1594
                    if ($sex2 === 'M') {
1595
                        if ($first === 'bro' && $sex1 === 'M') {
1596
                            return I18N::translateContext('(a man’s) brother’s great-great-great-grandson', 'great ×4 nephew');
1597
                        }
1598
1599
                        if ($first === 'sis' && $sex1 === 'M') {
1600
                            return I18N::translateContext('(a man’s) sister’s great-great-great-grandson', 'great ×4 nephew');
1601
                        }
1602
1603
                        return I18N::translateContext('(a woman’s) great ×4 nephew', 'great ×4 nephew');
1604
                    }
1605
1606
                    if ($sex2 === 'F') {
1607
                        if ($first === 'bro' && $sex1 === 'M') {
1608
                            return I18N::translateContext('(a man’s) brother’s great-great-great-granddaughter', 'great ×4 niece');
1609
                        }
1610
1611
                        if ($first === 'sis' && $sex1 === 'M') {
1612
                            return I18N::translateContext('(a man’s) sister’s great-great-great-granddaughter', 'great ×4 niece');
1613
                        }
1614
1615
                        return I18N::translateContext('(a woman’s) great ×4 niece', 'great ×4 niece');
1616
                    }
1617
1618
                    if ($first === 'bro' && $sex1 === 'M') {
1619
                        return I18N::translateContext('(a man’s) brother’s great-great-great-grandchild', 'great ×4 nephew/niece');
1620
                    }
1621
1622
                    if ($first === 'sis' && $sex1 === 'M') {
1623
                        return I18N::translateContext('(a man’s) sister’s great-great-great-grandchild', 'great ×4 nephew/niece');
1624
                    }
1625
1626
                    return I18N::translateContext('(a woman’s) great ×4 nephew/niece', 'great ×4 nephew/niece');
1627
1628
                case 7:
1629
                    if ($sex2 === 'M') {
1630
                        if ($first === 'bro' && $sex1 === 'M') {
1631
                            return I18N::translateContext('(a man’s) brother’s great ×4 grandson', 'great ×5 nephew');
1632
                        }
1633
1634
                        if ($first === 'sis' && $sex1 === 'M') {
1635
                            return I18N::translateContext('(a man’s) sister’s great ×4 grandson', 'great ×5 nephew');
1636
                        }
1637
1638
                        return I18N::translateContext('(a woman’s) great ×5 nephew', 'great ×5 nephew');
1639
                    }
1640
1641
                    if ($sex2 === 'F') {
1642
                        if ($first === 'bro' && $sex1 === 'M') {
1643
                            return I18N::translateContext('(a man’s) brother’s great ×4 granddaughter', 'great ×5 niece');
1644
                        }
1645
1646
                        if ($first === 'sis' && $sex1 === 'M') {
1647
                            return I18N::translateContext('(a man’s) sister’s great ×4 granddaughter', 'great ×5 niece');
1648
                        }
1649
1650
                        return I18N::translateContext('(a woman’s) great ×5 niece', 'great ×5 niece');
1651
                    }
1652
1653
                    if ($first === 'bro' && $sex1 === 'M') {
1654
                        return I18N::translateContext('(a man’s) brother’s great ×4 grandchild', 'great ×5 nephew/niece');
1655
                    }
1656
1657
                    if ($first === 'sis' && $sex1 === 'M') {
1658
                        return I18N::translateContext('(a man’s) sister’s great ×4 grandchild', 'great ×5 nephew/niece');
1659
                    }
1660
1661
                    return I18N::translateContext('(a woman’s) great ×5 nephew/niece', 'great ×5 nephew/niece');
1662
1663
                default:
1664
                    // Different languages have different rules for naming generations.
1665
                    // An English great ×12 nephew is a Polish great ×11 nephew.
1666
                    //
1667
                    // Need to find out which languages use which rules.
1668
                    switch (I18N::languageTag()) {
1669
                        case 'pl': // Source: Lukasz Wilenski
1670
                            if ($sex2 === 'M') {
1671
                                if ($first === 'bro' && $sex1 === 'M') {
1672
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 3));
1673
                                }
1674
1675
                                if ($first === 'sis' && $sex1 === 'M') {
1676
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 3));
1677
                                }
1678
1679
                                return I18N::translateContext('(a woman’s) great ×%s nephew', 'great ×%s nephew', I18N::number($down - 3));
1680
                            }
1681
1682
                            if ($sex2 === 'F') {
1683
                                if ($first === 'bro' && $sex1 === 'M') {
1684
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 3));
1685
                                }
1686
1687
                                if ($first === 'sis' && $sex1 === 'M') {
1688
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 3));
1689
                                }
1690
1691
                                return I18N::translateContext('(a woman’s) great ×%s niece', 'great ×%s niece', I18N::number($down - 3));
1692
                            }
1693
1694
                            if ($first === 'bro' && $sex1 === 'M') {
1695
                                return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 3));
1696
                            }
1697
1698
                            if ($first === 'sis' && $sex1 === 'M') {
1699
                                return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 3));
1700
                            }
1701
1702
                            return I18N::translateContext('(a woman’s) great ×%s nephew/niece', 'great ×%s nephew/niece', I18N::number($down - 3));
1703
1704
                        case 'ko': // Source: Jeongwan Nam ([email protected])
1705
                            if ($sex2 === 'M') {
1706
                                if ($first === 'bro' && $sex1 === 'M') {
1707
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 0));
1708
                                }
1709
1710
                                if ($first === 'sis' && $sex1 === 'M') {
1711
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 0));
1712
                                }
1713
1714
                                return I18N::translateContext('(a woman’s) great ×%s nephew', 'great ×%s nephew', I18N::number($down - 0));
1715
                            }
1716
1717
                            if ($sex2 === 'F') {
1718
                                if ($first === 'bro' && $sex1 === 'M') {
1719
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 3));
1720
                                }
1721
1722
                                if ($first === 'sis' && $sex1 === 'M') {
1723
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 3));
1724
                                }
1725
1726
                                return I18N::translateContext('(a woman’s) great ×%s niece', 'great ×%s niece', I18N::number($down - 3));
1727
                            }
1728
1729
                            if ($first === 'bro' && $sex1 === 'M') {
1730
                                return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 3));
1731
                            }
1732
1733
                            if ($first === 'sis' && $sex1 === 'M') {
1734
                                return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 3));
1735
                            }
1736
1737
                            return I18N::translateContext('(a woman’s) great ×%s nephew/niece', 'great ×%s nephew/niece', I18N::number($down - 3));
1738
1739
                        case 'zh-Hans': // Source: xmlf
1740
                        case 'zh-Hant':
1741
                            if ($sex2 === 'M') {
1742
                                if ($first === 'bro' && $sex1 === 'M') {
1743
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 1));
1744
                                }
1745
                                if ($first === 'sis' && $sex1 === 'M') {
1746
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandson', 'great ×%s nephew', I18N::number($down - 1));
1747
                                }
1748
1749
                                return I18N::translateContext('(a woman’s) great ×%s nephew', 'great ×%s nephew', I18N::number($down - 1));
1750
                            }
1751
                            if ($sex2 === 'F') {
1752
                                if ($first === 'bro' && $sex1 === 'M') {
1753
                                    return I18N::translateContext('(a man’s) brother’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 1));
1754
                                }
1755
                                if ($first === 'sis' && $sex1 === 'M') {
1756
                                    return I18N::translateContext('(a man’s) sister’s great ×(%s-1) granddaughter', 'great ×%s niece', I18N::number($down - 1));
1757
                                }
1758
1759
                                return I18N::translateContext('(a woman’s) great ×%s niece', 'great ×%s niece', I18N::number($down - 1));
1760
                            }
1761
                            if ($first === 'bro' && $sex1 === 'M') {
1762
                                return I18N::translateContext('(a man’s) brother’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 1));
1763
                            }
1764
                            if ($first === 'sis' && $sex1 === 'M') {
1765
                                return I18N::translateContext('(a man’s) sister’s great ×(%s-1) grandchild', 'great ×%s nephew/niece', I18N::number($down - 1));
1766
                            }
1767
1768
                            return I18N::translateContext('(a woman’s) great ×%s nephew/niece', 'great ×%s nephew/niece', I18N::number($down - 1));
1769
1770
                        case 'he': // Source: Meliza Amity
1771
                            if ($sex2 === 'M') {
1772
                                return I18N::translate('great ×%s nephew', I18N::number($down - 1));
1773
                            }
1774
1775
                            if ($sex2 === 'F') {
1776
                                return I18N::translate('great ×%s niece', I18N::number($down - 1));
1777
                            }
1778
1779
                            return I18N::translate('great ×%s nephew/niece', I18N::number($down - 1));
1780
1781
                        case 'hi': // Source: MrQD.
1782
                            if ($sex2 === 'M') {
1783
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1784
                                return I18N::translate('great ×%s nephew', I18N::number($down - 3));
1785
                            }
1786
1787
                            if ($sex2 === 'F') {
1788
                                return I18N::translate('great ×%s niece', I18N::number($down - 3));
1789
                            }
1790
1791
                            return I18N::translate('great ×%s nephew/niece', I18N::number($down - 3));
1792
1793
                        case 'it': // Source: Michele Locati.
1794
                        case 'en_AU':
1795
                        case 'en_GB':
1796
                        case 'en_US':
1797
                        default:
1798
                            if ($sex2 === 'M') {
1799
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1800
                                return I18N::translate('great ×%s nephew', I18N::number($down - 2));
1801
                            }
1802
1803
                            if ($sex2 === 'F') {
1804
                                return I18N::translate('great ×%s niece', I18N::number($down - 2));
1805
                            }
1806
1807
                            return I18N::translate('great ×%s nephew/niece', I18N::number($down - 2));
1808
                    }
1809
            }
1810
        }
1811
        if (preg_match('/^((?:mot|fat|par)*)$/', $path, $match) === 1) {
1812
            // direct ancestors
1813
            $up = intdiv(strlen($match[1]), 3);
1814
            switch ($up) {
1815
                case 4:
1816
                    if ($sex2 === 'M') {
1817
                        return I18N::translate('great-great-grandfather');
1818
                    }
1819
1820
                    if ($sex2 === 'F') {
1821
                        return I18N::translate('great-great-grandmother');
1822
                    }
1823
1824
                    return I18N::translate('great-great-grandparent');
1825
1826
                case 5:
1827
                    if ($sex2 === 'M') {
1828
                        return I18N::translate('great-great-great-grandfather');
1829
                    }
1830
1831
                    if ($sex2 === 'F') {
1832
                        return I18N::translate('great-great-great-grandmother');
1833
                    }
1834
1835
                    return I18N::translate('great-great-great-grandparent');
1836
1837
                case 6:
1838
                    if ($sex2 === 'M') {
1839
                        return I18N::translate('great ×4 grandfather');
1840
                    }
1841
1842
                    if ($sex2 === 'F') {
1843
                        return I18N::translate('great ×4 grandmother');
1844
                    }
1845
1846
                    return I18N::translate('great ×4 grandparent');
1847
1848
                case 7:
1849
                    if ($sex2 === 'M') {
1850
                        return I18N::translate('great ×5 grandfather');
1851
                    }
1852
1853
                    if ($sex2 === 'F') {
1854
                        return I18N::translate('great ×5 grandmother');
1855
                    }
1856
1857
                    return I18N::translate('great ×5 grandparent');
1858
1859
                case 8:
1860
                    if ($sex2 === 'M') {
1861
                        return I18N::translate('great ×6 grandfather');
1862
                    }
1863
1864
                    if ($sex2 === 'F') {
1865
                        return I18N::translate('great ×6 grandmother');
1866
                    }
1867
1868
                    return I18N::translate('great ×6 grandparent');
1869
1870
                case 9:
1871
                    if ($sex2 === 'M') {
1872
                        return I18N::translate('great ×7 grandfather');
1873
                    }
1874
1875
                    if ($sex2 === 'F') {
1876
                        return I18N::translate('great ×7 grandmother');
1877
                    }
1878
1879
                    return I18N::translate('great ×7 grandparent');
1880
1881
                default:
1882
                    // Different languages have different rules for naming generations.
1883
                    // An English great ×12 grandfather is a Danish great ×11 grandfather.
1884
                    //
1885
                    // Need to find out which languages use which rules.
1886
                    switch (I18N::languageTag()) {
1887
                        case 'da': // Source: Patrick Sorensen
1888
                            if ($sex2 === 'M') {
1889
                                return I18N::translate('great ×%s grandfather', I18N::number($up - 3));
1890
                            }
1891
1892
                            if ($sex2 === 'F') {
1893
                                return I18N::translate('great ×%s grandmother', I18N::number($up - 3));
1894
                            }
1895
1896
                            return I18N::translate('great ×%s grandparent', I18N::number($up - 3));
1897
1898
                        case 'it': // Source: Michele Locati
1899
                        case 'zh-Hans': // Source: xmlf
1900
                        case 'zh-Hant':
1901
                        case 'es': // Source: Wes Groleau
1902
                            if ($sex2 === 'M') {
1903
                                return I18N::translate('great ×%s grandfather', I18N::number($up));
1904
                            }
1905
1906
                            if ($sex2 === 'F') {
1907
                                return I18N::translate('great ×%s grandmother', I18N::number($up));
1908
                            }
1909
1910
                            return I18N::translate('great ×%s grandparent', I18N::number($up));
1911
1912
                        case 'fr': // Source: Jacqueline Tetreault
1913
                        case 'fr_CA':
1914
                            if ($sex2 === 'M') {
1915
                                return I18N::translate('great ×%s grandfather', I18N::number($up - 1));
1916
                            }
1917
1918
                            if ($sex2 === 'F') {
1919
                                return I18N::translate('great ×%s grandmother', I18N::number($up - 1));
1920
                            }
1921
1922
                            return I18N::translate('great ×%s grandparent', I18N::number($up - 1));
1923
1924
                        case 'ko': // Source : Jeongwan Nam ([email protected])
1925
                            if ($sex2 === 'M') {
1926
                                return I18N::translate('great ×%s grandfather', I18N::number($up + 1));
1927
                            }
1928
1929
                            if ($sex2 === 'F') {
1930
                                return I18N::translate('great ×%s grandmother', I18N::number($up + 1));
1931
                            }
1932
1933
                            return I18N::translate('great ×%s grandparent', I18N::number($up + 1));
1934
1935
                        case 'nn': // Source: Hogne Røed Nilsen (https://bugs.launchpad.net/webtrees/+bug/1168553)
1936
                        case 'nb':
1937
                            if ($sex2 === 'M') {
1938
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1939
                                return I18N::translate('great ×%s grandfather', I18N::number($up - 3));
1940
                            }
1941
1942
                            if ($sex2 === 'F') {
1943
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1944
                                return I18N::translate('great ×%s grandmother', I18N::number($up - 3));
1945
                            }
1946
1947
                            // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1948
                            return I18N::translate('great ×%s grandparent', I18N::number($up - 3));
1949
                        case 'en_AU':
1950
                        case 'en_GB':
1951
                        case 'en_US':
1952
                        default:
1953
                            if ($sex2 === 'M') {
1954
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1955
                                return I18N::translate('great ×%s grandfather', I18N::number($up - 2));
1956
                            }
1957
1958
                            if ($sex2 === 'F') {
1959
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1960
                                return I18N::translate('great ×%s grandmother', I18N::number($up - 2));
1961
                            }
1962
1963
                            // I18N: if you need a different number for %s, contact the developers, as a code-change is required
1964
                            return I18N::translate('great ×%s grandparent', I18N::number($up - 2));
1965
                    }
1966
            }
1967
        }
1968
        if (preg_match('/^((?:son|dau|chi)*)$/', $path, $match) === 1) {
1969
            // direct descendants
1970
            $up = intdiv(strlen($match[1]), 3);
1971
            switch ($up) {
1972
                case 4:
1973
                    if ($sex2 === 'M') {
1974
                        return I18N::translate('great-great-grandson');
1975
                    }
1976
1977
                    if ($sex2 === 'F') {
1978
                        return I18N::translate('great-great-granddaughter');
1979
                    }
1980
1981
                    return I18N::translate('great-great-grandchild');
1982
1983
                case 5:
1984
                    if ($sex2 === 'M') {
1985
                        return I18N::translate('great-great-great-grandson');
1986
                    }
1987
1988
                    if ($sex2 === 'F') {
1989
                        return I18N::translate('great-great-great-granddaughter');
1990
                    }
1991
1992
                    return I18N::translate('great-great-great-grandchild');
1993
1994
                case 6:
1995
                    if ($sex2 === 'M') {
1996
                        return I18N::translate('great ×4 grandson');
1997
                    }
1998
1999
                    if ($sex2 === 'F') {
2000
                        return I18N::translate('great ×4 granddaughter');
2001
                    }
2002
2003
                    return I18N::translate('great ×4 grandchild');
2004
2005
                case 7:
2006
                    if ($sex2 === 'M') {
2007
                        return I18N::translate('great ×5 grandson');
2008
                    }
2009
2010
                    if ($sex2 === 'F') {
2011
                        return I18N::translate('great ×5 granddaughter');
2012
                    }
2013
2014
                    return I18N::translate('great ×5 grandchild');
2015
2016
                case 8:
2017
                    if ($sex2 === 'M') {
2018
                        return I18N::translate('great ×6 grandson');
2019
                    }
2020
2021
                    if ($sex2 === 'F') {
2022
                        return I18N::translate('great ×6 granddaughter');
2023
                    }
2024
2025
                    return I18N::translate('great ×6 grandchild');
2026
2027
                case 9:
2028
                    if ($sex2 === 'M') {
2029
                        return I18N::translate('great ×7 grandson');
2030
                    }
2031
2032
                    if ($sex2 === 'F') {
2033
                        return I18N::translate('great ×7 granddaughter');
2034
                    }
2035
2036
                    return I18N::translate('great ×7 grandchild');
2037
2038
                default:
2039
                    // Different languages have different rules for naming generations.
2040
                    // An English great ×12 grandson is a Danish great ×11 grandson.
2041
                    //
2042
                    // Need to find out which languages use which rules.
2043
                    switch (I18N::languageTag()) {
2044
                        case 'nn': // Source: Hogne Røed Nilsen
2045
                        case 'nb':
2046
                        case 'da': // Source: Patrick Sorensen
2047
                            if ($sex2 === 'M') {
2048
                                return I18N::translate('great ×%s grandson', I18N::number($up - 3));
2049
                            }
2050
2051
                            if ($sex2 === 'F') {
2052
                                return I18N::translate('great ×%s granddaughter', I18N::number($up - 3));
2053
                            }
2054
2055
                            return I18N::translate('great ×%s grandchild', I18N::number($up - 3));
2056
2057
                        case 'ko': // Source : Jeongwan Nam ([email protected])
2058
                            if ($sex2 === 'M') {
2059
                                return I18N::translate('great ×%s grandson', I18N::number($up + 1));
2060
                            }
2061
2062
                            if ($sex2 === 'F') {
2063
                                return I18N::translate('great ×%s granddaughter', I18N::number($up + 1));
2064
                            }
2065
2066
                            return I18N::translate('great ×%s grandchild', I18N::number($up + 1));
2067
2068
                        case 'zh-Hans': // Source: xmlf
2069
                        case 'zh-Hant':
2070
                            if ($sex2 === 'M') {
2071
                                return I18N::translate('great ×%s grandson', I18N::number($up));
2072
                            }
2073
                            if ($sex2 === 'F') {
2074
                                return I18N::translate('great ×%s granddaughter', I18N::number($up));
2075
                            }
2076
2077
                            return I18N::translate('great ×%s grandchild', I18N::number($up));
2078
2079
                        case 'it':
2080
                            // Source: Michele Locati
2081
                        case 'es':
2082
                            // Source: Wes Groleau (adding doesn’t change behavior, but needs to be better researched)
2083
                        case 'en_AU':
2084
                        case 'en_GB':
2085
                        case 'en_US':
2086
                        default:
2087
                            if ($sex2 === 'M') {
2088
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
2089
                                return I18N::translate('great ×%s grandson', I18N::number($up - 2));
2090
                            }
2091
2092
                            if ($sex2 === 'F') {
2093
                                // I18N: if you need a different number for %s, contact the developers, as a code-change is required
2094
                                return I18N::translate('great ×%s granddaughter', I18N::number($up - 2));
2095
                            }
2096
2097
                            // I18N: if you need a different number for %s, contact the developers, as a code-change is required
2098
                            return I18N::translate('great ×%s grandchild', I18N::number($up - 2));
2099
                    }
2100
            }
2101
        }
2102
        if (preg_match('/^((?:mot|fat|par)+)(?:bro|sis|sib)((?:son|dau|chi)+)$/', $path, $match) === 1) {
2103
            // cousins in English
2104
            $ascent  = $match[1];
2105
            $descent = $match[2];
2106
            $up      = intdiv(strlen($ascent), 3);
2107
            $down    = intdiv(strlen($descent), 3);
2108
            $cousin  = min($up, $down); // Moved out of switch (en/default case) so that
2109
            $removed = abs($down - $up); // Spanish (and other languages) can use it, too.
2110
2111
            // Different languages have different rules for naming cousins. For example,
2112
            // an English “second cousin once removed” is a Polish “cousin of 7th degree”.
2113
            //
2114
            // Need to find out which languages use which rules.
2115
            switch (I18N::languageTag()) {
2116
                case 'pl': // Source: Lukasz Wilenski
2117
                    return self::legacyCousinName($up + $down + 2, $sex2);
2118
                case 'it':
2119
                    // Source: Michele Locati. See italian_cousins_names.zip
2120
                    // https://webtrees.net/forums/8-translation/1200-great-xn-grandparent?limit=6&start=6
2121
                    return self::legacyCousinName($up + $down - 3, $sex2);
2122
                case 'es':
2123
                    if ($down === $up) {
2124
                        return self::legacyCousinName($cousin, $sex2);
2125
                    }
2126
2127
                    if ($down < $up) {
2128
                        return self::legacyCousinName2($cousin + 1, $sex2, $this->legacyNameAlgorithm('sib' . $descent));
2129
                    }
2130
2131
                    if ($sex2 === 'M') {
2132
                        return self::legacyCousinName2($cousin + 1, $sex2, $this->legacyNameAlgorithm('bro' . $descent));
2133
                    }
2134
2135
                    if ($sex2 === 'F') {
2136
                        return self::legacyCousinName2($cousin + 1, $sex2, $this->legacyNameAlgorithm('sis' . $descent));
2137
                    }
2138
2139
                    return self::legacyCousinName2($cousin + 1, $sex2, $this->legacyNameAlgorithm('sib' . $descent));
2140
2141
                case 'en_AU': // See: https://en.wikipedia.org/wiki/File:CousinTree.svg
2142
                case 'en_GB':
2143
                case 'en_US':
2144
                default:
2145
                    switch ($removed) {
2146
                        case 0:
2147
                            return self::legacyCousinName($cousin, $sex2);
2148
                        case 1:
2149
                            if ($up > $down) {
2150
                                /* I18N: %s=“fifth cousin”, etc. */
2151
                                return I18N::translate('%s once removed ascending', self::legacyCousinName($cousin, $sex2));
2152
                            }
2153
2154
                            /* I18N: %s=“fifth cousin”, etc. */
2155
2156
                            return I18N::translate('%s once removed descending', self::legacyCousinName($cousin, $sex2));
2157
                        case 2:
2158
                            if ($up > $down) {
2159
                                /* I18N: %s=“fifth cousin”, etc. */
2160
                                return I18N::translate('%s twice removed ascending', self::legacyCousinName($cousin, $sex2));
2161
                            }
2162
2163
                            /* I18N: %s=“fifth cousin”, etc. */
2164
2165
                            return I18N::translate('%s twice removed descending', self::legacyCousinName($cousin, $sex2));
2166
                        case 3:
2167
                            if ($up > $down) {
2168
                                /* I18N: %s=“fifth cousin”, etc. */
2169
                                return I18N::translate('%s three times removed ascending', self::legacyCousinName($cousin, $sex2));
2170
                            }
2171
2172
                            /* I18N: %s=“fifth cousin”, etc. */
2173
2174
                            return I18N::translate('%s three times removed descending', self::legacyCousinName($cousin, $sex2));
2175
                        default:
2176
                            if ($up > $down) {
2177
                                /* I18N: %1$s=“fifth cousin”, etc., %2$s>=4 */
2178
                                return I18N::translate('%1$s %2$s times removed ascending', self::legacyCousinName($cousin, $sex2), I18N::number($removed));
2179
                            }
2180
2181
                            /* I18N: %1$s=“fifth cousin”, etc., %2$s>=4 */
2182
2183
                            return I18N::translate('%1$s %2$s times removed descending', self::legacyCousinName($cousin, $sex2), I18N::number($removed));
2184
                    }
2185
            }
2186
        }
2187
2188
        // Split the relationship into sub-relationships, e.g., third-cousin’s great-uncle.
2189
        // Try splitting at every point, and choose the path with the shorted translated name.
2190
        // But before starting to recursively go through all combinations, do a cache look-up
2191
2192
        static $relationshipsCache;
2193
        $relationshipsCache ??= [];
2194
        if (array_key_exists($path, $relationshipsCache)) {
2195
            return $relationshipsCache[$path];
2196
        }
2197
2198
        $relationship = '';
2199
        $path1        = substr($path, 0, 3);
2200
        $path2        = substr($path, 3);
2201
        while ($path2 !== '') {
2202
            // I18N: A complex relationship, such as “third-cousin’s great-uncle”
2203
            $tmp = I18N::translate(
2204
                '%1$s’s %2$s',
2205
                $this->legacyNameAlgorithm($path1),
2206
                $this->legacyNameAlgorithm($path2)
2207
            );
2208
            if ($relationship === '' || strlen($tmp) < strlen($relationship)) {
2209
                $relationship = $tmp;
2210
            }
2211
            $path1 .= substr($path2, 0, 3);
2212
            $path2 = substr($path2, 3);
2213
        }
2214
        // and store the result in the cache
2215
        $relationshipsCache[$path] = $relationship;
2216
2217
        return $relationship;
2218
    }
2219
2220
    /**
2221
     * Calculate the name of a cousin.
2222
     *
2223
     * @param int    $n
2224
     * @param string $sex
2225
     *
2226
     * @return string
2227
     *
2228
     * @deprecated
2229
     */
2230
    private static function legacyCousinName(int $n, string $sex): string
2231
    {
2232
        if ($sex === 'M') {
2233
            switch ($n) {
2234
                case 1:
2235
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2236
                    return I18N::translateContext('MALE', 'first cousin');
2237
                case 2:
2238
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2239
                    return I18N::translateContext('MALE', 'second cousin');
2240
                case 3:
2241
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2242
                    return I18N::translateContext('MALE', 'third cousin');
2243
                case 4:
2244
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2245
                    return I18N::translateContext('MALE', 'fourth cousin');
2246
                case 5:
2247
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2248
                    return I18N::translateContext('MALE', 'fifth cousin');
2249
                case 6:
2250
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2251
                    return I18N::translateContext('MALE', 'sixth cousin');
2252
                case 7:
2253
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2254
                    return I18N::translateContext('MALE', 'seventh cousin');
2255
                case 8:
2256
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2257
                    return I18N::translateContext('MALE', 'eighth cousin');
2258
                case 9:
2259
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2260
                    return I18N::translateContext('MALE', 'ninth cousin');
2261
                case 10:
2262
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2263
                    return I18N::translateContext('MALE', 'tenth cousin');
2264
                case 11:
2265
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2266
                    return I18N::translateContext('MALE', 'eleventh cousin');
2267
                case 12:
2268
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2269
                    return I18N::translateContext('MALE', 'twelfth cousin');
2270
                case 13:
2271
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2272
                    return I18N::translateContext('MALE', 'thirteenth cousin');
2273
                case 14:
2274
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2275
                    return I18N::translateContext('MALE', 'fourteenth cousin');
2276
                case 15:
2277
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2278
                    return I18N::translateContext('MALE', 'fifteenth cousin');
2279
                default:
2280
                    /* I18N: Note that for Italian and Polish, “N’th cousins” are different from English “N’th cousins”, and the software has already generated the correct “N” for your language. You only need to translate - you do not need to convert. For other languages, if your cousin rules are different from English, please contact the developers. */
2281
                    return I18N::translateContext('MALE', '%s × cousin', I18N::number($n));
2282
            }
2283
        }
2284
2285
        if ($sex === 'F') {
2286
            switch ($n) {
2287
                case 1:
2288
                    return I18N::translateContext('FEMALE', 'first cousin');
2289
                case 2:
2290
                    return I18N::translateContext('FEMALE', 'second cousin');
2291
                case 3:
2292
                    return I18N::translateContext('FEMALE', 'third cousin');
2293
                case 4:
2294
                    return I18N::translateContext('FEMALE', 'fourth cousin');
2295
                case 5:
2296
                    return I18N::translateContext('FEMALE', 'fifth cousin');
2297
                case 6:
2298
                    return I18N::translateContext('FEMALE', 'sixth cousin');
2299
                case 7:
2300
                    return I18N::translateContext('FEMALE', 'seventh cousin');
2301
                case 8:
2302
                    return I18N::translateContext('FEMALE', 'eighth cousin');
2303
                case 9:
2304
                    return I18N::translateContext('FEMALE', 'ninth cousin');
2305
                case 10:
2306
                    return I18N::translateContext('FEMALE', 'tenth cousin');
2307
                case 11:
2308
                    return I18N::translateContext('FEMALE', 'eleventh cousin');
2309
                case 12:
2310
                    return I18N::translateContext('FEMALE', 'twelfth cousin');
2311
                case 13:
2312
                    return I18N::translateContext('FEMALE', 'thirteenth cousin');
2313
                case 14:
2314
                    return I18N::translateContext('FEMALE', 'fourteenth cousin');
2315
                case 15:
2316
                    return I18N::translateContext('FEMALE', 'fifteenth cousin');
2317
                default:
2318
                    return I18N::translateContext('FEMALE', '%s × cousin', I18N::number($n));
2319
            }
2320
        }
2321
2322
        switch ($n) {
2323
            case 1:
2324
                return I18N::translate('first cousin');
2325
            case 2:
2326
                return I18N::translate('second cousin');
2327
            case 3:
2328
                return I18N::translate('third cousin');
2329
            case 4:
2330
                return I18N::translate('fourth cousin');
2331
            case 5:
2332
                return I18N::translate('fifth cousin');
2333
            case 6:
2334
                return I18N::translate('sixth cousin');
2335
            case 7:
2336
                return I18N::translate('seventh cousin');
2337
            case 8:
2338
                return I18N::translate('eighth cousin');
2339
            case 9:
2340
                return I18N::translate('ninth cousin');
2341
            case 10:
2342
                return I18N::translate('tenth cousin');
2343
            case 11:
2344
                return I18N::translate('eleventh cousin');
2345
            case 12:
2346
                return I18N::translate('twelfth cousin');
2347
            case 13:
2348
                return I18N::translate('thirteenth cousin');
2349
            case 14:
2350
                return I18N::translate('fourteenth cousin');
2351
            case 15:
2352
                return I18N::translate('fifteenth cousin');
2353
            default:
2354
                return I18N::translate('%s × cousin', I18N::number($n));
2355
        }
2356
    }
2357
2358
    /**
2359
     * A variation on cousin_name(), for constructs such as “sixth great-nephew”
2360
     * Currently used only by Spanish relationship names.
2361
     *
2362
     * @param int    $n
2363
     * @param string $sex
2364
     * @param string $relation
2365
     *
2366
     * @return string
2367
     *
2368
     * @deprecated
2369
     */
2370
    private static function legacyCousinName2(int $n, string $sex, string $relation): string
2371
    {
2372
        if ($sex === 'M') {
2373
            switch ($n) {
2374
                case 1:
2375
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2376
                    return I18N::translateContext('MALE', 'first %s', $relation);
2377
                case 2:
2378
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2379
                    return I18N::translateContext('MALE', 'second %s', $relation);
2380
                case 3:
2381
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2382
                    return I18N::translateContext('MALE', 'third %s', $relation);
2383
                case 4:
2384
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2385
                    return I18N::translateContext('MALE', 'fourth %s', $relation);
2386
                case 5:
2387
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2388
                    return I18N::translateContext('MALE', 'fifth %s', $relation);
2389
                default:
2390
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2391
                    return I18N::translateContext('MALE', '%1$s × %2$s', I18N::number($n), $relation);
2392
            }
2393
        }
2394
2395
        if ($sex === 'F') {
2396
            switch ($n) {
2397
                case 1:
2398
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2399
                    return I18N::translateContext('FEMALE', 'first %s', $relation);
2400
                case 2:
2401
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2402
                    return I18N::translateContext('FEMALE', 'second %s', $relation);
2403
                case 3:
2404
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2405
                    return I18N::translateContext('FEMALE', 'third %s', $relation);
2406
                case 4:
2407
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2408
                    return I18N::translateContext('FEMALE', 'fourth %s', $relation);
2409
                case 5:
2410
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2411
                    return I18N::translateContext('FEMALE', 'fifth %s', $relation);
2412
                default:
2413
                    /* I18N: A Spanish relationship name, such as third great-nephew */
2414
                    return I18N::translateContext('FEMALE', '%1$s × %2$s', I18N::number($n), $relation);
2415
            }
2416
        }
2417
2418
        switch ($n) {
2419
            case 1:
2420
                /* I18N: A Spanish relationship name, such as third great-nephew */
2421
                return I18N::translate('first %s', $relation);
2422
            case 2:
2423
                /* I18N: A Spanish relationship name, such as third great-nephew */
2424
                return I18N::translate('second %s', $relation);
2425
            case 3:
2426
                /* I18N: A Spanish relationship name, such as third great-nephew */
2427
                return I18N::translate('third %s', $relation);
2428
            case 4:
2429
                /* I18N: A Spanish relationship name, such as third great-nephew */
2430
                return I18N::translate('fourth %s', $relation);
2431
            case 5:
2432
                /* I18N: A Spanish relationship name, such as third great-nephew */
2433
                return I18N::translate('fifth %s', $relation);
2434
            default:
2435
                /* I18N: A Spanish relationship name, such as third great-nephew */
2436
                return I18N::translate('%1$s × %2$s', I18N::number($n), $relation);
2437
        }
2438
    }
2439
}
2440