strom-research 1.22.0-beta.8 → 1.22.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.cs.md +21 -6
  2. package/CHANGELOG.de.md +25 -6
  3. package/CHANGELOG.md +23 -8
  4. package/assets/lang/cs.json +60 -0
  5. package/assets/lang/de.json +60 -0
  6. package/assets/method/recording.md +4 -1
  7. package/assets/terms/participants.json +39 -0
  8. package/dist/brief/brief.js +42 -5
  9. package/dist/cli/menu-settings.js +17 -15
  10. package/dist/cli/menu.js +6 -7
  11. package/dist/cli/ui.js +36 -0
  12. package/dist/commands/analysis.js +4 -1
  13. package/dist/commands/checks.js +7 -1
  14. package/dist/commands/fixchecks.js +42 -13
  15. package/dist/commands/fixes.js +167 -18
  16. package/dist/commands/guide.js +12 -0
  17. package/dist/commands/index.js +2 -0
  18. package/dist/commands/intake.js +3 -1
  19. package/dist/commands/output.js +5 -2
  20. package/dist/commands/people.js +22 -8
  21. package/dist/commands/quality.js +111 -0
  22. package/dist/commands/session.js +11 -1
  23. package/dist/commands/surname.js +116 -0
  24. package/dist/commands/sync.js +8 -0
  25. package/dist/core/actions.js +26 -10
  26. package/dist/core/afterdeath.js +139 -0
  27. package/dist/core/changelog.js +30 -0
  28. package/dist/core/check.js +25 -0
  29. package/dist/core/document.js +10 -6
  30. package/dist/core/duplicates.js +121 -0
  31. package/dist/core/gdate.js +39 -0
  32. package/dist/core/indexfirst.js +3 -2
  33. package/dist/core/integrity.js +12 -8
  34. package/dist/core/live.js +131 -5
  35. package/dist/core/participantlinks.js +69 -0
  36. package/dist/core/participants.js +373 -0
  37. package/dist/core/people.js +11 -2
  38. package/dist/core/phrases.js +24 -0
  39. package/dist/core/quality.js +195 -0
  40. package/dist/core/stromapp.js +22 -0
  41. package/dist/core/surnames.js +828 -0
  42. package/dist/core/sync.js +46 -1
  43. package/dist/core/tree.js +50 -1
  44. package/dist/gedcom/export.js +11 -0
  45. package/dist/gedcom/parse.js +7 -2
  46. package/package.json +1 -1
package/CHANGELOG.cs.md CHANGED
@@ -7,7 +7,7 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
7
7
 
8
8
  ## 1.22.0
9
9
 
10
- *Připravuje se: bety do 1.22.0-beta.8. Oddíl bude úplný s vydáním.*
10
+ *Připravuje se: bety do 1.22.0-beta.9. Oddíl bude úplný s vydáním.*
11
11
 
12
12
  - **Jméno jednou v tom, co čeká.** Úkol k materiálu poslanému z aplikace Strom jmenoval ve widgetu a na stránce
13
13
  telefonu každého člověka dvakrát (podruhé v hranatých závorkách) a jeho text bez jmen nechal prázdné závorky; menu,
@@ -23,11 +23,11 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
23
23
  okamžitý, doctor ukazuje průběh a řekne, když výzkum leží v cloudu nebo na síťovém disku; `strom doctor --timing`
24
24
  ukáže, co trvalo dlouho. Na dlouhém výzkumu (stovky lidí, desítky tisíc skenů) ukáže menu hned, co čeká: počet se
25
25
  drží vedle výzkumu, dokud se nezmění nic, z čeho vychází, a pak se spočítá znovu za zlomek sekundy.
26
- - **Archiv, který neodpovídá, zastaví smyčku agenta.** `strom fetch` řekne, kdy to zkusit znovu a co dělat mezitím;
27
- stejný dotaz ve stejném sezení skončí hned (exit 8) a nic neodešle. Úkol, jehož archiv má pauzu, počká mimo frontu
28
- (plán ho ukáže odložený do té doby) a `strom run --loop` do té doby počká, místo aby pouštěl sezení bez práce.
29
- - **Čtenáři nečekají na umlklou službu.** Čtenář, jehož agent po opakovaném pokusu služby zmlkne, se jednou obnoví
30
- tam, kde skončil; když mlčí dál, hned skončí. Takový výpadek už nezmenšuje dávky čtení.
26
+ - **Archiv, který neodpovídá, zastaví smyčku agenta; čtenáři nečekají na umlklou službu.** `strom fetch` řekne, kdy to
27
+ zkusit znovu a co dělat mezitím; stejný dotaz ve stejném sezení skončí hned (exit 8) a nic neodešle. Úkol, jehož
28
+ archiv má pauzu, počká mimo frontu (plán ho ukáže odložený do té doby) a `strom run --loop` do té doby počká, místo
29
+ aby pouštěl sezení bez práce. Čtenář, jehož agent po opakovaném pokusu služby zmlkne, se jednou obnoví tam, kde
30
+ skončil; když mlčí dál, hned skončí. Takový výpadek už nezmenšuje dávky čtení.
31
31
  - **Brána týdne předplatného pustí víc sezení za sebou.** Když jednou čeká na náskok, pustí znovu až se dvěma body navíc
32
32
  (při 15: 17), místo jednoho sezení po každém čekání.
33
33
  - **Co předek zažil.** Události v místech stromu – dějiny kraje ze stránek, které strom sám stáhl a ověřil, místní
@@ -58,6 +58,21 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
58
58
  nikdy jako to, co dělal (u páru jen z let, kdy žili oba); nic skrytého ani smazaného. Schválené vyprávění zůstane, jak je – dějiny přijdou až s jeho
59
59
  další verzí, která čeká na schválení. Vypnout se dá v nastavení nebo v aplikaci Strom
60
60
  (`strom config set stories.history no`).
61
+ - **Tvary jednoho příjmení.** `strom surname suggest` navrhne, které zapsané tvary příjmení patří jedné rodině: táž
62
+ osoba zapsaná v záznamech různě (s citacemi), nebo táž hláska zapsaná jinak na stejném místě ve stejných letech –
63
+ nikdy rodina manžela a otce téže ženy. O nich rozhoduje jen člověk, v terminálu
64
+ (`strom surname accept|reject|group`); agent jen navrhuje. Tvary jedné rodiny rozšíří hledání záznamů i lidí, kteří
65
+ mohou být jedna osoba. `strom person compare` řekne o dvou lidech, zda jsou jedna osoba, dvě, nebo nejisté, a proč
66
+ (už sloučení, jiné pohlaví, roky daleko od sebe, život skončený dřív, než druhý začal, jiní rodiče, jeden zápis pro
67
+ oba…). Nález označený jako není chyba tak zůstane: `strom quality dismiss|undo|list`, jen rozhodnutí člověka – dva
68
+ lidé takto označení se už nikdy nenavrhnou jako jedna osoba. `strom check` upozorní na událost nebo sňatek datovaný
69
+ jistě po smrti (přibližné datum nikdy) a plán výzkumu založí úkol to prověřit, sám hotový, jakmile se datum opraví
70
+ nebo se nález označí jako není chyba. Účastník události (kmotr, svědek) je jméno a co o něm záznam říká, patří do
71
+ závorky na konci jako poznámka: `--with "godparent:Jan Lhoták (sedlák, Lhota č. 12)"`; jméno delší než čtyři slova
72
+ nebo s číslem se odmítne s tím, co dát do závorky, kněz zapsaný jako svědek se má zapsat jako oddávající nebo křtící
73
+ (`officiant`) a účastník, který může být někdo z rodiny okolo zápisu, se navrhne svým číslem, nikdy se nenapojí sám.
74
+ Takto zapsaná dřívější jména najde `strom check` a rozdělí `strom fixes show|run participant-names` – zdarma, podle
75
+ pravidel, i v archivu, vše jedním krokem, vrátí `strom fixes undo participant-names`; rozhoduje jen člověk.
61
76
 
62
77
  ## 1.21.0
63
78
 
package/CHANGELOG.de.md CHANGED
@@ -7,7 +7,7 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
7
7
 
8
8
  ## 1.22.0
9
9
 
10
- *In Vorbereitung: die Betas bis 1.22.0-beta.8. Der Abschnitt ist mit dem Release vollständig.*
10
+ *In Vorbereitung: die Betas bis 1.22.0-beta.9. Der Abschnitt ist mit dem Release vollständig.*
11
11
 
12
12
  - **Jeder Name einmal in dem, was wartet.** Eine Aufgabe zu Material, das aus der Strom-App gesendet wurde, nannte im
13
13
  Widget und auf der Seite des Telefons jede Person zweimal (das zweite Mal in eckigen Klammern), und ihr Text ohne
@@ -26,11 +26,11 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
26
26
  oder auf einem Netzlaufwerk liegt; `strom doctor --timing` zeigt, was lange dauerte. Bei einer langen Forschung
27
27
  (viele hundert Personen, Zehntausende Scans) zeigt das Menü sofort, was wartet: Die Zahl wird neben der Forschung
28
28
  gehalten, bis sich etwas ändert, worauf sie beruht, und dann in einem Bruchteil einer Sekunde neu gezählt.
29
- - **Ein Archiv, das nicht antwortet, beendet die Schleife des Agenten.** `strom fetch` sagt, wann es wieder zu
30
- versuchen ist und was bis dahin zu tun ist; dieselbe Anfrage in derselben Sitzung endet sofort (Exit 8) und sendet
31
- nichts. Eine Aufgabe, deren Archiv pausiert, wartet bis dahin außerhalb der Warteschlange (der Plan zeigt sie bis zu
32
- dieser Zeit zurückgestellt), und `strom run --loop` wartet bis dahin, statt Sitzungen ohne Arbeit zu starten.
33
- - **Leser warten nicht mehr auf einen verstummten Dienst.** Ein Leser, dessen Agent nach einem erneuten Versuch seines
29
+ - **Ein Archiv, das nicht antwortet, beendet die Schleife des Agenten; Leser warten nicht mehr auf einen verstummten
30
+ Dienst.** `strom fetch` sagt, wann es wieder zu versuchen ist und was bis dahin zu tun ist; dieselbe Anfrage in
31
+ derselben Sitzung endet sofort (Exit 8) und sendet nichts. Eine Aufgabe, deren Archiv pausiert, wartet bis dahin
32
+ außerhalb der Warteschlange (der Plan zeigt sie bis zu dieser Zeit zurückgestellt), und `strom run --loop` wartet
33
+ bis dahin, statt Sitzungen ohne Arbeit zu starten. Ein Leser, dessen Agent nach einem erneuten Versuch seines
34
34
  Dienstes verstummt, wird einmal dort fortgesetzt, wo er aufhörte; bleibt er still, endet er sofort. Ein solcher
35
35
  Ausfall verkleinert die Lesestapel nicht mehr.
36
36
  - **Das Tor der Abo-Woche lässt mehrere Sitzungen hintereinander laufen.** Wartet es einmal auf Vorsprung, lässt es
@@ -69,6 +69,25 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
69
69
  beide lebten); nichts Ausgeblendetes oder Gelöschtes. Eine
70
70
  freigegebene Erzählung bleibt, wie sie ist – die Geschichte kommt erst mit ihrer nächsten Fassung, die auf Freigabe
71
71
  wartet. Abschaltbar in den Einstellungen oder in der Strom-App (`strom config set stories.history no`).
72
+ - **Die Formen eines Familiennamens.** `strom surname suggest` schlägt vor, welche geschriebenen Formen eines
73
+ Familiennamens zu einer Familie gehören: dieselbe Person in den Einträgen verschieden geschrieben (mit den Belegen),
74
+ oder derselbe Laut anders geschrieben am selben Ort in denselben Jahren – nie die Familien von Ehemann und Vater
75
+ einer Frau. Entscheiden kann nur ein Mensch, im Terminal (`strom surname accept|reject|group`); ein Agent schlägt
76
+ nur vor. Die Formen einer Familie erweitern die Suche nach Einträgen und nach Personen, die eine sein können.
77
+ `strom person compare` sagt von zwei Personen, ob sie eine sind, zwei oder unsicher, und warum (schon
78
+ zusammengeführt, ein anderes Geschlecht, Jahre weit auseinander, ein Leben, das endete, bevor das andere begann,
79
+ andere Eltern, ein Eintrag für beide…). Ein als kein Fehler markierter Befund bleibt es:
80
+ `strom quality dismiss|undo|list`, allein die Entscheidung eines Menschen – zwei so markierte Personen werden nie
81
+ wieder als eine vorgeschlagen. `strom check` warnt vor einem Ereignis oder einer Heirat, die sicher nach dem Tod
82
+ einer Person datiert ist (ein ungefähres Datum nie), und der Plan einer Forschung legt eine Aufgabe an, das zu
83
+ prüfen – von selbst erledigt, sobald das Datum berichtigt oder der Befund als kein Fehler markiert ist. Ein
84
+ Beteiligter eines Ereignisses (ein Pate, ein Zeuge) ist ein Name, und was der Eintrag über ihn sagt, steht als
85
+ Notiz in Klammern am Ende: `--with "godparent:Jan Lhoták (sedlák, Lhota č. 12)"`; ein Name aus mehr als vier
86
+ Wörtern oder mit einer Zahl wird abgelehnt, mit dem, was in die Klammern gehört, ein als Zeuge genannter Geistlicher
87
+ wird als Amtsträger (`officiant`) erfasst, und ein Beteiligter, der jemand aus der Familie um den Eintrag sein kann,
88
+ wird mit seiner Nummer vorgeschlagen, nie von selbst verknüpft. So früher geschriebene Namen findet `strom check`,
89
+ und `strom fixes show|run participant-names` trennt sie – kostenlos, nach Regeln, auch in einem Archiv, alles in
90
+ einem Schritt, zurück mit `strom fixes undo participant-names`; entscheiden kann nur ein Mensch.
72
91
 
73
92
  ## 1.21.0
74
93
 
package/CHANGELOG.md CHANGED
@@ -7,7 +7,7 @@ The changes of each version in short. The full notes and the install lines are o
7
7
 
8
8
  ## 1.22.0
9
9
 
10
- *In preparation: the betas up to 1.22.0-beta.8. The section is complete with the release.*
10
+ *In preparation: the betas up to 1.22.0-beta.9. The section is complete with the release.*
11
11
 
12
12
  - **Each name once in what waits.** A task for material sent from the Strom app named each person twice in the
13
13
  widget and on the phone's page (the second time in brackets), and its text without names kept empty brackets; the
@@ -24,13 +24,12 @@ The changes of each version in short. The full notes and the install lines are o
24
24
  network drive; `strom doctor --timing` tells what took long. On a long research (hundreds of people, tens of
25
25
  thousands of scans) the menu shows what waits at once: its count is kept beside the research until something it
26
26
  stands on changes, and counted again in a fraction of a second.
27
- - **An archive that stops answering stops the agent's loop.** `strom fetch` says when to try again and what to do
28
- meanwhile; asked again in the same session it ends at once (exit 8) and sends nothing. A task whose archive is left
29
- alone waits out of the queue until then (the plan shows it put off until that time), and `strom run --loop` waits
30
- for that time instead of starting sessions with nothing to do.
31
- - **Readers no longer wait out a silent service.** A reader whose agent goes silent after a retry of its service is
32
- resumed once where it stopped; still silent, it ends at once. Such a failure no longer makes the reading batches
33
- smaller.
27
+ - **An archive that stops answering stops the agent's loop; readers no longer wait out a silent service.** `strom
28
+ fetch` says when to try again and what to do meanwhile; asked again in the same session it ends at once (exit 8) and
29
+ sends nothing. A task whose archive is left alone waits out of the queue until then (the plan shows it put off until
30
+ that time), and `strom run --loop` waits for that time instead of starting sessions with nothing to do. A reader
31
+ whose agent goes silent after a retry of its service is resumed once where it stopped; still silent, it ends at
32
+ once. Such a failure no longer makes the reading batches smaller.
34
33
  - **The gate of the subscription's week lets several sessions run in a row.** Once it waits for points in hand, it goes again only
35
34
  with two more (at 15: 17), instead of one session after every wait.
36
35
  - **What the ancestors lived through.** The events at the places of the tree — the history of a region from pages
@@ -67,6 +66,22 @@ The changes of each version in short. The full notes and the install lines are o
67
66
  deleted. An approved story stays as it is —
68
67
  the history comes only with its next version, waiting for approval. Off in the settings or in the Strom app
69
68
  (`strom config set stories.history no`).
69
+ - **The forms of one surname.** `strom surname suggest` suggests which written forms of a surname are one family's: one
70
+ person written so in the records (with the citations), or one sound spelled otherwise at one place in the same years
71
+ — never the families of a woman's husband and her father. Only a person decides, in the terminal
72
+ (`strom surname accept|reject|group`); an agent only suggests. The forms of one family widen the search of records
73
+ and of people who may be one. `strom person compare` says of two people whether they are one, two or unsure, and why
74
+ (merged already, a different sex, years far apart, a life that ended before the other began, other parents, one
75
+ entry for both…). A finding marked no error stays so: `strom quality dismiss|undo|list`, the person's alone — two
76
+ people marked so are never suggested as one again. `strom check` warns of an event or a marriage dated certainly
77
+ after a person's death (an approximate date never), and the plan of a research raises a task to look into it, done
78
+ by itself once the date is corrected or the finding marked no error. A participant of an event (a godparent, a
79
+ witness) is a name, and what the record says of them goes in closing parentheses as the note:
80
+ `--with "godparent:Jan Lhoták (sedlák, Lhota č. 12)"`; a name of more than four words or with a number is refused
81
+ with what to put in the parentheses, a priest named as a witness is told to be the officiant, and a participant
82
+ who may be one of the family around the entry is suggested by their number, never linked by itself. Names written
83
+ so before are found by `strom check` and set apart by `strom fixes show|run participant-names` — free, by rules,
84
+ in an archive too, all in one step, put back with `strom fixes undo participant-names`; only a person decides.
70
85
 
71
86
  ## 1.21.0
72
87
 
@@ -733,6 +733,30 @@
733
733
  "history.why.archive": "Dokud je výzkum vedený jako archiv, nic se nehledá.",
734
734
  "history.why.held": "Události regionu {region} hledá jiný výzkum na tomto počítači.",
735
735
  "history.why.nobody": "V těch letech tu nikdo ze stromu nežil.",
736
+ "surname.suggest.person": "Tatáž osoba je v záznamech zapsaná jako {forms}.",
737
+ "surname.suggest.phonetic": "{forms}: tatáž hláska zapsaná jinak ({keys}), na stejném místě ve stejných letech.",
738
+ "surname.suggest.spelling": "{forms}: jiný pravopis ({keys}) na stejném místě ve stejných letech.",
739
+ "surname.key.double": "zdvojené písmeno",
740
+ "surname.key.diacritics": "diakritika",
741
+ "log.surname.group": "Tvary příjmení jedné rodiny: {forms}",
742
+ "log.surname.ungroup": "Tvary příjmení rozděleny: {forms}",
743
+ "log.surname.reject": "Tvary příjmení nejsou jedné rodiny: {forms}",
744
+ "log.surname.unreject": "Tvary příjmení se smějí znovu navrhnout: {forms}",
745
+ "log.surname.undo": "Tvary příjmení vráceny, jak byly",
746
+ "log.participant.fix": "Jména kmotrů a svědků oddělena od poznámek: {n}",
747
+ "log.participant.unfix": "Jména kmotrů a svědků vrácena, jak byla: {n}",
748
+ "afterdeath.what": "{name}: prověřit událost datovanou po smrti ({year})",
749
+ "afterdeath.wedding.what": "{name}: prověřit sňatek datovaný po smrti ({year})",
750
+ "afterdeath.why": "Smrt: {death}. Datováno po ní: {event}. Jedno z dat je špatně přečtené, nebo jde o záznamy dvou lidí.",
751
+ "afterdeath.done": "oba záznamy přečtené znovu: špatně přečtené datum opravené i se záznamem, dva lidé rozlišení, nebo poznámka, proč platí obě data",
752
+ "afterdeath.fact": "{kind} {id} {date}{place} ({cites})",
753
+ "afterdeath.fact.uncited": "{kind} {id} {date}{place} (bez citovaného pramene)",
754
+ "afterdeath.result.dismissed": "Označeno jako není chyba ({event}): není co prověřovat.",
755
+ "afterdeath.result.gone": "{event} už není datováno po smrti: není co prověřovat.",
756
+ "log.quality.two": "Dva lidé, ne jeden: {persons}",
757
+ "log.quality.afterdeath": "{persons}: datováno po smrti ({subject}), není chyba",
758
+ "log.quality.dismiss": "Označeno jako není chyba: {what}",
759
+ "log.quality.undo": "Už neoznačeno jako není chyba: {what}",
736
760
  "history.places.what": "Místa údajů pro dějiny předků ({n}): regiony, kam které patřilo, i s roky",
737
761
  "history.places.why": "události dějin (války, epidemie, sčítání lidu, změny státu) se ukazují u míst údajů podle regionů, kam patřila",
738
762
  "history.places.done": "každé místo přiřazené k regionům i s roky, nebo označené jako nedohledané",
@@ -1315,6 +1339,21 @@
1315
1339
  "ui.sync.fact.part.gone": "{n}. {who}: {kind} {when} — už neuvedeno: {wasRole} {part}",
1316
1340
  "ui.sync.person.cite": "{n}. {who}: pramen z aplikace k osobě — {sources}",
1317
1341
  "ui.sync.family.cite": "{n}. {who}: pramen z aplikace k rodině — {sources}",
1342
+ "ui.sync.surname.group": "{n}. Tvary příjmení jedné rodiny: {forms}",
1343
+ "ui.sync.surname.group.set": "{n}. Tvary příjmení jedné rodiny: {was} → {forms}",
1344
+ "ui.sync.surname.group.remove": "{n}. Tvary příjmení už nejsou jedné rodiny: {was}",
1345
+ "ui.sync.surname.group.kept": "{n}. Tvary příjmení jedné rodiny se liší: v aplikaci {forms}, ve výzkumu {was}",
1346
+ "ui.sync.surname.reject": "{n}. Tvary příjmení odmítnuté jako jedna rodina: {forms}",
1347
+ "ui.sync.surname.reject.set": "{n}. Tvary příjmení odmítnuté jako jedna rodina: {was} → {forms}",
1348
+ "ui.sync.surname.reject.remove": "{n}. Tvary příjmení už nejsou odmítnuté: {was}",
1349
+ "ui.sync.surname.reject.kept": "{n}. Tvary příjmení odmítnuté jako jedna rodina se liší: v aplikaci {forms}, ve výzkumu {was}",
1350
+ "ui.sync.surname.warning": "{n}. Řádek tvarů příjmení nepřečten ({why}): {line}",
1351
+ "ui.sync.surname.why.empty": "prázdný tvar",
1352
+ "ui.sync.surname.why.separator": "tvar se znakem |",
1353
+ "ui.sync.surname.why.damaged": "poškozené znaky — seznam zůstává, jak je",
1354
+ "ui.sync.do.surname": "zapíše se, jak bylo rozhodnuto v aplikaci",
1355
+ "ui.sync.do.surname.kept": "zůstává, jak je ve výzkumu",
1356
+ "ui.sync.do.surname.report": "nic se nemění",
1318
1357
  "ui.sync.do.part": "doplní se k údaji s odkazem na strom z aplikace",
1319
1358
  "ui.sync.do.part.user": "platí úprava z aplikace (verze výzkumu zůstane v historii)",
1320
1359
  "ui.sync.do.person.cite": "bude ho citovat jméno osoby (vodítko, dokud výzkum záznam nepřečte)",
@@ -1667,6 +1706,10 @@
1667
1706
  "ui.check.notdownloaded.hint": "otevřít složku výzkumu ve Finderu a stáhnout ji (nebo složku z iCloud Drive vyjmout), pak znovu zkontrolovat – ne strom repair",
1668
1707
  "ui.check.sharedmissing": "na tomto počítači chybí ve sdílené složce: {n, plural, one {# soubor} few {# soubory} other {# souborů}} (první: {first}) – rodinné materiály a obrázky leží vedle výzkumu, ne v jeho složce",
1669
1708
  "ui.check.sharedmissing.hint": "vrátit ze zálohy celou složku Strom (podsložku shared); kam patří první: {show}",
1709
+ "ui.check.afterdeath": "{person}: {event} {date} datováno po smrti ({death})",
1710
+ "ui.check.afterdeath.wedding": "{person}: sňatek {date} datovaný po smrti ({death})",
1711
+ "ui.check.afterdeath.hint": "oba záznamy přečíst znovu: {show}; není chyba → {dismiss}",
1712
+ "ui.check.participantnames": "{n, plural, one {# jméno kmotra nebo svědka obsahuje} few {# jména kmotrů a svědků obsahují} other {# jmen kmotrů a svědků obsahuje}} víc než jméno",
1670
1713
  "ui.error.tree.not-downloaded": "z iCloudu ještě nestaženo: {n, plural, one {# soubor} few {# soubory} other {# souborů}} výzkumu (první: {first}) – nic se nezapsalo",
1671
1714
  "ui.error.tree.not-downloaded.hint": "otevřít složku výzkumu ve Finderu a soubory stáhnout (nebo složku z iCloud Drive vyjmout), pak spustit znovu – ne strom repair",
1672
1715
  "ui.export.file": "{file} ({for}) — {persons, plural, one {# osoba} few {# osoby} other {# osob}} · {families, plural, one {# rodina} few {# rodiny} other {# rodin}} · {facts, plural, one {# údaj} few {# údaje} other {# údajů}} · {sources, plural, one {# pramen} few {# prameny} other {# pramenů}}{skipped}",
@@ -2931,6 +2974,23 @@
2931
2974
  "ui.fixes.estimate.direct": "Sporná a přímá linie: {part} · {level} · {calls} volání čtenáře · asi {minutes} min",
2932
2975
  "ui.fixes.reader.before": " · četl je už dřív",
2933
2976
  "ui.fixes.reader.other": " · doporučeno: jiný, než který četl dřív",
2977
+ "ui.fixes.title.archive": "Opravy: co v tomto výzkumu zanechaly dřívější verze stromu",
2978
+ "ui.fixes.participant-names.name": "Jména kmotrů a svědků s tím, co zápis uvádí u nich",
2979
+ "ui.fixes.participant-names.what": "Před stromem {since} mohlo jméno kmotra, svědka nebo jiného účastníka obsahovat i to, co zápis uvádí u něj – stav, povolání, obec, číslo domu. Od té doby stojí jméno samo a zbytek je jeho poznámka: hledání, duplicity i aplikace Strom čtou jen jméno.",
2980
+ "ui.fixes.participant-names.found": "Nalezeno: {found, plural, one {# účastník} few {# účastníci} other {# účastníků}}, jejichž jméno obsahuje víc než jméno – {split} lze rozdělit podle pravidel, {unsplit} zůstane, jak je (nedá se určit jistě)",
2981
+ "ui.fixes.participant-names.example": " {event} · {role} · {before} → {name}{note}",
2982
+ "ui.fixes.participant-names.note": " · poznámka: {note}",
2983
+ "ui.fixes.participant-names.more": " … a {n} dalších: strom fixes show {id} --json",
2984
+ "ui.fixes.participant-names.does": "Oprava: každé jméno se podle pravidel rozdělí – jméno samo, s tituly; zbytek za poznámku, kterou účastník měl. Jeden krok historie, zdarma, nic se nikam neposílá; strom fixes undo {id} vrátí všechna jména, jak byla.",
2985
+ "ui.fixes.participant-names.not": "Nic se nesmaže: žádné slovo jména se neztratí a nikdo se nespojí s osobou stromu. Jména, která se nedají určit jistě, zůstanou, jak jsou.",
2986
+ "ui.fixes.participant-names.ask": "Mají se jména rozdělit?",
2987
+ "ui.fixes.participant-names.all": "Ano: rozdělit {n, plural, one {# jméno} few {# jména} other {# jmen}}",
2988
+ "ui.fixes.participant-names.done": "Rozděleno: {n, plural, one {# jméno} few {# jména} other {# jmen}} v {records, plural, one {# záznamu} few {# záznamech} other {# záznamech}}. Vrátit, jak byla: strom fixes undo {id}",
2989
+ "ui.fixes.participant-names.undone": "Vráceno, jak bylo: {n, plural, one {# jméno} few {# jména} other {# jmen}}{left}",
2990
+ "ui.fixes.participant-names.left": " · {n, plural, one {# se mezitím změnilo, ponecháno} few {# se mezitím změnila, ponechána} other {# se mezitím změnilo, ponecháno}}",
2991
+ "ui.fixes.participant-names.nothing": "Není co vracet: jména se nerozdělovala, nebo už jsou zpět, jak byla.",
2992
+ "ui.fixes.state.pending.participants": "{count, plural, one {# jméno čeká} few {# jména čekají} other {# jmen čeká}} na rozhodnutí",
2993
+ "ui.doc.fixes.pending.participants": "{name}: {count, plural, one {# jméno čeká} few {# jména čekají} other {# jmen čeká}} na rozhodnutí",
2934
2994
  "fix.reread.what": "Porovnat dvě čtení {media}: dřívější {old} a nové {now} z ostřejší kopie",
2935
2995
  "fix.reread.why.differ": "Oprava stromu ({since}): místo se přečetlo znovu stejně jako dřív, z ostřejší kopie {from}, a čtení se liší — dřívější se četlo ze zmenšeného snímku.",
2936
2996
  "fix.reread.why.refined": "Oprava stromu ({since}): místo se přečetlo znovu stejně jako dřív, z ostřejší kopie {from}, a nové čtení přečetlo víc nebo jistěji — dřívější se četlo ze zmenšeného snímku.",
@@ -733,6 +733,30 @@
733
733
  "history.why.archive": "Solange die Forschung als Archiv geführt wird, wird nichts gesucht.",
734
734
  "history.why.held": "Die Ereignisse von {region} sucht eine andere Forschung auf diesem Computer.",
735
735
  "history.why.nobody": "In jenen Jahren lebte hier niemand aus dem Stammbaum.",
736
+ "surname.suggest.person": "In den Einträgen steht dieselbe Person als {forms}.",
737
+ "surname.suggest.phonetic": "{forms}: derselbe Laut anders geschrieben ({keys}), am selben Ort in denselben Jahren.",
738
+ "surname.suggest.spelling": "{forms}: eine andere Schreibweise ({keys}) am selben Ort in denselben Jahren.",
739
+ "surname.key.double": "ein doppelter Buchstabe",
740
+ "surname.key.diacritics": "Akzente",
741
+ "log.surname.group": "Namensformen einer Familie: {forms}",
742
+ "log.surname.ungroup": "Namensformen getrennt: {forms}",
743
+ "log.surname.reject": "Namensformen keiner gemeinsamen Familie: {forms}",
744
+ "log.surname.unreject": "Namensformen dürfen wieder vorgeschlagen werden: {forms}",
745
+ "log.surname.undo": "Namensformen wiederhergestellt, wie sie waren",
746
+ "log.participant.fix": "Namen von Paten und Zeugen von ihren Notizen getrennt: {n}",
747
+ "log.participant.unfix": "Namen von Paten und Zeugen wiederhergestellt, wie sie waren: {n}",
748
+ "afterdeath.what": "{name} prüfen: ein Ereignis nach dem Tod datiert ({year})",
749
+ "afterdeath.wedding.what": "{name} prüfen: eine Heirat nach dem Tod datiert ({year})",
750
+ "afterdeath.why": "Der Tod: {death}. Danach datiert: {event}. Eines der Daten ist falsch gelesen, oder die Einträge betreffen zwei Personen.",
751
+ "afterdeath.done": "beide Einträge neu gelesen: ein falsch gelesenes Datum mit seinem Eintrag berichtigt, zwei Personen getrennt, oder eine Notiz, warum beide Daten gelten",
752
+ "afterdeath.fact": "{kind} {id} {date}{place} ({cites})",
753
+ "afterdeath.fact.uncited": "{kind} {id} {date}{place} (keine Quelle zitiert)",
754
+ "afterdeath.result.dismissed": "Als kein Fehler markiert ({event}): nichts zu prüfen.",
755
+ "afterdeath.result.gone": "{event} ist nicht mehr nach dem Tod datiert: nichts zu prüfen.",
756
+ "log.quality.two": "Zwei Personen, nicht eine: {persons}",
757
+ "log.quality.afterdeath": "{persons}: nach dem Tod datiert ({subject}), kein Fehler",
758
+ "log.quality.dismiss": "Als kein Fehler markiert: {what}",
759
+ "log.quality.undo": "Nicht mehr als kein Fehler markiert: {what}",
736
760
  "history.places.what": "Die Orte der Angaben für die Geschichte der Vorfahren ({n}): zu welchen Regionen jeder gehörte, mit den Jahren",
737
761
  "history.places.why": "die Ereignisse der Geschichte (Kriege, Epidemien, Volkszählungen, Wechsel des Staates) erscheinen an den Orten der Angaben nach den Regionen, zu denen sie gehörten",
738
762
  "history.places.done": "jeder Ort seinen Regionen mit den Jahren zugeordnet oder als nicht auffindbar vermerkt",
@@ -1315,6 +1339,21 @@
1315
1339
  "ui.sync.fact.part.gone": "{n}. {who}: {kind} {when} — nicht mehr genannt: {wasRole} {part}",
1316
1340
  "ui.sync.person.cite": "{n}. {who}: eigene Quelle zur Person — {sources}",
1317
1341
  "ui.sync.family.cite": "{n}. {who}: eigene Quelle zur Familie — {sources}",
1342
+ "ui.sync.surname.group": "{n}. Namensformen einer Familie: {forms}",
1343
+ "ui.sync.surname.group.set": "{n}. Namensformen einer Familie: {was} → {forms}",
1344
+ "ui.sync.surname.group.remove": "{n}. Namensformen nicht mehr einer Familie: {was}",
1345
+ "ui.sync.surname.group.kept": "{n}. Namensformen einer Familie weichen ab: in der App {forms}, in der Forschung {was}",
1346
+ "ui.sync.surname.reject": "{n}. Namensformen als eine Familie abgelehnt: {forms}",
1347
+ "ui.sync.surname.reject.set": "{n}. Abgelehnte Namensformen: {was} → {forms}",
1348
+ "ui.sync.surname.reject.remove": "{n}. Namensformen nicht mehr abgelehnt: {was}",
1349
+ "ui.sync.surname.reject.kept": "{n}. Abgelehnte Namensformen weichen ab: in der App {forms}, in der Forschung {was}",
1350
+ "ui.sync.surname.warning": "{n}. Eine Zeile mit Namensformen nicht gelesen ({why}): {line}",
1351
+ "ui.sync.surname.why.empty": "eine leere Form",
1352
+ "ui.sync.surname.why.separator": "eine Form mit |",
1353
+ "ui.sync.surname.why.damaged": "beschädigte Zeichen — die Liste bleibt, wie sie ist",
1354
+ "ui.sync.do.surname": "übernommen, wie in der App entschieden",
1355
+ "ui.sync.do.surname.kept": "bleibt, wie die Forschung es hat",
1356
+ "ui.sync.do.surname.report": "nichts ändert sich",
1318
1357
  "ui.sync.do.part": "wird der Angabe hinzugefügt, mit Verweis auf den eigenen Stammbaum",
1319
1358
  "ui.sync.do.part.user": "Die eigene Änderung gilt (die der Forschung bleibt in ihrer Geschichte)",
1320
1359
  "ui.sync.do.person.cite": "der Name der Person zitiert sie (ein Hinweis, bis die Forschung den Eintrag liest)",
@@ -1667,6 +1706,10 @@
1667
1706
  "ui.check.notdownloaded.hint": "den Ordner der Forschung im Finder öffnen und laden (oder den Ordner aus iCloud Drive herausnehmen), dann erneut prüfen – nicht strom repair",
1668
1707
  "ui.check.sharedmissing": "im gemeinsamen Ordner fehlen auf diesem Computer: {n, plural, one {# Datei} other {# Dateien}} (die erste: {first}) – Familienmaterial und Bilder liegen neben der Forschung, nicht in ihrem Ordner",
1669
1708
  "ui.check.sharedmissing.hint": "den ganzen Strom-Ordner aus einer Sicherung zurückholen (seinen Unterordner shared); wohin die erste gehört: {show}",
1709
+ "ui.check.afterdeath": "{person}: {event} {date} nach dem Tod datiert ({death})",
1710
+ "ui.check.afterdeath.wedding": "{person}: eine Heirat {date} nach dem Tod datiert ({death})",
1711
+ "ui.check.afterdeath.hint": "beide Einträge neu lesen: {show}; kein Fehler → {dismiss}",
1712
+ "ui.check.participantnames": "{n, plural, one {# Name eines Paten oder Zeugen enthält} other {# Namen von Paten und Zeugen enthalten}} mehr als einen Namen",
1670
1713
  "ui.error.tree.not-downloaded": "noch nicht aus iCloud geladen: {n, plural, one {# Datei} other {# Dateien}} der Forschung (die erste: {first}) – nichts wurde geschrieben",
1671
1714
  "ui.error.tree.not-downloaded.hint": "den Ordner der Forschung im Finder öffnen und die Dateien laden (oder den Ordner aus iCloud Drive herausnehmen), dann erneut ausführen – nicht strom repair",
1672
1715
  "ui.export.file": "{file} ({for}) — {persons, plural, one {# Person} other {# Personen}} · {families, plural, one {# Familie} other {# Familien}} · {facts, plural, one {# Angabe} other {# Angaben}} · {sources, plural, one {# Quelle} other {# Quellen}}{skipped}",
@@ -2931,6 +2974,23 @@
2931
2974
  "ui.fixes.estimate.direct": "Die strittigen und die direkte Linie: {part} · {level} · {calls, plural, one {# Leseraufruf} other {# Leseraufrufe}} · etwa {minutes} Min.",
2932
2975
  "ui.fixes.reader.before": " · hat sie schon gelesen",
2933
2976
  "ui.fixes.reader.other": " · empfohlen: ein anderer als der frühere",
2977
+ "ui.fixes.title.archive": "Korrekturen: was frühere Versionen von strom in dieser Forschung hinterlassen haben",
2978
+ "ui.fixes.participant-names.name": "Namen von Paten und Zeugen mit dem, was der Eintrag dazu sagt",
2979
+ "ui.fixes.participant-names.what": "Vor strom {since} konnte der Name eines Paten, eines Zeugen oder einer anderen beteiligten Person auch enthalten, was der Eintrag dazu sagt – Stand, Beruf, Ort und Nummer. Nun steht der Name allein und der Rest ist seine Notiz: die Suche, die Duplikate und die Strom-App lesen nur den Namen.",
2980
+ "ui.fixes.participant-names.found": "Gefunden: {found, plural, one {# Beteiligter} other {# Beteiligte}}, deren Name mehr als einen Namen enthält – {split} nach Regeln zu trennen, {unsplit} bleiben, wie sie sind (nicht sicher zu bestimmen)",
2981
+ "ui.fixes.participant-names.example": " {event} · {role} · {before} → {name}{note}",
2982
+ "ui.fixes.participant-names.note": " · Notiz: {note}",
2983
+ "ui.fixes.participant-names.more": " … {n} weitere: strom fixes show {id} --json",
2984
+ "ui.fixes.participant-names.does": "Korrektur: jeder Name wird nach den Regeln getrennt – der Name allein, mit seinen Titeln; der Rest hinter die Notiz, die der Beteiligte hatte. Ein Schritt der Geschichte, kostenlos, nichts wird irgendwohin gesendet; strom fixes undo {id} stellt alle Namen wieder her, wie sie waren.",
2985
+ "ui.fixes.participant-names.not": "Nichts wird gelöscht: kein Wort eines Namens geht verloren, und niemand wird mit einer Person des Stammbaums verbunden. Namen, die sich nicht sicher bestimmen lassen, bleiben, wie sie sind.",
2986
+ "ui.fixes.participant-names.ask": "Namen trennen?",
2987
+ "ui.fixes.participant-names.all": "Ja: {n, plural, one {# Namen} other {# Namen}} trennen",
2988
+ "ui.fixes.participant-names.done": "Getrennt: {n, plural, one {# Name} other {# Namen}} in {records, plural, one {# Datensatz} other {# Datensätzen}}. Wiederherstellen, wie sie waren: strom fixes undo {id}",
2989
+ "ui.fixes.participant-names.undone": "Wiederhergestellt, wie sie waren: {n, plural, one {# Name} other {# Namen}}{left}",
2990
+ "ui.fixes.participant-names.left": " · {n, plural, one {# seitdem geändert, so gelassen} other {# seitdem geändert, so gelassen}}",
2991
+ "ui.fixes.participant-names.nothing": "Nichts wiederherzustellen: die Namen wurden nicht getrennt oder sind schon wieder, wie sie waren.",
2992
+ "ui.fixes.state.pending.participants": "{count, plural, one {# Name wartet} other {# Namen warten}} auf eine Entscheidung",
2993
+ "ui.doc.fixes.pending.participants": "{name}: {count, plural, one {# Name wartet} other {# Namen warten}} auf eine Entscheidung",
2934
2994
  "fix.reread.what": "Die zwei Lesungen von {media} vergleichen: die frühere {old} und die neue {now} aus einer schärferen Kopie",
2935
2995
  "fix.reread.why.differ": "Eine Korrektur von strom ({since}): die Stelle wurde wie zuvor erneut gelesen, aus der schärferen Kopie {from}, und die Lesungen weichen ab — die frühere wurde aus einem verkleinerten Bild gelesen.",
2936
2996
  "fix.reread.why.refined": "Eine Korrektur von strom ({since}): die Stelle wurde wie zuvor erneut gelesen, aus der schärferen Kopie {from}, und die neue Lesung liest mehr oder sicherer — die frühere wurde aus einem verkleinerten Bild gelesen.",
@@ -45,7 +45,10 @@ creates, `@name` uses it later; a found search comes after the source it found
45
45
  grandparents too ("daughter of Jan Novák, cottager in Týnec No. 7"), cited
46
46
  `--information secondary`. The next search identifies them by exactly these.
47
47
  - Everyone the entry names in a role goes in `--with`: godparent, witness,
48
- midwife, officiant, informant, other.
48
+ midwife, officiant, informant, other — the name alone, what the entry says of
49
+ them in closing parentheses: `--with "godparent:Jan Lhoták (sedlák, Lhota
50
+ č. 12)"`. A priest is the officiant. "is this P…?" is linked only when the
51
+ record says so.
49
52
  - A child the record does not name (stillborn, died unbaptised) has no given
50
53
  name — never a description in its place: `person add "/Novák/" --sex M`, then
51
54
  `event add P… DEAT --date … --age stillborn` (or a `--note`).
@@ -0,0 +1,39 @@
1
+ {
2
+ "about": "Words that tell a participant's name (godparent, witness, priest) from what a record says beside it. Compared folded (no accents, lower case). Words of describing and house written with a capital letter count only in the languages listed in nounsCapitalized: elsewhere a capitalized word may be a surname.",
3
+ "clergy": {
4
+ "cs": ["farář", "kaplan", "děkan", "kněz", "p.", "páter", "administrátor", "vikář", "kooperátor", "arciděkan"],
5
+ "de": ["pfarrer", "kaplan", "dechant", "pater", "kooperator", "vikar", "pfarradministrator", "kurat"],
6
+ "la": ["parochus", "cooperator", "presbyter", "sacerdos", "capellanus", "administrator", "vicarius", "decanus", "curatus", "rev.", "r.d.", "adm."],
7
+ "pl": ["proboszcz", "wikary", "ksiądz", "ks.", "wikariusz", "kapelan", "dziekan"]
8
+ },
9
+ "nounsCapitalized": ["de"],
10
+ "describing": {
11
+ "cs": [
12
+ "sedlák", "sedláka", "sedlačka", "chalupník", "chalupníka", "chalupnice", "domkář", "domkáře", "domkářka", "podruh", "podruha", "podruhyně",
13
+ "čeledín", "děvečka", "nádeník", "nádeníka", "výměnkář", "výminkář", "gruntovník", "rolník", "rolníka", "mlynář", "mlynáře", "kovář", "kováře",
14
+ "krejčí", "švec", "ševce", "tkadlec", "tkalce", "truhlář", "tesař", "zedník", "řezník", "pekař", "hostinský", "kupec", "obchodník", "učitel", "učitele",
15
+ "rychtář", "rychtáře", "starosta", "purkmistr", "mistr", "tovaryš", "měšťan", "měšťana", "soused", "souseda", "poddaný", "hajný", "myslivec", "ovčák",
16
+ "pastýř", "kočí", "služka", "sluha", "šafář", "kantor", "zahradník", "dělník", "horník", "obyvatel", "obyvatelka", "majitel", "majitelka", "vdova",
17
+ "vdovec", "manželka", "manžel", "manžela", "choť", "syn", "dcera", "dcery", "bratr", "sestra", "strýc", "teta", "otec", "matka", "děd", "bába",
18
+ "vnuk", "vnučka", "švagr", "švagrová", "zeť", "snacha", "svobodná", "svobodný", "panna", "mládenec", "obec", "ves", "farnost", "panství",
19
+ "zápis", "zápisu", "číslo", "čís.", "obč.", "rol.", "chal.", "dom.", "podr.", "manž.", "svob."
20
+ ],
21
+ "de": [
22
+ "bäuerin", "häusler", "kleinhäusler", "inwohner", "inwohnerin", "inleute", "magd", "witwe", "witwer", "ehefrau", "ehemann", "ehewirtin", "gattin",
23
+ "tochter", "taglöhner", "tagelöhner", "ausnehmer", "auszügler", "halbbauer", "viertelbauer", "besitzer", "besitzerin", "bürger", "insasse",
24
+ "gemeinde", "pfarrei", "dorf", "eintrag", "ledig", "ledige", "lediger", "jungfrau", "junggeselle", "hausbesitzer", "grundbesitzer"
25
+ ],
26
+ "la": [
27
+ "colonus", "coloni", "inquilinus", "inquilina", "subditus", "subdita", "filius", "filia", "vidua", "viduus", "uxor", "coniux", "conjux", "faber",
28
+ "sutor", "sartor", "molitor", "textor", "incola", "civis", "rusticus", "hortulanus", "famulus", "ancilla", "virgo", "juvenis", "honestus",
29
+ "honesta", "laboriosus", "laboriosa", "pudica", "agricola", "domo", "pagus", "pago"
30
+ ],
31
+ "pl": [
32
+ "gospodarz", "gospodyni", "chałupnik", "komornik", "wyrobnik", "parobek", "służąca", "młynarz", "kowal", "krawiec", "szewc", "karczmarz",
33
+ "wdowa", "wdowiec", "żona", "mąż", "córka", "siostra", "panna", "kawaler", "mieszczanin", "zagrodnik", "kmieć", "włościanin", "rolnik", "wieś",
34
+ "parafia", "wpis"
35
+ ]
36
+ },
37
+ "house": ["č.", "čp.", "č.p.", "č.d.", "čís.", "nr.", "no.", "nro.", "num.", "hnr.", "hausnr.", "dom", "domu", "n."],
38
+ "place": ["z", "ze", "v", "ve", "na", "u", "aus", "in", "zu", "bei", "ex", "w", "we", "of", "from", "de"]
39
+ }
@@ -26,6 +26,8 @@ import { readingsLine, readingsOf } from "../core/readsync.js";
26
26
  import { commandSheet } from "./sheet.js";
27
27
  import { foldText } from "../core/text.js";
28
28
  import { subjectPeople } from "../core/records.js";
29
+ import { familyFormsFor, groupedForms, readSurnames, sameFamily } from "../core/surnames.js";
30
+ import { twoPeople } from "../core/quality.js";
29
31
  import { searchedAs } from "../core/evidence.js";
30
32
  import { hypothesisPeople } from "../core/directions.js";
31
33
  import { imagesIndex, weakScans } from "../core/mediaindex.js";
@@ -378,6 +380,37 @@ function materialText(tree, inputs) {
378
380
  }
379
381
  return { texts, unknown };
380
382
  }
383
+ /**
384
+ * How a lesson of the whole project goes with a task: `named` — it names the task's book or archive, or a family or
385
+ * place of the task; `general` — it names nothing of the tree; none — it is about other families, places, books or
386
+ * archives.
387
+ */
388
+ /**
389
+ * The forms of the task's people's surnames the user made one family (core/surnames.ts): a register may write any of
390
+ * them — searched under each. None when no group holds them.
391
+ */
392
+ function surnameFormLines(tree, task) {
393
+ const groups = readSurnames(tree).groups;
394
+ if (!groups.length)
395
+ return [];
396
+ const out = [];
397
+ const seen = new Set();
398
+ for (const id of task.subject) {
399
+ const p = /^P\d{4,}$/u.test(id) ? tree.get(id) : undefined;
400
+ if (!p || p.type !== "person")
401
+ continue;
402
+ for (const n of p.names) {
403
+ const s = n.surname?.trim();
404
+ if (!s || seen.has(s))
405
+ continue;
406
+ seen.add(s);
407
+ const more = groupedForms(tree, s, groups);
408
+ if (more.length)
409
+ out.push(`forms: ${s} is also written ${more.join(", ")} (one family, the user's groups) — search under each`);
410
+ }
411
+ }
412
+ return out;
413
+ }
381
414
  /**
382
415
  * Which lessons of the whole project (no record set, archive or place of their own) go with a task: one naming a
383
416
  * family or a place of this tree goes with a task of that family or place (its text, its people, their facts, its
@@ -393,11 +426,13 @@ function projectLessonFit(tree, task, persons, where, material) {
393
426
  if (material?.unknown)
394
427
  return () => "general";
395
428
  const known = new Set();
429
+ // a surname's forms: the language's and those of the groups the person made one family (core/surnames.ts)
430
+ const formsOf = familyFormsFor(tree);
396
431
  const add = (n) => {
397
432
  if (!n)
398
433
  return;
399
434
  const first = n.trim().split(/[\s,]+/u)[0] ?? "";
400
- for (const f of [first, ...surnameForms(first)]) {
435
+ for (const f of [first, ...formsOf(first)]) {
401
436
  const s = stem(f);
402
437
  if (s)
403
438
  known.add(s);
@@ -427,7 +462,7 @@ function projectLessonFit(tree, task, persons, where, material) {
427
462
  const knownList = [...known];
428
463
  // the names the task gives in its own words, and the surnames of its people in their forms
429
464
  const names = new Set([...taskTexts, ...(material?.texts ?? [])].flatMap((t) => textNames(t, true)).filter((w) => [...w].length >= 4 && !given.has(w)));
430
- const surnames = [...new Set(persons.flatMap((p) => p.names.flatMap((n) => (n.surname ? [...surnameForms(n.surname)] : []))))];
465
+ const surnames = [...new Set(persons.flatMap((p) => p.names.flatMap((n) => (n.surname ? [...formsOf(n.surname)] : []))))];
431
466
  // a book as a lesson names it: the start of its title ("Lhota 03, N 1700–1750" → "lhota 03"), or its ID
432
467
  const bookName = (b) => {
433
468
  const head = foldedWords(b.title.split(",")[0] ?? "").join(" ");
@@ -900,6 +935,7 @@ export function buildBrief(tree, opts) {
900
935
  `why: ${task.why}`,
901
936
  `done: ${task.doneWhen}`,
902
937
  task.subject.length ? `about: ${task.subject.join(" ")}` : "",
938
+ ...surnameFormLines(tree, task),
903
939
  ...task.notes.slice(-3).map((n) => `note: ${n.text}`),
904
940
  task.notes.length > 3 ? `(notes: the last 3 of ${task.notes.length} — all: strom task show ${task.id})` : "",
905
941
  ...personAnswers(tree, task),
@@ -920,7 +956,7 @@ export function buildBrief(tree, opts) {
920
956
  for (const input of treeInputs) {
921
957
  const system = `gedcom:${input.sha.slice(0, 12)}`;
922
958
  const ids = tree.list("person").filter((p) => !p.retracted && p.refs?.some((r) => r.system === system)).map((p) => p.id);
923
- const dupes = likelyDuplicates(tree, ids);
959
+ const dupes = likelyDuplicates(tree, ids, sameFamily(tree), twoPeople(tree));
924
960
  if (dupes.length)
925
961
  sections.push({
926
962
  name: "duplicates",
@@ -1016,11 +1052,12 @@ export function buildBrief(tree, opts) {
1016
1052
  // nothing to tell it by: kept
1017
1053
  return !places.length || (!span && !taskPlaces.size);
1018
1054
  };
1019
- const named = (s) => (s.scope.surnames ?? []).some((x) => theirs.some((y) => sameFamilyName(x, y)));
1055
+ const sameName = sameFamily(tree);
1056
+ const named = (s) => (s.scope.surnames ?? []).some((x) => theirs.some((y) => sameName(x, y)));
1020
1057
  const premise = tree.list("search").filter((s) => s.recordsets.some((b) => where.has(b)) || (s.task && s.task === task?.id) || named(s));
1021
1058
  const searches = premise.filter((s) => s.recordsets.some((b) => where.has(b)) || (s.task && s.task === task?.id) || nearby(s));
1022
1059
  const elsewhere = premise.filter((s) => !searches.includes(s));
1023
- const elsewhereNames = [...new Set(elsewhere.flatMap((s) => theirs.filter((y) => (s.scope.surnames ?? []).some((x) => sameFamilyName(x, y)))))];
1060
+ const elsewhereNames = [...new Set(elsewhere.flatMap((s) => theirs.filter((y) => (s.scope.surnames ?? []).some((x) => sameName(x, y)))))];
1024
1061
  const ofPlace = (id) => {
1025
1062
  const pl = tree.get(id);
1026
1063
  return pl?.type === "place" && [...pl.names.map((n) => n.name), ...pl.jurisdictions.map((j) => j.name)].some((n) => [...bookPlaces].some((b) => b === foldText(n) || b.startsWith(`${foldText(n)},`)));
@@ -27,7 +27,7 @@ import { calibrationOffer, viewSizesFor } from "../core/viewsizes.js";
27
27
  import { archivePermissions, archiveState, browserOnlyHere } from "./menu-archives.js";
28
28
  import { chooseTerminal, terminalState } from "./terminals.js";
29
29
  import { phoneState } from "../core/phone.js";
30
- import { fixEnv, pendingCount, pendingFixes } from "../commands/fixchecks.js";
30
+ import { fixEnv, PARTICIPANT_NAMES, pendingCount, pendingFixes } from "../commands/fixchecks.js";
31
31
  import { isArchive as archiveHere } from "../core/mode.js";
32
32
  import { isAgent } from "../core/which.js";
33
33
  import { placesWithCoords } from "../core/placehistory.js";
@@ -227,20 +227,22 @@ async function maintenance(ctx, run, lang, root, newer) {
227
227
  // what a fix of strom's left behind in this research: what it is, then the person's choice (strom fixes run)
228
228
  if (root) {
229
229
  const tree = Tree.open(root, ctx.env);
230
- if (!archiveHere(tree)) {
231
- const waits = (pendingFixes(tree, fixEnv(ctx, tree)) ?? []).reduce((n, s) => n + pendingCount(s), 0);
232
- items.push({
233
- key: "3",
234
- label: t("ui.maintenance.fixes", { state: waits ? t("ui.maintenance.fixes.waits", { n: waits }) : "" }),
235
- act: async () => {
236
- if (waits)
237
- await run(["fixes", "run"]);
238
- else
239
- await run(["fixes"]);
240
- await pause(ctx, lang);
241
- },
242
- });
243
- }
230
+ // an archive: only what is rewritten by rules (participant-names) — nobody reads there, nothing of an agent said
231
+ const archive = archiveHere(tree);
232
+ const waits = (pendingFixes(tree, fixEnv(ctx, tree)) ?? []).filter((s) => !archive || s.check.remedy === "rewrite").reduce((n, s) => n + pendingCount(s), 0);
233
+ items.push({
234
+ key: "3",
235
+ label: t("ui.maintenance.fixes", { state: waits ? t("ui.maintenance.fixes.waits", { n: waits }) : "" }),
236
+ act: async () => {
237
+ if (archive)
238
+ await run(waits ? ["fixes", "run", PARTICIPANT_NAMES.id] : ["fixes", "show", PARTICIPANT_NAMES.id]);
239
+ else if (waits)
240
+ await run(["fixes", "run"]);
241
+ else
242
+ await run(["fixes"]);
243
+ await pause(ctx, lang);
244
+ },
245
+ });
244
246
  }
245
247
  if (newer)
246
248
  items.push({
package/dist/cli/menu.js CHANGED
@@ -263,13 +263,12 @@ export async function runMenu(ctx, run) {
263
263
  // a fix of strom's left something behind here that waits for the person's decision: one line (the settings)
264
264
  // (as kept while nothing it is found by changed: else looked through in the background, said at the next look —
265
265
  // the menu never waits for it; found on Windows, a research on OneDrive: minutes before the menu showed)
266
- const fix = archive
267
- ? undefined
268
- : timed("menu.fixes", () => {
269
- const env = fixEnv(ctx, tree);
270
- // (a backup not made: nothing written to the research, nothing looked through either)
271
- return (pendingFixes(tree, env, { kept: true }) ?? (ctx.backupFailed ? undefined : refreshFixesSoon(tree, env, ctx.env)))?.[0];
272
- });
266
+ // (an archive: only what is rewritten by rules — nobody reads there)
267
+ const fix = timed("menu.fixes", () => {
268
+ const env = fixEnv(ctx, tree);
269
+ // (a backup not made: nothing written to the research, nothing looked through either)
270
+ return (pendingFixes(tree, env, { kept: true }) ?? (ctx.backupFailed ? undefined : refreshFixesSoon(tree, env, ctx.env)))?.find((f) => !archive || f.check.remedy === "rewrite");
271
+ });
273
272
  // a fix of strom's said as one; another model reading now, as readings to read again
274
273
  if (fix)
275
274
  out(t(fix.check.id === "new-model" ? "ui.fixes.hint.readings" : "ui.fixes.hint", { name: t(`ui.fixes.${fix.check.id}.name`), count: pendingCount(fix) }));