Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ Files in this PR:
Register matches PR #40 + PR #44: polite Japanese (です・ます), neutral Mandarin with S/T distinguished where they diverge (继续/繼續, 字 is shared, 书写/書寫, etc.).

Cherry-picked from the larger sweep branch (`feat/chrome-translation-tier2-3`) as a reviewable per-flow PR. Native review pass still planned post-merge.
---
### Chrome translation — reflection flow (Reflection + Gratitude + Feedback)

**Adds ja / zh-Hans / zh-Hant keys to the three post-write reflection screens.**

Files in this PR:
- `ReflectionScreen.tsx` — Back, "Taking a moment to reflect..." loader, error-state Continue, response placeholder + optional-helper, "Finish with gratitude" bilingual anchor
- `GratitudeScreen.tsx` — Back, optional-skip helper, placeholder, Complete journal, Skip and finish
- `FeedbackScreen.tsx` — header "A moment of clarity", three section bilingual anchors (Naming with precision / A small note / Vocabulary bridge), Continue, Skip

Note: the hard-coded `isFr ? : isEs ? : ...` ternaries in ReflectionScreen and FeedbackScreen are pre-existing technical debt (they are not `t()` calls). They are outside the scope of this chrome-translation pass and are tracked separately.

Register matches PR #40 + PR #44.

---

Expand Down
14 changes: 7 additions & 7 deletions src/components/journal/FeedbackScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
const hasEmotionalGranularity = feedback?.emotionalGranularity?.detected &&
feedback?.emotionalGranularity?.alternatives?.length > 0;

const headerText = t({ fr: 'Un moment de clarté', en: 'A moment of clarity', es: 'Un momento de claridad' });
const headerText = t({ fr: 'Un moment de clarté', en: 'A moment of clarity', es: 'Un momento de claridad', ja: '澄んだひととき', 'zh-Hans': '清明片刻', 'zh-Hant': '清明片刻' });

return (
<div className="min-h-screen flex flex-col px-6 py-12">
Expand Down Expand Up @@ -193,7 +193,7 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
<div className="flex items-center gap-2 text-foreground">
<Sparkles className="w-4 h-4 text-primary" />
<span className="text-sm font-medium">
{bilingual({ fr: 'Nommer avec précision', en: 'Naming with precision', es: 'Nombrar con precisión', 'zh-Hans': '精准命名', 'zh-Hant': '精準命名' })}
{bilingual({ fr: 'Nommer avec précision', en: 'Naming with precision', es: 'Nombrar con precisión', ja: '言葉で正確に表す', 'zh-Hans': '精准命名', 'zh-Hant': '精準命名' })}
</span>
</div>

Expand Down Expand Up @@ -234,7 +234,7 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
<div className="space-y-3">
<div className="flex items-center gap-2 text-muted-foreground">
<MessageCircle className="w-4 h-4" />
<span className="text-sm">{bilingual({ fr: 'Une petite note', en: 'A small note', es: 'Una pequeña nota', 'zh-Hans': '小提示', 'zh-Hant': '小提示' })}</span>
<span className="text-sm">{bilingual({ fr: 'Une petite note', en: 'A small note', es: 'Una pequeña nota', ja: 'ひとこと', 'zh-Hans': '小提示', 'zh-Hant': '小提示' })}</span>
</div>
<div className="bg-card border border-border rounded-xl p-4 space-y-2">
<div className="flex items-center gap-2 text-sm flex-wrap">
Expand All @@ -259,7 +259,7 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
<div className="space-y-3">
<div className="flex items-center gap-2 text-muted-foreground">
<MessageCircle className="w-4 h-4" />
<span className="text-sm">{bilingual({ fr: 'Une petite note', en: 'A small note', es: 'Una pequeña nota', 'zh-Hans': '小提示', 'zh-Hant': '小提示' })}</span>
<span className="text-sm">{bilingual({ fr: 'Une petite note', en: 'A small note', es: 'Una pequeña nota', ja: 'ひとこと', 'zh-Hans': '小提示', 'zh-Hant': '小提示' })}</span>
</div>
{feedback.suggestions.slice(0, 1).map((suggestion, index) => (
<div key={index} className="bg-card border border-border rounded-xl p-4 space-y-2">
Expand Down Expand Up @@ -287,7 +287,7 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
<div className="flex items-center gap-2 text-foreground">
<BookOpen className="w-4 h-4 text-primary" />
<span className="text-sm font-medium">
{bilingual({ fr: 'Pont de vocabulaire', en: 'Vocabulary bridge', es: 'Puente de vocabulario', 'zh-Hans': '词汇桥梁', 'zh-Hant': '詞彙橋樑' })}
{bilingual({ fr: 'Pont de vocabulaire', en: 'Vocabulary bridge', es: 'Puente de vocabulario', ja: '語彙の橋渡し', 'zh-Hans': '词汇桥梁', 'zh-Hant': '詞彙橋樑' })}
</span>
<span className={`text-xs px-2 py-0.5 rounded-full ${
feedback.vocabularyBridge.isRevisit
Expand Down Expand Up @@ -331,13 +331,13 @@ export function FeedbackScreen({ journalContent, onContinue, onSkip }: FeedbackS
{/* Actions */}
<div className="mt-8 space-y-3">
<Button variant="default" size="full" onClick={onContinue}>
{t({ fr: 'Continuer', en: 'Continue', es: 'Continuar' }).primary}
{t({ fr: 'Continuer', en: 'Continue', es: 'Continuar', ja: '続ける', 'zh-Hans': '继续', 'zh-Hant': '繼續' }).primary}
<ArrowRight className="w-5 h-5 ml-2" />
</Button>

{isLoading && (
<Button variant="skip" size="full" onClick={onSkip}>
{t({ fr: 'Passer', en: 'Skip', es: 'Omitir' }).primary}
{t({ fr: 'Passer', en: 'Skip', es: 'Omitir', ja: 'スキップ', 'zh-Hans': '跳过', 'zh-Hant': '跳過' }).primary}
</Button>
)}
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/journal/GratitudeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function GratitudeScreen({ prompt, onSave, onSkip, onBack }: GratitudeScr
className="inline-flex items-center text-muted-foreground hover:text-foreground transition-colors mb-8 self-start"
>
<ArrowLeft className="w-4 h-4 mr-2" />
<span className="text-sm">{t({ fr: 'Retour', en: 'Back', es: 'Volver' }).primary}</span>
<span className="text-sm">{t({ fr: 'Retour', en: 'Back', es: 'Volver', ja: '戻る', 'zh-Hans': '返回', 'zh-Hant': '返回' }).primary}</span>
</button>

{/* Header */}
Expand All @@ -43,7 +43,7 @@ export function GratitudeScreen({ prompt, onSave, onSkip, onBack }: GratitudeScr
{promptText.secondary}
</p>
<p className="text-muted-foreground text-sm mt-2">
{t({ fr: "C'est optionnel. Passez si rien ne vous vient.", en: 'This is optional. Skip if nothing comes to mind.', es: 'Es opcional. Omite si no se te ocurre nada.' }).primary}
{t({ fr: "C'est optionnel. Passez si rien ne vous vient.", en: 'This is optional. Skip if nothing comes to mind.', es: 'Es opcional. Omite si no se te ocurre nada.', ja: '任意です。思い浮かばなければスキップしてください。', 'zh-Hans': '这是可选的,想不到就跳过。', 'zh-Hant': '這是選填的,想不到就跳過。' }).primary}
</p>
</div>

Expand All @@ -52,7 +52,7 @@ export function GratitudeScreen({ prompt, onSave, onSkip, onBack }: GratitudeScr
<Textarea
value={gratitude}
onChange={(e) => setGratitude(e.target.value)}
placeholder={t({ fr: 'Quelque chose de petit suffit...', en: 'Something small is perfect...', es: 'Algo pequeño es suficiente...' }).primary}
placeholder={t({ fr: 'Quelque chose de petit suffit...', en: 'Something small is perfect...', es: 'Algo pequeño es suficiente...', ja: '小さなことで大丈夫です...', 'zh-Hans': '一件小事就足够...', 'zh-Hant': '一件小事就足夠...' }).primary}
className="min-h-[150px] resize-none bg-card border-border text-foreground placeholder:text-muted-foreground focus:ring-primary/20 text-lg leading-relaxed p-4 rounded-xl"
/>
</div>
Expand All @@ -61,11 +61,11 @@ export function GratitudeScreen({ prompt, onSave, onSkip, onBack }: GratitudeScr
<div className="mt-8 space-y-3">
<Button variant="default" size="full" onClick={handleSave}>
<Check className="w-5 h-5 mr-2" />
{t({ fr: 'Terminer le journal', en: 'Complete journal', es: 'Terminar el diario' }).primary}
{t({ fr: 'Terminer le journal', en: 'Complete journal', es: 'Terminar el diario', ja: '日記を完了する', 'zh-Hans': '完成日记', 'zh-Hant': '完成日記' }).primary}
</Button>

<Button variant="skip" size="full" onClick={onSkip}>
{t({ fr: 'Passer et terminer', en: 'Skip and finish', es: 'Omitir y terminar' }).primary}
{t({ fr: 'Passer et terminer', en: 'Skip and finish', es: 'Omitir y terminar', ja: 'スキップして終了', 'zh-Hans': '跳过并结束', 'zh-Hant': '跳過並結束' }).primary}
</Button>
</div>
</div>
Expand Down
12 changes: 6 additions & 6 deletions src/components/journal/ReflectionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export function ReflectionScreen({
className="inline-flex items-center text-muted-foreground hover:text-foreground transition-colors"
>
<ArrowLeft className="w-4 h-4 mr-2" />
<span className="text-sm">{t({ fr: 'Retour', en: 'Back', es: 'Volver' }).primary}</span>
<span className="text-sm">{t({ fr: 'Retour', en: 'Back', es: 'Volver', ja: '戻る', 'zh-Hans': '返回', 'zh-Hant': '返回' }).primary}</span>
</button>

{/* Cycle dots */}
Expand All @@ -143,7 +143,7 @@ export function ReflectionScreen({
<div className="flex-1 flex flex-col items-center justify-center space-y-4">
<Loader2 className="w-8 h-8 animate-spin text-primary/60" />
<p className="text-muted-foreground text-sm">
{t({ fr: 'Prenant un moment pour réfléchir...', en: 'Taking a moment to reflect...', es: 'Tomando un momento para reflexionar...' }).primary}
{t({ fr: 'Prenant un moment pour réfléchir...', en: 'Taking a moment to reflect...', es: 'Tomando un momento para reflexionar...', ja: '少し考えています...', 'zh-Hans': '稍等,正在思考...', 'zh-Hant': '稍等,正在思考...' }).primary}
</p>
</div>
)}
Expand All @@ -157,7 +157,7 @@ export function ReflectionScreen({
<div className="mt-auto">
<Button variant="default" size="full" onClick={handleSkip}>
<ArrowRight className="w-5 h-5 mr-2" />
{t({ fr: 'Continuer', en: 'Continue', es: 'Continuar' }).primary}
{t({ fr: 'Continuer', en: 'Continue', es: 'Continuar', ja: '続ける', 'zh-Hans': '继续', 'zh-Hant': '繼續' }).primary}
</Button>
</div>
</div>
Expand Down Expand Up @@ -194,11 +194,11 @@ export function ReflectionScreen({
<Textarea
value={response}
onChange={(e) => setResponse(e.target.value)}
placeholder={t({ fr: 'Prenez votre temps...', en: 'Take your time...', es: 'Tómate tu tiempo...' }).primary}
placeholder={t({ fr: 'Prenez votre temps...', en: 'Take your time...', es: 'Tómate tu tiempo...', ja: 'ゆっくりどうぞ...', 'zh-Hans': '慢慢来...', 'zh-Hant': '慢慢來...' }).primary}
className="min-h-[120px] resize-none bg-card border-border text-foreground placeholder:text-muted-foreground focus:ring-primary/20 text-lg leading-relaxed p-4 rounded-xl"
/>
<p className="text-muted-foreground text-xs mt-2">
{t({ fr: "C'est optionnel. Passez si vous préférez.", en: 'This is optional. Skip if you prefer.', es: 'Es opcional. Omite si prefieres.' }).primary}
{t({ fr: "C'est optionnel. Passez si vous préférez.", en: 'This is optional. Skip if you prefer.', es: 'Es opcional. Omite si prefieres.', ja: '任意です。スキップしてもかまいません。', 'zh-Hans': '这是可选的,可以跳过。', 'zh-Hant': '這是選填的,可以跳過。' }).primary}
</p>
</div>

Expand All @@ -215,7 +215,7 @@ export function ReflectionScreen({
</p>
<Button variant="default" size="full" onClick={handleMoveToGratitude}>
<Heart className="w-5 h-5 mr-2" />
{bilingual({ fr: 'Terminer avec la gratitude', en: 'Finish with gratitude', es: 'Terminar con gratitud' })}
{bilingual({ fr: 'Terminer avec la gratitude', en: 'Finish with gratitude', es: 'Terminar con gratitud', ja: '感謝で締めくくる', 'zh-Hans': '以感恩收尾', 'zh-Hant': '以感恩收尾' })}
</Button>
</div>
) : (
Expand Down
Loading