Issues (2559)

app/Gedcom.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use Fisharebest\Webtrees\Contracts\ElementFactoryInterface;
23
use Fisharebest\Webtrees\Contracts\ElementInterface;
24
use Fisharebest\Webtrees\CustomTags\Aldfaer;
25
use Fisharebest\Webtrees\CustomTags\Ancestry;
26
use Fisharebest\Webtrees\CustomTags\BrothersKeeper;
27
use Fisharebest\Webtrees\CustomTags\FamilySearch;
28
use Fisharebest\Webtrees\CustomTags\FamilyTreeBuilder;
29
use Fisharebest\Webtrees\CustomTags\FamilyTreeMaker;
30
use Fisharebest\Webtrees\CustomTags\Gedcom7;
31
use Fisharebest\Webtrees\CustomTags\GedcomL;
32
use Fisharebest\Webtrees\CustomTags\Geneatique;
33
use Fisharebest\Webtrees\CustomTags\GenPlusWin;
34
use Fisharebest\Webtrees\CustomTags\Heredis;
35
use Fisharebest\Webtrees\CustomTags\Legacy;
36
use Fisharebest\Webtrees\CustomTags\MyHeritage;
37
use Fisharebest\Webtrees\CustomTags\PersonalAncestralFile;
38
use Fisharebest\Webtrees\CustomTags\PhpGedView;
39
use Fisharebest\Webtrees\CustomTags\ProGen;
40
use Fisharebest\Webtrees\CustomTags\Reunion;
41
use Fisharebest\Webtrees\CustomTags\RootsMagic;
42
use Fisharebest\Webtrees\CustomTags\TheMasterGenealogist;
43
use Fisharebest\Webtrees\CustomTags\TheNextGeneration;
44
use Fisharebest\Webtrees\Elements\AddressCity;
45
use Fisharebest\Webtrees\Elements\AddressCountry;
46
use Fisharebest\Webtrees\Elements\AddressEmail;
47
use Fisharebest\Webtrees\Elements\AddressFax;
48
use Fisharebest\Webtrees\Elements\AddressLine;
49
use Fisharebest\Webtrees\Elements\AddressLine1;
50
use Fisharebest\Webtrees\Elements\AddressLine2;
51
use Fisharebest\Webtrees\Elements\AddressLine3;
52
use Fisharebest\Webtrees\Elements\AddressPostalCode;
53
use Fisharebest\Webtrees\Elements\AddressState;
54
use Fisharebest\Webtrees\Elements\AddressWebPage;
55
use Fisharebest\Webtrees\Elements\AdoptedByWhichParent;
56
use Fisharebest\Webtrees\Elements\Adoption;
57
use Fisharebest\Webtrees\Elements\AdultChristening;
58
use Fisharebest\Webtrees\Elements\AgeAtEvent;
59
use Fisharebest\Webtrees\Elements\AncestralFileNumber;
60
use Fisharebest\Webtrees\Elements\Annulment;
61
use Fisharebest\Webtrees\Elements\ApprovedSystemId;
62
use Fisharebest\Webtrees\Elements\AutomatedRecordId;
63
use Fisharebest\Webtrees\Elements\Baptism;
64
use Fisharebest\Webtrees\Elements\BarMitzvah;
65
use Fisharebest\Webtrees\Elements\BasMitzvah;
66
use Fisharebest\Webtrees\Elements\Birth;
67
use Fisharebest\Webtrees\Elements\Blessing;
68
use Fisharebest\Webtrees\Elements\Burial;
69
use Fisharebest\Webtrees\Elements\CasteName;
70
use Fisharebest\Webtrees\Elements\CauseOfEvent;
71
use Fisharebest\Webtrees\Elements\Census;
72
use Fisharebest\Webtrees\Elements\CertaintyAssessment;
73
use Fisharebest\Webtrees\Elements\Change;
74
use Fisharebest\Webtrees\Elements\ChangeDate;
75
use Fisharebest\Webtrees\Elements\CharacterSet;
76
use Fisharebest\Webtrees\Elements\ChildLinkageStatus;
77
use Fisharebest\Webtrees\Elements\Christening;
78
use Fisharebest\Webtrees\Elements\Confirmation;
79
use Fisharebest\Webtrees\Elements\ContentDescription;
80
use Fisharebest\Webtrees\Elements\Coordinates;
81
use Fisharebest\Webtrees\Elements\CopyrightFile;
82
use Fisharebest\Webtrees\Elements\CopyrightSourceData;
83
use Fisharebest\Webtrees\Elements\CountOfChildren;
84
use Fisharebest\Webtrees\Elements\CountOfChildrenFam;
85
use Fisharebest\Webtrees\Elements\CountOfMarriages;
86
use Fisharebest\Webtrees\Elements\Cremation;
87
use Fisharebest\Webtrees\Elements\CustomElement;
88
use Fisharebest\Webtrees\Elements\DateLdsOrd;
89
use Fisharebest\Webtrees\Elements\DateValue;
90
use Fisharebest\Webtrees\Elements\DateValueExact;
91
use Fisharebest\Webtrees\Elements\Death;
92
use Fisharebest\Webtrees\Elements\DescriptiveTitle;
93
use Fisharebest\Webtrees\Elements\Divorce;
94
use Fisharebest\Webtrees\Elements\DivorceFiled;
95
use Fisharebest\Webtrees\Elements\Emigration;
96
use Fisharebest\Webtrees\Elements\EmptyElement;
97
use Fisharebest\Webtrees\Elements\Engagement;
98
use Fisharebest\Webtrees\Elements\EventOrFactClassification;
99
use Fisharebest\Webtrees\Elements\EventsRecorded;
100
use Fisharebest\Webtrees\Elements\EventTypeCitedFrom;
101
use Fisharebest\Webtrees\Elements\FamilyCensus;
102
use Fisharebest\Webtrees\Elements\FamilyEvent;
103
use Fisharebest\Webtrees\Elements\FamilyRecord;
104
use Fisharebest\Webtrees\Elements\FileName;
105
use Fisharebest\Webtrees\Elements\FirstCommunion;
106
use Fisharebest\Webtrees\Elements\Form;
107
use Fisharebest\Webtrees\Elements\GedcomElement;
108
use Fisharebest\Webtrees\Elements\GenerationsOfAncestors;
109
use Fisharebest\Webtrees\Elements\GenerationsOfDescendants;
110
use Fisharebest\Webtrees\Elements\Graduation;
111
use Fisharebest\Webtrees\Elements\HeaderRecord;
112
use Fisharebest\Webtrees\Elements\Immigration;
113
use Fisharebest\Webtrees\Elements\IndividualEvent;
114
use Fisharebest\Webtrees\Elements\IndividualFact;
115
use Fisharebest\Webtrees\Elements\IndividualRecord;
116
use Fisharebest\Webtrees\Elements\LanguageId;
117
use Fisharebest\Webtrees\Elements\LdsBaptism;
118
use Fisharebest\Webtrees\Elements\LdsBaptismDateStatus;
119
use Fisharebest\Webtrees\Elements\LdsChildSealing;
120
use Fisharebest\Webtrees\Elements\LdsChildSealingDateStatus;
121
use Fisharebest\Webtrees\Elements\LdsConfirmation;
122
use Fisharebest\Webtrees\Elements\LdsEndowment;
123
use Fisharebest\Webtrees\Elements\LdsEndowmentDateStatus;
124
use Fisharebest\Webtrees\Elements\LdsSpouseSealing;
125
use Fisharebest\Webtrees\Elements\LdsSpouseSealingDateStatus;
126
use Fisharebest\Webtrees\Elements\Marriage;
127
use Fisharebest\Webtrees\Elements\MarriageBanns;
128
use Fisharebest\Webtrees\Elements\MarriageContract;
129
use Fisharebest\Webtrees\Elements\MarriageLicence;
130
use Fisharebest\Webtrees\Elements\MarriageSettlement;
131
use Fisharebest\Webtrees\Elements\MarriageType;
132
use Fisharebest\Webtrees\Elements\MediaRecord;
133
use Fisharebest\Webtrees\Elements\MultimediaFileReference;
134
use Fisharebest\Webtrees\Elements\MultimediaFormat;
135
use Fisharebest\Webtrees\Elements\NameOfBusiness;
136
use Fisharebest\Webtrees\Elements\NameOfFamilyFile;
137
use Fisharebest\Webtrees\Elements\NameOfProduct;
138
use Fisharebest\Webtrees\Elements\NameOfRepository;
139
use Fisharebest\Webtrees\Elements\NameOfSourceData;
140
use Fisharebest\Webtrees\Elements\NamePersonal;
141
use Fisharebest\Webtrees\Elements\NamePhoneticVariation;
142
use Fisharebest\Webtrees\Elements\NamePieceGiven;
143
use Fisharebest\Webtrees\Elements\NamePieceNickname;
144
use Fisharebest\Webtrees\Elements\NamePiecePrefix;
145
use Fisharebest\Webtrees\Elements\NamePieceSuffix;
146
use Fisharebest\Webtrees\Elements\NamePieceSurname;
147
use Fisharebest\Webtrees\Elements\NamePieceSurnamePrefix;
148
use Fisharebest\Webtrees\Elements\NameRomanizedVariation;
149
use Fisharebest\Webtrees\Elements\NameType;
150
use Fisharebest\Webtrees\Elements\NationalIdNumber;
151
use Fisharebest\Webtrees\Elements\NationalOrTribalOrigin;
152
use Fisharebest\Webtrees\Elements\Naturalization;
153
use Fisharebest\Webtrees\Elements\NobilityTypeTitle;
154
use Fisharebest\Webtrees\Elements\NoteRecord;
155
use Fisharebest\Webtrees\Elements\NoteStructure;
156
use Fisharebest\Webtrees\Elements\Occupation;
157
use Fisharebest\Webtrees\Elements\OrdinanceProcessFlag;
158
use Fisharebest\Webtrees\Elements\Ordination;
159
use Fisharebest\Webtrees\Elements\PedigreeLinkageType;
160
use Fisharebest\Webtrees\Elements\PermanentRecordFileNumber;
161
use Fisharebest\Webtrees\Elements\PhoneNumber;
162
use Fisharebest\Webtrees\Elements\PhoneticType;
163
use Fisharebest\Webtrees\Elements\PhysicalDescription;
164
use Fisharebest\Webtrees\Elements\PlaceHierarchy;
165
use Fisharebest\Webtrees\Elements\PlaceLatitude;
166
use Fisharebest\Webtrees\Elements\PlaceLivingOrdinance;
167
use Fisharebest\Webtrees\Elements\PlaceLongtitude;
168
use Fisharebest\Webtrees\Elements\PlaceName;
169
use Fisharebest\Webtrees\Elements\PlacePhoneticVariation;
170
use Fisharebest\Webtrees\Elements\PlaceRomanizedVariation;
171
use Fisharebest\Webtrees\Elements\Possessions;
172
use Fisharebest\Webtrees\Elements\Probate;
173
use Fisharebest\Webtrees\Elements\PublicationDate;
174
use Fisharebest\Webtrees\Elements\ReceivingSystemName;
175
use Fisharebest\Webtrees\Elements\RelationIsDescriptor;
176
use Fisharebest\Webtrees\Elements\ReligiousAffiliation;
177
use Fisharebest\Webtrees\Elements\RepositoryRecord;
178
use Fisharebest\Webtrees\Elements\Residence;
179
use Fisharebest\Webtrees\Elements\ResponsibleAgency;
180
use Fisharebest\Webtrees\Elements\RestrictionNotice;
181
use Fisharebest\Webtrees\Elements\Retirement;
182
use Fisharebest\Webtrees\Elements\RoleInEvent;
183
use Fisharebest\Webtrees\Elements\RomanizedType;
184
use Fisharebest\Webtrees\Elements\ScholasticAchievement;
185
use Fisharebest\Webtrees\Elements\SexValue;
186
use Fisharebest\Webtrees\Elements\SocialSecurityNumber;
187
use Fisharebest\Webtrees\Elements\SourceCallNumber;
188
use Fisharebest\Webtrees\Elements\SourceData;
189
use Fisharebest\Webtrees\Elements\SourceFiledByEntry;
190
use Fisharebest\Webtrees\Elements\SourceJurisdictionPlace;
191
use Fisharebest\Webtrees\Elements\SourceMediaType;
192
use Fisharebest\Webtrees\Elements\SourceOriginator;
193
use Fisharebest\Webtrees\Elements\SourcePublicationFacts;
194
use Fisharebest\Webtrees\Elements\SourceRecord;
195
use Fisharebest\Webtrees\Elements\SubmissionRecord;
196
use Fisharebest\Webtrees\Elements\SubmitterName;
197
use Fisharebest\Webtrees\Elements\SubmitterRecord;
198
use Fisharebest\Webtrees\Elements\SubmitterRegisteredRfn;
199
use Fisharebest\Webtrees\Elements\SubmitterText;
200
use Fisharebest\Webtrees\Elements\TempleCode;
201
use Fisharebest\Webtrees\Elements\TextFromSource;
202
use Fisharebest\Webtrees\Elements\TimeValueNow;
203
use Fisharebest\Webtrees\Elements\TransmissionDate;
204
use Fisharebest\Webtrees\Elements\UserReferenceNumber;
205
use Fisharebest\Webtrees\Elements\UserReferenceType;
206
use Fisharebest\Webtrees\Elements\VersionNumber;
207
use Fisharebest\Webtrees\Elements\WebtreesUser;
208
use Fisharebest\Webtrees\Elements\WhereWithinSource;
209
use Fisharebest\Webtrees\Elements\Will;
210
use Fisharebest\Webtrees\Elements\XrefAssociate;
211
use Fisharebest\Webtrees\Elements\XrefFamily;
212
use Fisharebest\Webtrees\Elements\XrefIndividual;
213
use Fisharebest\Webtrees\Elements\XrefMedia;
214
use Fisharebest\Webtrees\Elements\XrefRepository;
215
use Fisharebest\Webtrees\Elements\XrefSource;
216
use Fisharebest\Webtrees\Elements\XrefSubmission;
217
use Fisharebest\Webtrees\Elements\XrefSubmitter;
218
219
/**
220
 * GEDCOM 5.5.1 specification
221
 */
222
class Gedcom
223
{
224
    // 255 less the EOL character.
225
    public const int LINE_LENGTH = 253;
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 225 at column 21
Loading history...
226
227
    // Gedcom tags which indicate the start of life.
228
    public const array BIRTH_EVENTS = ['BIRT', 'CHR', 'BAPM'];
229
230
    // Gedcom tags which indicate the end of life.
231
    public const array DEATH_EVENTS = ['DEAT', 'BURI', 'CREM'];
232
233
    // Gedcom tags which indicate the start of a relationship.
234
    public const array MARRIAGE_EVENTS = ['MARR', '_NMR'];
235
236
    // Gedcom tags which indicate the end of a relationship.
237
    public const array DIVORCE_EVENTS = ['DIV', 'ANUL', '_SEPR'];
238
239
    // Regular expression to match a GEDCOM tag.
240
    public const string REGEX_TAG = '[_A-Z][_A-Z0-9]*';
241
242
    // Regular expression to match a GEDCOM XREF.
243
    public const string REGEX_XREF = '[A-Za-z0-9:_.-]{1,20}';
244
245
    // Regular expression to match a GEDCOM fact/event for editing raw GEDCOM.
246
    private const string REGEX_VALUE   = '( .+)?';
247
    private const string REGEX_LEVEL_9 = '\n9 ' . self::REGEX_TAG . self::REGEX_VALUE;
248
    private const string REGEX_LEVEL_8 = '\n8 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_9 . ')*';
249
    private const string REGEX_LEVEL_7 = '\n7 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_8 . ')*';
250
    private const string REGEX_LEVEL_6 = '\n6 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_7 . ')*';
251
    private const string REGEX_LEVEL_5 = '\n5 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_6 . ')*';
252
    private const string REGEX_LEVEL_4 = '\n4 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_5 . ')*';
253
    private const string REGEX_LEVEL_3 = '\n3 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_4 . ')*';
254
    private const string REGEX_LEVEL_2 = '\n2 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_3 . ')*';
255
    public const string  REGEX_FACT    = '1 ' . self::REGEX_TAG . self::REGEX_VALUE . '(' . self::REGEX_LEVEL_2 . ')*\n?';
256
257
    // Separates the parts of a place name.
258
    public const string PLACE_SEPARATOR = ', ';
259
260
    // Regex to match a (badly formed) GEDCOM place separator.
261
    public const string PLACE_SEPARATOR_REGEX = '/ *,[, ]*/';
262
263
    // LATI and LONG tags
264
    public const string LATITUDE_NORTH = 'N';
265
    public const string LATITUDE_SOUTH = 'S';
266
    public const string LONGITUDE_EAST = 'E';
267
    public const string LONGITUDE_WEST = 'W';
268
269
    // Not all record types allow a CHAN event.
270
    public const array RECORDS_WITH_CHAN = [
271
        Family::RECORD_TYPE,
272
        Individual::RECORD_TYPE,
273
        Media::RECORD_TYPE,
274
        Note::RECORD_TYPE,
275
        Repository::RECORD_TYPE,
276
        Source::RECORD_TYPE,
277
        Submitter::RECORD_TYPE,
278
    ];
279
280
    // These preferences control multiple tag definitions
281
    public const array HIDDEN_TAGS = [
282
        // Individual names
283
        'NAME_NPFX'  => ['INDI:NAME:NPFX', 'INDI:NAME:FONE:NPFX', 'INDI:NAME:ROMN:NPFX'],
284
        'NAME_SPFX'  => ['INDI:NAME:SPFX', 'INDI:NAME:FONE:SPFX', 'INDI:NAME:ROMN:SPFX'],
285
        'NAME_NSFX'  => ['INDI:NAME:NSFX', 'INDI:NAME:FONE:NSFX', 'INDI:NAME:ROMN:NSFX'],
286
        'NAME_NICK'  => ['INDI:NAME:NICK', 'INDI:NAME:FONE:NICK', 'INDI:NAME:ROMN:NICK'],
287
        'NAME_FONE'  => ['INDI:NAME:FONE'],
288
        'NAME_ROMN'  => ['INDI:NAME:ROMN'],
289
        'NAME_NOTE'  => ['INDI:NAME:NOTE', 'INDI:NAME:FONE:NOTE', 'INDI:NAME:ROMN:NOTE'],
290
        'NAME_SOUR'  => ['INDI:NAME:SOUR', 'INDI:NAME:FONE:SOUR', 'INDI:NAME:ROMN:SOUR'],
291
        // Places
292
        'PLAC_MAP'   => [':PLAC:MAP'],
293
        'PLAC_FONE'  => [':PLAC:FONE'],
294
        'PLAC_ROMN'  => [':PLAC:ROMN'],
295
        'PLAC_FORM'  => [':PLAC:FORM', 'HEAD:PLAC'],
296
        'PLAC_NOTE'  => [':PLAC:NOTE'],
297
        // Addresses
298
        'ADDR_EMAIL' => [':EMAIL'],
299
        'ADDR_PHON'  => [':PHON'],
300
        'ADDR_WWW'   => [':WWW'],
301
        // Source citations
302
        'SOUR_EVEN'  => [':SOUR:EVEN'],
303
        'SOUR_DATE'  => [':SOUR:DATA:DATE'],
304
        'SOUR_NOTE'  => [':SOUR:NOTE'],
305
        'SOUR_QUAY'  => [':SOUR:QUAY'],
306
        // Sources
307
        'SOUR_DATA'  => ['SOUR:DATA:EVEN', 'SOUR:DATA:AGNC', 'SOUR:DATA:NOTE'],
308
        // Individuals
309
        'BIRT_FAMC'  => ['INDI:BIRT:FAMC'],
310
        'RELI'       => ['INDI:RELI'],
311
        'BAPM'       => ['INDI:BAPM'],
312
        'CHR'        => ['INDI:CHR', 'INDI:CHRA'],
313
        'FCOM'       => ['INDI:FCOM', 'INDI:CONF'],
314
        'ORDN'       => ['INDI:ORDN'],
315
        'BARM'       => ['INDI:BARM', 'INDI:BASM'],
316
        'ALIA'       => ['INDI:ALIA'],
317
        'ASSO'       => ['INDI:ASSO'],
318
        // Families
319
        'ENGA'       => ['FAM:ENGA'],
320
        'MARB'       => ['FAM:MARB'],
321
        'MARC'       => ['FAM:MARC'],
322
        'MARL'       => ['FAM:MARL'],
323
        'MARS'       => ['FAM:MARS'],
324
        'ANUL'       => ['FAM:ANUL'],
325
        'DIVF'       => ['FAM:DIVF'],
326
        'FAM_RESI'   => ['FAM:RESI'],
327
        'FAM_CENS'   => ['FAM:CENS'],
328
        // LDS church
329
        'LDS'        => ['INDI:BAPL', 'INDI:CONL', 'INDI:ENDL', 'INDI:SLGC', 'FAM:SLGS', 'HEAD:SUBN'],
330
        // Identifiers
331
        'AFN'        => ['INDI:AFN'],
332
        'IDNO'       => ['INDI:IDNO'],
333
        'SSN'        => ['INDI:SSN'],
334
        'RFN'        => [':RFN'],
335
        'REFN'       => [':REFN'],
336
        'RIN'        => [':RIN'],
337
        // Submitters
338
        'SUBM'       => ['INDI:SUBM', 'FAM:SUBM'],
339
        'ANCI'       => ['INDI:ANCI', 'INDI:DESI'],
340
    ];
341
342
    // Custom GEDCOM tags that can be created in webtrees.
343
    public const array CUSTOM_FAMILY_TAGS = [
344
        'FACT',
345
        '_COML',
346
        '_MARI',
347
        '_MBON',
348
        '_NMR',
349
        '_SEPR',
350
    ];
351
352
    public const array CUSTOM_INDIVIDUAL_TAGS = [
353
        '_BRTM',
354
        '_CIRC',
355
        '_DEG',
356
        '_DNA',
357
        '_EXCM',
358
        '_EYEC',
359
        '_FNRL',
360
        '_FSFTID',
361
        '_HAIR',
362
        '_HEIG',
363
        '_INTE',
364
        '_MDCL',
365
        '_MEDC',
366
        '_MILI',
367
        '_MILT',
368
        '_NAMS',
369
        '_NMAR',
370
        '_PRMN',
371
        '_WEIG',
372
        '_YART',
373
    ];
374
375
    // Some applications create GEDCOM files containing records without XREFS.
376
    // We cannot process these.
377
    public const array CUSTOM_RECORDS_WITHOUT_XREFS = [
378
        'EMOTIONALRELATIONSHIP', // GenoPro
379
        'GENOMAP', // GenoPro
380
        'GLOBAL', // GenoPro
381
        'LABEL', // GenoPro
382
        'PEDIGREELINK', // GenoPro
383
        'SOCIALRELATIONSHIP', // GenoPro
384
        '_EVDEF', // RootsMagic
385
        '_EVENT_DEFN', // PAF and Legacy
386
        '_HASHTAG_DEFN', // Legacy
387
        '_PUBLISH', // MyHeritage
388
        '_TASK', // Ages
389
        '_TODO', // Legacy
390
    ];
391
392
    /**
393
     * Definitions for GEDCOM 5.5.1.
394
     *
395
     * @return array<string,ElementInterface>
396
     */
397
    private function gedcom551Tags(): array
398
    {
399
        return [
400
            'FAM'                        => new FamilyRecord(I18N::translate('Family')),
401
            'FAM:*:ADDR'                 => new AddressLine(I18N::translate('Address')),
402
            'FAM:*:ADDR:ADR1'            => new AddressLine1(I18N::translate('Address line 1')),
403
            'FAM:*:ADDR:ADR2'            => new AddressLine2(I18N::translate('Address line 2')),
404
            'FAM:*:ADDR:ADR3'            => new AddressLine3(I18N::translate('Address line 3')),
405
            'FAM:*:ADDR:CITY'            => new AddressCity(I18N::translate('City')),
406
            'FAM:*:ADDR:CTRY'            => new AddressCountry(I18N::translate('Country')),
407
            'FAM:*:ADDR:POST'            => new AddressPostalCode(I18N::translate('Postal code')),
408
            'FAM:*:ADDR:STAE'            => new AddressState(I18N::translate('State')),
409
            'FAM:*:AGNC'                 => new ResponsibleAgency(I18N::translate('Agency')),
410
            'FAM:*:CAUS'                 => new CauseOfEvent(I18N::translate('Cause')),
411
            'FAM:*:DATE'                 => new DateValue(I18N::translate('Date')),
412
            'FAM:*:EMAIL'                => new AddressEmail(I18N::translate('Email address')),
413
            'FAM:*:FAX'                  => new AddressFax(I18N::translate('Fax')),
414
            'FAM:*:HUSB'                 => new EmptyElement(I18N::translate('Husband'), ['AGE' => '0:1']),
415
            'FAM:*:HUSB:AGE'             => new AgeAtEvent(I18N::translate('Husband’s age')),
416
            'FAM:*:NOTE'                 => new NoteStructure(I18N::translate('Note')),
417
            'FAM:*:OBJE'                 => new XrefMedia(I18N::translate('Media object')),
418
            'FAM:*:PHON'                 => new PhoneNumber(I18N::translate('Phone')),
419
            'FAM:*:PLAC'                 => new PlaceName(I18N::translate('Place')),
420
            'FAM:*:PLAC:FONE'            => new PlacePhoneticVariation(I18N::translate('Phonetic place')),
421
            'FAM:*:PLAC:FONE:TYPE'       => new PhoneticType(I18N::translate('Type')),
422
            'FAM:*:PLAC:FORM'            => new PlaceHierarchy(I18N::translate('Format')),
423
            'FAM:*:PLAC:MAP'             => new Coordinates(I18N::translate('Coordinates')),
424
            'FAM:*:PLAC:MAP:LATI'        => new PlaceLatitude(I18N::translate('Latitude')),
425
            'FAM:*:PLAC:MAP:LONG'        => new PlaceLongtitude(I18N::translate('Longitude')),
426
            'FAM:*:PLAC:NOTE'            => new NoteStructure(I18N::translate('Note on place')),
427
            'FAM:*:PLAC:ROMN'            => new PlaceRomanizedVariation(I18N::translate('Romanized place')),
428
            'FAM:*:PLAC:ROMN:TYPE'       => new RomanizedType(I18N::translate('Type')),
429
            'FAM:*:RELI'                 => new ReligiousAffiliation(I18N::translate('Religion'), []),
430
            'FAM:*:RESN'                 => new RestrictionNotice(I18N::translate('Restriction')),
431
            'FAM:*:SOUR'                 => new XrefSource(I18N::translate('Source citation')),
432
            'FAM:*:SOUR:DATA'            => new SourceData(I18N::translate('Data')),
433
            'FAM:*:SOUR:DATA:DATE'       => new DateValue(I18N::translate('Date of entry in original source')),
434
            'FAM:*:SOUR:DATA:TEXT'       => new TextFromSource(I18N::translate('Text')),
435
            'FAM:*:SOUR:EVEN'            => new EventTypeCitedFrom(I18N::translate('Event')),
436
            'FAM:*:SOUR:EVEN:ROLE'       => new RoleInEvent(I18N::translate('Role')),
437
            'FAM:*:SOUR:NOTE'            => new NoteStructure(I18N::translate('Note on source citation')),
438
            'FAM:*:SOUR:OBJE'            => new XrefMedia(I18N::translate('Media object')),
439
            'FAM:*:SOUR:PAGE'            => new WhereWithinSource(I18N::translate('Citation details')),
440
            'FAM:*:SOUR:QUAY'            => new CertaintyAssessment(I18N::translate('Quality of data')),
441
            'FAM:*:TYPE'                 => new EventOrFactClassification(I18N::translate('Type')),
442
            'FAM:*:WIFE'                 => new EmptyElement(I18N::translate('Wife'), ['AGE' => '0:1']),
443
            'FAM:*:WIFE:AGE'             => new AgeAtEvent(I18N::translate('Wife’s age')),
444
            'FAM:*:WWW'                  => new AddressWebPage(I18N::translate('URL')),
445
            'FAM:ANUL'                   => new Annulment(I18N::translate('Annulment')),
446
            'FAM:CENS'                   => new FamilyCensus(I18N::translate('Family census')),
447
            'FAM:CHAN'                   => new Change(I18N::translate('Last change')),
448
            'FAM:CHAN:DATE'              => new ChangeDate(I18N::translate('Date of last change')),
449
            'FAM:CHAN:DATE:TIME'         => new TimeValueNow(I18N::translate('Time of last change')),
450
            'FAM:CHAN:NOTE'              => new NoteStructure(I18N::translate('Note on last change')),
451
            'FAM:CHIL'                   => new XrefIndividual(I18N::translate('Child')),
452
            'FAM:DIV'                    => new Divorce(I18N::translate('Divorce')),
453
            'FAM:DIV:DATE'               => new DateValue(I18N::translate('Date of divorce')),
454
            'FAM:DIVF'                   => new DivorceFiled(I18N::translate('Divorce filed')),
455
            'FAM:ENGA'                   => new Engagement(I18N::translate('Engagement')),
456
            'FAM:ENGA:DATE'              => new DateValue(I18N::translate('Date of engagement')),
457
            'FAM:ENGA:PLAC'              => new PlaceName(I18N::translate('Place of engagement')),
458
            'FAM:EVEN'                   => new FamilyEvent(I18N::translate('Event')),
459
            'FAM:EVEN:TYPE'              => new EventOrFactClassification(I18N::translate('Type of event')),
460
            'FAM:HUSB'                   => new XrefIndividual(I18N::translate('Husband')),
461
            'FAM:MARB'                   => new MarriageBanns(I18N::translate('Marriage banns')),
462
            'FAM:MARB:DATE'              => new DateValue(I18N::translate('Date of marriage banns')),
463
            'FAM:MARB:PLAC'              => new PlaceName(I18N::translate('Place of marriage banns')),
464
            'FAM:MARC'                   => new MarriageContract(I18N::translate('Marriage contract')),
465
            'FAM:MARL'                   => new MarriageLicence(I18N::translate('Marriage license')),
466
            'FAM:MARR'                   => new Marriage(I18N::translate('Marriage')),
467
            'FAM:MARR:DATE'              => new DateValue(I18N::translate('Date of marriage')),
468
            'FAM:MARR:PLAC'              => new PlaceName(I18N::translate('Place of marriage')),
469
            'FAM:MARR:TYPE'              => new MarriageType(I18N::translate('Type of marriage')),
470
            'FAM:MARS'                   => new MarriageSettlement(I18N::translate('Marriage settlement')),
471
            'FAM:NCHI'                   => new CountOfChildrenFam(I18N::translate('Number of children')),
472
            'FAM:NOTE'                   => new NoteStructure(I18N::translate('Note')),
473
            'FAM:OBJE'                   => new XrefMedia(I18N::translate('Media object')),
474
            'FAM:REFN'                   => new UserReferenceNumber(I18N::translate('Reference number')),
475
            'FAM:REFN:TYPE'              => new UserReferenceType(I18N::translate('Type of reference number')),
476
            'FAM:RESI'                   => new Residence(I18N::translate('Family residence')),
477
            'FAM:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
478
            'FAM:RIN'                    => new AutomatedRecordId(I18N::translate('Record ID number')),
479
            'FAM:SLGS'                   => new LdsSpouseSealing(I18N::translate('LDS spouse sealing')),
480
            'FAM:SLGS:DATE'              => new DateLdsOrd(I18N::translate('Date of LDS spouse sealing')),
481
            'FAM:SLGS:PLAC'              => new PlaceLivingOrdinance(I18N::translate('Place of LDS spouse sealing')),
482
            'FAM:SLGS:STAT'              => new LdsSpouseSealingDateStatus(I18N::translate('Status')),
483
            'FAM:SLGS:STAT:DATE'         => new DateValueExact(I18N::translate('Status change date')),
484
            'FAM:SLGS:TEMP'              => new TempleCode(I18N::translate('Temple')),
485
            'FAM:SOUR'                   => new XrefSource(I18N::translate('Source citation')),
486
            'FAM:SOUR:DATA'              => new SourceData(I18N::translate('Data')),
487
            'FAM:SOUR:DATA:DATE'         => new DateValue(I18N::translate('Date of entry in original source')),
488
            'FAM:SOUR:DATA:TEXT'         => new TextFromSource(I18N::translate('Text')),
489
            'FAM:SOUR:EVEN'              => new EventTypeCitedFrom(I18N::translate('Event')),
490
            'FAM:SOUR:EVEN:ROLE'         => new RoleInEvent(I18N::translate('Role')),
491
            'FAM:SOUR:NOTE'              => new NoteStructure(I18N::translate('Note on source citation')),
492
            'FAM:SOUR:OBJE'              => new XrefMedia(I18N::translate('Media object')),
493
            'FAM:SOUR:PAGE'              => new WhereWithinSource(I18N::translate('Citation details')),
494
            'FAM:SOUR:QUAY'              => new CertaintyAssessment(I18N::translate('Quality of data')),
495
            'FAM:SUBM'                   => new XrefSubmitter(I18N::translate('Submitter')),
496
            'FAM:WIFE'                   => new XrefIndividual(I18N::translate('Wife')),
497
            'HEAD'                       => new HeaderRecord(I18N::translate('Header')),
498
            'HEAD:CHAR'                  => new CharacterSet(I18N::translate('Character set')),
499
            'HEAD:CHAR:VERS'             => new VersionNumber(I18N::translate('Version')),
500
            'HEAD:COPR'                  => new CopyrightFile(I18N::translate('Copyright')),
501
            'HEAD:DATE'                  => new TransmissionDate(I18N::translate('Date')),
502
            'HEAD:DATE:TIME'             => new TimeValueNow(I18N::translate('Time')),
503
            'HEAD:DEST'                  => new ReceivingSystemName(I18N::translate('Destination')),
504
            'HEAD:FILE'                  => new FileName(I18N::translate('Filename')),
505
            'HEAD:GEDC'                  => new GedcomElement(I18N::translate('GEDCOM')),
506
            'HEAD:GEDC:FORM'             => new Form(I18N::translate('Format')),
507
            'HEAD:GEDC:VERS'             => new VersionNumber(I18N::translate('Version')),
508
            'HEAD:LANG'                  => new LanguageId(I18N::translate('Language')),
509
            'HEAD:NOTE'                  => new ContentDescription(I18N::translate('Note')),
510
            'HEAD:PLAC'                  => new EmptyElement(I18N::translate('Place hierarchy'), ['FORM' => '1:1']),
511
            'HEAD:PLAC:FORM'             => new PlaceHierarchy(I18N::translate('Format')),
512
            'HEAD:SOUR'                  => new ApprovedSystemId(I18N::translate('Application ID')),
513
            'HEAD:SOUR:CORP'             => new NameOfBusiness(I18N::translate('Corporation')),
514
            'HEAD:SOUR:CORP:ADDR'        => new AddressLine(I18N::translate('Address')),
515
            'HEAD:SOUR:CORP:ADDR:ADR1'   => new AddressLine1(I18N::translate('Address line 1')),
516
            'HEAD:SOUR:CORP:ADDR:ADR2'   => new AddressLine2(I18N::translate('Address line 2')),
517
            'HEAD:SOUR:CORP:ADDR:ADR3'   => new AddressLine3(I18N::translate('Address line 3')),
518
            'HEAD:SOUR:CORP:ADDR:CITY'   => new AddressCity(I18N::translate('City')),
519
            'HEAD:SOUR:CORP:ADDR:CTRY'   => new AddressCountry(I18N::translate('Country')),
520
            'HEAD:SOUR:CORP:ADDR:POST'   => new AddressPostalCode(I18N::translate('Postal code')),
521
            'HEAD:SOUR:CORP:ADDR:STAE'   => new AddressState(I18N::translate('State')),
522
            'HEAD:SOUR:CORP:EMAIL'       => new AddressEmail(I18N::translate('Email address')),
523
            'HEAD:SOUR:CORP:FAX'         => new AddressFax(I18N::translate('Fax')),
524
            'HEAD:SOUR:CORP:PHON'        => new PhoneNumber(I18N::translate('Phone')),
525
            'HEAD:SOUR:CORP:WWW'         => new AddressWebPage(I18N::translate('URL')),
526
            'HEAD:SOUR:DATA'             => new NameOfSourceData(I18N::translate('Data')),
527
            'HEAD:SOUR:DATA:COPR'        => new CopyrightSourceData(I18N::translate('Copyright')),
528
            'HEAD:SOUR:DATA:DATE'        => new PublicationDate(I18N::translate('Date')),
529
            'HEAD:SOUR:NAME'             => new NameOfProduct(I18N::translate('Application name')),
530
            'HEAD:SOUR:VERS'             => new VersionNumber(I18N::translate('Version')),
531
            'HEAD:SUBM'                  => new XrefSubmitter(I18N::translate('Submitter')),
532
            'HEAD:SUBN'                  => new XrefSubmission(I18N::translate('Submission')),
533
            'INDI'                       => new IndividualRecord(I18N::translate('Individual')),
534
            'INDI:*:ADDR'                => new AddressLine(I18N::translate('Address')),
535
            'INDI:*:ADDR:ADR1'           => new AddressLine1(I18N::translate('Address line 1')),
536
            'INDI:*:ADDR:ADR2'           => new AddressLine2(I18N::translate('Address line 2')),
537
            'INDI:*:ADDR:ADR3'           => new AddressLine3(I18N::translate('Address line 3')),
538
            'INDI:*:ADDR:CITY'           => new AddressCity(I18N::translate('City')),
539
            'INDI:*:ADDR:CTRY'           => new AddressCountry(I18N::translate('Country')),
540
            'INDI:*:ADDR:POST'           => new AddressPostalCode(I18N::translate('Postal code')),
541
            'INDI:*:ADDR:STAE'           => new AddressState(I18N::translate('State')),
542
            'INDI:*:AGE'                 => new AgeAtEvent(I18N::translate('Age')),
543
            'INDI:*:AGNC'                => new ResponsibleAgency(I18N::translate('Agency')),
544
            'INDI:*:CAUS'                => new CauseOfEvent(I18N::translate('Cause')),
545
            'INDI:*:DATE'                => new DateValue(I18N::translate('Date')),
546
            'INDI:*:EMAIL'               => new AddressEmail(I18N::translate('Email address')),
547
            'INDI:*:FAX'                 => new AddressFax(I18N::translate('Fax')),
548
            'INDI:*:NOTE'                => new NoteStructure(I18N::translate('Note')),
549
            'INDI:*:OBJE'                => new XrefMedia(I18N::translate('Media object')),
550
            'INDI:*:PHON'                => new PhoneNumber(I18N::translate('Phone')),
551
            'INDI:*:PLAC'                => new PlaceName(I18N::translate('Place')),
552
            'INDI:*:PLAC:FONE'           => new PlacePhoneticVariation(I18N::translate('Phonetic place')),
553
            'INDI:*:PLAC:FONE:TYPE'      => new PhoneticType(I18N::translate('Type')),
554
            'INDI:*:PLAC:FORM'           => new PlaceHierarchy(I18N::translate('Format')),
555
            'INDI:*:PLAC:MAP'            => new Coordinates(I18N::translate('Coordinates')),
556
            'INDI:*:PLAC:MAP:LATI'       => new PlaceLatitude(I18N::translate('Latitude')),
557
            'INDI:*:PLAC:MAP:LONG'       => new PlaceLongtitude(I18N::translate('Longitude')),
558
            'INDI:*:PLAC:NOTE'           => new NoteStructure(I18N::translate('Note on place')),
559
            'INDI:*:PLAC:ROMN'           => new PlaceRomanizedVariation(I18N::translate('Romanized place')),
560
            'INDI:*:PLAC:ROMN:TYPE'      => new RomanizedType(I18N::translate('Type')),
561
            'INDI:*:RELI'                => new ReligiousAffiliation(I18N::translate('Religion'), []),
562
            'INDI:*:RESN'                => new RestrictionNotice(I18N::translate('Restriction')),
563
            'INDI:*:SOUR'                => new XrefSource(I18N::translate('Source citation')),
564
            'INDI:*:SOUR:DATA'           => new SourceData(I18N::translate('Data')),
565
            'INDI:*:SOUR:DATA:DATE'      => new DateValue(I18N::translate('Date of entry in original source')),
566
            'INDI:*:SOUR:DATA:TEXT'      => new TextFromSource(I18N::translate('Text')),
567
            'INDI:*:SOUR:EVEN'           => new EventTypeCitedFrom(I18N::translate('Event')),
568
            'INDI:*:SOUR:EVEN:ROLE'      => new RoleInEvent(I18N::translate('Role')),
569
            'INDI:*:SOUR:NOTE'           => new NoteStructure(I18N::translate('Note on source citation')),
570
            'INDI:*:SOUR:OBJE'           => new XrefMedia(I18N::translate('Media object')),
571
            'INDI:*:SOUR:PAGE'           => new WhereWithinSource(I18N::translate('Citation details')),
572
            'INDI:*:SOUR:QUAY'           => new CertaintyAssessment(I18N::translate('Quality of data')),
573
            'INDI:*:TYPE'                => new EventOrFactClassification(I18N::translate('Type')),
574
            'INDI:*:WWW'                 => new AddressWebPage(I18N::translate('URL')),
575
            'INDI:ADOP'                  => new Adoption(I18N::translate('Adoption')),
576
            'INDI:ADOP:DATE'             => new DateValue(I18N::translate('Date of adoption')),
577
            'INDI:ADOP:FAMC'             => new XrefFamily(I18N::translate('Adoptive parents'), ['ADOP' => '0:1']),
578
            'INDI:ADOP:FAMC:ADOP'        => new AdoptedByWhichParent(I18N::translate('Adoption')),
579
            'INDI:ADOP:PLAC'             => new PlaceName(I18N::translate('Place of adoption')),
580
            'INDI:AFN'                   => new AncestralFileNumber(I18N::translate('Ancestral file number')),
581
            'INDI:ALIA'                  => new XrefIndividual(I18N::translate('Alias')),
582
            'INDI:ANCI'                  => new XrefSubmitter(I18N::translate('Ancestors interest')),
583
            'INDI:ASSO'                  => new XrefAssociate(I18N::translate('Associate')),
584
            'INDI:ASSO:RELA'             => new RelationIsDescriptor(I18N::translate('Relationship')),
585
            'INDI:BAPL'                  => new LdsBaptism(I18N::translate('LDS baptism')),
586
            'INDI:BAPL:DATE'             => new DateLdsOrd(I18N::translate('Date of LDS baptism')),
587
            'INDI:BAPL:PLAC'             => new PlaceLivingOrdinance(I18N::translate('Place of LDS baptism')),
588
            'INDI:BAPL:STAT'             => new LdsBaptismDateStatus(I18N::translate('Status')),
589
            'INDI:BAPL:STAT:DATE'        => new DateValueExact(I18N::translate('Status change date')),
590
            'INDI:BAPL:TEMP'             => new TempleCode(I18N::translate('Temple')),
591
            'INDI:BAPM'                  => new Baptism(I18N::translate('Baptism')),
592
            'INDI:BAPM:DATE'             => new DateValue(I18N::translate('Date of baptism')),
593
            'INDI:BAPM:PLAC'             => new PlaceName(I18N::translate('Place of baptism')),
594
            'INDI:BARM'                  => new BarMitzvah(I18N::translate('Bar mitzvah')),
595
            'INDI:BARM:DATE'             => new DateValue(I18N::translate('Date of bar mitzvah')),
596
            'INDI:BARM:PLAC'             => new PlaceName(I18N::translate('Place of bar mitzvah')),
597
            'INDI:BASM'                  => new BasMitzvah(I18N::translate('Bat mitzvah')),
598
            'INDI:BASM:DATE'             => new DateValue(I18N::translate('Date of bat mitzvah')),
599
            'INDI:BASM:PLAC'             => new PlaceName(I18N::translate('Place of bat mitzvah')),
600
            'INDI:BIRT'                  => new Birth(I18N::translate('Birth')),
601
            'INDI:BIRT:DATE'             => new DateValue(I18N::translate('Date of birth')),
602
            'INDI:BIRT:FAMC'             => new XrefFamily(I18N::translate('Birth parents')),
603
            'INDI:BIRT:PLAC'             => new PlaceName(I18N::translate('Place of birth')),
604
            'INDI:BLES'                  => new Blessing(I18N::translate('Blessing')),
605
            'INDI:BLES:DATE'             => new DateValue(I18N::translate('Date of blessing')),
606
            'INDI:BLES:PLAC'             => new PlaceName(I18N::translate('Place of blessing')),
607
            'INDI:BURI'                  => new Burial(I18N::translate('Burial')),
608
            'INDI:BURI:DATE'             => new DateValue(I18N::translate('Date of burial')),
609
            'INDI:BURI:PLAC'             => new PlaceName(I18N::translate('Place of burial')),
610
            'INDI:CAST'                  => new CasteName(I18N::translate('Caste')),
611
            'INDI:CENS'                  => new Census(I18N::translate('Census')),
612
            'INDI:CENS:DATE'             => new DateValue(I18N::translate('Census date')),
613
            'INDI:CENS:PLAC'             => new PlaceName(I18N::translate('Census place')),
614
            'INDI:CHAN'                  => new Change(I18N::translate('Last change')),
615
            'INDI:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
616
            'INDI:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
617
            'INDI:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
618
            'INDI:CHR'                   => new Christening(I18N::translate('Christening')),
619
            'INDI:CHR:DATE'              => new DateValue(I18N::translate('Date of christening')),
620
            'INDI:CHR:FAMC'              => new XrefFamily(I18N::translate('Godparents')),
621
            'INDI:CHR:PLAC'              => new PlaceName(I18N::translate('Place of christening')),
622
            'INDI:CHRA'                  => new AdultChristening(I18N::translate('Adult christening')),
623
            'INDI:CHRA:PLAC'             => new PlaceName(I18N::translate('Place of christening')),
624
            'INDI:CONF'                  => new Confirmation(I18N::translate('Confirmation')),
625
            'INDI:CONF:DATE'             => new DateValue(I18N::translate('Date of confirmation')),
626
            'INDI:CONF:PLAC'             => new PlaceName(I18N::translate('Place of confirmation')),
627
            'INDI:CONL'                  => new LdsConfirmation(I18N::translate('LDS confirmation')),
628
            'INDI:CONL:DATE'             => new DateLdsOrd(I18N::translate('Date of LDS confirmation')),
629
            'INDI:CONL:PLAC'             => new PlaceLivingOrdinance(I18N::translate('Place of LDS confirmation')),
630
            'INDI:CONL:STAT'             => new LdsBaptismDateStatus(I18N::translate('Status')),
631
            'INDI:CONL:STAT:DATE'        => new DateValueExact(I18N::translate('Status change date')),
632
            'INDI:CONL:TEMP'             => new TempleCode(I18N::translate('Temple')),
633
            'INDI:CREM'                  => new Cremation(I18N::translate('Cremation')),
634
            'INDI:CREM:DATE'             => new DateValue(I18N::translate('Date of cremation')),
635
            'INDI:CREM:PLAC'             => new PlaceName(I18N::translate('Place of cremation')),
636
            'INDI:DEAT'                  => new Death(I18N::translate('Death')),
637
            'INDI:DEAT:CAUS'             => new CauseOfEvent(I18N::translate('Cause of death')),
638
            'INDI:DEAT:DATE'             => new DateValue(I18N::translate('Date of death')),
639
            'INDI:DEAT:PLAC'             => new PlaceName(I18N::translate('Place of death')),
640
            'INDI:DESI'                  => new XrefSubmitter(I18N::translate('Descendants interest')),
641
            'INDI:DSCR'                  => new PhysicalDescription(I18N::translate('Description')),
642
            'INDI:EDUC'                  => new ScholasticAchievement(I18N::translate('Education')),
643
            'INDI:EDUC:AGNC'             => new ResponsibleAgency(I18N::translate('School or college')),
644
            'INDI:EMIG'                  => new Emigration(I18N::translate('Emigration')),
645
            'INDI:EMIG:DATE'             => new DateValue(I18N::translate('Date of emigration')),
646
            'INDI:EMIG:PLAC'             => new PlaceName(I18N::translate('Place of emigration')),
647
            'INDI:ENDL'                  => new LdsEndowment(I18N::translate('LDS endowment')),
648
            'INDI:ENDL:DATE'             => new DateLdsOrd(I18N::translate('Date of LDS endowment')),
649
            'INDI:ENDL:PLAC'             => new PlaceLivingOrdinance(I18N::translate('Place of LDS endowment')),
650
            'INDI:ENDL:STAT'             => new LdsEndowmentDateStatus(I18N::translate('Status')),
651
            'INDI:ENDL:STAT:DATE'        => new DateValueExact(I18N::translate('Status change date')),
652
            'INDI:ENDL:TEMP'             => new TempleCode(I18N::translate('Temple')),
653
            'INDI:EVEN'                  => new IndividualEvent(I18N::translate('Event')),
654
            'INDI:EVEN:DATE'             => new DateValue(I18N::translate('Date of event')),
655
            'INDI:EVEN:PLAC'             => new PlaceName(I18N::translate('Place of event')),
656
            'INDI:EVEN:TYPE'             => new EventOrFactClassification(I18N::translate('Type of event')),
657
            'INDI:FACT'                  => new IndividualFact(I18N::translate('Fact')),
658
            'INDI:FACT:TYPE'             => new EventOrFactClassification(I18N::translate('Type of fact')),
659
            'INDI:FAMC'                  => new XrefFamily(I18N::translate('Family as a child'), ['NOTE' => '0:1', 'PEDI' => '0:1', 'STAT' => '0:1']),
660
            'INDI:FAMC:PEDI'             => new PedigreeLinkageType(I18N::translate('Relationship to parents')),
661
            'INDI:FAMC:STAT'             => new ChildLinkageStatus(I18N::translate('Status')),
662
            'INDI:FAMS'                  => new XrefFamily(I18N::translate('Family as a spouse')),
663
            'INDI:FCOM'                  => new FirstCommunion(I18N::translate('First communion')),
664
            'INDI:FCOM:DATE'             => new DateValue(I18N::translate('Date of first communion')),
665
            'INDI:FCOM:PLAC'             => new PlaceName(I18N::translate('Place of first communion')),
666
            'INDI:GRAD'                  => new Graduation(I18N::translate('Graduation')),
667
            'INDI:GRAD:AGNC'             => new ResponsibleAgency(I18N::translate('School or college')),
668
            'INDI:IDNO'                  => new NationalIdNumber(I18N::translate('Identification number')),
669
            'INDI:IDNO:TYPE'             => new EventOrFactClassification(I18N::translate('Type of identification number')),
670
            'INDI:IMMI'                  => new Immigration(I18N::translate('Immigration')),
671
            'INDI:IMMI:DATE'             => new DateValue(I18N::translate('Date of immigration')),
672
            'INDI:IMMI:PLAC'             => new PlaceName(I18N::translate('Place of immigration')),
673
            'INDI:NAME'                  => new NamePersonal(I18N::translate('Name')),
674
            'INDI:NAME:*:SOUR'           => new XrefSource(I18N::translate('Source citation')),
675
            'INDI:NAME:*:SOUR:DATA'      => new SourceData(I18N::translate('Data')),
676
            'INDI:NAME:*:SOUR:DATA:DATE' => new DateValue(I18N::translate('Date of entry in original source')),
677
            'INDI:NAME:*:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
678
            'INDI:NAME:*:SOUR:EVEN'      => new EventTypeCitedFrom(I18N::translate('Event')),
679
            'INDI:NAME:*:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
680
            'INDI:NAME:*:SOUR:NOTE'      => new NoteStructure(I18N::translate('Note on source citation')),
681
            'INDI:NAME:*:SOUR:OBJE'      => new XrefMedia(I18N::translate('Media object')),
682
            'INDI:NAME:*:SOUR:PAGE'      => new WhereWithinSource(I18N::translate('Citation details')),
683
            'INDI:NAME:*:SOUR:QUAY'      => new CertaintyAssessment(I18N::translate('Quality of data')),
684
            'INDI:NAME:FONE'             => new NamePhoneticVariation(I18N::translate('Phonetic name')),
685
            'INDI:NAME:FONE:GIVN'        => new NamePieceGiven(I18N::translate('Given names')),
686
            'INDI:NAME:FONE:NICK'        => new NamePieceNickname(I18N::translate('Nickname')),
687
            'INDI:NAME:FONE:NOTE'        => new NoteStructure(I18N::translate('Note on phonetic name')),
688
            'INDI:NAME:FONE:NPFX'        => new NamePiecePrefix(I18N::translate('Name prefix')),
689
            'INDI:NAME:FONE:NSFX'        => new NamePieceSuffix(I18N::translate('Name suffix')),
690
            'INDI:NAME:FONE:SOUR'        => new XrefSource(I18N::translate('Source citation')),
691
            'INDI:NAME:FONE:SPFX'        => new NamePieceSurnamePrefix(I18N::translate('Surname prefix')),
692
            'INDI:NAME:FONE:SURN'        => new NamePieceSurname(I18N::translate('Surname')),
693
            'INDI:NAME:FONE:TYPE'        => new PhoneticType(I18N::translate('Phonetic type')),
694
            'INDI:NAME:GIVN'             => new NamePieceGiven(I18N::translate('Given names')),
695
            'INDI:NAME:NICK'             => new NamePieceNickname(I18N::translate('Nickname')),
696
            'INDI:NAME:NPFX'             => new NamePiecePrefix(I18N::translate('Name prefix')),
697
            'INDI:NAME:NSFX'             => new NamePieceSuffix(I18N::translate('Name suffix')),
698
            'INDI:NAME:ROMN'             => new NameRomanizedVariation(I18N::translate('Romanized name')),
699
            'INDI:NAME:ROMN:GIVN'        => new NamePieceGiven(I18N::translate('Given names')),
700
            'INDI:NAME:ROMN:NICK'        => new NamePieceNickname(I18N::translate('Nickname')),
701
            'INDI:NAME:ROMN:NOTE'        => new NoteStructure(I18N::translate('Note on romanized name')),
702
            'INDI:NAME:ROMN:NPFX'        => new NamePiecePrefix(I18N::translate('Name prefix')),
703
            'INDI:NAME:ROMN:NSFX'        => new NamePieceSuffix(I18N::translate('Name suffix')),
704
            'INDI:NAME:ROMN:SOUR'        => new XrefSource(I18N::translate('Source citation')),
705
            'INDI:NAME:ROMN:SPFX'        => new NamePieceSurnamePrefix(I18N::translate('Surname prefix')),
706
            'INDI:NAME:ROMN:SURN'        => new NamePieceSurname(I18N::translate('Surname')),
707
            'INDI:NAME:ROMN:TYPE'        => new RomanizedType(I18N::translate('Romanized type')),
708
            'INDI:NAME:SPFX'             => new NamePieceSurnamePrefix(I18N::translate('Surname prefix')),
709
            'INDI:NAME:SURN'             => new NamePieceSurname(I18N::translate('Surname')),
710
            'INDI:NAME:TYPE'             => new NameType(I18N::translate('Type of name')),
711
            'INDI:NATI'                  => new NationalOrTribalOrigin(I18N::translate('Nationality')),
712
            'INDI:NATU'                  => new Naturalization(I18N::translate('Naturalization')),
713
            'INDI:NATU:DATE'             => new DateValue(I18N::translate('Date of naturalization')),
714
            'INDI:NATU:PLAC'             => new PlaceName(I18N::translate('Place of naturalization')),
715
            'INDI:NCHI'                  => new CountOfChildren(I18N::translate('Number of children')),
716
            'INDI:NMR'                   => new CountOfMarriages(I18N::translate('Number of marriages')),
717
            'INDI:NOTE'                  => new NoteStructure(I18N::translate('Note')),
718
            'INDI:OBJE'                  => new XrefMedia(I18N::translate('Media object')),
719
            'INDI:OCCU'                  => new Occupation(I18N::translate('Occupation')),
720
            'INDI:OCCU:AGNC'             => new ResponsibleAgency(I18N::translate('Employer')),
721
            'INDI:ORDN'                  => new Ordination(I18N::translate('Ordination')),
722
            'INDI:ORDN:AGNC'             => new ResponsibleAgency(I18N::translate('Religious institution')),
723
            'INDI:ORDN:DATE'             => new DateValue(I18N::translate('Date of ordination')),
724
            'INDI:ORDN:PLAC'             => new PlaceName(I18N::translate('Place of ordination')),
725
            'INDI:PROB'                  => new Probate(I18N::translate('Probate')),
726
            'INDI:PROP'                  => new Possessions(I18N::translate('Property')),
727
            'INDI:REFN'                  => new UserReferenceNumber(I18N::translate('Reference number')),
728
            'INDI:REFN:TYPE'             => new UserReferenceType(I18N::translate('Type of reference number')),
729
            'INDI:RELI'                  => new ReligiousAffiliation(I18N::translate('Religion')),
730
            'INDI:RESI'                  => new Residence(I18N::translate('Residence')),
731
            'INDI:RESI:DATE'             => new DateValue(I18N::translate('Date of residence')),
732
            'INDI:RESI:PLAC'             => new PlaceName(I18N::translate('Place of residence')),
733
            'INDI:RESN'                  => new RestrictionNotice(I18N::translate('Restriction')),
734
            'INDI:RETI'                  => new Retirement(I18N::translate('Retirement')),
735
            'INDI:RETI:AGNC'             => new ResponsibleAgency(I18N::translate('Employer')),
736
            'INDI:RFN'                   => new PermanentRecordFileNumber(I18N::translate('Record file number')),
737
            'INDI:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
738
            'INDI:SEX'                   => new SexValue(I18N::translate('Sex')),
739
            'INDI:SLGC'                  => new LdsChildSealing(I18N::translate('LDS child sealing')),
740
            'INDI:SLGC:DATE'             => new DateLdsOrd(I18N::translate('Date of LDS child sealing')),
741
            'INDI:SLGC:FAMC'             => new XrefFamily(I18N::translate('Parents')),
742
            'INDI:SLGC:PLAC'             => new PlaceLivingOrdinance(I18N::translate('Place of LDS child sealing')),
743
            'INDI:SLGC:STAT'             => new LdsChildSealingDateStatus(I18N::translate('Status')),
744
            'INDI:SLGC:STAT:DATE'        => new DateValueExact(I18N::translate('Status change date')),
745
            'INDI:SLGC:TEMP'             => new TempleCode(I18N::translate('Temple')),
746
            'INDI:SOUR'                  => new XrefSource(I18N::translate('Source citation')),
747
            'INDI:SOUR:DATA'             => new SourceData(I18N::translate('Data')),
748
            'INDI:SOUR:DATA:DATE'        => new DateValue(I18N::translate('Date of entry in original source')),
749
            'INDI:SOUR:DATA:TEXT'        => new TextFromSource(I18N::translate('Text')),
750
            'INDI:SOUR:EVEN'             => new EventTypeCitedFrom(I18N::translate('Event')),
751
            'INDI:SOUR:EVEN:ROLE'        => new RoleInEvent(I18N::translate('Role')),
752
            'INDI:SOUR:NOTE'             => new NoteStructure(I18N::translate('Note on source citation')),
753
            'INDI:SOUR:OBJE'             => new XrefMedia(I18N::translate('Media object')),
754
            'INDI:SOUR:PAGE'             => new WhereWithinSource(I18N::translate('Citation details')),
755
            'INDI:SOUR:QUAY'             => new CertaintyAssessment(I18N::translate('Quality of data')),
756
            'INDI:SSN'                   => new SocialSecurityNumber(I18N::translate('Social security number')),
757
            'INDI:SUBM'                  => new XrefSubmitter(I18N::translate('Submitter')),
758
            'INDI:TITL'                  => new NobilityTypeTitle(I18N::translate('Title')),
759
            'INDI:WILL'                  => new Will(I18N::translate('Will')),
760
            'NOTE'                       => new NoteRecord(I18N::translate('Shared note')),
761
            'NOTE:CHAN'                  => new Change(I18N::translate('Last change')),
762
            'NOTE:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
763
            'NOTE:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
764
            'NOTE:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
765
            'NOTE:CONC'                  => new SubmitterText(I18N::translate('Note')),
766
            'NOTE:CONT'                  => new SubmitterText(I18N::translate('Continuation')),
767
            'NOTE:REFN'                  => new UserReferenceNumber(I18N::translate('Reference number')),
768
            'NOTE:REFN:TYPE'             => new UserReferenceType(I18N::translate('Type of reference number')),
769
            'NOTE:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
770
            'NOTE:SOUR'                  => new XrefSource(I18N::translate('Source citation')),
771
            'NOTE:SOUR:DATA'             => new SourceData(I18N::translate('Data')),
772
            'NOTE:SOUR:DATA:DATE'        => new DateValue(I18N::translate('Date of entry in original source')),
773
            'NOTE:SOUR:DATA:TEXT'        => new TextFromSource(I18N::translate('Text')),
774
            'NOTE:SOUR:EVEN'             => new EventTypeCitedFrom(I18N::translate('Event')),
775
            'NOTE:SOUR:EVEN:ROLE'        => new RoleInEvent(I18N::translate('Role')),
776
            'NOTE:SOUR:NOTE'             => new NoteStructure(I18N::translate('Note on source citation')),
777
            'NOTE:SOUR:OBJE'             => new XrefMedia(I18N::translate('Media object')),
778
            'NOTE:SOUR:PAGE'             => new WhereWithinSource(I18N::translate('Citation details')),
779
            'NOTE:SOUR:QUAY'             => new CertaintyAssessment(I18N::translate('Quality of data')),
780
            'OBJE'                       => new MediaRecord(I18N::translate('Media object')),
781
            'OBJE:BLOB'                  => new CustomElement(I18N::translate('Binary data object')),
782
            'OBJE:CHAN'                  => new Change(I18N::translate('Last change')),
783
            'OBJE:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
784
            'OBJE:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
785
            'OBJE:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
786
            'OBJE:FILE'                  => new MultimediaFileReference(I18N::translate('Filename')),
787
            'OBJE:FILE:FORM'             => new MultimediaFormat(I18N::translate('Format')),
788
            'OBJE:FILE:FORM:TYPE'        => new SourceMediaType(I18N::translate('Media type')),
789
            'OBJE:FILE:TITL'             => new DescriptiveTitle(I18N::translate('Title')),
790
            'OBJE:NOTE'                  => new NoteStructure(I18N::translate('Note')),
791
            'OBJE:REFN'                  => new UserReferenceNumber(I18N::translate('Reference number')),
792
            'OBJE:REFN:TYPE'             => new UserReferenceType(I18N::translate('Type of reference number')),
793
            'OBJE:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
794
            'OBJE:SOUR'                  => new XrefSource(I18N::translate('Source citation')),
795
            'OBJE:SOUR:DATA'             => new SourceData(I18N::translate('Data')),
796
            'OBJE:SOUR:DATA:DATE'        => new DateValue(I18N::translate('Date of entry in original source')),
797
            'OBJE:SOUR:DATA:TEXT'        => new TextFromSource(I18N::translate('Text')),
798
            'OBJE:SOUR:EVEN'             => new EventTypeCitedFrom(I18N::translate('Event')),
799
            'OBJE:SOUR:EVEN:ROLE'        => new RoleInEvent(I18N::translate('Role')),
800
            'OBJE:SOUR:NOTE'             => new NoteStructure(I18N::translate('Note on source citation')),
801
            'OBJE:SOUR:OBJE'             => new XrefMedia(I18N::translate('Media object')),
802
            'OBJE:SOUR:PAGE'             => new WhereWithinSource(I18N::translate('Citation details')),
803
            'OBJE:SOUR:QUAY'             => new CertaintyAssessment(I18N::translate('Quality of data')),
804
            'REPO'                       => new RepositoryRecord(I18N::translate('Repository')),
805
            'REPO:ADDR'                  => new AddressLine(I18N::translate('Address')),
806
            'REPO:ADDR:ADR1'             => new AddressLine1(I18N::translate('Address line 1')),
807
            'REPO:ADDR:ADR2'             => new AddressLine2(I18N::translate('Address line 2')),
808
            'REPO:ADDR:ADR3'             => new AddressLine3(I18N::translate('Address line 3')),
809
            'REPO:ADDR:CITY'             => new AddressCity(I18N::translate('City')),
810
            'REPO:ADDR:CTRY'             => new AddressCountry(I18N::translate('Country')),
811
            'REPO:ADDR:POST'             => new AddressPostalCode(I18N::translate('Postal code')),
812
            'REPO:ADDR:STAE'             => new AddressState(I18N::translate('State')),
813
            'REPO:CHAN'                  => new Change(I18N::translate('Last change')),
814
            'REPO:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
815
            'REPO:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
816
            'REPO:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
817
            'REPO:EMAIL'                 => new AddressEmail(I18N::translate('Email address')),
818
            'REPO:FAX'                   => new AddressFax(I18N::translate('Fax')),
819
            'REPO:NAME'                  => new NameOfRepository(I18N::translateContext('Repository', 'Name')),
820
            'REPO:NOTE'                  => new NoteStructure(I18N::translate('Note')),
821
            'REPO:PHON'                  => new PhoneNumber(I18N::translate('Phone')),
822
            'REPO:REFN'                  => new UserReferenceNumber(I18N::translate('Reference number')),
823
            'REPO:REFN:TYPE'             => new UserReferenceType(I18N::translate('Type of reference number')),
824
            'REPO:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
825
            'REPO:WWW'                   => new AddressWebPage(I18N::translate('URL')),
826
            'SOUR'                       => new SourceRecord(I18N::translate('Source')),
827
            'SOUR:ABBR'                  => new SourceFiledByEntry(I18N::translate('Abbreviation')),
828
            'SOUR:AUTH'                  => new SourceOriginator(I18N::translate('Author')),
829
            'SOUR:CHAN'                  => new Change(I18N::translate('Last change')),
830
            'SOUR:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
831
            'SOUR:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
832
            'SOUR:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
833
            'SOUR:DATA'                  => new EmptyElement(I18N::translate('Data'), ['EVEN' => '0:M', 'AGNC' => '0:1', 'NOTE' => '0:M']),
834
            'SOUR:DATA:AGNC'             => new ResponsibleAgency(I18N::translate('Agency')),
835
            'SOUR:DATA:EVEN'             => new EventsRecorded(I18N::translate('Events')),
836
            'SOUR:DATA:EVEN:DATE'        => new DateValue(I18N::translate('Date range')),
837
            'SOUR:DATA:EVEN:PLAC'        => new SourceJurisdictionPlace(I18N::translate('Place'), []),
838
            'SOUR:DATA:NOTE'             => new NoteStructure(I18N::translate('Note on source data')),
839
            'SOUR:NOTE'                  => new NoteStructure(I18N::translate('Note on source')),
840
            'SOUR:OBJE'                  => new XrefMedia(I18N::translate('Media object')),
841
            'SOUR:PUBL'                  => new SourcePublicationFacts(I18N::translate('Publication')),
842
            'SOUR:REFN'                  => new UserReferenceNumber(I18N::translate('Reference number')),
843
            'SOUR:REFN:TYPE'             => new UserReferenceType(I18N::translate('Type of reference number')),
844
            'SOUR:REPO'                  => new XrefRepository(I18N::translate('Repository')),
845
            'SOUR:REPO:CALN'             => new SourceCallNumber(I18N::translate('Call number')),
846
            'SOUR:REPO:CALN:MEDI'        => new SourceMediaType(I18N::translate('Media type')),
847
            'SOUR:REPO:NOTE'             => new NoteStructure(I18N::translate('Note on repository reference')),
848
            'SOUR:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
849
            'SOUR:TEXT'                  => new TextFromSource(I18N::translate('Text')),
850
            'SOUR:TITL'                  => new DescriptiveTitle(I18N::translate('Title')),
851
            'SUBM'                       => new SubmitterRecord(I18N::translate('Submitter')),
852
            'SUBM:ADDR'                  => new AddressLine(I18N::translate('Address')),
853
            'SUBM:ADDR:ADR1'             => new AddressLine1(I18N::translate('Address line 1')),
854
            'SUBM:ADDR:ADR2'             => new AddressLine2(I18N::translate('Address line 2')),
855
            'SUBM:ADDR:ADR3'             => new AddressLine3(I18N::translate('Address line 3')),
856
            'SUBM:ADDR:CITY'             => new AddressCity(I18N::translate('City')),
857
            'SUBM:ADDR:CTRY'             => new AddressCountry(I18N::translate('Country')),
858
            'SUBM:ADDR:POST'             => new AddressPostalCode(I18N::translate('Postal code')),
859
            'SUBM:ADDR:STAE'             => new AddressState(I18N::translate('State')),
860
            'SUBM:CHAN'                  => new Change(I18N::translate('Last change')),
861
            'SUBM:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
862
            'SUBM:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
863
            'SUBM:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
864
            'SUBM:EMAIL'                 => new AddressEmail(I18N::translate('Email address')),
865
            'SUBM:FAX'                   => new AddressFax(I18N::translate('Fax')),
866
            'SUBM:LANG'                  => new LanguageId(I18N::translate('Language')),
867
            'SUBM:NAME'                  => new SubmitterName(I18N::translate('Name')),
868
            'SUBM:NOTE'                  => new NoteStructure(I18N::translate('Note')),
869
            'SUBM:OBJE'                  => new XrefMedia(I18N::translate('Media object')),
870
            'SUBM:PHON'                  => new PhoneNumber(I18N::translate('Phone')),
871
            'SUBM:RFN'                   => new SubmitterRegisteredRfn(I18N::translate('Record file number')),
872
            'SUBM:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
873
            'SUBM:WWW'                   => new AddressWebPage(I18N::translate('URL')),
874
            'SUBN'                       => new SubmissionRecord(I18N::translate('Submission')),
875
            'SUBN:ANCE'                  => new GenerationsOfAncestors(I18N::translate('Generations of ancestors')),
876
            'SUBN:CHAN'                  => new Change(I18N::translate('Last change')),
877
            'SUBN:CHAN:DATE'             => new ChangeDate(I18N::translate('Date of last change')),
878
            'SUBN:CHAN:DATE:TIME'        => new TimeValueNow(I18N::translate('Time of last change')),
879
            'SUBN:CHAN:NOTE'             => new NoteStructure(I18N::translate('Note on last change')),
880
            'SUBN:DESC'                  => new GenerationsOfDescendants(I18N::translate('Generations of descendants')),
881
            'SUBN:FAMF'                  => new NameOfFamilyFile(I18N::translate('Family file')),
882
            'SUBN:NOTE'                  => new NoteStructure(I18N::translate('Note')),
883
            'SUBN:ORDI'                  => new OrdinanceProcessFlag(I18N::translate('Ordinance')),
884
            'SUBN:RIN'                   => new AutomatedRecordId(I18N::translate('Record ID number')),
885
            'SUBN:SUBM'                  => new XrefSubmitter(I18N::translate('Submitter')),
886
            'SUBN:TEMP'                  => new TempleCode(/* I18N: https://en.wikipedia.org/wiki/Temple_(LDS_Church)*/ I18N::translate('Temple')),
887
            'TRLR'                       => new EmptyElement(I18N::translate('Trailer')),
888
        ];
889
    }
890
891
    /**
892
     * Custom tags for webtrees.
893
     *
894
     * @return array<string,ElementInterface>
895
     */
896
    private function webtreesTags(): array
897
    {
898
        return [
899
            'FAM:CHAN:_WT_USER'           => new WebtreesUser(I18N::translate('Author of last change')),
900
            'FAM:*:_ASSO'                 => new XrefAssociate(I18N::translate('Associate')),
901
            'FAM:*:_ASSO:NOTE'            => new NoteStructure(I18N::translate('Note on association')),
902
            'FAM:*:_ASSO:RELA'            => new RelationIsDescriptor(I18N::translate('Relationship')),
903
            'FAM:*:_ASSO:SOUR'            => new XrefSource(I18N::translate('Source citation')),
904
            'FAM:*:_ASSO:SOUR:DATA'       => new SourceData(I18N::translate('Data')),
905
            'FAM:*:_ASSO:SOUR:DATA:DATE'  => new DateValue(I18N::translate('Date of entry in original source')),
906
            'FAM:*:_ASSO:SOUR:DATA:TEXT'  => new TextFromSource(I18N::translate('Text')),
907
            'FAM:*:_ASSO:SOUR:EVEN'       => new EventTypeCitedFrom(I18N::translate('Event')),
908
            'FAM:*:_ASSO:SOUR:EVEN:ROLE'  => new RoleInEvent(I18N::translate('Role')),
909
            'FAM:*:_ASSO:SOUR:NOTE'       => new NoteStructure(I18N::translate('Note on source citation')),
910
            'FAM:*:_ASSO:SOUR:OBJE'       => new XrefMedia(I18N::translate('Media object')),
911
            'FAM:*:_ASSO:SOUR:PAGE'       => new WhereWithinSource(I18N::translate('Citation details')),
912
            'FAM:*:_ASSO:SOUR:QUAY'       => new CertaintyAssessment(I18N::translate('Quality of data')),
913
            'INDI:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
914
            'INDI:*:_ASSO'                => new XrefAssociate(I18N::translate('Associate')),
915
            'INDI:*:_ASSO:NOTE'           => new NoteStructure(I18N::translate('Note on association')),
916
            'INDI:*:_ASSO:RELA'           => new RelationIsDescriptor(I18N::translate('Relationship')),
917
            'INDI:*:_ASSO:SOUR'           => new XrefSource(I18N::translate('Source citation')),
918
            'INDI:*:_ASSO:SOUR:DATA'      => new SourceData(I18N::translate('Data')),
919
            'INDI:*:_ASSO:SOUR:DATA:DATE' => new DateValue(I18N::translate('Date of entry in original source')),
920
            'INDI:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
921
            'INDI:*:_ASSO:SOUR:EVEN'      => new EventTypeCitedFrom(I18N::translate('Event')),
922
            'INDI:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
923
            'INDI:*:_ASSO:SOUR:NOTE'      => new NoteStructure(I18N::translate('Note on source citation')),
924
            'INDI:*:_ASSO:SOUR:OBJE'      => new XrefMedia(I18N::translate('Media object')),
925
            'INDI:*:_ASSO:SOUR:PAGE'      => new WhereWithinSource(I18N::translate('Citation details')),
926
            'INDI:*:_ASSO:SOUR:QUAY'      => new CertaintyAssessment(I18N::translate('Quality of data')),
927
            'NOTE:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
928
            'NOTE:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
929
            'OBJE:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
930
            'OBJE:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
931
            'REPO:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
932
            'REPO:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
933
            'SOUR:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
934
            'SOUR:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
935
            'SUBM:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
936
            'SUBM:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
937
            '_LOC:CHAN:_WT_USER'          => new WebtreesUser(I18N::translate('Author of last change')),
938
            '_LOC:RESN'                   => new RestrictionNotice(I18N::translate('Restriction')),
939
        ];
940
    }
941
942
    /**
943
     * @return array<string,array<int,array<int,string>>>
944
     */
945
    private function webtreesSubTags(): array
946
    {
947
        return [
948
            'FAM'              => [['_UID', '0:M']],
949
            'FAM:*:SOUR:DATA'  => [['TEXT', '0:1']],
950
            'FAM:ANUL'         => [['_ASSO', '0:M', 'NOTE']],
951
            'FAM:CENS'         => [['_ASSO', '0:M', 'NOTE']],
952
            'FAM:CHAN'         => [['_WT_USER', '0:1']],
953
            'FAM:DIV'          => [['_ASSO', '0:M', 'NOTE']],
954
            'FAM:DIVF'         => [['_ASSO', '0:M', 'NOTE']],
955
            'FAM:ENGA'         => [['_ASSO', '0:M', 'NOTE']],
956
            'FAM:EVEN'         => [['_ASSO', '0:M', 'NOTE']],
957
            'FAM:MARB'         => [['_ASSO', '0:M', 'NOTE']],
958
            'FAM:MARC'         => [['_ASSO', '0:M', 'NOTE']],
959
            'FAM:MARL'         => [['_ASSO', '0:M', 'NOTE']],
960
            'FAM:MARR'         => [['_ASSO', '2:M', 'NOTE']],
961
            'FAM:MARS'         => [['_ASSO', '0:M', 'NOTE']],
962
            'FAM:SLGS'         => [['_ASSO', '0:M', 'NOTE']],
963
            'FAM:SOUR:DATA'    => [['TEXT', '0:1']],
964
            'INDI'             => [['_UID', '0:M']],
965
            'INDI:*:SOUR:DATA' => [['TEXT', '0:1']],
966
            'INDI:ADOP'        => [['_ASSO', '0:M', 'NOTE']],
967
            'INDI:BAPL'        => [['_ASSO', '0:M', 'NOTE']],
968
            'INDI:BAPM'        => [['_ASSO', '2:M', 'NOTE']],
969
            'INDI:BARM'        => [['_ASSO', '0:M', 'NOTE']],
970
            'INDI:BASM'        => [['_ASSO', '0:M', 'NOTE']],
971
            'INDI:BIRT'        => [['_ASSO', '0:M', 'NOTE'], ['FAMC', '0:0']],
972
            'INDI:BURI'        => [['_ASSO', '0:M', 'NOTE']],
973
            'INDI:CENS'        => [['_ASSO', '0:M', 'NOTE']],
974
            'INDI:CHAN'        => [['_WT_USER', '0:1']],
975
            'INDI:CHR'         => [['_ASSO', '2:M', 'NOTE']],
976
            'INDI:CHRA'        => [['_ASSO', '0:M', 'NOTE']],
977
            'INDI:CONF'        => [['_ASSO', '0:M', 'NOTE']],
978
            'INDI:CONL'        => [['_ASSO', '0:M', 'NOTE']],
979
            'INDI:CREM'        => [['_ASSO', '0:M', 'NOTE']],
980
            'INDI:DEAT'        => [['_ASSO', '0:M', 'NOTE']],
981
            'INDI:EDUC'        => [['_ASSO', '0:M', 'NOTE']],
982
            'INDI:EMIG'        => [['_ASSO', '0:M', 'NOTE']],
983
            'INDI:ENDL'        => [['_ASSO', '0:M', 'NOTE']],
984
            'INDI:EVEN'        => [['_ASSO', '0:M', 'NOTE']],
985
            'INDI:GRAD'        => [['_ASSO', '0:M', 'NOTE']],
986
            'INDI:IMMI'        => [['_ASSO', '0:M', 'NOTE']],
987
            'INDI:NATU'        => [['_ASSO', '0:M', 'NOTE']],
988
            'INDI:OCCU'        => [['_ASSO', '0:M', 'NOTE']],
989
            'INDI:ORDN'        => [['_ASSO', '0:M', 'NOTE']],
990
            'INDI:PROB'        => [['_ASSO', '0:M', 'NOTE']],
991
            'INDI:PROP'        => [['_ASSO', '0:M', 'NOTE']],
992
            'INDI:RESI'        => [['_ASSO', '0:M', 'NOTE']],
993
            'INDI:RETI'        => [['_ASSO', '0:M', 'NOTE']],
994
            'INDI:SLGC'        => [['_ASSO', '0:M', 'NOTE']],
995
            'INDI:SOUR:DATA'   => [['TEXT', '0:1']],
996
            'INDI:TITL'        => [['_ASSO', '0:M', 'NOTE']],
997
            'INDI:WILL'        => [['_ASSO', '0:M', 'NOTE']],
998
            'NOTE'             => [['RESN', '0:1', 'CHAN']],
999
            'NOTE:CHAN'        => [['_WT_USER', '0:1']],
1000
            'NOTE:SOUR:DATA'   => [['TEXT', '0:1']],
1001
            'OBJE'             => [['RESN', '0:1', 'CHAN'], ['_UID', '0:M']],
1002
            'OBJE:CHAN'        => [['_WT_USER', '0:1']],
1003
            'OBJE:SOUR:DATA'   => [['TEXT', '0:1']],
1004
            'REPO'             => [['RESN', '0:1', 'CHAN'], ['_UID', '0:M']],
1005
            'REPO:CHAN'        => [['_WT_USER', '0:1']],
1006
            'SOUR'             => [['RESN', '0:1', 'CHAN'], ['_UID', '0:M']],
1007
            'SOUR:CHAN'        => [['_WT_USER', '0:1']],
1008
            'SUBM'             => [['RESN', '0:1', 'CHAN']],
1009
            'SUBM:CHAN'        => [['_WT_USER', '0:1']],
1010
        ];
1011
    }
1012
1013
    /**
1014
     * @return array<string,array<int,array<int,string>>>
1015
     */
1016
    private function customSubTags(): array
1017
    {
1018
        $custom_family_tags     = array_filter(explode(',', Site::getPreference('CUSTOM_FAMILY_TAGS')));
1019
        $custom_individual_tags = array_filter(explode(',', Site::getPreference('CUSTOM_INDIVIDUAL_TAGS')));
1020
1021
        $subtags = [
1022
            'FAM'  => array_map(static fn (string $tag): array => [$tag, '0:M'], $custom_family_tags),
1023
            'INDI' => array_map(static fn (string $tag): array => [$tag, '0:M'], $custom_individual_tags),
1024
        ];
1025
1026
        // GEDCOM 7 tags
1027
        if (Site::getPreference('CUSTOM_FAM_FACT') === '1') {
1028
            $subtags['FAM'][] = ['FACT', '0:M'];
1029
        }
1030
        if (Site::getPreference('CUSTOM_FAM_NCHI') === '1') {
1031
            $subtags['FAM:NCHI'] = [
1032
                ['TYPE', '0:1:?'],
1033
                ['DATE', '0:1'],
1034
                ['PLAC', '0:1:?'],
1035
                ['ADDR', '0:1:?'],
1036
                ['EMAIL', '0:1:?'],
1037
                ['WWW', '0:1:?'],
1038
                ['PHON', '0:1:?'],
1039
                ['FAX', '0:1:?'],
1040
                ['CAUS', '0:1:?'],
1041
                ['AGNC', '0:1:?'],
1042
                ['RELI', '0:1:?'],
1043
                ['NOTE', '0:M'],
1044
                ['OBJE', '0:M'],
1045
                ['SOUR', '0:M'],
1046
                ['RESN', '0:1'],
1047
            ];
1048
        }
1049
1050
        if (Site::getPreference('CUSTOM_TIME_TAGS') === '1') {
1051
            $subtags['INDI:BIRT:DATE'][] = ['TIME', '0:1'];
1052
            $subtags['INDI:DEAT:DATE'][] = ['TIME', '0:1'];
1053
        }
1054
1055
        if (Site::getPreference('CUSTOM_GEDCOM_L_TAGS') === '1') {
1056
            $subtags['FAM'][]               = ['_ASSO', '0:M'];
1057
            $subtags['FAM'][]               = ['_STAT', '0:1'];
1058
            $subtags['FAM'][]               = ['_UID', '0:M'];
1059
            $subtags['FAM:*:ADDR']          = [['_NAME', '0:1:?', 'ADR1']];
1060
            $subtags['FAM:*:PLAC']          = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1061
            $subtags['FAM:ENGA:PLAC']       = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1062
            $subtags['FAM:MARB:PLAC']       = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1063
            $subtags['FAM:MARR']            = [['_WITN', '0:1']];
1064
            $subtags['FAM:MARR:PLAC']       = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1065
            $subtags['FAM:SLGS:PLAC']       = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1066
            $subtags['INDI'][]              = ['_UID', '0:M'];
1067
            $subtags['INDI:*:ADDR']         = [['_NAME', '0:1:?', 'ADR1']];
1068
            $subtags['INDI:*:PLAC']         = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1069
            $subtags['INDI:ADOP:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1070
            $subtags['INDI:BAPL:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1071
            $subtags['INDI:BAPM']           = [['_GODP', '0:1'], ['_WITN', '0:1']];
1072
            $subtags['INDI:BAPM:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1073
            $subtags['INDI:BARM:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1074
            $subtags['INDI:BASM:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1075
            $subtags['INDI:BIRT:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1076
            $subtags['INDI:BLES:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1077
            $subtags['INDI:BURI:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1078
            $subtags['INDI:CENS:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1079
            $subtags['INDI:CHR']            = [['_GODP', '0:1'], ['_WITN', '0:1']];
1080
            $subtags['INDI:CHR:PLAC']       = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1081
            $subtags['INDI:CHRA:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1082
            $subtags['INDI:CONF:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1083
            $subtags['INDI:CONL:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1084
            $subtags['INDI:CREM:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1085
            $subtags['INDI:DEAT:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1086
            $subtags['INDI:EMIG:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1087
            $subtags['INDI:ENDL:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1088
            $subtags['INDI:EVEN:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1089
            $subtags['INDI:FCOM:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1090
            $subtags['INDI:IMMI:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1091
            $subtags['INDI:NAME']           = [['_RUFNAME', '0:1', 'SPFX']];
1092
            $subtags['INDI:NATU:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1093
            $subtags['INDI:ORDN:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1094
            $subtags['INDI:RESI:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1095
            $subtags['INDI:SLGC:PLAC']      = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1096
            $subtags['NOTE']                = [['_UID', '0:M']];
1097
            $subtags['OBJE']                = [['_PRIM', '0:1:?'], ['_UID', '0:M']];
1098
            $subtags['REPO']                = [['_UID', '0:M']];
1099
            $subtags['REPO:ADDR']           = [['_NAME', '0:1', 'ADR1']];
1100
            $subtags['SOUR']                = [['_UID', '0:M']];
1101
            $subtags['SOUR:DATA:EVEN:PLAC'] = [['_POST', '0:1'], ['_MAIDENHEAD', '0:1:?'], ['_LOC', '0:1'], ['_GOV', '0:1']];
1102
            $subtags['SUBM']                = [['_UID', '0:M']];
1103
            $subtags['SUBM:ADDR']           = [['_NAME', '0:1', 'ADR1']];
1104
        }
1105
1106
        return $subtags;
1107
    }
1108
1109
    /**
1110
     * @param ElementFactoryInterface $element_factory
1111
     * @param bool                    $include_custom_tags
1112
     *
1113
     * @return void
1114
     */
1115
    public function registerTags(ElementFactoryInterface $element_factory, bool $include_custom_tags): void
1116
    {
1117
        // Standard GEDCOM.
1118
        $element_factory->registerTags($this->gedcom551Tags());
1119
1120
        // webtrees extensions.
1121
        $element_factory->registerTags($this->webtreesTags());
1122
1123
        if ($include_custom_tags) {
1124
            // webtrees extensions.
1125
            $element_factory->registerSubTags($this->webtreesSubTags());
1126
1127
            $custom_tags = [
1128
                new Aldfaer(),
1129
                new Ancestry(),
1130
                new BrothersKeeper(),
1131
                new FamilySearch(),
1132
                new FamilyTreeBuilder(),
1133
                new FamilyTreeMaker(),
1134
                new Gedcom7(),
1135
                new GedcomL(),
1136
                new Geneatique(),
1137
                new GenPlusWin(),
1138
                new Heredis(),
1139
                new Legacy(),
1140
                new MyHeritage(),
1141
                new PersonalAncestralFile(),
1142
                new PhpGedView(),
1143
                new ProGen(),
1144
                new Reunion(),
1145
                new RootsMagic(),
1146
                new TheMasterGenealogist(),
1147
                new TheNextGeneration(),
1148
            ];
1149
1150
            foreach ($custom_tags as $custom_tag) {
1151
                $element_factory->registerTags($custom_tag->tags());
1152
            }
1153
1154
            // Creating tags from all the above are grouped into one place
1155
            $element_factory->registerSubTags($this->customSubTags());
1156
        }
1157
    }
1158
}
1159