// Telegram bot i18n — string tables for en/ru // Usage: let i18n = require("service.i18n"); let t = i18n.bind(lang); let strings = { en: { // ─── Setting schema labels ─── dns_type: "DNS Type", dns_server: "DNS Servers", dns_upstream_mode: "DNS Upstream Mode", dns_fallback_server: "Fallback DNS Servers", bootstrap_dns_server: "Bootstrap DNS", dns_strategy: "DNS Strategy", dns_detour_enabled: "DNS Detour", routed_dns_enabled: "Routed DNS", routed_dns_type: "Routed DNS Protocol", routed_dns_server: "Routed DNS Servers", source_network_interfaces: "Inbound Interfaces", enable_output_network_interface: "Bind to WAN", enable_badwan_interface_monitoring: "WAN Monitoring", enable_yacd: "YACD Panel", disable_quic: "Block QUIC", block_doh: "Block DoH", list_update_enabled: "List Updates", component_update_check_enabled: "Core Updates", download_lists_via_proxy: "Lists via Proxy", download_components_via_proxy: "Core via Proxy", dont_touch_dhcp: "Don't Touch DHCP", isolate_p2p: "Isolate P2P", log_level: "Log Level", exclude_ntp: "Exclude NTP", route_router_traffic: "Route Router Traffic", route_router_traffic_section: "Router Traffic Section", shutdown_correctly: "Clean Shutdown", smart_detect: "Smart Detect", smart_detect_sections: "Smart Detect Sections", enabled: "Bot Enabled", bot_token: "Token", admin_ids: "Admin IDs", poll_interval: "Poll Interval", notify_crash: "Core Crashes", notify_restart: "Restarts", notify_server_switch: "Server Switches", notify_subscription: "Subscription Status", notify_cert: "Certificates", notify_dns_leak: "DNS Leaks", daily_report_enabled: "Daily Report", daily_report_hour: "Report Time (hour)", quiet_hours_enabled: "Quiet Hours", quiet_hours_start: "Quiet Hours Start", quiet_hours_end: "Quiet Hours End", fallback_socks: "Fallback SOCKS5", language: "Language", section: "Section", auto_user_agent: "Auto User-Agent", user_agent: "User-Agent", auto_hwid: "Auto HWID", subscription_update_enabled: "Auto Update", subscription_update_interval: "Update Interval", download_via_proxy_enabled: "Via Proxy", show_dashboard_metadata: "Subscription Metadata", prefix_nodes: "Node Prefix", node_prefix: "Prefix String", include_urltest_groups: "URL-Test Groups", hide_urltest_group_outbounds: "Hide Group Nodes", hide_detour_outbounds: "Hide Detour Nodes", label: "Name", protocol: "Protocol", routing_mode: "Routing Mode", // ─── Navigation ─── nav_next: "Next ▶️", nav_prev: "◀️ Prev", nav_back: "◀️ Back", nav_menu: "🏠 Menu", nav_settings: "⚙️ Settings", // ─── Menu ─── menu_all_settings: "⚙️ All Settings", menu_choose_cat: "Select category to edit:", menu_global: "🌍 Global Settings", menu_telegram: "🤖 Telegram Settings", menu_subscriptions: "📋 Subscriptions", menu_servers: "🖥️ Custom Servers", menu_servers_short: "🖥 Custom Servers", menu_dns_presets: "🌐 DNS Servers (Presets)", menu_quiet_hours: "🔕 Quiet Hours", menu_test_rule: "🔍 Test Rule", menu_export_config: "📤 Export Config", menu_help: "📖 Help", menu_status: "📊 System Status", menu_diagnostic: "🩺 Diagnostics", menu_logs: "📝 Logs", menu_connections: "🔌 Connections", menu_outbounds: "🚀 Proxy Servers", menu_sections: "📁 Routing Sections", menu_speed: "⚡ Speed Test", menu_ping: "📡 Ping", menu_restart: "🔄 Restart Tachyon", menu_language: "🌐 Language", menu_extras: "🔧 Extras", menu_info: "ℹ️ System Info", menu_close_conn: "🗑️ Close All Connections", menu_check_updates: "🔄 Check Updates", menu_doctor: "🩺 Doctor", menu_test_rule: "🔍 Test Rule", menu_devices: "💻 Network Devices", menu_instances: "Live Servers", menu_categories: "🔙 Categories", menu_qos: "🎮 Gaming & Voice QoS", menu_heal: "🤖 AI Self-Healing", choose_section: "Select section to manage:", // ─── Status ─── status_version: "Version", status_uptime: "Uptime", status_cpu: "Router CPU", status_active_servers: "Active servers:", status_no_server: "No server selected", status_enabled: "Enabled ✅", status_disabled: "Disabled ❌", status_target: "Target", status_running: "🟢 Running", status_stopped: "🔴 Stopped", status_unknown: "Unknown", status_normal: "All systems are operating normally! 🟢", status_pause: "Paused", status_header: "System Status", status_sections: "Sections", // ─── Stats ─── stat_download: "Downloaded", stat_upload: "Uploaded", stat_memory: "Memory", stat_connections: "Connections", // ─── Section management ─── section_type: "Type", section_status: "Status", section_choose_group: "Select a group to configure the server:", section_group: "Group", section_server: "Server", section_check_singbox: "Check if sing-box is running and Clash API is accessible.", section_domains: "Domains", section_sources: "Sources", section_section: "Section", section_edit: "Editing", section_create: "➕ Create section", section_action: "Action", section_list: "List", section_empty: "Empty", section_showing_partial: "(Showing not all items for deletion)", btn_rename: "✏️ Rename", btn_delete: "❌ Delete", btn_add: "➕ Add", btn_clear_all: "➖ Clear all", btn_add_items: "➕ Add items", btn_clear_list: "➖ Clear list", btn_back_to_section: "🔙 Back to section", // ─── Diagnostics ─── diag_running: "Diagnostics already running by another process.", diag_issues: "Issues: %s", diag_issues_count: "Issues: %s", diag_fixed_count: ", fixed: %s", diag_planned_count: ", planned: %d", diag_clean: "No issues detected.", diag_no_output: "No diagnostic output.", diag_error: "Failed to start diagnostics.", diag_empty: "Empty diagnostics report.", diag_all_fixed: "All systems restored to normal! 🟢", diag_starting: "Running diagnostics...", diag_results: "Tachyon Doctor Results:", report_truncated: "report truncated", // ─── Logs ─── logs_no_records: "No records for this level.\n", logs_read_error: "Failed to read logs.\n", // ─── Connections ─── conn_none: "No active connections.", // ─── Subscriptions ─── sub_none: "No subscriptions. Add a subscription URL to download rules.", sub_found: "Found subscriptions: ", // ─── Latency ─── latency_no_data: "No latency data. Press «Refresh».", latency_none: "No latency data available.", // ─── Servers ─── servers_none: "No servers found or sing-box is not running.", servers_api_error: "API parsing error: ", // ─── QoS ─── qos_status: "Status", qos_enabled: "🟢 Enabled", qos_disabled: "❌ Disabled", qos_start: "Start", qos_end: "End", qos_quiet_hint: "During quiet hours, the bot only sends critical notifications.", // ─── Errors ─── err_invalid_format: "Invalid command format", err_not_allowed: "Command '%s' is not in the allowed list", err_args_mismatch: "Arguments don't match the allowed template", err_wrong_arg_count: "Wrong number of arguments", err_arg_not_allowed: "Argument not allowed: %s", err_subcmd_not_allowed: "Subcommand '%s' is not in the whitelist", err_host_unknown: "Unknown", err_api_parse: "Failed to read info: ", err_status_fetch: "Failed to get status.", err_no_data: "No data.", err_log_read: "Failed to read logs.", err_no_output: "No output", // ─── Notifications ─── notif_restart_services: "Tachyon services restarted", notif_nft_cleared: "nftables rules table cleared or corrupted", notif_nft_regenerated: "TachyonTable and TPROXY chain rules regenerated", notif_qos_not_found: "Gaming & Voice QoS Accelerator rules not found in nftables", notif_qos_applied: "High-priority DSCP labels applied: EF (0x2e) for Voice/RTC, AF41 (0x22) for Gaming", notif_restart_skipped: "Restart skipped: rate limit or active block", notif_singbox_hung: "Proxy port hang or incomplete response sing-box", notif_singbox_restarted: "sing-box restarted", notif_cache_cleared: "cache.db cleared; restart skipped by rate limit", notif_sets_empty: "Empty nftables sets (community) — data not loaded on reload", notif_sets_restored: "nftables sets restored from persistent cache", notif_fw_reload: "reload_firewall executed to restore TPROXY rules", notif_config_invalid: "Restart cancelled: sing-box config fails validation", notif_restart_rate: "Restart skipped by rate limit", notif_rpcd_restarted: "rpcd restarted to free file descriptors", // ─── Route failure ─── route_proxy_alive: "proxy port is listening, but requests through it are failing", route_proxy_down: "proxy port not listening (sing-box mixed-inbound not up)", route_hint: "Reason: %s.\nDirect API access enabled as fallback — commands may respond with delay.\nRun /test to check connectivity.", route_alert_title: "Tachyon Telegram bot", route_alert_consecutive: "consecutive API poll failures", route_reason: "Reason:", route_fallback_hint: "Direct API access enabled as fallback — commands may respond with delay.", route_heal_hint: "Watchdog will try to restore proxy automatically.", // ─── Command descriptions (for setMyCommands) ─── cmd_menu: "Main menu", cmd_status: "System status", cmd_runtime: "Traffic stats", cmd_outbounds: "Proxy servers", cmd_sections: "Routing sections", cmd_speed: "Speed test", cmd_ping: "Server latency", cmd_test: "Quick diagnostics", cmd_logs: "View logs", cmd_info: "System info", cmd_connections: "Active connections", cmd_test_rule: "Test rule", cmd_help: "Help", cmd_check_updates: "Check for updates", cmd_close_connections: "Close all connections", cmd_doctor: "Diagnostics", cmd_restart: "Restart Tachyon services", cmd_instances: "Live servers", cmd_lang: "Change language", cmd_guest: "Guest mode", menu_guest: "👥 Guest Mode", guest_title: "Guest Mode", guest_enabled: "Guest mode enabled", guest_disabled: "Guest mode disabled", guest_status_active: "🟢 Active", guest_status_inactive: "⚪ Inactive", guest_mode_selected: "Selected devices", guest_mode_inverted: "All except trusted", guest_devices_count: "Guest devices: %d", guest_trusted_count: "Trusted devices: %d", guest_time_limit: "Daily time limit: %d min", guest_traffic_limit: "Daily traffic limit: %s", guest_lan_isolated: "LAN isolation: Active (RFC1918 & router management blocked)", guest_time_spent: "Time spent today: %d min", guest_traffic_spent: "Traffic used today: %s", guest_limit_reached_time: "⏳ Guest device %s reached daily time limit (%d min). Access blocked until midnight.", guest_limit_reached_traffic: "📊 Guest device %s reached daily traffic limit (%s). Access blocked until midnight.", btn_guest_toggle: "👥 Guest Mode", // ─── Misc ─── exec_done: "Done (code %s):\n
%s", hour_enter: "⏰ Enter the hour for %s (0-23):", hour_start: "start", hour_end: "end", blocked_activity: "Blocked Activity", blocked_attempts: "Attempts to access blocked sites", daily_digest_title: "🌅 Morning Tachyon Digest", daily_rx: "Current RX", daily_tx: "Current TX", daily_connections: "Connections", daily_memory: "Memory", choose_language: "Select language:", lang_current: "Current language: %s", lang_saved: "Language saved!", ping_enter_host: "Enter a domain or IP to check:", test_rule_prompt: "Enter the rule to test:", rule_label: "Rule", // ─── Speed test ─── speed_starting: "⚡ Starting Speedtest...\n\nTesting via Cloudflare. Takes up to 30 seconds.", speed_result: "⚡ Speedtest Result", speed_direct: "📥 Direct:", speed_proxy: "📤 Via proxy:", speed_fail_measure: "❌ failed to measure", speed_error: "❌ Failed to run test. Check if sing-box is running.", speed_check_hint: "Check internet access and sing-box.", // ─── Ping ─── ping_api_error: "❌ Failed to get data from Clash API.", // ─── Quick test ─── test_title: "Quick Diagnostics", test_direct: "Direct connection", test_proxy: "Proxy connection", test_service: "Tachyon service", test_nft: "nftables rules", test_pause: "Pause:", test_rule: "Rule check:", test_match: "Match", test_no_match: "No matches found. Traffic goes through default route.", // ─── Connections ─── conn_total_down: "Total downloaded", conn_total_up: "Total uploaded", conn_closed: "All active connections closed.", // ─── Devices ─── device_block: "🚫 Block device", device_unblock: "🔓 Unblock device", device_blocked: "blocked", device_unblocked: "unblocked", // ─── AI Doctor ─── ai_doctor_title: "Tachyon AI Doctor", ai_status_collecting: "Collecting full AI Watchdog status...", ai_incident: "⚠️ Incident resolved:", ai_resolution: "🔧 Auto-fix:", ai_all_ok: "All network services, DNS, nftables and memory are working perfectly.", ai_diag_ok: "Diagnostics completed successfully.", ai_diag_done: "Diagnostics completed.", // ─── Buttons ─── btn_refresh: "🔄 Refresh", btn_retry: "🔄 Retry", btn_again: "🔄 Again", btn_speed_again: "⚡ Again", btn_back_to_groups: "🔙 Groups", btn_back_to_sections: "🔙 Sections", btn_close_all: "❌ Close All", btn_test: "🩺 Test", nav_cancel: "⬅️ Cancel", restart_confirm: "⚠️ Restart Tachyon services?\nCurrent connections will be interrupted during restart.", restart_yes: "✅ Yes, restart", restart_in_progress: "Restarting Tachyon services...", restart_success: "Restart completed successfully!", restart_error: "Restart failed.", conn_closed: "All active connections closed.", // ─── Outbounds ─── servers_not_found: "Servers not found or sing-box not running.", outbounds_hint: "Press the button to switch servers.", // ─── Logs ─── logs_level: "level", // ─── Info ─── info_device: "Device", err_info_read: "Failed to read info:", // ─── Updates ─── update_new_build: "New release build available!", update_available: "Component update available!", build_label: "Build", // ─── Healer ─── heal_running: "AI Doctor running diagnostics and self-healing...", // ─── Batch i18n additions ─── msg_truncated: "... (message truncated)", menu_version: "Version", menu_cpu: "Router CPU", list_header: "⚙️ List:", outbound_groups_not_found: "Proxy groups not found.", outbound_switch_failed: "Failed to switch server", outbound_group: "Group", outbound_server: "Server", outbound_check_hint: "Check if sing-box is running and Clash API is accessible.", section_ips: "IP addresses", section_list_header: "⚙️ Section:", backup_archive_empty: "Archive is empty or corrupted.", backup_archive_unsafe: "Unsafe archive:", backup_restoring: "Checking and restoring backup...", backup_extract_failed: "Failed to extract archive.", backup_not_valid_uci: "config/tachyon in archive is not a valid UCI.", backup_no_config: "Archive contains neither config/tachyon nor tachyon/ directory.", backup_save_config_failed: "Failed to save current config.", backup_save_data_failed: "Failed to save current /etc/tachyon data.", backup_write_config_error: "Write error /etc/config/tachyon.", backup_uci_validation_failed: "Restored config failed UCI validation. Rolled back.", backup_write_data_error: "Write error /etc/tachyon. Rolled back.", backup_restored: "Backup restored. Restarting Tachyon...", backup_done: "Done!", backup_collecting: "Collecting backup...", backup_create_error: "Backup creation error.", support_bundle_collecting: "Generating Support Bundle...", support_bundle_error: "Bundle generation error.", conn_closed_msg: "All active connections closed.\nThey will be re-established along new routes.", conn_close_error: "Connection reset error.\nsing-box Clash API is unavailable.", updates_title: "Component Updates", updates_disabled: "Update check is disabled in settings.", updates_cache_empty: "Cache is empty. Check later.", update_check_error: "Check error", update_new_release_build: "new release build", update_dev_build: "dev build", update_all_current: "All components up to date.", update_cache_parse_error: "Cache parse error:", update_cache_unavailable: "Check cache empty or unavailable.", btn_refresh_cache: "Refresh cache", api_parse_error: "API parsing error:", api_connect_failed: "Failed to connect to sing-box API.", logs_header: "Tachyon Logs", info_title: "System Information", info_device_label: "Device", info_fetch_failed: "Failed to get system information.", sub_empty_url: "(empty)", btn_sub_delete: " 🗑 Delete", btn_add_subscription: "Add subscription", sub_section_subs: "Subscriptions", sub_found_count: "Subscriptions found:", sub_enter_url: "Enter subscription URL:\n\nSend /cancel to cancel", sub_url_empty: "URL cannot be empty.", sub_added: "Subscription added to section", sub_not_found: "Subscription not found.", sub_removed: "Subscription removed from section", help_title: "Command Help", help_main_section: "Main:", help_diag_section: "Diagnostics:", help_manage_section: "Management:", help_settings_section: "Settings:", help_this: "This help", quiet_hours_title: "Quiet Hours", quiet_status: "Status", quiet_enabled_label: "Enabled", quiet_disabled_label: "Disabled", quiet_start_label: "Start:", quiet_end_label: "End:", quiet_hint: "During quiet hours, the bot only sends critical notifications.", btn_enable: "Enable", btn_disable: "Disable", quiet_hour_start: "start", quiet_hour_end: "end", quiet_enter_hour: "⏰ Enter the hour for %s quiet hours (0–23):\n\nSend /cancel to cancel", test_rule_header: "Rule check:", test_match: "Match:", test_action: "Action:", export_title: "Exporting config...", export_done: "Config exported.\n\n⚠️ File contains sensitive data. Do not publish it.", export_failed: "Failed to export config.", mac_invalid: "Invalid MAC address.", mac_firewall_open_failed: "Failed to open firewall configuration.", mac_block_rule_failed: "Failed to create block rule.", mac_blocked: "blocked.", mac_unblocked: "unblocked.", mac_firewall_bg: "Firewall rules are applying in background (a few seconds).", devices_title: "Network Devices:", device_hostname_unknown: "Unknown", devices_not_found: "No devices found.", watchdog_stop_btn: "Stop Watchdog", ai_full_status_btn: "Full AI Status", watchdog_start_btn: "Start Watchdog", ai_incident: "⚠️ Incident resolved:", ai_resolution: "🔧 Auto-fix:", ai_all_ok: "All network services, DNS, nftables and memory are working perfectly.", ai_diag_ok: "Diagnostics completed successfully.", ai_diag_done: "Diagnostics completed.", ai_status_collecting: "Collecting full AI Watchdog status...", ai_status_error: "Status fetch error.", ai_data_truncated: "data truncated", ai_full_status_header: "AI Watchdog — Full Status:", qos_title: "Gaming & Voice QoS Accelerator", qos_enabled_detail: "Enabled (DSCP EF + AF41 Priority)", qos_disabled_detail: "Disabled", qos_priority_rules: "Priority rules:", btn_qos_disable: "Disable QoS", btn_qos_enable: "Enable QoS", cb_obsolete: "Button obsolete — open the list again.", update_component_title: "Starting update", update_bg_hint: "Process started in background. Check later.", err_unknown_component: "Unknown component:", admin_invalid_chat_id: "Invalid Chat ID.", admin_user_added: "User added to admin list.", admin_user_already: "User is already an admin.", sec_enter_name: "Enter a Latin name for the new section (e.g.
my_vpn):\n\nSend /cancel to cancel",
sec_enter_label: "Enter a new human-readable name (Label) for section",
sec_enter_target: "Enter outbound interface (target) for section",
sec_not_found: "Section not found.",
sec_deleted: "Section deleted.",
sec_add_items_prompt: "Send items to add (one per line or space-separated):\n\nSend /cancel to cancel",
sec_sub_add_prompt: "Enter section name and subscription URL separated by a space:\n/sec_sub_add Main https://...\n\nSend /cancel to cancel",
sec_enter_value: "Enter new value for",
sec_add_list_prompt: "Send items to add to the list (space or newline separated):\n\nSend /cancel to cancel",
dns_all_protocols: "All protocols",
dns_servers_applied: "DNS servers set:",
dns_restart_hint: "Restart to apply.",
dns_bootstrap_applied: "Bootstrap DNS servers set:",
exec_cmd_error: "Command execution error:",
exec_access_denied: "Access denied. Your Chat ID:",
backup_downloading: "Downloading backup...",
backup_download_error: "File download error.",
backup_url_error: "File URL error.",
backup_format_hint: "Please send the backup in .tar.gz format.",
admin_forward_prompt: "Forwarded a message from user",
btn_admin_add: "Add",
exec_whitelist_msg: "Executing from whitelist:",
exec_no_output: "No output",
action_cancelled: "Action cancelled.",
err_text_expected: "A text message is expected. Try again or /cancel.",
value_saved: "Value saved.",
items_added: "items added.",
section_created: "Section created!",
err_invalid_section_name: "Invalid name. Only letters, numbers, and underscores allowed.",
label_changed: "Name changed.",
target_changed: "Target changed.",
items_added_count: "Added",
nothing_added: "Nothing added.",
err_format_section_url: "Format: section URL\nExample: Main https://...",
err_invalid_hour: "Enter an integer from 0 to 23.",
saved: "Saved.",
daily_unknown: "unknown",
daily_uptime: "OS Uptime:",
daily_rx: "Current RX:",
daily_tx: "Current TX:",
build_label: "Build:",
blocked_activity: "Blocked Activity",
blocked_rule_label: "Rule:",
blocked_rule: "Rule:",
blocked_attempts: "Attempts to access blocked sites",
info_read_error: "Error reading info:",
quiet_start_word: "start",
quiet_end_word: "end",
quiet_saved: "Settings saved.",
sec_created: "Section created!",
sec_invalid_name: "Invalid name. Only Latin letters, digits, underscores.",
sec_invalid_hour: "Invalid hour.",
sec_changed_to: "changed to",
exec_value_saved: "Value saved. Restart needed for changes.",
exec_items_added: "Added",
exec_items_added_count: "Added",
exec_nothing_added: "Nothing added.",
exec_changed_to: "changed to",
exec_format_hint: "Format: domain.com or [ip]/prefix.",
update_new_release_header: "New build of current release!",
update_build_label: "Build",
critical_keyword_fallen: "Down",
critical_keyword_error: "Error",
btn_update_component: "Update",
fptn_token_title: "FPTN Access Token",
fptn_token_prompt: "Send the new FPTN access token (copy/forward from @fptn_bot):",
fptn_token_invalid: "Invalid or empty FPTN token. Please provide a valid token.",
fptn_token_no_section: "Token saved, but no section with action 'fptn' was found.",
fptn_restarting: "Updating token and connecting FPTN...",
fptn_token_updated_title: "FPTN Token Updated and Connected!",
fptn_token_saved_connecting: "Token saved. Connecting to FPTN...",
fptn_token_retry_hint: "Connecting in background; routing will be applied automatically.",
fptn_token_change_btn: "FPTN Token",
fptn_token_btn: "FPTN Token",
},
ru: {
// ─── Setting schema labels ───
dns_type: "Тип DNS",
dns_server: "DNS Серверы",
dns_upstream_mode: "Режим DNS Upstream",
dns_fallback_server: "Резервные DNS Серверы",
bootstrap_dns_server: "Bootstrap DNS",
dns_strategy: "Стратегия DNS",
dns_detour_enabled: "DNS Detour",
routed_dns_enabled: "Маршрутизированный DNS",
routed_dns_type: "Протокол маршрутизированного DNS",
routed_dns_server: "Серверы маршрутизированного DNS",
source_network_interfaces: "Входящие интерфейсы",
enable_output_network_interface: "Привязка к WAN",
enable_badwan_interface_monitoring: "Мониторинг WAN",
enable_yacd: "Панель YACD",
disable_quic: "Блокировать QUIC",
block_doh: "Блокировать DoH",
list_update_enabled: "Обновление списков",
component_update_check_enabled: "Обновление ядра",
download_lists_via_proxy: "Списки через прокси",
download_components_via_proxy: "Ядро через прокси",
dont_touch_dhcp: "Не трогать DHCP",
isolate_p2p: "Изолировать P2P",
log_level: "Уровень логов",
exclude_ntp: "Исключить NTP",
route_router_traffic: "Маршрутизировать трафик роутера",
route_router_traffic_section: "Секция трафика роутера",
shutdown_correctly: "Корректное завершение",
smart_detect: "Smart Detect",
smart_detect_sections: "Секции Smart Detect",
enabled: "Бот Включен",
bot_token: "Токен",
admin_ids: "Admin IDs",
poll_interval: "Интервал опроса",
notify_crash: "Сбои ядра",
notify_restart: "Перезапуски",
notify_server_switch: "Переключение серверов",
notify_subscription: "Статус подписок",
notify_cert: "Сертификаты",
notify_dns_leak: "Утечки DNS",
daily_report_enabled: "Ежедневный отчет",
daily_report_hour: "Время отчета (час)",
quiet_hours_enabled: "Тихие часы",
quiet_hours_start: "Начало тихих часов",
quiet_hours_end: "Конец тихих часов",
fallback_socks: "Резервный SOCKS5",
language: "Язык",
section: "Секция",
auto_user_agent: "Auto User-Agent",
user_agent: "User-Agent",
auto_hwid: "Auto HWID",
subscription_update_enabled: "Автообновление",
subscription_update_interval: "Интервал обновления",
download_via_proxy_enabled: "Через прокси",
show_dashboard_metadata: "Метаданные подписки",
prefix_nodes: "Префикс узлов",
node_prefix: "Строка префикса",
include_urltest_groups: "Группы URL-Test",
hide_urltest_group_outbounds: "Скрыть узлы групп",
hide_detour_outbounds: "Скрыть Detour узлы",
label: "Название",
enabled: "Включен",
protocol: "Протокол",
routing_mode: "Режим",
// ─── Navigation ───
nav_next: "След ▶️",
nav_prev: "◀️ Пред",
nav_back: "◀️ Назад",
nav_menu: "🏠 Меню",
nav_settings: "⚙️ Настройки",
// ─── Menu ───
menu_all_settings: "⚙️ Все Настройки",
menu_choose_cat: "Выберите категорию для редактирования:",
menu_global: "🌍 Глобальные настройки",
menu_telegram: "🤖 Настройки Telegram",
menu_subscriptions: "📋 Подписки",
menu_servers: "🖥️ Пользовательские серверы",
menu_servers_short: "🖥 Кастомные серверы",
menu_dns_presets: "🌐 DNS Серверы (Пресеты)",
menu_quiet_hours: "🔕 Тихие часы",
menu_test_rule: "🔍 Проверка правила",
menu_export_config: "📤 Экспорт конфига",
menu_help: "📖 Справка",
menu_status: "📊 Статус системы",
menu_diagnostic: "🩺 Диагностика",
menu_logs: "📝 Логи",
menu_connections: "🔌 Подключения",
menu_outbounds: "🚀 Прокси серверы",
menu_sections: "📁 Секции маршрутизации",
menu_speed: "⚡ Тест скорости",
menu_ping: "📡 Задержка",
menu_restart: "🔄 Перезапуск Tachyon",
menu_language: "🌐 Язык",
menu_extras: "🔧 Дополнительно",
menu_info: "ℹ️ Информация о системе",
menu_close_conn: "🗑️ Закрыть все соединения",
menu_check_updates: "🔄 Проверить обновления",
menu_doctor: "🩺 Диагностика",
menu_test_rule: "🔍 Проверка правила",
menu_devices: "💻 Устройства в сети",
menu_instances: "Live серверы",
menu_categories: "🔙 Категории",
menu_qos: "🎮 Игровой QoS",
menu_heal: "🤖 ИИ-Самолечение",
choose_section: "Выберите раздел для управления:",
// ─── Status ───
status_version: "Версия",
status_uptime: "Аптайм",
status_cpu: "Роутер CPU",
status_active_servers: "Активные серверы:",
status_no_server: "Активный сервер: Не выбран",
status_enabled: "Включена ✅",
status_disabled: "Выключена ❌",
status_target: "Цель (Target)",
status_running: "🟢 Запущен",
status_stopped: "🔴 Остановлен",
status_unknown: "Неизвестно",
status_normal: "Все системы приведены в штатную норму! 🟢",
status_pause: "Пауза",
status_header: "Статус Системы",
status_sections: "Секции",
// ─── Stats ───
stat_download: "Скачано",
stat_upload: "Отдано",
stat_memory: "Память",
stat_connections: "Соединений",
// ─── Section management ───
section_type: "Тип",
section_status: "Статус",
section_choose_group: "Выберите группу для настройки сервера:\n\n",
section_group: "Группа",
section_server: "Сервер",
section_check_singbox: "Проверьте, запущен ли sing-box и доступен ли Clash API.",
section_domains: "Домены",
section_sources: "Источники (Source)",
section_section: "Секция",
section_edit: "Редактирование",
section_create: "➕ Создать секцию",
section_action: "Действие",
section_list: "Список",
section_empty: "Пусто",
section_showing_partial: "(Показаны не все элементы для удаления)",
btn_rename: "✏️ Имя",
btn_delete: "❌ Удалить",
btn_add: "➕ Добавить",
btn_clear_all: "➖ Очистить все",
btn_add_items: "➕ Добавить элементы",
btn_clear_list: "➖ Очистить список",
btn_back_to_section: "🔙 Назад к секции",
// ─── Diagnostics ───
diag_running: "Диагностика уже выполняется другим процессом.",
diag_issues: "Проблем: %s",
diag_issues_count: "Проблем: %s",
diag_fixed_count: ", исправлено: %s",
diag_planned_count: ", планируется к исправлению: %d\n(применение: tachyon doctor --fix или кнопка в LuCI)",
diag_clean: "Проблем не обнаружено.",
diag_no_output: "Нет вывода диагностики.",
diag_error: "Ошибка запуска диагностики.",
diag_empty: "Пустой отчёт диагностики.",
diag_all_fixed: "Все системы приведены в штатную норму! 🟢",
diag_starting: "Запуск диагностики...",
diag_results: "Результаты Tachyon Doctor:",
report_truncated: "отчёт сокращён",
// ─── Logs ───
logs_no_records: "Нет записей для данного уровня.\n",
logs_read_error: "Не удалось прочитать логи.\n",
// ─── Connections ───
conn_none: "Нет активных подключений.",
// ─── Subscriptions ───
sub_none: "Нет подписок. Добавьте URL подписки для скачивания правил.",
sub_found: "Найдено подписок: ",
// ─── Latency ───
latency_no_data: "Нет данных о задержках. Нажмите «Обновить».",
latency_none: "Нет данных о задержках.",
// ─── Servers ───
servers_none: "Серверы не найдены или sing-box не запущен.",
servers_api_error: "Ошибка парсинга API: ",
// ─── QoS ───
qos_status: "Статус",
qos_enabled: "🟢 Включены",
qos_disabled: "❌ Выключены",
qos_start: "Начало",
qos_end: "Конец",
qos_quiet_hint: "В тихие часы бот отправляет только критические уведомления.",
// ─── Errors ───
err_invalid_format: "Неверный формат команды",
err_not_allowed: "Команда '%s' не входит в список разрешённых",
err_args_mismatch: "Аргументы не совпадают с разрешённым шаблоном",
err_wrong_arg_count: "Неверное число аргументов",
err_arg_not_allowed: "Аргумент не разрешён: %s",
err_subcmd_not_allowed: "Подкоманда '%s' не входит в whitelist",
err_host_unknown: "Неизвестно",
err_api_parse: "Ошибка чтения информации: ",
err_status_fetch: "Ошибка получения статуса.",
err_no_data: "Нет данных.",
err_log_read: "Не удалось прочитать логи.",
err_no_output: "Нет вывода",
// ─── Notifications ───
notif_restart_services: "Выполнен перезапуск служб Tachyon",
notif_nft_cleared: "Таблица правил nftables очищена или повреждена",
notif_nft_regenerated: "Выполнена быстрая регенерация правил TachyonTable и цепочки TPROXY",
notif_qos_not_found: "Правила Игрового & Голосового QoS Ускорителя не найдены в nftables",
notif_qos_applied: "Применены высокоприоритетные метки DSCP EF (0x2e) для Voice/RTC и DSCP AF41 (0x22) для Gaming",
notif_restart_skipped: "Перезапуск не выполнен: лимит частоты или активная блокировка",
notif_singbox_hung: "Зависание или неполный отклик прокси-порту sing-box",
notif_singbox_restarted: "Выполнен перезапуск sing-box",
notif_cache_cleared: "Очищена база cache.db; перезапуск пропущен по лимиту частоты",
notif_sets_empty: "Пустые nftables sets подсетей (community) — данные не были загружены при reload",
notif_sets_restored: "Восстановлены nftables sets из persistent кеша",
notif_fw_reload: "Выполнен reload_firewall для восстановления TPROXY правил",
notif_config_invalid: "Перезапуск отменён: конфигурация sing-box не проходит валидацию",
notif_restart_rate: "Перезапуск пропущен по лимиту частоты",
notif_rpcd_restarted: "Выполнен перезапуск rpcd для освобождения файловых дескрипторов",
// ─── Route failure ───
route_proxy_alive: "прокси-порт слушается, но запросы через него не проходят",
route_proxy_down: "прокси-порт не слушается (mixed-inbound sing-box не поднялся)",
route_hint: "Причина: %s.\nВключён прямой доступ к API как запасной путь — команды могут отвечать с задержкой.\nВыполните /test для проверки связи.",
route_alert_title: "Tachyon Telegram бот",
route_alert_consecutive: "неудачных попыток связи с API подряд",
route_reason: "Причина:",
route_fallback_hint: "Включён прямой доступ к API как запасной путь — команды могут отвечать с задержкой.",
route_heal_hint: "Watchdog попробует восстановить прокси автоматически.",
// ─── Command descriptions ───
cmd_menu: "Главное меню",
cmd_status: "Статус системы",
cmd_runtime: "Статистика трафика",
cmd_outbounds: "Прокси серверы",
cmd_sections: "Секции маршрутизации",
cmd_speed: "Тест скорости",
cmd_ping: "Задержка до серверов",
cmd_test: "Быстрая диагностика",
cmd_logs: "Просмотр логов",
cmd_info: "Информация о системе",
cmd_connections: "Активные подключения",
cmd_test_rule: "Проверка правила",
cmd_help: "Справка",
cmd_check_updates: "Проверить обновления",
cmd_close_connections: "Закрыть все соединения",
cmd_doctor: "Диагностика",
cmd_restart: "Перезапуск служб Tachyon",
cmd_instances: "Live серверы",
cmd_lang: "Сменить язык",
cmd_guest: "Гостевой режим",
menu_guest: "👥 Гостевой режим",
guest_title: "Гостевой режим",
guest_enabled: "Гостевой режим включён",
guest_disabled: "Гостевой режим выключен",
guest_status_active: "🟢 Активен",
guest_status_inactive: "⚪ Отключен",
guest_mode_selected: "Выбранные устройства",
guest_mode_inverted: "Все, кроме доверенных",
guest_devices_count: "Гостевых устройств: %d",
guest_trusted_count: "Доверенных устройств: %d",
guest_time_limit: "Дневной лимит времени: %d мин",
guest_traffic_limit: "Дневной лимит трафика: %s",
guest_lan_isolated: "Изоляция LAN: Активна (доступ к локальной сети и роутеру заблокирован)",
guest_time_spent: "Времени сегодня: %d мин",
guest_traffic_spent: "Трафика сегодня: %s",
guest_limit_reached_time: "⏳ Гостевое устройство %s исчерпало дневной лимит времени (%d мин). Доступ заблокирован до полуночи.",
guest_limit_reached_traffic: "📊 Гостевое устройство %s исчерпало дневной лимит трафика (%s). Доступ заблокирован до полуночи.",
btn_guest_toggle: "👥 Гостевой режим",
// ─── Misc ───
exec_done: "Выполнено (код %s):\n%s", hour_enter: "⏰ Введите час %s (0-23):", hour_start: "начала", hour_end: "окончания", blocked_activity: "Заблокированная активность", blocked_attempts: "Попыток доступа к заблокированным сайтам", daily_digest_title: "🌅 Утренний дайджест Tachyon", daily_rx: "Текущий RX", daily_tx: "Текущий TX", daily_connections: "Соединений", daily_memory: "Память", choose_language: "Выберите язык:", lang_current: "Текущий язык: %s", lang_saved: "Язык сохранён!", ping_enter_host: "Введите домен или IP для проверки:", test_rule_prompt: "Введите правило для проверки:", rule_label: "Правило", // ─── Speed test ─── speed_starting: "⚡ Запуск Speedtest...\n\nТестирую скорость через Cloudflare. Это займёт до 30 секунд.", speed_result: "⚡ Результат Speedtest", speed_direct: "📥 Прямое соединение:", speed_proxy: "📤 Через прокси:", speed_fail_measure: "❌ не удалось измерить", speed_error: "❌ Не удалось выполнить тест. Проверьте, запущен ли sing-box.", speed_check_hint: "Проверьте доступ в интернет и работу sing-box.", // ─── Ping ─── ping_api_error: "❌ Не удалось получить данные из Clash API.", // ─── Quick test ─── test_title: "Быстрая диагностика", test_direct: "Прямое соединение", test_proxy: "Прокси соединение", test_service: "Tachyon сервис", test_nft: "nftables правила", test_pause: "Пауза:", test_rule: "Проверка правила:", test_match: "Совпадение", test_no_match: "Совпадений не найдено. Трафик пойдёт через default route.", // ─── Connections ─── conn_total_down: "Всего скачано", conn_total_up: "Всего отдано", conn_closed: "Все активные соединения сброшены.", // ─── Devices ─── device_block: "🚫 Заблокировать", device_unblock: "🔓 Разблокировать", device_blocked: "заблокировано", device_unblocked: "разблокировано", // ─── AI Doctor ─── ai_doctor_title: "ИИ-Автомеханик Tachyon", ai_status_collecting: "Сбор полного AI Watchdog статуса...", ai_incident: "⚠️ Устранён инцидент:", ai_resolution: "🔧 Авто-решение:", ai_all_ok: "Все сетевые службы, DNS, nftables и память работают идеально.", ai_diag_ok: "Диагностика завершена успешно.", ai_diag_done: "Диагностика завершена.", // ─── Buttons ─── btn_refresh: "🔄 Обновить", btn_retry: "🔄 Повторить", btn_again: "🔄 Ещё раз", btn_speed_again: "⚡ Ещё раз", btn_back_to_groups: "🔙 К списку групп", btn_back_to_sections: "🔙 К списку секций", btn_close_all: "❌ Закрыть все", btn_test: "🩺 Тест", nav_cancel: "⬅️ Отмена", restart_confirm: "⚠️ Перезапустить службы Tachyon?\nТекущие соединения будут разорваны на время перезапуска.", restart_yes: "✅ Да, перезапустить", restart_in_progress: "Перезапускаю службы Tachyon...", restart_success: "Перезапуск выполнен успешно!", restart_error: "Ошибка при перезапуске.", conn_closed: "Все активные соединения сброшены.", // ─── Outbounds ─── servers_not_found: "Серверы не найдены или sing-box не запущен.", outbounds_hint: "Нажмите кнопку, чтобы переключить сервер.", // ─── Logs ─── logs_level: "уровень", // ─── Info ─── info_device: "Устройство", err_info_read: "Ошибка чтения информации:", // ─── Updates ─── update_new_build: "Новая сборка текущего релиза!", update_available: "Доступно обновление компонента!", build_label: "Сборка", // ─── Healer ─── heal_running: "ИИ-Автомеханик выполняет диагностику и самолечение...", // ─── Batch i18n additions ─── msg_truncated: "... (сообщение сокращено)", menu_version: "Версия", menu_cpu: "Роутер CPU", list_header: "⚙️ Список:", outbound_groups_not_found: "Группы прокси не найдены.", outbound_switch_failed: "Не удалось переключить сервер", outbound_group: "Группа", outbound_server: "Сервер", outbound_check_hint: "Проверьте, запущен ли sing-box и доступен ли Clash API.", section_ips: "IP адреса", section_list_header: "⚙️ Секция:", backup_archive_empty: "Архив пуст или повреждён.", backup_archive_unsafe: "Небезопасный архив:", backup_restoring: "Проверяю и восстанавливаю бэкап...", backup_extract_failed: "Не удалось распаковать архив.", backup_not_valid_uci: "config/tachyon в архиве не похож на валидный UCI.", backup_no_config: "В архиве нет ни config/tachyon, ни каталога tachyon/.", backup_save_config_failed: "Не удалось сохранить текущий конфиг.", backup_save_data_failed: "Не удалось сохранить текущие данные /etc/tachyon.", backup_write_config_error: "Ошибка записи /etc/config/tachyon.", backup_uci_validation_failed: "Восстановленный конфиг не прошёл проверку UCI. Выполнен откат.", backup_write_data_error: "Ошибка записи /etc/tachyon. Выполнен откат.", backup_restored: "Бэкап восстановлен. Перезапускаю Tachyon...", backup_done: "Готово!", backup_collecting: "Собираю бэкап...", backup_create_error: "Ошибка создания бэкапа.", support_bundle_collecting: "Формирую Support Bundle...", support_bundle_error: "Ошибка генерации Bundle.", conn_closed_msg: "Все активные соединения сброшены.\nОни будут переустановлены по новым маршрутам.", conn_close_error: "Ошибка сброса соединений.\nsing-box Clash API недоступен.", updates_title: "Обновления компонентов", updates_disabled: "Проверка обновлений отключена в настройках.", updates_cache_empty: "Кэш пуст. Зайдите позже.", update_check_error: "Ошибка проверки", update_new_release_build: "новая сборка релиза", update_dev_build: "dev-сборка", update_all_current: "Все компоненты актуальны.", update_cache_parse_error: "Ошибка парсинга кэша:", update_cache_unavailable: "Кэш проверок пуст или недоступен.", btn_refresh_cache: "Обновить кэш", api_parse_error: "Ошибка парсинга API:", api_connect_failed: "Не удалось подключиться к API sing-box.", logs_header: "Логи Tachyon", info_title: "Информация о системе", info_device_label: "Устройство", info_fetch_failed: "Не удалось получить информацию о системе.", sub_empty_url: "(пусто)", btn_sub_delete: " 🗑 Удалить", btn_add_subscription: "Добавить подписку", sub_section_subs: "Подписки секции", sub_found_count: "Найдено подписок:", sub_enter_url: "Введите URL подписки:\n\nОтправьте /cancel для отмены", sub_url_empty: "URL не может быть пустым.", sub_added: "Подписка добавлена в секцию", sub_not_found: "Подписка не найдена.", sub_removed: "Подписка удалена из секции", help_title: "Справка по командам", help_main_section: "Основные:", help_diag_section: "Диагностика:", help_manage_section: "Управление:", help_settings_section: "Настройка:", help_this: "Эта справка", quiet_hours_title: "Тихие часы", quiet_status: "Статус", quiet_enabled_label: "Включены", quiet_disabled_label: "Выключены", quiet_start_label: "Начало:", quiet_end_label: "Конец:", quiet_hint: "В тихие часы бот отправляет только критические уведомления.", btn_enable: "Включить", btn_disable: "Выключить", quiet_hour_start: "начала", quiet_hour_end: "окончания", quiet_enter_hour: "⏰ Введите час %s тихих часов (0–23):\n\nОтправьте /cancel для отмены", test_rule_header: "Проверка правила:", test_match: "Совпадение:", test_action: "Действие:", export_title: "Экспорт конфига...", export_done: "Конфиг экспортирован.\n\n⚠️ Файл содержит чувствительные данные. Не публикуйте его.", export_failed: "Не удалось экспортировать конфиг.", mac_invalid: "Некорректный MAC-адрес.", mac_firewall_open_failed: "Не удалось открыть конфигурацию firewall.", mac_block_rule_failed: "Не удалось создать правило блокировки.", mac_blocked: "заблокировано.", mac_unblocked: "разблокировано.", mac_firewall_bg: "Правила firewall применяются в фоне (несколько секунд).", devices_title: "Устройства в сети:", device_hostname_unknown: "Неизвестно", devices_not_found: "Устройства не найдены.", watchdog_stop_btn: "Остановить Watchdog", ai_full_status_btn: "Полный статус AI", watchdog_start_btn: "Запустить Watchdog", ai_incident: "⚠️ Устранён инцидент:", ai_resolution: "🔧 Авто-решение:", ai_all_ok: "Все сетевые службы, DNS, nftables и память работают идеально.", ai_diag_ok: "Диагностика завершена успешно.", ai_diag_done: "Диагностика завершена.", ai_status_collecting: "Сбор полного AI Watchdog статуса...", ai_status_error: "Ошибка получения статуса.", ai_data_truncated: "данные сокращены", ai_full_status_header: "AI Watchdog — Полный статус:", qos_title: "Игровой & Голосовой QoS Ускоритель", qos_enabled_detail: "Включен (DSCP EF + AF41 Priority)", qos_disabled_detail: "Выключен", qos_priority_rules: "Приоритетные правила:", btn_qos_disable: "Отключить QoS", btn_qos_enable: "Включить QoS", cb_obsolete: "Кнопка устарела — откройте список заново.", update_component_title: "Запуск обновления", update_bg_hint: "Процесс запущен в фоне. Зайдите позже.", err_unknown_component: "Неизвестный компонент:", admin_invalid_chat_id: "Неверный Chat ID.", admin_user_added: "Пользователь добавлен в список администраторов.", admin_user_already: "Пользователь уже является администратором.", sec_enter_name: "Введите латинское имя для новой секции (например,
my_vpn):\n\nОтправьте /cancel для отмены",
sec_enter_label: "Введите новое понятное имя (Label) для секции",
sec_enter_target: "Введите имя исходящего интерфейса (target) для секции",
sec_not_found: "Секция не найдена.",
sec_deleted: "Секция удалена.",
sec_add_items_prompt: "Отправьте элементы для добавления (по одному в строке или через пробел):\n\nОтправьте /cancel для отмены",
sec_sub_add_prompt: "Введите имя секции и URL подписки через пробел:\n/sec_sub_add Main https://...\n\nОтправьте /cancel для отмены",
sec_enter_value: "Введите новое значение для",
sec_add_list_prompt: "Отправьте элементы для добавления в список (через пробел или с новой строки):\n\nОтправьте /cancel для отмены",
dns_all_protocols: "Все протоколы",
dns_servers_applied: "DNS серверы установлены:",
dns_restart_hint: "Выполните перезапуск для применения.",
dns_bootstrap_applied: "Bootstrap DNS серверы установлены:",
exec_cmd_error: "Ошибка выполнения команды:",
exec_access_denied: "Доступ запрещен. Ваш Chat ID:",
backup_downloading: "Скачиваю бэкап...",
backup_download_error: "Ошибка загрузки файла.",
backup_url_error: "Ошибка получения URL файла.",
backup_format_hint: "Пожалуйста, отправьте бэкап в формате `.tar.gz`.",
admin_forward_prompt: "Вы переслали сообщение от пользователя",
btn_admin_add: "Добавить",
exec_whitelist_msg: "Выполняю из разрешённого набора (whitelist):",
exec_no_output: "Нет вывода",
action_cancelled: "Действие отменено.",
err_text_expected: "Ожидается текстовое сообщение. Попробуйте ещё раз или /cancel.",
value_saved: "Значение сохранено.",
items_added: "элементов.",
section_created: "Секция создана!",
err_invalid_section_name: "Неверное имя. Разрешены только буквы, цифры и подчеркивания.",
label_changed: "Имя изменено.",
target_changed: "Цель изменена.",
items_added_count: "Добавлено",
nothing_added: "Ничего не добавлено.",
err_format_section_url: "Формат: секция URL\nНапример: Main https://...",
err_invalid_hour: "Введите целое число от 0 до 23.",
saved: "Сохранено.",
daily_unknown: "неизвестно",
daily_uptime: "Аптайм ОС:",
daily_rx: "Текущий RX:",
daily_tx: "Текущий TX:",
build_label: "Сборка:",
blocked_activity: "Заблокированная активность",
blocked_rule_label: "Правило:",
blocked_rule: "Правило:",
blocked_attempts: "Попыток доступа к заблокированным сайтам",
info_read_error: "Ошибка чтения информации:",
quiet_start_word: "начала",
quiet_end_word: "окончания",
quiet_saved: "Настройки сохранены.",
sec_created: "Секция создана!",
sec_invalid_name: "Некорректное имя. Только латиница, цифры и _.",
sec_invalid_hour: "Некорректное время.",
sec_changed_to: "изменено на",
exec_value_saved: "Значение сохранено. Для вступления изменений в силу необходим перезапуск.",
exec_items_added: "Добавлено",
exec_items_added_count: "Добавлено",
exec_nothing_added: "Ничего не добавлено.",
exec_changed_to: "изменено на",
exec_format_hint: "Формат: domain.com или [ip]/prefix.",
update_new_release_header: "Новая сборка текущего релиза!",
update_build_label: "Сборка",
critical_keyword_fallen: "Упал",
critical_keyword_error: "Ошибка",
btn_update_component: "Обновить",
fptn_token_title: "FPTN Access Token",
fptn_token_prompt: "Пришлите новый токен доступа FPTN (скопируйте или перешлите из @fptn_bot):",
fptn_token_invalid: "Некорректный или пустой токен FPTN. Попробуйте ещё раз.",
fptn_token_no_section: "Токен сохранён, но секция с действием 'fptn' не найдена в конфиге.",
fptn_restarting: "Обновляю токен и запускаю FPTN...",
fptn_token_updated_title: "FPTN токен успешно обновлён и подключён!",
fptn_token_saved_connecting: "Токен сохранён, но FPTN пока подключается.",
fptn_token_retry_hint: "Подключение продолжится в фоне; маршрутизация применится автоматически.",
fptn_token_change_btn: "Сменить токен FPTN",
fptn_token_btn: "Токен FPTN",
}
};
// Resolve language: UCI setting → fallback to "en"
function resolve_lang(cfg_lang) {
let lang = cfg_lang || "en";
if (type(lang) != "string") lang = "en";
// trim whitespace
lang = replace(lang, /^\s+|\s+$/g, "");
if (lang == "" || (lang != "en" && lang != "ru")) lang = "en";
return lang;
}
// Return a translation function bound to the given language.
// Usage: let t = i18n.bind(lang); t("key") → translated string.
// Strings with %s placeholders use sprintf for substitution.
function bind(lang) {
lang = resolve_lang(lang);
return function(key, ...args) {
let table = strings[lang];
let val = table ? table[key] : null;
if (val == null) val = strings.en[key];
if (val == null) return key;
// sprintf-style substitution: pass extra args to t("key", arg1, arg2)
if (length(args) > 0) {
try { return sprintf(val, ...args); } catch(e) { return val; }
}
return val;
};
}
// Return available languages as [{code, label, available}]
function available_languages(cfg_lang) {
let current = resolve_lang(cfg_lang);
let langs = [
{ code: "en", label: "English", available: true },
{ code: "ru", label: "Русский", available: true }
];
return langs;
}
// Module exports
return { resolve_lang, bind, available_languages, strings };