feat: introduce i18n system and class invitation codes
Add complete i18n infrastructure using next-intl (cookie-driven, without i18n routing) with zh-CN/en dictionary files, locale switcher, and NextIntlClientProvider in root layout. Add class invitation code system with new class_invitation_codes table, data-access layer (generate/validate/consume/revoke), server actions with permission checks, rate limiting, and audit logging. Add class-invitation-manager UI component. Refactor onboarding stepper to use i18n translations and accept new invitation code format (6-char alphanumeric) with backward compatibility for legacy 6-digit codes.
This commit is contained in:
43
src/shared/i18n/locale.ts
Normal file
43
src/shared/i18n/locale.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 项目 i18n 配置(without i18n routing 模式)。
|
||||
*
|
||||
* 设计决策(v3 引入完整 i18n 体系):
|
||||
* - 采用 next-intl 4.x,官方推荐的 Next.js App Router 方案
|
||||
* - 不使用 `[locale]` 路由段,避免破坏现有 (auth)/(dashboard)/(onboarding) 路由组结构
|
||||
* - locale 通过 cookie 持久化,SSR 时从 cookie 读取
|
||||
* - 字典放在 shared/i18n/messages/,符合三层架构约束
|
||||
*
|
||||
* 支持的 locale:
|
||||
* - zh-CN(默认):简体中文
|
||||
* - en:英文
|
||||
*/
|
||||
|
||||
export const LOCALES = ["zh-CN", "en"] as const;
|
||||
export type Locale = (typeof LOCALES)[number];
|
||||
|
||||
export const DEFAULT_LOCALE: Locale = "zh-CN";
|
||||
|
||||
/** Cookie 名称,与 proxy.ts / request.ts 保持一致 */
|
||||
export const LOCALE_COOKIE = "NEXT_LOCALE";
|
||||
|
||||
/** Cookie 属性:1 年有效期,全站可读 */
|
||||
export const LOCALE_COOKIE_OPTIONS = {
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
path: "/",
|
||||
sameSite: "lax" as const,
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验字符串是否为受支持的 locale。
|
||||
* 用于从 cookie/header 读取后的容错处理。
|
||||
*/
|
||||
export function isLocale(value: string | null | undefined): value is Locale {
|
||||
return typeof value === "string" && (LOCALES as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将任意字符串归一化为合法 locale,非法值回退到默认。
|
||||
*/
|
||||
export function normalizeLocale(value: string | null | undefined): Locale {
|
||||
return isLocale(value) ? value : DEFAULT_LOCALE;
|
||||
}
|
||||
Reference in New Issue
Block a user