Skip to content

为 MysticX 添加新语言

本指南涵盖了为应用添加新语言所需的每个文件和步骤。请按阶段顺序执行。

当前语言(12 种): enzh_CNzh_TWjakoptesfrdearidnl


阶段 1:基础设施

这 7 个文件将新语言接入框架。在触碰任何翻译内容之前,请先完成所有这些文件。

1.1 i18n/consts.ts

将新语言添加到 LOCALE_AND_LABEL_MAP(母语名称标签)和 LOCALE_ENGLISH_LABELS(管理面板的英文标签):

typescript
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):

typescript
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 吞掉:

typescript
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 相同的逻辑 — 同样的排序规则适用:

typescript
if (detected?.toLowerCase() === 'xx') return 'xx';  // 添加

1.5 i18n/useAppLocale.tsx

同样的逻辑:

typescript
if (locale.toLowerCase() === 'xx') return 'xx';  // 添加

1.6 i18n/request.ts

更新 messageFile 映射以将新语言解析到其 JSON 文件。如果新语言有连字符 URL 段(如 zh-TW),请将其添加在通用前缀检查之前:

typescript
const messageFile =
  locale.startsWith('zh-TW') || locale.startsWith('zh_TW') ? 'zh_TW'
  : locale.startsWith('zh') ? 'zh_CN'
  : locale;

对于没有前缀冲突的简单语言(xx),此处无需更改 — 默认直通处理即可。

1.7 创建消息文件

bash
echo '{}' > messages/xx.json

验证基础设施

bash
pnpm tsc --noEmit 2>&1 | head -60

你应该看到 TypeScript 错误,指出每个缺少 xx:trans() 调用。该数量就是你阶段 4 的范围。


阶段 2:认证与本地化文件

将新语言键添加到 lib/localization/ 中的每个错误消息对象。每个文件遵循此模式:

typescript
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 段格式):

typescript
const localesInUrl = ['zh-CN', 'zh-TW', 'ja', 'ko', 'pt', 'es', 'fr', 'de', 'ar', 'id', 'nl', 'xx'];

3.2 添加到 alternates.languages

typescript
languages: {
  'en-US': buildUrl('en'),
  // ...现有条目...
  'xx-XX': buildUrl('xx'),  // 添加 — 使用正确的 BCP 47 标签
  'x-default': buildUrl('en'),
},

阶段 4:trans() 调用点

将新语言键添加到应用中每个 trans({...}) 调用。运行以下命令获取当前文件列表:

bash
grep -rl "trans({" --include="*.tsx" --include="*.ts" app/ components/ lib/ providers/ | sort

规则

  1. 不要使用英文作为回退值。 每个值都必须是目标语言。
  2. 对于 JSX 值(如 trans({ en: <><strong>Hello</strong></> })),保持 JSX 结构不变 — 只翻译内部文本。
  3. 编辑每个文件后进行抽查:grep -c "xx:" <file> 应大致等于 grep -c "trans({" <file>

当前文件列表(截至 2026 年 4 月约 75 个文件)

根布局、错误和 404 页面 — app/[locale]/

  • layout.tsx
  • error.tsx
  • not-found.tsx

网站头部 — app/[locale]/_components/SiteHeader/

  • CardOfDayButton.tsx
  • CreditBadge.tsx
  • LanguageModal.tsx
  • MobileDrawerMenu.tsx
  • NotificationBell.tsx
  • SiteHeader.tsx
  • UserDropdown.tsx
  • SiteFooter.tsx

其他共享布局组件 — app/[locale]/_components/

  • OnLayoutLoaded.tsx

首页和认证页面 — app/[locale]/(main)/

  • page.tsx
  • auth/[path]/page.tsx
  • auth/telegram/page.tsx

选牌弹窗 — app/[locale]/(main)/_components/CardPickerModal/

  • CardPickerModalCenterInfo.tsx
  • CardPickerModalControls.tsx
  • CardRevealSpread.tsx

首页主视觉区 — app/[locale]/(main)/_components/

  • HeroSection.tsx

仪表盘 — app/[locale]/(main)/dashboard/

  • BtnSignOut.tsx
  • page.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.tsx
  • card-skins/_components/MyCardSkinsContent.tsx
  • invitations/_components/InvitationsPageContent.tsx
  • membership/_components/MembershipContent.tsx
  • tarot-readers/_components/MyTarotReadersContent.tsx

会员/定价页面 — app/[locale]/(main)/membership/

  • _components/ComparePlansSection.tsx
  • _components/FAQSection.tsx
  • _components/HeroSection.tsx
  • _components/MembershipPageContent.tsx
  • _components/PricingSection.tsx
  • page.tsx

卡牌皮肤市场 — app/[locale]/(main)/market/card-skins/

  • _components/CardSkinsMarketContent.tsx
  • page.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.tsx
  • page.tsx

占卜聊天界面 — app/[locale]/(main)/tarot-readings/[readingId]/_components/

  • ChatMessageBubble.tsx
  • ChatUserInput.tsx
  • LuckEnhancement.tsx
  • MessageVoiceButton.tsx
  • ReadingDetailView.tsx
  • ReadingInfoPanel.tsx
  • SuggestedQuestions.tsx

共享牌阵页面组件 — app/[locale]/(main)/(tarot-spreads)/_components/

  • CardPositionsExplainedSection.tsx
  • SpreadFAQSection.tsx
  • SpreadHeroSection/SpreadHeroSection.tsx

各牌阵页面(13 种牌阵) — app/[locale]/(main)/(tarot-spreads)/

  • celtic-cross/_components/CelticCrossContent.tsx + page.tsx
  • daily-tarot/_components/DailyTarotContent.tsx + page.tsx
  • inner-child-healing/_components/InnerChildHealingContent.tsx + page.tsx
  • love-tarot/_components/LoveReadingChoiceModal.tsx
  • love-tarot/_components/LoveTarotContent.tsx + page.tsx
  • obstacle-key/_components/ObstacleKeyContent.tsx + page.tsx
  • one-card/_components/OneCardContent.tsx + page.tsx
  • relationship-compass/_components/RelationshipCompassContent.tsx + page.tsx
  • shadow-work/_components/ShadowWorkContent.tsx + page.tsx
  • three-card/_components/ThreeCardContent.tsx + page.tsx
  • twin-flame-mirror/_components/TwinFlameMirrorContent.tsx + page.tsx
  • two-path-choice/_components/TwoPathChoiceContent.tsx + page.tsx
  • yes-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.tsx
  • page.tsx

博客 — app/[locale]/(main)/blog/

  • _components/BlogIndexContent.tsx
  • [slug]/_components/BlogPostPageContent.tsx
  • category/[categorySlug]/_components/BlogCategoryPageContent.tsx
  • category/[categorySlug]/page.tsx
  • page.tsx
  • tag/[tagSlug]/_components/BlogTagPageContent.tsx
  • tag/[tagSlug]/page.tsx

每日卡牌(公开列表) — app/[locale]/(main)/card-of-day/

  • CardOfDayHistory.tsx
  • CardOfDayListView.tsx

反馈/支持页面 — app/[locale]/(main)/feedback/

  • _components/FeedbackFAQ.tsx
  • _components/FeedbackForm.tsx
  • _components/FeedbackPageContent.tsx
  • page.tsx

分享页面和 OG 图片 — app/[locale]/(main)/share/card-of-the-day/[slug]/

  • opengraph-image.tsx
  • page.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.tsx
  • page.tsx

API 路由 — app/api/

  • me/avatar/route.ts

全局共享组件 — components/

  • AiMemoryModal.tsx
  • AuthModal.tsx
  • AvatarCropModal/AvatarCropModal.tsx
  • blog/BlogCard.tsx
  • blog/BlogCategoryChips.tsx
  • blog/BlogHero.tsx
  • blog/BlogRelatedPosts.tsx
  • CardOfDay/CardOfDayCalendar.tsx
  • CardOfDay/CardOfDayModal.tsx
  • CardOfDay/CardOfDayPicker.tsx
  • CardOfDay/CardOfDayResult.tsx
  • CardOfDay/ShareModal.tsx
  • CreditPurchaseModal.tsx
  • DeleteAccountCard.tsx
  • FeedbackWidget/FeedbackWidget.tsx
  • list/AllLoaded.tsx
  • list/NoData.tsx
  • SpreadSuggestOverlay/SpreadSuggestOverlay.tsx
  • TarotReaderSelector.tsx

认证库和应用提供商 — lib/providers/

  • lib/auth.tsx
  • providers/AppAuthUIProvider.tsx

阶段 5:检查新文件

批量工作完成后,重新运行发现 grep 以捕获自本指南上次更新以来新增的文件:

bash
grep -rl "trans({" --include="*.tsx" --include="*.ts" app/ components/ lib/ providers/ | sort

与阶段 4 的列表进行对比。任何新文件都必须添加新语言键。


阶段 6:验证

6.1 TypeScript — 必须零错误

bash
pnpm tsc --noEmit

错误会指向缺少语言键的确切文件和行。

6.2 Grep 检查

bash
# 查找 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
done

6.3 构建检查

bash
pnpm build

6.4 视觉质量检查

启动开发服务器并访问:

URL检查项
/xx新语言的首页
/xx/one-card单牌牌阵页面
/xx/three-card三牌牌阵页面
/xx/daily-tarot每日塔罗页面

注意:应翻译却仍显示英文的文本、布局错乱、语言切换下拉菜单中缺少该语言。


常见陷阱

zh_TW 被通用 zh 检查吞掉 — 如果添加任何 zh-* 变体,请在 useTrans.tsgetTrans.tsuseAppLocale.tsxrequest.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。

Internal documentation for MysticX team