- Astro statique, EN par defaut + /fr/ + /nl/, detection langue navigateur - i18n par fichiers JSON, ajouter une langue = ajouter des traductions - Contenu original porte a l'identique (diff d'inventaire par langue) - Chronologie legislative corrigee sur sources primaires (PE, Conseil, votes) - Timeline 23 precedents + observatoire 35 items + annuaire 66 outils FOSS, chaque affirmation verifiee et sourcee - Zero requete tierce (teste), lisible JS coupe, axe WCAG AA x2 themes - Version offline monofichier par langue a chaque build - Docker multi-stage vers nginx + CSP stricte auto-generee - CI GitHub Actions (SHA-pinnees) + verification hebdo des liens - CONTRIBUTING : divulgation d'affiliation obligatoire, allowlist de domaines testee en CI
8.0 KiB
Content-migration diff report
Legacy monolith index.html (591 tri-lingual span lines / 659 data-l spans per
language) → per-locale MDX under src/content/sections/{en,fr,nl}/ + src/data/tools.json.
How the inventories were produced
legacy-<lang>.txt— the FULL legacy document reduced to one language (everydata-lrun resolved), one line of whitespace-collapsed text per block-level element. Chrome (topbar, hero, status banner, TOC, footer) is included so nothing is hidden from this audit.built-<lang>.txt— same dump algorithm overdist/index.html,dist/fr/index.html,dist/nl/index.htmlafterpnpm build. One deviation:<noscript>is skipped because parse5 exposes its children as raw text (it is new chrome, absent from the legacy page anyway).<b class="lbl">/<b class="tool-label">elements are flushed to their own line on both sides so tool-card prose lines stay comparable.- Non-breaking spaces are folded into plain spaces on both sides.
node migration/compare.mjsthen segments each file into head-chrome / narrative window / tail-chrome (narrative = firstPART 00 ·line through the manifesto paragraph) and walks the two narrative windows in order, line by line. Every line must either match exactly or fall in one of the categories below; anything else exits non-zero.
Result
| lang | legacy narrative lines | built narrative lines | unexplained differences |
|---|---|---|---|
| en | 789 | 991 | 0 |
| fr | 789 | 991 | 0 |
| nl | 789 | 991 | 0 |
991 = 789 − 6 (deferred legislative rows) + 208 (tool-card labels) in every
language. Full machine output: compare-report.json.
Explained differences, exhaustively
1. Tool-card labels are now real text (+208 lines per language)
In the legacy page the field labels ("What it's for / Why it matters /
For whom & when", À quoi ça sert / Pourquoi / Pour qui & quand,
Waarvoor dient het / …) and the Install & use summary were CSS
::before/::after generated content, so they never appeared in the DOM
text. ToolCard.astro renders them as real, accessible text:
54 cards × 3 field labels + 46 install summaries = 208 new lines per language.
The label strings are byte-identical to the legacy CSS content values.
2. Deferred legislative-status claims (−6 lines per language)
Per the migration plan, adopted/extended claims are being rewritten from
verified sources separately. Dropped from the #menace comparison table
(rows 4 and 5), verbatim:
- EN:
Duration/Temporary, extended to 3 April 2028/Permanent/Status (Jul 2026)/Adopted 9 July 2026/Under negotiation, 5th trilogue failed 29 June - FR:
Durée/Temporaire, prolongé jusqu'au 3 avril 2028/Permanent/Statut (juil. 2026)/Adopté le 9 juillet 2026/En négociation, 5e trilogue échoué le 29 juin - NL:
Looptijd/Tijdelijk, verlengd tot 3 april 2028/Permanent/Status (juli 2026)/Aangenomen op 9 juli 2026/In onderhandeling, 5e trialoog mislukt op 29 juni
The status banner ("Dernière minute : 9 juillet 2026" block) was excluded
wholesale with the chrome (see §4); it is replaced by StatusBanner.astro
fed from verified sources in the parallel workstream.
Two borderline in-prose date claims were kept (judgment call — they are
narrative argument, not adopted/extended status): the 19 March 2026 joint
appeal, and the "kept scanning after the legal basis lapsed on 4 April 2026"
sentence, both in #menace.
3. Typographic quotes (smartypants)
Astro's default markdown pipeline (markdown.smartypants, ON by default)
typesets " as “ ” and ' as ’ in the rendered pages
(en: 138 lines affected, fr: 225, nl: 70). No other smartypants rule fires:
the legacy prose contains no ASCII ... or -- (its 3 … are literal and
pass through unchanged). This is the only character-level transformation
in the entire narrative.
It could not be disabled from inside this migration's ownership
(astro.config.mjs is off-limits). If byte-identical glyphs are required,
the config owner can add markdown: { smartypants: false } — one line — and
the compare gate can then drop its quote normalization.
4. Chrome not migrated (rebuilt separately)
Legacy head-chrome (36 lines/language) and tail-chrome (8 lines/language) deliberately not migrated to MDX:
- topbar: brand
◆ EXIT CHAT CONTROL, FR/NL/EN buttons, theme toggle; - hero: stamp (
Mass surveillance · EU · 2026), eyebrow,Exit Chat Control_h1, lede paragraph, three profile meta tags; - status banner:
Breaking: 9 July 2026+ the vote paragraph (superseded content, see §2); - TOC (24 lines) — duplicates the section titles that ARE migrated;
- footer: Resources / Sources / Act link lists, GitHub link, disclaimer
(
Facts last updated: 9 July 2026).
The built pages already contain replacement chrome from the parallel rebuild workstream (TopBar/Footer/StatusBanner components, new hero h1 + description, skip-link, noscript note, rewritten source lists citing official roll-call results). That text intentionally differs from the legacy chrome and is not part of this migration's fidelity claim; it sits outside the compared narrative window (built head-chrome: 6 lines, tail-chrome: 9 lines).
- The legacy inline JS (lang/theme toggles, simpleicons CDN loader) was not
migrated by design. Icon rendering will be rebuilt offline later; the
legacy CDN slugs are preserved in
tools.json.iconSlug(validated, §6).
5. NL gaps
None. Every one of the 649 data-l runs carries all three languages
(en/fr/nl counts are equal in every run), so the EN-fallback path never
fired. extract-log.json → nlGaps: [].
6. tools.json notes (language-neutral facts)
- 54 entries; every entry has
url(first neutral footer link), fulllinksarray (21 cards have 2–3 links),monogram,color,profiles(the 🟢🟡🔴 footer note),levelwhere the legacy card carried a level tag (34 cards; the other 20 never had one). iconSlugwas validated against the installedsimple-iconspackage. Renamed upstream and mapped:tutanota → tuta,aegis → aegisauthenticator. Removed upstream and therefore set to null (6):briar,molly,ivpn,protonpass,nostr,standardnotes. 5 cards never had an icon in the legacy map (t-bitchat,t-databrokers,t-mailbox,t-website,t-yunohost).- 7 cards have localized display names (e.g.
Firefox (durci/hardened/gehard),Sauvegardes chiffrées / Encrypted backups / Versleutelde back-ups).tools.json.namestores the EN plain text; the localized display name is rendered through the ToolCardnameslot from each locale's MDX. - One presentational attribute dropped:
style="margin:.6rem 0 .4rem"on the warning box insidet-bitchat(styling is rebuilt later; text intact).
7. Anchors
anchors.json lists all 82 id= values of the legacy file. 78 exist in
every built page: 23 section ids on <section> wrappers, 54 tool ids on
ToolCard <article> roots, top on the hero h1. The 4 missing are the
legacy control-widget ids lang-fr, lang-nl, lang-en, theme
(toggle buttons, never used as link targets — chrome rebuilt separately).
8. External domains for the allowlist review
The migrated content links to 75 unique external hostnames
(external-domains.txt). The parallel workstream's
tests/unit/dist-links.test.ts fails until these are reviewed and added to
src/data/domains-allowlist.json — that file requires review and is outside
this migration's ownership, so it was left untouched.
Reproduce
node migration/extract.mjs # MDX + tools.json + anchors + legacy dumps
pnpm build
node migration/extract.mjs --built # built-page dumps
node migration/compare.mjs # exits non-zero on any unexplained diff