feat: Japanese target language + edge function consolidation - #36
Merged
Conversation
…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>
2 tasks
This was referenced May 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two things in one PR (the second made the first possible):
targetLang.french-feedback+chinese-feedback→language-feedback. Same for chat.ja) as a target language. TargetLang union + picker + edge functiontargetNameladder. Existingt()calls without aja:key fall back to English chrome (acceptable v1).Commits
0daa233refactor: consolidatefrench-*+chinese-*edge fns intolanguage-feedback+language-chat8cc1e92chore: deletefrench-*+chinese-*edge function sourcec8f394afeat: add Japanese as target language with graceful English chrome fallback660dfe4fix: tighten Japanese prompts after native-speaker-perspective reviewWhy 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 separatechore/duplication-watch-rulePR.)Quality caveats (read before merging)
Pre-existing routing bug preserved
Before this PR,
targetLang === 'es'was routing tofrench-feedbackandfrench-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: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
Without these, existing French/Chinese users will hit 404s.
Test plan
npx tsc --noEmit— cleannpx vitest run— 188 passed / 189 (only pre-existinguseJournal startFreeWritefails; confirmed unchanged on main)LanguageContext.test.tsxtests fortarget='ja'hydration + invariante2e/language.spec.tsfor "Japanese — target-only" picker flowlanguage-feedbackinstead offrench-feedback/chinese-feedbackLanguageSettingsScreen→ toggle shows日→ chrome falls back to English whereja:key missingWriteScreen→language-feedbackreturns Japanese reflectionOut of scope (follow-ups)
t()call sites (~chrome translation pass)🤖 Generated with Claude Code