Files
pokopiawiki.tootaio.com/frontend/src/i18n.ts
xiaomai 6812ddc428 feat(ui): use modal dialogs for entity creation and editing
Introduce reusable Modal component for forms
Update router to preserve scroll position when toggling modals
Refactor admin and entity views to render editors as overlays
2026-05-01 13:44:34 +08:00

568 lines
18 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 { createI18n } from 'vue-i18n';
export const defaultLocale = 'en';
const localeStorageKey = 'pokopia_locale';
const localeChangeEvent = 'pokopia-locale-change';
const messages = {
en: {
common: {
add: 'Add',
admin: 'Admin',
all: 'All',
back: 'Back',
backToList: 'Back to list',
cancel: 'Cancel',
close: 'Close',
create: 'Create',
delete: 'Delete',
edit: 'Edit',
filters: 'Filters',
loading: 'Loading',
name: 'Name',
new: 'New',
none: 'None',
save: 'Save',
saving: 'Saving',
search: 'Search',
select: 'Select',
selected: 'Selected',
system: 'System',
noRecords: 'No records',
fieldForLanguage: '{field} ({language})',
searchOrSelect: 'Search or select',
noMatches: 'No matches',
createNamed: 'Add "{name}"',
creating: 'Adding',
removeNamed: 'Remove {name}',
quantity: 'Quantity',
required: 'Required'
},
nav: {
pokemon: 'Pokemon',
habitats: 'Habitats',
items: 'Items',
recipes: 'Recipes',
checklist: 'CheckList',
admin: 'Admin',
main: 'Main navigation',
language: 'Language',
login: 'Log in',
logout: 'Log out',
register: 'Register'
},
auth: {
email: 'Email',
password: 'Password',
displayName: 'Display name',
loginTitle: 'Log in',
loginSubtitle: 'Use a verified email to enter Pokopia Wiki.',
loggingIn: 'Logging in',
loginFailed: 'Login failed',
noAccount: 'No account yet?',
registerTitle: 'Register',
registerSubtitle: 'Verify your email after creating an account.',
registerFailed: 'Registration failed',
sending: 'Sending',
sendVerification: 'Send verification email',
hasAccount: 'Already have an account?',
verifyTitle: 'Email verification',
verifySubtitle: 'You can log in after verification is complete.',
verifyingEmail: 'Verifying email',
invalidVerification: 'The verification link is invalid or expired.',
verifyFailed: 'Email verification failed',
goLogin: 'Go to login'
},
errors: {
requestFailed: 'Request failed ({status})',
operationFailed: 'Operation failed',
loadFailed: 'Load failed',
addFailed: 'Add failed',
saveFailed: 'Save failed',
completeEmailVerification: 'Please complete email verification first.'
},
pages: {
pokemon: {
title: 'Pokemon',
subtitle: 'Search Pokemon and filter by specialities, ideal habitat, and favourites.',
detailKicker: 'Pokédex Detail',
editKicker: 'Pokédex Edit',
editSubtitle: 'Maintain Pokemon profile, specialities, and favourites.',
newTitle: 'New Pokemon',
editTitle: 'Edit #{id} {name}',
loadingList: 'Loading Pokemon list',
loadingDetail: 'Loading Pokemon detail',
loadingEdit: 'Loading Pokemon editor',
environmentPrefix: 'Ideal Habitat: {name}',
environment: 'Ideal Habitat',
skills: 'Specialities',
skillMatchMode: 'Speciality match mode',
any: 'Any',
all: 'All',
favoriteThings: 'Favourites',
favoriteThingMatchMode: 'Favourites match mode',
skillDrops: 'Speciality drops',
skillDrop: '{name} drop',
dropItem: 'Drop item',
searchPokemon: 'Search Pokemon',
relatedItems: 'Related items',
relatedItemCategory: 'Related item category',
habitats: 'Habitats',
namePlaceholder: 'Name',
searchEnvironment: 'Search ideal habitats',
searchSkills: 'Search specialities',
searchFavoriteThings: 'Search favourites',
searchItems: 'Search items'
},
habitats: {
title: 'Habitats',
subtitle: 'View recipes and Pokemon that may appear.',
detailSubtitle: 'Habitat detail',
editSubtitle: 'Maintain habitat recipes and possible Pokemon appearances.',
newTitle: 'New habitat',
editTitle: 'Edit {name}',
fallbackName: 'Habitat',
loadingList: 'Loading habitat list',
loadingDetail: 'Loading habitat detail',
loadingEdit: 'Loading habitat editor',
recipe: 'Recipe',
recipeList: 'Recipe list',
possiblePokemon: 'Possible Pokemon',
addItem: 'Add item',
addPokemon: 'Add Pokemon',
maps: 'Maps',
searchMaps: 'Search maps'
},
items: {
title: 'Items',
subtitle: 'Browse items by category, usage, and tags.',
detailKicker: 'Item Detail',
detailSubtitle: 'Item detail',
editKicker: 'Item Edit',
editSubtitle: 'Maintain item category, usage, acquisition methods, customization, and tags.',
newTitle: 'New item',
editTitle: 'Edit {name}',
fallbackName: 'Item',
loadingList: 'Loading item list',
loadingDetail: 'Loading item detail',
loadingEdit: 'Loading item editor',
category: 'Category',
usage: 'Usage',
tags: 'Tags',
acquisitionMethods: 'Acquisition methods',
customization: 'Customization',
dyeable: 'Dyeable',
dualDyeable: 'Dual dyeable',
patternEditable: 'Pattern editable',
noRecipe: 'No recipe',
recipeInfo: 'Recipe info',
relatedRecipes: 'Related recipes',
relatedHabitats: 'Related habitats',
pokemonDrops: 'Pokemon drops',
createRecipe: 'Create recipe',
searchCategory: 'Search categories',
searchUsage: 'Search usages',
searchMethods: 'Search acquisition methods',
searchTags: 'Search tags'
},
recipes: {
title: 'Recipes',
subtitle: 'Browse recipes by category, usage, and tags.',
detailKicker: 'Recipe Detail',
detailSubtitle: 'Recipe detail',
editKicker: 'Recipe Edit',
editSubtitle: 'Maintain result item, acquisition methods, and materials.',
newTitle: 'New recipe',
editTitle: 'Edit {name}',
fallbackName: 'Recipe',
loadingList: 'Loading recipe list',
loadingDetail: 'Loading recipe detail',
loadingEdit: 'Loading recipe editor',
item: 'Item',
materials: 'Materials',
addMaterial: 'Add material'
},
checklist: {
title: 'Daily checklist',
subtitle: 'See what can be completed each day.',
sectionTitle: 'Daily tasks',
empty: 'No daily checklist',
loading: 'Loading daily checklist',
task: 'Task',
newTask: 'New task',
editTask: 'Edit task'
},
admin: {
title: 'Admin',
subtitle: 'Maintain system configuration and manage Wiki records.',
modules: 'Admin modules',
loading: 'Loading admin list',
config: 'System config',
configType: 'System config type',
checklist: 'CheckList',
pokemonList: 'Pokemon list',
itemList: 'Item list',
recipeList: 'Recipe list',
habitatList: 'Habitat list',
languages: 'Languages',
newConfig: 'New {name}',
editConfig: 'Edit {name}',
hasItemDrop: 'Has item drop',
dragSort: 'Drag to reorder: {name}',
dragSortTitle: 'Drag to reorder',
languageCode: 'Code',
languageName: 'Language name',
enabled: 'Enabled',
defaultLanguage: 'Default language',
sortOrder: 'Sort order',
newLanguage: 'New language',
editLanguage: 'Edit language'
}
},
config: {
skills: 'Specialities',
environments: 'Ideal Habitats',
favoriteThings: 'Favourites / tags',
itemCategories: 'Item categories',
itemUsages: 'Item usages',
acquisitionMethods: 'Acquisition methods',
maps: 'Maps'
},
appearance: {
time: 'Time',
weather: 'Weather',
rarity: 'Rarity',
map: 'Map',
maps: 'Maps',
morning: 'Morning',
noon: 'Noon',
evening: 'Evening',
night: 'Night',
sunny: 'Sunny',
cloudy: 'Cloudy',
rainy: 'Rainy',
stars: '{count} stars'
},
history: {
title: 'Contribution records',
createdBy: 'Created by',
lastEdited: 'Last edited',
editHistory: 'Edit history',
before: 'Before',
after: 'After',
author: 'Author',
time: 'Time',
action: 'Action',
create: 'Create',
update: 'Edit',
delete: 'Delete',
empty: 'No edit history'
}
},
'zh-CN': {
common: {
add: '添加',
admin: '管理',
all: '全部',
back: '返回',
backToList: '返回列表',
cancel: '取消',
close: '关闭',
create: '创建',
delete: '删除',
edit: '编辑',
filters: '筛选',
loading: '加载中',
name: '名称',
new: '新建',
none: '无',
save: '保存',
saving: '保存中',
search: '搜索',
select: '请选择',
selected: '已选',
system: '系统',
noRecords: '暂无记录',
fieldForLanguage: '{field}{language}',
searchOrSelect: '搜索或选择',
noMatches: '没有匹配项',
createNamed: '添加「{name}」',
creating: '添加中',
removeNamed: '移除{name}',
quantity: '数量',
required: '必填'
},
nav: {
pokemon: 'Pokemon',
habitats: '栖息地',
items: '物品',
recipes: '材料单',
checklist: 'CheckList',
admin: '管理',
main: '主导航',
language: '语言',
login: '登录',
logout: '退出',
register: '注册'
},
auth: {
email: '邮箱',
password: '密码',
displayName: '显示名',
loginTitle: '登录',
loginSubtitle: '使用已验证邮箱进入 Pokopia Wiki',
loggingIn: '登录中',
loginFailed: '登录失败',
noAccount: '还没有账号?',
registerTitle: '注册',
registerSubtitle: '创建账号后需要完成邮箱验证',
registerFailed: '注册失败',
sending: '发送中',
sendVerification: '发送验证邮件',
hasAccount: '已有账号?',
verifyTitle: '邮箱验证',
verifySubtitle: '完成验证后即可登录',
verifyingEmail: '正在验证邮箱',
invalidVerification: '验证链接无效或已过期',
verifyFailed: '邮箱验证失败',
goLogin: '去登录'
},
errors: {
requestFailed: '请求失败({status}',
operationFailed: '操作失败',
loadFailed: '加载失败',
addFailed: '添加失败',
saveFailed: '保存失败',
completeEmailVerification: '请先完成邮箱验证'
},
pages: {
pokemon: {
title: 'Pokemon',
subtitle: '搜索宝可梦,并按特长、环境、喜欢的东西筛选。',
detailKicker: 'Pokédex Detail',
editKicker: 'Pokédex Edit',
editSubtitle: '维护 Pokemon 基本资料、特长和喜欢的东西。',
newTitle: '新增 Pokemon',
editTitle: '编辑 #{id} {name}',
loadingList: '正在加载 Pokemon 列表',
loadingDetail: '正在加载 Pokemon 详情',
loadingEdit: '正在加载 Pokemon 编辑内容',
environmentPrefix: '喜欢的环境:{name}',
environment: '喜欢的环境',
skills: '特长',
skillMatchMode: '特长匹配方式',
any: '任意',
all: '全部',
favoriteThings: '喜欢的东西',
favoriteThingMatchMode: '喜欢的东西匹配方式',
skillDrops: '特长掉落物',
skillDrop: '{name}掉落物',
dropItem: '掉落物',
searchPokemon: '搜索 Pokemon',
relatedItems: '关联物品',
relatedItemCategory: '关联物品分类',
habitats: '栖息地',
namePlaceholder: '名字',
searchEnvironment: '搜索喜欢的环境',
searchSkills: '搜索特长',
searchFavoriteThings: '搜索喜欢的东西',
searchItems: '搜索物品'
},
habitats: {
title: '栖息地',
subtitle: '查看配方和可能出现的宝可梦。',
detailSubtitle: '栖息地详情',
editSubtitle: '维护栖息地配方和可能出现的 Pokemon。',
newTitle: '新增栖息地',
editTitle: '编辑 {name}',
fallbackName: '栖息地',
loadingList: '正在加载栖息地列表',
loadingDetail: '正在加载栖息地详情',
loadingEdit: '正在加载栖息地编辑内容',
recipe: '配方',
recipeList: '配方列表',
possiblePokemon: '可能出现的宝可梦',
addItem: '添加物品',
addPokemon: '添加 Pokemon',
maps: '地图',
searchMaps: '搜索地图'
},
items: {
title: '物品',
subtitle: '按分类、用途、标签查看物品。',
detailKicker: 'Item Detail',
detailSubtitle: '物品详情',
editKicker: 'Item Edit',
editSubtitle: '维护物品分类、用途、入手方式、自定义和标签。',
newTitle: '新增物品',
editTitle: '编辑 {name}',
fallbackName: '物品',
loadingList: '正在加载列表',
loadingDetail: '正在加载物品详情',
loadingEdit: '正在加载物品编辑内容',
category: '分类',
usage: '用途',
tags: '标签',
acquisitionMethods: '入手方式',
customization: '自定义',
dyeable: '可染色',
dualDyeable: '可双区染色',
patternEditable: '可改花纹',
noRecipe: '无材料单',
recipeInfo: '材料单信息',
relatedRecipes: '相关材料单',
relatedHabitats: '相关栖息地',
pokemonDrops: 'Pokemon 掉落',
createRecipe: '创建材料单',
searchCategory: '搜索分类',
searchUsage: '搜索用途',
searchMethods: '搜索入手方式',
searchTags: '搜索标签'
},
recipes: {
title: '材料单',
subtitle: '按分类、用途、标签查看材料单。',
detailKicker: 'Recipe Detail',
detailSubtitle: '材料单详情',
editKicker: 'Recipe Edit',
editSubtitle: '维护材料单结果物品、入手方式和需要材料。',
newTitle: '新增材料单',
editTitle: '编辑 {name}',
fallbackName: '材料单',
loadingList: '正在加载材料单列表',
loadingDetail: '正在加载材料单详情',
loadingEdit: '正在加载材料单编辑内容',
item: '物品',
materials: '需要材料',
addMaterial: '添加材料'
},
checklist: {
title: '每日清单',
subtitle: '查看每天可以完成的事项。',
sectionTitle: '每日做什么',
empty: '暂无每日清单',
loading: '正在加载每日清单',
task: 'Task',
newTask: '新增 Task',
editTask: '编辑 Task'
},
admin: {
title: '管理',
subtitle: '维护系统配置,查看并删除 Wiki 数据记录。',
modules: '管理模块',
loading: '正在加载管理列表',
config: '系统配置',
configType: '系统配置类型',
checklist: 'CheckList',
pokemonList: 'Pokemon 列表',
itemList: '物品列表',
recipeList: '材料单列表',
habitatList: '栖息地列表',
languages: '语言',
newConfig: '新增{name}',
editConfig: '编辑{name}',
hasItemDrop: '有掉落物',
dragSort: '拖曳排序:{name}',
dragSortTitle: '拖曳排序',
languageCode: 'Code',
languageName: '语言名称',
enabled: '启用',
defaultLanguage: '默认语言',
sortOrder: '排序',
newLanguage: '新增语言',
editLanguage: '编辑语言'
}
},
config: {
skills: '特长',
environments: '喜欢的环境',
favoriteThings: '喜欢的东西 / 标签',
itemCategories: '物品分类',
itemUsages: '物品用途',
acquisitionMethods: '入手方式',
maps: '地图'
},
appearance: {
time: '时段',
weather: '天气',
rarity: '稀有度',
map: '地图',
maps: '出现地图',
morning: '早晨',
noon: '中午',
evening: '傍晚',
night: '晚上',
sunny: '晴天',
cloudy: '阴天',
rainy: '雨天',
stars: '{count} 星'
},
history: {
title: '贡献记录',
createdBy: '由谁创建',
lastEdited: '最后编辑',
editHistory: '编辑历史',
before: '修改前',
after: '修改后',
author: '作者',
time: '时间',
action: '动作',
create: '创建',
update: '编辑',
delete: '删除',
empty: '暂无编辑历史'
}
}
};
export type MessageKey = keyof typeof messages.en;
export const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: readStoredLocale(),
fallbackLocale: defaultLocale,
messages
});
function readStoredLocale(): string {
if (typeof localStorage === 'undefined') {
return defaultLocale;
}
const storedLocale = localStorage.getItem(localeStorageKey);
return storedLocale && storedLocale.trim() !== '' ? storedLocale : defaultLocale;
}
function globalLocaleRef() {
return i18n.global.locale as unknown as { value: string };
}
export function getCurrentLocale(): string {
return globalLocaleRef().value || defaultLocale;
}
export function setCurrentLocale(locale: string): void {
const nextLocale = locale || defaultLocale;
globalLocaleRef().value = nextLocale;
if (typeof document !== 'undefined') {
document.documentElement.lang = nextLocale;
}
if (typeof localStorage !== 'undefined') {
localStorage.setItem(localeStorageKey, nextLocale);
}
if (typeof window !== 'undefined') {
window.dispatchEvent(new Event(localeChangeEvent));
}
}
export function onLocaleChange(callback: () => void): () => void {
window.addEventListener(localeChangeEvent, callback);
return () => window.removeEventListener(localeChangeEvent, callback);
}
setCurrentLocale(getCurrentLocale());