From c7dec4c5d80ac94430f0dd2c929c17703f680bd1 Mon Sep 17 00:00:00 2001 From: Pikman Date: Thu, 4 Apr 2024 01:34:44 +0300 Subject: [PATCH 01/19] =?UTF-8?q?Fink=20=F0=9F=90=A6:=20update=20translati?= =?UTF-8?q?ons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_ru.i18n.json | 35 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 0678666d..2be9b987 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -15,7 +15,13 @@ "notSet": "Не задано", "agree": "Соглашаться", "decline": "Отклонить", - "unknown": "Неизвестный" + "unknown": "Неизвестный", + "timeout": "Таймаут", + "clipboardExportSuccessMsg": "Скопировано", + "showMore": "Развернуть ", + "showLess": "Свернуть ", + "openAppSettings": "Открыть настройки", + "grantPermission": "Дать права доступа" }, "intro": { "termsAndPolicyCaution(rich)": "Продолжая, Вы соглашаетесь с ${tap(@:about.termsAndConditions)}", @@ -27,10 +33,14 @@ "noActiveProfileMsg": "Выберите профиль" }, "stats": { + "traffic": "Трафик", "trafficLive": "Текущий трафик", "trafficTotal": "Трафик", "uplink": "Скорость отправки", - "downlink": "Скорость загрузки" + "downlink": "Скорость загрузки", + "connection": "Соединение ", + "speed": "Скорость", + "totalTransferred": "Всего передано" }, "profile": { "overviewPageTitle": "Профили", @@ -44,7 +54,11 @@ "remainingDuration": "Ещё ${duration} дн", "remainingTrafficSemanticLabel": "Использовано ${consumed} трафика из ${total}", "expired": "Истекло", - "noTraffic": "Нет доступного трафика" + "noTraffic": "Нет доступного трафика", + "upload": "Отправлено ", + "download": "Скачано", + "total": "Всего трафика", + "expireDate": "Дата окончания подписки " }, "sortBy": { "lastUpdate": "по последнему обновлению", @@ -59,7 +73,8 @@ "permissionDeniedError": "Нет прав", "unexpectedError": "Неизвестная ошибка", "torchSemanticLabel": "Вспышка", - "facingSemanticLabel": "Фронтальная камера" + "facingSemanticLabel": "Фронтальная камера", + "permissionRequest": "Права на использование камеры для считывания QR" }, "manually": "Ввести вручную", "addingProfileMsg": "Добавление профиля", @@ -113,10 +128,22 @@ "emptyProxiesMsg": "Нет доступных прокси", "delayTestTooltip": "Тестирование задержки", "sortTooltip": "Сортировка прокси", + "checkIp": "Проверить IP", + "unknownIp": "Неизвестный IP", "sortOptions": { "unsorted": "По умолчанию", "name": "По алфавиту", "delay": "По задержке" + }, + "activeProxySemanticLabel": "Активный прокси", + "delaySemantics": { + "result": "Задержка: ${delay}мс", + "timeout": "Тайм-аут при тестировании задержки", + "testing": "Задержка: Тестирование..." + }, + "ipInfoSemantics": { + "address": "IP адрес", + "country": "Страна" } }, "logs": { From 9faac83e317c9c83a866b1680836545f7da0e49d Mon Sep 17 00:00:00 2001 From: Pikman Date: Thu, 4 Apr 2024 01:36:13 +0300 Subject: [PATCH 02/19] =?UTF-8?q?Fink=20=F0=9F=90=A6:=20update=20translati?= =?UTF-8?q?ons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_ru.i18n.json | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 2be9b987..6550d8cc 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -187,7 +187,8 @@ "openWorkingDir": "Открыть рабочую папку", "ignoreBatteryOptimizations": "Отключить оптимизацию батареи", "ignoreBatteryOptimizationsMsg": "Отключение ограничений для оптимальной производительности VPN.", - "dynamicNotification": "Отображение скорости в уведомлении" + "dynamicNotification": "Отображение скорости в уведомлении", + "autoIpCheck": "Автоматически проверять IP-адрес соединения" }, "advanced": { "sectionTitle": "Расширенные", @@ -308,17 +309,29 @@ "connecting": "Подключение", "disconnecting": "Отключение", "connected": "Подключено", + "reconnect": "Восстановить соединение", + "connectAnyWay": "Подключиться", "experimentalNotice": "Экспериментальные функции в использовании", "experimentalNoticeMsg": "Вы включили некоторые экспериментальные функции, которые могут повлиять на качество соединения и вызвать непредвиденные ошибки. Вы всегда можете изменить или сбросить эти параметры на странице параметров конфигурации.", - "disableExperimentalNotice": "Больше не показывать" + "disableExperimentalNotice": "Больше не показывать", + "reconnectMsg": "Подключитесь повторно, чтобы изменения вступили в силу" }, "config": { "resetBtn": "Сбросить параметры", "serviceMode": "Режим работы", + "quickSettings": "Быстрые настройки", + "allOptions": "Все параметры ", "serviceModes": { "proxy": "Прокси", "systemProxy": "Системный прокси", - "tun": "VPN" + "tun": "VPN", + "tunService": "VPN-сервис" + }, + "shortServiceModes": { + "proxy": "Прокси", + "systemProxy": "Системный прокси", + "tun": "VPN", + "tunService": "VPN-сервис" }, "section": { "route": "Варианты маршрутизации", From 75ec20a2d16cbb4c6003280561b605a7da74cb3a Mon Sep 17 00:00:00 2001 From: keyang556 <65295310+keyang556@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:25:32 +0800 Subject: [PATCH 03/19] =?UTF-8?q?chore:=20update=20translations=20with=20F?= =?UTF-8?q?ink=20=F0=9F=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_zh-TW.i18n.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/translations/strings_zh-TW.i18n.json b/assets/translations/strings_zh-TW.i18n.json index 4b7ecb07..b6c4dddb 100644 --- a/assets/translations/strings_zh-TW.i18n.json +++ b/assets/translations/strings_zh-TW.i18n.json @@ -12,7 +12,17 @@ "sort": "排序", "sortBy": "排序方式", "addToClipboard": "新增至剪貼簿", - "timeout": "暫停" + "notSet": "未設定", + "agree": "同意", + "decline": "拒絕", + "unknown": "不明", + "hidden": "隱藏", + "timeout": "超時", + "clipboardExportSuccessMsg": "已匯出至剪貼簿", + "showMore": "顯示更多", + "showLess": "顯示較少", + "openAppSettings": "開啟應用程式設定", + "grantPermission": "授予權限" }, "intro": { "termsAndPolicyCaution(rich)": "繼續即表示您同意協議 ${tap(@:about.termsAndConditions)}", From 7cf0e9582267602b96a2b7f260a9da4dda88185f Mon Sep 17 00:00:00 2001 From: keyang556 <65295310+keyang556@users.noreply.github.com> Date: Sun, 21 Apr 2024 18:15:05 +0800 Subject: [PATCH 04/19] =?UTF-8?q?chore:=20update=20translations=20with=20F?= =?UTF-8?q?ink=20=F0=9F=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_zh-TW.i18n.json | 69 ++++++++++++++------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/assets/translations/strings_zh-TW.i18n.json b/assets/translations/strings_zh-TW.i18n.json index b6c4dddb..b7e992da 100644 --- a/assets/translations/strings_zh-TW.i18n.json +++ b/assets/translations/strings_zh-TW.i18n.json @@ -25,33 +25,41 @@ "grantPermission": "授予權限" }, "intro": { - "termsAndPolicyCaution(rich)": "繼續即表示您同意協議 ${tap(@:about.termsAndConditions)}", + "termsAndPolicyCaution(rich)": "繼續即表示您同意合約 ${tap(@:about.termsAndConditions)}", "start": "開始" }, "home": { - "pageTitle": "主頁", + "pageTitle": "首頁", "emptyProfilesMsg": "首先新增訂閱設定檔", "noActiveProfileMsg": "選擇設定檔" }, "stats": { + "traffic": "流量", "trafficLive": "即時流量", "trafficTotal": "總流量", "uplink": "上行", - "downlink": "下行" + "downlink": "下行", + "connection": "連線", + "speed": "速度", + "totalTransferred": "總傳輸量" }, "profile": { "overviewPageTitle": "設定檔", "detailsPageTitle": "設定檔", "activeProfileNameSemanticLabel": "活動設定檔名稱:“${name}”。", - "activeProfileBtnSemanticLabel": "查看所有設定檔。", + "activeProfileBtnSemanticLabel": "查看所有設定檔", "nonActiveProfileBtnSemanticLabel": "選擇“${name}”作為活動設定檔。", "subscription": { "traffic": "流量", - "updatedTimeAgo": "更新${timeago}", + "updatedTimeAgo": "更新於 ${timeago}", "remainingDuration": "剩餘 ${duration} 天", "remainingTrafficSemanticLabel": "已使用 ${consumed} 流量,總共 ${total} 流量。", "expired": "已到期", - "noTraffic": "超過配額" + "noTraffic": "超過配額", + "upload": "上傳", + "download": "下載", + "total": "總流量", + "expireDate": "到期時間" }, "sortBy": { "lastUpdate": "最近更新", @@ -60,13 +68,14 @@ "add": { "buttonText": "新的設定檔", "shortBtnTxt": "新的設定檔", - "fromClipboard": "從剪貼簿添加", + "fromClipboard": "從剪貼簿新增", "scanQr": "掃描 QR code", "qrScanner": { "permissionDeniedError": "沒有權限", "unexpectedError": "出了點問題", "torchSemanticLabel": "手電筒", - "facingSemanticLabel": "相機朝向" + "facingSemanticLabel": "相機朝向", + "permissionRequest": "授予相機權限已允許掃描 QR code" }, "manually": "手動輸入", "addingProfileMsg": "新增設定檔", @@ -91,11 +100,11 @@ }, "edit": { "buttonTxt": "編輯", - "selectActiveTxt": "選擇並激活設定檔" + "selectActiveTxt": "選擇活動設定檔" }, "delete": { "buttonTxt": "刪除", - "confirmationMsg": "永久刪除設定檔嗎?", + "confirmationMsg": "要永久刪除設定檔嗎?", "successMsg": "設定檔刪除成功" }, "save": { @@ -120,18 +129,30 @@ "emptyProxiesMsg": "沒有可用的代理", "delayTestTooltip": "測試延遲", "sortTooltip": "對代理進行排序", + "checkIp": "檢測 IP 地址", + "unknownIp": "不明的 IP", "sortOptions": { "unsorted": "預設", "name": "按字母排序", "delay": "按延遲排序" + }, + "activeProxySemanticLabel": "生效中的代理", + "delaySemantics": { + "result": "延遲: ${delay}ms", + "timeout": "延遲測試超時", + "testing": "正在測試延遲" + }, + "ipInfoSemantics": { + "address": "IP 地址", + "country": "國家" } }, "logs": { - "pageTitle": "日誌", + "pageTitle": "記錄檔", "filterHint": "篩選", "allLevelsFilter": "全部", - "shareCoreLogs": "分享核心日誌", - "shareAppLogs": "共享應用程式日誌", + "shareCoreLogs": "分享核心記錄檔", + "shareAppLogs": "分享應用程式記錄檔", "pauseTooltip": "暫停", "resumeTooltip": "恢復", "clearTooltip": "清除" @@ -141,17 +162,21 @@ "requiresRestartMsg": "若要使其生效,請重新啟動應用程式", "experimental": "實驗性的", "experimentalMsg": "帶有實驗標誌的功能仍在開發中,可能會導致問題。", + "exportOptions": "匯出選項至剪貼簿", + "exportAllOptions": "匯出選項至剪貼不(用於偵錯) ", + "importOptions": "從剪貼簿匯入選項", + "importOptionsMsg": "浙江使用提供的值複寫所有配置選項。您確定嗎?", "general": { - "sectionTitle": "一般的", + "sectionTitle": "一般", "locale": "語言", "region": "地區", "regionMsg": "幫助設定預設選項以繞過國內地址", "regions": { "ir": "伊朗 (ir)", - "cn": "中國大陸 (cn)", + "cn": "中國 (cn)", "ru": "俄羅斯 (ru)", "af": "阿富汗 (af)", - "other": "其他地區" + "other": "其他" }, "themeMode": "主題模式", "themeModes": { @@ -161,18 +186,20 @@ "black": "黑色" }, "enableAnalytics": "啟用分析", - "enableAnalyticsMsg": "授予收集分析並發送崩潰報告以改進應用程式的權限", + "enableAnalyticsMsg": "授予收集分析並傳送崩潰報告以改進應用程式的權限", "autoStart": "隨系統啟動", "silentStart": "啟動最小化", "openWorkingDir": "開啟工作目錄", - "ignoreBatteryOptimizations": "停用電池優化", + "ignoreBatteryOptimizations": "停用電池最佳化", "ignoreBatteryOptimizationsMsg": "消除限制以獲得最佳 VPN 效能", - "dynamicNotification": "在通知中顯示速度" + "dynamicNotification": "在通知中顯示速度", + "hapticFeedback": "觸覺回饋", + "autoIpCheck": "自動檢查連線的 IP" }, "advanced": { - "sectionTitle": "高級設定", + "sectionTitle": "進階設定", "debugMode": "偵錯模式", - "debugModeMsg": "重新啟動應用程式以應用此更改", + "debugModeMsg": "重新啟動應用程式以套用此變更", "memoryLimit": "記憶體限制", "memoryLimitMsg": "如果您遇到記憶體不足錯誤或頻繁應用程式崩潰,請啟用", "resetTunnel": "重置 VPN 設定檔" From 6979487d3fc8d1119b9cdd572b21c77a43c39b8a Mon Sep 17 00:00:00 2001 From: keyang556 <65295310+keyang556@users.noreply.github.com> Date: Sun, 21 Apr 2024 18:55:32 +0800 Subject: [PATCH 05/19] =?UTF-8?q?chore:=20update=20translations=20with=20F?= =?UTF-8?q?ink=20=F0=9F=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_zh-TW.i18n.json | 54 +++++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/assets/translations/strings_zh-TW.i18n.json b/assets/translations/strings_zh-TW.i18n.json index b7e992da..11c4f67c 100644 --- a/assets/translations/strings_zh-TW.i18n.json +++ b/assets/translations/strings_zh-TW.i18n.json @@ -223,7 +223,7 @@ "geoip": "GeoIP", "geosite": "Geosite", "version": "版本${version}", - "fileMissing": "文件遺失", + "fileMissing": "檔案遺失", "update": "更新", "download": "下載", "failureMsg": "更新資源文件失敗", @@ -235,7 +235,7 @@ "about": { "pageTitle": "關於", "version": "版本", - "sourceCode": "原始代碼", + "sourceCode": "原始碼", "telegramChannel": "Telegram 頻道", "checkForUpdate": "檢查更新", "privacyPolicy": "隱私政策", @@ -254,12 +254,12 @@ "tray": { "dashboard": "儀表板", "quit": "退出", - "open": "打開", + "open": "開啟", "status": { - "connect": "連接", - "connecting": "連接中", - "disconnect": "斷開連接", - "disconnecting": "斷連中" + "connect": "連線", + "connecting": "連線中", + "disconnect": "中斷連線", + "disconnecting": "中斷連線中" } }, "failure": { @@ -270,8 +270,8 @@ }, "singbox": { "unexpected": "意外服務錯誤", - "serviceNotRunning": "服務未運行", - "missingPrivilege": "缺乏權限", + "serviceNotRunning": "服務未執行", + "missingPrivilege": "缺少權限", "missingPrivilegeMsg": "VPN 模式需要管理員權限。以管理員身分重新啟動應用程式或變更服務模式。", "missingGeoAssets": "缺少 GEO 資源文件", "missingGeoAssetsMsg": "GEO 資源文件缺失。請考慮更改活動資源文件或在設定中下載選定的資源文件。", @@ -311,42 +311,54 @@ "full_description": "Hiddify 的主要目標是提供安全、使用者友好且高效率的隧道用戶端。它使您能夠利用 VPN 服務權限將所有流量或選定的應用程式流量路由到您選擇的遠端伺服器。\n\n註:我們不提供任何伺服器;使用者需要使用自己的自託管伺服器或受信任的伺服器來確保其線上活動的隱私。\n\n我們透過以下方式支援伺服器:\n - 普通 V2ray/Xray 訂閱連結\n - Clash 訂閱連結\n - Sing-Box 訂閱連結\n\n 我們的獨特功能是什麼?\n - 使用者友善\n - 最佳化且快速\n - 自動選擇最低延遲\n - 顯示使用者使用資訊\n - 使用一鍵連結輕鬆導入\n - 免費且無廣告\n - 輕鬆切換線路\n - 等等\n 支援:\n - Sing-Box 支援的所有協定 \n - VLESS + XTLS Reality、Vision 協定 \n - VMESS\n - Trojan\n - ShoadowSocks\n - Reality\n - WireGuard\n - V2ray\n - Hystria2\n - TUIC \n - SSH\n - ShadowTLS\n\n\n 原始碼位於 https://github.com/hiddify/Hiddify-Next\n 應用程式核心基於開源的 Sing-Box。\n\n權限說明:\n\n - VPN 服務:由於此應用程式的目標是提供安全性、使用者友好且高效的隧道用戶端,因此我們需要此權限才能透過隧道將流量路由到遠端伺服器。\n - 獲取應用程式列表:此權限用於允許使用者包含或排除隧道的特定應用程式。\n - 接收啟動廣播:可以從應用程式設定中啟用或停用此權限,以在裝置啟動時啟動此應用程式。\n - 發送通知:此權限至關重要,因為我們使用前台服務來確保 VPN 服務的持續運作。\n - 該應用程式沒有廣告。分析和崩潰數據僅在用戶首次使用應用程式時明確同意的情況下才會出現。" }, "connection": { - "tapToConnect": "點擊以連接", - "connecting": "連接中", - "disconnecting": "斷連中", - "connected": "已連接", + "tapToConnect": "點擊以連線", + "connecting": "連線中", + "disconnecting": "中斷連線中", + "connected": "已連線", + "reconnect": "重新連線", + "connectAnyWay": "連線", "experimentalNotice": "使用中的實驗性功能", "experimentalNoticeMsg": "您啟用了一些實驗性功能,這些功能可能會影響連線品質並導致某些意外錯誤。您始終可以從「配置選項」頁面變更或重設這些選項。", - "disableExperimentalNotice": "不再提示" + "disableExperimentalNotice": "不再提示", + "reconnectMsg": "重新連線以使變更生效" }, "config": { "resetBtn": "重置選項", "serviceMode": "服務模式", "quickSettings": "快速設定", "setupWarp": "配置 WARP", + "allOptions": "全部配置選項", "serviceModes": { "proxy": "僅代理", "systemProxy": "系統代理", "tun": "VPN", "tunService": "VPN 服務" }, + "shortServiceModes": { + "proxy": "僅代理", + "systemProxy": "系統代理", + "tun": "VPN", + "tunService": "VPN 服務" + }, "section": { "route": "路由選項", "dns": "DNS 選項", "inbound": "入站選項", "mux": "復用器", "outbound": "出站選項", - "tlsTricks": "TLS Tricks", + "tlsTricks": "TLS 特性", + "warp": "WARP 選項", "misc": "其他選項" }, "warpConsent": { - "title": "Cloudflare WARP 許可條款" + "title": "Cloudflare WARP 授權條款", + "description(rich)": "Cloudflare WARP 是一個免費的 WireGuard VPN 提供商。啟用此選項即表示您同意 Cloudflare WARP 的 ${tos(服務條款)} 和 ${privacy(隱私權政策)}" }, "generateWarpConfig": "生成 WARP 配置檔案", "missingWarpConfig": "WARP 配置檔案缺失", "warpConfigGenerated": "WARP 配置檔案已生成", "pageTitle": "配置選項", - "logLevel": "日誌等級", + "logLevel": "紀錄等級", "resolveDestination": "解析目標地址", "ipv6Mode": "IPv6 路由", "ipv6Modes": { @@ -362,11 +374,11 @@ "mixedPort": "混合連接埠", "tproxyPort": "透明代理埠", "localDnsPort": "本地 DNS 連接埠", - "allowConnectionFromLan": "允許區域網連線", + "allowConnectionFromLan": "允許區域網路連線", "tunImplementation": "TUN 實現", "mtu": "MTU", - "connectionTestUrl": "連接測試網址", - "urlTestInterval": "URL 測試間隔", + "connectionTestUrl": "連線測試網址", + "urlTestInterval": "網址測試間隔", "enableClashApi": "啟用 Clash API", "clashApiPort": "Clash API 連接埠", "enableTun": "啟用 TUN", @@ -392,7 +404,7 @@ "inbound": "透過代理繞過 WARP", "outbound": "透過 WARP 繞過代理" }, - "warpLicenseKey": "許可證金鑰", + "warpLicenseKey": "授權金鑰", "warpCleanIp": "清理 IP", "warpPort": "埠", "warpNoise": "噪音計數", From 305db9250376f58e5ea869c4de0bf7cf009816dc Mon Sep 17 00:00:00 2001 From: Muhammadhussein Ammari Date: Mon, 20 May 2024 09:07:38 +0330 Subject: [PATCH 06/19] Update make_config.yaml --- windows/packaging/exe/make_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/packaging/exe/make_config.yaml b/windows/packaging/exe/make_config.yaml index 4f9e2e93..41e8c246 100644 --- a/windows/packaging/exe/make_config.yaml +++ b/windows/packaging/exe/make_config.yaml @@ -5,7 +5,7 @@ display_name: Hiddify executable_name: Hiddify.exe output_base_file_name: Hiddify.exe create_desktop_icon: true -install_dir_name: "{autopf64}\\hiddify" +install_dir_name: "{autopf64}\\Hiddify" setup_icon_file: ..\..\windows\runner\resources\app_icon.ico locales: - en From 2164ce75205a8db83de41714d8f34f0e875eef5b Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:09:50 +0300 Subject: [PATCH 07/19] Create strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 413 +++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 assets/translations/strings_ar.i18n.json diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json new file mode 100644 index 00000000..0177a91b --- /dev/null +++ b/assets/translations/strings_ar.i18n.json @@ -0,0 +1,413 @@ +{ + "general": { + "appTitle": "Hiddify", + "reset": "Reset", + "toggle": { + "enabled": "Enabled", + "disabled": "Disabled" + }, + "state": { + "disable": "Disable" + }, + "sort": "Sort", + "sortBy": "Sort by", + "addToClipboard": "Add To Clipboard", + "notSet": "Not Set", + "agree": "Agree", + "decline": "Decline", + "unknown": "Unknown", + "hidden": "Hidden", + "timeout": "Timeout", + "clipboardExportSuccessMsg": "Added To Clipboard", + "showMore": "Show More", + "showLess": "Show Less", + "openAppSettings": "Open App Settings", + "grantPermission": "Grant Permission" + }, + "intro": { + "termsAndPolicyCaution(rich)": "By Continuing You Agree With ${tap(@:about.termsAndConditions)}", + "start": "Start" + }, + "home": { + "pageTitle": "Home", + "emptyProfilesMsg": "Begin by Adding a Subscription Profile", + "noActiveProfileMsg": "Choose a Profile" + }, + "stats": { + "traffic": "Traffic", + "trafficLive": "Live Traffic", + "trafficTotal": "Total Traffic", + "uplink": "Uplink", + "downlink": "Downlink", + "connection": "Connection", + "speed": "Speed", + "totalTransferred": "Total Transferred" + }, + "profile": { + "overviewPageTitle": "Profiles", + "detailsPageTitle": "Profile", + "activeProfileNameSemanticLabel": "Active Profile Name: \"${name}\".", + "activeProfileBtnSemanticLabel": "View All Profiles", + "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as Active Profile", + "subscription": { + "traffic": "Traffic", + "updatedTimeAgo": "Updated ${timeago}", + "remainingDuration": "${duration} Days Remaining", + "remainingTrafficSemanticLabel": "${consumed} of ${total} Traffic Consumed", + "expired": "Expired", + "noTraffic": "Out of Quota", + "upload": "Upload", + "download": "Download", + "total": "Total Traffic", + "expireDate": "Expire Date" + }, + "sortBy": { + "lastUpdate": "Recently Updated", + "name": "Alphabetically" + }, + "add": { + "buttonText": "New Profile", + "shortBtnTxt": "New Profile", + "fromClipboard": "Add From Clipboard", + "scanQr": "Scan QR Code", + "qrScanner": { + "permissionDeniedError": "Permission Denied", + "unexpectedError": "Something Went Wrong", + "torchSemanticLabel": "Flash Light", + "facingSemanticLabel": "Camera Facing", + "permissionRequest": "Permission to camera to scan QR Code" + }, + "manually": "Manual Entry", + "addingProfileMsg": "Adding Profile", + "failureMsg": "Failed to Add Profile" + }, + "update": { + "buttonTxt": "Update", + "tooltip": "Update Profile", + "updateSubscriptions": "Update Subscriptions", + "failureMsg": "Failed to Update Profile", + "successMsg": "Profile Updated Successfully", + "namedFailureMsg": "Failed to Update \"${name}\"", + "namedSuccessMsg": "\"${name}\" Updated Successfully" + }, + "share": { + "buttonText": "Share", + "exportToClipboardSuccess": "Exported to Clipboard", + "exportSubLinkToClipboard": "Export Subscription Link to Clipboard", + "subLinkQrCode": "Subscription Link QR Code", + "exportConfigToClipboard": "Export Configuration to Clipboard", + "exportConfigToClipboardSuccess": "Configuration Copied to Clipboard" + }, + "edit": { + "buttonTxt": "Edit", + "selectActiveTxt": "Select Active Profile" + }, + "delete": { + "buttonTxt": "Delete", + "confirmationMsg": "Delete Profile Permanently?", + "successMsg": "Profile Deleted Successfully" + }, + "save": { + "buttonText": "Save", + "successMsg": "Profile Saved Successfully", + "failureMsg": "Failed to Save Profile" + }, + "detailsForm": { + "nameLabel": "Name", + "nameHint": "Profile Name", + "urlLabel": "URL", + "urlHint": "Full Config URL", + "emptyNameMsg": "Name Is Required", + "invalidUrlMsg": "Invalid URL", + "lastUpdate": "Last Update", + "updateInterval": "Auto Update", + "updateIntervalDialogTitle": "Auto Update Interval (in Hours)" + } + }, + "proxies": { + "pageTitle": "Proxies", + "emptyProxiesMsg": "No Proxies Available", + "delayTestTooltip": "Test Delay", + "sortTooltip": "Sort Proxies", + "checkIp": "Check IP", + "unknownIp": "Unknown IP", + "sortOptions": { + "unsorted": "Default", + "name": "Alphabetically", + "delay": "By Delay" + }, + "activeProxySemanticLabel": "Active Proxy", + "delaySemantics": { + "result": "Delay: ${delay}ms", + "timeout": "Delay Test Timeout", + "testing": "Delay: Testing..." + }, + "ipInfoSemantics": { + "address": "IP Address", + "country": "Country" + } + }, + "logs": { + "pageTitle": "Logs", + "filterHint": "Filter", + "allLevelsFilter": "All", + "shareCoreLogs": "Share Core Logs", + "shareAppLogs": "Share App Logs", + "pauseTooltip": "Pause", + "resumeTooltip": "Resume", + "clearTooltip": "Clear" + }, + "settings": { + "pageTitle": "Settings", + "requiresRestartMsg": "For this to take effect restart the app", + "experimental": "Experimental", + "experimentalMsg": "Features with Experimental flag are still in development and might cause issues.", + "exportOptions": "Export Options to Clipboard", + "exportAllOptions": "Export Options to Clipboard (Debug)", + "importOptions": "Import Options From Clipboard", + "importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?", + "general": { + "sectionTitle": "General", + "locale": "Language", + "region": "Region", + "regionMsg": "Helps set default options to bypass domestic addresses", + "regions": { + "ir": "Iran (ir)", + "cn": "China (cn)", + "ru": "Russia (ru)", + "af": "Afghanistan (af)", + "other": "Other" + }, + "themeMode": "Theme Mode", + "themeModes": { + "system": "Follow System Theme", + "dark": "Dark Mode", + "light": "Light Mode", + "black": "Black Mode" + }, + "enableAnalytics": "Enable Analytics", + "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", + "autoStart": "Start At Login", + "silentStart": "Start Minimized", + "openWorkingDir": "Open Working Directory", + "ignoreBatteryOptimizations": "Disable Battery Optimization", + "ignoreBatteryOptimizationsMsg": "Remove Restrictions For Optimal VPN Performance", + "dynamicNotification": "Display Speed in Notification", + "hapticFeedback": "Haptic Feedback", + "autoIpCheck": "Automatically Check Connection IP" + }, + "advanced": { + "sectionTitle": "Advanced", + "debugMode": "Debug Mode", + "debugModeMsg": "Restart the app for applying this change", + "memoryLimit": "Memory Limit", + "memoryLimitMsg": "Enable if you're experiencing out of memory errors or frequent app crash", + "resetTunnel": "Reset VPN Profile" + }, + "network": { + "perAppProxyPageTitle": "Per-App Proxy", + "perAppProxyModes": { + "off": "All", + "offMsg": "Proxy All Apps", + "include": "Proxy", + "includeMsg": "Proxy Only Selected Apps", + "exclude": "Bypass", + "excludeMsg": "Do Not Proxy Selected Apps" + }, + "showSystemApps": "Show System Apps", + "hideSystemApps": "Hide System Apps", + "clearSelection": "Clear Selection" + }, + "geoAssets": { + "pageTitle": "Routing Assets", + "geoip": "GeoIP", + "geosite": "GeoSite", + "version": "Version ${version}", + "fileMissing": "File Missing", + "update": "Update", + "download": "Download", + "failureMsg": "Failed to Update Asset", + "successMsg": "Successfully Updated Asset", + "addRecommended": "Add Recommended Assets", + "missingGeoAssetsMsg": "Selected Routing Assets' files are missing. Either download them or choose existing ones" + } + }, + "about": { + "pageTitle": "About", + "version": "Version", + "sourceCode": "Source Code", + "telegramChannel": "Telegram Channel", + "checkForUpdate": "Check For Update", + "privacyPolicy": "Privacy Policy", + "termsAndConditions": "Terms and Conditions" + }, + "appUpdate": { + "notAvailableMsg": "Already Using The Latest Version", + "dialogTitle": "Update Available", + "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", + "currentVersionLbl": "Current Version", + "newVersionLbl": "New Version", + "updateNowBtnTxt": "Update Now", + "laterBtnTxt": "Later", + "ignoreBtnTxt": "Ignore" + }, + "tray": { + "dashboard": "Dashboard", + "quit": "Quit", + "open": "Open", + "status": { + "connect": "Connect", + "connecting": "Connecting", + "disconnect": "Disconnect", + "disconnecting": "Disconnecting" + } + }, + "failure": { + "unexpected": "Unexpected Error", + "clash": { + "unexpected": "Unexpected Error", + "core": "Clash Error ${reason}" + }, + "singbox": { + "unexpected": "Unexpected Service Error", + "serviceNotRunning": "Service is Not Running", + "missingPrivilege": "Missing Privilege", + "missingPrivilegeMsg": "VPN Mode Requires Administrator Privileges. Either relaunch the app as administrator or change service mode.", + "missingGeoAssets": "Missing Geo Assets", + "missingGeoAssetsMsg": "Geo Assets Are Missing. Consider changing active asset or download selected one in the settings.", + "invalidConfigOptions": "Invalid Configuration Options", + "invalidConfig": "Invalid Configuration", + "create": "Service Creation Error", + "start": "Service Startup Error" + }, + "connectivity": { + "unexpected": "Unexpected Failure", + "missingVpnPermission": "Missing VPN Permission", + "missingNotificationPermission": "Missing Notification Permission", + "core": "Core Error" + }, + "profiles": { + "unexpected": "Unexpected Error", + "notFound": "Profile Not Found", + "invalidConfig": "Invalid Configs", + "invalidUrl": "Invalid URL" + }, + "connection": { + "unexpected": "Unexpected Connection Error", + "timeout": "Connection Timeout", + "badResponse": "Bad Response", + "connectionError": "Connection Error", + "badCertificate": "Bad Certificate" + }, + "geoAssets": { + "unexpected": "Unexpected Error", + "notUpdate": "No Update Available", + "activeNotFound": "Active Geo Asset Not Found" + } + }, + "play": { + "title": "Hiddify (Preview)", + "short_description": "Auto, SSH, VLESS, VMess, Trojan, Reality, Sing-Box, Clash, XRay, Shadowsocks", + "full_description": "The key goal of Hiddify is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe Support Servers With:\n- Normal V2Ray/XRay Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - More and more\n\nSupport:\n- All Protocols Supported by Sing-Box \n- VLESS + XTLS Reality, Vision\n- VMess\n- Trojan\n- ShoadowSocks\n- Reality\n- WireGuard\n- V2Ray\n- Hysteria2\n- TUICv5\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source Sing-Box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." + }, + "connection": { + "tapToConnect": "Tap To Connect", + "connecting": "Connecting", + "disconnecting": "Disconnecting", + "connected": "Connected", + "reconnect": "Reconnect", + "connectAnyWay": "Connect", + "experimentalNotice": "Experimental Features In Use", + "experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.", + "disableExperimentalNotice": "Don't Show Again", + "reconnectMsg": "Reconnect for Changes to Take Effect" + }, + "config": { + "resetBtn": "Reset Options", + "serviceMode": "Service Mode", + "quickSettings": "Quick Settings", + "setupWarp": "Setup WARP", + "allOptions": "All Config Options", + "serviceModes": { + "proxy": "Proxy Service Only", + "systemProxy": "Set System Proxy", + "tun": "VPN", + "tunService": "VPN Service" + }, + "shortServiceModes": { + "proxy": "Proxy", + "systemProxy": "System Proxy", + "tun": "VPN", + "tunService": "VPN Service" + }, + "section": { + "route": "Route Options", + "dns": "DNS Options", + "inbound": "Inbound Options", + "mux": "MultiPlexer", + "outbound": "Outbound Options", + "tlsTricks": "TLS Tricks", + "warp": "WARP Options", + "misc": "Misc Options" + }, + "warpConsent": { + "title": "Cloudflare WARP Consent", + "description(rich)": "Cloudflare WARP is a Free WireGuard VPN Provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}." + }, + "generateWarpConfig": "Generate WARP Config", + "missingWarpConfig": "Missing WARP Config", + "warpConfigGenerated": "WARP Config Generated", + "pageTitle": "Config Options", + "logLevel": "Log Level", + "resolveDestination": "Resolve Destination", + "ipv6Mode": "IPv6 Route", + "ipv6Modes": { + "disable": "Disable", + "enable": "Enable", + "prefer": "Preferred", + "only": "Exclusive" + }, + "remoteDnsAddress": "Remote DNS", + "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", + "directDnsAddress": "Direct DNS", + "directDnsDomainStrategy": "Direct DNS Domain Strategy", + "mixedPort": "Mixed Port", + "tproxyPort": "Transparent Proxy Port", + "localDnsPort": "Local DNS Port", + "allowConnectionFromLan": "Share VPN in Local Network", + "tunImplementation": "TUN Implementation", + "mtu": "MTU", + "connectionTestUrl": "Connection Test URL", + "urlTestInterval": "URL Test Interval", + "enableClashApi": "Enable Clash API", + "clashApiPort": "Clash API Port", + "enableTun": "Enable TUN", + "setSystemProxy": "Set System Proxy", + "enableDnsRouting": "Enable DNS Routing", + "enableFakeDns": "Enable Fake DNS", + "bypassLan": "Bypass Lan", + "strictRoute": "Strict Route", + "enableTlsFragment": "Enable TLS Fragment", + "tlsFragmentSize": "TLS Fragment Size", + "tlsFragmentSleep": "TLS Fragment Sleep", + "enableTlsMixedSniCase": "Enable TLS Mixed SNI Case", + "enableTlsPadding": "Enable TLS Padding", + "tlsPaddingSize": "TLS Padding", + "enableMux": "Enable Mux", + "muxProtocol": "Mux Protocol", + "muxMaxStreams": "Max Concurrent Streams", + "enableWarp": "Enable WARP", + "warpDetourMode": "Detour Mode", + "warpDetourModes": { + "proxyOverWarp": "Detour Proxies Through WARP", + "warpOverProxy": "Detour WARP Through Proxies", + "inbound": "Detour WARP Through Proxies", + "outbound": "Detour Proxies Through WARP" + }, + "warpLicenseKey": "License Key", + "warpCleanIp": "Clean IP", + "warpPort": "Port", + "warpNoise": "Noise Count", + "warpNoiseDelay": "Noise Delay" + } + } From b0c7a958bf2e0d7d868d1b05ba378c29ea7f0b99 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:15:23 +0300 Subject: [PATCH 08/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 527 ++++++++--------------- 1 file changed, 172 insertions(+), 355 deletions(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index 0177a91b..f41066ff 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -1,413 +1,230 @@ { "general": { "appTitle": "Hiddify", - "reset": "Reset", + "reset": "إعادة تعيين", "toggle": { - "enabled": "Enabled", - "disabled": "Disabled" + "enabled": "ممكّن", + "disabled": "معطل" }, "state": { - "disable": "Disable" + "disable": "تعطيل" }, - "sort": "Sort", - "sortBy": "Sort by", - "addToClipboard": "Add To Clipboard", - "notSet": "Not Set", - "agree": "Agree", - "decline": "Decline", - "unknown": "Unknown", - "hidden": "Hidden", - "timeout": "Timeout", - "clipboardExportSuccessMsg": "Added To Clipboard", - "showMore": "Show More", - "showLess": "Show Less", - "openAppSettings": "Open App Settings", - "grantPermission": "Grant Permission" + "sort": "فرز", + "sortBy": "فرز حسب", + "addToClipboard": "إضافة إلى الحافظة", + "notSet": "غير مضبوط", + "agree": "موافق", + "decline": "رفض", + "unknown": "غير معروف", + "hidden": "مخفي", + "timeout": "انتهاء الوقت", + "clipboardExportSuccessMsg": "أضيف إلى الحافظة", + "showMore": "إظهار المزيد", + "showLess": "إظهار أقل", + "openAppSettings": "فتح إعدادات التطبيق", + "grantPermission": "منح الإذن" }, "intro": { - "termsAndPolicyCaution(rich)": "By Continuing You Agree With ${tap(@:about.termsAndConditions)}", - "start": "Start" + "termsAndPolicyCaution(rich)": "بالمتابعة توافق على ${tap(@:about.termsAndConditions)}", + "start": "بدء" }, "home": { - "pageTitle": "Home", - "emptyProfilesMsg": "Begin by Adding a Subscription Profile", - "noActiveProfileMsg": "Choose a Profile" + "pageTitle": "الرئيسية", + "emptyProfilesMsg": "ابدأ بإضافة ملف اشتراك", + "noActiveProfileMsg": "اختر ملفًا" }, "stats": { - "traffic": "Traffic", - "trafficLive": "Live Traffic", - "trafficTotal": "Total Traffic", - "uplink": "Uplink", - "downlink": "Downlink", - "connection": "Connection", - "speed": "Speed", - "totalTransferred": "Total Transferred" + "traffic": "حركة المرور", + "trafficLive": "حركة المرور المباشرة", + "trafficTotal": "إجمالي حركة المرور", + "uplink": "الرفع", + "downlink": "التنزيل", + "connection": "الاتصال", + "speed": "السرعة", + "totalTransferred": "إجمالي المنقول" }, "profile": { - "overviewPageTitle": "Profiles", - "detailsPageTitle": "Profile", - "activeProfileNameSemanticLabel": "Active Profile Name: \"${name}\".", - "activeProfileBtnSemanticLabel": "View All Profiles", - "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as Active Profile", + "overviewPageTitle": "الملفات", + "detailsPageTitle": "ملف", + "activeProfileNameSemanticLabel": "اسم الملف النشط: \"${name}\".", + "activeProfileBtnSemanticLabel": "عرض جميع الملفات", + "nonActiveProfileBtnSemanticLabel": "اختر \"${name}\" كملف نشط", "subscription": { - "traffic": "Traffic", - "updatedTimeAgo": "Updated ${timeago}", - "remainingDuration": "${duration} Days Remaining", - "remainingTrafficSemanticLabel": "${consumed} of ${total} Traffic Consumed", - "expired": "Expired", - "noTraffic": "Out of Quota", - "upload": "Upload", - "download": "Download", - "total": "Total Traffic", - "expireDate": "Expire Date" + "traffic": "حركة المرور", + "updatedTimeAgo": "تم التحديث منذ ${timeago}", + "remainingDuration": "متبقي ${duration} أيام", + "remainingTrafficSemanticLabel": "تم استهلاك ${consumed} من أصل ${total}", + "expired": "منتهي الصلاحية", + "noTraffic": "نفذت الحصة", + "upload": "رفع", + "download": "تنزيل", + "total": "إجمالي حركة المرور", + "expireDate": "تاريخ الانتهاء" }, "sortBy": { - "lastUpdate": "Recently Updated", - "name": "Alphabetically" + "lastUpdate": "محدث حديثًا", + "name": "أبجديًا" }, "add": { - "buttonText": "New Profile", - "shortBtnTxt": "New Profile", - "fromClipboard": "Add From Clipboard", - "scanQr": "Scan QR Code", + "buttonText": "ملف جديد", + "shortBtnTxt": "ملف جديد", + "fromClipboard": "إضافة من الحافظة", + "scanQr": "مسح رمز الاستجابة السريعة", "qrScanner": { - "permissionDeniedError": "Permission Denied", - "unexpectedError": "Something Went Wrong", - "torchSemanticLabel": "Flash Light", - "facingSemanticLabel": "Camera Facing", - "permissionRequest": "Permission to camera to scan QR Code" + "permissionDeniedError": "الإذن مرفوض", + "unexpectedError": "حدث خطأ غير متوقع", + "torchSemanticLabel": "ضوء الفلاش", + "facingSemanticLabel": "اتجاه الكاميرا", + "permissionRequest": "الإذن بالكاميرا لمسح رمز الاستجابة السريعة" }, - "manually": "Manual Entry", - "addingProfileMsg": "Adding Profile", - "failureMsg": "Failed to Add Profile" + "manually": "إدخال يدوي", + "addingProfileMsg": "يتم إضافة الملف", + "failureMsg": "فشل في إضافة الملف" }, "update": { - "buttonTxt": "Update", - "tooltip": "Update Profile", - "updateSubscriptions": "Update Subscriptions", - "failureMsg": "Failed to Update Profile", - "successMsg": "Profile Updated Successfully", - "namedFailureMsg": "Failed to Update \"${name}\"", - "namedSuccessMsg": "\"${name}\" Updated Successfully" + "buttonTxt": "تحديث", + "tooltip": "تحديث الملف", + "updateSubscriptions": "تحديث الاشتراكات", + "failureMsg": "فشل في تحديث الملف", + "successMsg": "تم تحديث الملف بنجاح", + "namedFailureMsg": "فشل في تحديث \"${name}\"", + "namedSuccessMsg": "تم تحديث \"${name}\" بنجاح" }, "share": { - "buttonText": "Share", - "exportToClipboardSuccess": "Exported to Clipboard", - "exportSubLinkToClipboard": "Export Subscription Link to Clipboard", - "subLinkQrCode": "Subscription Link QR Code", - "exportConfigToClipboard": "Export Configuration to Clipboard", - "exportConfigToClipboardSuccess": "Configuration Copied to Clipboard" + "buttonText": "مشاركة", + "exportToClipboardSuccess": "تم التصدير إلى الحافظة", + "exportSubLinkToClipboard": "تصدير رابط الاشتراك إلى الحافظة", + "subLinkQrCode": "رمز الاستجابة السريعة لرابط الاشتراك", + "exportConfigToClipboard": "تصدير التكوين إلى الحافظة", + "exportConfigToClipboardSuccess": "تم نسخ التكوين إلى الحافظة" }, "edit": { - "buttonTxt": "Edit", - "selectActiveTxt": "Select Active Profile" + "buttonTxt": "تعديل", + "selectActiveTxt": "اختر الملف النشط" }, "delete": { - "buttonTxt": "Delete", - "confirmationMsg": "Delete Profile Permanently?", - "successMsg": "Profile Deleted Successfully" + "buttonTxt": "حذف", + "confirmationMsg": "حذف الملف نهائيًا؟", + "successMsg": "تم حذف الملف بنجاح" }, "save": { - "buttonText": "Save", - "successMsg": "Profile Saved Successfully", - "failureMsg": "Failed to Save Profile" + "buttonText": "حفظ", + "successMsg": "تم حفظ الملف بنجاح", + "failureMsg": "فشل في حفظ الملف" }, "detailsForm": { - "nameLabel": "Name", - "nameHint": "Profile Name", - "urlLabel": "URL", - "urlHint": "Full Config URL", - "emptyNameMsg": "Name Is Required", - "invalidUrlMsg": "Invalid URL", - "lastUpdate": "Last Update", - "updateInterval": "Auto Update", - "updateIntervalDialogTitle": "Auto Update Interval (in Hours)" + "nameLabel": "الاسم", + "nameHint": "اسم الملف", + "urlLabel": "الرابط", + "urlHint": "الرابط الكامل للتكوين", + "emptyNameMsg": "الاسم مطلوب", + "invalidUrlMsg": "رابط غير صالح", + "lastUpdate": "آخر تحديث", + "updateInterval": "تحديث تلقائي", + "updateIntervalDialogTitle": "فاصل التحديث التلقائي (بالساعات)" } }, "proxies": { - "pageTitle": "Proxies", - "emptyProxiesMsg": "No Proxies Available", - "delayTestTooltip": "Test Delay", - "sortTooltip": "Sort Proxies", - "checkIp": "Check IP", - "unknownIp": "Unknown IP", + "pageTitle": "الوكلاء", + "emptyProxiesMsg": "لا يوجد وكلاء متاحين", + "delayTestTooltip": "اختبار التأخير", + "sortTooltip": "فرز الوكلاء", + "checkIp": "تحقق من IP", + "unknownIp": "IP غير معروف", "sortOptions": { - "unsorted": "Default", - "name": "Alphabetically", - "delay": "By Delay" + "unsorted": "افتراضي", + "name": "أبجديًا", + "delay": "حسب التأخير" }, - "activeProxySemanticLabel": "Active Proxy", + "activeProxySemanticLabel": "الوكيل النشط", "delaySemantics": { - "result": "Delay: ${delay}ms", - "timeout": "Delay Test Timeout", - "testing": "Delay: Testing..." + "result": "التأخير: ${delay}مللي ثانية", + "timeout": "مهلة اختبار التأخير", + "testing": "التأخير: يتم الاختبار..." }, "ipInfoSemantics": { - "address": "IP Address", - "country": "Country" + "address": "عنوان IP", + "country": "البلد" } }, "logs": { - "pageTitle": "Logs", - "filterHint": "Filter", - "allLevelsFilter": "All", - "shareCoreLogs": "Share Core Logs", - "shareAppLogs": "Share App Logs", - "pauseTooltip": "Pause", - "resumeTooltip": "Resume", - "clearTooltip": "Clear" + "pageTitle": "السجلات", + "filterHint": "تصفية", + "allLevelsFilter": "الكل", + "shareCoreLogs": "مشاركة سجلات النواة", + "shareAppLogs": "مشاركة سجلات التطبيق", + "pauseTooltip": "إيقاف مؤقت", + "resumeTooltip": "استئناف", + "clearTooltip": "مسح" }, "settings": { - "pageTitle": "Settings", - "requiresRestartMsg": "For this to take effect restart the app", - "experimental": "Experimental", - "experimentalMsg": "Features with Experimental flag are still in development and might cause issues.", - "exportOptions": "Export Options to Clipboard", - "exportAllOptions": "Export Options to Clipboard (Debug)", - "importOptions": "Import Options From Clipboard", - "importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?", + "pageTitle": "الإعدادات", + "requiresRestartMsg": "لنفاذ هذا التغيير، أعد تشغيل التطبيق", + "experimental": "تجريبي", + "experimentalMsg": "الميزات التي تحمل العلم التجريبي لا تزال قيد التطوير وقد تسبب مشاكل.", + "exportOptions": "تصدير الخيارات إلى الحافظة", + "exportAllOptions": "تصدير جميع الخيارات إلى الحافظة (تصحيح)", + "importOptions": "استيراد الخيارات من الحافظة", + "importOptionsMsg": "سيتم إعادة كتابة جميع خيارات التكوين بالقيم المقدمة. هل أنت متأكد؟", "general": { - "sectionTitle": "General", - "locale": "Language", - "region": "Region", - "regionMsg": "Helps set default options to bypass domestic addresses", + "sectionTitle": "عام", + "locale": "اللغة", + "region": "المنطقة", + "regionMsg": "يساعد في ضبط الخيارات الافتراضية لتجاوز العناوين المحلية", "regions": { - "ir": "Iran (ir)", - "cn": "China (cn)", - "ru": "Russia (ru)", - "af": "Afghanistan (af)", - "other": "Other" + "ir": "إيران (ir)", + "cn": "الصين (cn)", + "ru": "روسيا (ru)", + "af": "أفغانستان (af)", + "other": "أخرى" }, - "themeMode": "Theme Mode", + "themeMode": "وضع السمة", "themeModes": { - "system": "Follow System Theme", - "dark": "Dark Mode", - "light": "Light Mode", - "black": "Black Mode" + "system": "اتباع سمة النظام", + "dark": "الوضع الداكن", + "light": "الوضع الفاتح", + "black": "الوضع الأسود" }, - "enableAnalytics": "Enable Analytics", - "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", - "autoStart": "Start At Login", - "silentStart": "Start Minimized", - "openWorkingDir": "Open Working Directory", - "ignoreBatteryOptimizations": "Disable Battery Optimization", - "ignoreBatteryOptimizationsMsg": "Remove Restrictions For Optimal VPN Performance", - "dynamicNotification": "Display Speed in Notification", - "hapticFeedback": "Haptic Feedback", - "autoIpCheck": "Automatically Check Connection IP" + "enableAnalytics": "تمكين التحليلات", + "enableAnalyticsMsg": "امنح الإذن لجمع التحليلات وإرسال تقارير الأعطال لتحسين التطبيق", + "autoStart": "بدء عند تسجيل الدخول", + "silentStart": "بدء مصغر", + "openWorkingDir": "فتح دليل العمل", + "ignoreBatteryOptimizations": "تعطيل تحسين البطارية", + "ignoreBatteryOptimizationsMsg": "إزالة القيود لأداء VPN الأمثل", + "dynamicNotification": "عرض السرعة في الإشعار", + "hapticFeedback": "تغذية مرتدة لمسية", + "autoIpCheck": "التحقق التلقائي من IP الاتصال" }, "advanced": { - "sectionTitle": "Advanced", - "debugMode": "Debug Mode", - "debugModeMsg": "Restart the app for applying this change", - "memoryLimit": "Memory Limit", - "memoryLimitMsg": "Enable if you're experiencing out of memory errors or frequent app crash", - "resetTunnel": "Reset VPN Profile" - }, - "network": { - "perAppProxyPageTitle": "Per-App Proxy", - "perAppProxyModes": { - "off": "All", - "offMsg": "Proxy All Apps", - "include": "Proxy", - "includeMsg": "Proxy Only Selected Apps", - "exclude": "Bypass", - "excludeMsg": "Do Not Proxy Selected Apps" - }, - "showSystemApps": "Show System Apps", - "hideSystemApps": "Hide System Apps", - "clearSelection": "Clear Selection" - }, - "geoAssets": { - "pageTitle": "Routing Assets", - "geoip": "GeoIP", - "geosite": "GeoSite", - "version": "Version ${version}", - "fileMissing": "File Missing", - "update": "Update", - "download": "Download", - "failureMsg": "Failed to Update Asset", - "successMsg": "Successfully Updated Asset", - "addRecommended": "Add Recommended Assets", - "missingGeoAssetsMsg": "Selected Routing Assets' files are missing. Either download them or choose existing ones" + "sectionTitle": "متقدم", + "debugMode": "وضع التصحيح", + "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", + "memoryLimit": "حد الذاكرة", + "memoryLimitMsg": "فعّل إذا كنت تواجه أخ + +طاء تفيض الذاكرة. القيمة الافتراضية 0. يرجى الإشارة إلى القيمة بالميجابايت.", + "ping": "الأمر ping", + "pingMsg": "القيمة الافتراضية 1.0.0.1.", + "alwaysOn": "تفعيل دائمًا", + "disableScv": "تعطيل SCV", + "v2FlyService": "خدمة v2Fly", + "disableV2FlyServiceMsg": "تعطيل خدمة v2Fly", + "allowInsecureConnections": "السماح بالاتصالات غير الآمنة", + "trustedCertificates": "شهادات موثوقة" } }, + "help": { + "pageTitle": "المساعدة", + "documentation": "التوثيق", + "tutorial": "الدروس التعليمية", + "contact": "اتصل بنا" + }, "about": { - "pageTitle": "About", - "version": "Version", - "sourceCode": "Source Code", - "telegramChannel": "Telegram Channel", - "checkForUpdate": "Check For Update", - "privacyPolicy": "Privacy Policy", - "termsAndConditions": "Terms and Conditions" - }, - "appUpdate": { - "notAvailableMsg": "Already Using The Latest Version", - "dialogTitle": "Update Available", - "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", - "currentVersionLbl": "Current Version", - "newVersionLbl": "New Version", - "updateNowBtnTxt": "Update Now", - "laterBtnTxt": "Later", - "ignoreBtnTxt": "Ignore" - }, - "tray": { - "dashboard": "Dashboard", - "quit": "Quit", - "open": "Open", - "status": { - "connect": "Connect", - "connecting": "Connecting", - "disconnect": "Disconnect", - "disconnecting": "Disconnecting" - } - }, - "failure": { - "unexpected": "Unexpected Error", - "clash": { - "unexpected": "Unexpected Error", - "core": "Clash Error ${reason}" - }, - "singbox": { - "unexpected": "Unexpected Service Error", - "serviceNotRunning": "Service is Not Running", - "missingPrivilege": "Missing Privilege", - "missingPrivilegeMsg": "VPN Mode Requires Administrator Privileges. Either relaunch the app as administrator or change service mode.", - "missingGeoAssets": "Missing Geo Assets", - "missingGeoAssetsMsg": "Geo Assets Are Missing. Consider changing active asset or download selected one in the settings.", - "invalidConfigOptions": "Invalid Configuration Options", - "invalidConfig": "Invalid Configuration", - "create": "Service Creation Error", - "start": "Service Startup Error" - }, - "connectivity": { - "unexpected": "Unexpected Failure", - "missingVpnPermission": "Missing VPN Permission", - "missingNotificationPermission": "Missing Notification Permission", - "core": "Core Error" - }, - "profiles": { - "unexpected": "Unexpected Error", - "notFound": "Profile Not Found", - "invalidConfig": "Invalid Configs", - "invalidUrl": "Invalid URL" - }, - "connection": { - "unexpected": "Unexpected Connection Error", - "timeout": "Connection Timeout", - "badResponse": "Bad Response", - "connectionError": "Connection Error", - "badCertificate": "Bad Certificate" - }, - "geoAssets": { - "unexpected": "Unexpected Error", - "notUpdate": "No Update Available", - "activeNotFound": "Active Geo Asset Not Found" - } - }, - "play": { - "title": "Hiddify (Preview)", - "short_description": "Auto, SSH, VLESS, VMess, Trojan, Reality, Sing-Box, Clash, XRay, Shadowsocks", - "full_description": "The key goal of Hiddify is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe Support Servers With:\n- Normal V2Ray/XRay Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - More and more\n\nSupport:\n- All Protocols Supported by Sing-Box \n- VLESS + XTLS Reality, Vision\n- VMess\n- Trojan\n- ShoadowSocks\n- Reality\n- WireGuard\n- V2Ray\n- Hysteria2\n- TUICv5\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source Sing-Box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." - }, - "connection": { - "tapToConnect": "Tap To Connect", - "connecting": "Connecting", - "disconnecting": "Disconnecting", - "connected": "Connected", - "reconnect": "Reconnect", - "connectAnyWay": "Connect", - "experimentalNotice": "Experimental Features In Use", - "experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.", - "disableExperimentalNotice": "Don't Show Again", - "reconnectMsg": "Reconnect for Changes to Take Effect" - }, - "config": { - "resetBtn": "Reset Options", - "serviceMode": "Service Mode", - "quickSettings": "Quick Settings", - "setupWarp": "Setup WARP", - "allOptions": "All Config Options", - "serviceModes": { - "proxy": "Proxy Service Only", - "systemProxy": "Set System Proxy", - "tun": "VPN", - "tunService": "VPN Service" - }, - "shortServiceModes": { - "proxy": "Proxy", - "systemProxy": "System Proxy", - "tun": "VPN", - "tunService": "VPN Service" - }, - "section": { - "route": "Route Options", - "dns": "DNS Options", - "inbound": "Inbound Options", - "mux": "MultiPlexer", - "outbound": "Outbound Options", - "tlsTricks": "TLS Tricks", - "warp": "WARP Options", - "misc": "Misc Options" - }, - "warpConsent": { - "title": "Cloudflare WARP Consent", - "description(rich)": "Cloudflare WARP is a Free WireGuard VPN Provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}." - }, - "generateWarpConfig": "Generate WARP Config", - "missingWarpConfig": "Missing WARP Config", - "warpConfigGenerated": "WARP Config Generated", - "pageTitle": "Config Options", - "logLevel": "Log Level", - "resolveDestination": "Resolve Destination", - "ipv6Mode": "IPv6 Route", - "ipv6Modes": { - "disable": "Disable", - "enable": "Enable", - "prefer": "Preferred", - "only": "Exclusive" - }, - "remoteDnsAddress": "Remote DNS", - "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", - "directDnsAddress": "Direct DNS", - "directDnsDomainStrategy": "Direct DNS Domain Strategy", - "mixedPort": "Mixed Port", - "tproxyPort": "Transparent Proxy Port", - "localDnsPort": "Local DNS Port", - "allowConnectionFromLan": "Share VPN in Local Network", - "tunImplementation": "TUN Implementation", - "mtu": "MTU", - "connectionTestUrl": "Connection Test URL", - "urlTestInterval": "URL Test Interval", - "enableClashApi": "Enable Clash API", - "clashApiPort": "Clash API Port", - "enableTun": "Enable TUN", - "setSystemProxy": "Set System Proxy", - "enableDnsRouting": "Enable DNS Routing", - "enableFakeDns": "Enable Fake DNS", - "bypassLan": "Bypass Lan", - "strictRoute": "Strict Route", - "enableTlsFragment": "Enable TLS Fragment", - "tlsFragmentSize": "TLS Fragment Size", - "tlsFragmentSleep": "TLS Fragment Sleep", - "enableTlsMixedSniCase": "Enable TLS Mixed SNI Case", - "enableTlsPadding": "Enable TLS Padding", - "tlsPaddingSize": "TLS Padding", - "enableMux": "Enable Mux", - "muxProtocol": "Mux Protocol", - "muxMaxStreams": "Max Concurrent Streams", - "enableWarp": "Enable WARP", - "warpDetourMode": "Detour Mode", - "warpDetourModes": { - "proxyOverWarp": "Detour Proxies Through WARP", - "warpOverProxy": "Detour WARP Through Proxies", - "inbound": "Detour WARP Through Proxies", - "outbound": "Detour Proxies Through WARP" - }, - "warpLicenseKey": "License Key", - "warpCleanIp": "Clean IP", - "warpPort": "Port", - "warpNoise": "Noise Count", - "warpNoiseDelay": "Noise Delay" + "pageTitle": "حول", + "termsAndConditions": "الشروط والأحكام", + "privacyPolicy": "سياسة الخصوصية", + "softwareLicenses": "تراخيص البرمجيات", + "version": "الإصدار ${version}" } - } +} From 81cb818297678121ceed7125f6fca5dd0f2d03bc Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:17:08 +0300 Subject: [PATCH 09/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index f41066ff..e30527d0 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -221,7 +221,7 @@ "contact": "اتصل بنا" }, "about": { - "pageTitle": "حول", + "pageTitle": "حول.\nتعريب:م. ابراهيم قاسم", "termsAndConditions": "الشروط والأحكام", "privacyPolicy": "سياسة الخصوصية", "softwareLicenses": "تراخيص البرمجيات", From 4398b5cce802b3445b8e3778f33ba33748041ae3 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:26:40 +0300 Subject: [PATCH 10/19] Update locale_extensions.dart --- lib/core/localization/locale_extensions.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/localization/locale_extensions.dart b/lib/core/localization/locale_extensions.dart index 2c8c1768..417d7ba7 100644 --- a/lib/core/localization/locale_extensions.dart +++ b/lib/core/localization/locale_extensions.dart @@ -6,6 +6,7 @@ extension AppLocaleX on AppLocale { this == AppLocale.fa ? FontFamily.shabnam : FontFamily.emoji; String get localeName => switch (flutterLocale.toString()) { + "ar" => "العربية", "en" => "English", "fa" => "فارسی", "ru" => "Русский", From 22ac75f18051b202516379c68d1097d270d4b0d2 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:32:12 +0300 Subject: [PATCH 11/19] Update settings.json --- project.inlang/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/project.inlang/settings.json b/project.inlang/settings.json index 79bf5db7..ebc5edb5 100644 --- a/project.inlang/settings.json +++ b/project.inlang/settings.json @@ -2,6 +2,7 @@ "$schema": "https://inlang.com/schema/project-settings", "sourceLanguageTag": "en", "languageTags": [ + "ar", "en", "ckb-KUR", "es", From 67f39b1757cf84a6b4ac55e6a32243a6eb3946a4 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:33:31 +0300 Subject: [PATCH 12/19] Update make_config.yaml --- windows/packaging/exe/make_config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/packaging/exe/make_config.yaml b/windows/packaging/exe/make_config.yaml index 4f9e2e93..96fccaae 100644 --- a/windows/packaging/exe/make_config.yaml +++ b/windows/packaging/exe/make_config.yaml @@ -8,6 +8,7 @@ create_desktop_icon: true install_dir_name: "{autopf64}\\hiddify" setup_icon_file: ..\..\windows\runner\resources\app_icon.ico locales: + - ar - en - fa - ru From 1fbb13834916943fad183c2d9e02a02b69408afd Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:35:17 +0300 Subject: [PATCH 13/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index e30527d0..650c2308 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -201,9 +201,7 @@ "debugMode": "وضع التصحيح", "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", "memoryLimit": "حد الذاكرة", - "memoryLimitMsg": "فعّل إذا كنت تواجه أخ - -طاء تفيض الذاكرة. القيمة الافتراضية 0. يرجى الإشارة إلى القيمة بالميجابايت.", + "memoryLimitMsg": "فعّل إذا كنت تواجه أخطاء تفيض الذاكرة. القيمة الافتراضية 0. يرجى الإشارة إلى القيمة بالميجابايت.", "ping": "الأمر ping", "pingMsg": "القيمة الافتراضية 1.0.0.1.", "alwaysOn": "تفعيل دائمًا", From 837ff0a8b5cf1d39c4174cea6f53533b3cb6a4a2 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:48:37 +0300 Subject: [PATCH 14/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 523 +++++++++++++++-------- 1 file changed, 354 insertions(+), 169 deletions(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index 650c2308..e4f24d7e 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -1,228 +1,413 @@ { "general": { "appTitle": "Hiddify", - "reset": "إعادة تعيين", + "reset": "Reset", "toggle": { - "enabled": "ممكّن", - "disabled": "معطل" + "enabled": "Enabled", + "disabled": "Disabled" }, "state": { - "disable": "تعطيل" + "disable": "Disable" }, - "sort": "فرز", - "sortBy": "فرز حسب", - "addToClipboard": "إضافة إلى الحافظة", - "notSet": "غير مضبوط", - "agree": "موافق", - "decline": "رفض", - "unknown": "غير معروف", - "hidden": "مخفي", - "timeout": "انتهاء الوقت", - "clipboardExportSuccessMsg": "أضيف إلى الحافظة", - "showMore": "إظهار المزيد", - "showLess": "إظهار أقل", - "openAppSettings": "فتح إعدادات التطبيق", - "grantPermission": "منح الإذن" + "sort": "Sort", + "sortBy": "Sort by", + "addToClipboard": "Add To Clipboard", + "notSet": "Not Set", + "agree": "Agree", + "decline": "Decline", + "unknown": "Unknown", + "hidden": "Hidden", + "timeout": "Timeout", + "clipboardExportSuccessMsg": "Added To Clipboard", + "showMore": "Show More", + "showLess": "Show Less", + "openAppSettings": "Open App Settings", + "grantPermission": "Grant Permission" }, "intro": { - "termsAndPolicyCaution(rich)": "بالمتابعة توافق على ${tap(@:about.termsAndConditions)}", - "start": "بدء" + "termsAndPolicyCaution(rich)": "By Continuing You Agree With ${tap(@:about.termsAndConditions)}", + "start": "Start" }, "home": { - "pageTitle": "الرئيسية", - "emptyProfilesMsg": "ابدأ بإضافة ملف اشتراك", - "noActiveProfileMsg": "اختر ملفًا" + "pageTitle": "Home", + "emptyProfilesMsg": "Begin by Adding a Subscription Profile", + "noActiveProfileMsg": "Choose a Profile" }, "stats": { - "traffic": "حركة المرور", - "trafficLive": "حركة المرور المباشرة", - "trafficTotal": "إجمالي حركة المرور", - "uplink": "الرفع", - "downlink": "التنزيل", - "connection": "الاتصال", - "speed": "السرعة", - "totalTransferred": "إجمالي المنقول" + "traffic": "Traffic", + "trafficLive": "Live Traffic", + "trafficTotal": "Total Traffic", + "uplink": "Uplink", + "downlink": "Downlink", + "connection": "Connection", + "speed": "Speed", + "totalTransferred": "Total Transferred" }, "profile": { - "overviewPageTitle": "الملفات", - "detailsPageTitle": "ملف", - "activeProfileNameSemanticLabel": "اسم الملف النشط: \"${name}\".", - "activeProfileBtnSemanticLabel": "عرض جميع الملفات", - "nonActiveProfileBtnSemanticLabel": "اختر \"${name}\" كملف نشط", + "overviewPageTitle": "Profiles", + "detailsPageTitle": "Profile", + "activeProfileNameSemanticLabel": "Active Profile Name: \"${name}\".", + "activeProfileBtnSemanticLabel": "View All Profiles", + "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as Active Profile", "subscription": { - "traffic": "حركة المرور", - "updatedTimeAgo": "تم التحديث منذ ${timeago}", - "remainingDuration": "متبقي ${duration} أيام", - "remainingTrafficSemanticLabel": "تم استهلاك ${consumed} من أصل ${total}", - "expired": "منتهي الصلاحية", - "noTraffic": "نفذت الحصة", - "upload": "رفع", - "download": "تنزيل", - "total": "إجمالي حركة المرور", - "expireDate": "تاريخ الانتهاء" + "traffic": "Traffic", + "updatedTimeAgo": "Updated ${timeago}", + "remainingDuration": "${duration} Days Remaining", + "remainingTrafficSemanticLabel": "${consumed} of ${total} Traffic Consumed", + "expired": "Expired", + "noTraffic": "Out of Quota", + "upload": "Upload", + "download": "Download", + "total": "Total Traffic", + "expireDate": "Expire Date" }, "sortBy": { - "lastUpdate": "محدث حديثًا", - "name": "أبجديًا" + "lastUpdate": "Recently Updated", + "name": "Alphabetically" }, "add": { - "buttonText": "ملف جديد", - "shortBtnTxt": "ملف جديد", - "fromClipboard": "إضافة من الحافظة", - "scanQr": "مسح رمز الاستجابة السريعة", + "buttonText": "New Profile", + "shortBtnTxt": "New Profile", + "fromClipboard": "Add From Clipboard", + "scanQr": "Scan QR Code", "qrScanner": { - "permissionDeniedError": "الإذن مرفوض", - "unexpectedError": "حدث خطأ غير متوقع", - "torchSemanticLabel": "ضوء الفلاش", - "facingSemanticLabel": "اتجاه الكاميرا", - "permissionRequest": "الإذن بالكاميرا لمسح رمز الاستجابة السريعة" + "permissionDeniedError": "Permission Denied", + "unexpectedError": "Something Went Wrong", + "torchSemanticLabel": "Flash Light", + "facingSemanticLabel": "Camera Facing", + "permissionRequest": "Permission to camera to scan QR Code" }, - "manually": "إدخال يدوي", - "addingProfileMsg": "يتم إضافة الملف", - "failureMsg": "فشل في إضافة الملف" + "manually": "Manual Entry", + "addingProfileMsg": "Adding Profile", + "failureMsg": "Failed to Add Profile" }, "update": { - "buttonTxt": "تحديث", - "tooltip": "تحديث الملف", - "updateSubscriptions": "تحديث الاشتراكات", - "failureMsg": "فشل في تحديث الملف", - "successMsg": "تم تحديث الملف بنجاح", - "namedFailureMsg": "فشل في تحديث \"${name}\"", - "namedSuccessMsg": "تم تحديث \"${name}\" بنجاح" + "buttonTxt": "Update", + "tooltip": "Update Profile", + "updateSubscriptions": "Update Subscriptions", + "failureMsg": "Failed to Update Profile", + "successMsg": "Profile Updated Successfully", + "namedFailureMsg": "Failed to Update \"${name}\"", + "namedSuccessMsg": "\"${name}\" Updated Successfully" }, "share": { - "buttonText": "مشاركة", - "exportToClipboardSuccess": "تم التصدير إلى الحافظة", - "exportSubLinkToClipboard": "تصدير رابط الاشتراك إلى الحافظة", - "subLinkQrCode": "رمز الاستجابة السريعة لرابط الاشتراك", - "exportConfigToClipboard": "تصدير التكوين إلى الحافظة", - "exportConfigToClipboardSuccess": "تم نسخ التكوين إلى الحافظة" + "buttonText": "Share", + "exportToClipboardSuccess": "Exported to Clipboard", + "exportSubLinkToClipboard": "Export Subscription Link to Clipboard", + "subLinkQrCode": "Subscription Link QR Code", + "exportConfigToClipboard": "Export Configuration to Clipboard", + "exportConfigToClipboardSuccess": "Configuration Copied to Clipboard" }, "edit": { - "buttonTxt": "تعديل", - "selectActiveTxt": "اختر الملف النشط" + "buttonTxt": "Edit", + "selectActiveTxt": "Select Active Profile" }, "delete": { - "buttonTxt": "حذف", - "confirmationMsg": "حذف الملف نهائيًا؟", - "successMsg": "تم حذف الملف بنجاح" + "buttonTxt": "Delete", + "confirmationMsg": "Delete Profile Permanently?", + "successMsg": "Profile Deleted Successfully" }, "save": { - "buttonText": "حفظ", - "successMsg": "تم حفظ الملف بنجاح", - "failureMsg": "فشل في حفظ الملف" + "buttonText": "Save", + "successMsg": "Profile Saved Successfully", + "failureMsg": "Failed to Save Profile" }, "detailsForm": { - "nameLabel": "الاسم", - "nameHint": "اسم الملف", - "urlLabel": "الرابط", - "urlHint": "الرابط الكامل للتكوين", - "emptyNameMsg": "الاسم مطلوب", - "invalidUrlMsg": "رابط غير صالح", - "lastUpdate": "آخر تحديث", - "updateInterval": "تحديث تلقائي", - "updateIntervalDialogTitle": "فاصل التحديث التلقائي (بالساعات)" + "nameLabel": "Name", + "nameHint": "Profile Name", + "urlLabel": "URL", + "urlHint": "Full Config URL", + "emptyNameMsg": "Name Is Required", + "invalidUrlMsg": "Invalid URL", + "lastUpdate": "Last Update", + "updateInterval": "Auto Update", + "updateIntervalDialogTitle": "Auto Update Interval (in Hours)" } }, "proxies": { - "pageTitle": "الوكلاء", - "emptyProxiesMsg": "لا يوجد وكلاء متاحين", - "delayTestTooltip": "اختبار التأخير", - "sortTooltip": "فرز الوكلاء", - "checkIp": "تحقق من IP", - "unknownIp": "IP غير معروف", + "pageTitle": "Proxies", + "emptyProxiesMsg": "No Proxies Available", + "delayTestTooltip": "Test Delay", + "sortTooltip": "Sort Proxies", + "checkIp": "Check IP", + "unknownIp": "Unknown IP", "sortOptions": { - "unsorted": "افتراضي", - "name": "أبجديًا", - "delay": "حسب التأخير" + "unsorted": "Default", + "name": "Alphabetically", + "delay": "By Delay" }, - "activeProxySemanticLabel": "الوكيل النشط", + "activeProxySemanticLabel": "Active Proxy", "delaySemantics": { - "result": "التأخير: ${delay}مللي ثانية", - "timeout": "مهلة اختبار التأخير", - "testing": "التأخير: يتم الاختبار..." + "result": "Delay: ${delay}ms", + "timeout": "Delay Test Timeout", + "testing": "Delay: Testing..." }, "ipInfoSemantics": { - "address": "عنوان IP", - "country": "البلد" + "address": "IP Address", + "country": "Country" } }, "logs": { - "pageTitle": "السجلات", - "filterHint": "تصفية", - "allLevelsFilter": "الكل", - "shareCoreLogs": "مشاركة سجلات النواة", - "shareAppLogs": "مشاركة سجلات التطبيق", - "pauseTooltip": "إيقاف مؤقت", - "resumeTooltip": "استئناف", - "clearTooltip": "مسح" + "pageTitle": "Logs", + "filterHint": "Filter", + "allLevelsFilter": "All", + "shareCoreLogs": "Share Core Logs", + "shareAppLogs": "Share App Logs", + "pauseTooltip": "Pause", + "resumeTooltip": "Resume", + "clearTooltip": "Clear" }, "settings": { - "pageTitle": "الإعدادات", - "requiresRestartMsg": "لنفاذ هذا التغيير، أعد تشغيل التطبيق", - "experimental": "تجريبي", - "experimentalMsg": "الميزات التي تحمل العلم التجريبي لا تزال قيد التطوير وقد تسبب مشاكل.", - "exportOptions": "تصدير الخيارات إلى الحافظة", - "exportAllOptions": "تصدير جميع الخيارات إلى الحافظة (تصحيح)", - "importOptions": "استيراد الخيارات من الحافظة", - "importOptionsMsg": "سيتم إعادة كتابة جميع خيارات التكوين بالقيم المقدمة. هل أنت متأكد؟", + "pageTitle": "Settings", + "requiresRestartMsg": "For this to take effect restart the app", + "experimental": "Experimental", + "experimentalMsg": "Features with Experimental flag are still in development and might cause issues.", + "exportOptions": "Export Options to Clipboard", + "exportAllOptions": "Export Options to Clipboard (Debug)", + "importOptions": "Import Options From Clipboard", + "importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?", "general": { - "sectionTitle": "عام", - "locale": "اللغة", - "region": "المنطقة", - "regionMsg": "يساعد في ضبط الخيارات الافتراضية لتجاوز العناوين المحلية", + "sectionTitle": "General", + "locale": "Language", + "region": "Region", + "regionMsg": "Helps set default options to bypass domestic addresses", "regions": { - "ir": "إيران (ir)", - "cn": "الصين (cn)", - "ru": "روسيا (ru)", - "af": "أفغانستان (af)", - "other": "أخرى" + "ir": "Iran (ir)", + "cn": "China (cn)", + "ru": "Russia (ru)", + "af": "Afghanistan (af)", + "other": "Other" }, - "themeMode": "وضع السمة", + "themeMode": "Theme Mode", "themeModes": { - "system": "اتباع سمة النظام", - "dark": "الوضع الداكن", - "light": "الوضع الفاتح", - "black": "الوضع الأسود" + "system": "Follow System Theme", + "dark": "Dark Mode", + "light": "Light Mode", + "black": "Black Mode" }, - "enableAnalytics": "تمكين التحليلات", - "enableAnalyticsMsg": "امنح الإذن لجمع التحليلات وإرسال تقارير الأعطال لتحسين التطبيق", - "autoStart": "بدء عند تسجيل الدخول", - "silentStart": "بدء مصغر", - "openWorkingDir": "فتح دليل العمل", - "ignoreBatteryOptimizations": "تعطيل تحسين البطارية", - "ignoreBatteryOptimizationsMsg": "إزالة القيود لأداء VPN الأمثل", - "dynamicNotification": "عرض السرعة في الإشعار", - "hapticFeedback": "تغذية مرتدة لمسية", - "autoIpCheck": "التحقق التلقائي من IP الاتصال" + "enableAnalytics": "Enable Analytics", + "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", + "autoStart": "Start At Login", + "silentStart": "Start Minimized", + "openWorkingDir": "Open Working Directory", + "ignoreBatteryOptimizations": "Disable Battery Optimization", + "ignoreBatteryOptimizationsMsg": "Remove Restrictions For Optimal VPN Performance", + "dynamicNotification": "Display Speed in Notification", + "hapticFeedback": "Haptic Feedback", + "autoIpCheck": "Automatically Check Connection IP" }, "advanced": { - "sectionTitle": "متقدم", - "debugMode": "وضع التصحيح", - "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", - "memoryLimit": "حد الذاكرة", - "memoryLimitMsg": "فعّل إذا كنت تواجه أخطاء تفيض الذاكرة. القيمة الافتراضية 0. يرجى الإشارة إلى القيمة بالميجابايت.", - "ping": "الأمر ping", - "pingMsg": "القيمة الافتراضية 1.0.0.1.", - "alwaysOn": "تفعيل دائمًا", - "disableScv": "تعطيل SCV", - "v2FlyService": "خدمة v2Fly", - "disableV2FlyServiceMsg": "تعطيل خدمة v2Fly", - "allowInsecureConnections": "السماح بالاتصالات غير الآمنة", - "trustedCertificates": "شهادات موثوقة" + "sectionTitle": "Advanced", + "debugMode": "Debug Mode", + "debugModeMsg": "Restart the app for applying this change", + "memoryLimit": "Memory Limit", + "memoryLimitMsg": "Enable if you're experiencing out of memory errors or frequent app crash", + "resetTunnel": "Reset VPN Profile" + }, + "network": { + "perAppProxyPageTitle": "Per-App Proxy", + "perAppProxyModes": { + "off": "All", + "offMsg": "Proxy All Apps", + "include": "Proxy", + "includeMsg": "Proxy Only Selected Apps", + "exclude": "Bypass", + "excludeMsg": "Do Not Proxy Selected Apps" + }, + "showSystemApps": "Show System Apps", + "hideSystemApps": "Hide System Apps", + "clearSelection": "Clear Selection" + }, + "geoAssets": { + "pageTitle": "Routing Assets", + "geoip": "GeoIP", + "geosite": "GeoSite", + "version": "Version ${version}", + "fileMissing": "File Missing", + "update": "Update", + "download": "Download", + "failureMsg": "Failed to Update Asset", + "successMsg": "Successfully Updated Asset", + "addRecommended": "Add Recommended Assets", + "missingGeoAssetsMsg": "Selected Routing Assets' files are missing. Either download them or choose existing ones" } }, - "help": { - "pageTitle": "المساعدة", - "documentation": "التوثيق", - "tutorial": "الدروس التعليمية", - "contact": "اتصل بنا" - }, "about": { - "pageTitle": "حول.\nتعريب:م. ابراهيم قاسم", - "termsAndConditions": "الشروط والأحكام", - "privacyPolicy": "سياسة الخصوصية", - "softwareLicenses": "تراخيص البرمجيات", - "version": "الإصدار ${version}" + "pageTitle": "About", + "version": "Version", + "sourceCode": "Source Code", + "telegramChannel": "Telegram Channel", + "checkForUpdate": "Check For Update", + "privacyPolicy": "Privacy Policy", + "termsAndConditions": "Terms and Conditions" + }, + "appUpdate": { + "notAvailableMsg": "Already Using The Latest Version", + "dialogTitle": "Update Available", + "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", + "currentVersionLbl": "Current Version", + "newVersionLbl": "New Version", + "updateNowBtnTxt": "Update Now", + "laterBtnTxt": "Later", + "ignoreBtnTxt": "Ignore" + }, + "tray": { + "dashboard": "Dashboard", + "quit": "Quit", + "open": "Open", + "status": { + "connect": "Connect", + "connecting": "Connecting", + "disconnect": "Disconnect", + "disconnecting": "Disconnecting" + } + }, + "failure": { + "unexpected": "Unexpected Error", + "clash": { + "unexpected": "Unexpected Error", + "core": "Clash Error ${reason}" + }, + "singbox": { + "unexpected": "Unexpected Service Error", + "serviceNotRunning": "Service is Not Running", + "missingPrivilege": "Missing Privilege", + "missingPrivilegeMsg": "VPN Mode Requires Administrator Privileges. Either relaunch the app as administrator or change service mode.", + "missingGeoAssets": "Missing Geo Assets", + "missingGeoAssetsMsg": "Geo Assets Are Missing. Consider changing active asset or download selected one in the settings.", + "invalidConfigOptions": "Invalid Configuration Options", + "invalidConfig": "Invalid Configuration", + "create": "Service Creation Error", + "start": "Service Startup Error" + }, + "connectivity": { + "unexpected": "Unexpected Failure", + "missingVpnPermission": "Missing VPN Permission", + "missingNotificationPermission": "Missing Notification Permission", + "core": "Core Error" + }, + "profiles": { + "unexpected": "Unexpected Error", + "notFound": "Profile Not Found", + "invalidConfig": "Invalid Configs", + "invalidUrl": "Invalid URL" + }, + "connection": { + "unexpected": "Unexpected Connection Error", + "timeout": "Connection Timeout", + "badResponse": "Bad Response", + "connectionError": "Connection Error", + "badCertificate": "Bad Certificate" + }, + "geoAssets": { + "unexpected": "Unexpected Error", + "notUpdate": "No Update Available", + "activeNotFound": "Active Geo Asset Not Found" + } + }, + "play": { + "title": "Hiddify (Preview)", + "short_description": "Auto, SSH, VLESS, VMess, Trojan, Reality, Sing-Box, Clash, XRay, Shadowsocks", + "full_description": "The key goal of Hiddify is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe Support Servers With:\n- Normal V2Ray/XRay Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - More and more\n\nSupport:\n- All Protocols Supported by Sing-Box \n- VLESS + XTLS Reality, Vision\n- VMess\n- Trojan\n- ShoadowSocks\n- Reality\n- WireGuard\n- V2Ray\n- Hysteria2\n- TUICv5\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source Sing-Box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." + }, + "connection": { + "tapToConnect": "Tap To Connect", + "connecting": "Connecting", + "disconnecting": "Disconnecting", + "connected": "Connected", + "reconnect": "Reconnect", + "connectAnyWay": "Connect", + "experimentalNotice": "Experimental Features In Use", + "experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.", + "disableExperimentalNotice": "Don't Show Again", + "reconnectMsg": "Reconnect for Changes to Take Effect" + }, + "config": { + "resetBtn": "Reset Options", + "serviceMode": "Service Mode", + "quickSettings": "Quick Settings", + "setupWarp": "Setup WARP", + "allOptions": "All Config Options", + "serviceModes": { + "proxy": "Proxy Service Only", + "systemProxy": "Set System Proxy", + "tun": "VPN", + "tunService": "VPN Service" + }, + "shortServiceModes": { + "proxy": "Proxy", + "systemProxy": "System Proxy", + "tun": "VPN", + "tunService": "VPN Service" + }, + "section": { + "route": "Route Options", + "dns": "DNS Options", + "inbound": "Inbound Options", + "mux": "MultiPlexer", + "outbound": "Outbound Options", + "tlsTricks": "TLS Tricks", + "warp": "WARP Options", + "misc": "Misc Options" + }, + "warpConsent": { + "title": "Cloudflare WARP Consent", + "description(rich)": "Cloudflare WARP is a Free WireGuard VPN Provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}." + }, + "generateWarpConfig": "Generate WARP Config", + "missingWarpConfig": "Missing WARP Config", + "warpConfigGenerated": "WARP Config Generated", + "pageTitle": "Config Options", + "logLevel": "Log Level", + "resolveDestination": "Resolve Destination", + "ipv6Mode": "IPv6 Route", + "ipv6Modes": { + "disable": "Disable", + "enable": "Enable", + "prefer": "Preferred", + "only": "Exclusive" + }, + "remoteDnsAddress": "Remote DNS", + "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", + "directDnsAddress": "Direct DNS", + "directDnsDomainStrategy": "Direct DNS Domain Strategy", + "mixedPort": "Mixed Port", + "tproxyPort": "Transparent Proxy Port", + "localDnsPort": "Local DNS Port", + "allowConnectionFromLan": "Share VPN in Local Network", + "tunImplementation": "TUN Implementation", + "mtu": "MTU", + "connectionTestUrl": "Connection Test URL", + "urlTestInterval": "URL Test Interval", + "enableClashApi": "Enable Clash API", + "clashApiPort": "Clash API Port", + "enableTun": "Enable TUN", + "setSystemProxy": "Set System Proxy", + "enableDnsRouting": "Enable DNS Routing", + "enableFakeDns": "Enable Fake DNS", + "bypassLan": "Bypass Lan", + "strictRoute": "Strict Route", + "enableTlsFragment": "Enable TLS Fragment", + "tlsFragmentSize": "TLS Fragment Size", + "tlsFragmentSleep": "TLS Fragment Sleep", + "enableTlsMixedSniCase": "Enable TLS Mixed SNI Case", + "enableTlsPadding": "Enable TLS Padding", + "tlsPaddingSize": "TLS Padding", + "enableMux": "Enable Mux", + "muxProtocol": "Mux Protocol", + "muxMaxStreams": "Max Concurrent Streams", + "enableWarp": "Enable WARP", + "warpDetourMode": "Detour Mode", + "warpDetourModes": { + "proxyOverWarp": "Detour Proxies Through WARP", + "warpOverProxy": "Detour WARP Through Proxies", + "inbound": "Detour WARP Through Proxies", + "outbound": "Detour Proxies Through WARP" + }, + "warpLicenseKey": "License Key", + "warpCleanIp": "Clean IP", + "warpPort": "Port", + "warpNoise": "Noise Count", + "warpNoiseDelay": "Noise Delay" } } From f6584eba6550ff737749541f90668ac51dc33cc0 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:53:29 +0300 Subject: [PATCH 15/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 606 +++++++++++------------ 1 file changed, 303 insertions(+), 303 deletions(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index e4f24d7e..c62ebe42 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -1,413 +1,413 @@ { "general": { "appTitle": "Hiddify", - "reset": "Reset", + "reset": "إعادة تعيين", "toggle": { - "enabled": "Enabled", - "disabled": "Disabled" + "enabled": "مُفعّل", + "disabled": "غير مُفعّل" }, "state": { - "disable": "Disable" + "disable": "تعطيل" }, - "sort": "Sort", - "sortBy": "Sort by", - "addToClipboard": "Add To Clipboard", - "notSet": "Not Set", - "agree": "Agree", - "decline": "Decline", - "unknown": "Unknown", - "hidden": "Hidden", - "timeout": "Timeout", - "clipboardExportSuccessMsg": "Added To Clipboard", - "showMore": "Show More", - "showLess": "Show Less", - "openAppSettings": "Open App Settings", - "grantPermission": "Grant Permission" + "sort": "فرز", + "sortBy": "فرز حسب", + "addToClipboard": "إضافة إلى الحافظة", + "notSet": "غير مُحدد", + "agree": "موافقة", + "decline": "رفض", + "unknown": "غير معروف", + "hidden": "مُخفي", + "timeout": "انتهاء الوقت", + "clipboardExportSuccessMsg": "تمت إضافة البيانات إلى الحافظة", + "showMore": "عرض المزيد", + "showLess": "عرض أقل", + "openAppSettings": "فتح إعدادات التطبيق", + "grantPermission": "منح الإذن" }, "intro": { - "termsAndPolicyCaution(rich)": "By Continuing You Agree With ${tap(@:about.termsAndConditions)}", - "start": "Start" + "termsAndPolicyCaution(rich)": "بمواصلة استخدامك، فإنك توافق على ${tap(@:about.termsAndConditions)}", + "start": "ابدأ" }, "home": { - "pageTitle": "Home", - "emptyProfilesMsg": "Begin by Adding a Subscription Profile", - "noActiveProfileMsg": "Choose a Profile" + "pageTitle": "الصفحة الرئيسية", + "emptyProfilesMsg": "ابدأ بإضافة ملف تعريف اشتراك", + "noActiveProfileMsg": "اختر ملف تعريف" }, "stats": { - "traffic": "Traffic", - "trafficLive": "Live Traffic", - "trafficTotal": "Total Traffic", - "uplink": "Uplink", - "downlink": "Downlink", - "connection": "Connection", - "speed": "Speed", - "totalTransferred": "Total Transferred" + "traffic": "حركة المرور", + "trafficLive": "حركة المرور الحية", + "trafficTotal": "إجمالي حركة المرور", + "uplink": "الصعود", + "downlink": "الهبوط", + "connection": "الاتصال", + "speed": "السرعة", + "totalTransferred": "إجمالي البيانات المنقولة" }, "profile": { - "overviewPageTitle": "Profiles", - "detailsPageTitle": "Profile", - "activeProfileNameSemanticLabel": "Active Profile Name: \"${name}\".", - "activeProfileBtnSemanticLabel": "View All Profiles", - "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as Active Profile", + "overviewPageTitle": "الملفات الشخصية", + "detailsPageTitle": "ملف التعريف", + "activeProfileNameSemanticLabel": "اسم ملف التعريف النشط: \"${name}\".", + "activeProfileBtnSemanticLabel": "عرض جميع ملفات التعريف", + "nonActiveProfileBtnSemanticLabel": "اختر \"${name}\" كملف تعريف نشط", "subscription": { - "traffic": "Traffic", - "updatedTimeAgo": "Updated ${timeago}", - "remainingDuration": "${duration} Days Remaining", - "remainingTrafficSemanticLabel": "${consumed} of ${total} Traffic Consumed", - "expired": "Expired", - "noTraffic": "Out of Quota", - "upload": "Upload", - "download": "Download", - "total": "Total Traffic", - "expireDate": "Expire Date" + "traffic": "حركة المرور", + "updatedTimeAgo": "تم التحديث قبل ${timeago}", + "remainingDuration": "تبقى ${duration} يومًا", + "remainingTrafficSemanticLabel": "استُهلك ${consumed} من أصل ${total} حركة مرور", + "expired": "منتهي الصلاحية", + "noTraffic": "غير متاح", + "upload": "التحميل", + "download": "التنزيل", + "total": "إجمالي حركة المرور", + "expireDate": "تاريخ انتهاء الصلاحية" }, "sortBy": { - "lastUpdate": "Recently Updated", - "name": "Alphabetically" + "lastUpdate": "تم التحديث مؤخرًا", + "name": "أبجديًا" }, "add": { - "buttonText": "New Profile", - "shortBtnTxt": "New Profile", - "fromClipboard": "Add From Clipboard", - "scanQr": "Scan QR Code", + "buttonText": "ملف تعريف جديد", + "shortBtnTxt": "ملف تعريف جديد", + "fromClipboard": "إضافة من الحافظة", + "scanQr": "مسح رمز الاستجابة السريعة", "qrScanner": { - "permissionDeniedError": "Permission Denied", - "unexpectedError": "Something Went Wrong", - "torchSemanticLabel": "Flash Light", - "facingSemanticLabel": "Camera Facing", - "permissionRequest": "Permission to camera to scan QR Code" + "permissionDeniedError": "تم رفض الإذن", + "unexpectedError": "حدث خطأ ما", + "torchSemanticLabel": "ضوء فلاش", + "facingSemanticLabel": "اتجاه الكاميرا", + "permissionRequest": "الإذن للكاميرا لمسح رمز الاستجابة السريعة" }, - "manually": "Manual Entry", - "addingProfileMsg": "Adding Profile", - "failureMsg": "Failed to Add Profile" + "manually": "إدخال يدوي", + "addingProfileMsg": "إضافة ملف التعريف", + "failureMsg": "فشل في إضافة ملف التعريف" }, "update": { - "buttonTxt": "Update", - "tooltip": "Update Profile", - "updateSubscriptions": "Update Subscriptions", - "failureMsg": "Failed to Update Profile", - "successMsg": "Profile Updated Successfully", - "namedFailureMsg": "Failed to Update \"${name}\"", - "namedSuccessMsg": "\"${name}\" Updated Successfully" + "buttonTxt": "تحديث", + "tooltip": "تحديث ملف التعريف", + "updateSubscriptions": "تحديث الاشتراكات", + "failureMsg": "فشل في تحديث ملف التعريف", + "successMsg": "تم تحديث ملف التعريف بنجاح", + "namedFailureMsg": "فشل في تحديث \"${name}\"", + "namedSuccessMsg": "تم تحديث \"${name}\" بنجاح" }, "share": { - "buttonText": "Share", - "exportToClipboardSuccess": "Exported to Clipboard", - "exportSubLinkToClipboard": "Export Subscription Link to Clipboard", - "subLinkQrCode": "Subscription Link QR Code", - "exportConfigToClipboard": "Export Configuration to Clipboard", - "exportConfigToClipboardSuccess": "Configuration Copied to Clipboard" + "buttonText": "مشاركة", + "exportToClipboardSuccess": "تم تصدير البيانات إلى الحافظة", + "exportSubLinkToClipboard": "تصدير رابط الاشتراك إلى الحافظة", + "subLinkQrCode": "رمز الاستجابة السريعة لرابط الاشتراك", + "exportConfigToClipboard": "تصدير التكوين إلى الحافظة", + "exportConfigToClipboardSuccess": "تم نسخ التكوين إلى الحافظة" }, "edit": { - "buttonTxt": "Edit", - "selectActiveTxt": "Select Active Profile" + "buttonTxt": "تحرير", + "selectActiveTxt": "اختر ملف التعريف النشط" }, "delete": { - "buttonTxt": "Delete", - "confirmationMsg": "Delete Profile Permanently?", - "successMsg": "Profile Deleted Successfully" + "buttonTxt": "حذف", + "confirmationMsg": "حذف ملف التعريف نهائياً؟", + "successMsg": "تم حذف ملف التعريف بنجاح" }, "save": { - "buttonText": "Save", - "successMsg": "Profile Saved Successfully", - "failureMsg": "Failed to Save Profile" + "buttonText": "حفظ", + "successMsg": "تم حفظ ملف التعريف بنجاح", + "failureMsg": "فشل في حفظ ملف التعريف" }, "detailsForm": { - "nameLabel": "Name", - "nameHint": "Profile Name", - "urlLabel": "URL", - "urlHint": "Full Config URL", - "emptyNameMsg": "Name Is Required", - "invalidUrlMsg": "Invalid URL", - "lastUpdate": "Last Update", - "updateInterval": "Auto Update", - "updateIntervalDialogTitle": "Auto Update Interval (in Hours)" + "nameLabel": "الاسم", + "nameHint": "اسم ملف التعريف", + "urlLabel": "العنوان", + "urlHint": "عنوان URL للتكوين الكامل", + "emptyNameMsg": "الاسم مطلوب", + "invalidUrlMsg": "عنوان URL غير صالح", + "lastUpdate": "آخر تحديث", + "updateInterval": "التحديث التلقائي", + "updateIntervalDialogTitle": "مُدة التحديث التلقائي (بالساعات)" } }, "proxies": { - "pageTitle": "Proxies", - "emptyProxiesMsg": "No Proxies Available", - "delayTestTooltip": "Test Delay", - "sortTooltip": "Sort Proxies", - "checkIp": "Check IP", - "unknownIp": "Unknown IP", + "pageTitle": "الخوادم الوكيلية", + "emptyProxiesMsg": "لا توجد خوادم وكيلية متاحة", + "delayTestTooltip": "اختبار التأخير", + "sortTooltip": "فرز الخوادم الوكيلية", + "checkIp": "تحقق من عنوان IP", + "unknownIp": "عنوان IP غير معروف", "sortOptions": { - "unsorted": "Default", - "name": "Alphabetically", - "delay": "By Delay" + "unsorted": "افتراضي", + "name": "أبجديًا", + "delay": "حسب التأخير" }, - "activeProxySemanticLabel": "Active Proxy", + "activeProxySemanticLabel": "الخادم الوكيل النشط", "delaySemantics": { - "result": "Delay: ${delay}ms", - "timeout": "Delay Test Timeout", - "testing": "Delay: Testing..." + "result": "التأخير: ${delay} مللي ثانية", + "timeout": "انتهاء الوقت في اختبار التأخير", + "testing": "التأخير: قيد الاختبار..." }, "ipInfoSemantics": { - "address": "IP Address", - "country": "Country" + "address": "عنوان IP", + "country": "الدولة" } }, "logs": { - "pageTitle": "Logs", - "filterHint": "Filter", - "allLevelsFilter": "All", - "shareCoreLogs": "Share Core Logs", - "shareAppLogs": "Share App Logs", - "pauseTooltip": "Pause", - "resumeTooltip": "Resume", - "clearTooltip": "Clear" + "pageTitle": "السجلات", + "filterHint": "تصفية", + "allLevelsFilter": "الكل", + "shareCoreLogs": "مشاركة سجلات النواة", + "shareAppLogs": "مشاركة سجلات التطبيق", + "pauseTooltip": "إيقاف مؤقت", + "resumeTooltip": "استئناف", + "clearTooltip": "مسح" }, "settings": { - "pageTitle": "Settings", - "requiresRestartMsg": "For this to take effect restart the app", - "experimental": "Experimental", - "experimentalMsg": "Features with Experimental flag are still in development and might cause issues.", - "exportOptions": "Export Options to Clipboard", - "exportAllOptions": "Export Options to Clipboard (Debug)", - "importOptions": "Import Options From Clipboard", - "importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?", + "pageTitle": "الإعدادات", + "requiresRestartMsg": "لتطبيق هذه التغييرات، أعد تشغيل التطبيق", + "experimental": "تجريبي", + "experimentalMsg": "الميزات ذات العلامة التجريبية لا تزال قيد التطوير وقد تسبب مشاكل.", + "exportOptions": "تصدير الخيارات إلى الحافظة", + "exportAllOptions": "تصدير جميع الخيارات إلى الحافظة (تصحيح الأخطاء)", + "importOptions": "استيراد الخيارات من الحافظة", + "importOptionsMsg": "سيؤدي هذا إلى إعادة كتابة جميع خيارات التكوين بالقيم المحددة. هل أنت متأكد؟", "general": { - "sectionTitle": "General", - "locale": "Language", - "region": "Region", - "regionMsg": "Helps set default options to bypass domestic addresses", + "sectionTitle": "عام", + "locale": "اللغة", + "region": "المنطقة", + "regionMsg": "يساعد على تعيين الخيارات الافتراضية لتجاوز العناوين المحلية", "regions": { - "ir": "Iran (ir)", - "cn": "China (cn)", - "ru": "Russia (ru)", - "af": "Afghanistan (af)", - "other": "Other" + "ir": "إيران (ir)", + "cn": "الصين (cn)", + "ru": "روسيا (ru)", + "af": "أفغانستان (af)", + "other": "أخرى" }, - "themeMode": "Theme Mode", + "themeMode": "وضع السمة", "themeModes": { - "system": "Follow System Theme", - "dark": "Dark Mode", - "light": "Light Mode", - "black": "Black Mode" + "system": "اتباع سمة النظام", + "dark": "الوضع الداكن", + "light": "الوضع الفاتح", + "black": "الوضع الأسود" }, - "enableAnalytics": "Enable Analytics", - "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", - "autoStart": "Start At Login", - "silentStart": "Start Minimized", - "openWorkingDir": "Open Working Directory", - "ignoreBatteryOptimizations": "Disable Battery Optimization", - "ignoreBatteryOptimizationsMsg": "Remove Restrictions For Optimal VPN Performance", - "dynamicNotification": "Display Speed in Notification", - "hapticFeedback": "Haptic Feedback", - "autoIpCheck": "Automatically Check Connection IP" + "enableAnalytics": "تمكين التحليلات", + "enableAnalyticsMsg": "منح الإذن بجمع التحليلات وإرسال تقارير الأعطال لتحسين التطبيق", + "autoStart": "بدء التشغيل عند تسجيل الدخول", + "silentStart": "بدء التشغيل مُصغر", + "openWorkingDir": "فتح دليل العمل", + "ignoreBatteryOptimizations": "تعطيل تحسينات البطارية", + "ignoreBatteryOptimizationsMsg": "إزالة القيود للحصول على أفضل أداء VPN", + "dynamicNotification": "عرض السرعة في الإشعار", + "hapticFeedback": "ردود فعل اللمس", + "autoIpCheck": "التحقق من عنوان IP للاتصال تلقائيًا" }, "advanced": { - "sectionTitle": "Advanced", - "debugMode": "Debug Mode", - "debugModeMsg": "Restart the app for applying this change", - "memoryLimit": "Memory Limit", - "memoryLimitMsg": "Enable if you're experiencing out of memory errors or frequent app crash", - "resetTunnel": "Reset VPN Profile" + "sectionTitle": "متقدم", + "debugMode": "وضع تصحيح الأخطاء", + "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", + "memoryLimit": "حد الذاكرة", + "memoryLimitMsg": "قم بتمكين هذه الميزة إذا كنت تواجه أخطاء "عدم كفاية الذاكرة" أو تعطل التطبيق بشكل متكرر", + "resetTunnel": "إعادة تعيين ملف تعريف VPN" }, "network": { - "perAppProxyPageTitle": "Per-App Proxy", + "perAppProxyPageTitle": "الوكيل لكل تطبيق", "perAppProxyModes": { - "off": "All", - "offMsg": "Proxy All Apps", - "include": "Proxy", - "includeMsg": "Proxy Only Selected Apps", - "exclude": "Bypass", - "excludeMsg": "Do Not Proxy Selected Apps" + "off": "الكل", + "offMsg": "وكيل جميع التطبيقات", + "include": "وكيل", + "includeMsg": "وكيل التطبيقات المحددة فقط", + "exclude": "تجاوز", + "excludeMsg": "لا تستخدم الوكيل للتطبيقات المحددة" }, - "showSystemApps": "Show System Apps", - "hideSystemApps": "Hide System Apps", - "clearSelection": "Clear Selection" + "showSystemApps": "عرض تطبيقات النظام", + "hideSystemApps": "إخفاء تطبيقات النظام", + "clearSelection": "مسح الاختيار" }, "geoAssets": { - "pageTitle": "Routing Assets", + "pageTitle": "أصول التوجيه", "geoip": "GeoIP", "geosite": "GeoSite", - "version": "Version ${version}", - "fileMissing": "File Missing", - "update": "Update", - "download": "Download", - "failureMsg": "Failed to Update Asset", - "successMsg": "Successfully Updated Asset", - "addRecommended": "Add Recommended Assets", - "missingGeoAssetsMsg": "Selected Routing Assets' files are missing. Either download them or choose existing ones" + "version": "الإصدار ${version}", + "fileMissing": "الملف مفقود", + "update": "تحديث", + "download": "تنزيل", + "failureMsg": "فشل في تحديث الأصل", + "successMsg": "تم تحديث الأصل بنجاح", + "addRecommended": "إضافة الأصول الموصى بها", + "missingGeoAssetsMsg": "ملفات أصول التوجيه المحددة مفقودة. قم بتنزيلها أو اختر ملفات موجودة" } }, "about": { - "pageTitle": "About", - "version": "Version", - "sourceCode": "Source Code", - "telegramChannel": "Telegram Channel", - "checkForUpdate": "Check For Update", - "privacyPolicy": "Privacy Policy", - "termsAndConditions": "Terms and Conditions" + "pageTitle": "حول\nتعريب:م. ابراهيم قاسم", + "version": "الإصدار", + "sourceCode": "رمز المصدر", + "telegramChannel": "قناة Telegram", + "checkForUpdate": "التحقق من وجود تحديث", + "privacyPolicy": "سياسة الخصوصية", + "termsAndConditions": "الشروط والأحكام" }, "appUpdate": { - "notAvailableMsg": "Already Using The Latest Version", - "dialogTitle": "Update Available", - "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", - "currentVersionLbl": "Current Version", - "newVersionLbl": "New Version", - "updateNowBtnTxt": "Update Now", - "laterBtnTxt": "Later", - "ignoreBtnTxt": "Ignore" + "notAvailableMsg": "أنت تستخدم أحدث إصدار بالفعل", + "dialogTitle": "تحديث متاح", + "updateMsg": "إصدار جديد من @:general.appTitle متاح. هل تريد التحديث الآن؟", + "currentVersionLbl": "الإصدار الحالي", + "newVersionLbl": "الإصدار الجديد", + "updateNowBtnTxt": "تحديث الآن", + "laterBtnTxt": "لاحقًا", + "ignoreBtnTxt": "تجاهل" }, "tray": { - "dashboard": "Dashboard", - "quit": "Quit", - "open": "Open", + "dashboard": "لوحة التحكم", + "quit": "إنهاء", + "open": "فتح", "status": { - "connect": "Connect", - "connecting": "Connecting", - "disconnect": "Disconnect", - "disconnecting": "Disconnecting" + "connect": "الاتصال", + "connecting": "جار الاتصال", + "disconnect": "فصم الاتصال", + "disconnecting": "جار فصم الاتصال" } }, "failure": { - "unexpected": "Unexpected Error", + "unexpected": "خطأ غير متوقع", "clash": { - "unexpected": "Unexpected Error", - "core": "Clash Error ${reason}" + "unexpected": "خطأ غير متوقع", + "core": "خطأ في Clash ${reason}" }, "singbox": { - "unexpected": "Unexpected Service Error", - "serviceNotRunning": "Service is Not Running", - "missingPrivilege": "Missing Privilege", - "missingPrivilegeMsg": "VPN Mode Requires Administrator Privileges. Either relaunch the app as administrator or change service mode.", - "missingGeoAssets": "Missing Geo Assets", - "missingGeoAssetsMsg": "Geo Assets Are Missing. Consider changing active asset or download selected one in the settings.", - "invalidConfigOptions": "Invalid Configuration Options", - "invalidConfig": "Invalid Configuration", - "create": "Service Creation Error", - "start": "Service Startup Error" + "unexpected": "خطأ في الخدمة غير متوقع", + "serviceNotRunning": "الخدمة غير قيد التشغيل", + "missingPrivilege": "غياب الامتياز", + "missingPrivilegeMsg": "وضع VPN يتطلب امتيازات المسؤول. أعد تشغيل التطبيق كمسؤول أو غيّر وضع الخدمة.", + "missingGeoAssets": "أصول Geo مفقودة", + "missingGeoAssetsMsg": "أصول Geo مفقودة. فكر في تغيير الأصل النشط أو تنزيل الأصل المحدد في الإعدادات.", + "invalidConfigOptions": "خيارات التكوين غير صحيحة", + "invalidConfig": "تكوين غير صالح", + "create": "خطأ في إنشاء الخدمة", + "start": "خطأ في بدء تشغيل الخدمة" }, "connectivity": { - "unexpected": "Unexpected Failure", - "missingVpnPermission": "Missing VPN Permission", - "missingNotificationPermission": "Missing Notification Permission", - "core": "Core Error" + "unexpected": "فشل غير متوقع", + "missingVpnPermission": "غياب إذن VPN", + "missingNotificationPermission": "غياب إذن الإشعارات", + "core": "خطأ في النواة" }, "profiles": { - "unexpected": "Unexpected Error", - "notFound": "Profile Not Found", - "invalidConfig": "Invalid Configs", - "invalidUrl": "Invalid URL" + "unexpected": "خطأ غير متوقع", + "notFound": "لم يتم العثور على ملف التعريف", + "invalidConfig": "تكوينات غير صحيحة", + "invalidUrl": "عنوان URL غير صالح" }, "connection": { - "unexpected": "Unexpected Connection Error", - "timeout": "Connection Timeout", - "badResponse": "Bad Response", - "connectionError": "Connection Error", - "badCertificate": "Bad Certificate" + "unexpected": "خطأ في الاتصال غير متوقع", + "timeout": "انتهاء الوقت في الاتصال", + "badResponse": "استجابة سيئة", + "connectionError": "خطأ في الاتصال", + "badCertificate": "شهادة غير صالحة" }, "geoAssets": { - "unexpected": "Unexpected Error", - "notUpdate": "No Update Available", - "activeNotFound": "Active Geo Asset Not Found" + "unexpected": "خطأ غير متوقع", + "notUpdate": "لا يوجد تحديث متاح", + "activeNotFound": "لم يتم العثور على أصل Geo النشط" } }, "play": { - "title": "Hiddify (Preview)", + "title": "Hiddify (معاينة)", "short_description": "Auto, SSH, VLESS, VMess, Trojan, Reality, Sing-Box, Clash, XRay, Shadowsocks", - "full_description": "The key goal of Hiddify is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe Support Servers With:\n- Normal V2Ray/XRay Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - More and more\n\nSupport:\n- All Protocols Supported by Sing-Box \n- VLESS + XTLS Reality, Vision\n- VMess\n- Trojan\n- ShoadowSocks\n- Reality\n- WireGuard\n- V2Ray\n- Hysteria2\n- TUICv5\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source Sing-Box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." + "full_description": "الهدف الرئيسي لـ Hiddify هو توفير عميل نفق آمن وسهل الاستخدام وكفاءة. يمكّنك من توجيه جميع حركة المرور أو حركة المرور من التطبيق المحدد إلى خادم بعيد من اختيارك، باستخدام إذن VPN-Service. \n\nملاحظة: لا نوفر أي خادم، ويتعين على المستخدمين ضمان بقاء أنشطتهم عبر الإنترنت خاصة باستخدام خادمهم المخصص أو الخوادم الموثوقة. \n \nندعم الخوادم مع:\n- رابط اشتراك V2Ray/XRay عادي \n- رابط اشتراك Clash \n- رابط اشتراك Sing-Box \n\nما هي ميزاتنا الفريدة؟\n - سهل الاستخدام \n - مُحسّن وسريع \n - اختيار أدنى Ping تلقائيًا \n - عرض معلومات استخدام المستخدم \n - استيراد sublink بسهولة بنقرة واحدة باستخدام deeplinking \n - مجاني وخالي من الإعلانات \n - تبديل sublinks بسهولة \n - المزيد والمزيد \n\nالدعم:\n- جميع البروتوكولات التي تدعمها Sing-Box \n- VLESS + XTLS Reality, Vision \n- VMess \n- Trojan \n- ShoadowSocks \n- Reality \n- WireGuard \n- V2Ray \n- Hysteria2 \n- TUICv5 \n- SSH \n- ShadowTLS \n\n\nرمز المصدر موجود في https://github.com/hiddify/Hiddify-Next \nتعتمد نواة التطبيق على Sing-Box مفتوحة المصدر.\n\nوصف الإذن:\n- VPN Service: نظرًا لأن هدف هذا التطبيق هو توفير عميل نفق آمن وسهل الاستخدام وكفاءة، نحتاج إلى هذا الإذن لنتمكن من توجيه حركة المرور عبر النفق إلى الخادم البعيد. \n- QUERY ALL PACKAGES: يستخدم هذا الإذن للسماح للمستخدمين بتضمين أو استبعاد تطبيقات محددة للأنفاق. \n- RECEIVE BOOT COMPLETED: يمكن تمكين أو تعطيل هذا الإذن من إعدادات التطبيق لتنشيط هذا التطبيق عند تشغيل الجهاز. \n- POST NOTIFICATIONS: هذا الإذن ضروري لأننا نستخدم خدمة المقدمة لضمان تشغيل خدمة VPN بشكل مستمر. \n- هذا التطبيق خالي من الإعلانات. يتم جمع التحليلات وبيانات الأعطال فقط بموافقة صريحة من المستخدم في أول استخدام للتطبيق." }, "connection": { - "tapToConnect": "Tap To Connect", - "connecting": "Connecting", - "disconnecting": "Disconnecting", - "connected": "Connected", - "reconnect": "Reconnect", - "connectAnyWay": "Connect", - "experimentalNotice": "Experimental Features In Use", - "experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.", - "disableExperimentalNotice": "Don't Show Again", - "reconnectMsg": "Reconnect for Changes to Take Effect" + "tapToConnect": "انقر للاتصال", + "connecting": "جار الاتصال", + "disconnecting": "جار فصم الاتصال", + "connected": "متصل", + "reconnect": "أعد الاتصال", + "connectAnyWay": "اتصل", + "experimentalNotice": "ميزات تجريبية قيد الاستخدام", + "experimentalNoticeMsg": "لقد قمت بتمكين بعض الميزات التجريبية التي قد تؤثر على جودة الاتصال وتسبب أخطاء غير متوقعة. يمكنك دائمًا تغيير هذه الخيارات أو إعادة تعيينها من صفحة خيارات التكوين.", + "disableExperimentalNotice": "لا تعرض مرة أخرى", + "reconnectMsg": "أعد الاتصال ليتم تطبيق التغييرات" }, "config": { - "resetBtn": "Reset Options", - "serviceMode": "Service Mode", - "quickSettings": "Quick Settings", - "setupWarp": "Setup WARP", - "allOptions": "All Config Options", + "resetBtn": "إعادة تعيين الخيارات", + "serviceMode": "وضع الخدمة", + "quickSettings": "الإعدادات السريعة", + "setupWarp": "إعداد WARP", + "allOptions": "جميع خيارات التكوين", "serviceModes": { - "proxy": "Proxy Service Only", - "systemProxy": "Set System Proxy", + "proxy": "خدمة الوكيل فقط", + "systemProxy": "تعيين وكيل النظام", "tun": "VPN", - "tunService": "VPN Service" + "tunService": "خدمة VPN" }, "shortServiceModes": { - "proxy": "Proxy", - "systemProxy": "System Proxy", + "proxy": "وكيل", + "systemProxy": "وكيل النظام", "tun": "VPN", - "tunService": "VPN Service" + "tunService": "خدمة VPN" }, "section": { - "route": "Route Options", - "dns": "DNS Options", - "inbound": "Inbound Options", + "route": "خيارات التوجيه", + "dns": "خيارات DNS", + "inbound": "خيارات الوارد", "mux": "MultiPlexer", - "outbound": "Outbound Options", - "tlsTricks": "TLS Tricks", - "warp": "WARP Options", - "misc": "Misc Options" + "outbound": "خيارات الخارج", + "tlsTricks": "حيل TLS", + "warp": "خيارات WARP", + "misc": "خيارات متنوعة" }, "warpConsent": { - "title": "Cloudflare WARP Consent", - "description(rich)": "Cloudflare WARP is a Free WireGuard VPN Provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}." + "title": "موافقة Cloudflare WARP", + "description(rich)": "Cloudflare WARP هو مزود VPN مجاني لـ WireGuard. بتمكين هذا الخيار، فإنك توافق على ${tos(Terms of Service)} و ${privacy(Privacy Policy)} لـ Cloudflare WARP." }, - "generateWarpConfig": "Generate WARP Config", - "missingWarpConfig": "Missing WARP Config", - "warpConfigGenerated": "WARP Config Generated", - "pageTitle": "Config Options", - "logLevel": "Log Level", - "resolveDestination": "Resolve Destination", - "ipv6Mode": "IPv6 Route", + "generateWarpConfig": "إنشاء تكوين WARP", + "missingWarpConfig": "تكوين WARP مفقود", + "warpConfigGenerated": "تم إنشاء تكوين WARP", + "pageTitle": "خيارات التكوين", + "logLevel": "مستوى السجل", + "resolveDestination": "حل الوجهة", + "ipv6Mode": "توجيه IPv6", "ipv6Modes": { - "disable": "Disable", - "enable": "Enable", - "prefer": "Preferred", - "only": "Exclusive" + "disable": "تعطيل", + "enable": "تمكين", + "prefer": "مُفضل", + "only": "حصري" }, - "remoteDnsAddress": "Remote DNS", - "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", - "directDnsAddress": "Direct DNS", - "directDnsDomainStrategy": "Direct DNS Domain Strategy", - "mixedPort": "Mixed Port", - "tproxyPort": "Transparent Proxy Port", - "localDnsPort": "Local DNS Port", - "allowConnectionFromLan": "Share VPN in Local Network", - "tunImplementation": "TUN Implementation", + "remoteDnsAddress": "DNS البعيد", + "remoteDnsDomainStrategy": "استراتيجية مجال DNS البعيد", + "directDnsAddress": "DNS المباشر", + "directDnsDomainStrategy": "استراتيجية مجال DNS المباشر", + "mixedPort": "منفذ مُختلط", + "tproxyPort": "منفذ الوكيل الشفاف", + "localDnsPort": "منفذ DNS المحلي", + "allowConnectionFromLan": "مشاركة VPN على الشبكة المحلية", + "tunImplementation": "تنفيذ TUN", "mtu": "MTU", - "connectionTestUrl": "Connection Test URL", - "urlTestInterval": "URL Test Interval", - "enableClashApi": "Enable Clash API", - "clashApiPort": "Clash API Port", - "enableTun": "Enable TUN", - "setSystemProxy": "Set System Proxy", - "enableDnsRouting": "Enable DNS Routing", - "enableFakeDns": "Enable Fake DNS", - "bypassLan": "Bypass Lan", - "strictRoute": "Strict Route", - "enableTlsFragment": "Enable TLS Fragment", - "tlsFragmentSize": "TLS Fragment Size", - "tlsFragmentSleep": "TLS Fragment Sleep", - "enableTlsMixedSniCase": "Enable TLS Mixed SNI Case", - "enableTlsPadding": "Enable TLS Padding", - "tlsPaddingSize": "TLS Padding", - "enableMux": "Enable Mux", - "muxProtocol": "Mux Protocol", - "muxMaxStreams": "Max Concurrent Streams", - "enableWarp": "Enable WARP", - "warpDetourMode": "Detour Mode", + "connectionTestUrl": "عنوان URL لاختبار الاتصال", + "urlTestInterval": "مُدة اختبار عنوان URL", + "enableClashApi": "تمكين Clash API", + "clashApiPort": "منفذ Clash API", + "enableTun": "تمكين TUN", + "setSystemProxy": "تعيين وكيل النظام", + "enableDnsRouting": "تمكين توجيه DNS", + "enableFakeDns": "تمكين DNS المزيف", + "bypassLan": "تجاوز LAN", + "strictRoute": "توجيه صارم", + "enableTlsFragment": "تمكين تجزئة TLS", + "tlsFragmentSize": "حجم تجزئة TLS", + "tlsFragmentSleep": "وقت تعليق تجزئة TLS", + "enableTlsMixedSniCase": "تمكين خلط حالات SNI في TLS", + "enableTlsPadding": "تمكين حشو TLS", + "tlsPaddingSize": "حشو TLS", + "enableMux": "تمكين Mux", + "muxProtocol": "بروتوكول Mux", + "muxMaxStreams": "أقصى عدد من التدفقات المتزامنة", + "enableWarp": "تمكين WARP", + "warpDetourMode": "وضع التفاف", "warpDetourModes": { - "proxyOverWarp": "Detour Proxies Through WARP", - "warpOverProxy": "Detour WARP Through Proxies", - "inbound": "Detour WARP Through Proxies", - "outbound": "Detour Proxies Through WARP" + "proxyOverWarp": "لف الخوادم الوكيلية عبر WARP", + "warpOverProxy": "لف WARP عبر الخوادم الوكيلية", + "inbound": "لف WARP عبر الخوادم الوكيلية", + "outbound": "لف الخوادم الوكيلية عبر WARP" }, - "warpLicenseKey": "License Key", - "warpCleanIp": "Clean IP", - "warpPort": "Port", - "warpNoise": "Noise Count", - "warpNoiseDelay": "Noise Delay" + "warpLicenseKey": "مفتاح الترخيص", + "warpCleanIp": "عنوان IP نظيف", + "warpPort": "المنفذ", + "warpNoise": "عدد الضوضاء", + "warpNoiseDelay": "تأخير الضوضاء" } } From b867bb7729c9b3214fe84f6bb930e60583cf3dd1 Mon Sep 17 00:00:00 2001 From: ibrahem Qasim Date: Wed, 22 May 2024 06:55:17 +0300 Subject: [PATCH 16/19] Update strings_ar.i18n.json --- assets/translations/strings_ar.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index c62ebe42..d6c00773 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -201,7 +201,7 @@ "debugMode": "وضع تصحيح الأخطاء", "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", "memoryLimit": "حد الذاكرة", - "memoryLimitMsg": "قم بتمكين هذه الميزة إذا كنت تواجه أخطاء "عدم كفاية الذاكرة" أو تعطل التطبيق بشكل متكرر", + "memoryLimitMsg": "قم بتمكين هذه الميزة إذا كنت تواجه أخطاء «عدم كفاية الذاكرة» أو تعطل التطبيق بشكل متكرر", "resetTunnel": "إعادة تعيين ملف تعريف VPN" }, "network": { From 01e692996307e91f1f2587bb285083ea78ad3e63 Mon Sep 17 00:00:00 2001 From: MrIbrahem Date: Wed, 22 May 2024 07:09:41 +0300 Subject: [PATCH 17/19] =?UTF-8?q?chore:=20update=20translations=20with=20F?= =?UTF-8?q?ink=20=F0=9F=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/strings_ar.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/translations/strings_ar.i18n.json b/assets/translations/strings_ar.i18n.json index d6c00773..c92f0d02 100644 --- a/assets/translations/strings_ar.i18n.json +++ b/assets/translations/strings_ar.i18n.json @@ -16,7 +16,7 @@ "agree": "موافقة", "decline": "رفض", "unknown": "غير معروف", - "hidden": "مُخفي", + "hidden": "مخفي", "timeout": "انتهاء الوقت", "clipboardExportSuccessMsg": "تمت إضافة البيانات إلى الحافظة", "showMore": "عرض المزيد", From e2b67c317b2293cc07c589fc1ce5f1e33047e5ca Mon Sep 17 00:00:00 2001 From: hiddify-com <114227601+hiddify-com@users.noreply.github.com> Date: Fri, 31 May 2024 13:41:35 +0200 Subject: [PATCH 18/19] add warp config, update to flutter 1.22 --- .github/workflows/build.yml | 2 +- .vscode/launch.json | 10 ++ .vscode/settings.json | 21 +++ assets/translations/strings_en.i18n.json | 6 +- assets/translations/strings_fa.i18n.json | 4 +- assets/translations/strings_ru.i18n.json | 3 +- assets/translations/strings_zh-CN.i18n.json | 3 +- lib/core/analytics/analytics_controller.dart | 3 +- .../data/config_option_repository.dart | 83 +++++---- .../notifier/warp_option_notifier.dart | 48 +++-- .../overview/config_options_page.dart | 144 +++++---------- .../overview/warp_options_widgets.dart | 14 +- .../widget/quick_settings_modal.dart | 41 +++-- .../profile/add/add_profile_modal.dart | 166 ++++++++++++------ .../profile/notifier/profile_notifier.dart | 19 +- .../overview/proxies_overview_notifier.dart | 73 ++++---- lib/singbox/model/singbox_config_option.dart | 13 +- lib/singbox/service/ffi_singbox_service.dart | 88 ++++------ libcore | 2 +- pubspec.lock | 55 +++--- pubspec.yaml | 10 +- 21 files changed, 423 insertions(+), 385 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db102505..1f01aef9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ on: env: IS_GITHUB_ACTIONS: 1 CHANNEL: "${{ inputs.channel }}" - FLUTTER_VERSION: '3.19.x' + FLUTTER_VERSION: '3.22.x' NDK_VERSION: r26b UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}" TAG_NAME: "${{ inputs.tag-name }}" diff --git a/.vscode/launch.json b/.vscode/launch.json index fa9c6ea9..373f8b4c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,16 @@ { "version": "0.2.0", "configurations": [ + { + "name": "go Package", + "type": "go", + "request": "launch", + "mode": "auto", + "cwd": "./libcore", + "program": "./libcore/cli/main.go", + "args": ["build","-c","a.txt","-d","b.txt","--full-config"] , + "buildFlags": "-tags with_clash_api,with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server" + }, { "name": "Hiddify Dev", "request": "launch", diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..bedb24e9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "dart.lineLength": 250, + "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", + "editor.formatOnSave": true, + "editor.formatOnType": true, + "editor.tabSize": 2, + "editor.rulers": [ + 250 + ], + "editor.detectIndentation": false, + "editor.selectionHighlight": false, + "editor.suggest.snippetsPreventQuickSuggestions": false, + "editor.suggestSelection": "first", + "editor.tabCompletion": "onlySnippets", + "editor.wordBasedSuggestions": "off" + }, + + "html.format.wrapLineLength": 250, + +} \ No newline at end of file diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index e4f24d7e..830c2ff1 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -78,8 +78,10 @@ "permissionRequest": "Permission to camera to scan QR Code" }, "manually": "Manual Entry", + "addWarp": "Add Warp", "addingProfileMsg": "Adding Profile", "failureMsg": "Failed to Add Profile" + }, "update": { "buttonTxt": "Update", @@ -162,8 +164,8 @@ "requiresRestartMsg": "For this to take effect restart the app", "experimental": "Experimental", "experimentalMsg": "Features with Experimental flag are still in development and might cause issues.", - "exportOptions": "Export Options to Clipboard", - "exportAllOptions": "Export Options to Clipboard (Debug)", + "exportOptions": "Copy Anonymous Options to Clipboard", + "exportAllOptions": "Copy All Options to Clipboard", "importOptions": "Import Options From Clipboard", "importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?", "general": { diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 4c17d07b..60dd17c9 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -162,8 +162,8 @@ "requiresRestartMsg": "برای اعمال این تنظیم، برنامه را دوباره راه‌اندازی کنید", "experimental": "آزمایشی", "experimentalMsg": "تنظیماتی که عنوان آزمایشی دارند، هم‌چنان در دست توسعه هستند و فعال‌سازی آن‌ها می‌تواند باعث بروز مشکلاتی شود. ", - "exportOptions": "صادر کردن تنظیمات به کلیپ‌بورد", - "exportAllOptions": "صادر کردن تنظیمات به کلیپ‌بورد (اشکال‌زدایی)", + "exportOptions": "کپی تنظیمات ساده به کلیپ‌بورد", + "exportAllOptions": "کپی همه تنظیمات به کلیپ‌بورد", "importOptions": "وارد کردن تنظیمات از کلیپ‌بورد", "importOptionsMsg": "این اقدام همه‌ی تنظیمات پیکربندی را با مقادیر اولیه بازنویسی می‌کند. آیا مطمئن هستید؟", "general": { diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 85ffe63b..6189c587 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -162,8 +162,7 @@ "requiresRestartMsg": "Чтобы применить изменения, перезапустите приложение.", "experimental": "Экспериментальный", "experimentalMsg": "Функции с флагом «Экспериментально» все еще находятся в разработке и могут вызвать проблемы.", - "exportOptions": "Экспорт параметров в буфер обмена", - "exportAllOptions": "Экспорт параметров в буфер обмена (отладка)", + "importOptions": "Импорт параметров из буфера обмена", "importOptionsMsg": "Это перезапишет все параметры конфига предоставленными значениями. Вы уверены?", "general": { diff --git a/assets/translations/strings_zh-CN.i18n.json b/assets/translations/strings_zh-CN.i18n.json index 4db410c4..1e36a4a0 100644 --- a/assets/translations/strings_zh-CN.i18n.json +++ b/assets/translations/strings_zh-CN.i18n.json @@ -162,8 +162,7 @@ "requiresRestartMsg": "要使其生效,请重新启动应用程序", "experimental": "实验性选项", "experimentalMsg": "带有实验标志的功能仍在开发中,可能会出现问题。", - "exportOptions": "导出选项到剪切板", - "exportAllOptions": "导出选项到剪切板(用于调试)", + "importOptions": "从剪切板导入选项", "importOptionsMsg": "这将使用提供的值重写所有配置选项。您确定吗?", "general": { diff --git a/lib/core/analytics/analytics_controller.dart b/lib/core/analytics/analytics_controller.dart index ad0f7649..a27e6498 100644 --- a/lib/core/analytics/analytics_controller.dart +++ b/lib/core/analytics/analytics_controller.dart @@ -23,8 +23,7 @@ class AnalyticsController extends _$AnalyticsController with AppLogger { return _preferences.getBool(enableAnalyticsPrefKey) ?? true; } - SharedPreferences get _preferences => - ref.read(sharedPreferencesProvider).requireValue; + SharedPreferences get _preferences => ref.read(sharedPreferencesProvider).requireValue; Future enableAnalytics() async { if (state case AsyncData(value: final enabled)) { diff --git a/lib/features/config_option/data/config_option_repository.dart b/lib/features/config_option/data/config_option_repository.dart index 14f5d6cd..bc61a9c3 100644 --- a/lib/features/config_option/data/config_option_repository.dart +++ b/lib/features/config_option/data/config_option_repository.dart @@ -51,8 +51,7 @@ abstract class ConfigOptions { validator: (value) => value.isNotBlank, ); - static final remoteDnsDomainStrategy = - PreferencesNotifier.create( + static final remoteDnsDomainStrategy = PreferencesNotifier.create( "remote-dns-domain-strategy", DomainStrategy.auto, mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value), @@ -65,8 +64,7 @@ abstract class ConfigOptions { validator: (value) => value.isNotBlank, ); - static final directDnsDomainStrategy = - PreferencesNotifier.create( + static final directDnsDomainStrategy = PreferencesNotifier.create( "direct-dns-domain-strategy", DomainStrategy.auto, mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value), @@ -91,8 +89,7 @@ abstract class ConfigOptions { validator: (value) => isPort(value.toString()), ); - static final tunImplementation = - PreferencesNotifier.create( + static final tunImplementation = PreferencesNotifier.create( "tun-implementation", TunImplementation.mixed, mapFrom: TunImplementation.values.byName, @@ -101,8 +98,7 @@ abstract class ConfigOptions { static final mtu = PreferencesNotifier.create("mtu", 9000); - static final strictRoute = - PreferencesNotifier.create("strict-route", true); + static final strictRoute = PreferencesNotifier.create("strict-route", true); static final connectionTestUrl = PreferencesNotifier.create( "connection-test-url", @@ -128,8 +124,7 @@ abstract class ConfigOptions { validator: (value) => isPort(value.toString()), ); - static final bypassLan = - PreferencesNotifier.create("bypass-lan", false); + static final bypassLan = PreferencesNotifier.create("bypass-lan", false); static final allowConnectionFromLan = PreferencesNotifier.create( "allow-connection-from-lan", @@ -156,16 +151,14 @@ abstract class ConfigOptions { false, ); - static final tlsFragmentSize = - PreferencesNotifier.create( + static final tlsFragmentSize = PreferencesNotifier.create( "tls-fragment-size", const OptionalRange(min: 1, max: 500), mapFrom: OptionalRange.parse, mapTo: const OptionalRangeJsonConverter().toJson, ); - static final tlsFragmentSleep = - PreferencesNotifier.create( + static final tlsFragmentSleep = PreferencesNotifier.create( "tls-fragment-sleep", const OptionalRange(min: 0, max: 500), mapFrom: OptionalRange.parse, @@ -182,8 +175,7 @@ abstract class ConfigOptions { false, ); - static final tlsPaddingSize = - PreferencesNotifier.create( + static final tlsPaddingSize = PreferencesNotifier.create( "tls-padding-size", const OptionalRange(min: 1, max: 1500), mapFrom: OptionalRange.parse, @@ -218,8 +210,7 @@ abstract class ConfigOptions { false, ); - static final warpDetourMode = - PreferencesNotifier.create( + static final warpDetourMode = PreferencesNotifier.create( "warp-detour-mode", WarpDetourMode.proxyOverWarp, mapFrom: WarpDetourMode.values.byName, @@ -230,16 +221,28 @@ abstract class ConfigOptions { "warp-license-key", "", ); + static final warp2LicenseKey = PreferencesNotifier.create( + "warp2s-license-key", + "", + ); static final warpAccountId = PreferencesNotifier.create( "warp-account-id", "", ); + static final warp2AccountId = PreferencesNotifier.create( + "warp2-account-id", + "", + ); static final warpAccessToken = PreferencesNotifier.create( "warp-access-token", "", ); + static final warp2AccessToken = PreferencesNotifier.create( + "warp2-access-token", + "", + ); static final warpCleanIp = PreferencesNotifier.create( "warp-clean-ip", @@ -259,8 +262,7 @@ abstract class ConfigOptions { mapTo: const OptionalRangeJsonConverter().toJson, ); - static final warpNoiseDelay = - PreferencesNotifier.create( + static final warpNoiseDelay = PreferencesNotifier.create( "warp-noise-delay", const OptionalRange(min: 20, max: 200), mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true), @@ -271,6 +273,10 @@ abstract class ConfigOptions { "warp-wireguard-config", "", ); + static final warp2WireguardConfig = PreferencesNotifier.create( + "warp2-wireguard-config", + "", + ); static final hasExperimentalFeatures = Provider.autoDispose( (ref) { @@ -278,13 +284,7 @@ abstract class ConfigOptions { if (PlatformUtils.isDesktop && mode == ServiceMode.tun) { return true; } - if (ref.watch(enableTlsFragment) || - ref.watch(enableTlsMixedSniCase) || - ref.watch(enableTlsPadding) || - ref.watch(enableMux) || - ref.watch(enableWarp) || - ref.watch(bypassLan) || - ref.watch(allowConnectionFromLan)) { + if (ref.watch(enableTlsFragment) || ref.watch(enableTlsMixedSniCase) || ref.watch(enableTlsPadding) || ref.watch(enableMux) || ref.watch(enableWarp) || ref.watch(bypassLan) || ref.watch(allowConnectionFromLan)) { return true; } @@ -298,10 +298,13 @@ abstract class ConfigOptions { "warp.access-token", "warp.account-id", "warp.wireguard-config", + "warp2.license-key", + "warp2.access-token", + "warp2.account-id", + "warp2.wireguard-config", }; - static final Map> - preferences = { + static final Map> preferences = { "service-mode": serviceMode, "log-level": logLevel, "resolve-destination": resolveDestination, @@ -348,6 +351,10 @@ abstract class ConfigOptions { "warp.noise": warpNoise, "warp.noise-delay": warpNoiseDelay, "warp.wireguard-config": warpWireguardConfig, + "warp2.license-key": warp2LicenseKey, + "warp2.account-id": warp2AccountId, + "warp2.access-token": warp2AccessToken, + "warp2.wireguard-config": warp2WireguardConfig, }; static final singboxConfigOptions = FutureProvider( @@ -386,8 +393,7 @@ abstract class ConfigOptions { }; final geoAssetsRepo = await ref.watch(geoAssetRepositoryProvider.future); - final geoAssets = - await geoAssetsRepo.getActivePair().getOrElse((l) => throw l).run(); + final geoAssets = await geoAssetsRepo.getActivePair().getOrElse((l) => throw l).run(); final mode = ref.watch(serviceMode); return SingboxConfigOption( @@ -443,6 +449,18 @@ abstract class ConfigOptions { noise: ref.watch(warpNoise), noiseDelay: ref.watch(warpNoiseDelay), ), + warp2: SingboxWarpOption( + enable: ref.watch(enableWarp), + mode: ref.watch(warpDetourMode), + wireguardConfig: ref.watch(warp2WireguardConfig), + licenseKey: ref.watch(warp2LicenseKey), + accountId: ref.watch(warp2AccountId), + accessToken: ref.watch(warp2AccessToken), + cleanIp: ref.watch(warpCleanIp), + cleanPort: ref.watch(warpPort), + noise: ref.watch(warpNoise), + noiseDelay: ref.watch(warpNoiseDelay), + ), geoipPath: ref.watch(geoAssetPathResolverProvider).relativePath( geoAssets.geoip.providerName, geoAssets.geoip.fileName, @@ -470,8 +488,7 @@ class ConfigOptionRepository with ExceptionHandler, InfraLogger { final GeoAssetRepository geoAssetRepository; final GeoAssetPathResolver geoAssetPathResolver; - TaskEither - getFullSingboxConfigOption() { + TaskEither getFullSingboxConfigOption() { return exceptionHandler( () async { return right(await getConfigOptions()); diff --git a/lib/features/config_option/notifier/warp_option_notifier.dart b/lib/features/config_option/notifier/warp_option_notifier.dart index 5dcdc2ed..b1a32810 100644 --- a/lib/features/config_option/notifier/warp_option_notifier.dart +++ b/lib/features/config_option/notifier/warp_option_notifier.dart @@ -26,20 +26,14 @@ class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger { return WarpOptions( consentGiven: consent, - configGeneration: hasWarpConfig - ? const AsyncValue.data("") - : AsyncError(const MissingWarpConfigFailure(), StackTrace.current), + configGeneration: hasWarpConfig ? const AsyncValue.data("") : AsyncError(const MissingWarpConfigFailure(), StackTrace.current), ); } - SharedPreferences get _prefs => - ref.read(sharedPreferencesProvider).requireValue; + SharedPreferences get _prefs => ref.read(sharedPreferencesProvider).requireValue; Future agree() async { - await ref - .read(sharedPreferencesProvider) - .requireValue - .setBool(warpConsentGiven, true); + await ref.read(sharedPreferencesProvider).requireValue.setBool(warpConsentGiven, true); state = state.copyWith(consentGiven: true); await generateWarpConfig(); } @@ -59,15 +53,33 @@ class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger { .getOrElse((l) => throw l) .run(); - await ref - .read(ConfigOptions.warpAccountId.notifier) - .update(warp.accountId); - await ref - .read(ConfigOptions.warpAccessToken.notifier) - .update(warp.accessToken); - await ref - .read(ConfigOptions.warpWireguardConfig.notifier) - .update(warp.wireguardConfig); + await ref.read(ConfigOptions.warpAccountId.notifier).update(warp.accountId); + await ref.read(ConfigOptions.warpAccessToken.notifier).update(warp.accessToken); + await ref.read(ConfigOptions.warpWireguardConfig.notifier).update(warp.wireguardConfig); + return warp.log; + }); + + state = state.copyWith(configGeneration: result); + } + + Future generateWarp2Config() async { + if (state.configGeneration.isLoading) return; + state = state.copyWith(configGeneration: const AsyncLoading()); + + final result = await AsyncValue.guard(() async { + final warp = await ref + .read(singboxServiceProvider) + .generateWarpConfig( + licenseKey: ref.read(ConfigOptions.warpLicenseKey), + previousAccountId: ref.read(ConfigOptions.warp2AccountId), + previousAccessToken: ref.read(ConfigOptions.warp2AccessToken), + ) + .getOrElse((l) => throw l) + .run(); + + await ref.read(ConfigOptions.warp2AccountId.notifier).update(warp.accountId); + await ref.read(ConfigOptions.warp2AccessToken.notifier).update(warp.accessToken); + await ref.read(ConfigOptions.warp2WireguardConfig.notifier).update(warp.wireguardConfig); return warp.log; }); diff --git a/lib/features/config_option/overview/config_options_page.dart b/lib/features/config_option/overview/config_options_page.dart index c9de0956..3ef2a948 100644 --- a/lib/features/config_option/overview/config_options_page.dart +++ b/lib/features/config_option/overview/config_options_page.dart @@ -23,17 +23,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:humanizer/humanizer.dart'; enum ConfigOptionSection { - warp; + warp, + fragment; static final _warpKey = GlobalKey(debugLabel: "warp-section-key"); + static final _fragmentKey = GlobalKey(debugLabel: "fragment-section-key"); - GlobalKey get key => switch (this) { _ => _warpKey }; + GlobalKey get key => switch (this) { + ConfigOptionSection.warp => _warpKey, + ConfigOptionSection.fragment => _fragmentKey, + }; } class ConfigOptionsPage extends HookConsumerWidget { - ConfigOptionsPage({super.key, String? section}) - : section = - section != null ? ConfigOptionSection.values.byName(section) : null; + ConfigOptionsPage({super.key, String? section}) : section = section != null ? ConfigOptionSection.values.byName(section) : null; final ConfigOptionSection? section; @@ -47,14 +50,10 @@ class ConfigOptionsPage extends HookConsumerWidget { if (section != null) { WidgetsBinding.instance.addPostFrameCallback( (_) { - final box = - section!.key.currentContext?.findRenderObject() as RenderBox?; + final box = section!.key.currentContext?.findRenderObject() as RenderBox?; final offset = box?.localToGlobal(Offset.zero); if (offset == null) return; - final height = scrollController.offset + - offset.dy - - MediaQueryData.fromView(View.of(context)).padding.top - - kToolbarHeight; + final height = scrollController.offset + offset.dy - MediaQueryData.fromView(View.of(context)).padding.top - kToolbarHeight; scrollController.animateTo( height, duration: const Duration(milliseconds: 500), @@ -83,36 +82,26 @@ class ConfigOptionsPage extends HookConsumerWidget { itemBuilder: (context) { return [ PopupMenuItem( - onTap: () async => ref - .read(configOptionNotifierProvider.notifier) - .exportJsonToClipboard() - .then((success) { + onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard().then((success) { if (success) { - ref - .read(inAppNotificationControllerProvider) - .showSuccessToast( + ref.read(inAppNotificationControllerProvider).showSuccessToast( t.general.clipboardExportSuccessMsg, ); } }), child: Text(t.settings.exportOptions), ), - if (ref.watch(debugModeNotifierProvider)) - PopupMenuItem( - onTap: () async => ref - .read(configOptionNotifierProvider.notifier) - .exportJsonToClipboard(excludePrivate: false) - .then((success) { - if (success) { - ref - .read(inAppNotificationControllerProvider) - .showSuccessToast( - t.general.clipboardExportSuccessMsg, - ); - } - }), - child: Text(t.settings.exportAllOptions), - ), + // if (ref.watch(debugModeNotifierProvider)) + PopupMenuItem( + onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard(excludePrivate: false).then((success) { + if (success) { + ref.read(inAppNotificationControllerProvider).showSuccessToast( + t.general.clipboardExportSuccessMsg, + ); + } + }), + child: Text(t.settings.exportAllOptions), + ), PopupMenuItem( onTap: () async { final shouldImport = await showConfirmationDialog( @@ -121,9 +110,7 @@ class ConfigOptionsPage extends HookConsumerWidget { message: t.settings.importOptionsMsg, ); if (shouldImport) { - await ref - .read(configOptionNotifierProvider.notifier) - .importFromClipboard(); + await ref.read(configOptionNotifierProvider.notifier).importFromClipboard(); } }, child: Text(t.settings.importOptions), @@ -131,9 +118,7 @@ class ConfigOptionsPage extends HookConsumerWidget { PopupMenuItem( child: Text(t.config.resetBtn), onTap: () async { - await ref - .read(configOptionNotifierProvider.notifier) - .resetOption(); + await ref.read(configOptionNotifierProvider.notifier).resetOption(); }, ), ]; @@ -158,15 +143,12 @@ class ConfigOptionsPage extends HookConsumerWidget { SwitchListTile( title: Text(experimental(t.config.bypassLan)), value: ref.watch(ConfigOptions.bypassLan), - onChanged: - ref.watch(ConfigOptions.bypassLan.notifier).update, + onChanged: ref.watch(ConfigOptions.bypassLan.notifier).update, ), SwitchListTile( title: Text(t.config.resolveDestination), value: ref.watch(ConfigOptions.resolveDestination), - onChanged: ref - .watch(ConfigOptions.resolveDestination.notifier) - .update, + onChanged: ref.watch(ConfigOptions.resolveDestination.notifier).update, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.ipv6Mode), @@ -179,28 +161,24 @@ class ConfigOptionsPage extends HookConsumerWidget { SettingsSection(t.config.section.dns), ValuePreferenceWidget( value: ref.watch(ConfigOptions.remoteDnsAddress), - preferences: - ref.watch(ConfigOptions.remoteDnsAddress.notifier), + preferences: ref.watch(ConfigOptions.remoteDnsAddress.notifier), title: t.config.remoteDnsAddress, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy), - preferences: ref - .watch(ConfigOptions.remoteDnsDomainStrategy.notifier), + preferences: ref.watch(ConfigOptions.remoteDnsDomainStrategy.notifier), choices: DomainStrategy.values, title: t.config.remoteDnsDomainStrategy, presentChoice: (value) => value.displayName, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.directDnsAddress), - preferences: - ref.watch(ConfigOptions.directDnsAddress.notifier), + preferences: ref.watch(ConfigOptions.directDnsAddress.notifier), title: t.config.directDnsAddress, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.directDnsDomainStrategy), - preferences: ref - .watch(ConfigOptions.directDnsDomainStrategy.notifier), + preferences: ref.watch(ConfigOptions.directDnsDomainStrategy.notifier), choices: DomainStrategy.values, title: t.config.directDnsDomainStrategy, presentChoice: (value) => value.displayName, @@ -208,9 +186,7 @@ class ConfigOptionsPage extends HookConsumerWidget { SwitchListTile( title: Text(t.config.enableDnsRouting), value: ref.watch(ConfigOptions.enableDnsRouting), - onChanged: ref - .watch(ConfigOptions.enableDnsRouting.notifier) - .update, + onChanged: ref.watch(ConfigOptions.enableDnsRouting.notifier).update, ), // const SettingsDivider(), // SettingsSection(experimental(t.config.section.mux)), @@ -247,13 +223,11 @@ class ConfigOptionsPage extends HookConsumerWidget { SwitchListTile( title: Text(t.config.strictRoute), value: ref.watch(ConfigOptions.strictRoute), - onChanged: - ref.watch(ConfigOptions.strictRoute.notifier).update, + onChanged: ref.watch(ConfigOptions.strictRoute.notifier).update, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.tunImplementation), - preferences: - ref.watch(ConfigOptions.tunImplementation.notifier), + preferences: ref.watch(ConfigOptions.tunImplementation.notifier), choices: TunImplementation.values, title: t.config.tunImplementation, presentChoice: (value) => value.name, @@ -287,25 +261,21 @@ class ConfigOptionsPage extends HookConsumerWidget { experimental(t.config.allowConnectionFromLan), ), value: ref.watch(ConfigOptions.allowConnectionFromLan), - onChanged: ref - .read(ConfigOptions.allowConnectionFromLan.notifier) - .update, + onChanged: ref.read(ConfigOptions.allowConnectionFromLan.notifier).update, ), const SettingsDivider(), SettingsSection( experimental(t.config.section.tlsTricks), + key: ConfigOptionSection._fragmentKey, ), SwitchListTile( title: Text(t.config.enableTlsFragment), value: ref.watch(ConfigOptions.enableTlsFragment), - onChanged: ref - .watch(ConfigOptions.enableTlsFragment.notifier) - .update, + onChanged: ref.watch(ConfigOptions.enableTlsFragment.notifier).update, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsFragmentSize), - preferences: - ref.watch(ConfigOptions.tlsFragmentSize.notifier), + preferences: ref.watch(ConfigOptions.tlsFragmentSize.notifier), title: t.config.tlsFragmentSize, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.present(t), @@ -313,8 +283,7 @@ class ConfigOptionsPage extends HookConsumerWidget { ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsFragmentSleep), - preferences: - ref.watch(ConfigOptions.tlsFragmentSleep.notifier), + preferences: ref.watch(ConfigOptions.tlsFragmentSleep.notifier), title: t.config.tlsFragmentSleep, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.present(t), @@ -323,21 +292,16 @@ class ConfigOptionsPage extends HookConsumerWidget { SwitchListTile( title: Text(t.config.enableTlsMixedSniCase), value: ref.watch(ConfigOptions.enableTlsMixedSniCase), - onChanged: ref - .watch(ConfigOptions.enableTlsMixedSniCase.notifier) - .update, + onChanged: ref.watch(ConfigOptions.enableTlsMixedSniCase.notifier).update, ), SwitchListTile( title: Text(t.config.enableTlsPadding), value: ref.watch(ConfigOptions.enableTlsPadding), - onChanged: ref - .watch(ConfigOptions.enableTlsPadding.notifier) - .update, + onChanged: ref.watch(ConfigOptions.enableTlsPadding.notifier).update, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsPaddingSize), - preferences: - ref.watch(ConfigOptions.tlsPaddingSize.notifier), + preferences: ref.watch(ConfigOptions.tlsPaddingSize.notifier), title: t.config.tlsPaddingSize, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.format(), @@ -350,38 +314,26 @@ class ConfigOptionsPage extends HookConsumerWidget { SettingsSection(t.config.section.misc), ValuePreferenceWidget( value: ref.watch(ConfigOptions.connectionTestUrl), - preferences: - ref.watch(ConfigOptions.connectionTestUrl.notifier), + preferences: ref.watch(ConfigOptions.connectionTestUrl.notifier), title: t.config.connectionTestUrl, ), ListTile( title: Text(t.config.urlTestInterval), subtitle: Text( - ref - .watch(ConfigOptions.urlTestInterval) - .toApproximateTime(isRelativeToNow: false), + ref.watch(ConfigOptions.urlTestInterval).toApproximateTime(isRelativeToNow: false), ), onTap: () async { final urlTestInterval = await SettingsSliderDialog( title: t.config.urlTestInterval, - initialValue: ref - .watch(ConfigOptions.urlTestInterval) - .inMinutes - .coerceIn(0, 60) - .toDouble(), - onReset: ref - .read(ConfigOptions.urlTestInterval.notifier) - .reset, + initialValue: ref.watch(ConfigOptions.urlTestInterval).inMinutes.coerceIn(0, 60).toDouble(), + onReset: ref.read(ConfigOptions.urlTestInterval.notifier).reset, min: 1, max: 60, divisions: 60, - labelGen: (value) => Duration(minutes: value.toInt()) - .toApproximateTime(isRelativeToNow: false), + labelGen: (value) => Duration(minutes: value.toInt()).toApproximateTime(isRelativeToNow: false), ).show(context); if (urlTestInterval == null) return; - await ref - .read(ConfigOptions.urlTestInterval.notifier) - .update(Duration(minutes: urlTestInterval.toInt())); + await ref.read(ConfigOptions.urlTestInterval.notifier).update(Duration(minutes: urlTestInterval.toInt())); }, ), ValuePreferenceWidget( diff --git a/lib/features/config_option/overview/warp_options_widgets.dart b/lib/features/config_option/overview/warp_options_widgets.dart index 112487e4..ba02bf00 100644 --- a/lib/features/config_option/overview/warp_options_widgets.dart +++ b/lib/features/config_option/overview/warp_options_widgets.dart @@ -63,17 +63,15 @@ class WarpOptionsTiles extends HookConsumerWidget { AsyncLoading() => const LinearProgressIndicator(), AsyncError() => Text( t.config.missingWarpConfig, - style: - TextStyle(color: Theme.of(context).colorScheme.error), + style: TextStyle(color: Theme.of(context).colorScheme.error), ), _ => null, } : null, enabled: canChangeOptions, onTap: () async { - await ref - .read(warpOptionNotifierProvider.notifier) - .generateWarpConfig(); + await ref.read(warpOptionNotifierProvider.notifier).generateWarpConfig(); + await ref.read(warpOptionNotifierProvider.notifier).generateWarp2Config(); }, ), ChoicePreferenceWidget( @@ -111,8 +109,7 @@ class WarpOptionsTiles extends HookConsumerWidget { preferences: ref.watch(ConfigOptions.warpNoise.notifier), enabled: canChangeOptions, title: t.config.warpNoise, - inputToValue: (input) => - OptionalRange.tryParse(input, allowEmpty: true), + inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true), presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), ), @@ -121,8 +118,7 @@ class WarpOptionsTiles extends HookConsumerWidget { preferences: ref.watch(ConfigOptions.warpNoiseDelay.notifier), enabled: canChangeOptions, title: t.config.warpNoiseDelay, - inputToValue: (input) => - OptionalRange.tryParse(input, allowEmpty: true), + inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true), presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), ), diff --git a/lib/features/config_option/widget/quick_settings_modal.dart b/lib/features/config_option/widget/quick_settings_modal.dart index e5acea73..f3ffb578 100644 --- a/lib/features/config_option/widget/quick_settings_modal.dart +++ b/lib/features/config_option/widget/quick_settings_modal.dart @@ -16,8 +16,7 @@ class QuickSettingsModal extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); - final warpPrefaceCompleted = - ref.watch(warpOptionNotifierProvider).consentGiven; + final warpPrefaceCompleted = ref.watch(warpOptionNotifierProvider).consentGiven; return SingleChildScrollView( child: Column( @@ -33,37 +32,41 @@ class QuickSettingsModal extends HookConsumerWidget { e.presentShort(t), overflow: TextOverflow.ellipsis, ), - tooltip: - e.isExperimental ? t.settings.experimental : null, + tooltip: e.isExperimental ? t.settings.experimental : null, ), ) .toList(), selected: {ref.watch(ConfigOptions.serviceMode)}, - onSelectionChanged: (newSet) => ref - .read(ConfigOptions.serviceMode.notifier) - .update(newSet.first), + onSelectionChanged: (newSet) => ref.read(ConfigOptions.serviceMode.notifier).update(newSet.first), ), ), const Gap(8), if (warpPrefaceCompleted) - SwitchListTile( - value: ref.watch(ConfigOptions.enableWarp), - onChanged: ref.watch(ConfigOptions.enableWarp.notifier).update, - title: Text(t.config.enableWarp), + GestureDetector( + onLongPress: () { + ConfigOptionsRoute(section: ConfigOptionSection.warp.name).go(context); + }, + child: SwitchListTile( + value: ref.watch(ConfigOptions.enableWarp), + onChanged: ref.watch(ConfigOptions.enableWarp.notifier).update, + title: Text(t.config.enableWarp), + ), ) else ListTile( title: Text(t.config.setupWarp), trailing: const Icon(FluentIcons.chevron_right_24_regular), - onTap: () => - ConfigOptionsRoute(section: ConfigOptionSection.warp.name) - .go(context), + onTap: () => ConfigOptionsRoute(section: ConfigOptionSection.warp.name).go(context), + ), + GestureDetector( + onLongPress: () { + ConfigOptionsRoute(section: ConfigOptionSection.fragment.name).go(context); + }, + child: SwitchListTile( + value: ref.watch(ConfigOptions.enableTlsFragment), + onChanged: ref.watch(ConfigOptions.enableTlsFragment.notifier).update, + title: Text(t.config.enableTlsFragment), ), - SwitchListTile( - value: ref.watch(ConfigOptions.enableTlsFragment), - onChanged: - ref.watch(ConfigOptions.enableTlsFragment.notifier).update, - title: Text(t.config.enableTlsFragment), ), // SwitchListTile( // value: ref.watch(ConfigOptions.enableMux), diff --git a/lib/features/profile/add/add_profile_modal.dart b/lib/features/profile/add/add_profile_modal.dart index 5752d532..7e5989ad 100644 --- a/lib/features/profile/add/add_profile_modal.dart +++ b/lib/features/profile/add/add_profile_modal.dart @@ -5,11 +5,16 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; +import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/core/router/router.dart'; import 'package:hiddify/features/common/qr_code_scanner_screen.dart'; +import 'package:hiddify/features/config_option/notifier/warp_option_notifier.dart'; +import 'package:hiddify/features/config_option/overview/config_options_page.dart'; +import 'package:hiddify/features/config_option/overview/warp_options_widgets.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class AddProfileModal extends HookConsumerWidget { const AddProfileModal({ @@ -17,7 +22,7 @@ class AddProfileModal extends HookConsumerWidget { this.url, this.scrollController, }); - + static const warpConsentGiven = "warp_consent_given"; final String? url; final ScrollController? scrollController; @@ -58,8 +63,7 @@ class AddProfileModal extends HookConsumerWidget { child: LayoutBuilder( builder: (context, constraints) { // temporary solution, aspect ratio widget relies on height and in a row there no height! - final buttonWidth = - constraints.maxWidth / 2 - (buttonsPadding + (buttonsGap / 2)); + final buttonWidth = constraints.maxWidth / 2 - (buttonsPadding + (buttonsGap / 2)); return AnimatedCrossFade( firstChild: SizedBox( @@ -93,8 +97,7 @@ class AddProfileModal extends HookConsumerWidget { secondChild: Column( children: [ Padding( - padding: - const EdgeInsets.symmetric(horizontal: buttonsPadding), + padding: const EdgeInsets.symmetric(horizontal: buttonsPadding), child: Row( children: [ _Button( @@ -103,13 +106,9 @@ class AddProfileModal extends HookConsumerWidget { icon: FluentIcons.clipboard_paste_24_regular, size: buttonWidth, onTap: () async { - final captureResult = - await Clipboard.getData(Clipboard.kTextPlain) - .then((value) => value?.text ?? ''); + final captureResult = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text ?? ''); if (addProfileState.isLoading) return; - ref - .read(addProfileProvider.notifier) - .add(captureResult); + ref.read(addProfileProvider.notifier).add(captureResult); }, ), const Gap(buttonsGap), @@ -120,8 +119,7 @@ class AddProfileModal extends HookConsumerWidget { icon: FluentIcons.qr_code_24_regular, size: buttonWidth, onTap: () async { - final cr = - await QRCodeScannerScreen().open(context); + final cr = await QRCodeScannerScreen().open(context); if (cr == null) return; if (addProfileState.isLoading) return; @@ -142,56 +140,118 @@ class AddProfileModal extends HookConsumerWidget { ], ), ), - if (!PlatformUtils.isDesktop) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: buttonsPadding, - vertical: 16, - ), - child: Semantics( - button: true, - child: SizedBox( - height: 36, - child: Material( - key: const ValueKey("add_manually_button"), - elevation: 8, - color: theme.colorScheme.surface, - surfaceTintColor: theme.colorScheme.surfaceTint, - shadowColor: Colors.transparent, - borderRadius: BorderRadius.circular(8), - clipBehavior: Clip.antiAlias, - child: InkWell( - onTap: () async { - context.pop(); - await const NewProfileRoute().push(context); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - FluentIcons.add_24_regular, - color: theme.colorScheme.primary, - ), - const Gap(8), - Text( - t.profile.add.manually, - style: theme.textTheme.labelLarge?.copyWith( + Padding( + padding: const EdgeInsets.symmetric( + horizontal: buttonsPadding, + vertical: 16, + ), + child: Column( + children: [ + Semantics( + button: true, + child: SizedBox( + height: 36, + child: Material( + key: const ValueKey("add_warp_button"), + elevation: 8, + color: theme.colorScheme.surface, + surfaceTintColor: theme.colorScheme.surfaceTint, + shadowColor: Colors.transparent, + borderRadius: BorderRadius.circular(8), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () async { + Future.microtask(() async { + context.pop(); + final _prefs = ref.read(sharedPreferencesProvider).requireValue; + final consent = _prefs.getBool(warpConsentGiven) ?? false; + if (!consent) { + final agreed = await showDialog( + context: context, + builder: (context) => const WarpLicenseAgreementModal(), + ); + + if (agreed ?? false) { + await ref.read(warpOptionNotifierProvider.notifier).agree(); + } + } + + final accountId = _prefs.getString("warp2-account-id"); + final accessToken = _prefs.getString("warp2-access-token"); + final hasWarp2Config = accountId != null && accessToken != null; + + if (!hasWarp2Config) { + await ref.read(warpOptionNotifierProvider.notifier).generateWarp2Config(); + } + await ref.read(addProfileProvider.notifier).add("#profile-title: Hiddify WARP\nwarp://p2@auto#Remote&&detour=warp://p1@auto#Local"); // + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + FluentIcons.add_24_regular, color: theme.colorScheme.primary, ), - ), - ], + const SizedBox(width: 8), + Text( + t.profile.add.addWarp, + style: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.primary, + ), + ), + ], + ), ), ), ), ), - ), + if (!PlatformUtils.isDesktop) const SizedBox(height: 16), // Spacing between the buttons + if (!PlatformUtils.isDesktop) + Semantics( + button: true, + child: SizedBox( + height: 36, + child: Material( + key: const ValueKey("add_manually_button"), + elevation: 8, + color: theme.colorScheme.surface, + surfaceTintColor: theme.colorScheme.surfaceTint, + shadowColor: Colors.transparent, + borderRadius: BorderRadius.circular(8), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () async { + context.pop(); + await const NewProfileRoute().push(context); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + FluentIcons.add_24_regular, + color: theme.colorScheme.primary, + ), + const SizedBox(width: 8), + Text( + t.profile.add.manually, + style: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.primary, + ), + ), + ], + ), + ), + ), + ), + ), + ], ), + ), const Gap(24), ], ), - crossFadeState: addProfileState.isLoading - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, + crossFadeState: addProfileState.isLoading ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: const Duration(milliseconds: 250), ); }, diff --git a/lib/features/profile/notifier/profile_notifier.dart b/lib/features/profile/notifier/profile_notifier.dart index 98c1368e..d351d999 100644 --- a/lib/features/profile/notifier/profile_notifier.dart +++ b/lib/features/profile/notifier/profile_notifier.dart @@ -47,8 +47,7 @@ class AddProfile extends _$AddProfile with AppLogger { return const AsyncData(null); } - ProfileRepository get _profilesRepo => - ref.read(profileRepositoryProvider).requireValue; + ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; CancelToken? _cancelToken; Future add(String rawInput) async { @@ -57,8 +56,7 @@ class AddProfile extends _$AddProfile with AppLogger { state = await AsyncValue.guard( () async { final activeProfile = await ref.read(activeProfileProvider.future); - final markAsActive = - activeProfile == null || ref.read(Preferences.markNewProfileActive); + final markAsActive = activeProfile == null || ref.read(Preferences.markNewProfileActive); final TaskEither task; if (LinkParser.parse(rawInput) case (final link)?) { loggy.debug("adding profile, url: [${link.url}]"); @@ -70,7 +68,11 @@ class AddProfile extends _$AddProfile with AppLogger { } else if (LinkParser.protocol(rawInput) case (final parsed)?) { loggy.debug("adding profile, content"); var name = parsed.name; - + var oldItem = await _profilesRepo.getByName(name); + if (name == "Hiddify WARP" && oldItem != null) { + _profilesRepo.setAsActive(oldItem.id).run(); + return unit; + } while (await _profilesRepo.getByName(name) != null) { name += '${randomInt(0, 9).run()}'; } @@ -122,8 +124,7 @@ class UpdateProfile extends _$UpdateProfile with AppLogger { return const AsyncData(null); } - ProfileRepository get _profilesRepo => - ref.read(profileRepositoryProvider).requireValue; + ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; Future updateProfile(RemoteProfileEntity profile) async { if (state.isLoading) return; @@ -143,9 +144,7 @@ class UpdateProfile extends _$UpdateProfile with AppLogger { await ref.read(activeProfileProvider.future).then((active) async { if (active != null && active.id == profile.id) { - await ref - .read(connectionNotifierProvider.notifier) - .reconnect(profile); + await ref.read(connectionNotifierProvider.notifier).reconnect(profile); } }); return unit; diff --git a/lib/features/proxy/overview/proxies_overview_notifier.dart b/lib/features/proxy/overview/proxies_overview_notifier.dart index deb27f38..d4f96ee8 100644 --- a/lib/features/proxy/overview/proxies_overview_notifier.dart +++ b/lib/features/proxy/overview/proxies_overview_notifier.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:combine/combine.dart'; import 'package:dartx/dartx.dart'; +import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; @@ -85,46 +85,41 @@ class ProxiesOverviewNotifier extends _$ProxiesOverviewNotifier with AppLogger { List proxies, ProxiesSort sortBy, ) async { - return CombineWorker().execute( - () { - final groupWithSelected = { - for (final o in proxies) o.tag: o.selected, - }; - final sortedProxies = []; - for (final group in proxies) { - final sortedItems = switch (sortBy) { - ProxiesSort.name => group.items.sortedWith((a, b) { - if (a.type.isGroup && !b.type.isGroup) return -1; - if (!a.type.isGroup && b.type.isGroup) return 1; - return a.tag.compareTo(b.tag); - }), - ProxiesSort.delay => group.items.sortedWith((a, b) { - if (a.type.isGroup && !b.type.isGroup) return -1; - if (!a.type.isGroup && b.type.isGroup) return 1; + final groupWithSelected = { + for (final o in proxies) o.tag: o.selected, + }; + final sortedProxies = []; + for (final group in proxies) { + final sortedItems = switch (sortBy) { + ProxiesSort.name => group.items.sortedWith((a, b) { + if (a.type.isGroup && !b.type.isGroup) return -1; + if (!a.type.isGroup && b.type.isGroup) return 1; + return a.tag.compareTo(b.tag); + }), + ProxiesSort.delay => group.items.sortedWith((a, b) { + if (a.type.isGroup && !b.type.isGroup) return -1; + if (!a.type.isGroup && b.type.isGroup) return 1; - final ai = a.urlTestDelay; - final bi = b.urlTestDelay; - if (ai == 0 && bi == 0) return -1; - if (ai == 0 && bi > 0) return 1; - if (ai > 0 && bi == 0) return -1; - return ai.compareTo(bi); - }), - ProxiesSort.unsorted => group.items, - }; - final items = []; - for (final item in sortedItems) { - if (groupWithSelected.keys.contains(item.tag)) { - items - .add(item.copyWith(selectedTag: groupWithSelected[item.tag])); - } else { - items.add(item); - } - } - sortedProxies.add(group.copyWith(items: items)); + final ai = a.urlTestDelay; + final bi = b.urlTestDelay; + if (ai == 0 && bi == 0) return -1; + if (ai == 0 && bi > 0) return 1; + if (ai > 0 && bi == 0) return -1; + return ai.compareTo(bi); + }), + ProxiesSort.unsorted => group.items, + }; + final items = []; + for (final item in sortedItems) { + if (groupWithSelected.keys.contains(item.tag)) { + items.add(item.copyWith(selectedTag: groupWithSelected[item.tag])); + } else { + items.add(item); } - return sortedProxies; - }, - ); + } + sortedProxies.add(group.copyWith(items: items)); + } + return sortedProxies; } Future changeProxy(String groupTag, String outboundTag) async { diff --git a/lib/singbox/model/singbox_config_option.dart b/lib/singbox/model/singbox_config_option.dart index ef4871c0..3bcd2798 100644 --- a/lib/singbox/model/singbox_config_option.dart +++ b/lib/singbox/model/singbox_config_option.dart @@ -48,6 +48,7 @@ class SingboxConfigOption with _$SingboxConfigOption { required SingboxMuxOption mux, required SingboxTlsTricks tlsTricks, required SingboxWarpOption warp, + required SingboxWarpOption warp2, }) = _SingboxConfigOption; String format() { @@ -55,8 +56,7 @@ class SingboxConfigOption with _$SingboxConfigOption { return encoder.convert(toJson()); } - factory SingboxConfigOption.fromJson(Map json) => - _$SingboxConfigOptionFromJson(json); + factory SingboxConfigOption.fromJson(Map json) => _$SingboxConfigOptionFromJson(json); } @freezed @@ -75,8 +75,7 @@ class SingboxWarpOption with _$SingboxWarpOption { @OptionalRangeJsonConverter() required OptionalRange noiseDelay, }) = _SingboxWarpOption; - factory SingboxWarpOption.fromJson(Map json) => - _$SingboxWarpOptionFromJson(json); + factory SingboxWarpOption.fromJson(Map json) => _$SingboxWarpOptionFromJson(json); } @freezed @@ -89,8 +88,7 @@ class SingboxMuxOption with _$SingboxMuxOption { required MuxProtocol protocol, }) = _SingboxMuxOption; - factory SingboxMuxOption.fromJson(Map json) => - _$SingboxMuxOptionFromJson(json); + factory SingboxMuxOption.fromJson(Map json) => _$SingboxMuxOptionFromJson(json); } @freezed @@ -105,6 +103,5 @@ class SingboxTlsTricks with _$SingboxTlsTricks { @OptionalRangeJsonConverter() required OptionalRange paddingSize, }) = _SingboxTlsTricks; - factory SingboxTlsTricks.fromJson(Map json) => - _$SingboxTlsTricksFromJson(json); + factory SingboxTlsTricks.fromJson(Map json) => _$SingboxTlsTricksFromJson(json); } diff --git a/lib/singbox/service/ffi_singbox_service.dart b/lib/singbox/service/ffi_singbox_service.dart index 83583a0f..889fa91c 100644 --- a/lib/singbox/service/ffi_singbox_service.dart +++ b/lib/singbox/service/ffi_singbox_service.dart @@ -4,7 +4,6 @@ import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; -import 'package:combine/combine.dart'; import 'package:ffi/ffi.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/model/directories.dart'; @@ -52,10 +51,7 @@ class FFISingboxService with InfraLogger implements SingboxService { Future init() async { loggy.debug("initializing"); _statusReceiver = ReceivePort('service status receiver'); - final source = _statusReceiver - .asBroadcastStream() - .map((event) => jsonDecode(event as String)) - .map(SingboxStatus.fromEvent); + final source = _statusReceiver.asBroadcastStream().map((event) => jsonDecode(event as String)).map(SingboxStatus.fromEvent); _status = ValueConnectableStream.seeded( source, const SingboxStopped(), @@ -69,8 +65,8 @@ class FFISingboxService with InfraLogger implements SingboxService { ) { final port = _statusReceiver.sendPort.nativePort; return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { _box.setupOnce(NativeApi.initializeApiDLData); final err = _box .setup( @@ -98,8 +94,8 @@ class FFISingboxService with InfraLogger implements SingboxService { bool debug, ) { return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final err = _box .parse( path.toNativeUtf8().cast(), @@ -120,13 +116,10 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither changeOptions(SingboxConfigOption options) { return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final json = jsonEncode(options.toJson()); - final err = _box - .changeConfigOptions(json.toNativeUtf8().cast()) - .cast() - .toDartString(); + final err = _box.changeConfigOptions(json.toNativeUtf8().cast()).cast().toDartString(); if (err.isNotEmpty) { return left(err); } @@ -141,8 +134,8 @@ class FFISingboxService with InfraLogger implements SingboxService { String path, ) { return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final response = _box .generateConfig( path.toNativeUtf8().cast(), @@ -166,8 +159,8 @@ class FFISingboxService with InfraLogger implements SingboxService { ) { loggy.debug("starting, memory limit: [${!disableMemoryLimit}]"); return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final err = _box .start( configPath.toNativeUtf8().cast(), @@ -187,8 +180,8 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither stop() { return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final err = _box.stop().cast().toDartString(); if (err.isNotEmpty) { return left(err); @@ -207,8 +200,8 @@ class FFISingboxService with InfraLogger implements SingboxService { ) { loggy.debug("restarting, memory limit: [${!disableMemoryLimit}]"); return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final err = _box .restart( configPath.toNativeUtf8().cast(), @@ -265,10 +258,7 @@ class FFISingboxService with InfraLogger implements SingboxService { }, ); - final err = _box - .startCommandClient(1, receiver.sendPort.nativePort) - .cast() - .toDartString(); + final err = _box.startCommandClient(1, receiver.sendPort.nativePort).cast().toDartString(); if (err.isNotEmpty) { loggy.error("error starting status command: $err"); throw err; @@ -310,10 +300,7 @@ class FFISingboxService with InfraLogger implements SingboxService { ); try { - final err = _box - .startCommandClient(5, receiver.sendPort.nativePort) - .cast() - .toDartString(); + final err = _box.startCommandClient(5, receiver.sendPort.nativePort).cast().toDartString(); if (err.isNotEmpty) { logger.error("error starting group command: $err"); throw err; @@ -357,10 +344,7 @@ class FFISingboxService with InfraLogger implements SingboxService { ); try { - final err = _box - .startCommandClient(13, receiver.sendPort.nativePort) - .cast() - .toDartString(); + final err = _box.startCommandClient(13, receiver.sendPort.nativePort).cast().toDartString(); if (err.isNotEmpty) { logger.error("error starting: $err"); throw err; @@ -376,8 +360,8 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither selectOutbound(String groupTag, String outboundTag) { return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final err = _box .selectOutbound( groupTag.toNativeUtf8().cast(), @@ -397,12 +381,9 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither urlTest(String groupTag) { return TaskEither( - () => CombineWorker().execute( - () { - final err = _box - .urlTest(groupTag.toNativeUtf8().cast()) - .cast() - .toDartString(); + () => Future.microtask( + () async { + final err = _box.urlTest(groupTag.toNativeUtf8().cast()).cast().toDartString(); if (err.isNotEmpty) { return left(err); } @@ -418,9 +399,7 @@ class FFISingboxService with InfraLogger implements SingboxService { @override Stream> watchLogs(String path) async* { yield await _readLogFile(File(path)); - yield* Watcher(path, pollingDelay: const Duration(seconds: 1)) - .events - .asyncMap((event) async { + yield* Watcher(path, pollingDelay: const Duration(seconds: 1)).events.asyncMap((event) async { if (event.type == ChangeType.MODIFY) { await _readLogFile(File(path)); } @@ -431,17 +410,18 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither clearLogs() { return TaskEither( - () async { - _logBuffer.clear(); - return right(unit); - }, + () => Future.microtask( + () async { + _logBuffer.clear(); + return right(unit); + }, + ), ); } Future> _readLogFile(File file) async { if (_logFilePosition == 0 && file.lengthSync() == 0) return []; - final content = - await file.openRead(_logFilePosition).transform(utf8.decoder).join(); + final content = await file.openRead(_logFilePosition).transform(utf8.decoder).join(); _logFilePosition = file.lengthSync(); final lines = const LineSplitter().convert(content); if (lines.length > 300) { @@ -464,8 +444,8 @@ class FFISingboxService with InfraLogger implements SingboxService { }) { loggy.debug("generating warp config"); return TaskEither( - () => CombineWorker().execute( - () { + () => Future.microtask( + () async { final response = _box .generateWarpConfig( licenseKey.toNativeUtf8().cast(), diff --git a/libcore b/libcore index b2658863..bc48ec07 160000 --- a/libcore +++ b/libcore @@ -1 +1 @@ -Subproject commit b26588630694cf99f66292d2703476f9deee79d7 +Subproject commit bc48ec07a8cb85c43a8794b8780c7f732af010d1 diff --git a/pubspec.lock b/pubspec.lock index 9a19197a..c447c967 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -218,14 +218,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - combine: - dependency: "direct main" - description: - name: combine - sha256: "8b52083c822a614a448fdd307e78c05266080e9747604b61fca5ddfe736a6c1e" - url: "https://pub.dev" - source: hosted - version: "0.5.7-0.1.pre" convert: dependency: transitive description: @@ -801,11 +793,12 @@ packages: humanizer: dependency: "direct main" description: - name: humanizer - sha256: "08728a4b6d62accd7d09e668bd54e81e6e09a82c8cfda30553224b3eb868d4f2" - url: "https://pub.dev" - source: hosted - version: "2.2.0" + path: "." + ref: up-version + resolved-ref: "8ae61d68357fae197be7ee71d67ccb9498b9d5c7" + url: "https://github.com/alex-relov/humanizer" + source: git + version: "2.3.0" iconsax_flutter: dependency: transitive description: @@ -850,10 +843,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -922,26 +915,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lint: dependency: "direct dev" description: @@ -1002,10 +995,10 @@ packages: dependency: "direct main" description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1703,26 +1696,26 @@ packages: dependency: transitive description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" time: dependency: transitive description: @@ -1935,10 +1928,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" watcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3a857efc..b7f29620 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter_localizations: sdk: flutter cupertino_icons: ^1.0.6 - intl: ^0.18.1 + intl: ^0.19.0 slang: ^3.30.1 slang_flutter: ^3.30.0 fpdart: ^1.1.0 @@ -40,7 +40,7 @@ dependencies: launch_at_startup: ^0.2.2 sentry_flutter: ^7.16.1 sentry_dart_plugin: ^1.7.1 - combine: ^0.5.7-0.1.pre + path: ^1.8.3 loggy: ^2.0.3 flutter_loggy: ^2.0.2 @@ -58,7 +58,11 @@ dependencies: percent_indicator: ^4.2.3 sliver_tools: ^0.2.12 flutter_adaptive_scaffold: ^0.1.8 - humanizer: ^2.2.0 + # humanizer: ^2.2.0 + humanizer: + git: + url: https://github.com/alex-relov/humanizer + ref: up-version upgrader: ^9.0.0 toastification: ^1.2.1 version: ^3.0.2 From c5486963d1f379debc08d43c59afc253bcf9b7aa Mon Sep 17 00:00:00 2001 From: hiddify-com <114227601+hiddify-com@users.noreply.github.com> Date: Fri, 31 May 2024 14:04:41 +0200 Subject: [PATCH 19/19] fix lang update --- assets/translations/strings_ru.i18n.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index aefe217d..8ddb8d98 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -21,8 +21,8 @@ "showMore": "Развернуть ", "showLess": "Свернуть ", "openAppSettings": "Открыть настройки", - "grantPermission": "Дать права доступа" - "hidden": "Скрытый", + "grantPermission": "Дать права доступа", + "hidden": "Скрытый" }, "intro": { "termsAndPolicyCaution(rich)": "Продолжая, Вы соглашаетесь с ${tap(@:about.termsAndConditions)}",