fix(localization): Memo Back/Face EN/RU/PT StaticConversions (#358) - #443
Conversation
…localization (#358) Memo templates have two FR-only elements that FrontFieldConversions cannot handle: 1. Hardcoded subtitle 'L art de jamais avoir tort' — not a {{variable}}, so template.Replace with FormatField patterns misses it. 2. Bare text_fr in {{#ifCond Famille "==" text_fr }} — the trailing space before }} prevents FormatField (which appends }}) from matching. Solution: StaticConversions on the shared Fallacies+Memo CardSetLocalization entry (Memo is already listed at lines 106-107). Two conversions added: - Subtitle: FR → EN/RU/PT/ES/AR/FA/ZH translations - ifCond bare text_fr: text_fr<space> → text_en<space>/text_ru<space>/text_pt<space> Safe for Fallacies templates (no-op: neither string appears in them). #216 compliance: verified target field names exist in Memo templates; FormatField('Famille') = 'Famille}}' does NOT collide with 'Famille_camelCase}}'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] LGTM — clean StaticConversions for hardcoded FR text.
The trailing-space approach on text_fr is well-documented and avoids false positives on {{text_fr}} (already handled by FrontFieldConversions). Seven languages covered, Memo-only scope (Fallacies grep-verified as no-op).
Minor note: visual validation in PDF pipeline post-merge would be ideal to confirm the ifCond rendering actually switches correctly — the PR body already flags this.
…version (#443 review) Two bugs found by ai-01 live-engine validation (PR #443 review comment): Bug 1 — apostrophe codepoint mismatch (silent no-op, #216 class): - Template Memo_Back uses U+0027 (straight apostrophe) in subtitle. - StaticConversions source string used U+2019 (curly apostrophe). - template.Replace() found nothing -> subtitle stayed FR in EN/RU/PT. Fix: U+2019 -> U+0027 in the source string. Bug 2 — text_fr StaticConversion breaks family grouping: - ifCond selector {{#ifCond Famille "==" text_fr }} compares Famille(FR) with text_fr(FR) to detect family boundary rows. - Converting text_fr -> text_en makes it compare Famille(FR) == text_en(EN), which only matches 2/8 families (Influence/Obstruction share FR/EN spelling). - 6/8 family sections disappear in localized Memo. Fix: remove the entire text_fr StaticConversion block. The selector must stay FR-invariant; labels localize separately via FrontFieldConversions. Added 3 regression tests (Memo_Back_Template_Subtitle_Is_Translated_And_Selector_Stays_FR) for EN/RU/PT asserting: (a) no FR subtitle, (b) text_fr still in ifCond. Updated docs/publication/validation-guide.fr.md §2ter: Memo Back question resolved (decision jsboige: localize, ref PR #443). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] Still LGTM — both post-review fixes are correct and well-motivated.
What changed since 70ce9bb review:
-
Apostrophe codepoint fix (U+2019 -> U+0027). Classic #216-class silent no-op:
Replace()found nothing because the source string had a curly apostrophe while the template uses a straight one. Clean fix — align source to template. -
text_fr StaticConversion removed entirely. This is the important one. Converting
text_fr->text_enin the ifCond selector made it compare Famille(FR) == text_en(EN), matching only 2/8 families (cognate overlap). Removing it keeps the selector FR-invariant, which is the correct behavior — labels localize separately via FrontFieldConversions. The reasoning is solid. -
3 regression tests (Theory for en/ru/pt) asserting both invariants: subtitle translated AND ifCond selector stays FR. Good coverage.
-
Docs updated — validation-guide marks the Memo Back editorial question as resolved.
No concerns. The two bugs caught by live validation were real and the fixes are minimal and targeted. The StaticConversions approach still holds — it is just that the text_fr entry should never have been there. Remaining translations (subtitle only, 7 languages) are unaffected and correct.
Security scan: clean.
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] Still LGTM — both post-review fixes are correct and well-motivated.
What changed since 70ce9bb review:
-
Apostrophe codepoint fix (U+2019 -> U+0027). Classic #216-class silent no-op:
Replace()found nothing because the source string had a curly apostrophe while the template uses a straight one. Clean fix — align source to template. -
text_fr StaticConversion removed entirely. This is the important one. Converting
text_fr->text_enin the ifCond selector made it compare Famille(FR) == text_en(EN), matching only 2/8 families (cognate overlap). Removing it keeps the selector FR-invariant, which is the correct behavior — labels localize separately via FrontFieldConversions. The reasoning is solid. -
3 regression tests (Theory for en/ru/pt) asserting both invariants: subtitle translated AND ifCond selector stays FR. Good coverage.
-
Docs updated — validation-guide marks the Memo Back editorial question as resolved.
No concerns. The two bugs caught by live validation were real and the fixes are minimal and targeted. The StaticConversions approach still holds — it is just that the text_fr entry should never have been there. Remaining translations (subtitle only, 7 languages) are unaffected and correct.
Security scan: clean.
…p) (#446) The Mémo Back card kept its taxonomy labels (Famille / Sous-Famille / Soussousfamille) in French for EN/RU/PT because the Memo BackFieldConversions only carried tagline_fr. At runtime the Back is rendered through BackFieldConversions (TranslateCardSetInfo, front:false), so its taxonomy display tokens were never localized — only the subtitle (via StaticConversions) and the Face (via FrontFieldConversions) were. Add Soussousfamille/Sous-Famille/Famille → Family columns to the Memo BackFieldConversions, most-specific-first so "Famille}}" cannot clobber "Sous-Famille}}". Safe for the FR-invariant ifCond family selector: FormatField appends "}}" with NO space, so it matches the {{Famille}} display tokens but not the `Famille "=="` operand nor {{Famille_camelCase}} CSS class — the data-driven 8/8 family grouping stays untouched. Adds FallaciesLocalizationTests regression coverage that applies BackFieldConversions (not Front) to the real Memo Back template for EN/RU/PT, asserting taxonomy tokens localize while the grouping selector stays FR, plus a config-shape ordering test. Full suite: 155 pass / 0 fail. Refs #358 #435 #443 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…p) (#446) The Mémo Back card kept its taxonomy labels (Famille / Sous-Famille / Soussousfamille) in French for EN/RU/PT because the Memo BackFieldConversions only carried tagline_fr. At runtime the Back is rendered through BackFieldConversions (TranslateCardSetInfo, front:false), so its taxonomy display tokens were never localized — only the subtitle (via StaticConversions) and the Face (via FrontFieldConversions) were. Add Soussousfamille/Sous-Famille/Famille → Family columns to the Memo BackFieldConversions, most-specific-first so "Famille}}" cannot clobber "Sous-Famille}}". Safe for the FR-invariant ifCond family selector: FormatField appends "}}" with NO space, so it matches the {{Famille}} display tokens but not the `Famille "=="` operand nor {{Famille_camelCase}} CSS class — the data-driven 8/8 family grouping stays untouched. Adds FallaciesLocalizationTests regression coverage that applies BackFieldConversions (not Front) to the real Memo Back template for EN/RU/PT, asserting taxonomy tokens localize while the grouping selector stays FR, plus a config-shape ordering test. Full suite: 155 pass / 0 fail. Refs #358 #435 #443 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…le caveat (#448) §2ter updated to reflect that #438/#439/#443/#446 are now MERGED on master (dc01445), not on feature branches. Records the 2026-06-08 full-regen Debug validation: 6/6 CardSets PASS, Mémo Back localized 8/8 (incl. AR/ES/FA/ZH = no CSV gap), confirming #446 on the merged deliverable. Adds a verified caveat: the local Release build (2026-06-02) predates #443/#446, so its Mémo Back is stale FR (native crop confirms FR subtitle + FR taxonomy). The final -c Release regen must be re-run on dc01445 with explicit Memo harvest clobber before any sign-off/tag. §1 build-selection note flagged accordingly. Refs #140 #134 #443 #446 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…uping (#449) The Mémo Back card collapsed from the spec 7×3×3 taxonomy grid (7 families × 3 sub-families × 3 sub-sub-families) to an irregular shape. Root cause: the template grouped the grid via name-coincidence markers ({{#ifCond Famille "==" text_fr}} …) that fired only on rows whose text_fr happened to equal their own level label. FR rename campaigns (#351 ×27, #308 ×13) changed text_fr → the coincidences vanished → families collapsed (Tricherie sub-families = 0, Erreur math/raisonnement = 1, …). The taxonomy DATA is correct; only the template's grouping was broken. Fix (lane ai-01 = TEMPLATE, data is fine): - CardPen main.js: add 3 control-break helpers reading the language-invariant FR taxonomy columns (Sous-Famille / Soussousfamille), present in every row regardless of render language. They carry NO column token, so the localizer (CardSetLocalization.FormatField appends "}}") cannot corrupt the helper call: ifFamilyHeader = Sous-Famille=='' && Soussousfamille=='' ifSubfamilyHeader= Sous-Famille!='' && Soussousfamille=='' ifLeafFirst = Soussousfamille!='' on first occurrence (control-break) - Argumentum_Memo_Back_fr.json: swap the 3 name-coincidence conditions for the new helpers (display tokens {{Famille}}/{{Sous-Famille}}/{{Soussousfamille}} unchanged → localization preserved); fix colorPalette to iterate the rowset and emit one swatch per family-root (was fixed indices 0/25/50/…/150, which duplicated Influence and omitted Obstruction). Single shared template → fix propagates to all 8 languages. Validated three ways on the real injected data (taxonomy WHERE carte∈{1,2}, 176 rows): (1) Node + vendored Handlebars → 7 families / 21 sub-families / 63 leaves; (2) OLD-template render proving the regression (10/53, Tricherie=0); (3) LIVE CardPen engine (Playwright) → 7×3×3 DOM + 7 distinct palette colors (Obstruction red present, no Influence duplicate) + viewed screenshot. Refs #443 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…-up) (#450) * fix(memo): #443 Mémo Back grid — language-invariant control-break grouping The Mémo Back card collapsed from the spec 7×3×3 taxonomy grid (7 families × 3 sub-families × 3 sub-sub-families) to an irregular shape. Root cause: the template grouped the grid via name-coincidence markers ({{#ifCond Famille "==" text_fr}} …) that fired only on rows whose text_fr happened to equal their own level label. FR rename campaigns (#351 ×27, #308 ×13) changed text_fr → the coincidences vanished → families collapsed (Tricherie sub-families = 0, Erreur math/raisonnement = 1, …). The taxonomy DATA is correct; only the template's grouping was broken. Fix (lane ai-01 = TEMPLATE, data is fine): - CardPen main.js: add 3 control-break helpers reading the language-invariant FR taxonomy columns (Sous-Famille / Soussousfamille), present in every row regardless of render language. They carry NO column token, so the localizer (CardSetLocalization.FormatField appends "}}") cannot corrupt the helper call: ifFamilyHeader = Sous-Famille=='' && Soussousfamille=='' ifSubfamilyHeader= Sous-Famille!='' && Soussousfamille=='' ifLeafFirst = Soussousfamille!='' on first occurrence (control-break) - Argumentum_Memo_Back_fr.json: swap the 3 name-coincidence conditions for the new helpers (display tokens {{Famille}}/{{Sous-Famille}}/{{Soussousfamille}} unchanged → localization preserved); fix colorPalette to iterate the rowset and emit one swatch per family-root (was fixed indices 0/25/50/…/150, which duplicated Influence and omitted Obstruction). Single shared template → fix propagates to all 8 languages. Validated three ways on the real injected data (taxonomy WHERE carte∈{1,2}, 176 rows): (1) Node + vendored Handlebars → 7 families / 21 sub-families / 63 leaves; (2) OLD-template render proving the regression (10/53, Tricherie=0); (3) LIVE CardPen engine (Playwright) → 7×3×3 DOM + 7 distinct palette colors (Obstruction red present, no Influence duplicate) + viewed screenshot. Refs #443 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(rules): index-independent heading CSS + cover titles (#438 follow-up) The 24->15 Rules deck restructure (#438) broke the heading CSS, which keyed banners and number-boxes to card INDEX (nth-of-type + hardcoded content '01'..'05' + [class~="4"]). After the restructure those indices were wrong: 1. «Déroulé de la manche» rendered in a colored banner instead of a plain big title like «Règles du jeu…». 2. «Argumentum» rendered big on the sub-game cover cards instead of the rule name. 3. Inconsistent paragraph headers: empty floating number boxes + size mismatches on cards 6/10/12/14/15. Fix (index-independent, semantic headings): - h2:first-child = card main title -> plain, big, no banner - other h2 = section sub-head -> plain, thin theme-colored underline - h3 = numbered step -> theme-colored banner; the «1.» number is already in the heading text, so no separate (broken) box - removed the leading «# Argumentum» line from the 4 sub-game cover rows (Rules_07/09/11/13) across all 8 language columns so «## <rule name>» becomes the title; Rules_01 (main cover) keeps «# Argumentum». - tightened heading margins/padding so the densest cards do not overflow worse than the committed baseline. Validated live on the real CardPen engine (all 15 cards): zero bannered titles, covers lead with the rule name, uniform theme-colored step banners; total text overflow reduced 208px -> 110px vs baseline (cards 11/13 fully un-clipped, only card 12 +1px = parity). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…454) * feat(i18n): add AR/FA/ZH language support for MindMap generation (#134 8-lang scope) - Add AR/FA/ZH properties + CsvHelper mappings to Fallacy.cs entity (FamilyAr/Fa/Zh, SubfamilyAr/Fa/Zh, TextAr/Fa/Zh, DescAr/Fa/Zh, ExampleAr/Fa/Zh, LinkAr/Fa/Zh) - Extend MindMapLocalization StaticConversions in AssetConverterConfig.cs to cover AR/FA/ZH for text, desc, example, link, and family hierarchy - Add ES/AR/FA/ZH to FallacyMindMapCreatorConfig Translations list - Add ES/AR/FA/ZH to VirtueMindMapCreatorConfig Translations list - Update TaxonomyValidationTests _supportedLanguages from 4 to 8 CSV data for AR/FA/ZH has been 100% complete since PRs #432/#434/#443. CardSets (PDFs) already support 8 languages via FrontFieldConversions. This change extends MindMap SVG generation to the same 8-language scope. Build: 0 errors, 15 warnings (pre-existing) Tests: 149 pass, 6 fail (pre-existing, unrelated), 5 skip Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(i18n): add Link{Es,Ar,Fa,Zh}Fallback properties for MindMap link cascade Without these computed properties, ~93% of AR/FA/ZH mindmap nodes would have empty links (CSV link_ar/fa/zh coverage is 6-7%). The fallback cascade matches the existing pattern: own lang → EN → FR. Also restores FallaciesLocalizationTests.cs to master (control-break helpers from #449), reversing an erroneous test regression in PR #454. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Operationalizes ai-01's visual release verdict as a fillable multi-axis checklist, complementing (not duplicating) the existing validation guide (narrative) and the 8-language QA scenario (walkthrough + coarse section 5 grid). Decomposes each CardSet x language into 5 measurable axes instead of a single PASS/FAIL: (A) localized text form, (B) localized taxonomy, (C) geometry (count columns/rows per family, not just "text translated" — the geometry-not-just-text lesson), (D) overflow, (E) cover/title. Adds a dedicated RED-FLAG section for the stale-harvest signature (localized subtitle + residual FR taxonomy = cached .harvest.json not regenerated — the exact pattern of the EN memo_back.png pre-#443 caveat in validation-guide section 2ter), with SHA256-vs-previous detection and the explicit clobber requirement. Positioning section 0 makes explicit that this is the 3rd pillar: guide = manual, scenario = walkthrough, this = verdict capture form. The section 9 final grid replaces/refines the scenario's coarse section 5 grid (Language x 5 decks = 1 verdict) into 6 logical CardSets, each verdict being the trace of the 5 axes. Docs-only. No code, no CSV. Base master 330ce49. Serves #140 + gate #134. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Problem
Memo Back and Memo Face templates contain hardcoded French text that
FrontFieldConversionscannot localize:Lart de jamais avoir tort— plain text, not a{{variable}}{{#ifCond Famille "==" text_fr }}— baretext_fr(no{{}}wrapping) with a trailing space before}}, soFormatField("text_fr")=text_fr}}does not matchResult: Memo pages in EN/RU/PT PDFs show French subtitle + French filter logic → cards render empty/FR.
Solution
Added
StaticConversionsto the shared Fallacies+MemoCardSetLocalizationentry (Memo already listed at lines 106-107):Lart de jamais avoir torttext_fr(bare)text_entext_rutext_ptAlso includes ES/AR/FA/ZH for both.
Safety
Lart de jamais avoir tortnortext_fr(with space) appear in Fallacies Face templates — verified by grepFormatField("Famille")=Famille}}does NOT matchFamille_camelCase}}(verified bug: non-FR PDFs contain French text — translations not applied #216)Visual Validation Required
ai-01: After merge, run the pipeline and visually verify that Memo Back EN/RU/PT PDFs show:
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com