Skip to content

feat: dedupe bilingual() identical sides + add CTA ja/zh translations - #39

Merged
TrainTravel merged 1 commit into
mainfrom
feat/bilingual-dedupe-and-cta-i18n
May 26, 2026
Merged

feat: dedupe bilingual() identical sides + add CTA ja/zh translations#39
TrainTravel merged 1 commit into
mainfrom
feat/bilingual-dedupe-and-cta-i18n

Conversation

@TrainTravel

Copy link
Copy Markdown
Owner

Summary

Fixes the "Write today / Write today" rendering when the active language pair has no translation for either side (e.g. target=ja and primary=zh-Hant, where both fall back to en via the Translations fallback chain).

Two changes

1. bilingual() dedupes identical sides (structural fix)

One-line change in src/contexts/LanguageContext.tsx:

return tgt === prm ? tgt : `${tgt} / ${prm}`;

When targetLang and primaryLang resolve to the same string, render once instead of "X / X". Silent improvement for every bilingual() call site across the app — no API change, no semantic change for pairs that legitimately differ (e.g. fr / en).

This means chrome strings without ja: / zh-*: keys silently improve too: "Brain Dump / Brain Dump" becomes "Brain Dump" when both sides fall back to en.

2. CTA gets explicit Japanese + Simplified Chinese + Traditional Chinese (per-call site fix)

The most visually prominent bilingual call on HomeScreen. "Write today" / "Write another" now render in the target language:

  • ja: 今日書きましょう / もう一度書きましょう (polite, inviting register matching the encouragement tone of the button)
  • zh-Hans: 今天写日记 / 再写一篇
  • zh-Hant: 今天寫日記 / 再寫一篇

Other chrome anchors (Brain Dump, Thought Garden, French journaling practice, More tools, etc.) are NOT translated in this PR — the dedupe handles their visual ugliness, and a translation-pass PR will fill in real ja / zh-* keys for them.

Test plan

  • npx tsc --noEmit — clean
  • npx vitest run — 191 passed / 192 (only pre-existing useJournal startFreeWrite)
  • 3 new tests in LanguageContext.test.tsx:
    • same-string-both-sides → renders once
    • different-strings → keeps the pair
    • all keys provided → no false dedupe; actual ja/zh-Hant pair renders
  • Manual: open Home with target=ja primary=zh-Hant — CTA reads 今日書きましょう / 今天寫日記 instead of Write today / Write today
  • Manual: existing pairs (target=fr primary=en) — CTA still reads Écrire aujourd'hui / Write today (no regression)
  • Manual: any bilingual anchor (Brain Dump) with target=ja — renders Brain Dump once instead of Brain Dump / Brain Dump

Native review caveat

I picked the Japanese and Chinese phrasings myself. For 今日書きましょう I went with polite-encouraging register matching the button's vibe (not curt 今日書く, not heavy 今日の日記を書きましょう). A native pass via iTalki would catch any awkwardness — same recommendation as the Japanese prompts from PR #36.

Out of scope (follow-up translation pass)

  • Adding ja: / zh-*: keys to other bilingual() call sites (Brain Dump, Thought Garden, etc.)
  • Same for t() call sites in journaling-flow screens
  • formatDate locale handling for primary=zh-* / target=ja (currently falls back to en-US)

🤖 Generated with Claude Code

Fixes the "Write today / Write today" rendering when target=ja and
primary=zh-Hant (or any pair where both sides fall back to en via
the Translations fallback chain).

Two changes:

1. bilingual() dedupe — one-line change in LanguageContext.tsx.
   When target and primary resolve to the same string, render once
   instead of "X / X". Silent improvement for every existing call
   site without modifying their Translations objects.

2. HomeScreen CTA gets explicit ja + zh-Hans + zh-Hant keys.
   "Write today" / "Write another" now render in the target
   language when those are the active target:
     ja:       今日書きましょう / もう一度書きましょう
     zh-Hans:  今天写日记 / 再写一篇
     zh-Hant:  今天寫日記 / 再寫一篇

Three new bilingual() tests cover the dedupe behavior:
- Same string both sides → renders once
- Different strings → keeps "X / Y" pair
- Both keys provided → renders the actual pair (no false dedupe)

Other chrome anchors (Brain Dump, Thought Garden, etc.) still
fall back to en but no longer say "Brain Dump / Brain Dump" —
the dedupe handles them transparently. A future translation-pass
PR will add real Japanese and Chinese keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TrainTravel
TrainTravel force-pushed the feat/bilingual-dedupe-and-cta-i18n branch from 74d1d5e to 35a62f1 Compare May 26, 2026 23:35
@TrainTravel
TrainTravel merged commit 097eafe into main May 26, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant