Files
TurboSu/static/js/utils/lang.js
T

365 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const Lang = {
_current: 'zh',
_strings: {
zh: {
// Nav
home: '首页', dashboard: '仪表盘', scene: '场景', market: '市场',
record: '录制回放', debug: '数据测试', settings: '设置',
selectGame: '选择游戏', collapseSidebar: '折叠侧边栏',
// Connection
connected: '已连接', disconnected: '未连接', connectionError: '连接错误',
// Status cards
serverStatus: '服务器状态', telemetryStatus: '遥测监听',
wsConnections: 'WebSocket 连接', packetCount: '数据包接收',
lastPacket: '最后数据包', currentGame: '当前游戏',
liveSpeed: '实时速度', liveRpm: '实时转速', liveGear: '当前档位',
systemOverview: '系统概览', overviewDesc: '实时监控 TurboSu 运行状态与游戏遥测连接',
running: '运行中', stopped: '已停止', notStarted: '未启动',
noData: '暂无数据', notSelected: '未选择',
listening: '监听中',
// Dashboard
searchDashboards: '搜索仪表盘...', sortByName: '名称', sortByCategory: '分类',
sortByAuthor: '作者', importTsd: '导入 .tsd', newDashboard: '+ 新建',
noDashboards: '暂无仪表盘主题', createFirstDash: '创建或导入你的第一个仪表盘主题',
noCompatibleDash: '当前游戏没有兼容的仪表盘',
allCategory: '全部', builtin: '内置', custom: '自定义',
fullCompatible: '全', limitedCompatible: '限',
// Scene
sceneManagement: '场景管理', noGameSelected: '请先在侧边栏选择一个游戏',
newScene: '+ 新建场景', importTss: '导入 .tss',
noScenes: '暂无场景', createFirstScene: '点击"新建场景"创建你的第一个场景布局',
render: '渲染', edit: '编辑', delete: '删除', export: '导出',
sceneName: '场景名称', sceneDescription: '场景描述(可选)',
sceneCanvasHint: '提示:保存后可在场景编辑器中添加仪表盘、调整布局和画布比例。',
save: '保存', cancel: '取消', editScene: '编辑场景',
canvasesCount: '个画布', backToList: '← 返回', saveScene: '保存场景',
dashListDrag: '仪表盘列表 (拖到画布)', noCompatibleDashForScene: '无兼容仪表盘',
// Market
marketPage: '仪表盘市场', marketDesc: '浏览并安装社区分享的仪表盘和场景',
notLoggedIn: '未登录', loggedIn: '已登录',
login: '登录', register: '注册', logout: '退出',
publish: '发布你的作品', install: '安装', installed: '已安装',
installScene: '安装场景', uninstall: '卸载',
pending: '待审核', approved: '已通过',
marketLogin: '市场账户',
email: '邮箱', password: '密码', username: '用户名',
loginSuccess: '登录成功', loginFailed: '登录失败',
registerSuccess: '注册成功,请查收验证邮件(点击关闭)', registerFailed: '注册失败',
publishTitle: '发布作品', publishDesc: '选择要发布的仪表盘或场景',
chooseFile: '📁 选择文件 (.tsd / .tss)',
orChooseLocal: '— 或从本地选择 —',
localDashboards: '本地仪表盘', localScenes: '本地场景',
noPublishable: '没有可发布的内容', publishFailed: '发布失败',
publishSuccess: '发布成功',
uploadPreview: '上传预览图', dropPreview: '点击或拖放图片到此处 (最大 6MB)',
previewTooLarge: '预览图不能超过 6MB', imageOnly: '仅支持图片格式',
updateItem: '更新', confirmDelete: '确定删除?',
myItems: '我的', myDashboards: '我的仪表盘', myScenes: '我的场景',
noUploads: '你还没有上传任何内容',
refresh: '🔄', retry: '重试', loadFailed: '加载失败',
dashboardsTab: '仪表盘', scenesTab: '场景', myTab: '我的',
installedHint: '安装成功!请在仪表盘页面查看',
installFailed: '安装失败',
needLogin: '请先登录',
confirmUninstall: '确定卸载 "$1"',
// Record
recordPage: '录制与回放', recordDesc: '录制原始 UDP 流到 .tsr 文件,回放调试仪表盘',
recordControl: '录制控制', startRecord: '开始录制', stopRecord: '停止录制',
ready: '就绪', recording: '录制中...', autoDetect: '自动检测',
playbackActive: '正在回放: ', playbackHint: '打开仪表盘页面即可看到数据',
stopPlayback: '停止', recordFiles: '录制文件', importTsr: '导入 .tsr',
noRecordings: '暂无录制文件', play: '播放',
// Debug
debugPage: '数据测试', debugToolbar: '',
parsedFields: '解析后的数据字段', rawJsonStream: '原始 JSON 数据流',
waitingData: '等待数据...', pause: '⏸ 暂停', resume: '▶ 继续',
clear: '清空', autoScroll: '自动滚动', viewLog: '📄 日志',
// Settings
connectionSettings: '连接设置',
telemetryPort: '遥测监听端口', telemetryPortDesc: '全局统一端口',
tcpPort: 'TCP 默认端口', tcpPortDesc: 'TCP 协议默认监听端口',
unifiedPort: '统一端口', unifiedPortDesc: '关闭后切游戏自动切换到各游戏的默认端口',
unifiedPortToggle: '使用游戏独立端口',
perGamePort: '各游戏独立端口', savePorts: '保存端口',
restartListener: '重启遥测监听', restartListenerDesc: '修改端口或切换游戏后',
restartBtn: '重启监听',
dataForwarding: '数据转发', dataForwardingDesc: '将收到的游戏原始 UDP 数据包完整转发到下游物理外设',
addTarget: '+ 添加目标', saveEffective: '保存并生效',
noForwardTargets: '暂无转发目标,点击下方添加',
forwardEnabled: '启用',
dashManagement: '仪表盘管理', rightClickExport: '右键卡片导出',
pluginManagement: '游戏插件管理', importTsp: '导入 .tsp',
reloadPlugins: '重新加载', noPlugins: '暂无游戏插件',
dataBackup: '数据备份', exportBackup: '导出全量备份',
backupDesc: '打包仪表盘+场景+配置为 .tsb', downloadTsb: '下载 .tsb',
restoreBackup: '恢复备份', restoreBackupDesc: '从 .tsb 文件恢复',
importTsb: '导入 .tsb',
devTools: '开发工具', testMode: 'UI 测试模式',
testModeDesc: '本地随机模拟遥测数据',
testModeOn: '测试模式已开启', testModeOff: '测试模式已关闭',
systemSection: '系统', restartServer: '重启 TurboSu',
restartServerDesc: '重新加载所有代码和配置', restartBtnSys: '重启服务',
confirmRestart: '确定重启 TurboSu 服务?',
restarting: '服务正在重启,3秒后自动刷新...',
about: '关于', aboutDesc: '赛车遥测仪表盘 · 极速中枢 · Yei.J. (AskaEth)',
// General
loading: '加载中...', confirm: '确定', success: '成功', error: '错误',
clickClose: '点击关闭', saved: '已保存', updated: '已更新',
deleted: '已删除', portUpdated: '端口已更新,重启后生效',
configSaved: '已保存', telemetryRestarted: '遥测监听已重启',
pluginInstalled: '插件安装成功', pluginRemoved: '插件已移除',
pluginReloaded: '插件已重新加载',
// Counters & formats
connectionsCount: '%d 个客户端', packetsCount: '%d 包',
secondsAgo: '%d秒前', gearLabel: '档位 %s',
downloadCount: '%d 次下载', downloadCountShort: '%d 次',
canvasesLabel: '%d 个画布', forwardsSaved: '已保存 %d 个转发目标',
defaultPortLabel: '默认: ',
// More UI
noGamePlugins: '暂无可用的游戏插件',
noGame: '未关联游戏', currentGamePrefix: '当前游戏: ',
community: '社区',
noScenesMarket: '暂无场景', noDashboardsMarket: '暂无仪表盘',
// Prompts / modals
dashboardNamePrompt: '仪表盘名称:',
marketLoginPrompt: '请先登录市场账户',
enterEmailPassword: '请输入邮箱和密码',
selectFileOrPreview: '请选择文件或预览图',
confirmRemove: '移除?',
// Toast: import / create / upload
importSuccess: '导入成功', importFailed: '导入失败',
createSuccess: '创建成功', createFailed: '创建失败',
uploadSuccess: '上传成功', uploadFailed: '上传失败',
uploadFailedMsg: '上传失败: ',
installSuccess: '安装成功', installFailedMsg: '安装失败: ',
remove: '移除',
uninstallSuccess: '已卸载', uninstallFailed: '卸载失败',
uninstallNotFound: '未找到本地安装',
deleteFailed: '删除失败', loggedOut: '已退出',
sceneSaveSuccess: '场景保存成功', saveFailed: '保存失败: ',
sceneImportSuccess: '场景导入成功',
playbackStarted: '回放已开始', playbackStartedHint: '回放已开始,打开仪表盘查看数据',
restoreSuccess: '恢复成功', restoreFailed: '恢复失败: ',
removeSuccess: '已移除',
exportTsdHint: '正在下载 .tsd 文件...',
linkCopied: '链接已复制,可在局域网设备访问',
linkCopiedShort: '链接已复制',
portsSaved: '端口已保存',
marketEmpty: '市场暂无仪表盘', marketLoadFailed: '市场加载失败',
installDataIncomplete: '安装失败:数据不完整',
installNetworkError: '安装失败:网络错误',
restartRequestSent: '重启请求已发送,请手动刷新',
marketNoItems: '暂无',
confirmUpdate: '确认更新',
},
en: {
home: 'Home', dashboard: 'Dashboard', scene: 'Scene', market: 'Market',
record: 'Record', debug: 'Debug', settings: 'Settings',
selectGame: 'Select Game', collapseSidebar: 'Collapse',
connected: 'Connected', disconnected: 'Disconnected', connectionError: 'Error',
serverStatus: 'Server', telemetryStatus: 'Telemetry',
wsConnections: 'Connections', packetCount: 'Packets',
lastPacket: 'Last Packet', currentGame: 'Game',
liveSpeed: 'Speed', liveRpm: 'RPM', liveGear: 'Gear',
systemOverview: 'System Overview', overviewDesc: 'Real-time monitoring',
running: 'Running', stopped: 'Stopped', notStarted: 'Off',
noData: 'No Data', notSelected: 'None',
listening: 'Listening',
searchDashboards: 'Search...', sortByName: 'Name', sortByCategory: 'Category',
sortByAuthor: 'Author', importTsd: 'Import .tsd', newDashboard: '+ New',
noDashboards: 'No dashboards', createFirstDash: 'Create or import your first dashboard',
noCompatibleDash: 'No compatible dashboards for current game',
allCategory: 'All', builtin: 'Built-in', custom: 'Custom',
fullCompatible: 'All', limitedCompatible: 'Lim',
sceneManagement: 'Scene Manager', noGameSelected: 'Select a game first',
newScene: '+ New Scene', importTss: 'Import .tss',
noScenes: 'No scenes', createFirstScene: 'Create your first scene',
render: 'Render', edit: 'Edit', delete: 'Delete', export: 'Export',
sceneName: 'Name', sceneDescription: 'Description (optional)',
sceneCanvasHint: 'Tip: Save first, then edit layout in scene editor.',
save: 'Save', cancel: 'Cancel', editScene: 'Edit Scene',
canvasesCount: 'canvases', backToList: '← Back', saveScene: 'Save Scene',
dashListDrag: 'Dashboards (drag to canvas)', noCompatibleDashForScene: 'No compatible dashboards',
marketPage: 'Marketplace', marketDesc: 'Browse and install community dashboards & scenes',
notLoggedIn: 'Not logged in', loggedIn: 'Logged in',
login: 'Login', register: 'Register', logout: 'Logout',
publish: 'Publish', install: 'Install', installed: 'Installed',
installScene: 'Install Scene', uninstall: 'Uninstall',
pending: 'Pending', approved: 'Approved',
marketLogin: 'Account',
email: 'Email', password: 'Password', username: 'Username',
loginSuccess: 'Login successful', loginFailed: 'Login failed',
registerSuccess: 'Registered! Check email to verify (click to close)', registerFailed: 'Registration failed',
publishTitle: 'Publish', publishDesc: 'Select a dashboard or scene',
chooseFile: '📁 Choose file (.tsd / .tss)',
orChooseLocal: '— or select from local —',
localDashboards: 'Local Dashboards', localScenes: 'Local Scenes',
noPublishable: 'Nothing to publish', publishFailed: 'Publish failed',
publishSuccess: 'Published successfully',
uploadPreview: 'Upload Preview', dropPreview: 'Click or drop image (max 6MB)',
previewTooLarge: 'Preview too large (6MB max)', imageOnly: 'Images only',
updateItem: 'Update', confirmDelete: 'Confirm delete?',
myItems: 'Mine', myDashboards: 'My Dashboards', myScenes: 'My Scenes',
noUploads: 'Nothing uploaded yet',
refresh: '🔄', retry: 'Retry', loadFailed: 'Load failed',
dashboardsTab: 'Dashboards', scenesTab: 'Scenes', myTab: 'Mine',
installedHint: 'Installed! Check dashboard page',
installFailed: 'Install failed',
needLogin: 'Please login first',
confirmUninstall: 'Uninstall "$1"?',
recordPage: 'Record & Playback', recordDesc: 'Record raw UDP to .tsr, replay for debugging',
recordControl: 'Record Control', startRecord: 'Start', stopRecord: 'Stop',
ready: 'Ready', recording: 'Recording...', autoDetect: 'Auto Detect',
playbackActive: 'Playing: ', playbackHint: 'Open a dashboard to see data',
stopPlayback: 'Stop', recordFiles: 'Recordings', importTsr: 'Import .tsr',
noRecordings: 'No recordings', play: 'Play',
debugPage: 'Debug', debugToolbar: '',
parsedFields: 'Parsed Fields', rawJsonStream: 'Raw JSON Stream',
waitingData: 'Waiting...', pause: '⏸ Pause', resume: '▶ Resume',
clear: 'Clear', autoScroll: 'Auto Scroll', viewLog: '📄 Log',
connectionSettings: 'Connection',
telemetryPort: 'Telemetry Port', telemetryPortDesc: 'Global port',
tcpPort: 'TCP Port', tcpPortDesc: 'Default TCP port',
unifiedPort: 'Unified Port', unifiedPortDesc: 'Turn off for per-game ports',
unifiedPortToggle: 'Use per-game ports',
perGamePort: 'Per-Game Ports', savePorts: 'Save Ports',
restartListener: 'Restart Listener', restartListenerDesc: 'After port/game change',
restartBtn: 'Restart',
dataForwarding: 'Data Forwarding', dataForwardingDesc: 'Forward raw UDP to downstream devices',
addTarget: '+ Add', saveEffective: 'Save',
noForwardTargets: 'No targets, add below',
forwardEnabled: 'Enabled',
dashManagement: 'Dashboard Management', rightClickExport: 'Right-click to export',
pluginManagement: 'Plugin Management', importTsp: 'Import .tsp',
reloadPlugins: 'Reload', noPlugins: 'No plugins',
dataBackup: 'Backup', exportBackup: 'Export Backup',
backupDesc: 'Pack dashboards+scenes+config as .tsb', downloadTsb: 'Download .tsb',
restoreBackup: 'Restore Backup', restoreBackupDesc: 'Restore from .tsb',
importTsb: 'Import .tsb',
devTools: 'Dev Tools', testMode: 'UI Test Mode',
testModeDesc: 'Simulate telemetry locally',
testModeOn: 'Test mode on', testModeOff: 'Test mode off',
systemSection: 'System', restartServer: 'Restart TurboSu',
restartServerDesc: 'Reload all code and config', restartBtnSys: 'Restart',
confirmRestart: 'Restart TurboSu?',
restarting: 'Restarting, will refresh in 3s...',
about: 'About', aboutDesc: 'Racing Telemetry Dashboard · Yei.J. (AskaEth)',
loading: 'Loading...', confirm: 'Confirm', success: 'Success', error: 'Error',
clickClose: 'Click to close', saved: 'Saved', updated: 'Updated',
deleted: 'Deleted', portUpdated: 'Port updated, restart to apply',
configSaved: 'Saved', telemetryRestarted: 'Listener restarted',
pluginInstalled: 'Plugin installed', pluginRemoved: 'Plugin removed',
pluginReloaded: 'Plugins reloaded',
connectionsCount: '%d clients', packetsCount: '%d packets',
secondsAgo: '%ds ago', gearLabel: 'Gear %s',
downloadCount: '%d downloads', downloadCountShort: '%d',
canvasesLabel: '%d canvases', forwardsSaved: '%d targets saved',
defaultPortLabel: 'Default: ',
noGamePlugins: 'No game plugins available',
noGame: 'No game', currentGamePrefix: 'Current game: ',
community: 'Community',
noScenesMarket: 'No scenes', noDashboardsMarket: 'No dashboards',
dashboardNamePrompt: 'Dashboard name:',
marketLoginPrompt: 'Please login first',
enterEmailPassword: 'Enter email and password',
selectFileOrPreview: 'Select file or preview',
confirmRemove: 'Remove?',
importSuccess: 'Imported', importFailed: 'Import failed',
createSuccess: 'Created', createFailed: 'Create failed',
uploadSuccess: 'Uploaded', uploadFailed: 'Upload failed',
uploadFailedMsg: 'Upload failed: ',
installSuccess: 'Installed', installFailedMsg: 'Install failed: ',
remove: 'Remove',
uninstallSuccess: 'Uninstalled', uninstallFailed: 'Uninstall failed',
uninstallNotFound: 'Not found locally',
deleteFailed: 'Delete failed', loggedOut: 'Logged out',
sceneSaveSuccess: 'Scene saved', saveFailed: 'Save failed: ',
sceneImportSuccess: 'Scene imported',
playbackStarted: 'Playback started', playbackStartedHint: 'Playback started, open dashboard to view',
restoreSuccess: 'Restored', restoreFailed: 'Restore failed: ',
removeSuccess: 'Removed',
exportTsdHint: 'Downloading .tsd...',
linkCopied: 'Link copied, accessible on LAN',
linkCopiedShort: 'Link copied',
portsSaved: 'Ports saved',
marketEmpty: 'No dashboards on market', marketLoadFailed: 'Market load failed',
installDataIncomplete: 'Incomplete data',
installNetworkError: 'Network error',
restartRequestSent: 'Restart sent, refresh manually',
marketNoItems: 'None',
confirmUpdate: 'Confirm Update',
}
},
init() {
this._current = localStorage.getItem('turbosu-lang') || 'zh';
const btn = document.getElementById('lang-toggle');
if (btn) btn.textContent = this._current === 'zh' ? 'EN' : '中';
this._scan();
},
t(key) {
return (this._strings[this._current] || this._strings.zh)[key] || key;
},
_scan() {
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
const text = this.t(key);
if (el.tagName === 'INPUT' && (el.type === 'text' || el.type === 'search' || el.type === 'email' || el.type === 'password')) {
el.placeholder = text;
} else {
el.textContent = text;
}
});
},
toggle() {
this._current = this._current === 'zh' ? 'en' : 'zh';
localStorage.setItem('turbosu-lang', this._current);
location.reload();
}
};
window.Lang = Lang;