Files
frontend_v2/src/i18n/dictionary.ts
toom1996 64af75c0a9
Some checks failed
deploy / deploy (push) Has been cancelled
update
2026-09-02 21:50:30 +08:00

223 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { DEFAULT_LOCALE, type Locale } from '@/i18n/config';
/**
* 单文件翻译字典。
*
* 设计(按你的要求):
* - key 就是「英文原文」,直接当文案用,无需额外维护英文副本;
* - value 只存「非默认语言」的翻译(本项目默认语言是 en,所以只存 cn 等);
* - 英文环境调用 t(key) 直接返回 key 本身;
* - 其它语言从 value 里取对应翻译,取不到就回退英文(开发环境会告警,方便发现漏翻)。
*
* 新增一条:直接加一行 `'英文原文': { cn: '中文' }` 即可;
* 当前是英文时根本不需要在字典里登记,写了 t('Nice day!') 就能渲染 "Nice day!"。
*/
// 除默认语言外的所有语言(如 'cn')。加新语言(ja 等)时这里会自动包含。
type OtherLocale = Exclude<Locale, typeof DEFAULT_LOCALE>;
type TranslationEntry = Partial<Record<OtherLocale, string>>;
export const dict: Record<string, TranslationEntry> = {
// ── 导航 / Navigation ──
'全部的 runway': { cn: '全部的 runway' },
'home': { cn: '首页' },
'login': { cn: '登录' },
'portfolio': { cn: '作品集' },
'runway archive': { cn: '走秀档案' },
// ── Runway 子分类(header 下拉,与列表页 COLLECTION 筛选一致)──
'ready-to-wear': { cn: '女装成衣' },
'menswear': { cn: '男装' },
'couture': { cn: '高级定制' },
'resort': { cn: '度假系列' },
'pre-fall': { cn: '早秋系列' },
'signup': { cn: '注册' },
// ── 账号 / Account ──
'hi': { cn: '你好' },
'password': { cn: '密码' },
'account': { cn: '账户' },
'sign out': { cn: '退出登录' },
'my account': { cn: '我的账户' },
'username or email': { cn: '用户名或邮箱' },
'login failed': { cn: '登录失败,请检查账号或密码' },
'please log in': { cn: '请先登录' },
'login to continue': { cn: '登录以继续使用筛选与翻页' },
'login prompt': { cn: '登录后可筛选与翻页浏览全部档案' },
'members': { cn: '会员专区' },
'back to site': { cn: '返回网站' },
'email': { cn: '邮箱' },
'my collections': { cn: '我的收藏' },
'my history': { cn: '浏览历史' },
'coming soon': { cn: '敬请期待' },
// ── 账户中心 / Account Center(企业级个人中心)──
'overview': { cn: '概览' },
'sessions & security': { cn: '会话与安全' },
'settings': { cn: '设置' },
'saved looks': { cn: '收藏造型' },
'active sessions': { cn: '活跃会话' },
'member since': { cn: '加入于' },
'role': { cn: '角色' },
'internal member': { cn: '内部成员' },
'items': { cn: '条' },
'account overview': { cn: '账户概览' },
'account details': { cn: '账户详情' },
'username': { cn: '用户名' },
'edit profile': { cn: '编辑资料' },
'view archive': { cn: '浏览档案' },
'no collections yet': { cn: '暂无收藏内容' },
'no history yet': { cn: '暂无浏览记录' },
'browse the archive': { cn: '去浏览档案' },
'current session': { cn: '当前会话' },
'this device': { cn: '此设备' },
'last active': { cn: '最近活跃' },
'sign out all devices': { cn: '退出所有设备' },
'single session note': { cn: '为保障账号安全,同一账号仅允许在一处登录,新登录会顶替旧会话。' },
'preferences': { cn: '偏好设置' },
'appearance': { cn: '外观' },
'notifications': { cn: '通知' },
'soon': { cn: '即将推出' },
'quick stats': { cn: '数据概览' },
'now': { cn: '刚刚' },
'member profile': { cn: '会员资料' },
'save': { cn: '收藏' },
'saved': { cn: '已收藏' },
'remove': { cn: '移除' },
'cancel': { cn: '取消' },
'save changes': { cn: '保存修改' },
'display name': { cn: '昵称' },
'bio': { cn: '简介' },
'clear history': { cn: '清空历史' },
'no saved looks yet': { cn: '暂无收藏造型' },
'no history yet': { cn: '暂无浏览记录' },
'all': { cn: '全部' },
'image': { cn: '图片' },
'gallery': { cn: '图集' },
'viewed gallery': { cn: '浏览的图集' },
'light': { cn: '浅色' },
'dark': { cn: '深色' },
'on': { cn: '开' },
'off': { cn: '关' },
// ── 控件 / Controls ──
'back to index': { cn: '返回首页' },
'back to top': { cn: '回到顶部' },
'close': { cn: '关闭' },
'previous image': { cn: '上一张' },
'next image': { cn: '下一张' },
'previous': { cn: '上一组' },
'next': { cn: '下一组' },
// ── 错误 / Errors ──
'no article id specified': { cn: '未指定文章 ID' },
'failed to load article. please refresh or try later.': { cn: '加载文章失败,请刷新或稍后再试' },
'click image to zoom · scroll to view · esc to close': { cn: '点击图片放大 · 上下滚动查看 · Esc 关闭' },
// ── 页脚 / Footer ──
'© 2026 portfolio studio. all rights reserved.': { cn: '© 2026 PORTFOLIO STUDIO. 保留所有权利。' },
// ── 页脚工具 / Footer tools ──
'color studio tools': { cn: '色彩工作室工具' },
'ai color trend generator': { cn: 'AI 色彩趋势生成器' },
'photo swatch extractor': { cn: '图片色板提取器' },
'upload': { cn: '上传' },
'reset b&w theme': { cn: '重置黑白主题' },
'default': { cn: '默认' },
// ── 页脚简介 / Manifesto ──
'minimalist editorial portfolio documenting haute couture shows, backstage fittings, and street culture. integrated with 2026 color forecast intelligence and ai palette generator.':
{ cn: '极简编辑风格作品集,记录高级定制秀场、后台试装与街头文化。集成 2026 色彩预测情报与 AI 调色板生成器。' },
// ── 登录态 / Auth ──
'hi': { cn: '你好' },
'logout': { cn: '退出' },
'language': { cn: '语言' },
'runway looks': { cn: '秀场造型' },
// ── 街拍 / Street Snaps ──
'street style': { cn: '街拍' },
'street snaps': { cn: '街拍' },
'all street snaps': { cn: '全部街拍' },
'no street snap id specified': { cn: '未指定街拍 ID' },
'failed to load street snap. please refresh or try later.': { cn: '加载街拍失败,请刷新或稍后再试' },
// ── 地区 / Cities(街拍按城市分类,值对齐后端 street_snap.city 列;translate 会小写化 key)──
'city': { cn: '地区' },
'paris': { cn: '巴黎' },
'new york': { cn: '纽约' },
'milan': { cn: '米兰' },
'london': { cn: '伦敦' },
'copenhagen': { cn: '哥本哈根' },
'florence': { cn: '佛罗伦萨' },
'berlin': { cn: '柏林' },
'tokyo': { cn: '东京' },
'seoul': { cn: '首尔' },
'shanghai': { cn: '上海' },
'beijing': { cn: '北京' },
'rome': { cn: '罗马' },
'madrid': { cn: '马德里' },
'amsterdam': { cn: '阿姆斯特丹' },
'antwerp': { cn: '安特卫普' },
'los angeles': { cn: '洛杉矶' },
'moscow': { cn: '莫斯科' },
'mumbai': { cn: '孟买' },
};
/** 把 {name} 形式的占位符替换成参数值。 */
function interpolate(text: string, params?: Record<string, string | number>): string {
if (!params) return text;
return text.replace(/\{(\w+)\}/g, (_, k) => (k in params ? String(params[k]) : `{${k}}`));
}
/**
* 取翻译:
* - 当前是默认语言(en)→ 直接返回 key(英文原文);
* - 其它语言 → 优先取字典里的翻译,缺失则回退英文并返回 key(不会白屏)。
*/
export function translate(key: string, locale: Locale, params?: Record<string, string | number>): string {
if (locale === DEFAULT_LOCALE) return interpolate(key, params);
const localized = dict[key.toLowerCase()]?.[locale as OtherLocale];
if (localized) return interpolate(localized, params);
if (import.meta.env.DEV) {
console.warn(`[i18n] 缺少 "${locale}" 翻译:"${key}",已回退英文`);
}
return interpolate(key, params);
}