RelationshipService::legacyNameAlgorithm()   F
last analyzed

Complexity

Conditions 708

Size

Total Lines 1891
Code Lines 1306

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 708
eloc 1306
c 1
b 1
f 0
nop 3
dl 0
loc 1891
rs 3.3333

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