Functions::getRelationshipNameFromPath()   F
last analyzed

Complexity

Conditions 709

Size

Total Lines 1884
Code Lines 1306

Duplication

Lines 0
Ratio 0 %

Importance

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