import { createContext, useContext, useMemo, useState, type ReactNode } from 'react';
import type { Locale } from '../types';
const messages = {
'zh-CN': {
'app.name': 'XFEServerManager',
'app.subtitle': 'Forge 服务器控制台',
'nav.core': '核心管理',
'nav.governance': '治理与恢复',
'nav.overview': '服务器概览',
'nav.players': '在线玩家',
'nav.economy': '经济系统',
'nav.messages': '触发器',
'nav.menus': 'UI 菜单',
'nav.content': '内容创作',
'nav.policies': '命令策略',
'nav.audit': '审计日志',
'nav.console': '安全控制台',
'nav.settings': '系统设置',
'nav.moderation': '处罚运营',
'nav.maintenance': '维护与计划',
'nav.claims': '区块认领',
'nav.rollback': '追踪与回滚',
'connection.online': '实时连接',
'connection.connecting': '正在连接',
'connection.offline': '管理接口离线',
'connection.degraded': '连接不稳定',
'connection.offlineDetail': '无法连接服务器管理接口。这里不会使用模拟数据;请确认专用服务器和 Web 服务已启动。',
'connection.retry': '重新连接',
'common.loading': '正在读取服务器数据…',
'common.empty': '没有可显示的数据',
'common.error': '读取失败',
'common.refresh': '刷新',
'common.search': '搜索',
'common.cancel': '取消',
'common.edit': '编辑',
'common.delete': '删除',
'common.confirm': '确认',
'common.save': '保存更改',
'common.enabled': '已启用',
'common.disabled': '未启用',
'common.preview': '预览',
'common.reason': '操作理由',
'common.reasonRequired': '必须填写审计理由',
'common.previous': '上一页',
'common.next': '下一页',
'common.notAvailable': '不可用',
'common.signOut': '退出登录',
'common.close': '关闭',
'access.title': '无权访问',
'access.description': '当前角色没有打开此管理页面所需的权限。',
'access.detail': '此功能已在界面中隐藏或禁用;如需使用,请由 Owner 调整账户角色。',
'overview.title': '服务器概览',
'overview.description': '来自专用服务器的实时健康状态与容量信号。',
'overview.tps': '每秒 Tick',
'overview.mspt': '平均 MSPT',
'overview.players': '在线玩家',
'overview.heap': '堆内存',
'overview.cpu': '进程 CPU',
'overview.uptime': '运行时间',
'overview.disk': '磁盘可用',
'overview.ping': '平均延迟',
'overview.performance': '性能趋势',
'overview.performanceHint': 'SSE 推送的最近采样;连接前不显示推测值。',
'overview.dimensions': '维度负载',
'overview.dimension': '维度',
'overview.chunks': '已加载区块',
'overview.entities': '实体',
'overview.serverIdentity': '运行环境',
'overview.sampledAt': '最后采样',
'players.title': '在线玩家',
'players.description': '检查身份、位置和状态,并发起受审计的玩家操作。',
'players.player': '玩家',
'players.role': '权限',
'players.location': '位置',
'players.state': '状态',
'players.actions': '操作',
'players.inspect': '管理',
'players.readOnly': '只读',
'players.online': '在线',
'players.offline': '离线',
'players.noPlayers': '当前没有在线玩家',
'players.actionTitle': '对玩家执行操作',
'players.action': '操作类型',
'players.kick': '踢出',
'players.message': '私信',
'players.heal': '治疗',
'players.feed': '恢复饱食度',
'players.clearEffects': '清除效果',
'players.teleport': '传送',
'players.recall': '召回到上次传送前位置',
'players.gameMode': '游戏模式',
'players.whitelist': '白名单',
'players.ban': '封禁',
'players.messageText': '消息内容',
'players.destination': '在线玩家名称或三个绝对坐标',
'players.whitelistMode': '白名单操作',
'players.whitelistAdd': '加入白名单',
'players.whitelistRemove': '移出白名单',
'players.submit': '提交操作',
'players.actionsTab': '玩家操作',
'players.inventory': '背包',
'players.enderChest': '末影箱',
'players.inventoryRevision': '槽位 revision',
'players.inventoryHint': '每次只修改一个槽位,并使用当前 revision 做服务端 CAS;冲突时会拒绝覆盖。',
'players.inventoryLoading': '正在读取在线玩家容器…',
'players.inventoryEmpty': '容器没有返回槽位数据。',
'players.slot': '槽位',
'players.itemId': '物品注册 ID',
'players.count': '数量(0–127)',
'players.editSlot': '编辑槽位',
'players.previewSlot': '预览槽位更改',
'players.saveSlot': '确认 CAS 修改',
'players.opaqueProtected': '此槽位包含无法由安全接口还原的原始数据,已禁止编辑以避免数据丢失。',
'players.slotBefore': '修改前',
'players.slotAfter': '修改后',
'players.inventorySaved': '槽位修改已提交',
'policies.title': '命令策略',
'policies.description': '管理不可变策略版本,并在发布前模拟 Brigadier 决策。',
'policies.name': '策略',
'policies.version': '版本',
'policies.rules': '规则',
'policies.groups': '策略组',
'policies.updated': '更新',
'policies.state': '状态',
'policies.simulator': '策略模拟器',
'policies.command': '待解析命令',
'policies.playerUuid': '玩家 UUID(可选)',
'policies.run': '模拟决策',
'policies.result': '决策解释',
'policies.noResult': '输入命令后运行模拟;不会在服务器执行。',
'policies.publish': '发布版本',
'policies.publishWarning': '发布将原子替换当前活动策略。必须填写理由。',
'policies.edit': '编辑',
'policies.validate': '校验',
'policies.editor': '策略规则编辑器',
'policies.editorHint': '编辑器会从不可变版本复制规则并建立新草稿;保存不会发布,发布前请先校验和模拟影响。',
'policies.draftDescription': '草稿说明与审计理由',
'policies.rulesJson': '规则 JSON',
'policies.saveDraft': '校验并保存草稿',
'policies.draftSaved': '策略草稿已保存',
'policies.saveAndApply': '校验、保存并立即生效',
'policies.appliedImmediately': '策略已原子发布并立即生效,无需重启',
'policies.liveHint': '发布会热替换当前命令策略,成功后立即用于下一条命令,不需要重启服务器。',
'policies.validationComplete': '策略校验已完成',
'policies.valid': '校验通过',
'policies.invalid': '校验失败',
'policies.rulesArrayRequired': '规则 JSON 必须是对象数组。',
'audit.title': '审计日志',
'audit.description': '查询管理操作、策略决策与失败结果。敏感值由服务端脱敏。',
'audit.time': '时间',
'audit.actor': '操作者',
'audit.action': '动作',
'audit.target': '目标',
'audit.result': '结果',
'audit.request': '请求 ID',
'console.title': '安全控制台',
'console.description': '仅执行 Minecraft 命令;不会提供操作系统 Shell 或文件访问。',
'console.locked': '控制台在服务端配置中关闭,或当前会话没有 owner 权限。',
'console.command': 'Minecraft 命令',
'console.execute': '确认执行',
'console.warning': '此操作需要 TOTP 与近期重新认证,并会写入审计日志。',
'console.history': '本次会话提交',
'settings.title': '系统设置',
'settings.description': '查看 Web 边界、集成状态与 v2 功能开关。',
'settings.network': '网络边界',
'settings.bind': '监听地址',
'settings.port': '端口',
'settings.proxies': '可信代理 CIDR',
'settings.security': '安全状态',
'settings.offlineMode': 'Minecraft offline-mode',
'settings.integrations': '软集成',
'settings.features': '功能开关',
'settings.readOnly': '这里显示当前进程的生效值;运行时安全项保存即生效,只有启动项按提示重启。',
'messages.title': '消息与公告',
'messages.sections': '消息类型',
'messages.description': '可视化配置入服消息、规则提醒和每日公告,使用原版客户端富文本能力。',
'messages.richHint': '每条消息可独立修改发送者、颜色、样式、链接、命令动作、悬停展开和插入文本。',
'messages.variablesHint': '点击“插入变量”可搜索服务器提供的 100+ 个变量并查看说明。自定义时间推荐 {server.time:uuuu-MM-dd HH:mm:ss}:MM 是月份、mm 是分钟;不要使用表示周序年的 YYYY。保存后立即生效。',
'messages.joinWelcome': '每次入服欢迎',
'messages.firstJoin': '首次入服欢迎',
'messages.rules': '每日规则提醒',
'messages.daily': '每日公告',
'messages.onceDaily': '按玩家和所选时区每日仅发送一次',
'messages.timeZone': '公告时区',
'messages.dailyList': '公告列表(最多 20 条)',
'messages.add': '新增公告',
'messages.item': '公告',
'messages.behavior': '其他入服行为',
'messages.maintenanceReminder': '维护模式入服提醒',
'messages.maintenanceHint': '对维护白名单内允许进入的玩家显示当前维护状态',
'messages.ownerOnly': '只有 Owner 可以修改消息配置。',
'messages.save': '保存消息配置',
'messages.saved': '消息配置已保存并立即生效。',
'messages.restart': '等待重启生效',
'messages.live': '已立即生效',
'accounts.title': 'Web 账户与角色',
'accounts.description': '管理 Web 控制台账户。密码只在创建请求中提交,之后永不显示。',
'accounts.locked': '账户管理需要 owner、已验证的 TOTP 和近期重新认证。请先在上方完成安全验证。',
'accounts.create': '创建账户',
'accounts.username': '用户名',
'accounts.initialPassword': '初始密码',
'accounts.passwordHint': '至少 12 个字符;提交成功后立即清空。',
'accounts.role': '角色',
'accounts.status': '状态',
'accounts.totp': 'TOTP',
'accounts.lastLogin': '最近登录',
'accounts.active': '正常',
'accounts.disabled': '已禁用',
'accounts.never': '从未登录',
'accounts.reviewChange': '预览账户变更',
'accounts.confirmChange': '确认账户变更',
'accounts.delete': '删除',
'accounts.confirmDelete': '确认删除',
'accounts.deleteWarning': '删除账户会立即撤销其访问,且无法通过此界面恢复。',
'accounts.changeWarning': '角色或禁用状态将在确认后立即生效,目标当前会话可能失效。',
'accounts.created': '账户已创建',
'accounts.updated': '账户已更新',
'accounts.deleted': '账户已删除',
'accounts.current': '当前账户',
'moderation.title': '处罚运营',
'moderation.description': '案件化管理警告、禁言、踢出与封禁。',
'moderation.newCase': '新建案件',
'moderation.player': '玩家 UUID 或名称',
'moderation.type': '处罚类型',
'moderation.reason': '处罚理由',
'moderation.message': '玩家可见消息',
'moderation.messageHint': '留空时使用处罚理由;警告、踢出和封禁时会向玩家显示。',
'moderation.duration': '时长(例如 2h、7d,可选)',
'moderation.create': '预览并创建',
'moderation.case': '案件',
'moderation.expiry': '到期时间',
'moderation.state': '状态',
'moderation.featureOff': '处罚模块尚未在服务端启用。',
'moderation.revoke': '撤销处罚',
'moderation.revoked': '处罚已撤销',
'maintenance.title': '维护与计划',
'maintenance.description': '管理维护准入、公告和仅限结构化动作的计划任务。',
'maintenance.featureOff': '处罚与维护模块尚未在服务端启用。',
'maintenance.mode': '维护模式',
'maintenance.modeHint': '启用后仅允许白名单 UUID 留在服务器。',
'maintenance.message': '玩家可见消息',
'maintenance.allowed': '允许的玩家 UUID(每行一个)',
'maintenance.saved': '维护状态已保存',
'maintenance.announce': '服务器公告',
'maintenance.announced': '公告已发送',
'maintenance.send': '立即发送',
'maintenance.schedule': '创建计划任务',
'maintenance.scheduled': '计划任务已创建',
'maintenance.executeAt': '执行时间',
'maintenance.action': '结构化动作',
'maintenance.enable': '开启维护',
'maintenance.disable': '关闭维护',
'maintenance.stop': '优雅关服',
'maintenance.countdown': '倒计时(例如 30s)',
'maintenance.stopScheduleHint': '创建后立即公告关服时间;最后 1 小时每 5 分钟提醒,最后 60 秒逐秒倒计时。',
'maintenance.jobs': '计划任务',
'claims.title': '区块认领',
'claims.description': '查看领地所有者、维度、成员与配额占用。',
'claims.owner': '所有者',
'claims.dimension': '维度',
'claims.chunks': '区块',
'claims.members': '成员',
'claims.featureOff': '区块认领模块尚未在服务端启用。',
'claims.create': '新建认领',
'claims.edit': '保存认领',
'claims.name': '认领名称',
'claims.chunkLines': '区块坐标(每行 x,z,必须相邻)',
'claims.memberLines': '成员(每行 UUID=manager/member/trusted)',
'claims.flags': '对外开放行为',
'claims.saved': '区块认领已保存',
'claims.deleted': '区块认领已删除',
'claims.deleteWarning': '这会立即解除所有区块保护。再次确认删除。',
'claims.transfer': '所有权转让(接收方必须确认)',
'claims.newOwner': '新所有者 UUID',
'claims.beginTransfer': '生成十分钟转让令牌',
'claims.transferToken': '转让令牌',
'claims.acceptingPlayer': '接收方 UUID',
'claims.confirmTransfer': '确认接收所有权',
'claims.transferReady': '转让令牌已生成,到期:',
'claims.transferred': '所有权转让已完成',
'rollback.title': '追踪与回滚',
'rollback.description': '先固定水位并预览冲突,再执行受审计、可恢复的回滚。',
'rollback.filter': '查询与预览',
'rollback.actor': '玩家 UUID(可选)',
'rollback.radius': '半径(方块)',
'rollback.since': '起始时间',
'rollback.action': '动作类型(可选)',
'rollback.dimension': '维度',
'rollback.center': '中心坐标',
'rollback.preview': '生成回滚预览',
'rollback.changes': '世界变化',
'rollback.position': '位置',
'rollback.resource': '资源',
'rollback.summary': '预览摘要',
'rollback.matched': '匹配记录',
'rollback.applicable': '可应用',
'rollback.conflicts': '冲突',
'rollback.unloaded': '未加载区块',
'rollback.gaps': '日志缺口',
'rollback.incompatible': '不兼容',
'rollback.execute': '执行此预览',
'rollback.pause': '暂停',
'rollback.resume': '继续',
'rollback.cancel': '取消任务',
'rollback.redo': '生成重做预览',
'rollback.featureOff': '世界追踪或回滚模块尚未在服务端启用。',
'auth.title': '管理身份验证',
'auth.setupTitle': '创建首位 Owner',
'auth.setupDescription': '引导码只在物理服务器终端生成,有效期十分钟且只能使用一次。',
'auth.description': '登录凭据只发送到此服务器的同源管理接口。',
'auth.username': '用户名',
'auth.password': '密码',
'auth.passwordConfirm': '确认密码',
'auth.passwordHint': '至少 12 个字符;建议使用密码管理器生成的独立密码。',
'auth.passwordMismatch': '两次输入的密码不一致。',
'auth.bootstrapToken': '终端引导码',
'auth.createOwner': '创建 Owner',
'auth.totp': 'TOTP(如已启用)',
'auth.recoveryCode': '恢复码',
'auth.useRecovery': '改用恢复码',
'auth.useTotp': '改用 TOTP',
'auth.signIn': '安全登录',
'auth.bootstrap': '尚未创建 owner。请先在物理服务器控制台生成十分钟单次引导码。',
'auth.totpSetup': '配置两步验证',
'auth.totpSetupHint': '安全控制台必须启用 TOTP。输入当前密码生成一次性密钥。',
'auth.startEnrollment': '生成 TOTP 密钥',
'auth.secret': '手动输入密钥',
'auth.provisioningUri': '身份验证器 URI',
'auth.confirmTotp': '确认并启用',
'auth.recoveryCodes': '恢复码只显示一次。立即保存到安全位置;每个恢复码只能使用一次。',
'auth.reauthenticate': '近期重新认证',
'auth.reauthenticateHint': '危险操作需要新的动态验证码;验证后会自动继续原操作。',
'auth.manualSetup': '无法扫码?显示手动绑定信息',
'auth.verifyAgain': '重新验证',
'auth.securityReady': '此会话已满足 TOTP 与近期认证要求。',
'auth.securityUpdated': '安全会话已更新。',
'operation.queued': '操作已排队',
},
'en-US': {
'app.name': 'XFEServerManager',
'app.subtitle': 'Forge server console',
'nav.core': 'Core management',
'nav.governance': 'Governance & recovery',
'nav.overview': 'Overview',
'nav.players': 'Online players',
'nav.economy': 'Economy',
'nav.messages': 'Triggers',
'nav.menus': 'UI menus',
'nav.content': 'Content studio',
'nav.policies': 'Command policies',
'nav.audit': 'Audit log',
'nav.console': 'Secure console',
'nav.settings': 'Settings',
'nav.moderation': 'Moderation',
'nav.maintenance': 'Maintenance & schedules',
'nav.claims': 'Claims',
'nav.rollback': 'Tracking & rollback',
'connection.online': 'Live connection',
'connection.connecting': 'Connecting',
'connection.offline': 'Management API offline',
'connection.degraded': 'Connection degraded',
'connection.offlineDetail': 'The management API cannot be reached. No demo values are substituted; check that the dedicated server and web service are running.',
'connection.retry': 'Reconnect',
'common.loading': 'Reading server data…',
'common.empty': 'No data to display',
'common.error': 'Unable to load data',
'common.refresh': 'Refresh',
'common.search': 'Search',
'common.cancel': 'Cancel',
'common.edit': 'Edit',
'common.delete': 'Delete',
'common.confirm': 'Confirm',
'common.save': 'Save changes',
'common.enabled': 'Enabled',
'common.disabled': 'Disabled',
'common.preview': 'Preview',
'common.reason': 'Audit reason',
'common.reasonRequired': 'An audit reason is required',
'common.previous': 'Previous',
'common.next': 'Next',
'common.notAvailable': 'Unavailable',
'common.signOut': 'Sign out',
'common.close': 'Close',
'access.title': 'Access denied',
'access.description': 'Your current role cannot open this management page.',
'access.detail': 'This feature is hidden or disabled for your account. Ask an Owner to change your role if access is required.',
'overview.title': 'Server overview',
'overview.description': 'Live health and capacity signals from the dedicated server.',
'overview.tps': 'Ticks per second',
'overview.mspt': 'Average MSPT',
'overview.players': 'Players online',
'overview.heap': 'Heap memory',
'overview.cpu': 'Process CPU',
'overview.uptime': 'Uptime',
'overview.disk': 'Disk available',
'overview.ping': 'Average latency',
'overview.performance': 'Performance trend',
'overview.performanceHint': 'Recent SSE-backed samples; no estimates appear before connection.',
'overview.dimensions': 'Dimension load',
'overview.dimension': 'Dimension',
'overview.chunks': 'Loaded chunks',
'overview.entities': 'Entities',
'overview.serverIdentity': 'Runtime',
'overview.sampledAt': 'Last sample',
'players.title': 'Online players',
'players.description': 'Inspect identity, position and state, then run audited player actions.',
'players.player': 'Player',
'players.role': 'Access',
'players.location': 'Location',
'players.state': 'State',
'players.actions': 'Actions',
'players.inspect': 'Manage',
'players.readOnly': 'Read only',
'players.online': 'Online',
'players.offline': 'Offline',
'players.noPlayers': 'No players are currently online',
'players.actionTitle': 'Run a player action',
'players.action': 'Action type',
'players.kick': 'Kick',
'players.message': 'Direct message',
'players.heal': 'Heal',
'players.feed': 'Restore food',
'players.clearEffects': 'Clear effects',
'players.teleport': 'Teleport',
'players.recall': 'Recall to pre-teleport location',
'players.gameMode': 'Game mode',
'players.whitelist': 'Whitelist',
'players.ban': 'Ban',
'players.messageText': 'Message',
'players.destination': 'Online player name or three absolute coordinates',
'players.whitelistMode': 'Whitelist operation',
'players.whitelistAdd': 'Add to whitelist',
'players.whitelistRemove': 'Remove from whitelist',
'players.submit': 'Submit action',
'players.actionsTab': 'Player actions',
'players.inventory': 'Inventory',
'players.enderChest': 'Ender chest',
'players.inventoryRevision': 'Slot revision',
'players.inventoryHint': 'One slot is changed at a time using the current server revision for compare-and-set; conflicts are rejected.',
'players.inventoryLoading': 'Loading the online player container…',
'players.inventoryEmpty': 'The container returned no slots.',
'players.slot': 'Slot',
'players.itemId': 'Item registry ID',
'players.count': 'Count (0–127)',
'players.editSlot': 'Edit slot',
'players.previewSlot': 'Preview slot change',
'players.saveSlot': 'Confirm CAS update',
'players.opaqueProtected': 'This slot contains opaque data that the safe API cannot restore, so editing is blocked to prevent data loss.',
'players.slotBefore': 'Before',
'players.slotAfter': 'After',
'players.inventorySaved': 'Slot update submitted',
'policies.title': 'Command policies',
'policies.description': 'Manage immutable policy versions and simulate Brigadier decisions before publishing.',
'policies.name': 'Policy',
'policies.version': 'Version',
'policies.rules': 'Rules',
'policies.groups': 'groups',
'policies.updated': 'Updated',
'policies.state': 'State',
'policies.simulator': 'Policy simulator',
'policies.command': 'Command to parse',
'policies.playerUuid': 'Player UUID (optional)',
'policies.run': 'Simulate decision',
'policies.result': 'Decision explanation',
'policies.noResult': 'Enter a command to simulate it without executing it on the server.',
'policies.publish': 'Publish version',
'policies.publishWarning': 'Publishing atomically replaces the active policy. An audit reason is required.',
'policies.edit': 'Edit',
'policies.validate': 'Validate',
'policies.editor': 'Policy rule editor',
'policies.editorHint': 'The editor copies rules from an immutable version into a new draft. Saving does not publish; validate and simulate the impact first.',
'policies.draftDescription': 'Draft description and audit reason',
'policies.rulesJson': 'Rules JSON',
'policies.saveDraft': 'Validate and save draft',
'policies.draftSaved': 'Policy draft saved',
'policies.saveAndApply': 'Validate, save and apply now',
'policies.appliedImmediately': 'Policy published atomically and applied immediately; no restart required',
'policies.liveHint': 'Publishing hot-swaps the command policy for the next command; no server restart is required.',
'policies.validationComplete': 'Policy validation completed',
'policies.valid': 'Validation passed',
'policies.invalid': 'Validation failed',
'policies.rulesArrayRequired': 'Rules JSON must be an array of objects.',
'audit.title': 'Audit log',
'audit.description': 'Query management actions, policy decisions and failures. Sensitive values are redacted server-side.',
'audit.time': 'Time',
'audit.actor': 'Actor',
'audit.action': 'Action',
'audit.target': 'Target',
'audit.result': 'Result',
'audit.request': 'Request ID',
'console.title': 'Secure console',
'console.description': 'Minecraft commands only; operating-system shell and file access are never exposed.',
'console.locked': 'The console is disabled by server configuration or this session does not have owner access.',
'console.command': 'Minecraft command',
'console.execute': 'Confirm execution',
'console.warning': 'This action requires TOTP and recent reauthentication and is always audited.',
'console.history': 'Submitted this session',
'settings.title': 'System settings',
'settings.description': 'Inspect the web boundary, integrations and v2 feature switches.',
'settings.network': 'Network boundary',
'settings.bind': 'Bind address',
'settings.port': 'Port',
'settings.proxies': 'Trusted proxy CIDRs',
'settings.security': 'Security posture',
'settings.offlineMode': 'Minecraft offline-mode',
'settings.integrations': 'Soft integrations',
'settings.features': 'Feature switches',
'settings.readOnly': 'These are the current runtime values. Runtime-safe changes apply on save; only startup fields show a restart prompt.',
'messages.title': 'Messages & announcements',
'messages.sections': 'Message types',
'messages.description': 'Visually configure join messages, rule reminders, and daily announcements using vanilla-client rich text.',
'messages.richHint': 'Each message has its own sender, color, styling, links, command actions, hover details, and insertion text.',
'messages.variablesHint': 'Open “Insert variable” to search 100+ server-provided variables and read their descriptions. For custom time use {server.time:uuuu-MM-dd HH:mm:ss}: MM is month and mm is minute; avoid the week-based year YYYY. Saved messages apply immediately.',
'messages.joinWelcome': 'Every-join welcome',
'messages.firstJoin': 'First-join welcome',
'messages.rules': 'Daily rules reminder',
'messages.daily': 'Daily announcements',
'messages.onceDaily': 'Sent once per player and day in the selected time zone',
'messages.timeZone': 'Announcement time zone',
'messages.dailyList': 'Announcements (maximum 20)',
'messages.add': 'Add announcement',
'messages.item': 'Announcement',
'messages.behavior': 'Other join behavior',
'messages.maintenanceReminder': 'Maintenance join reminder',
'messages.maintenanceHint': 'Show the current maintenance state to allow-listed players who join',
'messages.ownerOnly': 'Only an Owner can change message settings.',
'messages.save': 'Save message settings',
'messages.saved': 'Message settings saved and applied immediately.',
'messages.restart': 'Restart pending',
'messages.live': 'Applied immediately',
'accounts.title': 'Web accounts and roles',
'accounts.description': 'Manage Web control-plane accounts. Passwords are submitted only during creation and are never displayed afterward.',
'accounts.locked': 'Account management requires an owner with verified TOTP and recent reauthentication. Complete the security check above first.',
'accounts.create': 'Create account',
'accounts.username': 'Username',
'accounts.initialPassword': 'Initial password',
'accounts.passwordHint': 'At least 12 characters; cleared immediately after a successful request.',
'accounts.role': 'Role',
'accounts.status': 'Status',
'accounts.totp': 'TOTP',
'accounts.lastLogin': 'Last login',
'accounts.active': 'Active',
'accounts.disabled': 'Disabled',
'accounts.never': 'Never',
'accounts.reviewChange': 'Review account change',
'accounts.confirmChange': 'Confirm account change',
'accounts.delete': 'Delete',
'accounts.confirmDelete': 'Confirm deletion',
'accounts.deleteWarning': 'Deleting an account revokes its access immediately and cannot be undone from this screen.',
'accounts.changeWarning': 'Role or disabled-state changes take effect immediately and may invalidate the target’s current sessions.',
'accounts.created': 'Account created',
'accounts.updated': 'Account updated',
'accounts.deleted': 'Account deleted',
'accounts.current': 'Current account',
'moderation.title': 'Moderation',
'moderation.description': 'Case-based warnings, mutes, kicks and bans.',
'moderation.newCase': 'New case',
'moderation.player': 'Player UUID or name',
'moderation.type': 'Sanction type',
'moderation.reason': 'Sanction reason',
'moderation.message': 'Player-facing message',
'moderation.messageHint': 'Defaults to the sanction reason and is shown for warnings, kicks, and bans.',
'moderation.duration': 'Duration (such as 2h or 7d, optional)',
'moderation.create': 'Preview and create',
'moderation.case': 'Case',
'moderation.expiry': 'Expires',
'moderation.state': 'State',
'moderation.featureOff': 'The moderation module is not enabled on the server.',
'moderation.revoke': 'Revoke sanction',
'moderation.revoked': 'Sanction revoked',
'maintenance.title': 'Maintenance & schedules',
'maintenance.description': 'Control maintenance admission, announcements and structured scheduled actions.',
'maintenance.featureOff': 'The moderation and maintenance module is not enabled on the server.',
'maintenance.mode': 'Maintenance mode',
'maintenance.modeHint': 'When enabled, only allow-listed UUIDs may remain online.',
'maintenance.message': 'Player-facing message',
'maintenance.allowed': 'Allowed player UUIDs (one per line)',
'maintenance.saved': 'Maintenance state saved',
'maintenance.announce': 'Server announcement',
'maintenance.announced': 'Announcement sent',
'maintenance.send': 'Send now',
'maintenance.schedule': 'Create schedule',
'maintenance.scheduled': 'Scheduled action created',
'maintenance.executeAt': 'Execution time',
'maintenance.action': 'Structured action',
'maintenance.enable': 'Enable maintenance',
'maintenance.disable': 'Disable maintenance',
'maintenance.stop': 'Graceful stop',
'maintenance.countdown': 'Countdown (such as 30s)',
'maintenance.stopScheduleHint': 'Creation broadcasts the stop time immediately; reminders run every five minutes in the last hour and every second in the final minute.',
'maintenance.jobs': 'Scheduled jobs',
'claims.title': 'Chunk claims',
'claims.description': 'Inspect territory ownership, dimension, membership and quota usage.',
'claims.owner': 'Owner',
'claims.dimension': 'Dimension',
'claims.chunks': 'Chunks',
'claims.members': 'Members',
'claims.featureOff': 'The claims module is not enabled on the server.',
'claims.create': 'Create claim',
'claims.edit': 'Save claim',
'claims.name': 'Claim name',
'claims.chunkLines': 'Chunk coordinates (one x,z pair per line; contiguous)',
'claims.memberLines': 'Members (one UUID=manager/member/trusted per line)',
'claims.flags': 'Publicly allowed behavior',
'claims.saved': 'Chunk claim saved',
'claims.deleted': 'Chunk claim deleted',
'claims.deleteWarning': 'This immediately removes protection from every chunk. Confirm deletion again.',
'claims.transfer': 'Ownership transfer (recipient confirmation required)',
'claims.newOwner': 'New owner UUID',
'claims.beginTransfer': 'Create ten-minute transfer token',
'claims.transferToken': 'Transfer token',
'claims.acceptingPlayer': 'Accepting player UUID',
'claims.confirmTransfer': 'Accept ownership',
'claims.transferReady': 'Transfer token created; expires:',
'claims.transferred': 'Ownership transfer completed',
'rollback.title': 'Tracking & rollback',
'rollback.description': 'Pin a watermark and preview conflicts before an audited, resumable rollback.',
'rollback.filter': 'Query and preview',
'rollback.actor': 'Player UUID (optional)',
'rollback.radius': 'Radius (blocks)',
'rollback.since': 'Start time',
'rollback.action': 'Action type (optional)',
'rollback.dimension': 'Dimension',
'rollback.center': 'Center coordinates',
'rollback.preview': 'Generate rollback preview',
'rollback.changes': 'World changes',
'rollback.position': 'Position',
'rollback.resource': 'Resource',
'rollback.summary': 'Preview summary',
'rollback.matched': 'Matched records',
'rollback.applicable': 'Applicable',
'rollback.conflicts': 'Conflicts',
'rollback.unloaded': 'Unloaded chunks',
'rollback.gaps': 'Log gaps',
'rollback.incompatible': 'Incompatible',
'rollback.execute': 'Execute this preview',
'rollback.pause': 'Pause',
'rollback.resume': 'Resume',
'rollback.cancel': 'Cancel operation',
'rollback.redo': 'Preview redo',
'rollback.featureOff': 'World tracking or rollback is not enabled on the server.',
'auth.title': 'Administrator authentication',
'auth.setupTitle': 'Create the first owner',
'auth.setupDescription': 'The bootstrap code is generated only in the physical server console, expires in ten minutes, and is single-use.',
'auth.description': 'Credentials are sent only to this server’s same-origin management API.',
'auth.username': 'Username',
'auth.password': 'Password',
'auth.passwordConfirm': 'Confirm password',
'auth.passwordHint': 'Use at least 12 characters; a unique password generated by a password manager is recommended.',
'auth.passwordMismatch': 'The passwords do not match.',
'auth.bootstrapToken': 'Console bootstrap code',
'auth.createOwner': 'Create owner',
'auth.totp': 'TOTP (when enabled)',
'auth.recoveryCode': 'Recovery code',
'auth.useRecovery': 'Use a recovery code',
'auth.useTotp': 'Use TOTP instead',
'auth.signIn': 'Sign in securely',
'auth.bootstrap': 'No owner exists. Generate a ten-minute, single-use bootstrap code from the physical server console first.',
'auth.totpSetup': 'Set up two-factor authentication',
'auth.totpSetupHint': 'The secure console requires TOTP. Enter your current password to create a one-time secret.',
'auth.startEnrollment': 'Generate TOTP secret',
'auth.secret': 'Manual-entry secret',
'auth.provisioningUri': 'Authenticator URI',
'auth.confirmTotp': 'Confirm and enable',
'auth.recoveryCodes': 'Recovery codes are shown once. Store them securely now; each code can only be used once.',
'auth.reauthenticate': 'Recent reauthentication',
'auth.reauthenticateHint': 'Dangerous actions require a fresh authenticator code; the original action resumes automatically.',
'auth.manualSetup': 'Cannot scan? Show manual setup',
'auth.verifyAgain': 'Verify again',
'auth.securityReady': 'This session satisfies the TOTP and recent-authentication requirements.',
'auth.securityUpdated': 'The secure session has been updated.',
'operation.queued': 'Operation queued',
},
} as const;
type MessageKey = keyof (typeof messages)['en-US'];
interface I18nValue {
locale: Locale;
setLocale: (locale: Locale) => void;
t: (key: MessageKey) => string;
}
const I18nContext = createContext<I18nValue | null>(null);
function initialLocale(): Locale {
const saved = typeof localStorage !== 'undefined' ? localStorage.getItem('xfesm.locale') : null;
if (saved === 'zh-CN' || saved === 'en-US') return saved;
return typeof navigator !== 'undefined' && navigator.language.toLowerCase().startsWith('zh') ? 'zh-CN' : 'en-US';
}
export function I18nProvider({ children }: { children: ReactNode }) {
const [locale, updateLocale] = useState<Locale>(initialLocale);
const value = useMemo<I18nValue>(() => ({
locale,
setLocale(next) {
localStorage.setItem('xfesm.locale', next);
document.documentElement.lang = next;
updateLocale(next);
},
t: (key) => messages[locale][key] ?? messages['en-US'][key] ?? key,
}), [locale]);
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}
export function useI18n(): I18nValue {
const context = useContext(I18nContext);
if (!context) throw new Error('useI18n must be used inside I18nProvider');
return context;
}
import { createContext, useContext, useMemo, useState, type ReactNode } from 'react';
import type { Locale } from '../types';
const messages = {
'zh-CN': {
'app.name': 'XFEServerManager',
'app.subtitle': 'Forge 服务器控制台',
'nav.core': '核心管理',
'nav.governance': '治理与恢复',
'nav.overview': '服务器概览',
'nav.players': '在线玩家',
'nav.economy': '经济系统',
'nav.messages': '触发器',
'nav.menus': 'UI 菜单',
'nav.content': '内容创作',
'nav.policies': '命令策略',
'nav.audit': '审计日志',
'nav.console': '安全控制台',
'nav.settings': '系统设置',
'nav.moderation': '处罚运营',
'nav.maintenance': '维护与计划',
'nav.claims': '区块认领',
'nav.rollback': '追踪与回滚',
'connection.online': '实时连接',
'connection.connecting': '正在连接',
'connection.offline': '管理接口离线',
'connection.degraded': '连接不稳定',
'connection.offlineDetail': '无法连接服务器管理接口。这里不会使用模拟数据;请确认专用服务器和 Web 服务已启动。',
'connection.retry': '重新连接',
'common.loading': '正在读取服务器数据…',
'common.empty': '没有可显示的数据',
'common.error': '读取失败',
'common.refresh': '刷新',
'common.search': '搜索',
'common.cancel': '取消',
'common.edit': '编辑',
'common.delete': '删除',
'common.confirm': '确认',
'common.save': '保存更改',
'common.enabled': '已启用',
'common.disabled': '未启用',
'common.preview': '预览',
'common.reason': '操作理由',
'common.reasonRequired': '必须填写审计理由',
'common.previous': '上一页',
'common.next': '下一页',
'common.notAvailable': '不可用',
'common.signOut': '退出登录',
'common.close': '关闭',
'access.title': '无权访问',
'access.description': '当前角色没有打开此管理页面所需的权限。',
'access.detail': '此功能已在界面中隐藏或禁用;如需使用,请由 Owner 调整账户角色。',
'overview.title': '服务器概览',
'overview.description': '来自专用服务器的实时健康状态与容量信号。',
'overview.tps': '每秒 Tick',
'overview.mspt': '平均 MSPT',
'overview.players': '在线玩家',
'overview.heap': '堆内存',
'overview.cpu': '进程 CPU',
'overview.uptime': '运行时间',
'overview.disk': '磁盘可用',
'overview.ping': '平均延迟',
'overview.performance': '性能趋势',
'overview.performanceHint': 'SSE 推送的最近采样;连接前不显示推测值。',
'overview.dimensions': '维度负载',
'overview.dimension': '维度',
'overview.chunks': '已加载区块',
'overview.entities': '实体',
'overview.serverIdentity': '运行环境',
'overview.sampledAt': '最后采样',
'players.title': '在线玩家',
'players.description': '检查身份、位置和状态,并发起受审计的玩家操作。',
'players.player': '玩家',
'players.role': '权限',
'players.location': '位置',
'players.state': '状态',
'players.actions': '操作',
'players.inspect': '管理',
'players.readOnly': '只读',
'players.online': '在线',
'players.offline': '离线',
'players.noPlayers': '当前没有在线玩家',
'players.actionTitle': '对玩家执行操作',
'players.action': '操作类型',
'players.kick': '踢出',
'players.message': '私信',
'players.heal': '治疗',
'players.feed': '恢复饱食度',
'players.clearEffects': '清除效果',
'players.teleport': '传送',
'players.recall': '召回到上次传送前位置',
'players.gameMode': '游戏模式',
'players.whitelist': '白名单',
'players.ban': '封禁',
'players.messageText': '消息内容',
'players.destination': '在线玩家名称或三个绝对坐标',
'players.whitelistMode': '白名单操作',
'players.whitelistAdd': '加入白名单',
'players.whitelistRemove': '移出白名单',
'players.submit': '提交操作',
'players.actionsTab': '玩家操作',
'players.inventory': '背包',
'players.enderChest': '末影箱',
'players.inventoryRevision': '槽位 revision',
'players.inventoryHint': '每次只修改一个槽位,并使用当前 revision 做服务端 CAS;冲突时会拒绝覆盖。',
'players.inventoryLoading': '正在读取在线玩家容器…',
'players.inventoryEmpty': '容器没有返回槽位数据。',
'players.slot': '槽位',
'players.itemId': '物品注册 ID',
'players.count': '数量(0–127)',
'players.editSlot': '编辑槽位',
'players.previewSlot': '预览槽位更改',
'players.saveSlot': '确认 CAS 修改',
'players.opaqueProtected': '此槽位包含无法由安全接口还原的原始数据,已禁止编辑以避免数据丢失。',
'players.slotBefore': '修改前',
'players.slotAfter': '修改后',
'players.inventorySaved': '槽位修改已提交',
'policies.title': '命令策略',
'policies.description': '管理不可变策略版本,并在发布前模拟 Brigadier 决策。',
'policies.name': '策略',
'policies.version': '版本',
'policies.rules': '规则',
'policies.groups': '策略组',
'policies.updated': '更新',
'policies.state': '状态',
'policies.simulator': '策略模拟器',
'policies.command': '待解析命令',
'policies.playerUuid': '玩家 UUID(可选)',
'policies.run': '模拟决策',
'policies.result': '决策解释',
'policies.noResult': '输入命令后运行模拟;不会在服务器执行。',
'policies.publish': '发布版本',
'policies.publishWarning': '发布将原子替换当前活动策略。必须填写理由。',
'policies.edit': '编辑',
'policies.validate': '校验',
'policies.editor': '策略规则编辑器',
'policies.editorHint': '编辑器会从不可变版本复制规则并建立新草稿;保存不会发布,发布前请先校验和模拟影响。',
'policies.draftDescription': '草稿说明与审计理由',
'policies.rulesJson': '规则 JSON',
'policies.saveDraft': '校验并保存草稿',
'policies.draftSaved': '策略草稿已保存',
'policies.saveAndApply': '校验、保存并立即生效',
'policies.appliedImmediately': '策略已原子发布并立即生效,无需重启',
'policies.liveHint': '发布会热替换当前命令策略,成功后立即用于下一条命令,不需要重启服务器。',
'policies.validationComplete': '策略校验已完成',
'policies.valid': '校验通过',
'policies.invalid': '校验失败',
'policies.rulesArrayRequired': '规则 JSON 必须是对象数组。',
'audit.title': '审计日志',
'audit.description': '查询管理操作、策略决策与失败结果。敏感值由服务端脱敏。',
'audit.time': '时间',
'audit.actor': '操作者',
'audit.action': '动作',
'audit.target': '目标',
'audit.result': '结果',
'audit.request': '请求 ID',
'console.title': '安全控制台',
'console.description': '仅执行 Minecraft 命令;不会提供操作系统 Shell 或文件访问。',
'console.locked': '控制台在服务端配置中关闭,或当前会话没有 owner 权限。',
'console.command': 'Minecraft 命令',
'console.execute': '确认执行',
'console.warning': '此操作需要 TOTP 与近期重新认证,并会写入审计日志。',
'console.history': '本次会话提交',
'settings.title': '系统设置',
'settings.description': '查看 Web 边界、集成状态与 v2 功能开关。',
'settings.network': '网络边界',
'settings.bind': '监听地址',
'settings.port': '端口',
'settings.proxies': '可信代理 CIDR',
'settings.security': '安全状态',
'settings.offlineMode': 'Minecraft offline-mode',
'settings.integrations': '软集成',
'settings.features': '功能开关',
'settings.readOnly': '这里显示当前进程的生效值;运行时安全项保存即生效,只有启动项按提示重启。',
'messages.title': '消息与公告',
'messages.sections': '消息类型',
'messages.description': '可视化配置入服消息、规则提醒和每日公告,使用原版客户端富文本能力。',
'messages.richHint': '每条消息可独立修改发送者、颜色、样式、链接、命令动作、悬停展开和插入文本。',
'messages.variablesHint': '点击“插入变量”可搜索服务器提供的 100+ 个变量并查看说明。自定义时间推荐 {server.time:uuuu-MM-dd HH:mm:ss}:MM 是月份、mm 是分钟;不要使用表示周序年的 YYYY。保存后立即生效。',
'messages.joinWelcome': '每次入服欢迎',
'messages.firstJoin': '首次入服欢迎',
'messages.rules': '每日规则提醒',
'messages.daily': '每日公告',
'messages.onceDaily': '按玩家和所选时区每日仅发送一次',
'messages.timeZone': '公告时区',
'messages.dailyList': '公告列表(最多 20 条)',
'messages.add': '新增公告',
'messages.item': '公告',
'messages.behavior': '其他入服行为',
'messages.maintenanceReminder': '维护模式入服提醒',
'messages.maintenanceHint': '对维护白名单内允许进入的玩家显示当前维护状态',
'messages.ownerOnly': '只有 Owner 可以修改消息配置。',
'messages.save': '保存消息配置',
'messages.saved': '消息配置已保存并立即生效。',
'messages.restart': '等待重启生效',
'messages.live': '已立即生效',
'accounts.title': 'Web 账户与角色',
'accounts.description': '管理 Web 控制台账户。密码只在创建请求中提交,之后永不显示。',
'accounts.locked': '账户管理需要 owner、已验证的 TOTP 和近期重新认证。请先在上方完成安全验证。',
'accounts.create': '创建账户',
'accounts.username': '用户名',
'accounts.initialPassword': '初始密码',
'accounts.passwordHint': '至少 12 个字符;提交成功后立即清空。',
'accounts.role': '角色',
'accounts.status': '状态',
'accounts.totp': 'TOTP',
'accounts.lastLogin': '最近登录',
'accounts.active': '正常',
'accounts.disabled': '已禁用',
'accounts.never': '从未登录',
'accounts.reviewChange': '预览账户变更',
'accounts.confirmChange': '确认账户变更',
'accounts.delete': '删除',
'accounts.confirmDelete': '确认删除',
'accounts.deleteWarning': '删除账户会立即撤销其访问,且无法通过此界面恢复。',
'accounts.changeWarning': '角色或禁用状态将在确认后立即生效,目标当前会话可能失效。',
'accounts.created': '账户已创建',
'accounts.updated': '账户已更新',
'accounts.deleted': '账户已删除',
'accounts.current': '当前账户',
'moderation.title': '处罚运营',
'moderation.description': '案件化管理警告、禁言、踢出与封禁。',
'moderation.newCase': '新建案件',
'moderation.player': '玩家 UUID 或名称',
'moderation.type': '处罚类型',
'moderation.reason': '处罚理由',
'moderation.message': '玩家可见消息',
'moderation.messageHint': '留空时使用处罚理由;警告、踢出和封禁时会向玩家显示。',
'moderation.duration': '时长(例如 2h、7d,可选)',
'moderation.create': '预览并创建',
'moderation.case': '案件',
'moderation.expiry': '到期时间',
'moderation.state': '状态',
'moderation.featureOff': '处罚模块尚未在服务端启用。',
'moderation.revoke': '撤销处罚',
'moderation.revoked': '处罚已撤销',
'maintenance.title': '维护与计划',
'maintenance.description': '管理维护准入、公告和仅限结构化动作的计划任务。',
'maintenance.featureOff': '处罚与维护模块尚未在服务端启用。',
'maintenance.mode': '维护模式',
'maintenance.modeHint': '启用后仅允许白名单 UUID 留在服务器。',
'maintenance.message': '玩家可见消息',
'maintenance.allowed': '允许的玩家 UUID(每行一个)',
'maintenance.saved': '维护状态已保存',
'maintenance.announce': '服务器公告',
'maintenance.announced': '公告已发送',
'maintenance.send': '立即发送',
'maintenance.schedule': '创建计划任务',
'maintenance.scheduled': '计划任务已创建',
'maintenance.executeAt': '执行时间',
'maintenance.action': '结构化动作',
'maintenance.enable': '开启维护',
'maintenance.disable': '关闭维护',
'maintenance.stop': '优雅关服',
'maintenance.countdown': '倒计时(例如 30s)',
'maintenance.stopScheduleHint': '创建后立即公告关服时间;最后 1 小时每 5 分钟提醒,最后 60 秒逐秒倒计时。',
'maintenance.jobs': '计划任务',
'claims.title': '区块认领',
'claims.description': '查看领地所有者、维度、成员与配额占用。',
'claims.owner': '所有者',
'claims.dimension': '维度',
'claims.chunks': '区块',
'claims.members': '成员',
'claims.featureOff': '区块认领模块尚未在服务端启用。',
'claims.create': '新建认领',
'claims.edit': '保存认领',
'claims.name': '认领名称',
'claims.chunkLines': '区块坐标(每行 x,z,必须相邻)',
'claims.memberLines': '成员(每行 UUID=manager/member/trusted)',
'claims.flags': '对外开放行为',
'claims.saved': '区块认领已保存',
'claims.deleted': '区块认领已删除',
'claims.deleteWarning': '这会立即解除所有区块保护。再次确认删除。',
'claims.transfer': '所有权转让(接收方必须确认)',
'claims.newOwner': '新所有者 UUID',
'claims.beginTransfer': '生成十分钟转让令牌',
'claims.transferToken': '转让令牌',
'claims.acceptingPlayer': '接收方 UUID',
'claims.confirmTransfer': '确认接收所有权',
'claims.transferReady': '转让令牌已生成,到期:',
'claims.transferred': '所有权转让已完成',
'rollback.title': '追踪与回滚',
'rollback.description': '先固定水位并预览冲突,再执行受审计、可恢复的回滚。',
'rollback.filter': '查询与预览',
'rollback.actor': '玩家 UUID(可选)',
'rollback.radius': '半径(方块)',
'rollback.since': '起始时间',
'rollback.action': '动作类型(可选)',
'rollback.dimension': '维度',
'rollback.center': '中心坐标',
'rollback.preview': '生成回滚预览',
'rollback.changes': '世界变化',
'rollback.position': '位置',
'rollback.resource': '资源',
'rollback.summary': '预览摘要',
'rollback.matched': '匹配记录',
'rollback.applicable': '可应用',
'rollback.conflicts': '冲突',
'rollback.unloaded': '未加载区块',
'rollback.gaps': '日志缺口',
'rollback.incompatible': '不兼容',
'rollback.execute': '执行此预览',
'rollback.pause': '暂停',
'rollback.resume': '继续',
'rollback.cancel': '取消任务',
'rollback.redo': '生成重做预览',
'rollback.featureOff': '世界追踪或回滚模块尚未在服务端启用。',
'auth.title': '管理身份验证',
'auth.setupTitle': '创建首位 Owner',
'auth.setupDescription': '引导码只在物理服务器终端生成,有效期十分钟且只能使用一次。',
'auth.description': '登录凭据只发送到此服务器的同源管理接口。',
'auth.username': '用户名',
'auth.password': '密码',
'auth.passwordConfirm': '确认密码',
'auth.passwordHint': '至少 12 个字符;建议使用密码管理器生成的独立密码。',
'auth.passwordMismatch': '两次输入的密码不一致。',
'auth.bootstrapToken': '终端引导码',
'auth.createOwner': '创建 Owner',
'auth.totp': 'TOTP(如已启用)',
'auth.recoveryCode': '恢复码',
'auth.useRecovery': '改用恢复码',
'auth.useTotp': '改用 TOTP',
'auth.signIn': '安全登录',
'auth.bootstrap': '尚未创建 owner。请先在物理服务器控制台生成十分钟单次引导码。',
'auth.totpSetup': '配置两步验证',
'auth.totpSetupHint': '安全控制台必须启用 TOTP。输入当前密码生成一次性密钥。',
'auth.startEnrollment': '生成 TOTP 密钥',
'auth.secret': '手动输入密钥',
'auth.provisioningUri': '身份验证器 URI',
'auth.confirmTotp': '确认并启用',
'auth.recoveryCodes': '恢复码只显示一次。立即保存到安全位置;每个恢复码只能使用一次。',
'auth.reauthenticate': '近期重新认证',
'auth.reauthenticateHint': '危险操作需要新的动态验证码;验证后会自动继续原操作。',
'auth.manualSetup': '无法扫码?显示手动绑定信息',
'auth.verifyAgain': '重新验证',
'auth.securityReady': '此会话已满足 TOTP 与近期认证要求。',
'auth.securityUpdated': '安全会话已更新。',
'operation.queued': '操作已排队',
},
'en-US': {
'app.name': 'XFEServerManager',
'app.subtitle': 'Forge server console',
'nav.core': 'Core management',
'nav.governance': 'Governance & recovery',
'nav.overview': 'Overview',
'nav.players': 'Online players',
'nav.economy': 'Economy',
'nav.messages': 'Triggers',
'nav.menus': 'UI menus',
'nav.content': 'Content studio',
'nav.policies': 'Command policies',
'nav.audit': 'Audit log',
'nav.console': 'Secure console',
'nav.settings': 'Settings',
'nav.moderation': 'Moderation',
'nav.maintenance': 'Maintenance & schedules',
'nav.claims': 'Claims',
'nav.rollback': 'Tracking & rollback',
'connection.online': 'Live connection',
'connection.connecting': 'Connecting',
'connection.offline': 'Management API offline',
'connection.degraded': 'Connection degraded',
'connection.offlineDetail': 'The management API cannot be reached. No demo values are substituted; check that the dedicated server and web service are running.',
'connection.retry': 'Reconnect',
'common.loading': 'Reading server data…',
'common.empty': 'No data to display',
'common.error': 'Unable to load data',
'common.refresh': 'Refresh',
'common.search': 'Search',
'common.cancel': 'Cancel',
'common.edit': 'Edit',
'common.delete': 'Delete',
'common.confirm': 'Confirm',
'common.save': 'Save changes',
'common.enabled': 'Enabled',
'common.disabled': 'Disabled',
'common.preview': 'Preview',
'common.reason': 'Audit reason',
'common.reasonRequired': 'An audit reason is required',
'common.previous': 'Previous',
'common.next': 'Next',
'common.notAvailable': 'Unavailable',
'common.signOut': 'Sign out',
'common.close': 'Close',
'access.title': 'Access denied',
'access.description': 'Your current role cannot open this management page.',
'access.detail': 'This feature is hidden or disabled for your account. Ask an Owner to change your role if access is required.',
'overview.title': 'Server overview',
'overview.description': 'Live health and capacity signals from the dedicated server.',
'overview.tps': 'Ticks per second',
'overview.mspt': 'Average MSPT',
'overview.players': 'Players online',
'overview.heap': 'Heap memory',
'overview.cpu': 'Process CPU',
'overview.uptime': 'Uptime',
'overview.disk': 'Disk available',
'overview.ping': 'Average latency',
'overview.performance': 'Performance trend',
'overview.performanceHint': 'Recent SSE-backed samples; no estimates appear before connection.',
'overview.dimensions': 'Dimension load',
'overview.dimension': 'Dimension',
'overview.chunks': 'Loaded chunks',
'overview.entities': 'Entities',
'overview.serverIdentity': 'Runtime',
'overview.sampledAt': 'Last sample',
'players.title': 'Online players',
'players.description': 'Inspect identity, position and state, then run audited player actions.',
'players.player': 'Player',
'players.role': 'Access',
'players.location': 'Location',
'players.state': 'State',
'players.actions': 'Actions',
'players.inspect': 'Manage',
'players.readOnly': 'Read only',
'players.online': 'Online',
'players.offline': 'Offline',
'players.noPlayers': 'No players are currently online',
'players.actionTitle': 'Run a player action',
'players.action': 'Action type',
'players.kick': 'Kick',
'players.message': 'Direct message',
'players.heal': 'Heal',
'players.feed': 'Restore food',
'players.clearEffects': 'Clear effects',
'players.teleport': 'Teleport',
'players.recall': 'Recall to pre-teleport location',
'players.gameMode': 'Game mode',
'players.whitelist': 'Whitelist',
'players.ban': 'Ban',
'players.messageText': 'Message',
'players.destination': 'Online player name or three absolute coordinates',
'players.whitelistMode': 'Whitelist operation',
'players.whitelistAdd': 'Add to whitelist',
'players.whitelistRemove': 'Remove from whitelist',
'players.submit': 'Submit action',
'players.actionsTab': 'Player actions',
'players.inventory': 'Inventory',
'players.enderChest': 'Ender chest',
'players.inventoryRevision': 'Slot revision',
'players.inventoryHint': 'One slot is changed at a time using the current server revision for compare-and-set; conflicts are rejected.',
'players.inventoryLoading': 'Loading the online player container…',
'players.inventoryEmpty': 'The container returned no slots.',
'players.slot': 'Slot',
'players.itemId': 'Item registry ID',
'players.count': 'Count (0–127)',
'players.editSlot': 'Edit slot',
'players.previewSlot': 'Preview slot change',
'players.saveSlot': 'Confirm CAS update',
'players.opaqueProtected': 'This slot contains opaque data that the safe API cannot restore, so editing is blocked to prevent data loss.',
'players.slotBefore': 'Before',
'players.slotAfter': 'After',
'players.inventorySaved': 'Slot update submitted',
'policies.title': 'Command policies',
'policies.description': 'Manage immutable policy versions and simulate Brigadier decisions before publishing.',
'policies.name': 'Policy',
'policies.version': 'Version',
'policies.rules': 'Rules',
'policies.groups': 'groups',
'policies.updated': 'Updated',
'policies.state': 'State',
'policies.simulator': 'Policy simulator',
'policies.command': 'Command to parse',
'policies.playerUuid': 'Player UUID (optional)',
'policies.run': 'Simulate decision',
'policies.result': 'Decision explanation',
'policies.noResult': 'Enter a command to simulate it without executing it on the server.',
'policies.publish': 'Publish version',
'policies.publishWarning': 'Publishing atomically replaces the active policy. An audit reason is required.',
'policies.edit': 'Edit',
'policies.validate': 'Validate',
'policies.editor': 'Policy rule editor',
'policies.editorHint': 'The editor copies rules from an immutable version into a new draft. Saving does not publish; validate and simulate the impact first.',
'policies.draftDescription': 'Draft description and audit reason',
'policies.rulesJson': 'Rules JSON',
'policies.saveDraft': 'Validate and save draft',
'policies.draftSaved': 'Policy draft saved',
'policies.saveAndApply': 'Validate, save and apply now',
'policies.appliedImmediately': 'Policy published atomically and applied immediately; no restart required',
'policies.liveHint': 'Publishing hot-swaps the command policy for the next command; no server restart is required.',
'policies.validationComplete': 'Policy validation completed',
'policies.valid': 'Validation passed',
'policies.invalid': 'Validation failed',
'policies.rulesArrayRequired': 'Rules JSON must be an array of objects.',
'audit.title': 'Audit log',
'audit.description': 'Query management actions, policy decisions and failures. Sensitive values are redacted server-side.',
'audit.time': 'Time',
'audit.actor': 'Actor',
'audit.action': 'Action',
'audit.target': 'Target',
'audit.result': 'Result',
'audit.request': 'Request ID',
'console.title': 'Secure console',
'console.description': 'Minecraft commands only; operating-system shell and file access are never exposed.',
'console.locked': 'The console is disabled by server configuration or this session does not have owner access.',
'console.command': 'Minecraft command',
'console.execute': 'Confirm execution',
'console.warning': 'This action requires TOTP and recent reauthentication and is always audited.',
'console.history': 'Submitted this session',
'settings.title': 'System settings',
'settings.description': 'Inspect the web boundary, integrations and v2 feature switches.',
'settings.network': 'Network boundary',
'settings.bind': 'Bind address',
'settings.port': 'Port',
'settings.proxies': 'Trusted proxy CIDRs',
'settings.security': 'Security posture',
'settings.offlineMode': 'Minecraft offline-mode',
'settings.integrations': 'Soft integrations',
'settings.features': 'Feature switches',
'settings.readOnly': 'These are the current runtime values. Runtime-safe changes apply on save; only startup fields show a restart prompt.',
'messages.title': 'Messages & announcements',
'messages.sections': 'Message types',
'messages.description': 'Visually configure join messages, rule reminders, and daily announcements using vanilla-client rich text.',
'messages.richHint': 'Each message has its own sender, color, styling, links, command actions, hover details, and insertion text.',
'messages.variablesHint': 'Open “Insert variable” to search 100+ server-provided variables and read their descriptions. For custom time use {server.time:uuuu-MM-dd HH:mm:ss}: MM is month and mm is minute; avoid the week-based year YYYY. Saved messages apply immediately.',
'messages.joinWelcome': 'Every-join welcome',
'messages.firstJoin': 'First-join welcome',
'messages.rules': 'Daily rules reminder',
'messages.daily': 'Daily announcements',
'messages.onceDaily': 'Sent once per player and day in the selected time zone',
'messages.timeZone': 'Announcement time zone',
'messages.dailyList': 'Announcements (maximum 20)',
'messages.add': 'Add announcement',
'messages.item': 'Announcement',
'messages.behavior': 'Other join behavior',
'messages.maintenanceReminder': 'Maintenance join reminder',
'messages.maintenanceHint': 'Show the current maintenance state to allow-listed players who join',
'messages.ownerOnly': 'Only an Owner can change message settings.',
'messages.save': 'Save message settings',
'messages.saved': 'Message settings saved and applied immediately.',
'messages.restart': 'Restart pending',
'messages.live': 'Applied immediately',
'accounts.title': 'Web accounts and roles',
'accounts.description': 'Manage Web control-plane accounts. Passwords are submitted only during creation and are never displayed afterward.',
'accounts.locked': 'Account management requires an owner with verified TOTP and recent reauthentication. Complete the security check above first.',
'accounts.create': 'Create account',
'accounts.username': 'Username',
'accounts.initialPassword': 'Initial password',
'accounts.passwordHint': 'At least 12 characters; cleared immediately after a successful request.',
'accounts.role': 'Role',
'accounts.status': 'Status',
'accounts.totp': 'TOTP',
'accounts.lastLogin': 'Last login',
'accounts.active': 'Active',
'accounts.disabled': 'Disabled',
'accounts.never': 'Never',
'accounts.reviewChange': 'Review account change',
'accounts.confirmChange': 'Confirm account change',
'accounts.delete': 'Delete',
'accounts.confirmDelete': 'Confirm deletion',
'accounts.deleteWarning': 'Deleting an account revokes its access immediately and cannot be undone from this screen.',
'accounts.changeWarning': 'Role or disabled-state changes take effect immediately and may invalidate the target’s current sessions.',
'accounts.created': 'Account created',
'accounts.updated': 'Account updated',
'accounts.deleted': 'Account deleted',
'accounts.current': 'Current account',
'moderation.title': 'Moderation',
'moderation.description': 'Case-based warnings, mutes, kicks and bans.',
'moderation.newCase': 'New case',
'moderation.player': 'Player UUID or name',
'moderation.type': 'Sanction type',
'moderation.reason': 'Sanction reason',
'moderation.message': 'Player-facing message',
'moderation.messageHint': 'Defaults to the sanction reason and is shown for warnings, kicks, and bans.',
'moderation.duration': 'Duration (such as 2h or 7d, optional)',
'moderation.create': 'Preview and create',
'moderation.case': 'Case',
'moderation.expiry': 'Expires',
'moderation.state': 'State',
'moderation.featureOff': 'The moderation module is not enabled on the server.',
'moderation.revoke': 'Revoke sanction',
'moderation.revoked': 'Sanction revoked',
'maintenance.title': 'Maintenance & schedules',
'maintenance.description': 'Control maintenance admission, announcements and structured scheduled actions.',
'maintenance.featureOff': 'The moderation and maintenance module is not enabled on the server.',
'maintenance.mode': 'Maintenance mode',
'maintenance.modeHint': 'When enabled, only allow-listed UUIDs may remain online.',
'maintenance.message': 'Player-facing message',
'maintenance.allowed': 'Allowed player UUIDs (one per line)',
'maintenance.saved': 'Maintenance state saved',
'maintenance.announce': 'Server announcement',
'maintenance.announced': 'Announcement sent',
'maintenance.send': 'Send now',
'maintenance.schedule': 'Create schedule',
'maintenance.scheduled': 'Scheduled action created',
'maintenance.executeAt': 'Execution time',
'maintenance.action': 'Structured action',
'maintenance.enable': 'Enable maintenance',
'maintenance.disable': 'Disable maintenance',
'maintenance.stop': 'Graceful stop',
'maintenance.countdown': 'Countdown (such as 30s)',
'maintenance.stopScheduleHint': 'Creation broadcasts the stop time immediately; reminders run every five minutes in the last hour and every second in the final minute.',
'maintenance.jobs': 'Scheduled jobs',
'claims.title': 'Chunk claims',
'claims.description': 'Inspect territory ownership, dimension, membership and quota usage.',
'claims.owner': 'Owner',
'claims.dimension': 'Dimension',
'claims.chunks': 'Chunks',
'claims.members': 'Members',
'claims.featureOff': 'The claims module is not enabled on the server.',
'claims.create': 'Create claim',
'claims.edit': 'Save claim',
'claims.name': 'Claim name',
'claims.chunkLines': 'Chunk coordinates (one x,z pair per line; contiguous)',
'claims.memberLines': 'Members (one UUID=manager/member/trusted per line)',
'claims.flags': 'Publicly allowed behavior',
'claims.saved': 'Chunk claim saved',
'claims.deleted': 'Chunk claim deleted',
'claims.deleteWarning': 'This immediately removes protection from every chunk. Confirm deletion again.',
'claims.transfer': 'Ownership transfer (recipient confirmation required)',
'claims.newOwner': 'New owner UUID',
'claims.beginTransfer': 'Create ten-minute transfer token',
'claims.transferToken': 'Transfer token',
'claims.acceptingPlayer': 'Accepting player UUID',
'claims.confirmTransfer': 'Accept ownership',
'claims.transferReady': 'Transfer token created; expires:',
'claims.transferred': 'Ownership transfer completed',
'rollback.title': 'Tracking & rollback',
'rollback.description': 'Pin a watermark and preview conflicts before an audited, resumable rollback.',
'rollback.filter': 'Query and preview',
'rollback.actor': 'Player UUID (optional)',
'rollback.radius': 'Radius (blocks)',
'rollback.since': 'Start time',
'rollback.action': 'Action type (optional)',
'rollback.dimension': 'Dimension',
'rollback.center': 'Center coordinates',
'rollback.preview': 'Generate rollback preview',
'rollback.changes': 'World changes',
'rollback.position': 'Position',
'rollback.resource': 'Resource',
'rollback.summary': 'Preview summary',
'rollback.matched': 'Matched records',
'rollback.applicable': 'Applicable',
'rollback.conflicts': 'Conflicts',
'rollback.unloaded': 'Unloaded chunks',
'rollback.gaps': 'Log gaps',
'rollback.incompatible': 'Incompatible',
'rollback.execute': 'Execute this preview',
'rollback.pause': 'Pause',
'rollback.resume': 'Resume',
'rollback.cancel': 'Cancel operation',
'rollback.redo': 'Preview redo',
'rollback.featureOff': 'World tracking or rollback is not enabled on the server.',
'auth.title': 'Administrator authentication',
'auth.setupTitle': 'Create the first owner',
'auth.setupDescription': 'The bootstrap code is generated only in the physical server console, expires in ten minutes, and is single-use.',
'auth.description': 'Credentials are sent only to this server’s same-origin management API.',
'auth.username': 'Username',
'auth.password': 'Password',
'auth.passwordConfirm': 'Confirm password',
'auth.passwordHint': 'Use at least 12 characters; a unique password generated by a password manager is recommended.',
'auth.passwordMismatch': 'The passwords do not match.',
'auth.bootstrapToken': 'Console bootstrap code',
'auth.createOwner': 'Create owner',
'auth.totp': 'TOTP (when enabled)',
'auth.recoveryCode': 'Recovery code',
'auth.useRecovery': 'Use a recovery code',
'auth.useTotp': 'Use TOTP instead',
'auth.signIn': 'Sign in securely',
'auth.bootstrap': 'No owner exists. Generate a ten-minute, single-use bootstrap code from the physical server console first.',
'auth.totpSetup': 'Set up two-factor authentication',
'auth.totpSetupHint': 'The secure console requires TOTP. Enter your current password to create a one-time secret.',
'auth.startEnrollment': 'Generate TOTP secret',
'auth.secret': 'Manual-entry secret',
'auth.provisioningUri': 'Authenticator URI',
'auth.confirmTotp': 'Confirm and enable',
'auth.recoveryCodes': 'Recovery codes are shown once. Store them securely now; each code can only be used once.',
'auth.reauthenticate': 'Recent reauthentication',
'auth.reauthenticateHint': 'Dangerous actions require a fresh authenticator code; the original action resumes automatically.',
'auth.manualSetup': 'Cannot scan? Show manual setup',
'auth.verifyAgain': 'Verify again',
'auth.securityReady': 'This session satisfies the TOTP and recent-authentication requirements.',
'auth.securityUpdated': 'The secure session has been updated.',
'operation.queued': 'Operation queued',
},
} as const;
type MessageKey = keyof (typeof messages)['en-US'];
interface I18nValue {
locale: Locale;
setLocale: (locale: Locale) => void;
t: (key: MessageKey) => string;
}
const I18nContext = createContext<I18nValue | null>(null);
function initialLocale(): Locale {
const saved = typeof localStorage !== 'undefined' ? localStorage.getItem('xfesm.locale') : null;
if (saved === 'zh-CN' || saved === 'en-US') return saved;
return typeof navigator !== 'undefined' && navigator.language.toLowerCase().startsWith('zh') ? 'zh-CN' : 'en-US';
}
export function I18nProvider({ children }: { children: ReactNode }) {
const [locale, updateLocale] = useState<Locale>(initialLocale);
const value = useMemo<I18nValue>(() => ({
locale,
setLocale(next) {
localStorage.setItem('xfesm.locale', next);
document.documentElement.lang = next;
updateLocale(next);
},
t: (key) => messages[locale][key] ?? messages['en-US'][key] ?? key,
}), [locale]);
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}
export function useI18n(): I18nValue {
const context = useContext(I18nContext);
if (!context) throw new Error('useI18n must be used inside I18nProvider');
return context;
}