XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
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;
}