Replace Clerk with self-contained JWT auth (bcryptjs + jose), remove all external auth dependencies. Add full i18n system with Chinese as default language and English toggle — React Context + JSON dictionaries, no external i18n library. Auth: - Add passwordHash to User model, generate JWT secret - Create /api/auth/register, login, logout, me endpoints - Rewrite proxy.ts middleware for custom session validation - Add AuthProvider + useAuth hook, sign-in/sign-up pages - Wire auth into layout and all API routes i18n: - React Context I18nProvider with localStorage + cookie persistence - dictionaries/zh.ts (default) and dictionaries/en.ts - LanguageSwitcher component, server-side createServerT() utility - CJK font stack via [lang="zh"] CSS selector - HTML lang attribute synced on language change - All pages, components, TabBar, StageBadge translated - Engine fallback strings (reflection-analyzer, summary-generator) - API routes pass locale from cookie to engines Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
267 lines
8.6 KiB
TypeScript
267 lines
8.6 KiB
TypeScript
import type { Dict } from "@/lib/i18n/types";
|
|
|
|
const en: Dict = {
|
|
// -----------------------------------------------------------------------
|
|
// Navigation (TabBar)
|
|
// -----------------------------------------------------------------------
|
|
nav: {
|
|
today: "Today",
|
|
progress: "Progress",
|
|
rewards: "Rewards",
|
|
summary: "Summary",
|
|
settings: "Settings",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Shared labels
|
|
// -----------------------------------------------------------------------
|
|
shared: {
|
|
loading: "Loading...",
|
|
back: "← Back",
|
|
|
|
task_type: {
|
|
attention: "Attention",
|
|
emotion: "Emotion",
|
|
stress: "Stress",
|
|
clarity: "Clarity",
|
|
integration: "Integration",
|
|
},
|
|
|
|
difficulty: {
|
|
beginner: "Beginner",
|
|
intermediate: "Intermediate",
|
|
advanced: "Advanced",
|
|
},
|
|
|
|
stage: {
|
|
beginner: "Beginner",
|
|
foundation: "Foundation",
|
|
growing: "Growing",
|
|
established: "Established",
|
|
advanced: "Advanced",
|
|
},
|
|
|
|
reward_type: {
|
|
daily_completion: "Daily Practice",
|
|
streak_3: "3-Day Streak",
|
|
streak_7: "7-Day Streak",
|
|
streak_14: "14-Day Streak",
|
|
streak_30: "30-Day Streak",
|
|
stage_up: "Stage Advance",
|
|
first_session: "First Practice",
|
|
},
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Home page
|
|
// -----------------------------------------------------------------------
|
|
home: {
|
|
greeting: {
|
|
morning: "Good morning",
|
|
afternoon: "Good afternoon",
|
|
evening: "Good evening",
|
|
fallback_name: "there",
|
|
subtitle: "Your practice is waiting.",
|
|
},
|
|
|
|
onboarding: {
|
|
title: "One step left",
|
|
description: "Complete onboarding in Settings to start your practice.",
|
|
cta: "Go to Settings",
|
|
},
|
|
|
|
stats: {
|
|
streak: "Streak",
|
|
streak_unit: "days",
|
|
sessions: "Sessions",
|
|
sessions_unit: "total",
|
|
},
|
|
|
|
metrics: {
|
|
title: "Core metrics",
|
|
attention: "Attention",
|
|
emotion: "Emotion",
|
|
stress: "Stress",
|
|
consistency: "Consistency",
|
|
},
|
|
|
|
task: {
|
|
heading: "Today's practice",
|
|
no_task: "No task for today yet. Generate one to start your practice.",
|
|
generate: "Generate today's task",
|
|
generating: "Generating...",
|
|
begin: "Begin practice",
|
|
why_prefix: "", // unused in English, kept for key parity with zh
|
|
},
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Progress page
|
|
// -----------------------------------------------------------------------
|
|
progress: {
|
|
title: "Your progress",
|
|
metrics_title: "Core metrics",
|
|
stage_title: "Stage progress",
|
|
|
|
attention: "Attention",
|
|
emotional_stability: "Emotional Stability",
|
|
stress_level: "Stress Level",
|
|
consistency: "Consistency",
|
|
|
|
stages: {
|
|
beginner: "Beginner",
|
|
foundation: "Foundation",
|
|
growing: "Growing",
|
|
established: "Established",
|
|
advanced: "Advanced",
|
|
},
|
|
|
|
stage_prefix: "Stage",
|
|
onboarding_needed: "Complete onboarding to see your progress.",
|
|
loading: "Loading...",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Rewards page
|
|
// -----------------------------------------------------------------------
|
|
rewards: {
|
|
title: "Rewards",
|
|
empty_subtitle: "Your rewards will appear here as you practice.",
|
|
empty_message: "Complete your first practice to earn a reward.",
|
|
earned_one: "{count} reward earned",
|
|
earned_other: "{count} rewards earned",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Summary page
|
|
// -----------------------------------------------------------------------
|
|
summary: {
|
|
title: "Weekly summary",
|
|
week_of: "Week of {date}",
|
|
empty_subtitle: "Generated each week from your practice history",
|
|
empty_message:
|
|
"Your first weekly summary will be generated after you complete at least one practice session. Summaries run automatically each week.",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Settings page
|
|
// -----------------------------------------------------------------------
|
|
settings: {
|
|
title: "Settings",
|
|
|
|
onboarding: {
|
|
title: "Welcome to Inner OS",
|
|
description:
|
|
"Inner OS generates one personalized mental training task each day, guided by your practice history and inner development stage. Your reflections shape what comes next. Everything is private — just you and your practice.",
|
|
cta: "Get started",
|
|
setting_up: "Setting up...",
|
|
done: "You're all set. Head home to start your practice.",
|
|
error: "Something went wrong. Try again.",
|
|
},
|
|
|
|
profile: {
|
|
title: "Profile",
|
|
signed_in_as: "Signed in as",
|
|
stage_and_sessions: "Stage {stage} · {count} sessions",
|
|
},
|
|
|
|
about: {
|
|
title: "About Inner OS",
|
|
p1: "Inner OS is a private inner development practice. You receive one personalized mental training task each day, tailored to your current state and development stage.",
|
|
p2: "Your reflections are private. Your data is yours. There are no leaderboards, no social features, no competition — just you and the work of becoming more aware, more stable, and more yourself.",
|
|
},
|
|
|
|
sign_out: "Sign out",
|
|
signing_out: "Signing out...",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Practice page
|
|
// -----------------------------------------------------------------------
|
|
practice: {
|
|
title: "Your practice",
|
|
why_prefix: "Why this task: ",
|
|
start: "Start",
|
|
finish_early: "Finish early",
|
|
complete: "Practice complete",
|
|
reflect: "Reflect on your practice",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Reflection page
|
|
// -----------------------------------------------------------------------
|
|
reflect: {
|
|
no_session: "No session to reflect on. Go home.",
|
|
title: "Reflect on your practice",
|
|
label: "What did you notice during your practice?",
|
|
placeholder:
|
|
"Were you focused or distracted? What emotions came up? Did anything surprise you?",
|
|
submit: "Complete practice",
|
|
submitting: "Saving...",
|
|
error: "Failed to save reflection",
|
|
generic_error: "Something went wrong",
|
|
|
|
result_title: "Practice complete",
|
|
focus: "focus",
|
|
emotional_awareness: "emotional awareness",
|
|
back_home: "Back home",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Sign-in page
|
|
// -----------------------------------------------------------------------
|
|
sign_in: {
|
|
title: "Sign in",
|
|
subtitle: "Welcome back to Inner OS",
|
|
email: "Email",
|
|
password: "Password",
|
|
submit: "Sign in",
|
|
submitting: "Signing in...",
|
|
fallback_error: "Login failed",
|
|
network_error: "Network error. Try again.",
|
|
no_account: "No account?",
|
|
sign_up_link: "Sign up",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Sign-up page
|
|
// -----------------------------------------------------------------------
|
|
sign_up: {
|
|
title: "Sign up",
|
|
subtitle: "Start your inner development practice",
|
|
email: "Email",
|
|
password: "Password",
|
|
confirm_password: "Confirm password",
|
|
submit: "Create account",
|
|
submitting: "Creating account...",
|
|
password_mismatch: "Passwords do not match",
|
|
password_short: "Password must be at least 6 characters",
|
|
fallback_error: "Registration failed",
|
|
network_error: "Network error. Try again.",
|
|
has_account: "Already have an account?",
|
|
sign_in_link: "Sign in",
|
|
},
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Engine fallback strings (server-side, not React)
|
|
// -----------------------------------------------------------------------
|
|
engine: {
|
|
reflection: {
|
|
keyword_blocked: "Thank you for sharing.",
|
|
no_reflection: "You completed your practice today.",
|
|
safety_blocked: "You completed your practice today.",
|
|
llm_failed: "You showed up today. That's what matters.",
|
|
},
|
|
|
|
summary: {
|
|
no_sessions:
|
|
"No completed practice sessions this week. Every week is a fresh start — your practice is here when you're ready.",
|
|
safety_fallback: "Your practice summary for this week.",
|
|
fallback_template:
|
|
"You completed {count} practice session{plural} this week. Showing up consistently is the foundation everything else is built on. Your current streak is {streak} day{streak_plural}. See you next week.",
|
|
},
|
|
},
|
|
};
|
|
|
|
export default en;
|