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