Functions::cousinName()   F
last analyzed

Complexity

Conditions 48
Paths 48

Size

Total Lines 123
Code Lines 103

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 48
eloc 103
nc 48
nop 2
dl 0
loc 123
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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