334 lines
11 KiB
JavaScript
334 lines
11 KiB
JavaScript
// Simple i18n utility for NodeCrypt
|
|
// NodeCrypt 简单国际化工具
|
|
|
|
// Language definitions
|
|
// 语言定义
|
|
const LANGUAGES = {
|
|
en: {
|
|
code: 'en',
|
|
name: 'English',
|
|
flag: '🇺🇸',
|
|
translations: {
|
|
// Login and main UI
|
|
'ui.enter_node': 'Enter a Node',
|
|
'ui.username': 'Username',
|
|
'ui.node_name': 'Node Name',
|
|
'ui.node_password': 'Node Password',
|
|
'ui.optional': '(optional)',
|
|
'ui.enter': 'ENTER',
|
|
'ui.connecting': 'Connecting...',
|
|
'ui.node_exists': 'Node already exists',
|
|
'ui.my_name': 'My Name',
|
|
'ui.members': 'Members',
|
|
'ui.message': 'Message',
|
|
'ui.private_message_to': 'Private Message to',
|
|
'ui.me': ' (me)',
|
|
'ui.anonymous': 'Anonymous',
|
|
'ui.start_private_chat': 'Select for private chat',
|
|
|
|
// Settings panel
|
|
'settings.title': 'Settings',
|
|
'settings.notification': 'Notification Settings',
|
|
'settings.theme': 'Theme Settings',
|
|
'settings.language': 'Language Settings',
|
|
'settings.desktop_notifications': 'Desktop Notifications',
|
|
'settings.sound_notifications': 'Sound Notifications',
|
|
'settings.language_switch': 'Language',
|
|
'settings.chinese': 'Chinese',
|
|
'settings.english': 'English',
|
|
|
|
// File upload and transfer
|
|
'file.selected_files': 'Selected Files',
|
|
'file.clear_all': 'Clear All',
|
|
'file.cancel': 'Cancel',
|
|
'file.send_files': 'Send Files', 'file.sending': 'Sending',
|
|
'file.receiving': 'Receiving',
|
|
'file.files': 'files',
|
|
'file.total': 'Total',
|
|
'file.files_selected': '{count} files selected, {size} total',
|
|
'file.upload_files': 'Upload Files',
|
|
'file.attach_file': 'Attach file',
|
|
'file.no_password_required': 'No password required',
|
|
'file.drag_drop': 'Drag and drop files here',
|
|
'file.or': 'or',
|
|
'file.browse_files': 'browse files',
|
|
|
|
// Notifications and messages
|
|
'notification.enabled': 'Notifications enabled',
|
|
'notification.alert_here': 'You will receive alerts here.',
|
|
'notification.not_supported': 'Notifications are not supported by your browser.',
|
|
'notification.allow_browser': 'Please allow notifications in your browser settings.',
|
|
'notification.image': '[image]',
|
|
'notification.private': '(Private)',
|
|
|
|
// Actions and menu
|
|
'action.share': 'Share',
|
|
'action.exit': 'Exit',
|
|
'action.emoji': 'Emoji',
|
|
'action.settings': 'Settings',
|
|
'action.back': 'Back',
|
|
'action.copied': 'Copied to clipboard!',
|
|
'action.share_copied': 'Share link copied!',
|
|
'action.copy_failed': 'Copy failed, text:',
|
|
'action.copy_url_failed': 'Copy failed, url:',
|
|
'action.nothing_to_copy': 'Nothing to copy',
|
|
'action.copy_not_supported': 'Copy not supported in this environment',
|
|
'action.action_failed': 'Action failed. Please try again.',
|
|
'action.cannot_share': 'Cannot share:',
|
|
|
|
// System messages
|
|
'system.security_warning': '⚠️ This link uses an old format. Room data is not encrypted.',
|
|
'system.file_send_failed': 'Failed to send files:',
|
|
'system.joined': 'joined the conversation',
|
|
'system.left': 'left the conversation',
|
|
'system.secured': 'connection secured',
|
|
'system.private_message_failed': 'Cannot send private message to',
|
|
'system.private_file_failed': 'Cannot send private file to',
|
|
'system.user_not_connected': 'User might not be fully connected.',
|
|
}
|
|
},
|
|
zh: {
|
|
code: 'zh',
|
|
name: '中文',
|
|
flag: '🇨🇳',
|
|
translations: {
|
|
// Login and main UI
|
|
'ui.enter_node': '进入新的节点',
|
|
'ui.username': '用户名',
|
|
'ui.node_name': '节点名称',
|
|
'ui.node_password': '节点密码',
|
|
'ui.optional': '(可选)',
|
|
'ui.enter': '确定',
|
|
'ui.connecting': '连接中...',
|
|
'ui.node_exists': '此节点已存在',
|
|
'ui.my_name': '我的名字',
|
|
'ui.members': '在线成员',
|
|
'ui.message': '消息',
|
|
'ui.private_message_to': '私信给',
|
|
'ui.me': '(我)',
|
|
'ui.anonymous': '匿名用户',
|
|
'ui.start_private_chat': '选择用户开始私信',
|
|
|
|
// Settings panel
|
|
'settings.title': '设置',
|
|
'settings.notification': '通知设置',
|
|
'settings.theme': '主题设置',
|
|
'settings.language': '语言设置',
|
|
'settings.desktop_notifications': '桌面通知',
|
|
'settings.sound_notifications': '声音通知',
|
|
'settings.language_switch': '语言',
|
|
'settings.chinese': '中文',
|
|
'settings.english': 'English',
|
|
|
|
// File upload and transfer
|
|
'file.selected_files': '已选择的文件',
|
|
'file.clear_all': '清空所有',
|
|
'file.cancel': '取消',
|
|
'file.send_files': '发送文件', 'file.sending': '发送中',
|
|
'file.receiving': '接收中',
|
|
'file.files': '个文件',
|
|
'file.total': '总计',
|
|
'file.files_selected': '选中 {count} 个文件,总计 {size}',
|
|
'file.upload_files': '上传文件',
|
|
'file.attach_file': '附加文件',
|
|
'file.no_password_required': '无需密码',
|
|
'file.drag_drop': '拖拽文件到此处',
|
|
'file.or': '或',
|
|
'file.browse_files': '浏览文件',
|
|
|
|
// Notifications and messages
|
|
'notification.enabled': '通知已启用',
|
|
'notification.alert_here': '您将在此处收到通知。',
|
|
'notification.not_supported': '您的浏览器不支持通知功能。',
|
|
'notification.allow_browser': '请在浏览器设置中允许通知。',
|
|
'notification.image': '[图片]',
|
|
'notification.private': '(私信)',
|
|
|
|
// Actions and menu
|
|
'action.share': '分享',
|
|
'action.exit': '退出',
|
|
'action.emoji': '表情',
|
|
'action.settings': '设置',
|
|
'action.back': '返回',
|
|
'action.copied': '已复制到剪贴板!',
|
|
'action.share_copied': '分享链接已复制!',
|
|
'action.copy_failed': '复制失败,文本:',
|
|
'action.copy_url_failed': '复制失败,链接:',
|
|
'action.nothing_to_copy': '没有内容可复制',
|
|
'action.copy_not_supported': '此环境不支持复制功能',
|
|
'action.action_failed': '操作失败,请重试。',
|
|
'action.cannot_share': '无法分享:',
|
|
|
|
// System messages
|
|
'system.security_warning': '⚠️ 此链接使用旧格式,房间数据未加密。',
|
|
'system.file_send_failed': '文件发送失败:',
|
|
'system.joined': '加入了对话',
|
|
'system.left': '离开了对话',
|
|
'system.secured': '已建立端到端安全连接',
|
|
'system.private_message_failed': '无法发送私信给',
|
|
'system.private_file_failed': '无法发送私密文件给',
|
|
'system.user_not_connected': '用户可能未完全连接。',
|
|
}
|
|
}
|
|
};
|
|
|
|
// Current language
|
|
// 当前语言
|
|
let currentLanguage = 'en';
|
|
|
|
// Get translation for a key
|
|
// 获取翻译文本
|
|
export function t(key, fallback = key) {
|
|
const lang = LANGUAGES[currentLanguage];
|
|
if (lang && lang.translations && lang.translations[key]) {
|
|
return lang.translations[key];
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
// Set current language
|
|
// 设置当前语言
|
|
export function setLanguage(langCode) {
|
|
if (LANGUAGES[langCode]) {
|
|
currentLanguage = langCode;
|
|
// Update document language attribute
|
|
// 更新文档语言属性
|
|
document.documentElement.lang = langCode;
|
|
|
|
// Update static HTML texts
|
|
// 更新HTML中的静态文本
|
|
updateStaticTexts();
|
|
|
|
// Dispatch language change event for other components to listen
|
|
// 派发语言变更事件供其他组件监听
|
|
window.dispatchEvent(new CustomEvent('languageChange', {
|
|
detail: { language: langCode }
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Get current language
|
|
// 获取当前语言
|
|
export function getCurrentLanguage() {
|
|
return currentLanguage;
|
|
}
|
|
|
|
// Get all available languages
|
|
// 获取所有可用语言
|
|
export function getAvailableLanguages() {
|
|
return Object.keys(LANGUAGES).map(code => ({
|
|
code,
|
|
name: LANGUAGES[code].name,
|
|
flag: LANGUAGES[code].flag
|
|
}));
|
|
}
|
|
|
|
// Initialize i18n with settings
|
|
// 根据设置初始化国际化
|
|
export function initI18n(settings) {
|
|
if (settings && settings.language) {
|
|
setLanguage(settings.language);
|
|
} else {
|
|
// Auto-detect browser language
|
|
// 自动检测浏览器语言
|
|
const browserLang = detectBrowserLanguage();
|
|
setLanguage(browserLang);
|
|
}
|
|
}
|
|
|
|
// Detect browser language and return supported language code
|
|
// 检测浏览器语言并返回支持的语言代码
|
|
function detectBrowserLanguage() {
|
|
const navigatorLang = navigator.language || navigator.userLanguage || 'en';
|
|
|
|
// Extract language code (e.g., 'zh-CN' -> 'zh', 'en-US' -> 'en')
|
|
const langCode = navigatorLang.split('-')[0].toLowerCase();
|
|
|
|
// Check if we support this language
|
|
if (LANGUAGES[langCode]) {
|
|
return langCode;
|
|
}
|
|
|
|
// Default fallback to English
|
|
return 'en';
|
|
}
|
|
|
|
// Update static HTML text elements
|
|
// 更新HTML中的静态文本元素
|
|
export function updateStaticTexts() {
|
|
// 如果DOM还没准备好,等待DOM准备好再更新
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => updateStaticTexts());
|
|
return;
|
|
}
|
|
|
|
// Update login title
|
|
const loginTitle = document.getElementById('login-title');
|
|
if (loginTitle) {
|
|
loginTitle.textContent = t('ui.enter_node', 'Enter a Node');
|
|
}
|
|
// Update login form content with new translations
|
|
const loginFormContainer = document.getElementById('login-form');
|
|
if (loginFormContainer) {
|
|
// Use a custom event to trigger form regeneration instead of dynamic import
|
|
// 使用自定义事件触发表单重新生成,而不是动态导入
|
|
window.dispatchEvent(new CustomEvent('regenerateLoginForm'));
|
|
}
|
|
|
|
// Update sidebar username label
|
|
const sidebarUsername = document.getElementById('sidebar-username');
|
|
if (sidebarUsername) {
|
|
// Use a custom event to update sidebar username instead of dynamic import
|
|
// 使用自定义事件更新侧边栏用户名,而不是动态导入
|
|
window.dispatchEvent(new CustomEvent('updateSidebarUsername'));
|
|
}
|
|
// Update "Enter a Node" text in sidebar
|
|
const joinRoomText = document.getElementById('join-room-text');
|
|
if (joinRoomText) {
|
|
joinRoomText.textContent = t('ui.enter_node', 'Enter a Node');
|
|
}
|
|
|
|
// Update Members title in rightbar
|
|
const membersTitle = document.getElementById('members-title');
|
|
if (membersTitle) {
|
|
membersTitle.textContent = t('ui.members', 'Members');
|
|
}
|
|
|
|
// Update settings title
|
|
const settingsTitle = document.getElementById('settings-title');
|
|
if (settingsTitle) {
|
|
settingsTitle.textContent = t('settings.title', 'Settings');
|
|
}
|
|
|
|
// Update message placeholder
|
|
const messagePlaceholder = document.querySelector('.input-field-placeholder');
|
|
if (messagePlaceholder) {
|
|
messagePlaceholder.textContent = t('ui.message', 'Message');
|
|
}
|
|
|
|
// Update attach button title
|
|
const attachBtn = document.querySelector('.chat-attach-btn');
|
|
if (attachBtn) {
|
|
attachBtn.title = t('file.attach_file', 'Attach file');
|
|
}
|
|
|
|
// Update emoji button title
|
|
const emojiBtn = document.querySelector('.chat-emoji-btn');
|
|
if (emojiBtn) {
|
|
emojiBtn.title = t('action.emoji', 'Emoji');
|
|
}
|
|
// Update settings button title
|
|
const settingsBtn = document.getElementById('settings-btn');
|
|
if (settingsBtn) {
|
|
settingsBtn.title = t('action.settings', 'Settings');
|
|
settingsBtn.setAttribute('aria-label', t('action.settings', 'Settings'));
|
|
}
|
|
|
|
// Update back button title
|
|
const backBtn = document.getElementById('settings-back-btn');
|
|
if (backBtn) {
|
|
backBtn.title = t('action.back', 'Back');
|
|
backBtn.setAttribute('aria-label', t('action.back', 'Back'));
|
|
}
|
|
}
|