Files
todo-vibe-coding/web/src/i18n.ts
T

238 lines
8.6 KiB
TypeScript

import { computed, reactive } from 'vue'
const localeKey = 'supertodo_locale'
export type Locale = 'zh' | 'en'
const messages = {
en: {
brand_name: 'SuperTodo',
brand_caption: 'Simple team execution board',
nav_todo: 'Todo',
nav_team: 'Team',
nav_user_settings: 'User Settings',
nav_team_settings: 'Team Settings',
nav_notification_groups: 'Notify Groups',
nav_notification_dlq: 'Notification DLQ',
nav_login: 'Login',
logout: 'Logout',
guest_mode: 'Guest mode',
language: 'Language',
lang_switch: '中文',
login_title: 'Account Access',
login_subtitle: 'Use your account to enter the workspace.',
login_hint: 'Minimal Todo workspace for personal and team execution.',
login_tab: 'Login',
register_tab: 'Register',
email: 'Email',
password: 'Password',
auth_failed: 'Authentication failed, please check credentials.',
loading_wait: 'Please wait...',
register_and_login: 'Register and Login',
todo_title: 'Todo Board',
todo_subtitle: 'Simple execution list with team-ready structure.',
refresh: 'Refresh',
new_todo: 'New Todo',
no_description: 'No description',
priority_due: 'Priority P{priority} · Due {due}',
due_empty: '-',
edit: 'Edit',
delete: 'Delete',
create: 'Create',
loading_tasks: 'Loading tasks...',
load_tasks_failed: 'Failed to load tasks. Please login again.',
save_task_failed: 'Failed to save task.',
update_task_failed: 'Failed to update task.',
delete_task_failed: 'Failed to delete task.',
page: 'Page',
previous: 'Previous',
next: 'Next',
edit_todo: 'Edit Todo',
create_todo: 'New Todo',
title: 'Title',
due_at: 'Due At',
priority: 'Priority',
high: 'High',
medium: 'Medium',
low: 'Low',
tags: 'Tags',
tags_placeholder: 'backend, planning',
notify_groups: 'Notify Groups',
description: 'Description',
cancel: 'Cancel',
save: 'Save',
team_entry: 'Team Entry',
team_subtitle: 'Switch between teams and check active workload.',
team_members: '{count} members',
team_open_todos: '{count} open todos',
user_settings_title: 'Notification Preferences',
user_settings_subtitle: 'Manage your notification subscription and schedule.',
display_name: 'Display Name',
timezone: 'Timezone',
locale: 'Language',
dnd_start: 'DND Start',
dnd_end: 'DND End',
reminders: 'Receive notifications',
daily_summary_enabled: 'Enable Daily Summary',
daily_summary_time: 'Daily Summary Time',
save_settings: 'Save Settings',
save_settings_success: 'Settings updated successfully',
save_settings_failed: 'Failed to save settings',
load_settings_failed: 'Failed to load settings',
team_settings_title: 'Team Settings',
team_settings_subtitle: 'Global conventions for team collaboration.',
team_name: 'Team Name',
workspace_slug: 'Workspace Slug',
default_assignee: 'Default Assignee',
completion_rule: 'Completion Rule',
save_team_settings: 'Save Team Settings',
task_updated_success: 'Task updated successfully',
task_created_success: 'Task created successfully',
task_deleted_success: 'Task deleted successfully',
confirm_delete: 'Are you sure you want to delete this task?',
notification_groups_title: 'Notification Groups',
notification_groups_subtitle: 'Manage reusable recipient groups for tasks.',
group_name: 'Group Name',
group_emails: 'Emails',
group_emails_placeholder: 'a@example.com, b@example.com',
group_validation_error: 'Please input group name and emails',
group_created: 'Group created',
group_updated: 'Group updated',
group_deleted: 'Group deleted',
group_save_failed: 'Failed to save group',
group_delete_failed: 'Failed to delete group',
load_groups_failed: 'Failed to load groups',
notification_dlq_title: 'Notification DLQ',
notification_dlq_subtitle: 'Failed notification jobs waiting for compensation.',
load_dlq_failed: 'Failed to load DLQ records',
dlq_empty: 'No dead-letter records',
},
zh: {
brand_name: 'SuperTodo',
brand_caption: '简约的团队任务执行面板',
nav_todo: '待办',
nav_team: '团队入口',
nav_user_settings: '通知偏好',
nav_team_settings: '团队设置',
nav_notification_groups: '通知组',
nav_notification_dlq: '死信队列',
nav_login: '登录',
logout: '退出登录',
guest_mode: '访客模式',
language: '语言',
lang_switch: 'EN',
login_title: '账号入口',
login_subtitle: '使用账号登录后进入工作区。',
login_hint: '一个简洁的个人与团队任务协作空间。',
login_tab: '登录',
register_tab: '注册',
email: '邮箱',
password: '密码',
auth_failed: '认证失败,请检查邮箱和密码。',
loading_wait: '请稍候...',
register_and_login: '注册并登录',
todo_title: '待办看板',
todo_subtitle: '轻量执行清单,支持团队协作结构。',
refresh: '刷新',
new_todo: '新建待办',
no_description: '暂无描述',
priority_due: '优先级 P{priority} · 截止 {due}',
due_empty: '-',
edit: '编辑',
delete: '删除',
create: '创建',
loading_tasks: '任务加载中...',
load_tasks_failed: '加载任务失败,请重新登录。',
save_task_failed: '保存任务失败。',
update_task_failed: '更新任务失败。',
delete_task_failed: '删除任务失败。',
page: '第',
previous: '上一页',
next: '下一页',
edit_todo: '编辑待办',
create_todo: '新建待办',
title: '标题',
due_at: '截止时间',
priority: '优先级',
high: '高',
medium: '中',
low: '低',
tags: '标签',
tags_placeholder: '后端, 规划',
notify_groups: '通知组',
description: '描述',
cancel: '取消',
save: '保存',
team_entry: '团队入口',
team_subtitle: '在团队之间切换并查看当前工作量。',
team_members: '{count} 位成员',
team_open_todos: '{count} 个未完成任务',
user_settings_title: '通知偏好设置',
user_settings_subtitle: '管理订阅、免打扰与每日摘要。',
display_name: '显示名称',
timezone: '时区',
locale: '语言',
dnd_start: '免打扰开始',
dnd_end: '免打扰结束',
reminders: '接收通知',
daily_summary_enabled: '启用每日摘要',
daily_summary_time: '每日摘要时间',
save_settings: '保存设置',
save_settings_success: '设置保存成功',
save_settings_failed: '保存设置失败',
load_settings_failed: '加载设置失败',
team_settings_title: '团队设置',
team_settings_subtitle: '管理团队协作的默认规则。',
team_name: '团队名称',
workspace_slug: '工作区标识',
default_assignee: '默认负责人',
completion_rule: '完成规则',
save_team_settings: '保存团队设置',
task_updated_success: '任务更新成功',
task_created_success: '任务创建成功',
task_deleted_success: '任务删除成功',
confirm_delete: '确定要删除这个任务吗?',
notification_groups_title: '通知组管理',
notification_groups_subtitle: '为任务维护可复用收件人组。',
group_name: '组名称',
group_emails: '邮箱列表',
group_emails_placeholder: 'a@example.com, b@example.com',
group_validation_error: '请填写组名和至少一个邮箱',
group_created: '通知组已创建',
group_updated: '通知组已更新',
group_deleted: '通知组已删除',
group_save_failed: '保存通知组失败',
group_delete_failed: '删除通知组失败',
load_groups_failed: '加载通知组失败',
notification_dlq_title: '通知死信队列',
notification_dlq_subtitle: '查看投递失败并进入死信的通知任务。',
load_dlq_failed: '加载死信记录失败',
dlq_empty: '暂无死信记录',
},
} as const
type MessageKey = keyof typeof messages.en
const initialLocale = (localStorage.getItem(localeKey) as Locale | null) ?? 'zh'
export const i18n = reactive({
locale: initialLocale === 'en' ? 'en' : 'zh',
})
export const currentLocale = computed(() => i18n.locale)
export function toggleLocale() {
i18n.locale = i18n.locale === 'zh' ? 'en' : 'zh'
localStorage.setItem(localeKey, i18n.locale)
}
export function t(key: MessageKey, params?: Record<string, string | number>) {
const locale = i18n.locale as Locale
let text: string = messages[locale][key] ?? messages.en[key]
if (params) {
Object.entries(params).forEach(([k, v]) => {
text = text.replace(new RegExp(`{${k}}`, 'g'), String(v))
})
}
return text
}