Skip to content

feat: Japanese target language + edge function consolidation - #36

Merged
TrainTravel merged 4 commits into
mainfrom
feat/japanese-target-consolidate
May 26, 2026
Merged

feat: Japanese target language + edge function consolidation#36
TrainTravel merged 4 commits into
mainfrom
feat/japanese-target-consolidate

Conversation

@TrainTravel

Copy link
Copy Markdown
Owner

Summary

Two things in one PR (the second made the first possible):

  1. Consolidate 4 language-specific edge functions into 2 generic ones parameterized by targetLang. french-feedback + chinese-feedbacklanguage-feedback. Same for chat.
  2. Add Japanese (ja) as a target language. TargetLang union + picker + edge function targetName ladder. Existing t() calls without a ja: key fall back to English chrome (acceptable v1).

Commits

  • 0daa233 refactor: consolidate french-* + chinese-* edge fns into language-feedback + language-chat
  • 8cc1e92 chore: delete french-* + chinese-* edge function source
  • c8f394a feat: add Japanese as target language with graceful English chrome fallback
  • 660dfe4 fix: tighten Japanese prompts after native-speaker-perspective review

Why consolidate now

Adding Japanese was about to be instance #3 of the same "copy a language edge function and tweak the prompt" pattern. The Rule of Three says stop and parameterize at instance #3. We did. (This PR is the trigger that codified the rule in CLAUDE.md — see separate chore/duplication-watch-rule PR.)

Quality caveats (read before merging)

Pre-existing routing bug preserved

Before this PR, targetLang === 'es' was routing to french-feedback and french-chat — meaning Spanish learners got CBT scaffolding in French. The consolidation preserved this exact behavior (es maps to the French-prompt branch in the new generic function). Not introduced by this PR. Fix in a separate Spanish-CBT-translation PR.

Japanese prompts — pre-merge AI review pass, native review pending

The Japanese branches were authored by the subagent based on patterns from Chinese. After the agent reported, I did a Claude-self review (acting as a Japanese-fluent reviewer) and fixed 3 high-confidence issues in commit 660dfe4:

  • Crisis-clause calque from English (now natural Japanese)
  • Self-contradictory register guidance ("plain/neutral register (-ます form)" — fixed: use polite で す・ます)
  • Furigana > romaji preference for intermediate learners (was "hiragana/romaji")

A human native review (iTalki tutor or similar) is still planned post-merge — see CHANGELOG and the GPT/Gemini consensus-check bundle pasted into the dev notes.

⚠ MANUAL DEPLOYMENT STEPS

supabase functions deploy language-feedback language-chat
supabase functions delete french-feedback french-chat chinese-feedback chinese-chat

Without these, existing French/Chinese users will hit 404s.

Test plan

  • npx tsc --noEmit — clean
  • npx vitest run — 188 passed / 189 (only pre-existing useJournal startFreeWrite fails; confirmed unchanged on main)
  • 6 new LanguageContext.test.tsx tests for target='ja' hydration + invariant
  • 3 new e2e in e2e/language.spec.ts for "Japanese — target-only" picker flow
  • e2e mocks updated to route language-feedback instead of french-feedback / chinese-feedback
  • Manual: pick Japanese in LanguageSettingsScreen → toggle shows → chrome falls back to English where ja: key missing
  • Manual after deploy: write a Japanese sentence in WriteScreenlanguage-feedback returns Japanese reflection
  • Native review: iTalki tutor or r/translator post — schedule within the week

Out of scope (follow-ups)

  • Adding Japanese to existing t() call sites (~chrome translation pass)
  • IME composition handling on Enter-to-submit textareas (benefits Chinese too)
  • Japanese as PRIMARY language option
  • Spanish CBT scaffolding rewrite (the routing bug above)

🤖 Generated with Claude Code

TrainTravel and others added 4 commits May 26, 2026 16:34
…eedback + language-chat

Collapse 4 language-specific edge functions into 2 generic ones parameterized
by targetLang. The new functions branch internally on target (fr | es | zh-* | ja)
so callers no longer need to pick a function name based on language.

Zero behavior change for fr / es / zh-Hans / zh-Hant users — the language-specific
prompt branches (pinyin guidance for Chinese; emotion examples for French/Spanish;
CBT scaffolding) are preserved inside the consolidated functions.

The Japanese branch is wired in here so the consolidated functions are
target-complete, but no client surface picks 'ja' until the next commit adds
it to TargetLang.

Caller updates:
- FeedbackScreen, ChatScreen, useInlineAssist now call language-feedback /
  language-chat and pass targetLang in the body
- Old `lang` field kept on the body for forward-compat with any in-flight
  deployments of the old functions
- e2e mocks + spec routes updated to match the new endpoint paths

Old french-* / chinese-* source directories intentionally NOT deleted in this
commit — kept so any in-flight deployments don't 404. Deleted in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d by language-*

Removes the four source directories that were left in place during the
consolidation commit. Also drops their entries from supabase/config.toml.

MANUAL DEPLOY STEP REQUIRED:
  supabase functions deploy language-feedback language-chat
  supabase functions delete french-feedback french-chat chinese-feedback chinese-chat

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…llback

Japanese (ja) joins the TargetLang union as a target-only language. It is
intentionally NOT a valid PrimaryLang yet — that would require sweeping every
t() / bilingual() call site to add a ja: key, which is deferred.

What's wired:
- TargetLang += 'ja', TARGET_LANGS array += 'ja'
- Translations type += optional ja?: string key
- stringFor() returns ja ?? en (graceful chrome fallback)
- LanguageToggle LABELS['ja'] = '日'
- LanguageSettingsScreen TARGET_OPTIONS adds { code: 'ja', native: '日本語', en: 'Japanese' }
- reflection edge fn targetName ladder += 'ja' → 'Japanese'
- language-feedback / language-chat already shipped with ja support in commit 1

What's explicitly NOT touched (acceptable for v1):
- Existing t()/bilingual() call sites — chrome stays English when target=ja
- IME composition handling on text inputs (separate follow-up PR)
- Japanese-as-primary (deferred until chrome translation pass)
- SelfCompassion phrase data (matches the Chinese deferral pattern)

Tests:
- 6 new LanguageContext unit tests: ja hydration, ja-as-primary rejection,
  ja fallback to en when ja key missing, ja key used when present,
  availableTargets includes ja
- 3 new e2e tests: picking 日本語 persists pair, toggle shows 日,
  Japanese not offered as primary

CHANGELOG entry documents the chrome-English caveat and IME follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three high-confidence issues caught in pre-push review:

1. CRISIS CLAUSE — replaced "あなたの声が聞こえます" (calque from
   "I hear you") with "つらいですね" — natural Japanese empathy.
   Dropped あなた pronoun, which reads as cold/distant in
   emotionally supportive register.

2. REGISTER GUIDANCE — was self-contradictory ("plain/neutral
   register (-ます form is fine)" — -ます IS polite, not plain).
   Replaced with explicit: "Use polite register (です・ます形)
   by default. Don't use keigo. Don't drop into plain form."

3. READINGS — was "hiragana/romaji", let model choose. Tightened
   to "furigana (hiragana readings in parentheses)" with romaji
   only as fallback for all-romaji input. Matches JLPT pedagogy
   convention for intermediate learners.

Medium-confidence findings (CBT register softening, vague-word
list additions) deferred to a follow-up. A native speaker review
via iTalki is still planned to catch nuance issues my review
can't surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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