feat: add internationalization support with next-intl

This commit is contained in:
beilunyang
2025-10-13 00:57:32 +08:00
parent 0fcc4b9e85
commit d175017b51
46 changed files with 1436 additions and 432 deletions

11
app/i18n/config.ts Normal file
View File

@@ -0,0 +1,11 @@
export const locales = ['en', 'zh-CN'] as const
export type Locale = typeof locales[number]
export const defaultLocale: Locale = 'en'
export const i18n = {
locales,
defaultLocale,
localePrefix: 'as-needed',
}

View File

@@ -0,0 +1,43 @@
{
"signButton": {
"login": "Login / Sign Up",
"logout": "Logout",
"userAvatar": "User Avatar",
"linked": "Linked"
},
"loginForm": {
"title": "Welcome to MoeMail",
"subtitle": "Cute Temporary Email Service (。・∀・)",
"tabs": {
"login": "Login",
"register": "Sign Up"
},
"fields": {
"username": "Username",
"password": "Password",
"confirmPassword": "Confirm Password"
},
"actions": {
"login": "Login",
"register": "Sign Up",
"or": "OR",
"githubLogin": "Login with GitHub"
},
"errors": {
"usernameRequired": "Please enter username",
"passwordRequired": "Please enter password",
"confirmPasswordRequired": "Please confirm password",
"usernameInvalid": "Username cannot contain @ symbol",
"passwordTooShort": "Password must be at least 8 characters",
"passwordMismatch": "Passwords do not match"
},
"toast": {
"loginFailed": "Login Failed",
"loginFailedDesc": "Incorrect username or password",
"registerFailed": "Registration Failed",
"registerFailedDesc": "Please try again later",
"autoLoginFailed": "Auto-login failed, please login manually"
}
}
}

View File

@@ -0,0 +1,26 @@
{
"app": {
"name": "MoeMail"
},
"lang": {
"en": "English",
"zhCN": "简体中文",
"switch": "Switch Language"
},
"actions": {
"ok": "OK",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete"
},
"nav": {
"home": "Home",
"login": "Login",
"profile": "Profile",
"logout": "Logout",
"backToMailbox": "Back to Mailbox"
},
"github": "Get Source Code"
}

View File

@@ -0,0 +1,87 @@
{
"noPermission": {
"title": "Insufficient Permission",
"description": "You don't have permission to access this page. Please contact the website administrator.",
"adminContact": "Admin Contact",
"backToHome": "Back to Home"
},
"layout": {
"myEmails": "My Emails",
"selectEmail": "Select an email to view messages",
"messageContent": "Message Content",
"selectMessage": "Select a message to view details",
"backToEmailList": "← Back to Email List",
"backToMessageList": "← Back to Message List"
},
"list": {
"emailCount": "{count}/{max} emails",
"emailCountUnlimited": "{count}/∞ emails",
"loading": "Loading...",
"loadingMore": "Loading more...",
"noEmails": "No emails yet, create one!",
"expiresAt": "Expires",
"permanent": "Permanent",
"deleteConfirm": "Confirm Delete",
"deleteDescription": "Are you sure you want to delete {email}? This will also delete all messages in this mailbox and cannot be undone.",
"deleteSuccess": "Email deleted successfully",
"deleteFailed": "Failed to delete email",
"error": "Error",
"success": "Success"
},
"create": {
"title": "Create Email",
"name": "Email Prefix",
"namePlaceholder": "Leave empty for random generation",
"domain": "Domain",
"domainPlaceholder": "Select a domain",
"expiryTime": "Validity Period",
"oneHour": "1 Hour",
"oneDay": "1 Day",
"threeDays": "3 Days",
"permanent": "Permanent",
"create": "Create",
"creating": "Creating...",
"success": "Email created successfully",
"failed": "Failed to create email"
},
"messages": {
"received": "Received",
"sent": "Sent",
"noMessages": "No messages yet",
"from": "From",
"to": "To",
"subject": "Subject",
"date": "Date",
"loading": "Loading...",
"loadingMore": "Loading more..."
},
"send": {
"title": "Send Email",
"from": "From",
"to": "To",
"toPlaceholder": "Recipient email address",
"subject": "Subject",
"subjectPlaceholder": "Email subject",
"content": "Content",
"contentPlaceholder": "Email content (supports HTML)",
"send": "Send",
"sending": "Sending...",
"success": "Email sent successfully",
"failed": "Failed to send email",
"dailyLimitReached": "Daily sending limit reached",
"dailyLimit": "Daily Limit: {count}/{max}",
"dailyLimitUnit": "emails/day"
},
"messageView": {
"loading": "Loading message details...",
"loadError": "Failed to load message details",
"networkError": "Network error, please try again later",
"retry": "Click to retry",
"from": "From",
"to": "To",
"time": "Time",
"htmlFormat": "HTML Format",
"textFormat": "Plain Text Format"
}
}

View File

@@ -0,0 +1,23 @@
{
"title": "MoeMail",
"subtitle": "Cute Temporary Email Service",
"features": {
"privacy": {
"title": "Privacy Protection",
"description": "Protect your real email address"
},
"instant": {
"title": "Instant Delivery",
"description": "Receive emails in real-time"
},
"expiry": {
"title": "Auto Expiry",
"description": "Automatically expires when due"
}
},
"actions": {
"enterMailbox": "Enter Mailbox",
"getStarted": "Get Started"
}
}

View File

@@ -0,0 +1,6 @@
{
"title": "MoeMail - Cute Temporary Email Service",
"description": "Secure, fast, and disposable temporary email addresses. Protect your privacy and stay away from spam. Instant delivery with automatic expiration.",
"keywords": "temporary email, disposable email, anonymous email, privacy protection, spam filter, instant delivery, auto expiry, secure email, verification email, temporary account, cute email, email service, privacy security, MoeMail"
}

View File

@@ -0,0 +1,123 @@
{
"title": "Profile",
"card": {
"title": "User Information",
"name": "Name",
"role": "Role",
"roles": {
"EMPEROR": "Emperor",
"DUKE": "Duke",
"KNIGHT": "Knight",
"CIVILIAN": "Civilian"
}
},
"apiKey": {
"title": "API Key Management",
"description": "Create and manage API keys for accessing OpenAPI",
"create": "Create API Key",
"name": "Key Name",
"namePlaceholder": "Enter key name",
"key": "API Key",
"createdAt": "Created At",
"copy": "Copy",
"delete": "Delete",
"noKeys": "No API keys yet",
"createSuccess": "API key created successfully",
"createFailed": "Failed to create API key",
"deleteConfirm": "Confirm Delete",
"deleteDescription": "Are you sure you want to delete API key {name}? This action cannot be undone.",
"deleteSuccess": "API key deleted successfully",
"deleteFailed": "Failed to delete API key",
"viewDocs": "View Documentation",
"docs": {
"getConfig": "Get System Config",
"generateEmail": "Generate Temp Email",
"getEmails": "Get Email List",
"getMessages": "Get Message List",
"getMessage": "Get Single Message",
"notes": "Notes:",
"note1": "Replace YOUR_API_KEY with your actual API Key",
"note2": "/api/config endpoint provides system configuration including available email domains",
"note3": "emailId is the unique identifier for an email",
"note4": "messageId is the unique identifier for a message",
"note5": "expiryTime is the validity period in milliseconds: 3600000 (1 hour), 86400000 (1 day), 604800000 (7 days), 0 (permanent)",
"note6": "domain is the email domain, get available domains from /api/config endpoint",
"note7": "cursor is for pagination, get nextCursor from previous response",
"note8": "All requests require X-API-Key header"
}
},
"emailService": {
"title": "Resend Email Service Configuration",
"configRoleLabel": "Configurable Role Permissions",
"enable": "Enable Email Service",
"fixedRoleLimits": "Fixed Role Limits",
"emperorLimit": "Emperor can send unlimited emails without any restrictions",
"civilianLimit": "Cannot send emails",
"enableDescription": "When enabled, emails will be sent using Resend",
"apiKey": "Resend API Key",
"apiKeyPlaceholder": "Enter Resend API Key",
"dailyLimit": "Daily Limit",
"roleLimits": "Roles allowed to use sending feature",
"save": "Save Configuration",
"saving": "Saving...",
"saveSuccess": "Configuration saved successfully",
"saveFailed": "Failed to save configuration",
"unlimited": "Unlimited",
"disabled": "Sending permission not enabled",
"enabled": "Sending permission enabled"
},
"webhook": {
"title": "Webhook Configuration",
"description": "Notify specified URL when new emails arrive",
"description2": "We will send a POST request to this URL with information about the new email",
"description3": "View data format documentation",
"enable": "Enable Webhook",
"url": "Webhook URL",
"urlPlaceholder": "Enter webhook URL",
"test": "Test",
"testing": "Testing...",
"save": "Save Configuration",
"saving": "Saving...",
"saveSuccess": "Configuration saved successfully",
"saveFailed": "Failed to save configuration",
"testSuccess": "Webhook test successful",
"testFailed": "Webhook test failed",
"docs": {
"intro": "When a new email is received, we will send a POST request to the configured URL with the following headers:",
"exampleBody": "Request body example:",
"subject": "Email Subject",
"content": "Email text content",
"html": "Email HTML content"
}
},
"website": {
"title": "Website Configuration",
"description": "Configure website settings (Emperor only)",
"defaultRole": "Default Role for New Users",
"emailDomains": "Email Domains",
"emailDomainsPlaceholder": "Separate multiple domains with commas",
"adminContact": "Admin Contact",
"adminContactPlaceholder": "Email or other contact method",
"maxEmails": "Max Emails per User",
"save": "Save Configuration",
"saving": "Saving...",
"saveSuccess": "Configuration saved successfully",
"saveFailed": "Failed to save configuration"
},
"promote": {
"title": "Role Management",
"description": "Manage user roles (Emperor only)",
"search": "Search Users",
"searchPlaceholder": "Enter username or email",
"username": "Username",
"email": "Email",
"role": "Role",
"actions": "Actions",
"promote": "Set as",
"noUsers": "No users found",
"loading": "Loading...",
"updateSuccess": "User role updated successfully",
"updateFailed": "Failed to update user role"
}
}

View File

@@ -0,0 +1,43 @@
{
"signButton": {
"login": "登录/注册",
"logout": "登出",
"userAvatar": "用户头像",
"linked": "已关联"
},
"loginForm": {
"title": "欢迎使用 MoeMail",
"subtitle": "萌萌哒临时邮箱服务 (。・∀・)",
"tabs": {
"login": "登录",
"register": "注册"
},
"fields": {
"username": "用户名",
"password": "密码",
"confirmPassword": "确认密码"
},
"actions": {
"login": "登录",
"register": "注册",
"or": "或者",
"githubLogin": "使用 GitHub 账号登录"
},
"errors": {
"usernameRequired": "请输入用户名",
"passwordRequired": "请输入密码",
"confirmPasswordRequired": "请确认密码",
"usernameInvalid": "用户名不能包含 @ 符号",
"passwordTooShort": "密码长度必须大于等于8位",
"passwordMismatch": "两次输入的密码不一致"
},
"toast": {
"loginFailed": "登录失败",
"loginFailedDesc": "用户名或密码错误",
"registerFailed": "注册失败",
"registerFailedDesc": "请稍后重试",
"autoLoginFailed": "自动登录失败,请手动登录"
}
}
}

View File

@@ -0,0 +1,26 @@
{
"app": {
"name": "MoeMail"
},
"lang": {
"en": "English",
"zhCN": "简体中文",
"switch": "切换语言"
},
"actions": {
"ok": "确定",
"cancel": "取消",
"save": "保存",
"delete": "删除"
},
"nav": {
"home": "首页",
"login": "登录",
"profile": "个人中心",
"logout": "退出登录",
"backToMailbox": "返回邮箱"
},
"github": "获取网站源代码"
}

View File

@@ -0,0 +1,87 @@
{
"noPermission": {
"title": "权限不足",
"description": "你没有权限访问此页面,请联系网站管理员",
"adminContact": "管理员联系方式",
"backToHome": "返回首页"
},
"layout": {
"myEmails": "我的邮箱",
"selectEmail": "选择邮箱查看消息",
"messageContent": "邮件内容",
"selectMessage": "选择邮件查看详情",
"backToEmailList": "← 返回邮箱列表",
"backToMessageList": "← 返回消息列表"
},
"list": {
"emailCount": "{count}/{max} 个邮箱",
"emailCountUnlimited": "{count}/∞ 个邮箱",
"loading": "加载中...",
"loadingMore": "加载更多...",
"noEmails": "还没有邮箱,创建一个吧!",
"expiresAt": "过期时间",
"permanent": "永久有效",
"deleteConfirm": "确认删除",
"deleteDescription": "确定要删除邮箱 {email} 吗?此操作将同时删除该邮箱中的所有邮件,且不可恢复。",
"deleteSuccess": "邮箱已删除",
"deleteFailed": "删除邮箱失败",
"error": "错误",
"success": "成功"
},
"create": {
"title": "创建邮箱",
"name": "邮箱前缀",
"namePlaceholder": "留空则随机生成",
"domain": "域名",
"domainPlaceholder": "选择域名",
"expiryTime": "有效期",
"oneHour": "1 小时",
"oneDay": "1 天",
"threeDays": "3 天",
"permanent": "永久",
"create": "创建",
"creating": "创建中...",
"success": "邮箱创建成功",
"failed": "创建邮箱失败"
},
"messages": {
"received": "收件箱",
"sent": "已发送",
"noMessages": "暂无邮件",
"from": "发件人",
"to": "收件人",
"subject": "主题",
"date": "日期",
"loading": "加载中...",
"loadingMore": "加载更多..."
},
"send": {
"title": "发送邮件",
"from": "发件人",
"to": "收件人",
"toPlaceholder": "收件人邮箱地址",
"subject": "主题",
"subjectPlaceholder": "邮件主题",
"content": "内容",
"contentPlaceholder": "邮件内容(支持 HTML",
"send": "发送",
"sending": "发送中...",
"success": "邮件发送成功",
"failed": "发送邮件失败",
"dailyLimitReached": "已达每日发送上限",
"dailyLimit": "每日限额:{count}/{max}",
"dailyLimitUnit": "封/天"
},
"messageView": {
"loading": "加载邮件详情...",
"loadError": "获取邮件详情失败",
"networkError": "网络错误,请稍后重试",
"retry": "点击重试",
"from": "发件人",
"to": "收件人",
"time": "时间",
"htmlFormat": "HTML 格式",
"textFormat": "纯文本格式"
}
}

View File

@@ -0,0 +1,23 @@
{
"title": "MoeMail",
"subtitle": "萌萌哒临时邮箱服务",
"features": {
"privacy": {
"title": "隐私保护",
"description": "保护您的真实邮箱地址"
},
"instant": {
"title": "即时收件",
"description": "实时接收邮件通知"
},
"expiry": {
"title": "自动过期",
"description": "到期自动失效"
}
},
"actions": {
"enterMailbox": "进入邮箱",
"getStarted": "开始使用"
}
}

View File

@@ -0,0 +1,6 @@
{
"title": "MoeMail - 萌萌哒临时邮箱服务",
"description": "安全、快速、一次性的临时邮箱地址,保护您的隐私,远离垃圾邮件。支持即时收件,到期自动失效。",
"keywords": "临时邮箱, 一次性邮箱, 匿名邮箱, 隐私保护, 垃圾邮件过滤, 即时收件, 自动过期, 安全邮箱, 注册验证, 临时账号, 萌系邮箱, 电子邮件, 隐私安全, 邮件服务, MoeMail"
}

View File

@@ -0,0 +1,123 @@
{
"title": "个人中心",
"card": {
"title": "用户信息",
"name": "用户名",
"role": "角色",
"roles": {
"EMPEROR": "皇帝",
"DUKE": "公爵",
"KNIGHT": "骑士",
"CIVILIAN": "平民"
}
},
"apiKey": {
"title": "API Key 管理",
"description": "创建和管理用于访问 OpenAPI 的 API 密钥",
"create": "创建 API Key",
"name": "密钥名称",
"namePlaceholder": "输入密钥名称",
"key": "API Key",
"createdAt": "创建时间",
"copy": "复制",
"delete": "删除",
"noKeys": "暂无 API Key",
"createSuccess": "API Key 创建成功",
"createFailed": "创建 API Key 失败",
"deleteConfirm": "确认删除",
"deleteDescription": "确定要删除 API Key {name} 吗?此操作不可恢复。",
"deleteSuccess": "API Key 已删除",
"deleteFailed": "删除 API Key 失败",
"viewDocs": "查看使用文档",
"docs": {
"getConfig": "获取系统配置",
"generateEmail": "生成临时邮箱",
"getEmails": "获取邮箱列表",
"getMessages": "获取邮件列表",
"getMessage": "获取单封邮件",
"notes": "注意:",
"note1": "请将 YOUR_API_KEY 替换为你的实际 API Key",
"note2": "/api/config 接口可获取系统配置,包括可用的邮箱域名列表",
"note3": "emailId 是邮箱的唯一标识符",
"note4": "messageId 是邮件的唯一标识符",
"note5": "expiryTime 是邮箱的有效期毫秒可选值36000001小时、864000001天、6048000007天、0永久",
"note6": "domain 是邮箱域名,可通过 /api/config 接口获取可用域名列表",
"note7": "cursor 用于分页,从上一次请求的响应中获取 nextCursor",
"note8": "所有请求都需要包含 X-API-Key 请求头"
}
},
"emailService": {
"title": "Resend 发件服务配置",
"configRoleLabel": "可配置的角色权限",
"enable": "启用邮件服务",
"fixedRoleLimits": "固定权限规则",
"emperorLimit": "皇帝可以无限发件,不受任何限制",
"civilianLimit": "永远不能发件",
"enableDescription": "开启后将使用 Resend 发送邮件",
"apiKey": "Resend API Key",
"apiKeyPlaceholder": "输入 Resend API Key",
"dailyLimit": "每日限额",
"roleLimits": "允许使用发件功能的角色",
"save": "保存配置",
"saving": "保存中...",
"saveSuccess": "配置保存成功",
"saveFailed": "保存配置失败",
"unlimited": "无限制",
"disabled": "未启用发件权限",
"enabled": "已启用发件权限"
},
"webhook": {
"title": "Webhook 配置",
"description": "当收到新邮件时通知指定的 URL",
"description2": "我们会向此 URL 发送 POST 请求,包含新邮件的相关信息",
"description3": "查看数据格式说明",
"enable": "启用 Webhook",
"url": "Webhook URL",
"urlPlaceholder": "输入 webhook URL",
"test": "测试",
"testing": "测试中...",
"save": "保存配置",
"saving": "保存中...",
"saveSuccess": "配置保存成功",
"saveFailed": "保存配置失败",
"testSuccess": "Webhook 测试成功",
"testFailed": "Webhook 测试失败",
"docs": {
"intro": "当收到新邮件时,我们会向配置的 URL 发送 POST 请求,请求头包含:",
"exampleBody": "请求体示例:",
"subject": "邮件主题",
"content": "邮件文本内容",
"html": "邮件HTML内容"
}
},
"website": {
"title": "网站配置",
"description": "配置网站设置(仅皇帝可用)",
"defaultRole": "新用户默认角色",
"emailDomains": "邮箱域名",
"emailDomainsPlaceholder": "多个域名用逗号分隔",
"adminContact": "管理员联系方式",
"adminContactPlaceholder": "邮箱或其他联系方式",
"maxEmails": "每个用户最大邮箱数",
"save": "保存配置",
"saving": "保存中...",
"saveSuccess": "配置保存成功",
"saveFailed": "保存配置失败"
},
"promote": {
"title": "角色管理",
"description": "管理用户角色(仅皇帝可用)",
"search": "搜索用户",
"searchPlaceholder": "输入用户名或邮箱",
"username": "用户名",
"email": "邮箱",
"role": "角色",
"actions": "操作",
"promote": "设为",
"noUsers": "未找到用户",
"loading": "加载中...",
"updateSuccess": "用户角色更新成功",
"updateFailed": "更新用户角色失败"
}
}

18
app/i18n/request.ts Normal file
View File

@@ -0,0 +1,18 @@
import {getRequestConfig} from 'next-intl/server'
import {i18n} from '@/i18n/config'
export default getRequestConfig(async ({locale}) => {
const safeLocale = (i18n.locales.includes(locale as any) ? locale : i18n.defaultLocale) as string
try {
const common = (await import(`@/i18n/messages/${safeLocale}/common.json`)).default
const home = (await import(`@/i18n/messages/${safeLocale}/home.json`)).default
const auth = (await import(`@/i18n/messages/${safeLocale}/auth.json`)).default
const metadata = (await import(`@/i18n/messages/${safeLocale}/metadata.json`)).default
const emails = (await import(`@/i18n/messages/${safeLocale}/emails.json`)).default
const profile = (await import(`@/i18n/messages/${safeLocale}/profile.json`)).default
return {locale: safeLocale, messages: {common, home, auth, metadata, emails, profile}}
} catch {
return {locale: safeLocale, messages: {common: {}, home: {}, auth: {}, metadata: {}, emails: {}, profile: {}}}
}
})