为 MysticX 添加新语言
本指南涵盖了为应用添加新语言所需的每个文件和步骤。请按阶段顺序执行。
当前语言(12 种): en、zh_CN、zh_TW、ja、ko、pt、es、fr、de、ar、id、nl
阶段 1:基础设施
这 7 个文件将新语言接入框架。在触碰任何翻译内容之前,请先完成所有这些文件。
1.1 i18n/consts.ts
将新语言添加到 LOCALE_AND_LABEL_MAP(母语名称标签)和 LOCALE_ENGLISH_LABELS(管理面板的英文标签):
export const LOCALE_AND_LABEL_MAP = {
// ...现有条目...
xx: 'Native Name', // 添加
} as const;
export const LOCALE_ENGLISH_LABELS: Record<SupportedLocale, string> = {
// ...现有条目...
xx: 'English Name', // 添加
};此更改后,TypeScript 会在每个缺少新键的
trans()调用处报错。这就是你阶段 4 的工作清单。
1.2 i18n/routing.ts
将语言添加到 locales 数组。使用 URL 段格式(连字符,不是下划线 — 所以是 zh-TW 而不是 zh_TW):
locales: ['en', 'zh-CN', 'zh-TW', 'ja', 'ko', 'pt', 'es', 'fr', 'de', 'ar', 'id', 'nl', 'xx'],1.3 i18n/useTrans.ts
在 normalizedLocale 代码块中添加一个 case。如果语言以 zh 开头,请将其放在通用的 zh 检查之前,以避免被 zh_CN 吞掉:
const normalizedLocale: SupportedLocale = (() => {
const lower = locale.toLowerCase();
if (lower.startsWith('zh-tw') || lower.startsWith('zh_tw')) return 'zh_TW';
if (lower.startsWith('zh')) return 'zh_CN';
// ...现有 case...
if (lower === 'xx') return 'xx'; // 添加
return DEFAULT_LOCALE as SupportedLocale;
})();1.4 i18n/getTrans.ts
与 useTrans.ts 相同的逻辑 — 同样的排序规则适用:
if (detected?.toLowerCase() === 'xx') return 'xx'; // 添加1.5 i18n/useAppLocale.tsx
同样的逻辑:
if (locale.toLowerCase() === 'xx') return 'xx'; // 添加1.6 i18n/request.ts
更新 messageFile 映射以将新语言解析到其 JSON 文件。如果新语言有连字符 URL 段(如 zh-TW),请将其添加在通用前缀检查之前:
const messageFile =
locale.startsWith('zh-TW') || locale.startsWith('zh_TW') ? 'zh_TW'
: locale.startsWith('zh') ? 'zh_CN'
: locale;对于没有前缀冲突的简单语言(xx),此处无需更改 — 默认直通处理即可。
1.7 创建消息文件
echo '{}' > messages/xx.json验证基础设施
pnpm tsc --noEmit 2>&1 | head -60你应该看到 TypeScript 错误,指出每个缺少 xx: 的 trans() 调用。该数量就是你阶段 4 的范围。
阶段 2:认证与本地化文件
将新语言键添加到 lib/localization/ 中的每个错误消息对象。每个文件遵循此模式:
SOME_ERROR_CODE: {
en: 'Some error message',
zh_CN: '...',
// ...现有语言...
xx: '...', // 添加
},需要更新的文件(按大小排序):
| 文件 | 备注 |
|---|---|
lib/localization/anonymous-error-codes.ts | |
lib/localization/api-key-error-codes.ts | |
lib/localization/captcha-error-codes.ts | |
lib/localization/haveibeenpwned-error-codes.ts | |
lib/localization/passkey-error-codes.ts | |
lib/localization/phone-number-error-codes.ts | |
lib/localization/username-error-codes.ts | |
lib/localization/multi-session-error-codes.ts | |
lib/localization/team-error-codes.ts | |
lib/localization/two-factor-error-codes.ts | |
lib/localization/admin-error-codes.ts | |
lib/localization/generic-oauth-error-codes.ts | |
lib/localization/email-otp-error-codes.ts | |
lib/localization/organization-error-codes.ts | |
lib/localization/base-error-codes.ts | |
lib/localization/stripe-localization.ts | |
lib/localization/auth-localization.ts | 最大的文件 — 分 500 行一块处理 |
lib/localization/buildAuthLocalization.ts | 无需更改 — 自动从 TAppLocale 派生类型 |
阶段 3:SEO 与元数据
lib/buildPageMetadata.ts
3.1 添加到 localesInUrl(使用 URL 段格式):
const localesInUrl = ['zh-CN', 'zh-TW', 'ja', 'ko', 'pt', 'es', 'fr', 'de', 'ar', 'id', 'nl', 'xx'];3.2 添加到 alternates.languages:
languages: {
'en-US': buildUrl('en'),
// ...现有条目...
'xx-XX': buildUrl('xx'), // 添加 — 使用正确的 BCP 47 标签
'x-default': buildUrl('en'),
},阶段 4:trans() 调用点
将新语言键添加到应用中每个 trans({...}) 调用。运行以下命令获取当前文件列表:
grep -rl "trans({" --include="*.tsx" --include="*.ts" app/ components/ lib/ providers/ | sort规则
- 不要使用英文作为回退值。 每个值都必须是目标语言。
- 对于 JSX 值(如
trans({ en: <><strong>Hello</strong></> })),保持 JSX 结构不变 — 只翻译内部文本。 - 编辑每个文件后进行抽查:
grep -c "xx:" <file>应大致等于grep -c "trans({" <file>。
当前文件列表(截至 2026 年 4 月约 75 个文件)
根布局、错误和 404 页面 — app/[locale]/
layout.tsxerror.tsxnot-found.tsx
网站头部 — app/[locale]/_components/SiteHeader/
CardOfDayButton.tsxCreditBadge.tsxLanguageModal.tsxMobileDrawerMenu.tsxNotificationBell.tsxSiteHeader.tsxUserDropdown.tsx
网站底部 — app/[locale]/_components/SiteFooter/
SiteFooter.tsx
其他共享布局组件 — app/[locale]/_components/
OnLayoutLoaded.tsx
首页和认证页面 — app/[locale]/(main)/
page.tsxauth/[path]/page.tsxauth/telegram/page.tsx
选牌弹窗 — app/[locale]/(main)/_components/CardPickerModal/
CardPickerModalCenterInfo.tsxCardPickerModalControls.tsxCardRevealSpread.tsx
首页主视觉区 — app/[locale]/(main)/_components/
HeroSection.tsx
仪表盘 — app/[locale]/(main)/dashboard/
BtnSignOut.tsxpage.tsx
个人主页和洞察(我的账户) — app/[locale]/(main)/me/
_components/PersonalInfoModal.tsx_components/ProfilePageContent.tsx(insights)/_components/InsightsNav.tsx(insights)/card-of-day/_components/CardOfDayEntryModal.tsx(insights)/card-of-day/_components/CardOfDayHistoryContent.tsx(insights)/card-of-day/_components/CardOfDayListView.tsx(insights)/journal/_components/JournalContent.tsx(insights)/soul-journey/_components/SoulJourneyContent.tsx(insights)/weekly-guidance/_components/WeeklyGuidanceContent.tsxcard-skins/_components/MyCardSkinsContent.tsxinvitations/_components/InvitationsPageContent.tsxmembership/_components/MembershipContent.tsxtarot-readers/_components/MyTarotReadersContent.tsx
会员/定价页面 — app/[locale]/(main)/membership/
_components/ComparePlansSection.tsx_components/FAQSection.tsx_components/HeroSection.tsx_components/MembershipPageContent.tsx_components/PricingSection.tsxpage.tsx
卡牌皮肤市场 — app/[locale]/(main)/market/card-skins/
_components/CardSkinsMarketContent.tsxpage.tsx
塔罗师市场 — app/[locale]/(main)/market/tarot-readers/
_components/TarotReadersMarketContent.tsx[slug]/_components/ReaderAudioPlayer.tsx[slug]/_components/ReaderCta.tsx[slug]/_components/ReaderDetailContent.tsx[slug]/_components/ReaderPersonalityWidget.tsx[slug]/page.tsxpage.tsx
占卜聊天界面 — app/[locale]/(main)/tarot-readings/[readingId]/_components/
ChatMessageBubble.tsxChatUserInput.tsxLuckEnhancement.tsxMessageVoiceButton.tsxReadingDetailView.tsxReadingInfoPanel.tsxSuggestedQuestions.tsx
共享牌阵页面组件 — app/[locale]/(main)/(tarot-spreads)/_components/
CardPositionsExplainedSection.tsxSpreadFAQSection.tsxSpreadHeroSection/SpreadHeroSection.tsx
各牌阵页面(13 种牌阵) — app/[locale]/(main)/(tarot-spreads)/
celtic-cross/_components/CelticCrossContent.tsx+page.tsxdaily-tarot/_components/DailyTarotContent.tsx+page.tsxinner-child-healing/_components/InnerChildHealingContent.tsx+page.tsxlove-tarot/_components/LoveReadingChoiceModal.tsxlove-tarot/_components/LoveTarotContent.tsx+page.tsxobstacle-key/_components/ObstacleKeyContent.tsx+page.tsxone-card/_components/OneCardContent.tsx+page.tsxrelationship-compass/_components/RelationshipCompassContent.tsx+page.tsxshadow-work/_components/ShadowWorkContent.tsx+page.tsxthree-card/_components/ThreeCardContent.tsx+page.tsxtwin-flame-mirror/_components/TwinFlameMirrorContent.tsx+page.tsxtwo-path-choice/_components/TwoPathChoiceContent.tsx+page.tsxyes-or-no/_components/YesOrNoContent.tsx+page.tsx
账户设置返回链接 — app/[locale]/(main)/account/
[path]/_components/BackToProfileLink.tsx
联盟计划页面 — app/[locale]/(main)/affiliate-program/
_components/AffiliateProgramAllowedPromotionMethodsSection.tsx_components/AffiliateProgramCommissionLevelsSection.tsx_components/AffiliateProgramFAQ.tsx_components/AffiliateProgramFinalCtaSection.tsx_components/AffiliateProgramHeroSection.tsx_components/AffiliateProgramHowToJoinSection.tsx_components/AffiliateProgramLevelRequirementsSection.tsx_components/AffiliateProgramWhyPromoteSection.tsxpage.tsx
博客 — app/[locale]/(main)/blog/
_components/BlogIndexContent.tsx[slug]/_components/BlogPostPageContent.tsxcategory/[categorySlug]/_components/BlogCategoryPageContent.tsxcategory/[categorySlug]/page.tsxpage.tsxtag/[tagSlug]/_components/BlogTagPageContent.tsxtag/[tagSlug]/page.tsx
每日卡牌(公开列表) — app/[locale]/(main)/card-of-day/
CardOfDayHistory.tsxCardOfDayListView.tsx
反馈/支持页面 — app/[locale]/(main)/feedback/
_components/FeedbackFAQ.tsx_components/FeedbackForm.tsx_components/FeedbackPageContent.tsxpage.tsx
分享页面和 OG 图片 — app/[locale]/(main)/share/card-of-the-day/[slug]/
opengraph-image.tsxpage.tsx
"传播洞见"活动页面 — app/[locale]/(main)/spread-the-insight/
_components/ActivityPeriodBanner.tsx_components/ActivityRulesSection.tsx_components/ContentRequirementsSection.tsx_components/FinalCtaSection.tsx_components/HeroSection.tsx_components/HowToParticipateSection.tsx_components/PlatformsSection.tsx_components/RewardsTierSection.tsxpage.tsx
API 路由 — app/api/
me/avatar/route.ts
全局共享组件 — components/
AiMemoryModal.tsxAuthModal.tsxAvatarCropModal/AvatarCropModal.tsxblog/BlogCard.tsxblog/BlogCategoryChips.tsxblog/BlogHero.tsxblog/BlogRelatedPosts.tsxCardOfDay/CardOfDayCalendar.tsxCardOfDay/CardOfDayModal.tsxCardOfDay/CardOfDayPicker.tsxCardOfDay/CardOfDayResult.tsxCardOfDay/ShareModal.tsxCreditPurchaseModal.tsxDeleteAccountCard.tsxFeedbackWidget/FeedbackWidget.tsxlist/AllLoaded.tsxlist/NoData.tsxSpreadSuggestOverlay/SpreadSuggestOverlay.tsxTarotReaderSelector.tsx
认证库和应用提供商 — lib/ 和 providers/
lib/auth.tsxproviders/AppAuthUIProvider.tsx
阶段 5:检查新文件
批量工作完成后,重新运行发现 grep 以捕获自本指南上次更新以来新增的文件:
grep -rl "trans({" --include="*.tsx" --include="*.ts" app/ components/ lib/ providers/ | sort与阶段 4 的列表进行对比。任何新文件都必须添加新语言键。
阶段 6:验证
6.1 TypeScript — 必须零错误
pnpm tsc --noEmit错误会指向缺少语言键的确切文件和行。
6.2 Grep 检查
# 查找 trans() 计数与 xx: 计数不匹配的文件
for f in $(grep -rl "trans({" --include="*.tsx" --include="*.ts" app/ components/ lib/ providers/); do
trans_count=$(grep -c "trans({" "$f" 2>/dev/null || echo 0)
xx_count=$(grep -c "xx:" "$f" 2>/dev/null || echo 0)
if [ "$trans_count" -gt "$xx_count" ]; then
echo "NEEDS CHECK: $f (trans=$trans_count, xx=$xx_count)"
fi
done6.3 构建检查
pnpm build6.4 视觉质量检查
启动开发服务器并访问:
| URL | 检查项 |
|---|---|
/xx | 新语言的首页 |
/xx/one-card | 单牌牌阵页面 |
/xx/three-card | 三牌牌阵页面 |
/xx/daily-tarot | 每日塔罗页面 |
注意:应翻译却仍显示英文的文本、布局错乱、语言切换下拉菜单中缺少该语言。
常见陷阱
zh_TW 被通用 zh 检查吞掉 — 如果添加任何 zh-* 变体,请在 useTrans.ts、getTrans.ts、useAppLocale.tsx 和 request.ts 中将其放在 startsWith('zh') 检查之前。遗漏这一点会将所有 zh-XX URL 映射到简体中文。
语言槽位中出现英文文本 — 验证每个 id:、nl: 或新语言的值确实是目标语言。模型在不确定时有时会默认使用英文。
繁体中文 vs 简体中文 — zh_TW 使用繁体字(繁體)和与 zh_CN(简体)不同的词汇。切勿将 zh_CN 的值复制粘贴到 zh_TW。
buildAuthLocalization.ts 无需更改 — 一旦 consts.ts 更新,它会自动从 TAppLocale 派生类型。
buildPageMetadata.ts 中的 localesInUrl — 忘记在此处添加语言意味着新语言将缺少 hreflang 标签,影响 SEO。
新塔罗牌阵页面 — 每个新牌阵页面会添加 2 个文件(page.tsx + SpreadNameContent.tsx)。在宣布工作完成之前,重新运行阶段 5 的发现 grep。