diff --git a/ostp-gui/src/i18n.js b/ostp-gui/src/i18n.js new file mode 100644 index 0000000..5a744a7 --- /dev/null +++ b/ostp-gui/src/i18n.js @@ -0,0 +1,134 @@ +// ── OSTP GUI Internationalization ───────────────────────────────────────────── +// Supports: English (en), Russian (ru) + +const translations = { + en: { + // Home screen + status_disconnected: 'Disconnected', + status_connecting: 'Connecting...', + status_connected: 'Connected', + hint_tap: 'Tap to protect your traffic', + hint_connecting: 'Establishing secure tunnel...', + hint_connected: 'Your traffic is encrypted', + download: 'Download', + upload: 'Upload', + // Settings + settings_title: 'Configuration', + import_placeholder: 'Paste ostp:// share link here...', + import_btn: 'Import', + label_server: 'Server Address', + label_key: 'Access Key', + ph_key: 'Enter secure access key', + label_socks: 'Local Proxy Address', + label_dns: 'Custom DNS Server', + label_tun: 'TUN Tunnel Mode', + tun_hint: 'Route all system traffic (Admin req.)', + label_transport: 'Transport Protocol', + label_mtu: 'MTU Size', + label_transport: 'Transport Protocol', + label_sni: 'Stealth SNI (Fake Host)', + label_pbk: 'Reality PublicKey (pbk)', + label_sid: 'Reality ShortId (sid)', + label_mtu: 'MTU Size', + label_mux: 'Multiplexing (Mux)', + mux_hint: 'Run multiple streams over one connection', + label_mux_sessions: 'Mux Sessions', + label_debug: 'Debug Logs', + debug_hint: 'Enable verbose internal event outputs', + excl_title: 'Exclusions', + excl_hint: '(one per line)', + excl_domains: 'Bypass Domains', + excl_ips: 'Bypass IPs / CIDR Ranges', + excl_processes: 'Bypass Processes', + save_btn: 'Save & Apply', + toast_saved: 'Configuration saved', + toast_imported: 'Configuration imported', + toast_error: 'Error', + err_server_req: 'Server address is required', + err_key_req: 'Access key is required', + }, + ru: { + // Главный экран + status_disconnected: 'Отключено', + status_connecting: 'Подключение...', + status_connected: 'Подключено', + hint_tap: 'Нажмите для защиты трафика', + hint_connecting: 'Установка защищённого туннеля...', + hint_connected: 'Ваш трафик зашифрован', + download: 'Входящий', + upload: 'Исходящий', + // Настройки + settings_title: 'Настройки', + import_placeholder: 'Вставьте ostp:// ссылку...', + import_btn: 'Импорт', + label_server: 'Адрес сервера', + label_key: 'Ключ доступа', + ph_key: 'Введите ключ доступа', + label_socks: 'Адрес локального прокси', + label_dns: 'DNS сервер', + label_tun: 'Режим TUN-туннеля', + tun_hint: 'Направить весь трафик (нужны права администратора)', + label_transport: 'Транспортный протокол', + label_mtu: 'Размер MTU', + label_transport: 'Транспортный протокол', + label_sni: 'Маскировочный SNI', + label_pbk: 'Reality PublicKey (pbk)', + label_sid: 'Reality ShortId (sid)', + label_mtu: 'Размер MTU', + label_mux: 'Мультиплексирование (Mux)', + mux_hint: 'Несколько потоков через одно соединение', + label_mux_sessions: 'Сессий Mux', + label_debug: 'Журнал отладки', + debug_hint: 'Включить подробный вывод событий', + excl_title: 'Исключения', + excl_hint: '(по одному на строку)', + excl_domains: 'Обход для доменов', + excl_ips: 'Обход для IP / CIDR', + excl_processes: 'Обход для процессов', + save_btn: 'Сохранить', + toast_saved: 'Настройки сохранены', + toast_imported: 'Настройки импортированы', + toast_error: 'Ошибка', + err_server_req: 'Укажите адрес сервера', + err_key_req: 'Укажите ключ доступа', + }, +}; + +let currentLang = localStorage.getItem('ostp_lang') || 'en'; + +export function t(key) { + const dict = translations[currentLang] || translations.en; + return dict[key] || translations.en[key] || key; +} + +export function getLang() { + return currentLang; +} + +export function setLang(lang) { + if (!translations[lang]) return; + currentLang = lang; + localStorage.setItem('ostp_lang', lang); + applyTranslations(); +} + +export function toggleLang() { + const next = currentLang === 'en' ? 'ru' : 'en'; + setLang(next); + return next; +} + +export function applyTranslations() { + document.querySelectorAll('[data-i18n]').forEach(el => { + const key = el.getAttribute('data-i18n'); + const value = t(key); + if (value) el.textContent = value; + }); + document.querySelectorAll('[data-i18n-placeholder]').forEach(el => { + const key = el.getAttribute('data-i18n-placeholder'); + const value = t(key); + if (value) el.placeholder = value; + }); + const langLabel = document.getElementById('lang-label'); + if (langLabel) langLabel.textContent = currentLang.toUpperCase(); +} diff --git a/ostp-gui/src/index.html b/ostp-gui/src/index.html index 3493ba3..cc6a63f 100644 --- a/ostp-gui/src/index.html +++ b/ostp-gui/src/index.html @@ -210,6 +210,11 @@ +
+ + +
+
@@ -224,6 +229,19 @@
+
+
+ Multiplexing (Mux) + Run multiple streams over one connection +
+ +
+
Debug Logs diff --git a/ostp-gui/src/main.js b/ostp-gui/src/main.js index 2adaaf9..c26ad34 100644 --- a/ostp-gui/src/main.js +++ b/ostp-gui/src/main.js @@ -48,6 +48,8 @@ const inPbk = $('in-pbk'); const inSid = $('in-sid'); const inMtu = $('in-mtu'); const inTun = $('in-tun-mode'); +const inMux = $('in-mux-mode'); +const inMuxSessions = $('in-mux-sessions'); const inDebug = $('in-debug'); const inDomains = $('in-ex-domains'); const inIps = $('in-ex-ips'); @@ -233,6 +235,8 @@ async function loadConfigIntoForm() { inSid.value = c.reality?.sid || ''; inMtu.value = c.mtu || ''; inTun.checked = !!c.tun?.enable; + inMux.checked = !!c.mux?.enabled; + inMuxSessions.value = c.mux?.sessions || ''; inDns.value = c.tun?.dns || ''; inDebug.checked = !!c.debug; @@ -283,6 +287,13 @@ async function handleSave() { if (mtuStr) rawConfig.mtu = parseInt(mtuStr, 10); else delete rawConfig.mtu; + if (inMux.checked) { + const s = parseInt(inMuxSessions.value.trim(), 10); + rawConfig.mux = { enabled: true, sessions: isNaN(s) ? 1 : s }; + } else { + delete rawConfig.mux; + } + if (!rawConfig.tun) { rawConfig.tun = { wintun_path: './wintun.dll', ipv4_address: '10.1.0.2/24' }; }