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