merge with branch update UI

This commit is contained in:
Macbook
2024-12-20 21:01:52 +07:00
56 changed files with 3330 additions and 507 deletions

39
.clang-format Normal file
View File

@@ -0,0 +1,39 @@
BasedOnStyle: WebKit
AccessModifierOffset: '-4'
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: 'true'
AlignTrailingComments: 'true'
AllowAllArgumentsOnNextLine: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortEnumsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: None
AlwaysBreakTemplateDeclarations: 'No'
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakConstructorInitializers: BeforeColon
ColumnLimit: '120'
CommentPragmas: '"^!|^:"'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
ConstructorInitializerIndentWidth: '4'
ContinuationIndentWidth: '8'
IndentPPDirectives: BeforeHash
NamespaceIndentation: All
PenaltyExcessCharacter: '10'
PointerAlignment: Right
SortIncludes: 'true'
SpaceAfterTemplateKeyword: 'false'
Standard: Auto

20
.clang-format-ignore Normal file
View File

@@ -0,0 +1,20 @@
/client/3rd
/client/3rd-prebuild
/client/android
/client/cmake
/client/core/serialization
/client/daemon
/client/fonts
/client/images
/client/ios
/client/mozilla
/client/platforms/dummy
/client/platforms/linux
/client/platforms/macos
/client/platforms/windows
/client/server_scripts
/client/translations
/deploy
/docs
/metadata
/service/src

View File

@@ -1,9 +1,14 @@
# Amnezia VPN
## _The best client for self-hosted VPN_
### _The best client for self-hosted VPN_
[![Build Status](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)

75
README_RU.md Normal file
View File

@@ -0,0 +1,75 @@
# Amnezia VPN
### _Лучший клиент для создания VPN на собственном сервере_
[![Build Status](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/kldscp/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/kldscp/amnezia.org).
<a href="https://storage.googleapis.com/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
<br/>
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
## Особенности
- Простой в использовании — введите IP-адрес, SSH-логин и пароль, и Amnezia автоматически установит VPN-контейнеры Docker на ваш сервер и подключится к VPN.
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
## Ссылки
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси)
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский)
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
## Технологии
AmneziaVPN использует несколько проектов с открытым исходным кодом:
- [OpenSSL](https://www.openssl.org/)
- [OpenVPN](https://openvpn.net/)
- [Shadowsocks](https://shadowsocks.org/)
- [Qt](https://www.qt.io/)
- [LibSsh](https://libssh.org)
- и другие...
## Лицензия
GPL v3.0
## Донаты
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>
TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
## Благодарности
Этот проект тестируется с помощью BrowserStack.
Мы выражаем благодарность [BrowserStack](https://www.browserstack.com) за поддержку нашего проекта.

View File

@@ -69,7 +69,7 @@ void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
const QUrl url(QStringLiteral("qrc:/ui/qml/DefaultVpn/main.qml"));
QObject::connect(
m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
@@ -154,7 +154,7 @@ void AmneziaApplication::init()
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
#endif
m_engine->addImportPath("qrc:/ui/qml/Modules/");
m_engine->addImportPath("qrc:/ui/qml/DefaultVpn");
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
@@ -228,7 +228,7 @@ void AmneziaApplication::loadFonts()
{
QQuickStyle::setStyle("Basic");
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
QFontDatabase::addApplicationFont(":/fonts/VelaSans-GX.ttf");
}
void AmneziaApplication::loadTranslator()

View File

@@ -3,38 +3,169 @@
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUuid>
#include "logger.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
namespace {
Logger logger("XrayConfigurator");
}
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
QString xrayPublicKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
xrayPublicKey.replace("\n", "");
QString xrayUuid = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, errorCode);
xrayUuid.replace("\n", "");
QString xrayShortId =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
xrayShortId.replace("\n", "");
// Generate new UUID for client
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
// Get current server config
QString currentConfig = m_serverController->getTextFileFromContainer(
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to get server config file";
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayUuid);
// Parse current config as JSON
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
if (doc.isNull() || !doc.isObject()) {
logger.error() << "Failed to parse server config JSON";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonObject serverConfig = doc.object();
// Validate server config structure
if (!serverConfig.contains("inbounds")) {
logger.error() << "Server config missing 'inbounds' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonArray inbounds = serverConfig["inbounds"].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Server config has empty 'inbounds' array";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains("settings")) {
logger.error() << "Inbound missing 'settings' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonObject settings = inbound["settings"].toObject();
if (!settings.contains("clients")) {
logger.error() << "Settings missing 'clients' field";
errorCode = ErrorCode::InternalError;
return "";
}
QJsonArray clients = settings["clients"].toArray();
// Create configuration for new client
QJsonObject clientConfig {
{"id", clientId},
{"flow", "xtls-rprx-vision"}
};
clients.append(clientConfig);
// Update config
settings["clients"] = clients;
inbound["settings"] = settings;
inbounds[0] = inbound;
serverConfig["inbounds"] = inbounds;
// Save updated config to server
QString updatedConfig = QJsonDocument(serverConfig).toJson();
errorCode = m_serverController->uploadTextFileToContainer(
container,
credentials,
updatedConfig,
amnezia::protocols::xray::serverConfigPath,
libssh::ScpOverwriteMode::ScpOverwriteExisting
);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to upload updated config";
return "";
}
// Restart container
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
errorCode = m_serverController->runScript(
credentials,
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to restart container";
return "";
}
return clientId;
}
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
// Get client ID from prepareServerConfig
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
logger.error() << "Failed to prepare server config";
errorCode = ErrorCode::InternalError;
return "";
}
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
if (config.isEmpty()) {
logger.error() << "Failed to get config template";
errorCode = ErrorCode::InternalError;
return "";
}
QString xrayPublicKey =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
logger.error() << "Failed to get public key";
errorCode = ErrorCode::InternalError;
return "";
}
xrayPublicKey.replace("\n", "");
QString xrayShortId =
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
logger.error() << "Failed to get short ID";
errorCode = ErrorCode::InternalError;
return "";
}
xrayShortId.replace("\n", "");
// Validate all required variables are present
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
logger.error() << "Config template missing required variables:"
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
errorCode = ErrorCode::InternalError;
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayClientId);
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
config.replace("$XRAY_SHORT_ID", xrayShortId);

View File

@@ -14,6 +14,10 @@ public:
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
private:
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -379,6 +379,13 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
auto errorCode = checkErrors(sslErrors, reply);
reply->deleteLater();
if (errorCode == ErrorCode::NoError) {
if (!responseBody.contains("services")) {
return ErrorCode::ApiServicesMissingError;
}
}
return errorCode;
}

View File

@@ -109,6 +109,7 @@ namespace amnezia
ApiConfigSslError = 1104,
ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
ApiServicesMissingError = 1107,
// QFile errors
OpenError = 1200,

View File

@@ -63,7 +63,8 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break;
case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); break;
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
case(ErrorCode::ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;

Binary file not shown.

View File

@@ -0,0 +1,4 @@
<svg width="82" height="85" viewBox="0 0 82 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M61.5 14.4031C70.8668 20.9031 77 31.7353 77 44.0001C77 63.8823 60.8823 80.0001 41 80.0001C21.1177 80.0001 5 63.8823 5 44.0001C5 31.7353 11.1332 20.9031 20.5 14.4031" stroke="white" stroke-width="7" stroke-linecap="round"/>
<path d="M41 4V29" stroke="white" stroke-width="7" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -220,6 +220,28 @@
<file>ui/qml/Pages2/PageSettingsApiLanguageList.qml</file>
<file>images/controls/archive-restore.svg</file>
<file>images/controls/help-circle.svg</file>
<file>ui/qml/DefaultVpn/Controls/DropDownType.qml</file>
<file>ui/qml/DefaultVpn/main.qml</file>
<file>ui/qml/DefaultVpn/Pages/PageHome.qml</file>
<file>ui/qml/DefaultVpn/Controls/TextTypes/MediumTextType.qml</file>
<file>ui/qml/DefaultVpn/Config/DeviceInfo.qml</file>
<file>ui/qml/DefaultVpn/Config/qmldir</file>
<file>ui/qml/DefaultVpn/Controls/TextTypes/XSmallTextType.qml</file>
<file>ui/qml/DefaultVpn/Controls/ButtonType.qml</file>
<file>ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml</file>
<file>ui/qml/DefaultVpn/Controls/TextTypes/Header1TextType.qml</file>
<file>ui/qml/DefaultVpn/Controls/TextTypes/Header3TextType.qml</file>
<file>ui/qml/DefaultVpn/Config/Style.qml</file>
<file>ui/qml/DefaultVpn/Components/WhiteButtonWithBorder.qml</file>
<file>ui/qml/DefaultVpn/Components/WhiteButtonNoBorder.qml</file>
<file>ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml</file>
<file>ui/qml/DefaultVpn/Components/BlueButtonNoBorder.qml</file>
<file>ui/qml/DefaultVpn/Controls/InputType.qml</file>
<file>ui/qml/DefaultVpn/Controls/PopupType.qml</file>
<file>ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml</file>
<file>ui/qml/DefaultVpn/Controls/BusyIndicatorType.qml</file>
<file>images/controls/connect-button.svg</file>
<file>fonts/VelaSans-GX.ttf</file>
</qresource>
<qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>شبكة VPN كلاسيكية للعمل المريح وتنزيل الملفات الكبيرة ومشاهدة مقاطع الفيديو. تعمل مع أي موقع. تصل السرعة إلى %1 ميجابت/ثانية</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>شبكة VPN للولوج للمواقع المحظورة في بلاد ذو مستوي عالي من الرقابة علي الانترنت. </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>Amenzia Premium - شبكة VPN للعمل المريح, تحميل ملفات كبيرة الحجم, ومشاهدة مقاطع الفيديو ب جودة عالية. تعمل لجميع المواقع, حتي في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>Amnezia Free هو VPN مجاني لتخطي الحظر في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation>%1 ميجابت/ثانية</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation>%1 ايام</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>سيقوم VPN فقط بفتح المواقع المشهورة المحظورة في بلدك, مثل Instagram, Facebook, Twitter و مواقع اخري. المواقع الاخري ستٌفتح من عنوان ال IP الحقيقي الخاص بك, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;معلومات اخري علي الموقع.&lt;/a&gt;</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation>مجاني</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation>%1 دولار/الشهر</translation>
</message>
@@ -482,6 +482,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>لا يمكن تغير الخادم بينما هناك اتصال مفعل</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1376,6 +1391,14 @@ Already installed containers were found on the server. All installed containers
<translation>سياسات الخصوصية</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1389,64 +1412,69 @@ Already installed containers were found on the server. All installed containers
<translation>السعر</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation>مدة العمل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation>السرعة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation>علامة الدعم</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation>تم النسخ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation>إعادة تحميل تكوين API</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation>إعادة تحميل تكوين API</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation>واصل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation>إلغاء</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation>لا يمكن إعادة تحميل تكوين API اثناء تواجد اتصال نشط</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation>احذف من التطبيق</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation>احذف من التطبيق؟</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation>لا يمكن إزالة الخادم أثناء الاتصال النشط</translation>
</message>
@@ -2048,30 +2076,50 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation>اسم الخادم</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation>احفظ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>البروتوكولات</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation>الخدمات</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation>الإدارة</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2164,6 +2212,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation>الخوادم</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2419,6 +2472,31 @@ Already installed containers were found on the server. All installed containers
<source>I have nothing</source>
<translation>ليس لدي اي شئ</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">مفتاح</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3315,7 +3393,7 @@ Already installed containers were found on the server. All installed containers
<translation>هذا التكوين بالفعل تمت إضافتة للبرنامج</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation></translation>
</message>
@@ -3420,37 +3498,42 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: The file could not be opened</source>
<translation>خطأ QFile: لا يمكن فتح الملف</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: An error occurred when reading from the file</source>
<translation>خطأ QFile: ظهر خطأ اثناء القراءه من الملف</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: The file could not be accessed</source>
<translation>خطأ QFile: لا يمكن الوصول للملف</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: An unspecified error occurred</source>
<translation>خطأ QFile: ظهر خطأ غير محدد</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation>خطأ QFile: حدث خطأ فادح</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation>خطأ QFile: تم إحباط العملية</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>خطأ داخلي</translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیوها، از VPN کلاسیک استفاده کنید. این VPN برای هر سایتی کار میکند و سرعت آن تا %1 مگابیت بر ثانیه است.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>وی پی ان برای دسترسی به سایتهای مسدود شده در مناطق با سانسور شدید اینترنت.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>امنزیا پریمیوم - یک وی پی ان کلاسیک برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیو با کیفیت بالا. قابل استفاده برای تمامی سایتها، حتی در کشورهایی با بالاترین سطح سانسور اینترنت.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>امنزیا رایگان یک وی پی ان رایگان برای دور زدن مسدودیتها در کشورهایی با سطح بالای سانسور اینترنت است.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation>%1 MBit/s</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation>%1 روز</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>وی پی ان فقط سایتهای محبوبی را که در منطقه شما مسدود شدهاند، مانند اینستاگرام، فیسبوک، توییتر و غیره باز میکند. سایر سایتها با آدرس آیپی واقعی شما باز خواهند شد. &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation>رایگان</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation>%1 $/ماه</translation>
</message>
@@ -486,6 +486,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>امکان تغییر سرور در هنگام متصل بودن وجود ندارد</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1459,6 +1474,14 @@ Already installed containers were found on the server. All installed containers
<translation></translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1472,64 +1495,69 @@ Already installed containers were found on the server. All installed containers
<translation>قیمت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation>مدت زمان کار</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation>سرعت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation>کپی شد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation>بارگذاری مجدد پیکربندی API</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation>آیا میخواهید پیکربندی API را دوباره بارگذاری کنید؟</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation>ادامه دهید</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation>لغو</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation>نمیتوان پیکربندی API را در حین اتصال فعال دوباره بارگذاری کرد.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation>حذف از برنامه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation>آیا میخواهید از برنامه حذف کنید؟</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation>نمیتوان سرور را در حین اتصال فعال حذف کرد.</translation>
</message>
@@ -2143,30 +2171,50 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation>نام سرور</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation>ذخیره</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>پروتکلها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation>سرویسها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation>مدیریت</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2259,6 +2307,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation>سرورها</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2542,6 +2595,31 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<source>Key as text</source>
<translation type="vanished">متن شامل کلید</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">کلید</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3506,7 +3584,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<translation>این پیکربندی قبلاً به برنامه اضافه شده است</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>کد خطا: %1. </translation>
</message>
@@ -3606,37 +3684,42 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<source>QFile error: The file could not be opened</source>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: An error occurred when reading from the file</source>
<source>QFile error: The file could not be opened</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: The file could not be accessed</source>
<source>QFile error: An error occurred when reading from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: An unspecified error occurred</source>
<source>QFile error: The file could not be accessed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: A fatal error occurred</source>
<source>QFile error: An unspecified error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>Internal error</translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation type="unfinished"></translation>
</message>
@@ -482,6 +482,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>ि </translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1416,6 +1431,14 @@ Already installed containers were found on the server. All installed containers
<translation> ि</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1429,64 +1452,69 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation type="unfinished"> ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation type="unfinished"> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation type="unfinished"> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation type="unfinished">ि </translation>
</message>
@@ -2096,30 +2124,50 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation> </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2212,6 +2260,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2487,6 +2540,31 @@ Already installed containers were found on the server. All installed containers
<source>Key as text</source>
<translation type="vanished"> </translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3450,7 +3528,12 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>ErrorCode: %1. </translation>
</message>
@@ -3515,37 +3598,37 @@ Already installed containers were found on the server. All installed containers
<translation>ि ि ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: The file could not be opened</source>
<translation>Qफ़ ि: </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: An error occurred when reading from the file</source>
<translation>Qफ़ ि: ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: The file could not be accessed</source>
<translation>Qफ़ ि: </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: An unspecified error occurred</source>
<translation>Qफ़ ि: िि ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation>Qफ़ ि: ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation>Qफ़ ि: ि ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>ि ि</translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>ကကက ကက ကက VPN ကက %1 MBit/s </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>က က ကက က VPN. </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>Amnezia Premium - ကကက ကက ကကကက VPN က ကက .</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>Amnezia Free ကက က ကက VPN </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation>%1 MBit/s</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation>%1 က</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation> VPN Instagram Facebook Twitter ကက ကက ကက IP &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;ကကကက က&lt;/a&gt;</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation>%1 $/</translation>
</message>
@@ -482,6 +482,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>ကက က </translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1384,6 +1399,14 @@ Already installed containers were found on the server. All installed containers
<translation>ကကက</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1397,64 +1420,69 @@ Already installed containers were found on the server. All installed containers
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation>က tag</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation>API config က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation>API config က ?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation>ကက</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation>က API config က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation>က?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation>က က </translation>
</message>
@@ -2057,30 +2085,50 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2173,6 +2221,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2428,6 +2481,31 @@ Already installed containers were found on the server. All installed containers
<source>I have nothing</source>
<translation>က</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">Key</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3311,7 +3389,7 @@ Already installed containers were found on the server. All installed containers
<translation> config က က </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>က: %1. </translation>
</message>
@@ -3416,37 +3494,42 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: The file could not be opened</source>
<translation>QFile မှု: ဖိုင်ကို </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: An error occurred when reading from the file</source>
<translation>QFile မှု: ဖိုင်ကိုဖတ်န </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: The file could not be accessed</source>
<translation>QFile မှု: ဖိုင်ကို </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: An unspecified error occurred</source>
<translation>QFile မှု: သတ်မှတ်မထ </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation>QFile မှု: က </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation>QFile မှု: လုပ်ငန်က ကက</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>က </translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Работает для любых сайтов. Скорость до %1 Мбит/с</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>VPN для доступа к заблокированным сайтам в регионах с высоким уровнем интернет-цензуры. </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>Amnezia Premium классический VPN для комфортной работы, загрузки больших файлов и просмотра видео в высоком разрешении. Работает на всех сайтах, даже в странах с самым высоким уровнем интернет-цензуры.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>Amnezia Free - это бесплатный VPN для обхода блокировок в странах с высоким уровнем интернет-цензуры</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation>%1 дней</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>Через VPN будут открываться только популярные сайты, заблокированные в вашем регионе, такие как Instagram, Facebook, Twitter и другие. Остальные сайты будут открываться с вашего реального IP-адреса, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;подробности на сайте.&lt;/a&gt;</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation>Бесплатно</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation>%1 $/месяц</translation>
</message>
@@ -486,6 +486,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>Невозможно изменить сервер во время активного соединения</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1479,6 +1494,14 @@ Already installed containers were found on the server. All installed containers
<translation>Политика конфиденциальности</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1492,64 +1515,69 @@ Already installed containers were found on the server. All installed containers
<translation>Цена</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation>Период работы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation>Скорость</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation>Скопировано</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation>Перезагрузить конфигурацию API</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation>Перезагрузить конфигурацию API?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation>Отменить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation>Невозможно перзагрузить API конфигурацию при активном соединении</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation>Удалить из приложения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation>Удалить из приложения?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation>Невозможно удалить сервер во время активного соединения</translation>
</message>
@@ -2199,27 +2227,32 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation>Имя сервера</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation>Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>Протоколы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation>Сервисы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation>Управление</translation>
</message>
@@ -2227,6 +2260,21 @@ Already installed containers were found on the server. All installed containers
<source>Data</source>
<translation type="vanished">Данные</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2323,6 +2371,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation>Серверы</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2614,6 +2667,31 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<source>Key as text</source>
<translation type="vanished">Ключ в виде текста</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">Ключ</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3647,7 +3725,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Данная конфигурация уже была добавлена в приложение</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>Код ошибки: %1. </translation>
</message>
@@ -3741,37 +3819,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: The file could not be opened</source>
<translation>Ошибка QFile: не удалось открыть файл</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: An error occurred when reading from the file</source>
<translation>Ошибка QFile: произошла ошибка при чтении из файла</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: The file could not be accessed</source>
<translation>Ошибка QFile: не удалось получить доступ к файлу</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: An unspecified error occurred</source>
<translation>Ошибка QFile: произошла неизвестная ошибка</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation>Ошибка QFile: произошла фатальная ошибка</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation>Ошибка QFile: операция была прервана</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>Внутренняя ошибка</translation>
</message>

View File

@@ -27,52 +27,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>Звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео. Працює для будь-яких сайтів. Швидкість до %1 MBit/s</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>VPN для доступу до заблокованих сайтів у регіонах з високим рівнем інтернет-цензури. </translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>Amnezia Premium - звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео у високій роздільній здатності. Працює для всіх вебсайтів, навіть у країнах з найвищим рівнем інтернет-цензури.</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>Amnezia Free це безкоштовний VPN для обходу блокувань у країнах з високим рівнем інтернет-цензури</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation>%1 MBit/s</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation>%1 днів</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>Лише популярні сайти, які заблоковані у вашому регіоні, будуть відкриватись за допомогою VPN підключення (Instagram, Facebook, Twitter та ін.). Звичайні сайти будуть відкриватися без використання VPN, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;більш детально на нашому сайті.&lt;/a&gt;</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation>Безкоштовно</translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation>%1 $/місяць</translation>
</message>
@@ -512,6 +512,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>Не можна змінити сервер при активному підключенні</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1553,6 +1568,14 @@ Already installed containers were found on the server. All installed containers
<translation>Політика конфіденційності</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1566,64 +1589,69 @@ Already installed containers were found on the server. All installed containers
<translation>Ціна</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation>Період роботи</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation>Швидкість</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation>Скопійовано</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation>Перезавантажити конфігурацію API</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation>Перезавантажити конфігурацію API?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation>Продовжити</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation>Відмінити</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation>Неможливо перезавантажити конфігурацію API під час активного підключення</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation>Видалити з додатку</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation>Видалити з додатку?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation>Неможливо видалити сервер під час активного підключення</translation>
</message>
@@ -2285,27 +2313,32 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation>Імя сервера</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation>Зберегти</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>Протоколи</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation>Сервіси</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation>Управління</translation>
</message>
@@ -2313,6 +2346,21 @@ Already installed containers were found on the server. All installed containers
<source>Data</source>
<translation type="vanished">Дані</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2409,6 +2457,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation>Сервери</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2700,6 +2753,31 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<source>Key as text</source>
<translation type="vanished">Ключ у вигляді тексту</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">Ключ</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3743,7 +3821,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Ця конфігурація вже була додана в застосунок</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation type="unfinished"></translation>
</message>
@@ -3837,37 +3915,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<source>QFile error: The file could not be opened</source>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: An error occurred when reading from the file</source>
<source>QFile error: The file could not be opened</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: The file could not be accessed</source>
<source>QFile error: An error occurred when reading from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: An unspecified error occurred</source>
<source>QFile error: The file could not be accessed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: A fatal error occurred</source>
<source>QFile error: An unspecified error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>Internal error</translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation type="unfinished"></translation>
</message>
@@ -482,6 +482,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation>فعال کنکشن موجود ہونے کی وجہ سے سرور تبدیل کرنے میں ناکام ہیں</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1420,6 +1435,14 @@ Already installed containers were found on the server. All installed containers
<translation>رازداری کی پالیسی</translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1433,64 +1456,69 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation type="unfinished">چالو کنکشن کے دوران سرور کو ہٹایا نہیں جا سکتا</translation>
</message>
@@ -2100,30 +2128,50 @@ Already installed containers were found on the server. All installed containers
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation>سرور کا نام</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation>محفوظ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation>پروٹوکولات</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation>خدمات</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation>مینجمنٹ</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2216,6 +2264,11 @@ Already installed containers were found on the server. All installed containers
<source>Servers</source>
<translation>سرور</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2491,6 +2544,31 @@ Already installed containers were found on the server. All installed containers
<source>Key as text</source>
<translation type="vanished">متن کے طور پر کلید</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished">کلید</translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3414,7 +3492,7 @@ Already installed containers were found on the server. All installed containers
<translation>یہ تشکیل پہلے ہی ایپلی کیشن میں شامل کی جا چکی ہے</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>ایرر کوڈ: %1. </translation>
</message>
@@ -3519,37 +3597,42 @@ Already installed containers were found on the server. All installed containers
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: The file could not be opened</source>
<translation>QFile کی خرابی: فائل کو نہیں کھولا جا سکا</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: An error occurred when reading from the file</source>
<translation>کیو فائل کی خرابی: فائل سے پڑھتے وقت ایک خرابی پیش آگئی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: The file could not be accessed</source>
<translation>QFile کی خرابی: فائل تک رسائی نہیں ہو سکی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: An unspecified error occurred</source>
<translation>کیو فائل میں خرابی: ایک غیر متعینہ خرابی پیش آگئی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation>کیو فائل میں خرابی: ایک مہلک خرابی پیش آگئی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation>کیو فائل کی خرابی: آپریشن روک دیا گیا تھا</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation>داخلی خامی</translation>
</message>

View File

@@ -4,52 +4,52 @@
<context>
<name>ApiServicesModel</name>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="65"/>
<location filename="../ui/models/apiServicesModel.cpp" line="68"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="69"/>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="71"/>
<location filename="../ui/models/apiServicesModel.cpp" line="74"/>
<source>&lt;p&gt;&lt;a style=&quot;color: #EB5757;&quot;&gt;Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="78"/>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="81"/>
<location filename="../ui/models/apiServicesModel.cpp" line="84"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="94"/>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>%1 MBit/s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="101"/>
<location filename="../ui/models/apiServicesModel.cpp" line="103"/>
<source>%1 days</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="110"/>
<location filename="../ui/models/apiServicesModel.cpp" line="112"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="118"/>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<source>Free</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="120"/>
<location filename="../ui/models/apiServicesModel.cpp" line="122"/>
<source>%1 $/month</source>
<translation type="unfinished"></translation>
</message>
@@ -500,6 +500,21 @@ Already installed containers were found on the server. All installed containers
<source>Unable change server while there is an active connection</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="35"/>
<source>Offline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageHome.qml" line="43"/>
<source>Connection to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageProtocolAwgClientSettings</name>
@@ -1484,6 +1499,14 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation></translation>
</message>
</context>
<context>
<name>PageSettingsApiLanguageList</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiLanguageList.qml" line="61"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsApiServerInfo</name>
<message>
@@ -1497,64 +1520,69 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Work period</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="65"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="77"/>
<source>Speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="122"/>
<source>Copied</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="142"/>
<source>Reload API config</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="147"/>
<source>Reload API config?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="148"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="188"/>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="149"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="189"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="153"/>
<source>Cannot reload API config during active connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="182"/>
<source>Remove from application</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="187"/>
<source>Remove from application?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="193"/>
<source>Cannot remove server during active connection</source>
<translation type="unfinished"></translation>
</message>
@@ -2212,27 +2240,32 @@ And if you don&apos;t like the app, all the more support it - the donation will
<context>
<name>PageSettingsServerInfo</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="161"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="104"/>
<source>Subscription is valid until </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="167"/>
<source>Server name</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="174"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="180"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="220"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="224"/>
<source>Protocols</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="232"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="236"/>
<source>Services</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="242"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="246"/>
<source>Management</source>
<translation></translation>
</message>
@@ -2240,6 +2273,21 @@ And if you don&apos;t like the app, all the more support it - the donation will
<source>Data</source>
<translation type="vanished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="61"/>
<source>Server settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="73"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServerInfo.qml" line="90"/>
<source>Remove server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsServerProtocol</name>
@@ -2340,6 +2388,11 @@ And if you don&apos;t like the app, all the more support it - the donation will
<source>Servers</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSettingsServersList.qml" line="53"/>
<source>Connect to</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSettingsSplitTunneling</name>
@@ -2646,6 +2699,31 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<source>Key as text</source>
<translation type="vanished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="65"/>
<source>Adding a server to&#xa0;connect to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="77"/>
<source>Key</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="88"/>
<source>VPN://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="97"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/DefaultVpn/Pages/PageSetupWizardConfigSource.qml" line="103"/>
<source>Unsupported config file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardCredentials</name>
@@ -3748,37 +3826,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
<source>QFile error: The file could not be opened</source>
<location filename="../core/errorstrings.cpp" line="66"/>
<source>Missing list of available services</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>QFile error: An error occurred when reading from the file</source>
<source>QFile error: The file could not be opened</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="70"/>
<source>QFile error: The file could not be accessed</source>
<source>QFile error: An error occurred when reading from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="71"/>
<source>QFile error: An unspecified error occurred</source>
<source>QFile error: The file could not be accessed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="72"/>
<source>QFile error: A fatal error occurred</source>
<source>QFile error: An unspecified error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
<source>QFile error: A fatal error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="74"/>
<source>QFile error: The operation was aborted</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="80"/>
<location filename="../core/errorstrings.cpp" line="81"/>
<source>ErrorCode: %1. </source>
<translation>: %1. </translation>
</message>
@@ -3846,7 +3929,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="vanished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="77"/>
<location filename="../core/errorstrings.cpp" line="78"/>
<source>Internal error</source>
<translation></translation>
</message>

View File

@@ -55,7 +55,7 @@ void ConnectionController::openConnection()
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromGateway();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by end_date event";
qDebug() << "attempt to update api config by expires_at event";
if (configVersion == ApiConfigSources::Telegram) {
emit updateApiConfigFromTelegram();
} else {

View File

@@ -121,9 +121,8 @@ ErrorCode ExportController::generateNativeConfig(const DockerContainer container
jsonNativeConfig = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg) {
auto clientId = jsonNativeConfig.value(config_key::clientId).toString();
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials, serverController);
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
errorCode = m_clientManagementModel->appendClient(jsonNativeConfig, clientName, container, credentials, serverController);
}
return errorCode;
}
@@ -248,10 +247,10 @@ void ExportController::generateCloakConfig()
emit exportConfigChanged();
}
void ExportController::generateXrayConfig()
void ExportController::generateXrayConfig(const QString &clientName)
{
QJsonObject nativeConfig;
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, "", Proto::Xray, nativeConfig);
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray, nativeConfig);
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;

View File

@@ -28,7 +28,7 @@ public slots:
void generateAwgConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();
void generateXrayConfig();
void generateXrayConfig(const QString &clientName);
QString getConfig();
QString getNativeConfigString();

View File

@@ -51,7 +51,7 @@ QString PageController::getPagePath(PageLoader::PageEnum page)
{
QMetaEnum metaEnum = QMetaEnum::fromType<PageLoader::PageEnum>();
QString pageName = metaEnum.valueToKey(static_cast<int>(page));
return "qrc:/ui/qml/Pages2/" + pageName + ".qml";
return "qrc:/ui/qml/DefaultVpn/Pages/" + pageName + ".qml";
}
void PageController::closeWindow()

View File

@@ -27,6 +27,9 @@ namespace
constexpr char storeEndpoint[] = "store_endpoint";
constexpr char isAvailable[] = "is_available";
constexpr char subscription[] = "subscription";
constexpr char endDate[] = "end_date";
}
namespace serviceType
@@ -51,23 +54,23 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
return QVariant();
QJsonObject service = m_services.at(index.row()).toObject();
QJsonObject serviceInfo = service.value(configKey::serviceInfo).toObject();
auto serviceType = service.value(configKey::serviceType).toString();
auto apiServiceData = m_services.at(index.row());
auto serviceType = apiServiceData.type;
auto isServiceAvailable = apiServiceData.isServiceAvailable;
switch (role) {
case NameRole: {
return serviceInfo.value(configKey::name).toString();
return apiServiceData.serviceInfo.name;
}
case CardDescriptionRole: {
auto speed = serviceInfo.value(configKey::speed).toString();
auto speed = apiServiceData.serviceInfo.speed;
if (serviceType == serviceType::amneziaPremium) {
return tr("Classic VPN for comfortable work, downloading large files and watching videos. "
"Works for any sites. Speed up to %1 MBit/s")
.arg(speed);
} else if (serviceType == serviceType::amneziaFree){
QString description = tr("VPN to access blocked sites in regions with high levels of Internet censorship. ");
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
if (isServiceAvailable) {
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a>");
}
return description;
@@ -83,25 +86,24 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
}
case IsServiceAvailableRole: {
if (serviceType == serviceType::amneziaFree) {
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
if (isServiceAvailable) {
return false;
}
}
return true;
}
case SpeedRole: {
auto speed = serviceInfo.value(configKey::speed).toString();
return tr("%1 MBit/s").arg(speed);
return tr("%1 MBit/s").arg(apiServiceData.serviceInfo.speed);
}
case WorkPeriodRole: {
auto timelimit = serviceInfo.value(configKey::timelimit).toString();
if (timelimit == "0") {
case TimeLimitRole: {
auto timeLimit = apiServiceData.serviceInfo.timeLimit;
if (timeLimit == "0") {
return "";
}
return tr("%1 days").arg(timelimit);
return tr("%1 days").arg(timeLimit);
}
case RegionRole: {
return serviceInfo.value(configKey::region).toString();
return apiServiceData.serviceInfo.region;
}
case FeaturesRole: {
if (serviceType == serviceType::amneziaPremium) {
@@ -113,12 +115,15 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
}
}
case PriceRole: {
auto price = serviceInfo.value(configKey::price).toString();
auto price = apiServiceData.serviceInfo.price;
if (price == "free") {
return tr("Free");
}
return tr("%1 $/month").arg(price);
}
case EndDateRole: {
return QDateTime::fromString(apiServiceData.subscription.endDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
}
}
return QVariant();
@@ -128,15 +133,18 @@ void ApiServicesModel::updateModel(const QJsonObject &data)
{
beginResetModel();
m_countryCode = data.value(configKey::userCountryCode).toString();
m_services = data.value(configKey::services).toArray();
if (m_services.isEmpty()) {
QJsonObject service;
service.insert(configKey::serviceInfo, data.value(configKey::serviceInfo));
service.insert(configKey::serviceType, data.value(configKey::serviceType));
m_services.clear();
m_services.push_back(service);
m_countryCode = data.value(configKey::userCountryCode).toString();
auto services = data.value(configKey::services).toArray();
if (services.isEmpty()) {
m_services.push_back(getApiServicesData(data));
m_selectedServiceIndex = 0;
} else {
for (const auto &service : services) {
m_services.push_back(getApiServicesData(service.toObject()));
}
}
endResetModel();
@@ -149,32 +157,32 @@ void ApiServicesModel::setServiceIndex(const int index)
QJsonObject ApiServicesModel::getSelectedServiceInfo()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceInfo).toObject();
auto service = m_services.at(m_selectedServiceIndex);
return service.serviceInfo.object;
}
QString ApiServicesModel::getSelectedServiceType()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceType).toString();
auto service = m_services.at(m_selectedServiceIndex);
return service.type;
}
QString ApiServicesModel::getSelectedServiceProtocol()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceProtocol).toString();
auto service = m_services.at(m_selectedServiceIndex);
return service.protocol;
}
QString ApiServicesModel::getSelectedServiceName()
{
auto modelIndex = index(m_selectedServiceIndex, 0);
return data(modelIndex, ApiServicesModel::Roles::NameRole).toString();
auto service = m_services.at(m_selectedServiceIndex);
return service.serviceInfo.name;
}
QJsonArray ApiServicesModel::getSelectedServiceCountries()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::availableCountries).toArray();
auto service = m_services.at(m_selectedServiceIndex);
return service.availableCountries;
}
QString ApiServicesModel::getCountryCode()
@@ -184,8 +192,8 @@ QString ApiServicesModel::getCountryCode()
QString ApiServicesModel::getStoreEndpoint()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::storeEndpoint).toString();
auto service = m_services.at(m_selectedServiceIndex);
return service.storeEndpoint;
}
QVariant ApiServicesModel::getSelectedServiceData(const QString roleString)
@@ -209,10 +217,46 @@ QHash<int, QByteArray> ApiServicesModel::roleNames() const
roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsServiceAvailableRole] = "isServiceAvailable";
roles[SpeedRole] = "speed";
roles[WorkPeriodRole] = "workPeriod";
roles[TimeLimitRole] = "timeLimit";
roles[RegionRole] = "region";
roles[FeaturesRole] = "features";
roles[PriceRole] = "price";
roles[EndDateRole] = "endDate";
return roles;
}
ApiServicesModel::ApiServicesData ApiServicesModel::getApiServicesData(const QJsonObject &data)
{
auto serviceInfo = data.value(configKey::serviceInfo).toObject();
auto serviceType = data.value(configKey::serviceType).toString();
auto serviceProtocol = data.value(configKey::serviceProtocol).toString();
auto availableCountries = data.value(configKey::availableCountries).toArray();
auto subscriptionObject = data.value(configKey::subscription).toObject();
ApiServicesData serviceData;
serviceData.serviceInfo.name = serviceInfo.value(configKey::name).toString();
serviceData.serviceInfo.price = serviceInfo.value(configKey::price).toString();
serviceData.serviceInfo.region = serviceInfo.value(configKey::region).toString();
serviceData.serviceInfo.speed = serviceInfo.value(configKey::speed).toString();
serviceData.serviceInfo.timeLimit = serviceInfo.value(configKey::timelimit).toString();
serviceData.type = serviceType;
serviceData.protocol = serviceProtocol;
serviceData.storeEndpoint = serviceInfo.value(configKey::storeEndpoint).toString();
if (serviceInfo.value(configKey::isAvailable).isBool()) {
serviceData.isServiceAvailable = data.value(configKey::isAvailable).toBool();
} else {
serviceData.isServiceAvailable = true;
}
serviceData.serviceInfo.object = serviceInfo;
serviceData.availableCountries = availableCountries;
serviceData.subscription.endDate = subscriptionObject.value(configKey::endDate).toString();
return serviceData;
}

View File

@@ -3,6 +3,7 @@
#include <QAbstractListModel>
#include <QJsonArray>
#include <QJsonObject>
class ApiServicesModel : public QAbstractListModel
{
@@ -15,10 +16,11 @@ public:
ServiceDescriptionRole,
IsServiceAvailableRole,
SpeedRole,
WorkPeriodRole,
TimeLimitRole,
RegionRole,
FeaturesRole,
PriceRole
PriceRole,
EndDateRole
};
explicit ApiServicesModel(QObject *parent = nullptr);
@@ -48,8 +50,40 @@ protected:
QHash<int, QByteArray> roleNames() const override;
private:
struct ServiceInfo
{
QString name;
QString speed;
QString timeLimit;
QString region;
QString price;
QJsonObject object;
};
struct Subscription
{
QString endDate;
};
struct ApiServicesData
{
bool isServiceAvailable;
QString type;
QString protocol;
QString storeEndpoint;
ServiceInfo serviceInfo;
Subscription subscription;
QJsonArray availableCountries;
};
ApiServicesData getApiServicesData(const QJsonObject &data);
QString m_countryCode;
QJsonArray m_services;
QVector<ApiServicesData> m_services;
int m_selectedServiceIndex;
};

View File

@@ -106,6 +106,8 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
error = getOpenVpnClients(container, credentials, serverController, count);
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
error = getWireGuardClients(container, credentials, serverController, count);
} else if (container == DockerContainer::Xray) {
error = getXrayClients(container, credentials, serverController, count);
}
if (error != ErrorCode::NoError) {
endResetModel();
@@ -239,6 +241,68 @@ ErrorCode ClientManagementModel::getWireGuardClients(const DockerContainer conta
}
return error;
}
ErrorCode ClientManagementModel::getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
const QSharedPointer<ServerController> &serverController, int &count)
{
ErrorCode error = ErrorCode::NoError;
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the xray server config file from the server";
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
logger.error() << "Failed to parse xray server config JSON";
return ErrorCode::InternalError;
}
if (!serverConfig.object().contains("inbounds") || serverConfig.object()["inbounds"].toArray().isEmpty()) {
logger.error() << "Invalid xray server config structure";
return ErrorCode::InternalError;
}
const QJsonObject inbound = serverConfig.object()["inbounds"].toArray()[0].toObject();
if (!inbound.contains("settings")) {
logger.error() << "Missing settings in xray inbound config";
return ErrorCode::InternalError;
}
const QJsonObject settings = inbound["settings"].toObject();
if (!settings.contains("clients")) {
logger.error() << "Missing clients in xray settings config";
return ErrorCode::InternalError;
}
const QJsonArray clients = settings["clients"].toArray();
for (const auto &clientValue : clients) {
const QJsonObject clientObj = clientValue.toObject();
if (!clientObj.contains("id")) {
logger.error() << "Missing id in xray client config";
continue;
}
QString clientId = clientObj["id"].toString();
QString xrayDefaultUuid = serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
xrayDefaultUuid.replace("\n", "");
if (!isClientExists(clientId) && clientId != xrayDefaultUuid) {
QJsonObject client;
client[configKey::clientId] = clientId;
QJsonObject userData;
userData[configKey::clientName] = QString("Client %1").arg(count);
client[configKey::userData] = userData;
m_clientsTable.push_back(client);
count++;
}
}
return error;
}
ErrorCode ClientManagementModel::wgShow(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data)
@@ -326,17 +390,67 @@ ErrorCode ClientManagementModel::appendClient(const DockerContainer container, c
const QSharedPointer<ServerController> &serverController)
{
Proto protocol;
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
protocol = Proto::OpenVpn;
} else if (container == DockerContainer::OpenVpn || container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
protocol = ContainerProps::defaultProtocol(container);
} else {
return ErrorCode::NoError;
switch (container) {
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak:
protocol = Proto::OpenVpn;
break;
case DockerContainer::OpenVpn:
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Xray:
protocol = ContainerProps::defaultProtocol(container);
break;
default:
return ErrorCode::NoError;
}
auto protocolConfig = ContainerProps::getProtocolConfigFromContainer(protocol, containerConfig);
return appendClient(protocolConfig, clientName, container, credentials, serverController);
}
return appendClient(protocolConfig.value(config_key::clientId).toString(), clientName, container, credentials, serverController);
ErrorCode ClientManagementModel::appendClient(QJsonObject &protocolConfig, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController)
{
QString clientId;
if (container == DockerContainer::Xray) {
if (!protocolConfig.contains("outbounds")) {
return ErrorCode::InternalError;
}
QJsonArray outbounds = protocolConfig.value("outbounds").toArray();
if (outbounds.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject outbound = outbounds[0].toObject();
if (!outbound.contains("settings")) {
return ErrorCode::InternalError;
}
QJsonObject settings = outbound["settings"].toObject();
if (!settings.contains("vnext")) {
return ErrorCode::InternalError;
}
QJsonArray vnext = settings["vnext"].toArray();
if (vnext.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject vnextObj = vnext[0].toObject();
if (!vnextObj.contains("users")) {
return ErrorCode::InternalError;
}
QJsonArray users = vnextObj["users"].toArray();
if (users.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject user = users[0].toObject();
if (!user.contains("id")) {
return ErrorCode::InternalError;
}
clientId = user["id"].toString();
} else {
clientId = protocolConfig.value(config_key::clientId).toString();
}
return appendClient(clientId, clientName, container, credentials, serverController);
}
ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
@@ -422,10 +536,27 @@ ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContain
auto client = m_clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, serverController);
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
errorCode = revokeWireGuard(row, container, credentials, serverController);
switch(container)
{
case DockerContainer::OpenVpn:
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, serverController);
break;
}
case DockerContainer::WireGuard:
case DockerContainer::Awg: {
errorCode = revokeWireGuard(row, container, credentials, serverController);
break;
}
case DockerContainer::Xray: {
errorCode = revokeXray(row, container, credentials, serverController);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
if (errorCode == ErrorCode::NoError) {
@@ -463,19 +594,69 @@ ErrorCode ClientManagementModel::revokeClient(const QJsonObject &containerConfig
}
Proto protocol;
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
protocol = Proto::OpenVpn;
} else if (container == DockerContainer::OpenVpn || container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
protocol = ContainerProps::defaultProtocol(container);
} else {
return ErrorCode::NoError;
switch(container)
{
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak: {
protocol = Proto::OpenVpn;
break;
}
case DockerContainer::OpenVpn:
case DockerContainer::WireGuard:
case DockerContainer::Awg:
case DockerContainer::Xray: {
protocol = ContainerProps::defaultProtocol(container);
break;
}
default: {
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
}
auto protocolConfig = ContainerProps::getProtocolConfigFromContainer(protocol, containerConfig);
QString clientId;
if (container == DockerContainer::Xray) {
if (!protocolConfig.contains("outbounds")) {
return ErrorCode::InternalError;
}
QJsonArray outbounds = protocolConfig.value("outbounds").toArray();
if (outbounds.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject outbound = outbounds[0].toObject();
if (!outbound.contains("settings")) {
return ErrorCode::InternalError;
}
QJsonObject settings = outbound["settings"].toObject();
if (!settings.contains("vnext")) {
return ErrorCode::InternalError;
}
QJsonArray vnext = settings["vnext"].toArray();
if (vnext.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject vnextObj = vnext[0].toObject();
if (!vnextObj.contains("users")) {
return ErrorCode::InternalError;
}
QJsonArray users = vnextObj["users"].toArray();
if (users.isEmpty()) {
return ErrorCode::InternalError;
}
QJsonObject user = users[0].toObject();
if (!user.contains("id")) {
return ErrorCode::InternalError;
}
clientId = user["id"].toString();
} else {
clientId = protocolConfig.value(config_key::clientId).toString();
}
int row;
bool clientExists = false;
QString clientId = protocolConfig.value(config_key::clientId).toString();
for (row = 0; row < rowCount(); row++) {
auto client = m_clientsTable.at(row).toObject();
if (clientId == client.value(configKey::clientId).toString()) {
@@ -487,11 +668,28 @@ ErrorCode ClientManagementModel::revokeClient(const QJsonObject &containerConfig
return errorCode;
}
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
switch (container)
{
case DockerContainer::OpenVpn:
case DockerContainer::ShadowSocks:
case DockerContainer::Cloak: {
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, serverController);
} else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) {
errorCode = revokeWireGuard(row, container, credentials, serverController);
break;
}
case DockerContainer::WireGuard:
case DockerContainer::Awg: {
errorCode = revokeWireGuard(row, container, credentials, serverController);
break;
}
case DockerContainer::Xray: {
errorCode = revokeXray(row, container, credentials, serverController);
break;
}
default:
logger.error() << "Internal error: received unexpected container type";
return ErrorCode::InternalError;
}
return errorCode;
}
@@ -594,6 +792,117 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont
return ErrorCode::NoError;
}
ErrorCode ClientManagementModel::revokeXray(const int row,
const DockerContainer container,
const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController)
{
ErrorCode error = ErrorCode::NoError;
// Get server config
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = serverController->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the xray server config file";
return error;
}
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
if (serverConfig.isNull()) {
logger.error() << "Failed to parse xray server config JSON";
return ErrorCode::InternalError;
}
// Get client ID to remove
auto client = m_clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
// Remove client from server config
QJsonObject configObj = serverConfig.object();
if (!configObj.contains("inbounds")) {
logger.error() << "Missing inbounds in xray config";
return ErrorCode::InternalError;
}
QJsonArray inbounds = configObj["inbounds"].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Empty inbounds array in xray config";
return ErrorCode::InternalError;
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains("settings")) {
logger.error() << "Missing settings in xray inbound config";
return ErrorCode::InternalError;
}
QJsonObject settings = inbound["settings"].toObject();
if (!settings.contains("clients")) {
logger.error() << "Missing clients in xray settings";
return ErrorCode::InternalError;
}
QJsonArray clients = settings["clients"].toArray();
if (clients.isEmpty()) {
logger.error() << "Empty clients array in xray config";
return ErrorCode::InternalError;
}
for (int i = 0; i < clients.size(); ++i) {
QJsonObject clientObj = clients[i].toObject();
if (clientObj.contains("id") && clientObj["id"].toString() == clientId) {
clients.removeAt(i);
break;
}
}
// Update server config
settings["clients"] = clients;
inbound["settings"] = settings;
inbounds[0] = inbound;
configObj["inbounds"] = inbounds;
// Upload updated config
error = serverController->uploadTextFileToContainer(
container,
credentials,
QJsonDocument(configObj).toJson(),
serverConfigPath
);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload updated xray config";
return error;
}
// Remove from local table
beginRemoveRows(QModelIndex(), row, row);
m_clientsTable.removeAt(row);
endRemoveRows();
// Update clients table file on server
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable")
.arg(ContainerProps::containerTypeToString(container));
error = serverController->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file";
}
// Restart container
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
error = serverController->runScript(
credentials,
serverController->replaceVars(restartScript, serverController->genVarsForScript(credentials, container))
);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to restart xray container";
return error;
}
return error;
}
QHash<int, QByteArray> ClientManagementModel::roleNames() const
{
QHash<int, QByteArray> roles;
@@ -604,4 +913,4 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
roles[DataSentRole] = "dataSent";
roles[AllowedIpsRole] = "allowedIps";
return roles;
}
}

View File

@@ -40,6 +40,8 @@ public slots:
const QSharedPointer<ServerController> &serverController);
ErrorCode appendClient(const DockerContainer container, const ServerCredentials &credentials, const QJsonObject &containerConfig,
const QString &clientName, const QSharedPointer<ServerController> &serverController);
ErrorCode appendClient(QJsonObject &protocolConfig, const QString &clientName,const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController);
ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController);
ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container, const ServerCredentials &credentials,
@@ -64,11 +66,15 @@ private:
const QSharedPointer<ServerController> &serverController);
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController);
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController);
ErrorCode getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count);
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, int &count);
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
const QSharedPointer<ServerController> &serverController, int &count);
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data);

View File

@@ -22,7 +22,7 @@ namespace
constexpr char serviceProtocol[] = "service_protocol";
constexpr char publicKeyInfo[] = "public_key";
constexpr char endDate[] = "end_date";
constexpr char expiresAt[] = "expires_at";
}
}
@@ -39,6 +39,9 @@ ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
emit ServersModel::defaultServerNameChanged();
updateDefaultServerContainersModel();
});
connect(this, &ServersModel::processedServerIndexChanged, this, &ServersModel::processedServerChanged);
connect(this, &ServersModel::dataChanged, this, &ServersModel::processedServerChanged);
}
int ServersModel::rowCount(const QModelIndex &parent) const
@@ -79,6 +82,12 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
return true;
}
bool ServersModel::setData(const int index, const QVariant &value, int role)
{
QModelIndex modelIndex = this->index(index);
return setData(modelIndex, value, role);
}
QVariant ServersModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_servers.size())) {
@@ -679,6 +688,18 @@ QVariant ServersModel::getProcessedServerData(const QString roleString)
return {};
}
bool ServersModel::setProcessedServerData(const QString &roleString, const QVariant &value)
{
const auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return setData(m_processedServerIndex, value, it.key());
}
}
return false;
}
bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
{
auto server = m_servers.at(m_defaultServerIndex).toObject();
@@ -718,9 +739,9 @@ bool ServersModel::isApiKeyExpired(const int serverIndex)
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto publicKeyInfo = apiConfig.value(configKey::publicKeyInfo).toObject();
const QString endDate = publicKeyInfo.value(configKey::endDate).toString();
if (endDate.isEmpty()) {
publicKeyInfo.insert(configKey::endDate, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate));
const QString expiresAt = publicKeyInfo.value(configKey::expiresAt).toString();
if (expiresAt.isEmpty()) {
publicKeyInfo.insert(configKey::expiresAt, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate));
apiConfig.insert(configKey::publicKeyInfo, publicKeyInfo);
serverConfig.insert(configKey::apiConfig, apiConfig);
editServer(serverConfig, serverIndex);
@@ -728,8 +749,8 @@ bool ServersModel::isApiKeyExpired(const int serverIndex)
return false;
}
auto endDateDateTime = QDateTime::fromString(endDate, Qt::ISODate).toUTC();
if (endDateDateTime < QDateTime::currentDateTimeUtc()) {
auto expiresAtDateTime = QDateTime::fromString(expiresAt, Qt::ISODate).toUTC();
if (expiresAtDateTime < QDateTime::currentDateTimeUtc()) {
return true;
}
return false;

View File

@@ -46,6 +46,7 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setData(const int index, const QVariant &value, int role = Qt::EditRole);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant data(const int index, int role = Qt::DisplayRole) const;
@@ -115,6 +116,7 @@ public slots:
QVariant getDefaultServerData(const QString roleString);
QVariant getProcessedServerData(const QString roleString);
bool setProcessedServerData(const QString &roleString, const QVariant &value);
bool isDefaultServerDefaultContainerHasSplitTunneling();
@@ -127,6 +129,9 @@ protected:
signals:
void processedServerIndexChanged(const int index);
// emitted when the processed server index or processed server data is changed
void processedServerChanged();
void defaultServerIndexChanged(const int index);
void defaultServerNameChanged();
void defaultServerDescriptionChanged();

View File

@@ -0,0 +1,36 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "../Controls/TextTypes"
import "../Controls"
ButtonType {
defaultBackgroundColor: Style.color.accent1
defaultBorderColor: Style.color.gray3
defaultTextColor: Style.color.white
defaultImageColor: Style.color.white
hoveredBackgroundColor: Style.color.accent2
hoveredBorderColor: Style.color.gray3
hoveredTextColor: Style.color.white
hoveredImageColor: Style.color.white
pressedBackgroundColor: Style.color.accent3
pressedBorderColor: Style.color.gray3
pressedTextColor: Style.color.white
pressedImageColor: Style.color.white
disabledBackgroundColor: Style.color.gray6
disabledBorderColor: Style.color.gray3
disabledTextColor: Style.color.gray2
disabledImageColor: Style.color.gray2
defaultBorderWidth: 0
disabledBorderWidth: 0
}

View File

@@ -0,0 +1,36 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "../Controls/TextTypes"
import "../Controls"
ButtonType {
defaultBackgroundColor: Style.color.white
defaultBorderColor: Style.color.gray3
defaultTextColor: Style.color.accent1
defaultImageColor: Style.color.accent1
hoveredBackgroundColor: Style.color.gray1
hoveredBorderColor: Style.color.gray3
hoveredTextColor: Style.color.accent2
hoveredImageColor: Style.color.accent2
pressedBackgroundColor: Style.color.gray2
pressedBorderColor: Style.color.gray3
pressedTextColor: Style.color.accent3
pressedImageColor: Style.color.accent3
disabledBackgroundColor: Style.color.white
disabledBorderColor: Style.color.gray3
disabledTextColor: Style.color.gray8
disabledImageColor: Style.color.gray8
defaultBorderWidth: 0
disabledBorderWidth: 0
}

View File

@@ -0,0 +1,37 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "../Controls/TextTypes"
import "../Controls"
ButtonType {
defaultBackgroundColor: Style.color.white
defaultBorderColor: Style.color.gray3
defaultTextColor: Style.color.black
defaultImageColor: Style.color.black
hoveredBackgroundColor: Style.color.white
hoveredBorderColor: Style.color.gray6
hoveredTextColor: Style.color.black
hoveredImageColor: Style.color.black
pressedBackgroundColor: Style.color.gray1
pressedBorderColor: Style.color.gray6
pressedTextColor: Style.color.black
pressedImageColor: Style.color.black
disabledBackgroundColor: Style.color.gray3
disabledBorderColor: Style.color.gray2
disabledTextColor: Style.color.gray9
disabledImageColor: Style.color.gray9
defaultBorderWidth: 1
disabledBorderWidth: 1
hoveredBorderWidth: 1
}

View File

@@ -0,0 +1,37 @@
pragma Singleton
import QtQuick
Item {
readonly property int screenWidth: 380
readonly property int screenHeight: 680
function isMobile() {
if (Qt.platform.os === "android" ||
Qt.platform.os === "ios") {
return true
}
return false
}
function isDesktop() {
if (Qt.platform.os === "windows" ||
Qt.platform.os === "linux" ||
Qt.platform.os === "osx") {
return true
}
return false
}
TextEdit {
id: clipboard
visible: false
}
function copyToClipBoard(text) {
clipboard.text = text
clipboard.selectAll()
clipboard.copy()
clipboard.select(0, 0)
}
}

View File

@@ -0,0 +1,30 @@
pragma Singleton
import QtQuick
QtObject {
property QtObject color: QtObject {
readonly property color transparent: 'transparent'
readonly property color gray1: '#F2F2F7'
readonly property color gray2: '#E5E5EA'
readonly property color gray3: '#D1D1D6'
readonly property color gray4: '#C7C7CC'
readonly property color gray5: '#AEAEB2'
readonly property color gray6: '#8E8E93'
readonly property color gray7: '#7C7C83'
readonly property color gray8: '#707075'
readonly property color gray9: '#57575B'
readonly property color accent1: '#007AFF'
readonly property color accent2: '#0B6EDA'
readonly property color accent3: '#1256A1'
readonly property color error: '#FF3B30'
readonly property color warning: '#FF9500'
readonly property color success: '#34C759'
readonly property color black: '#000000'
readonly property color white: '#FFFFFF'
readonly property color transparentBlack: Qt.rgba(14/255, 14/255, 17/255, 0.8)
}
readonly property string font: "Vela Sans GX"
}

View File

@@ -0,0 +1,4 @@
module Config
singleton DeviceInfo 1.0 DeviceInfo.qml
singleton Style 1.0 Style.qml

View File

@@ -0,0 +1,70 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import Config 1.0
Popup {
id: root
anchors.centerIn: parent
modal: true
closePolicy: Popup.NoAutoClose
visible: false
Overlay.modal: Rectangle {
color: Style.color.transparentBlack
}
background: Rectangle {
color: Style.color.transparent
}
BusyIndicator {
id: busyIndicator
visible: true
running: true
contentItem: Item {
implicitWidth: 46
implicitHeight: 46
transformOrigin: Item.Center
Shape {
id: shape
width: parent.implicitWidth
height: parent.implicitHeight
anchors.bottom: parent.bottom
anchors.right: parent.right
layer.enabled: true
layer.samples: 4
ShapePath {
fillColor: Style.color.transparent
strokeColor: Style.color.gray3
strokeWidth: 3
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: shape.width / 2
centerY: shape.height / 2
radiusX: 18
radiusY: 18
startAngle: 225
sweepAngle: -90
}
}
RotationAnimator {
target: shape
running: busyIndicator.visible && busyIndicator.running
from: 0
to: 360
loops: Animation.Infinite
duration: 1250
}
}
}
}
}

View File

@@ -0,0 +1,154 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "TextTypes"
Button {
id: root
property string defaultBackgroundColor: Style.color.white
property string defaultBorderColor: Style.color.gray3
property string defaultTextColor: Style.color.accent1
property string defaultImageColor: Style.color.accent1
property string hoveredBackgroundColor: Style.color.gray1
property string hoveredBorderColor: Style.color.gray3
property string hoveredTextColor: Style.color.accent2
property string hoveredImageColor: Style.color.accent2
property string pressedBackgroundColor: Style.color.gray2
property string pressedBorderColor: Style.color.gray3
property string pressedTextColor: Style.color.accent3
property string pressedImageColor: Style.color.accent3
property string disabledBackgroundColor: Style.color.white
property string disabledBorderColor: Style.color.gray3
property string disabledTextColor: Style.color.gray8
property string disabledImageColor: Style.color.gray8
property int defaultBorderWidth: 0
property int disabledBorderWidth: 0
property int hoveredBorderWidth: 0
property string imageSource: ""
readonly property bool isImageOnly: root.text !== ""
background: Rectangle {
id: background
anchors.fill: parent
radius: 6
color: root.enabled ? root.defaultBackgroundColor : root.disabledBackgroundColor
border.color: root.enabled ? root.defaultBorderColor : root.disabledBorderColor
border.width: root.enabled ? root.defaultBorderWidth : root.disabledBorderWidth
}
MouseArea {
id: mouseArea
anchors.fill: background
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
enabled: root.enabled
onEntered: {
background.color = root.hoveredBackgroundColor
background.border.color = root.hoveredBorderColor
background.border.width = root.hoveredBorderWidth
image.imageColor = root.hoveredImageColor
buttonText.color = root.hoveredTextColor
}
onExited: {
background.color = root.defaultBackgroundColor
background.border.color = root.defaultBorderColor
background.border.width = root.defaultBorderWidth
image.imageColor = root.defaultImageColor
buttonText.color = root.defaultTextColor
}
onPressedChanged: {
if (pressed) {
background.color = root.pressedBackgroundColor
background.border.color = root.pressedBorderColor
image.imageColor = root.pressedImageColor
buttonText.color = root.pressedTextColor
} else if (entered) {
background.color = root.hoveredBackgroundColor
background.border.color = root.hoveredBorderColor
image.imageColor = root.hoveredImageColor
buttonText.color = root.hoveredTextColor
} else {
background.color = root.defaultBackgroundColor
background.border.color = root.defaultBorderColor
image.imageColor = root.defaultImageColor
buttonText.color = root.defaultTextColor
}
}
onClicked: {
root.clicked()
}
}
contentItem: Item {
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
RowLayout {
id: content
anchors.fill: parent
MediumTextType {
id: buttonText
Layout.fillWidth: true
Layout.topMargin: 12
Layout.bottomMargin: 12
Layout.leftMargin: 12
Layout.rightMargin: 12
visible: root.isImageOnly
color: root.defaultTextColor
text: root.text
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
Image {
id: image
property color imageColor: root.enabled ? root.defaultImageColor : root.disabledImageColor
Layout.preferredHeight: 22
Layout.preferredWidth: 22
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 12
Layout.bottomMargin: 12
Layout.leftMargin: 12
Layout.rightMargin: 12
source: root.imageSource
visible: root.imageSource === "" ? false : true
layer {
enabled: true
effect: ColorOverlay {
color: image.imageColor
}
}
}
}
}
}

View File

@@ -0,0 +1,99 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "TextTypes"
Button {
id: root
property string defaultBackgroundColor: "#FFFFFF"
property string defaultBorderColor: "#D1D1D6"
property string defaultTextColor: "#000000"
property string defaultImageColor: "#000000"
property string hoveredBackgroundColor: "#FFFFFF"
property string hoveredBorderColor: "#D1D1D6"
property string hoveredTextColor: "#D1D1D6"
property string hoveredImageColor: "#D1D1D6"
property string pressedBackgroundColor: "#FFFFFF"
property string pressedBorderColor: "#D1D1D6"
property string pressedTextColor: "#D1D1D6"
property string pressedImageColor: "#D1D1D6"
property string disabledBackgroundColor: "#FFFFFF"
property string disabledBorderColor: "#D1D1D6"
property string disabledTextColor: "#D1D1D6"
property string disabledImageColor: "#D1D1D6"
property string imageSource: "qrc:/images/controls/chevron-down.svg"
hoverEnabled: true
background: Rectangle {
id: focusBorder
color: root.defaultBackgroundColor
border.color: root.defaultBorderColor
border.width: 1
anchors.fill: parent
radius: 6
}
MouseArea {
anchors.fill: focusBorder
enabled: false
cursorShape: Qt.PointingHandCursor
}
contentItem: Item {
anchors.fill: focusBorder
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
RowLayout {
id: content
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
MediumTextType {
id: buttonText
Layout.fillWidth: true
Layout.topMargin: 12
Layout.bottomMargin: 12
color: root.defaultTextColor
text: root.text
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
}
Image {
Layout.preferredHeight: 22
Layout.preferredWidth: 22
source: root.imageSource
visible: root.imageSource === "" ? false : true
layer {
enabled: true
effect: ColorOverlay {
color: root.defaultImageColor
}
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "TextTypes"
TextField {
id: root
property string defaultBackgroundColor: Style.color.white
property string defaultBorderColor: Style.color.gray3
property string defaultTextColor: Style.color.gray6
property string hoveredBackgroundColor: Style.color.white
property string hoveredBorderColor: Style.color.gray6
property string hoveredTextColor: Style.color.black
property string disabledBackgroundColor: Style.color.gray2
property string disabledBorderColor: Style.color.gray3
property string disabledTextColor: Style.color.gray9
color: root.enabled ? root.defaultTextColor : (root.hovered || root.pressed) ? root.hoveredTextColor : root.disabledTextColor
background: Rectangle {
anchors.fill: parent
color: root.enabled ? root.defaultBackgroundColor : (root.hovered || root.pressed) ? root.hoveredBackgroundColor : root.disabledBackgroundColor
border.color: root.enabled ? root.defaultBorderColor : (root.hovered || root.pressed) ? root.hoveredBorderColor : root.disabledBorderColor
border.width: 1
radius: 6
}
topPadding: 12
bottomPadding: 12
leftPadding: 16
rightPadding: 16
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
selectionColor: Style.color.accent1
selectedTextColor: Style.color.white
font.pixelSize: 17
font.weight: 400
font.family: Style.font
wrapMode: TextEdit.Wrap
verticalAlignment: Text.AlignTop
}

View File

@@ -0,0 +1,96 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Config 1.0
import "TextTypes"
import "../Components"
Popup {
id: root
property string text
property bool closeButtonVisible: true
leftMargin: 25
rightMargin: 25
bottomMargin: 70
width: parent.width - leftMargin - rightMargin
anchors.centerIn: parent
modal: root.closeButtonVisible
closePolicy: Popup.CloseOnEscape
Overlay.modal: Rectangle {
visible: root.closeButtonVisible
color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
}
background: Rectangle {
anchors.fill: parent
color: Style.color.white
radius: 8
layer.enabled: true
layer.effect: DropShadow {
color: Style.color.gray3
horizontalOffset: 0
verticalOffset: 1
radius: 10
samples: 25
}
}
contentItem: Item {
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
anchors.fill: parent
RowLayout {
id: content
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
XSmallTextType {
horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
text: root.text
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
Item {
id: focusItem
KeyNavigation.tab: closeButton
}
WhiteButtonNoBorder {
id: closeButton
visible: closeButtonVisible
imageSource: "qrc:/images/controls/x-circle.svg"
onClicked: function() {
root.close()
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
import QtQuick
import Config 1.0
Text {
lineHeight: 34
lineHeightMode: Text.FixedHeight
color: Style.color.black
font.pixelSize: 28
font.weight: 700
font.family: Style.font
wrapMode: Text.WordWrap
}

View File

@@ -0,0 +1,15 @@
import QtQuick
import Config 1.0
Text {
lineHeight: 24
lineHeightMode: Text.FixedHeight
color: Style.color.black
font.pixelSize: 20
font.weight: 700
font.family: Style.font
wrapMode: Text.WordWrap
}

View File

@@ -0,0 +1,15 @@
import QtQuick
import Config 1.0
Text {
lineHeight: 22
lineHeightMode: Text.FixedHeight
color: Style.color.black
font.pixelSize: 17
font.weight: 400
font.family: Style.font
wrapMode: Text.WordWrap
}

View File

@@ -0,0 +1,15 @@
import QtQuick
import Config 1.0
Text {
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: Style.color.black
font.pixelSize: 13
font.weight: 400
font.family: Style.font
wrapMode: Text.WordWrap
}

View File

@@ -0,0 +1,122 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Config 1.0
import "../Components"
import "../Controls"
import "../Controls/TextTypes"
Page {
id: root
ColumnLayout {
anchors.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 36
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
Text {
lineHeight: 68
lineHeightMode: Text.FixedHeight
color: Style.color.gray2
font.pixelSize: 56
font.weight: 700
font.family: Style.font
horizontalAlignment: Qt.AlignLeft
text: ConnectionController.isConnected ? qsTr("Online") : qsTr("Offline")
}
Item {
Layout.fillHeight: true
}
XSmallTextType {
text: qsTr("Connection to")
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
}
RowLayout {
DropDownType {
Layout.fillWidth: true
text: ServersModel.defaultServerName
onClicked: function() {
PageController.goToPage(PageEnum.PageSettingsServersList)
}
}
WhiteButtonWithBorder {
imageSource: "qrc:/images/controls/plus.svg"
onClicked: function() {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
}
}
}
Button {
id: connectButton
Layout.fillWidth: true
implicitHeight: 358
Layout.topMargin: 16
background: Rectangle {
anchors.fill: parent
radius: 16
color: {
if (ConnectionController.isConnectionInProgress) {
return Style.color.accent3
} else if (ConnectionController.isConnected) {
return Style.color.accent1
} else {
return Style.color.black
}
}
ColumnLayout {
anchors.centerIn: parent
Image {
Layout.alignment: Qt.AlignCenter
source: "qrc:/images/controls/connect-button.svg"
}
Header3TextType {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 24
text: ConnectionController.connectionStateText
color: Style.color.white
}
Item {
Layout.fillWidth: true
}
}
}
onClicked: function() {
ServersModel.setProcessedServerIndex(ServersModel.defaultIndex)
ConnectionController.connectButtonClicked()
}
}
}
}

View File

@@ -0,0 +1,103 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Config 1.0
import "../Components"
import "../Controls"
import "../Controls/TextTypes"
Page {
id: root
Connections {
target: InstallController
function onRemoveProcessedServerFinished(finishedMessage) {
if (!ServersModel.getServersCount()) {
PageController.goToStartPage()
} else {
PageController.closePage()
}
PageController.showNotificationMessage(finishedMessage)
}
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.topMargin: 8
WhiteButtonNoBorder {
id: backButton
imageSource: "qrc:/images/controls/arrow-left.svg"
onClicked: PageController.closePage()
}
Item {
Layout.fillWidth: true
}
}
Header1TextType {
id: header
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 24
Layout.fillWidth: true
text: qsTr("Server settings")
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
}
XSmallTextType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 8
Layout.fillWidth: true
text: qsTr("Name")
}
InputType {
id: textKey
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true
}
WhiteButtonWithBorder {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 24
Layout.fillWidth: true
text: qsTr("Remove server")
onClicked: function() {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
}
Item {
Layout.fillHeight: true
}
}
}

View File

@@ -0,0 +1,165 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Config 1.0
import "../Components"
import "../Controls"
import "../Controls/TextTypes"
Page {
id: root
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.topMargin: 8
WhiteButtonNoBorder {
id: backButton
imageSource: "qrc:/images/controls/arrow-left.svg"
onClicked: PageController.closePage()
}
Item {
Layout.fillWidth: true
}
WhiteButtonNoBorder {
imageSource: "qrc:/images/controls/plus.svg"
onClicked: function() {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
}
}
}
Header1TextType {
id: header
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true
text: qsTr("Connect to")
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
}
ButtonGroup {
id: serversRadioButtonGroup
}
ListView {
id: serversListView
Layout.topMargin: 16
Layout.fillHeight: true
Layout.fillWidth: true
model: ServersModel
currentIndex: ServersModel.defaultIndex
ScrollBar.vertical: ScrollBar {}
Connections {
target: ServersModel
function onDefaultServerIndexChanged(serverIndex) {
serversListView.currentIndex = serverIndex
serversListView.positionViewAtIndex(serversListView.currentIndex, ListView.Contain)
}
}
Component.onCompleted: positionViewAtIndex(currentIndex, ListView.Center)
delegate: Item {
id: menuContentDelegate
required property string name
required property int index
implicitWidth: serversListView.width
implicitHeight: serverItem.implicitHeight
RadioButton {
id: serverItem
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
ButtonGroup.group: serversRadioButtonGroup
checked: index === serversListView.currentIndex
indicator: Item { }
contentItem: Item {
id: contentContainer
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: content.implicitHeight
Rectangle {
anchors.fill: parent
radius: 8
color: serverItem.checked ? Style.color.gray1 : Style.color.transparent
}
RowLayout {
id: content
anchors.fill: parent
Header3TextType {
Layout.fillWidth: true
Layout.leftMargin: 8
Layout.topMargin: 19
Layout.bottomMargin: 19
text: name
color: serverItem.hovered ? Style.color.gray9 : Style.color.black
}
ButtonType {
Layout.rightMargin: 8
imageSource: "qrc:/images/controls/edit-3.svg"
hoveredBorderColor: Style.color.gray2
hoveredBorderWidth: 1
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
}
}
}
onClicked: function() {
ServersModel.defaultIndex = index
}
MouseArea {
anchors.fill: serverItem
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
}
}
}
}

View File

@@ -0,0 +1,112 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Config 1.0
import "../Components"
import "../Controls"
import "../Controls/TextTypes"
Page {
id: root
Connections {
target: ImportController
function onImportErrorOccurred(error, goToPageHome) {
PageController.showErrorMessage(error)
}
function onImportFinished() {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
}
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.topMargin: 8
WhiteButtonNoBorder {
id: backButton
imageSource: "qrc:/images/controls/arrow-left.svg"
onClicked: PageController.closePage()
}
Item {
Layout.fillWidth: true
}
}
Header1TextType {
id: header
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 24
Layout.fillWidth: true
text: qsTr("Adding a server to connect to")
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
}
XSmallTextType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 8
Layout.fillWidth: true
text: qsTr("Key")
}
InputType {
id: textKey
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true
Layout.preferredHeight: 308
placeholderText: qsTr("VPN://")
}
BlueButtonNoBorder {
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true
text: qsTr("Add")
onClicked: function() {
if (ImportController.extractConfigFromData(textKey.text)) {
ImportController.importConfig()
} else {
PageController.showErrorMessage(qsTr("Unsupported config file"))
}
}
}
Item {
Layout.fillHeight: true
}
}
}

View File

@@ -0,0 +1,195 @@
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import Config 1.0
import PageEnum 1.0
import "Controls"
import "Pages"
ApplicationWindow {
id: root
objectName: "mainWindow"
visible: true
width: DeviceInfo.screenWidth
height: DeviceInfo.screenHeight
minimumWidth: DeviceInfo.isDesktop() ? 360 : 0
minimumHeight: DeviceInfo.isDesktop() ? 640 : 0
maximumWidth: 600
maximumHeight: 800
color: Style.color.white
onClosing: function() {
console.debug("QML onClosing signal")
PageController.closeWindow()
}
title: "DefaultVPN"
Connections {
target: PageController
function onRaiseMainWindow() {
root.show()
root.raise()
root.requestActivate()
}
function onHideMainWindow() {
root.hide()
}
function onShowErrorMessage(errorMessage) {
popupErrorMessage.text = errorMessage
popupErrorMessage.open()
}
function onShowNotificationMessage(message) {
popupNotificationMessage.text = message
popupNotificationMessage.closeButtonVisible = false
popupNotificationMessage.open()
popupNotificationTimer.start()
}
function onShowBusyIndicator(visible) {
busyIndicator.visible = visible
PageController.disableControls(visible)
}
function onClosePage() {
if (stackview.depth <= 1) {
PageController.hideWindow()
return
}
stackview.pop()
}
function onGoToPage(page, slide) {
var pagePath = PageController.getPagePath(page)
if (slide) {
stackview.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
} else {
stackview.push(pagePath, { "objectName" : pagePath }, StackView.Immediate)
}
}
function onGoToStartPage() {
while (stackview.depth > 1) {
stackview.pop()
}
}
}
Connections {
target: SettingsController
function onChangeSettingsFinished(finishedMessage) {
PageController.showNotificationMessage(finishedMessage)
}
}
StackView {
id: stackview
anchors.fill: parent
Component.onCompleted: {
var pagePath = PageController.getPagePath(PageEnum.PageHome)
ServersModel.processedIndex = ServersModel.defaultIndex
stackview.push(pagePath, { "objectName" : pagePath })
}
}
Item {
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
implicitHeight: popupNotificationMessage.height
PopupType {
id: popupNotificationMessage
}
Timer {
id: popupNotificationTimer
interval: 3000
repeat: false
running: false
onTriggered: {
popupNotificationMessage.close()
}
}
}
Item {
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
implicitHeight: popupErrorMessage.height
PopupType {
id: popupErrorMessage
}
}
// Item {
// anchors.fill: parent
// QuestionDrawer {
// id: questionDrawer
// anchors.fill: parent
// }
// }
Item {
anchors.fill: parent
BusyIndicatorType {
id: busyIndicator
anchors.centerIn: parent
z: 1
}
}
// function showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) {
// questionDrawer.headerText = headerText
// questionDrawer.descriptionText = descriptionText
// questionDrawer.yesButtonText = yesButtonText
// questionDrawer.noButtonText = noButtonText
// questionDrawer.yesButtonFunction = function() {
// questionDrawer.close()
// if (yesButtonFunction && typeof yesButtonFunction === "function") {
// yesButtonFunction()
// }
// }
// questionDrawer.noButtonFunction = function() {
// questionDrawer.close()
// if (noButtonFunction && typeof noButtonFunction === "function") {
// noButtonFunction()
// }
// }
// questionDrawer.open()
// }
FileDialog {
id: mainFileDialog
property bool isSaveMode: false
objectName: "mainFileDialog"
fileMode: isSaveMode ? FileDialog.SaveFile : FileDialog.OpenFile
onAccepted: SystemController.fileDialogClosed(true)
onRejected: SystemController.fileDialogClosed(false)
}
}

View File

@@ -54,8 +54,14 @@ PageType {
imageSource: "qrc:/images/controls/download.svg"
checked: index === ApiCountryModel.currentIndex
checkable: !ConnectionController.isConnected
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection"))
return
}
if (index !== ApiCountryModel.currentIndex) {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex

View File

@@ -56,12 +56,15 @@ PageType {
}
LabelWithImageType {
property bool showSubscriptionEndDate: ServersModel.getProcessedServerData("isCountrySelectionAvailable")
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/history.svg"
leftText: qsTr("Work period")
rightText: ApiServicesModel.getSelectedServiceData("workPeriod")
leftText: showSubscriptionEndDate ? qsTr("Valid until") : qsTr("Work period")
rightText: showSubscriptionEndDate ? ApiServicesModel.getSelectedServiceData("endDate")
: ApiServicesModel.getSelectedServiceData("workPeriod")
visible: rightText !== ""
}

View File

@@ -25,6 +25,8 @@ PageType {
property int pageSettingsApiServerInfo: 3
property int pageSettingsApiLanguageList: 4
property var processedServer
defaultActiveFocusItem: focusItem
Connections {
@@ -35,8 +37,18 @@ PageType {
}
}
Connections {
target: ServersModel
function onProcessedServerChanged() {
root.processedServer = proxyServersModel.get(0)
}
}
SortFilterProxyModel {
id: proxyServersModel
objectName: "proxyServersModel"
sourceModel: ServersModel
filters: [
ValueFilter {
@@ -44,147 +56,139 @@ PageType {
value: true
}
]
Component.onCompleted: {
root.processedServer = proxyServersModel.get(0)
}
}
Item {
id: focusItem
KeyNavigation.tab: header
//KeyNavigation.tab: header
}
ColumnLayout {
anchors.fill: parent
spacing: 16
spacing: 4
Repeater {
id: header
model: proxyServersModel
BackButtonType {
id: backButton
activeFocusOnTab: true
onFocusChanged: {
header.itemAt(0).focusItem.forceActiveFocus()
Layout.topMargin: 20
KeyNavigation.tab: headerContent.actionButton
backButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
root.processedServer.isCountrySelectionAvailable) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else {
PageController.closePage()
}
}
}
HeaderType {
id: headerContent
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg"
: "qrc:/images/controls/edit-3.svg"
headerText: root.processedServer.name
descriptionText: {
if (root.processedServer.isServerFromGatewayApi) {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
return qsTr("Subscription is valid until ") + ApiServicesModel.getSelectedServiceData("endDate")
} else {
return ApiServicesModel.getSelectedServiceData("serviceDescription")
}
} else if (root.processedServer.isServerFromTelegramApi) {
return root.processedServer.serverDescription
} else if (root.processedServer.hasWriteAccess) {
return root.processedServer.credentialsLogin + " · " + root.processedServer.hostName
} else {
return root.processedServer.hostName
}
}
delegate: ColumnLayout {
KeyNavigation.tab: tabBar
property alias focusItem: backButton
actionButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else {
serverNameEditDrawer.open()
}
}
}
id: content
DrawerType2 {
id: serverNameEditDrawer
Layout.topMargin: 20
parent: root
BackButtonType {
id: backButton
KeyNavigation.tab: headerContent.actionButton
anchors.fill: parent
expandedHeight: root.height * 0.35
backButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else {
PageController.closePage()
}
onClosed: {
if (!GC.isMobile()) {
headerContent.actionButton.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: serverNameEditDrawer
enabled: !GC.isMobile()
function onOpened() {
serverName.textField.forceActiveFocus()
}
}
HeaderType {
id: headerContent
Item {
id: focusItem1
KeyNavigation.tab: serverName.textField
}
TextFieldWithHeaderType {
id: serverName
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Server name")
textFieldText: root.processedServer.name
textField.maximumLength: 30
checkEmptyText: true
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg"
headerText: name
descriptionText: {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
return ApiServicesModel.getSelectedServiceData("serviceDescription")
} else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) {
return serverDescription
} else if (ServersModel.isProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName
} else {
return hostName
}
}
KeyNavigation.tab: tabBar
actionButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else {
serverNameEditDrawer.open()
}
}
KeyNavigation.tab: saveButton
}
DrawerType2 {
id: serverNameEditDrawer
BasicButtonType {
id: saveButton
parent: root
Layout.fillWidth: true
anchors.fill: parent
expandedHeight: root.height * 0.35
text: qsTr("Save")
KeyNavigation.tab: focusItem1
onClosed: {
if (!GC.isMobile()) {
headerContent.actionButton.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: serverNameEditDrawer
enabled: !GC.isMobile()
function onOpened() {
serverName.textField.forceActiveFocus()
}
clickedFunc: function() {
if (serverName.textFieldText === "") {
return
}
Item {
id: focusItem1
KeyNavigation.tab: serverName.textField
}
TextFieldWithHeaderType {
id: serverName
Layout.fillWidth: true
headerText: qsTr("Server name")
textFieldText: name
textField.maximumLength: 30
checkEmptyText: true
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
KeyNavigation.tab: focusItem1
clickedFunc: function() {
if (serverName.textFieldText === "") {
return
}
if (serverName.textFieldText !== name) {
name = serverName.textFieldText
}
serverNameEditDrawer.close()
}
if (serverName.textFieldText !== root.processedServer.name) {
ServersModel.setProcessedServerData("name", serverName.textFieldText);
}
serverNameEditDrawer.close()
}
}
}
@@ -257,8 +261,7 @@ PageType {
StackLayout {
id: nestedStackView
Layout.preferredWidth: root.width
Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight
Layout.fillWidth: true
currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ?
(ServersModel.getProcessedServerData("isCountrySelectionAvailable") ?

View File

@@ -92,7 +92,7 @@ PageType {
break
}
case PageShare.ConfigType.Xray: {
ExportController.generateXrayConfig()
ExportController.generateXrayConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_xray"

View File

@@ -0,0 +1,8 @@
<svg width="208" height="56" viewBox="0 0 208 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="208" height="56" rx="16" fill="#FBB36A"/>
<path d="M76.012 27.24C75.0654 27.24 74.212 27.0933 73.452 26.8C72.692 26.4933 72.0454 26.0467 71.512 25.46C70.9787 24.86 70.5654 24.12 70.272 23.24C69.9787 22.3467 69.832 21.3067 69.832 20.12C69.832 18.9467 69.9787 17.9067 70.272 17C70.5654 16.08 70.9787 15.3133 71.512 14.7C72.0454 14.0733 72.692 13.6 73.452 13.28C74.212 12.96 75.0654 12.8 76.012 12.8C77.3054 12.8 78.372 13.0667 79.212 13.6C80.052 14.12 80.7254 14.9467 81.232 16.08L78.612 17.44C78.4254 16.8533 78.132 16.3867 77.732 16.04C77.3454 15.68 76.772 15.5 76.012 15.5C75.1187 15.5 74.3987 15.7933 73.852 16.38C73.3187 16.9533 73.052 17.7933 73.052 18.9V21.14C73.052 22.2467 73.3187 23.0933 73.852 23.68C74.3987 24.2533 75.1187 24.54 76.012 24.54C76.7587 24.54 77.352 24.34 77.792 23.94C78.2454 23.5267 78.5787 23.0333 78.792 22.46L81.272 23.9C80.752 24.9667 80.0654 25.7933 79.212 26.38C78.372 26.9533 77.3054 27.24 76.012 27.24ZM83.2394 16.5H86.1994V20.52H87.7194L89.0994 17.96C89.3794 17.44 89.6927 17.0667 90.0394 16.84C90.3994 16.6133 90.8594 16.5 91.4194 16.5H92.6394V18.8H91.5794L90.6794 20.48C90.4927 20.8267 90.306 21.0733 90.1194 21.22C89.946 21.3667 89.7394 21.4733 89.4994 21.54V21.64C89.726 21.6933 89.9327 21.7867 90.1194 21.92C90.3194 22.0533 90.4994 22.2667 90.6594 22.56L91.8394 24.7H92.9194V27H91.5394C90.9794 27 90.526 26.8867 90.1794 26.66C89.8327 26.4333 89.506 26.0467 89.1994 25.5L87.7194 22.82H86.1994V27H83.2394V16.5ZM102.62 27C102.087 27 101.64 26.8333 101.28 26.5C100.934 26.1667 100.714 25.72 100.62 25.16H100.5C100.34 25.84 99.9871 26.36 99.4405 26.72C98.8938 27.0667 98.2205 27.24 97.4205 27.24C96.3671 27.24 95.5605 26.96 95.0005 26.4C94.4405 25.84 94.1605 25.1 94.1605 24.18C94.1605 23.0733 94.5605 22.2533 95.3605 21.72C96.1738 21.1733 97.2738 20.9 98.6605 20.9H100.32V20.24C100.32 19.7333 100.187 19.3333 99.9205 19.04C99.6538 18.7333 99.2071 18.58 98.5805 18.58C97.9938 18.58 97.5271 18.7067 97.1805 18.96C96.8338 19.2133 96.5471 19.5 96.3205 19.82L94.5605 18.26C94.9871 17.6333 95.5205 17.1467 96.1605 16.8C96.8138 16.44 97.6938 16.26 98.8005 16.26C100.294 16.26 101.414 16.5867 102.16 17.24C102.907 17.8933 103.28 18.8467 103.28 20.1V24.7H104.26V27H102.62ZM98.5205 25.18C99.0138 25.18 99.4338 25.0733 99.7805 24.86C100.14 24.6467 100.32 24.3 100.32 23.82V22.58H98.8805C97.7205 22.58 97.1405 22.9733 97.1405 23.76V24.06C97.1405 24.4467 97.2605 24.7333 97.5005 24.92C97.7405 25.0933 98.0805 25.18 98.5205 25.18ZM111.736 22.54H111.636C111.396 22.9667 111.049 23.3067 110.596 23.56C110.156 23.8 109.596 23.92 108.916 23.92C107.729 23.92 106.862 23.6133 106.316 23C105.782 22.3867 105.516 21.4467 105.516 20.18V16.5H108.476V20.04C108.476 20.56 108.596 20.94 108.836 21.18C109.089 21.4067 109.496 21.52 110.056 21.52C110.616 21.52 111.036 21.42 111.316 21.22C111.596 21.02 111.736 20.7667 111.736 20.46V16.5H114.696V27H111.736V22.54ZM125.218 27C124.685 27 124.238 26.8333 123.878 26.5C123.531 26.1667 123.311 25.72 123.218 25.16H123.098C122.938 25.84 122.585 26.36 122.038 26.72C121.491 27.0667 120.818 27.24 120.018 27.24C118.965 27.24 118.158 26.96 117.598 26.4C117.038 25.84 116.758 25.1 116.758 24.18C116.758 23.0733 117.158 22.2533 117.958 21.72C118.771 21.1733 119.871 20.9 121.258 20.9H122.918V20.24C122.918 19.7333 122.785 19.3333 122.518 19.04C122.251 18.7333 121.805 18.58 121.178 18.58C120.591 18.58 120.125 18.7067 119.778 18.96C119.431 19.2133 119.145 19.5 118.918 19.82L117.158 18.26C117.585 17.6333 118.118 17.1467 118.758 16.8C119.411 16.44 120.291 16.26 121.398 16.26C122.891 16.26 124.011 16.5867 124.758 17.24C125.505 17.8933 125.878 18.8467 125.878 20.1V24.7H126.858V27H125.218ZM121.118 25.18C121.611 25.18 122.031 25.0733 122.378 24.86C122.738 24.6467 122.918 24.3 122.918 23.82V22.58H121.478C120.318 22.58 119.738 22.9733 119.738 23.76V24.06C119.738 24.4467 119.858 24.7333 120.098 24.92C120.338 25.0933 120.678 25.18 121.118 25.18ZM130.553 18.8H127.513V16.5H136.553V18.8H133.513V27H130.553V18.8ZM138.337 16.5H141.297V19.62H144.057C144.564 19.62 145.037 19.7 145.477 19.86C145.917 20.02 146.297 20.26 146.617 20.58C146.95 20.8867 147.21 21.2667 147.397 21.72C147.584 22.1733 147.677 22.7 147.677 23.3C147.677 23.9 147.584 24.4333 147.397 24.9C147.21 25.3533 146.95 25.74 146.617 26.06C146.297 26.3667 145.917 26.6 145.477 26.76C145.037 26.92 144.564 27 144.057 27H138.337V16.5ZM143.417 24.9C143.804 24.9 144.11 24.8 144.337 24.6C144.577 24.3867 144.697 24.0867 144.697 23.7V22.92C144.697 22.5333 144.577 22.24 144.337 22.04C144.11 21.8267 143.804 21.72 143.417 21.72H141.297V24.9H143.417Z" fill="#472402"/>
<path opacity="0.8" d="M74.308 45.144C73.74 45.144 73.228 45.056 72.772 44.88C72.316 44.696 71.928 44.428 71.608 44.076C71.288 43.716 71.04 43.272 70.864 42.744C70.688 42.208 70.6 41.584 70.6 40.872C70.6 40.168 70.688 39.544 70.864 39C71.04 38.448 71.288 37.988 71.608 37.62C71.928 37.244 72.316 36.96 72.772 36.768C73.228 36.576 73.74 36.48 74.308 36.48C75.084 36.48 75.724 36.64 76.228 36.96C76.732 37.272 77.136 37.768 77.44 38.448L75.868 39.264C75.756 38.912 75.58 38.632 75.34 38.424C75.108 38.208 74.764 38.1 74.308 38.1C73.772 38.1 73.34 38.276 73.012 38.628C72.692 38.972 72.532 39.476 72.532 40.14V41.484C72.532 42.148 72.692 42.656 73.012 43.008C73.34 43.352 73.772 43.524 74.308 43.524C74.756 43.524 75.112 43.404 75.376 43.164C75.648 42.916 75.848 42.62 75.976 42.276L77.464 43.14C77.152 43.78 76.74 44.276 76.228 44.628C75.724 44.972 75.084 45.144 74.308 45.144ZM84.0243 45.144C83.5523 45.144 83.1323 45.068 82.7643 44.916C82.3963 44.756 82.0883 44.536 81.8403 44.256C81.5923 43.968 81.4043 43.62 81.2763 43.212C81.1483 42.804 81.0843 42.348 81.0843 41.844C81.0843 41.34 81.1483 40.888 81.2763 40.488C81.4043 40.08 81.5923 39.732 81.8403 39.444C82.0883 39.156 82.3963 38.936 82.7643 38.784C83.1323 38.632 83.5523 38.556 84.0243 38.556C84.6643 38.556 85.1963 38.7 85.6203 38.988C86.0523 39.276 86.3603 39.68 86.5443 40.2L85.1043 40.824C85.0483 40.584 84.9323 40.38 84.7563 40.212C84.5803 40.044 84.3363 39.96 84.0243 39.96C83.6643 39.96 83.3923 40.08 83.2083 40.32C83.0243 40.552 82.9323 40.868 82.9323 41.268V42.444C82.9323 42.844 83.0243 43.16 83.2083 43.392C83.3923 43.624 83.6643 43.74 84.0243 43.74C84.3443 43.74 84.5963 43.656 84.7803 43.488C84.9723 43.312 85.1123 43.084 85.2003 42.804L86.5803 43.404C86.3723 44.004 86.0483 44.444 85.6083 44.724C85.1763 45.004 84.6483 45.144 84.0243 45.144ZM92.3356 45C92.0156 45 91.7476 44.9 91.5316 44.7C91.3236 44.5 91.1916 44.232 91.1356 43.896H91.0636C90.9676 44.304 90.7556 44.616 90.4276 44.832C90.0996 45.04 89.6956 45.144 89.2156 45.144C88.5836 45.144 88.0996 44.976 87.7636 44.64C87.4276 44.304 87.2596 43.86 87.2596 43.308C87.2596 42.644 87.4996 42.152 87.9796 41.832C88.4676 41.504 89.1276 41.34 89.9596 41.34H90.9556V40.944C90.9556 40.64 90.8756 40.4 90.7156 40.224C90.5556 40.04 90.2876 39.948 89.9116 39.948C89.5596 39.948 89.2796 40.024 89.0716 40.176C88.8636 40.328 88.6916 40.5 88.5556 40.692L87.4996 39.756C87.7556 39.38 88.0756 39.088 88.4596 38.88C88.8516 38.664 89.3796 38.556 90.0436 38.556C90.9396 38.556 91.6116 38.752 92.0596 39.144C92.5076 39.536 92.7316 40.108 92.7316 40.86V43.62H93.3196V45H92.3356ZM89.8756 43.908C90.1716 43.908 90.4236 43.844 90.6316 43.716C90.8476 43.588 90.9556 43.38 90.9556 43.092V42.348H90.0916C89.3956 42.348 89.0476 42.584 89.0476 43.056V43.236C89.0476 43.468 89.1196 43.64 89.2636 43.752C89.4076 43.856 89.6116 43.908 89.8756 43.908ZM94.5116 38.7H96.2516V41.232L96.0956 42.696H96.1316L96.8636 41.352L98.6156 38.7H100.2V45H98.4596V42.468L98.6156 41.004H98.5796L97.8476 42.348L96.0956 45H94.5116V38.7ZM97.3676 38.052C96.8396 38.052 96.4116 37.936 96.0836 37.704C95.7556 37.472 95.4996 37.14 95.3156 36.708L95.0636 36.096L96.4436 35.58L96.8276 36.792C96.8756 36.816 96.9516 36.836 97.0556 36.852C97.1596 36.868 97.2636 36.876 97.3676 36.876C97.4716 36.876 97.5756 36.868 97.6796 36.852C97.7836 36.836 97.8596 36.816 97.9076 36.792L98.2916 35.58L99.6716 36.096L99.4196 36.708C99.2356 37.14 98.9796 37.472 98.6516 37.704C98.3236 37.936 97.8956 38.052 97.3676 38.052ZM103.095 40.08H101.271V38.7H106.695V40.08H104.871V45H103.095V40.08ZM112.363 45C112.043 45 111.775 44.9 111.559 44.7C111.351 44.5 111.219 44.232 111.163 43.896H111.091C110.995 44.304 110.783 44.616 110.455 44.832C110.127 45.04 109.723 45.144 109.243 45.144C108.611 45.144 108.127 44.976 107.791 44.64C107.455 44.304 107.287 43.86 107.287 43.308C107.287 42.644 107.527 42.152 108.007 41.832C108.495 41.504 109.155 41.34 109.987 41.34H110.983V40.944C110.983 40.64 110.903 40.4 110.743 40.224C110.583 40.04 110.315 39.948 109.939 39.948C109.587 39.948 109.307 40.024 109.099 40.176C108.891 40.328 108.719 40.5 108.583 40.692L107.527 39.756C107.783 39.38 108.103 39.088 108.487 38.88C108.879 38.664 109.407 38.556 110.071 38.556C110.967 38.556 111.639 38.752 112.087 39.144C112.535 39.536 112.759 40.108 112.759 40.86V43.62H113.347V45H112.363ZM109.903 43.908C110.199 43.908 110.451 43.844 110.659 43.716C110.875 43.588 110.983 43.38 110.983 43.092V42.348H110.119C109.423 42.348 109.075 42.584 109.075 43.056V43.236C109.075 43.468 109.147 43.64 109.291 43.752C109.435 43.856 109.639 43.908 109.903 43.908Z" fill="#472402"/>
<path d="M37.4987 29.5415V37.8748L33.332 33.7082" stroke="#472402" stroke-width="3.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M37.5 37.8752L41.6667 33.7085" stroke="#472402" stroke-width="3.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M29.5766 31.9052C28.7259 31.161 28.0598 30.2292 27.631 29.1833C27.2022 28.1374 27.0224 27.0062 27.1058 25.8789C27.1893 24.7516 27.5336 23.6592 28.1118 22.6879C28.6899 21.7165 29.4859 20.8929 30.437 20.282C31.388 19.6712 32.4681 19.2898 33.5919 19.1679C34.7157 19.0461 35.8524 19.1873 36.9122 19.5802C37.9721 19.9731 38.9261 20.6071 39.6989 21.432C40.4717 22.2569 41.0422 23.2502 41.3652 24.3334H43.2298C44.2423 24.3332 45.2277 24.661 46.0385 25.2676C46.8492 25.8742 47.4418 26.727 47.7274 27.6985C48.0131 28.6699 47.9765 29.7077 47.6232 30.6566C47.2698 31.6055 46.6187 32.4145 45.7673 32.9625" stroke="#472402" stroke-width="3.33" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB