gedcom-ts 2026.5.3 → 2026.6.0

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 (118) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +163 -29
  3. package/dist/booklet/booklet-date-en.d.ts +9 -0
  4. package/dist/booklet/booklet-date-factory.d.ts +21 -0
  5. package/dist/booklet/booklet-date-fr.d.ts +1 -0
  6. package/dist/booklet/booklet-date.d.ts +6 -0
  7. package/dist/booklet/booklet-estimate.d.ts +4 -3
  8. package/dist/booklet/booklet-gender-core.d.ts +3 -0
  9. package/dist/booklet/booklet-gender-data.d.ts +9 -0
  10. package/dist/booklet/booklet-gender.d.ts +8 -9
  11. package/dist/booklet/booklet-locale-helpers.d.ts +4 -0
  12. package/dist/booklet/booklet-locale-loader.d.ts +27 -0
  13. package/dist/booklet/booklet-locale-modules-source.d.ts +1 -0
  14. package/dist/booklet/booklet-locale-modules.d.ts +5 -0
  15. package/dist/booklet/booklet-locale.d.ts +15 -0
  16. package/dist/booklet/booklet-logo-loader.d.ts +8 -0
  17. package/dist/booklet/booklet-logo.d.ts +1 -5
  18. package/dist/booklet/booklet-messages-data.d.ts +15 -0
  19. package/dist/booklet/booklet-messages-types.d.ts +46 -0
  20. package/dist/booklet/booklet-messages.d.ts +7 -0
  21. package/dist/booklet/booklet-narrative-en.d.ts +10 -0
  22. package/dist/booklet/booklet-narrative-engine.d.ts +17 -0
  23. package/dist/booklet/booklet-narrative-fr.d.ts +10 -0
  24. package/dist/booklet/booklet-narrative-i18n.d.ts +44 -0
  25. package/dist/booklet/booklet-narrative-locales.d.ts +3 -0
  26. package/dist/booklet/booklet-narrative.d.ts +5 -8
  27. package/dist/booklet/booklet-pdf-bidi.d.ts +5 -0
  28. package/dist/booklet/booklet-pdf-content-text.d.ts +11 -0
  29. package/dist/booklet/booklet-pdf-font-runtime.d.ts +8 -0
  30. package/dist/booklet/booklet-pdf-font-runtime.node.d.ts +3 -0
  31. package/dist/booklet/booklet-pdf-font-sources.d.ts +6 -0
  32. package/dist/booklet/booklet-pdf-font.d.ts +14 -0
  33. package/dist/booklet/booklet-pdf-fontkit.d.ts +6 -0
  34. package/dist/booklet/booklet-pdf-text.d.ts +24 -0
  35. package/dist/booklet/booklet-pdf.d.ts +13 -0
  36. package/dist/booklet/booklet-person.d.ts +6 -3
  37. package/dist/booklet/booklet-place-en.d.ts +16 -0
  38. package/dist/booklet/booklet-place-factory.d.ts +28 -0
  39. package/dist/booklet/booklet-place.d.ts +13 -0
  40. package/dist/booklet/booklet-structure.d.ts +3 -2
  41. package/dist/booklet/booklet-timeline-canvas.d.ts +2 -9
  42. package/dist/booklet/booklet-timeline-loader.d.ts +6 -0
  43. package/dist/booklet/booklet-timeline-metrics.d.ts +8 -0
  44. package/dist/booklet/feature-chunks/booklet-logo-draw.d.ts +15 -0
  45. package/dist/booklet/feature-chunks/booklet-timeline-raster.d.ts +3 -0
  46. package/dist/booklet/font-chunks/booklet-font-subset-chars.d.ts +2 -0
  47. package/dist/booklet/font-chunks/booklet-pdf-font-bytes-ar.d.ts +2 -0
  48. package/dist/booklet/font-chunks/booklet-pdf-font-bytes-he.d.ts +2 -0
  49. package/dist/booklet/font-chunks/booklet-pdf-font-bytes-zh-ext.d.ts +2 -0
  50. package/dist/booklet/font-chunks/booklet-pdf-font-bytes-zh.d.ts +2 -0
  51. package/dist/booklet/index.d.ts +11 -3
  52. package/dist/booklet/locale-chunks/booklet-locale-ar.d.ts +3 -0
  53. package/dist/booklet/locale-chunks/booklet-locale-de.d.ts +3 -0
  54. package/dist/booklet/locale-chunks/booklet-locale-en.d.ts +3 -0
  55. package/dist/booklet/locale-chunks/booklet-locale-es.d.ts +3 -0
  56. package/dist/booklet/locale-chunks/booklet-locale-fr.d.ts +3 -0
  57. package/dist/booklet/locale-chunks/booklet-locale-he.d.ts +3 -0
  58. package/dist/booklet/locale-chunks/booklet-locale-it.d.ts +3 -0
  59. package/dist/booklet/locale-chunks/booklet-locale-nl.d.ts +3 -0
  60. package/dist/booklet/locale-chunks/booklet-locale-pl.d.ts +3 -0
  61. package/dist/booklet/locale-chunks/booklet-locale-pt.d.ts +3 -0
  62. package/dist/booklet/locale-chunks/booklet-locale-zh.d.ts +3 -0
  63. package/dist/booklet.cjs +1 -1
  64. package/dist/booklet.mjs +1 -1
  65. package/dist/commons/Act.d.ts +2 -3
  66. package/dist/commons/ObjeRecord.d.ts +23 -0
  67. package/dist/commons/Person.d.ts +2 -3
  68. package/dist/edit/ActEdit.d.ts +5 -0
  69. package/dist/edit/AudioInterviewActEdit.d.ts +35 -0
  70. package/dist/edit/GedcomExportOptionsEdit.d.ts +3 -0
  71. package/dist/edit/index.d.ts +1 -0
  72. package/dist/export/GEDCOM.d.ts +4 -1
  73. package/dist/feature-chunks/booklet-logo-draw.cjs +1 -0
  74. package/dist/feature-chunks/booklet-logo-draw.mjs +1 -0
  75. package/dist/feature-chunks/booklet-timeline-raster.cjs +1 -0
  76. package/dist/feature-chunks/booklet-timeline-raster.mjs +1 -0
  77. package/dist/font-chunks/booklet-pdf-font-bytes-ar.cjs +1 -0
  78. package/dist/font-chunks/booklet-pdf-font-bytes-ar.mjs +1 -0
  79. package/dist/font-chunks/booklet-pdf-font-bytes-he.cjs +1 -0
  80. package/dist/font-chunks/booklet-pdf-font-bytes-he.mjs +1 -0
  81. package/dist/font-chunks/booklet-pdf-font-bytes-zh-ext.cjs +1 -0
  82. package/dist/font-chunks/booklet-pdf-font-bytes-zh-ext.mjs +1 -0
  83. package/dist/font-chunks/booklet-pdf-font-bytes-zh.cjs +1 -0
  84. package/dist/font-chunks/booklet-pdf-font-bytes-zh.mjs +1 -0
  85. package/dist/import/ReadGed.d.ts +3 -1
  86. package/dist/import/SplitedInformations.d.ts +2 -3
  87. package/dist/index.cjs +1 -1
  88. package/dist/index.d.ts +8 -2
  89. package/dist/index.mjs +1 -1
  90. package/dist/locale-chunks/booklet-locale-ar.cjs +1 -0
  91. package/dist/locale-chunks/booklet-locale-ar.mjs +1 -0
  92. package/dist/locale-chunks/booklet-locale-de.cjs +1 -0
  93. package/dist/locale-chunks/booklet-locale-de.mjs +1 -0
  94. package/dist/locale-chunks/booklet-locale-en.cjs +1 -0
  95. package/dist/locale-chunks/booklet-locale-en.mjs +1 -0
  96. package/dist/locale-chunks/booklet-locale-es.cjs +1 -0
  97. package/dist/locale-chunks/booklet-locale-es.mjs +1 -0
  98. package/dist/locale-chunks/booklet-locale-fr.cjs +1 -0
  99. package/dist/locale-chunks/booklet-locale-fr.mjs +1 -0
  100. package/dist/locale-chunks/booklet-locale-he.cjs +1 -0
  101. package/dist/locale-chunks/booklet-locale-he.mjs +1 -0
  102. package/dist/locale-chunks/booklet-locale-it.cjs +1 -0
  103. package/dist/locale-chunks/booklet-locale-it.mjs +1 -0
  104. package/dist/locale-chunks/booklet-locale-nl.cjs +1 -0
  105. package/dist/locale-chunks/booklet-locale-nl.mjs +1 -0
  106. package/dist/locale-chunks/booklet-locale-pl.cjs +1 -0
  107. package/dist/locale-chunks/booklet-locale-pl.mjs +1 -0
  108. package/dist/locale-chunks/booklet-locale-pt.cjs +1 -0
  109. package/dist/locale-chunks/booklet-locale-pt.mjs +1 -0
  110. package/dist/locale-chunks/booklet-locale-zh.cjs +1 -0
  111. package/dist/locale-chunks/booklet-locale-zh.mjs +1 -0
  112. package/dist/utils/gedcom/audioInterviewAct.d.ts +64 -0
  113. package/dist/utils/gedcom/formatObjeRecord.d.ts +3 -0
  114. package/dist/utils/gedcom/guessMediFromForm.d.ts +2 -0
  115. package/dist/utils/gedcom/mediaFormFromUri.d.ts +5 -1
  116. package/dist/utils/gedcom/parseStandaloneObje.d.ts +6 -5
  117. package/dist/version.d.ts +1 -1
  118. package/package.json +104 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,60 @@
2
2
 
3
3
  All notable changes of gedcom-ts
4
4
 
5
+ ## [2026.6.0] - 2026-06-01
6
+
7
+ ### Added
8
+
9
+ - **Multimédia GEDCOM 7 (`OBJE`)** : import et export complets des enregistrements `0 @O…@ OBJE` avec plusieurs `FILE`, `FORM`, `MEDI` (`AUDIO`, `PHOTO`, `VIDEO`, …), `PHRASE`, `TITL` et variantes `FILE`.`TRAN` (transcodage, WebVTT).
10
+ - **`ReadGed.objeRecordsById`** : map des OBJE absorbés depuis le fichier (round-trip via `GedcomExportOptions.objeRecordsById`).
11
+ - **Types et utilitaires OBJE** : `ObjeRecord`, `ObjeFileEntry`, `primaryObjeFileUri`, `parseStandaloneObjeBlock`, `formatObjeRecordBlock`, `guessMediFromForm`, `registerObjeRecord`.
12
+ - **Entretiens audio / histoire orale** : actes `EVEN` + `TYPE Interview` (ou « Oral history ») avec lien `2 OBJE`, note de transcription (`2 NOTE`) et API dédiée — `createAudioInterviewAct`, `attachAudioInterviewToAct`, `editAct(act).asAudioInterview()`, `editAudioInterviewAct`.
13
+ - **Détection et lecture** : `isAudioInterviewAct`, `isAudioInterviewEvenType`, `getAudioInterviewTranscription`, `getAudioInterviewMedia`, `getAudioInterviewObjeRecords`, `primaryAudioInterviewUri`, `nextObjeXrefId`.
14
+ - **Constantes** : `GEDCOM_7_EVEN_TYPE_AUDIO_INTERVIEW`, `GEDCOM_7_EVEN_TYPE_ORAL_HISTORY`.
15
+ - **`GedcomExportOptionsEdit.setObjeRecordsById`** / **`clearObjeRecordsById`**.
16
+ - **Tests** : `parseStandaloneObje`, `guessMediFromForm`, `audioInterviewAct`, extension des specs export / import `maximal70`.
17
+
18
+ ### Changed
19
+
20
+ - **Export OBJE** : émission de `MEDI` dérivé du MIME lorsque absent ; sérialisation multi-fichiers via `formatObjeRecordBlock`.
21
+ - **Import** : `objePayloadById` remplacé par `objeRecordsById` (`PersonGedcomImportOptions`, `ActsByExtraction`).
22
+ - **CI** : script `test:ci` (`build` puis `vitest run`) pour les tests qui lisent `dist/` ; `prepublishOnly` aligné.
23
+
24
+ ### Fixed
25
+
26
+ - **GEDZIP** : inclusion des binaires locaux liés par pointeur `OBJE` (chemins résolus depuis `sourceUri` / `relativePath`).
27
+ - **FORM audio** : reconnaissance de **WebM**, OGG, WAV, M4A, FLAC, etc. (`guessMediaFormFromUri`, `guessMediaFormFromFile`) ; affinage des exports `application/octet-stream` issus de l’UI.
28
+
29
+ ## [2026.5.4] - 2026-05-24
30
+
31
+ ### Added
32
+
33
+ - **Livret multilingue** : traductions dédiées pour les 11 langues du site (`en`, `fr`, `de`, `nl`, `es`, `zh`, `it`, `pt`, `pl`, `ar`, `he`) — messages PDF, dates, lieux, accords de genre et récits narratifs.
34
+ - **RTL arabe / hébreu** : `isBookletLocaleRtl`, `bookletTextDirection`, mise en page PDF miroir ; **`bidi-js`** (réordonnancement visuel) et **`naqqash`** (formes contextuelles arabes).
35
+ - **Polices PDF intégrées** : Noto Sans Hebrew, Noto Sans Arabic et Noto Sans SC (subset, SIL OFL) en chunks lazy (`dist/font-chunks/`) ; chunk **`booklet-pdf-font-bytes-zh-ext`** (~1,4 MiB) si le GEDCOM contient des hanzi hors subset de base.
36
+ - **Rendu bi-police** : Helvetica pour le latin + Noto pour arabe / hébreu / chinois.
37
+ - **Architecture lazy du livret** : locales (`dist/locale-chunks/`), polices, logo SVG et frise canvas (`dist/feature-chunks/`) via `import()` — cœur `gedcom-ts/booklet` ~75 KiB minifié (~100 KiB obfusqué).
38
+ - **Exports TypeScript des chunks** : chaque sous-chemin `gedcom-ts/booklet/locale-chunks/*`, `font-chunks/*` et `feature-chunks/*` expose `types` + `import` / `require`.
39
+ - **Manifeste unique** : `scripts/booklet-chunk-manifest.mjs` alimente `build.js` et `package.json` (`npm run sync:exports` ou fin de `build:js`).
40
+ - **API hôte** : `registerBookletPdfFontBytes`, `registerPdfFontkit`, `ensureBookletLocale` — preload explicite des chunks (recommandé Vite/Angular).
41
+ - **Scripts** : `prepare:locales`, `prepare:fonts`, `sync:exports`.
42
+ - **Exports** : `BOOKLET_LOCALES`, `BOOKLET_RTL_LOCALES`, `isBookletLocale`, `preparePdfText`, `PdfTextOptions`, `loadBookletPdfFontBytes`, `localeNeedsUnicodePdfFont`, …
43
+ - **Tests** : `tests/booklet/` (i18n, RTL, polices ar/zh, bidi, chunk exports, dist imports).
44
+
45
+ ### Changed
46
+
47
+ - **`pdf-lib` externalisé** : peer dependency ; `@pdf-lib/fontkit` chargé à la demande pour `zh` / `ar` / `he`.
48
+ - **Pipeline build livret** : esbuild → réécriture `./…-chunks/` → `gedcom-ts/booklet/…-chunks/` → obfuscation avec `reservedStrings` sur les chemins d’import dynamiques (analyse statique Vite).
49
+ - **`resolveBookletLocale`** : retourne la locale demandée (plus de repli silencieux vers l’anglais).
50
+ - **`drawGedcomTsLogoOnPage`** : async ; logo lazy (`getGedcomTsLogoPaths()`).
51
+ - **`ensureBookletLocale(locale)`** requis avant les APIs sync.
52
+ - **Frise chronologique** : titre et libellé « dates inconnues » localisés.
53
+ - **README** : tableau des tailles, guide d’intégration Vite/Angular.
54
+
55
+ ### Fixed
56
+
57
+ - **Vite / Angular dev** : imports dynamiques relatifs pré-bundleés dans le cache deps — chemins package explicites dans `dist/booklet.mjs` + `exports` complets.
58
+
5
59
  ## [2026.5.3] - 2026-05-16
6
60
 
7
61
  ### Added
package/README.md CHANGED
@@ -6,8 +6,10 @@
6
6
  - work with a typed JSON model (persons, acts, dates, places, notes, media, name variants, attributes)
7
7
  - edit the model in-place through a fluent, chainable API (`editPerson`, `editAct`, …)
8
8
  - export data back to GEDCOM (`.ged`) or GEDZIP (`.zip`)
9
+ - attach **audio** and other media via GEDCOM 7 `OBJE` records (`MEDI AUDIO`, multi-`FILE`, `TRAN`)
10
+ - model **oral history interviews** as `EVEN` acts with `OBJE` + transcription notes
9
11
  - geocode event places (OpenStreetMap / Nominatim) and group similar city names
10
- - generate a **genealogy booklet** as PDF (`gedcom-ts/booklet`, French narratives)
12
+ - generate a **genealogy booklet** as PDF (`gedcom-ts/booklet`, 11 locales including RTL Arabic/Hebrew)
11
13
 
12
14
  ## Live demo
13
15
 
@@ -19,16 +21,17 @@ A graphical demo showcasing the public API (import a `.ged` / `.zip`, browse the
19
21
  - [Quick start](#quick-start)
20
22
  - [Geocoding places](#geocoding-places)
21
23
  - [Genealogy booklet (PDF)](#genealogy-booklet-pdf)
24
+ - [Audio and oral history (GEDCOM 7)](#audio-and-oral-history-gedcom-7)
22
25
  - [API reference](#api-reference)
23
26
  - [Error handling](#error-handling)
24
27
 
25
28
  ## Package
26
29
 
27
30
  - NPM: [gedcom-ts](https://www.npmjs.com/package/gedcom-ts)
28
- - Version format **CalVer** `AAAA.M.micro` (e.g. `2026.5.3` = May 2026). See [CHANGELOG.md](CHANGELOG.md).
31
+ - Current release **`2026.6.0`** — audio / OBJE / oral history. Version format **CalVer** `AAAA.M.micro` (e.g. `2026.6.0` = June 2026). See [CHANGELOG.md](CHANGELOG.md).
29
32
  - Entry points:
30
33
  - **`gedcom-ts`** — import, model, edit layer, export, geocoding
31
- - **`gedcom-ts/booklet`** — PDF livret (`pdf-lib` bundled in that chunk)
34
+ - **`gedcom-ts/booklet`** — PDF livret (`pdf-lib` en peerDependency, chargé depuis `node_modules`)
32
35
 
33
36
  ## Installation
34
37
 
@@ -36,7 +39,13 @@ A graphical demo showcasing the public API (import a `.ged` / `.zip`, browse the
36
39
  npm install gedcom-ts
37
40
  ```
38
41
 
39
- Both entry points come from the same package; no extra install for the booklet.
42
+ Both entry points come from the same package. For the PDF booklet, **`pdf-lib` is a peer dependency** — install it in your app if you use `gedcom-ts/booklet`:
43
+
44
+ ```bash
45
+ npm install pdf-lib
46
+ ```
47
+
48
+ For `zh` / `ar` / `he` PDF output, your bundler should also be able to resolve `@pdf-lib/fontkit` (included in `gedcom-ts` dependencies; the host app typically preloads it — see [Bundle layout](#bundle-layout) below).
40
49
 
41
50
  ## Runtime Requirements
42
51
 
@@ -193,19 +202,69 @@ The legacy callback `getCityCoordinates` is deprecated — use the flow above.
193
202
 
194
203
  ## Genealogy booklet (PDF)
195
204
 
196
- The **`gedcom-ts/booklet`** subpath builds a printable family booklet: cover page, table of contents, chapters by generation (Sosa), family sheets, and French narrative text from GEDCOM acts (birth, marriage, death, …). Optional generation timelines are rasterized for PDF.
205
+ The **`gedcom-ts/booklet`** subpath builds a printable family booklet: cover page, table of contents, chapters by generation (Sosa), family sheets, and narrative text from GEDCOM acts (birth, marriage, death, …). **English is the default locale** (`DEFAULT_BOOKLET_LOCALE`). Full copy is available for **`en`**, **`fr`**, **`de`**, **`nl`**, **`es`**, **`zh`**, **`it`**, **`pt`**, **`pl`**, **`ar`**, and **`he`** (aligned with [gedcomts.com](https://gedcomts.com) UI languages). Arabic and Hebrew use **RTL layout** in the PDF (`isBookletLocaleRtl`, `bookletTextDirection`).
206
+
207
+ ### Bundle layout
208
+
209
+ Code is split so the host app only downloads what it uses. Approximate sizes after minify (obfuscation adds ~25–30 KiB on `gedcom-ts/booklet`).
210
+
211
+ | Artifact | Size (typ.) | When loaded |
212
+ | --- | ---: | --- |
213
+ | `gedcom-ts` (`dist/index.mjs`) | ~180 KiB | GEDCOM import / edit / export |
214
+ | `gedcom-ts/booklet` (`dist/booklet.mjs`) | ~100 KiB | livret (collect, estimate, PDF) |
215
+ | `locale-chunks/booklet-locale-*` | 14–26 KiB | one UI language |
216
+ | `feature-chunks/booklet-logo-draw` | ~11 KiB | cover logo (`coverLogo !== false`) |
217
+ | `feature-chunks/booklet-timeline-raster` | ~3 KiB | `timelineStyle: "canvas"` |
218
+ | `font-chunks/booklet-pdf-font-bytes-ar` | ~43 KiB | Arabic PDF |
219
+ | `font-chunks/booklet-pdf-font-bytes-he` | ~13 KiB | Hebrew PDF |
220
+ | `font-chunks/booklet-pdf-font-bytes-zh` | ~190 KiB | Chinese PDF (base subset) |
221
+ | `font-chunks/booklet-pdf-font-bytes-zh-ext` | ~1.4 MiB | Chinese PDF when GEDCOM names need extra Han |
222
+ | `pdf-lib` (peer, not in gedcom-ts) | ~500 KiB+ | any PDF generation |
223
+
224
+ **Not bundled in gedcom-ts:** `fflate` (main entry, external), `pdf-lib` (booklet peer). **`bidi-js`** and **`naqqash`** (Arabic shaping / RTL reordering) ship inside `gedcom-ts/booklet` today.
225
+
226
+ Call **`await ensureBookletLocale(locale)`** before sync APIs. `generateGenealogyBookletPdf` preloads locale, logo, and timeline when needed.
227
+
228
+ **Lazy chunk subpaths** (for bundler preload or explicit `import()` — normal apps should use `ensureBookletLocale()` instead):
229
+
230
+ | Subpath | Role |
231
+ | --- | --- |
232
+ | `gedcom-ts/booklet/locale-chunks/booklet-locale-{en,fr,de,nl,es,zh,it,pt,pl,ar,he}` | Messages, narratives, date/place helpers per locale |
233
+ | `gedcom-ts/booklet/font-chunks/booklet-pdf-font-bytes-{ar,he,zh,zh-ext}` | Subset Noto font bytes |
234
+ | `gedcom-ts/booklet/feature-chunks/booklet-logo-draw` | Cover logo SVG paths |
235
+ | `gedcom-ts/booklet/feature-chunks/booklet-timeline-raster` | Canvas timeline PNG rasterizer |
236
+
237
+ Each subpath is listed in `package.json` **`exports`** with matching **`types`** (`.d.ts` under `dist/booklet/…`) and runtime (`.mjs` / `.cjs` under `dist/…`). TypeScript resolves them via `exports.types` (`moduleResolution: bundler` / `node16`). Adding a locale updates `scripts/booklet-chunk-manifest.mjs`; `npm run build:js` regenerates chunk files and syncs `exports`.
238
+
239
+ ### Vite / Angular integration
240
+
241
+ 1. **Lazy-load the booklet entry** — `import('gedcom-ts/booklet')` only when the user opens export / download (not on every tree page).
242
+ 2. **Preload the UI locale** — `import('gedcom-ts/booklet/locale-chunks/booklet-locale-fr')` (or your loader) before sync APIs; then `ensureBookletLocale('fr')`.
243
+ 3. **Unicode PDF (`zh` / `ar` / `he`)** — the host app should import font chunks explicitly and call `registerBookletPdfFontBytes` + `registerPdfFontkit` before `generateGenealogyBookletPdf` (see [gedcomts.com](https://gedcomts.com) `booklet-pdf-deps.loader.ts` for a reference pattern).
244
+ 4. **Dev server** — exclude the main booklet bundle from Vite pre-bundling so lazy subpaths resolve via `node_modules`:
245
+
246
+ ```ts
247
+ // vite.config.ts / angular.json (esbuild optimizeDeps)
248
+ optimizeDeps: {
249
+ exclude: ["gedcom-ts/booklet"],
250
+ },
251
+ ```
252
+
253
+ Published `dist/booklet.mjs` keeps **literal** dynamic `import('gedcom-ts/booklet/…')` strings (rewrite before obfuscation, paths reserved from string encoding) so Vite can statically analyze chunk loads.
197
254
 
198
255
  ### Workflow
199
256
 
200
257
  1. Import with `importGedFile` (`gedcom-ts`).
201
- 2. Collect persons with `collectBookletPersons` (`gedcom-ts/booklet`).
202
- 3. Optionally preview size with `estimateBookletSize` + `groupBookletIntoChapters`.
203
- 4. Build bytes with `generateGenealogyBookletPdf`, then `downloadBookletPdf` (browser).
258
+ 2. **`await ensureBookletLocale(locale)`** load messages/narratives for the chosen language.
259
+ 3. Collect persons with `collectBookletPersons` (`gedcom-ts/booklet`).
260
+ 4. Optionally preview size with `estimateBookletSize` + `groupBookletIntoChapters`.
261
+ 5. Build bytes with `generateGenealogyBookletPdf`, then `downloadBookletPdf` (browser).
204
262
 
205
263
  ```ts
206
264
  import { importGedFile } from "gedcom-ts";
207
265
  import {
208
266
  collectBookletPersons,
267
+ ensureBookletLocale,
209
268
  groupBookletIntoChapters,
210
269
  estimateBookletSize,
211
270
  generateGenealogyBookletPdf,
@@ -217,23 +276,27 @@ async function exportBooklet(file: File) {
217
276
  const ged = await importGedFile(file);
218
277
  const root = ged.persons[0] ?? null;
219
278
 
279
+ const locale = "en" as const; // BookletLocale — align with your UI language (en, fr, de, nl, es, zh, it, pt, pl, ar, he)
280
+ await ensureBookletLocale(locale);
281
+
220
282
  const entries = collectBookletPersons({
221
283
  ged,
222
284
  scope: "from-reference",
223
285
  referencePerson: root,
224
286
  maxGeneration: 6,
287
+ locale,
225
288
  });
226
289
 
227
- const chapters = groupBookletIntoChapters(entries);
290
+ const chapters = groupBookletIntoChapters(entries, locale);
228
291
  const families = chapters.reduce((n, ch) => n + ch.families.length, 0);
229
- const size = estimateBookletSize(chapters, entries.length, families, "summary", "canvas");
292
+ const size = estimateBookletSize(chapters, entries.length, families, "summary", "canvas", locale);
230
293
  console.log(size.label);
231
294
 
232
295
  const pdf = await generateGenealogyBookletPdf(
233
296
  entries,
234
297
  {
235
- title: "Livret familial",
236
- scopeLabel: "Ancêtres et descendance",
298
+ title: "Family booklet",
299
+ scopeLabel: "Ancestors and descendants",
237
300
  personCount: entries.length,
238
301
  referenceName: root ? personDisplayName(root) : undefined,
239
302
  },
@@ -241,10 +304,11 @@ async function exportBooklet(file: File) {
241
304
  detailLevel: "summary",
242
305
  timelineStyle: "canvas",
243
306
  coverLogo: true,
307
+ locale,
244
308
  },
245
309
  );
246
310
 
247
- downloadBookletPdf(pdf, "livret.pdf");
311
+ downloadBookletPdf(pdf, "family-booklet.pdf");
248
312
  }
249
313
  ```
250
314
 
@@ -258,8 +322,9 @@ Use `scope: "all"` and `referencePerson: null` to include every individual in th
258
322
  | `scope` | `"all"` or `"from-reference"` (Sosa from `referencePerson`) |
259
323
  | `referencePerson` | Root person for `"from-reference"`; `null` if scope is `"all"` |
260
324
  | `maxGeneration` | Max Sosa generation (e.g. `6`); ignored when `scope === "all"` |
325
+ | `locale` | `BookletLocale` (default `"en"`) — `en`, `fr`, `de`, `nl`, `es`, `zh`, `it`, `pt`, `pl`, `ar`, `he` |
261
326
 
262
- Helpers: `personDisplayName`, `buildBookletPersonEntry`, `sortBookletEntries`, `bookletSexFromPerson`.
327
+ Helpers: `personDisplayName`, `buildBookletPersonEntry`, `sortBookletEntries`, `bookletSexFromPerson`, `DEFAULT_BOOKLET_LOCALE`, `BOOKLET_LOCALES`, `isBookletLocaleRtl`.
263
328
 
264
329
  ### `generateGenealogyBookletPdf` options
265
330
 
@@ -268,24 +333,27 @@ Helpers: `personDisplayName`, `buildBookletPersonEntry`, `sortBookletEntries`, `
268
333
  | `detailLevel` | `"summary"` \| `"detailed"` | Short prose per person vs longer biographies |
269
334
  | `timelineStyle` | `"off"` \| `"canvas"` | Generation timeline pages per chapter |
270
335
  | `coverLogo` | `true` (default), `false`, or `DrawGedcomTsLogoOptions` | gedcom-ts vector logo on the cover |
336
+ | `locale` | `BookletLocale` (default `"en"`) | Narrative, chapter titles, PDF chrome, timeline legend |
337
+ | `unicodeFont` | `Uint8Array` (optional) | Replace bundled Noto font for `zh` / `ar` / `he` |
271
338
 
272
- `BookletPdfMeta`: `title`, `scopeLabel`, `personCount`, optional `referenceName`.
339
+ `BookletPdfMeta`: `title`, `scopeLabel`, `personCount`, optional `referenceName` (usually translated in the host app).
273
340
 
274
341
  ### Cover logo
275
342
 
276
- The cover draws the **gedcom-ts logo** from paths shipped in the package (`GEDCOM_TS_LOGO_PATHS`, `GEDCOM_TS_LOGO_VIEWBOX`). Reuse on custom PDF pages:
343
+ The cover draws the **gedcom-ts logo** from lazy-loaded SVG paths (`ensureBookletLogo`, `getGedcomTsLogoPaths`, `getGedcomTsLogoViewbox`). Reuse on custom PDF pages:
277
344
 
278
345
  ```ts
279
346
  import { PDFDocument } from "pdf-lib";
280
347
  import {
281
348
  drawGedcomTsLogoOnPage,
282
349
  defaultCoverLogoOptions,
283
- BOOKLET_BRAND_RGB,
350
+ ensureBookletLogo,
284
351
  } from "gedcom-ts/booklet";
285
352
 
286
353
  const doc = await PDFDocument.create();
287
354
  const page = doc.addPage();
288
- drawGedcomTsLogoOnPage(page, defaultCoverLogoOptions());
355
+ await ensureBookletLogo();
356
+ await drawGedcomTsLogoOnPage(page, defaultCoverLogoOptions());
289
357
  ```
290
358
 
291
359
  ### Booklet API cheat sheet
@@ -294,11 +362,72 @@ drawGedcomTsLogoOnPage(page, defaultCoverLogoOptions());
294
362
  | --- | --- |
295
363
  | Persons for the booklet | `collectBookletPersons` |
296
364
  | Chapters / families | `groupBookletIntoChapters`, `partnerNamesLabel` |
297
- | French narratives (custom UI) | `buildPersonSummaryNarrative`, `buildPersonDetailedNarratives`, `buildFamilyNarrative`, `buildChapterIntroNarrative` |
365
+ | Localized narratives (custom UI) | `buildPersonSummaryNarrative`, `buildPersonDetailedNarratives`, `buildFamilyNarrative`, `buildChapterIntroNarrative` |
298
366
  | Page estimate | `estimateBookletSize`, `bookletSizeAdvice` |
299
367
  | Timeline data / PNG | `buildGenerationTimeline`, `rasterizeGenerationTimelinePng` |
300
- | PDF output | `generateGenealogyBookletPdf`, `downloadBookletPdf`, `toPdfText` |
301
- | Logo | `drawGedcomTsLogoOnPage`, `defaultCoverLogoOptions`, `GEDCOM_TS_LOGO_PATHS` |
368
+ | PDF output | `generateGenealogyBookletPdf`, `downloadBookletPdf`, `toPdfText`, `preparePdfText` |
369
+ | Locale helpers | `resolveBookletLocale`, `bookletLocaleToBcp47`, `bookletTextDirection`, `BOOKLET_RTL_LOCALES`, `ensureBookletLocale` |
370
+ | Font preload (host) | `registerBookletPdfFontBytes`, `registerPdfFontkit`, `localeNeedsUnicodePdfFont` |
371
+ | Logo | `ensureBookletLogo`, `drawGedcomTsLogoOnPage`, `getGedcomTsLogoPaths`, `defaultCoverLogoOptions` |
372
+
373
+ ## Audio and oral history (GEDCOM 7)
374
+
375
+ Since **`2026.6.0`**, gedcom-ts models audio according to [FamilySearch GEDCOM 7](https://gedcom.io/): external files referenced by **`OBJE`** records (`FILE` + `FORM` + optional `MEDI AUDIO` + `TRAN` for alternate formats or WebVTT). Text transcriptions belong in **`NOTE`** under the event, not inside `OBJE`.
376
+
377
+ **Oral history pattern:** `1 EVEN` + `2 TYPE Interview` (or `Oral history`) + `2 OBJE @O…@` + `2 NOTE` (transcription).
378
+
379
+ ```ts
380
+ import {
381
+ createAudioInterviewAct,
382
+ editAct,
383
+ ExportGedzipFile,
384
+ type Person,
385
+ type ReadGed,
386
+ } from "gedcom-ts";
387
+
388
+ async function addInterview(readGed: ReadGed, person: Person, mp3: File) {
389
+ const objeMap = new Map(readGed.objeRecordsById);
390
+
391
+ const { act, objeRecord } = createAudioInterviewAct(
392
+ {
393
+ description: "Entretien avec grand-mère",
394
+ participantIndis: [person.INDI],
395
+ ownerIndi: person.INDI,
396
+ audioFile: mp3,
397
+ transcription: "Bonjour, je m'appelle Marie…",
398
+ vttUri: "media/transcript.vtt", // optional FILE.TRAN on OBJE
399
+ },
400
+ readGed.persons,
401
+ objeMap,
402
+ );
403
+
404
+ person.acts.add(act);
405
+ if (objeRecord) {
406
+ objeMap.set(objeRecord.id, objeRecord);
407
+ }
408
+
409
+ await new ExportGedzipFile("my-tree", readGed.persons, {
410
+ extraTopLevelRecords: readGed.preservedTopLevelRecords,
411
+ objeRecordsById: objeMap,
412
+ }).download();
413
+ }
414
+
415
+ // enrich an existing act:
416
+ editAct(act)
417
+ .asAudioInterview(readGed.persons, objeMap)
418
+ .setTranscription("Suite de l'entretien…")
419
+ .attachAudio({ audioUri: "https://example.org/rec.mp3", ownerIndi: person.INDI });
420
+ ```
421
+
422
+ | Goal | API |
423
+ | --- | --- |
424
+ | Create interview act | `createAudioInterviewAct` |
425
+ | Attach audio to act | `attachAudioInterviewToAct`, `editAct(act).asAudioInterview()` |
426
+ | Detect imported interviews | `isAudioInterviewAct`, `isAudioInterviewEvenType` |
427
+ | Read transcription / media | `getAudioInterviewTranscription`, `primaryAudioInterviewUri` |
428
+ | OBJE round-trip | `readGed.objeRecordsById` → `objeRecordsById` in export options |
429
+ | Low-level OBJE parse/emit | `parseStandaloneObjeBlock`, `formatObjeRecordBlock`, `guessMediFromForm` |
430
+ | EVEN type constants | `GEDCOM_7_EVEN_TYPE_AUDIO_INTERVIEW`, `GEDCOM_7_EVEN_TYPE_ORAL_HISTORY` |
302
431
 
303
432
  ## API reference
304
433
 
@@ -349,7 +478,7 @@ try {
349
478
 
350
479
  #### `ReadGed`
351
480
 
352
- Result of an import. Top-level GEDCOM records not modeled into the typed graph (`OBJE`, `REPO`, `SOUR`, `SUBM`, …) are kept in order on `readGed.preservedTopLevelRecords` for round-trip export.
481
+ Result of an import. Standalone `0 @O…@ OBJE` blocks with at least one `FILE` are parsed into **`objeRecordsById`** (multi-file, `MEDI`, `TRAN`). Other top-level records (`REPO`, `SOUR`, `SUBM`, …) stay on `preservedTopLevelRecords` for round-trip export.
353
482
 
354
483
  Notable members:
355
484
 
@@ -363,6 +492,7 @@ Notable members:
363
492
  | `placesMap: Map<string, Place>` | City name → `Place` (first occurrence wins). |
364
493
  | `mapFiles: Map<string, File>` | Relative path → media `File` (for ZIP imports). |
365
494
  | `datasetVersion: GedcomDatasetVersion` | `"7.0"`, `"5.5"` or `"unknown"`. |
495
+ | `objeRecordsById: Map<number, ObjeRecord>` | Parsed `OBJE` records (audio, images, …) keyed by `@O{n}@`. |
366
496
  | `preservedTopLevelRecords: string[]` | Raw blocks for partial round-trip. |
367
497
  | `resolveIndividualPointer(raw)` | Resolves `@I12@`, `I12`, `@Homer_Simpson@`, `Homer_Simpson` to a `Person`. |
368
498
  | `getChildrenForParent(parent)` | All children attached to a parent (via `FAMS`). |
@@ -404,6 +534,7 @@ import { ExportGedcomFile, type ReadGed } from "gedcom-ts";
404
534
  function exportGed(readGed: ReadGed) {
405
535
  new ExportGedcomFile("my-tree", readGed.persons, {
406
536
  extraTopLevelRecords: readGed.preservedTopLevelRecords,
537
+ objeRecordsById: readGed.objeRecordsById,
407
538
  headLanguageTag: "fr-FR",
408
539
  headCopyright: "© 2026 Family Archive",
409
540
  headDestination: "https://gedcom.io/",
@@ -432,6 +563,7 @@ Options shared by both exporters:
432
563
  | Option | Effect |
433
564
  | ---------------------- | -------------------------------------------------------------------------------------------------------- |
434
565
  | `extraTopLevelRecords` | Raw `0 …` blocks re-emitted before `SUBM` / `TRLR` (round-trip with `readGed.preservedTopLevelRecords`). |
566
+ | `objeRecordsById` | Full `OBJE` records (multi-`FILE`, `MEDI AUDIO`, `TRAN`) from `readGed.objeRecordsById`. |
435
567
  | `headLanguageTag` | BCP 47 tag for `HEAD`.`LANG` (defaults to `en-US`). |
436
568
  | `headCopyright` | One-line `1 COPR` notice. |
437
569
  | `headDestination` | Value of `HEAD`.`DEST` (target app / URI). |
@@ -577,7 +709,7 @@ const payload = place.toGedcom7PlacPayload(); // "Paris, , Île-de-France, Franc
577
709
 
578
710
  #### `MultimediaFile`, `MultimediaFiles`
579
711
 
580
- Wraps either a local `File`, an external URI (`sourceUri`) or an OBJE pointer (`objeXrefId`) so the same model can survive a GED / ZIP round-trip.
712
+ Wraps either a local `File`, an external URI (`sourceUri`) or an OBJE pointer (`objeXrefId`) so the same model can survive a GED / ZIP round-trip. See [Audio and oral history](#audio-and-oral-history-gedcom-7) for interview workflows.
581
713
 
582
714
  ```ts
583
715
  import { MultimediaFile, MultimediaFiles } from "gedcom-ts";
@@ -652,7 +784,8 @@ editDateAct(person.acts.list[0].dateAct!).setExactDate(1900, "JAN", 1);
652
784
  | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
653
785
  | `editPerson(person)` / `PersonEdit` | Identity, `FAMS` / `FAMC` / `removeFams*`, `nameVariants()` / `attributes()` / `multimedia()`, bulk `clear*`, entry points to `acts()` and `notes()`. |
654
786
  | `editActs(acts)` / `ActsEdit` | `add`, `addNew`, `insertAt`, `insertNewAt`, `replaceAt`, `removeAt`, `removeAct`, `removeLast`, `clear`, `sortByDate`, `at`, `indexOfAct`. |
655
- | `editAct(act)` / `ActEdit` | `setType`, `setIndis`, dates, place, EVEN fields, preserved lines, `notes()`, `multimedia()`, `clearNotes`, `clearMultimedia`. |
787
+ | `editAct(act)` / `ActEdit` | `setType`, `setIndis`, dates, place, EVEN fields, preserved lines, `notes()`, `multimedia()`, `asAudioInterview()`, `clearNotes`, `clearMultimedia`. |
788
+ | `editAudioInterviewAct(act)` / `AudioInterviewActEdit` | Oral-history facade: `attachAudio`, `setTranscription`, `setDescription`, `registerPendingObje`, `isAudioInterview`, … |
656
789
  | `editDateAct(dateAct)` / `DateActEdit` | `clear`, `applyGedcomPayload`, `setExactDate`, `setQualified`, `setBetween`, `setFromTo`, `setTime`, `setDatePhrase` / `appendDatePhrase`, `setVerbatimPayload`. |
657
790
  | `editPlace(place)` / `PlaceEdit` | `setFromGedcom7Payload`, `setCity`, `setCounty`, `setState`, `setCountry`, `setPlacPhrase` / `appendPlacPhrase` / `clearPlacPhrase`, `setCoordinates` / `clearCoordinates` / `replaceCoordinateModel`, `clearStructured`. |
658
791
  | `editNotes(notes)` / `NotesEdit` | `add`, `addNew`, `insertAt`, `insertNewAt`, `replaceAt`, `removeAt`, `removeNote`, `removeLast`, `clear`, `at`, `indexOfNote`. |
@@ -718,7 +851,7 @@ validateReadGed(readGed, {
718
851
  | Export | Role |
719
852
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
720
853
  | `editReadGed(readGed)` / `ReadGedEdit` | `addPerson`, `removePersonByIndi`, `preserved()` → `PreservedTopLevelEdit` (`append`, `insertAt`, `replaceAt`, `removeAt`, `clear`) on `preservedTopLevelRecords`. Low-level helpers: `addPersonToReadGed`, `removePersonFromReadGedByIndi`. |
721
- | `editGedcomExportOptions(opts)` / `GedcomExportOptionsEdit` | Fluent setters for `extraTopLevelRecords`, `headLanguageTag`, `headCopyright`, `headDestination`, `headSchemaTagDefs`. |
854
+ | `editGedcomExportOptions(opts)` / `GedcomExportOptionsEdit` | Fluent setters for `extraTopLevelRecords`, `objeRecordsById`, `headLanguageTag`, `headCopyright`, `headDestination`, `headSchemaTagDefs`. |
722
855
  | `clonePerson(person, newIndi)` / `cloneAct(act)` / `person.clone` / `act.clone` | Deep copies for templates or undo stacks. |
723
856
  | `nextFamilyId(persons)` | Next internal family id `F`. |
724
857
  | `createMarriageFamily`, `linkChildToFamily`, `unlinkChildFromFamily`, `removeFamilyReferencesFromDataset` | Create family `F` and spouse acts; optional `{ eventTag }` (default `MARR`, also `ENGA`, banns, contract, `EVEN` + `CreateActInit`, …). See `GEDCOM_7_PAIR_UNION_EVENT_TAGS`. |
@@ -765,12 +898,13 @@ const version = resolveDatasetVersion(headLines); // "7.0" | "5.5" | "unknown"
765
898
 
766
899
  #### `guessMediaFormFromUri(uri)`
767
900
 
768
- Best-effort media type detection from a path/URI (jpg, png, gif, webp, mp3, mp4, pdf, …). Used internally to fill `OBJE`.`FILE`.`FORM`.
901
+ Best-effort media type detection from a path/URI or local `File` (`file.type` when set). Covers images, **audio** (mp3, **webm**, ogg, wav, m4a, flac, …), video, WebVTT, PDF. Used to fill `OBJE`.`FILE`.`FORM`. Pair with `guessMediFromForm` for `MEDI` (`AUDIO`, `PHOTO`, …). `refineGenericObjeForm` upgrades legacy `application/octet-stream` exports using the file extension.
769
902
 
770
903
  ```ts
771
- import { guessMediaFormFromUri } from "gedcom-ts";
904
+ import { guessMediaFormFromUri, guessMediaFormFromFile } from "gedcom-ts";
772
905
 
773
- guessMediaFormFromUri("https://example.com/photo.jpg"); // "image/jpeg"
906
+ guessMediaFormFromUri("media/recording.webm"); // "audio/webm"
907
+ guessMediaFormFromFile(new File(["x"], "take.webm", { type: "audio/webm" })); // "audio/webm"
774
908
  ```
775
909
 
776
910
  #### `extractPersonNameVariants(personLines)` / `extractIndiAttributes(personLines)`
@@ -796,7 +930,7 @@ const primary = selectPrimaryNameVariant(person.nameVariants);
796
930
 
797
931
  #### `GEDCOM_LIBRARY_VERSION`
798
932
 
799
- CalVer string written in exported `HEAD`.`SOUR`.`VERS` (same value as the npm package version, e.g. `2026.5.2`).
933
+ CalVer string written in exported `HEAD`.`SOUR`.`VERS` (same value as the npm package version, e.g. `2026.6.0`).
800
934
 
801
935
  ```ts
802
936
  import { GEDCOM_LIBRARY_VERSION } from "gedcom-ts";
@@ -0,0 +1,9 @@
1
+ export declare const BOOKLET_UNKNOWN_DATE_EN = "Unknown date";
2
+ /** Year-only date (optional GEDCOM qualifier). */
3
+ export declare function isYearOnlyDate(dateLabel: string): boolean;
4
+ /** "on 15 March 1991", "in 1991", "about 1700", "before 1800", etc. */
5
+ export declare function onDate(dateLabel: string): string;
6
+ /** "From 1700 to 1850" or "From 15 March 1700 to 3 January 1850". */
7
+ export declare function lifeDateRange(from: string, to: string): string;
8
+ /** "was born in 1991" or "was born on 15 March 1991". */
9
+ export declare function seesDayOn(dateLabel: string): string;
@@ -0,0 +1,21 @@
1
+ export interface BookletDateConfig {
2
+ readonly unknownDate: string;
3
+ readonly onUnknown: string;
4
+ readonly inYear: (year: string) => string;
5
+ readonly beforeYear: (year: string) => string;
6
+ readonly afterYear: (year: string) => string;
7
+ readonly aboutYear: (year: string) => string;
8
+ readonly onFullDate: (date: string) => string;
9
+ readonly lifeRange: (from: string, to: string, fromIsYearOnly: boolean) => string;
10
+ readonly seesDayInYear: (year: string) => string;
11
+ readonly seesDayOnPhrase: (phrase: string) => string;
12
+ readonly seesDayOnFullDate: (date: string) => string;
13
+ }
14
+ export interface BookletDateModule {
15
+ readonly BOOKLET_UNKNOWN_DATE: string;
16
+ isYearOnlyDate(dateLabel: string): boolean;
17
+ onDate(dateLabel: string): string;
18
+ lifeDateRange(from: string, to: string): string;
19
+ seesDayOn(dateLabel: string): string;
20
+ }
21
+ export declare function createBookletDateModule(config: BookletDateConfig): BookletDateModule;
@@ -1,3 +1,4 @@
1
+ export declare const BOOKLET_UNKNOWN_DATE_FR = "Date inconnue";
1
2
  /** Date réduite à une année (éventuellement avec qualificateur GEDCOM). */
2
3
  export declare function isYearOnlyDate(dateLabel: string): boolean;
3
4
  /** « le 15 mars 1991 », « en 1991 », « vers 1700 », « avant 1800 »… */
@@ -0,0 +1,6 @@
1
+ import type { BookletLocale } from "./booklet-locale";
2
+ export declare function unknownDateLabel(locale?: BookletLocale): string;
3
+ export declare function isYearOnlyDate(dateLabel: string, locale?: BookletLocale): boolean;
4
+ export declare function onDate(dateLabel: string, locale?: BookletLocale): string;
5
+ export declare function lifeDateRange(from: string, to: string, locale?: BookletLocale): string;
6
+ export declare function seesDayOn(dateLabel: string, locale?: BookletLocale): string;
@@ -1,11 +1,12 @@
1
1
  import type { BookletTimelineStyle } from './booklet-timeline';
2
2
  import type { BookletChapter, BookletDetailLevel } from './booklet-structure';
3
+ import type { BookletLocale } from './booklet-locale';
3
4
  export interface BookletSizeEstimate {
4
5
  readonly pages: number;
5
6
  readonly label: string;
6
7
  }
7
8
  /** Nombre de pages frise estimé pour un chapitre (tranches dynamiques). */
8
9
  export declare function estimateTimelineSliceCount(rowCount: number): number;
9
- /** Estimation grossière du nombre de pages pour orienter lutilisateur. */
10
- export declare function estimateBookletSize(chapters: readonly BookletChapter[], personCount: number, familyCount: number, detailLevel: BookletDetailLevel, timelineStyle?: BookletTimelineStyle): BookletSizeEstimate;
11
- export declare function bookletSizeAdvice(personCount: number, detailLevel: BookletDetailLevel): string | null;
10
+ /** Estimation grossière du nombre de pages pour orienter l'utilisateur. */
11
+ export declare function estimateBookletSize(chapters: readonly BookletChapter[], personCount: number, familyCount: number, detailLevel: BookletDetailLevel, timelineStyle?: BookletTimelineStyle, locale?: BookletLocale): BookletSizeEstimate;
12
+ export declare function bookletSizeAdvice(personCount: number, detailLevel: BookletDetailLevel, locale?: BookletLocale): string | null;
@@ -0,0 +1,3 @@
1
+ import type { BookletSex, PersonGenderWords } from './booklet-gender';
2
+ export declare const WORDS_EN: Record<BookletSex, PersonGenderWords>;
3
+ export declare const WORDS_FR: Record<BookletSex, PersonGenderWords>;
@@ -0,0 +1,9 @@
1
+ import type { BookletLocale } from './booklet-locale';
2
+ import type { BookletSex, PersonGenderWords } from './booklet-gender';
3
+ export interface GenderPhraseBundle {
4
+ readonly recensedChildren: (count: number) => string;
5
+ readonly otherUnions: (count: number) => string;
6
+ readonly usesPluralS: boolean;
7
+ }
8
+ export declare const GENDER_WORDS: Partial<Record<BookletLocale, Record<BookletSex, PersonGenderWords>>>;
9
+ export declare const GENDER_PHRASES: Partial<Record<BookletLocale, GenderPhraseBundle>>;
@@ -1,3 +1,4 @@
1
+ import type { BookletLocale } from './booklet-locale';
1
2
  /** Sexe GEDCOM exploité pour les accords du livret (M, F, U, X). */
2
3
  export type BookletSex = 'M' | 'F' | 'U' | 'X';
3
4
  export interface PersonGenderWords {
@@ -11,13 +12,11 @@ export interface PersonGenderWords {
11
12
  readonly childOf: string;
12
13
  readonly parentOf: string;
13
14
  }
14
- /** Participes / noms accordés au genre de la personne. U et X formes neutres. */
15
- export declare function personGenderWords(sex: BookletSex): PersonGenderWords;
16
- /** Suffixe pluriel français (0 ou 1 → singulier). */
17
- export declare function pluralS(count: number): string;
18
- /** Choisit la forme singulier / pluriel. */
15
+ /** Participles / role words aligned with the person's sex. U and X use neutral forms. */
16
+ export declare function personGenderWords(sex: BookletSex, locale?: BookletLocale): PersonGenderWords;
17
+ /** Plural suffix (French-style locales: 0 or 1 → singular; English: unused). */
18
+ export declare function pluralS(count: number, locale?: BookletLocale): string;
19
+ /** Singular / plural pick. */
19
20
  export declare function pluralPick<T>(count: number, one: T, many: T): T;
20
- /** « 1 enfant recensé » / « N enfants recensés ». */
21
- export declare function recensedChildrenPhrase(count: number): string;
22
- /** « 1 autre union citée… » / « N autres unions citées… ». */
23
- export declare function otherUnionsPhrase(count: number): string;
21
+ export declare function recensedChildrenPhrase(count: number, locale?: BookletLocale): string;
22
+ export declare function otherUnionsPhrase(count: number, locale?: BookletLocale): string;
@@ -0,0 +1,4 @@
1
+ /** Shared place-prefix helpers for locale date/place modules. */
2
+ export declare function hasCountryPrefix(core: string, prefixes: readonly string[]): boolean;
3
+ export declare function prefixedLocation(core: string, prefixes: readonly string[], elsePrefix: string): string;
4
+ export declare function commaPrefixedLocation(core: string, prefixes: readonly string[], elsePrefix: string): string;
@@ -0,0 +1,27 @@
1
+ import type { BookletDateModule } from './booklet-date-factory';
2
+ import type { BookletPlaceModule } from './booklet-place-factory';
3
+ import type { BookletSex, PersonGenderWords } from './booklet-gender';
4
+ import type { GenderPhraseBundle } from './booklet-gender-data';
5
+ import type { BookletLocale } from './booklet-locale';
6
+ import type { BookletMessages } from './booklet-messages-types';
7
+ import type { NarrativeModule } from './booklet-narrative-engine';
8
+ export interface BookletLocaleBundle {
9
+ readonly messages: BookletMessages;
10
+ readonly genderWords: Record<BookletSex, PersonGenderWords>;
11
+ readonly genderPhrases?: GenderPhraseBundle;
12
+ readonly dateModule: BookletDateModule;
13
+ readonly placeModule: BookletPlaceModule;
14
+ readonly narrativeModule: NarrativeModule;
15
+ }
16
+ export declare class BookletLocaleNotLoadedError extends Error {
17
+ constructor(locale: BookletLocale);
18
+ }
19
+ /** Clears cached locale bundles (tests). */
20
+ export declare function clearBookletLocaleCache(): void;
21
+ /**
22
+ * Lazy-loads messages, gender, date/place, and narrative modules for one locale.
23
+ * Required before sync APIs (`getBookletMessages`, `collectBookletPersons`, …).
24
+ */
25
+ export declare function ensureBookletLocale(locale?: BookletLocale): Promise<BookletLocale>;
26
+ export declare function getBookletLocaleBundle(locale: BookletLocale): BookletLocaleBundle;
27
+ export declare function isBookletLocaleLoaded(locale: BookletLocale): boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { BookletLocale } from './booklet-locale';
2
+ export declare function getBookletDateModule(locale: BookletLocale): import("./booklet-date-factory").BookletDateModule;
3
+ export declare function getBookletPlaceModule(locale: BookletLocale): import("./booklet-place-factory").BookletPlaceModule;
4
+ export declare function getBookletDateModuleIfLoaded(locale?: BookletLocale): import("./booklet-date-factory").BookletDateModule;
5
+ export declare function getBookletPlaceModuleIfLoaded(locale?: BookletLocale): import("./booklet-place-factory").BookletPlaceModule;
@@ -0,0 +1,15 @@
1
+ /** Booklet narrative / PDF locales (aligned with gedcomts.com site languages). */
2
+ export type BookletLocale = 'en' | 'fr' | 'de' | 'nl' | 'es' | 'zh' | 'it' | 'pt' | 'pl' | 'ar' | 'he';
3
+ export declare const DEFAULT_BOOKLET_LOCALE: BookletLocale;
4
+ export declare const BOOKLET_LOCALES: readonly BookletLocale[];
5
+ /** Locales with full booklet copy (messages, dates, places, gender, narratives). */
6
+ export declare const BOOKLET_LOCALES_IMPLEMENTED: readonly BookletLocale[];
7
+ export declare const BOOKLET_RTL_LOCALES: readonly BookletLocale[];
8
+ export declare function isBookletLocale(value: string | null | undefined): value is BookletLocale;
9
+ export declare function resolveBookletLocale(locale?: BookletLocale): BookletLocale;
10
+ export declare function isBookletLocaleRtl(locale?: BookletLocale): boolean;
11
+ export declare function bookletTextDirection(locale?: BookletLocale): 'ltr' | 'rtl';
12
+ /** BCP 47 tag for `toLocaleDateString` on the PDF cover. */
13
+ export declare function bookletLocaleToBcp47(locale: BookletLocale): string;
14
+ /** `Intl.Collator` / `localeCompare` tag for chapter name ordering. */
15
+ export declare function bookletLocaleCompareTag(locale: BookletLocale): string;
@@ -0,0 +1,8 @@
1
+ import type { PDFPage } from 'pdf-lib';
2
+ import type { DrawGedcomTsLogoOptions, GedcomTsLogoLayout } from './booklet-logo';
3
+ export declare function clearBookletLogoCache(): void;
4
+ /** Loads SVG logo paths and draw helper (only when the cover logo is shown). */
5
+ export declare function ensureBookletLogo(): Promise<void>;
6
+ export declare function drawGedcomTsLogoOnPage(page: PDFPage, options?: DrawGedcomTsLogoOptions): Promise<GedcomTsLogoLayout>;
7
+ export declare function getGedcomTsLogoPaths(): Promise<readonly string[]>;
8
+ export declare function getGedcomTsLogoViewbox(): Promise<string>;