mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-26 11:16:20 +03:00
Merge pull request #20520 from Chocobo1/i18n
Use client side translation for public login page
This commit is contained in:
commit
931de85ab5
67 changed files with 789 additions and 19 deletions
|
@ -78,7 +78,11 @@ repos:
|
||||||
m4/.* |
|
m4/.* |
|
||||||
src/base/3rdparty/.* |
|
src/base/3rdparty/.* |
|
||||||
src/searchengine/nova3/socks.py |
|
src/searchengine/nova3/socks.py |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/lang/.* |
|
||||||
|
src/webui/www/private/scripts/lib/.* |
|
||||||
|
src/webui/www/public/lang/.* |
|
||||||
|
src/webui/www/public/scripts/lib/.* |
|
||||||
|
src/webui/www/transifex/.*
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
- ts
|
- ts
|
||||||
|
@ -102,7 +106,11 @@ repos:
|
||||||
m4/.* |
|
m4/.* |
|
||||||
src/base/3rdparty/.* |
|
src/base/3rdparty/.* |
|
||||||
src/searchengine/nova3/socks.py |
|
src/searchengine/nova3/socks.py |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/lang/.* |
|
||||||
|
src/webui/www/private/scripts/lib/.* |
|
||||||
|
src/webui/www/public/lang/.* |
|
||||||
|
src/webui/www/public/scripts/lib/.* |
|
||||||
|
src/webui/www/transifex/.*
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
- svg
|
- svg
|
||||||
|
|
|
@ -17,6 +17,14 @@ type = QT
|
||||||
minimum_perc = 23
|
minimum_perc = 23
|
||||||
lang_map = pt: pt_PT, zh: zh_CN
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
|
|
||||||
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_json]
|
||||||
|
file_filter = src/webui/www/transifex/<lang>.json
|
||||||
|
source_file = src/webui/www/transifex/en.json
|
||||||
|
source_lang = en
|
||||||
|
type = KEYVALUEJSON
|
||||||
|
minimum_perc = 23
|
||||||
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
|
|
||||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrentdesktop_master]
|
[o:sledgehammer999:p:qbittorrent:r:qbittorrentdesktop_master]
|
||||||
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
|
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"url": "https://github.com/qbittorrent/qBittorrent.git"
|
"url": "https://github.com/qbittorrent/qBittorrent.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"extract_translation": "i18next -c public/i18next-parser.config.mjs public/index.html public/scripts/login.js",
|
||||||
"format": "js-beautify -r private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js && prettier --write **.css",
|
"format": "js-beautify -r private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js && prettier --write **.css",
|
||||||
"lint": "eslint private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js && stylelint **/*.css && html-validate private public"
|
"lint": "eslint private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js && stylelint **/*.css && html-validate private public"
|
||||||
},
|
},
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
"eslint": "*",
|
"eslint": "*",
|
||||||
"eslint-plugin-html": "*",
|
"eslint-plugin-html": "*",
|
||||||
"html-validate": "*",
|
"html-validate": "*",
|
||||||
|
"i18next-parser": "*",
|
||||||
"js-beautify": "*",
|
"js-beautify": "*",
|
||||||
"prettier": "*",
|
"prettier": "*",
|
||||||
"stylelint": "*",
|
"stylelint": "*",
|
||||||
|
|
|
@ -31,7 +31,7 @@ body {
|
||||||
color: #f00;
|
color: #f00;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login {
|
#loginButton {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/webui/www/public/i18next-parser.config.mjs
Normal file
10
src/webui/www/public/i18next-parser.config.mjs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default {
|
||||||
|
createOldCatalogs: false,
|
||||||
|
failOnWarnings: true,
|
||||||
|
keySeparator: false,
|
||||||
|
lineEnding: 'lf',
|
||||||
|
locales: ['en'],
|
||||||
|
namespaceSeparator: false,
|
||||||
|
output: 'public/lang/$LOCALE.json',
|
||||||
|
sort: true
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="${LANG}">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
@ -16,7 +16,8 @@
|
||||||
<link rel="stylesheet" type="text/css" href="css/noscript.css?v=${CACHEID}" />
|
<link rel="stylesheet" type="text/css" href="css/noscript.css?v=${CACHEID}" />
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<script defer src="scripts/login.js?locale=${LANG}&v=${CACHEID}"></script>
|
<script defer src="scripts/lib/i18next.min.js"></script>
|
||||||
|
<script defer src="scripts/login.js?v=${CACHEID}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -29,17 +30,17 @@
|
||||||
<img src="images/qbittorrent-tray.svg" alt="qBittorrent logo" />
|
<img src="images/qbittorrent-tray.svg" alt="qBittorrent logo" />
|
||||||
</div>
|
</div>
|
||||||
<div id="formplace" class="col">
|
<div id="formplace" class="col">
|
||||||
<form id="loginform" method="post" onsubmit="submitLoginForm(event);">
|
<form id="loginform">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="username">Username</label><br />
|
<label for="username" class="qbt-translatable" data-i18n="Username">Username</label><br />
|
||||||
<input type="text" id="username" name="username" autocomplete="username" required />
|
<input type="text" id="username" name="username" autocomplete="username" autofocus required />
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="password">Password</label><br />
|
<label for="password" class="qbt-translatable" data-i18n="Password">Password</label><br />
|
||||||
<input type="password" id="password" name="password" autocomplete="current-password" required />
|
<input type="password" id="password" name="password" autocomplete="current-password" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="submit" id="login" value="Login" />
|
<input type="submit" id="loginButton" class="qbt-translatable" data-i18n="Login" value="Login" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
7
src/webui/www/public/lang/ar.json
Normal file
7
src/webui/www/public/lang/ar.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "اسم المستخدم أو كلمة السر غير صالحة.",
|
||||||
|
"Login": "ولوج",
|
||||||
|
"Password": "كلمة السر",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "اسم المستخدم"
|
||||||
|
}
|
7
src/webui/www/public/lang/az@latin.json
Normal file
7
src/webui/www/public/lang/az@latin.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "İstifadəçi adı və Şifrə səhvdir.",
|
||||||
|
"Login": "Giriş",
|
||||||
|
"Password": "Şifrə",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "İstifadəçi adı"
|
||||||
|
}
|
7
src/webui/www/public/lang/be.json
Normal file
7
src/webui/www/public/lang/be.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Нядзейсныя імя карыстальніка ці пароль.",
|
||||||
|
"Login": "Логін",
|
||||||
|
"Password": "Пароль",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Імя карыстальніка"
|
||||||
|
}
|
7
src/webui/www/public/lang/bg.json
Normal file
7
src/webui/www/public/lang/bg.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Невалидно потребителско име или парола.",
|
||||||
|
"Login": "Вход",
|
||||||
|
"Password": "Парола",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Потребителско име"
|
||||||
|
}
|
7
src/webui/www/public/lang/ca.json
Normal file
7
src/webui/www/public/lang/ca.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nom d'usuari o contrasenya incorrectes.",
|
||||||
|
"Login": "Inicia sessió",
|
||||||
|
"Password": "Contrasenya",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nom d'usuari"
|
||||||
|
}
|
7
src/webui/www/public/lang/cs.json
Normal file
7
src/webui/www/public/lang/cs.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Neplatné jméno nebo heslo",
|
||||||
|
"Login": "Přihlášení",
|
||||||
|
"Password": "Heslo",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Uživatelské jméno"
|
||||||
|
}
|
7
src/webui/www/public/lang/da.json
Normal file
7
src/webui/www/public/lang/da.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Ugyldigt brugernavn eller adgangskode.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Adgangskode",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Brugernavn"
|
||||||
|
}
|
7
src/webui/www/public/lang/de.json
Normal file
7
src/webui/www/public/lang/de.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Ungültiger Benutzername oder Passwort.",
|
||||||
|
"Login": "Anmelden",
|
||||||
|
"Password": "Passwort",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Benutzername"
|
||||||
|
}
|
7
src/webui/www/public/lang/el.json
Normal file
7
src/webui/www/public/lang/el.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Μη έγκυρο Όνομα Χρήστη ή Κωδικός Πρόσβασης.",
|
||||||
|
"Login": "Σύνδεση",
|
||||||
|
"Password": "Κωδικός Πρόσβασης",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Όνομα χρήστη"
|
||||||
|
}
|
7
src/webui/www/public/lang/en.json
Normal file
7
src/webui/www/public/lang/en.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Invalid Username or Password.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Password",
|
||||||
|
"Unable to log in, server is probably unreachable.": "Unable to log in, server is probably unreachable.",
|
||||||
|
"Username": "Username"
|
||||||
|
}
|
7
src/webui/www/public/lang/en_AU.json
Normal file
7
src/webui/www/public/lang/en_AU.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Invalid Username or Password.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Password",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Username"
|
||||||
|
}
|
7
src/webui/www/public/lang/en_GB.json
Normal file
7
src/webui/www/public/lang/en_GB.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": ""
|
||||||
|
}
|
7
src/webui/www/public/lang/eo.json
Normal file
7
src/webui/www/public/lang/eo.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Malvalida uzantnomo aŭ pasvorto.",
|
||||||
|
"Login": "Ensaluti",
|
||||||
|
"Password": "Pasvorto",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": ""
|
||||||
|
}
|
7
src/webui/www/public/lang/es.json
Normal file
7
src/webui/www/public/lang/es.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nombre de usuario o contraseña inválidos.",
|
||||||
|
"Login": "Iniciar sesión",
|
||||||
|
"Password": "Contraseña",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nombre de usuario"
|
||||||
|
}
|
7
src/webui/www/public/lang/et.json
Normal file
7
src/webui/www/public/lang/et.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Sobimatu Kasutajanimi või Parool",
|
||||||
|
"Login": "Logi sisse",
|
||||||
|
"Password": "Parool",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Kasutajanimi"
|
||||||
|
}
|
7
src/webui/www/public/lang/eu.json
Normal file
7
src/webui/www/public/lang/eu.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Erabiltzaile-izen edo Sarhitz baliogabea.",
|
||||||
|
"Login": "Hasi Saioa",
|
||||||
|
"Password": "Sarhitza",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Erabiltzaile-izena"
|
||||||
|
}
|
7
src/webui/www/public/lang/fa.json
Normal file
7
src/webui/www/public/lang/fa.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "نام کاربری یا کلمه عبور نامعتبر",
|
||||||
|
"Login": "ورود",
|
||||||
|
"Password": "کلمه عبور",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "نام کاربری"
|
||||||
|
}
|
7
src/webui/www/public/lang/fi.json
Normal file
7
src/webui/www/public/lang/fi.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Virheellinen käyttäjätunnus tai salasana.",
|
||||||
|
"Login": "Kirjaudu",
|
||||||
|
"Password": "Salasana",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Käyttäjänimi"
|
||||||
|
}
|
7
src/webui/www/public/lang/fr.json
Normal file
7
src/webui/www/public/lang/fr.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nom d'utilisateur ou mot de passe invalide.",
|
||||||
|
"Login": "Se connecter",
|
||||||
|
"Password": "Mot de passe",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nom d'utilisateur"
|
||||||
|
}
|
7
src/webui/www/public/lang/gl.json
Normal file
7
src/webui/www/public/lang/gl.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "O usuario ou o contrasinal son incorrectos.",
|
||||||
|
"Login": "Iniciar sesión",
|
||||||
|
"Password": "Contrasinal",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nome do usuario"
|
||||||
|
}
|
7
src/webui/www/public/lang/he.json
Normal file
7
src/webui/www/public/lang/he.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "שם משתמש או סיסמה בלתי תקפים.",
|
||||||
|
"Login": "היכנס",
|
||||||
|
"Password": "סיסמה",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "שם משתמש"
|
||||||
|
}
|
7
src/webui/www/public/lang/hi_IN.json
Normal file
7
src/webui/www/public/lang/hi_IN.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "यूजरनेम व पासवर्ड अमान्य हैं।",
|
||||||
|
"Login": "लॉगिन",
|
||||||
|
"Password": "पासवर्ड",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "यूजरनेम"
|
||||||
|
}
|
7
src/webui/www/public/lang/hr.json
Normal file
7
src/webui/www/public/lang/hr.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Neispravno korisničko ime ili lozinka",
|
||||||
|
"Login": "Prijava",
|
||||||
|
"Password": "Lozinka",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Korisničko ime"
|
||||||
|
}
|
7
src/webui/www/public/lang/hu.json
Normal file
7
src/webui/www/public/lang/hu.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Érvénytelen felhasználónév vagy Jelszó.",
|
||||||
|
"Login": "Bejelentkezés",
|
||||||
|
"Password": "Jelszó",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Felhasználónév"
|
||||||
|
}
|
7
src/webui/www/public/lang/hy.json
Normal file
7
src/webui/www/public/lang/hy.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "",
|
||||||
|
"Login": "Օգտանուն",
|
||||||
|
"Password": "Գաղտնաբառ",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Մուտքանուն"
|
||||||
|
}
|
7
src/webui/www/public/lang/id.json
Normal file
7
src/webui/www/public/lang/id.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nama Pengguna atau Sandi tidak valid.",
|
||||||
|
"Login": "Masuk",
|
||||||
|
"Password": "Sandi",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nama pengguna"
|
||||||
|
}
|
7
src/webui/www/public/lang/is.json
Normal file
7
src/webui/www/public/lang/is.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Rangt notandanafn eða lykilorð.",
|
||||||
|
"Login": "Skrá inn",
|
||||||
|
"Password": "Lykilorð",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": ""
|
||||||
|
}
|
7
src/webui/www/public/lang/it.json
Normal file
7
src/webui/www/public/lang/it.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Username o password errati.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Password",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nome utente"
|
||||||
|
}
|
7
src/webui/www/public/lang/ja.json
Normal file
7
src/webui/www/public/lang/ja.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "ユーザー名またはパスワーが正しくありません。",
|
||||||
|
"Login": "ログイン",
|
||||||
|
"Password": "パスワード",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "ユーザー名"
|
||||||
|
}
|
7
src/webui/www/public/lang/ka.json
Normal file
7
src/webui/www/public/lang/ka.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "არასწორი მომხმარებლის სახელი ან პაროლი.",
|
||||||
|
"Login": "შესვლა",
|
||||||
|
"Password": "პაროლი",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "მომხმარებლის სახელი"
|
||||||
|
}
|
7
src/webui/www/public/lang/ko.json
Normal file
7
src/webui/www/public/lang/ko.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "사용자 이름/암호가 올바르지 않습니다.",
|
||||||
|
"Login": "로그인",
|
||||||
|
"Password": "암호",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "사용자 이름"
|
||||||
|
}
|
7
src/webui/www/public/lang/lt.json
Normal file
7
src/webui/www/public/lang/lt.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Neteisingas naudotojo vardas ar slaptažodis.",
|
||||||
|
"Login": "Prisijungimas",
|
||||||
|
"Password": "Slaptažodis",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Naudotojo vardas"
|
||||||
|
}
|
7
src/webui/www/public/lang/ltg.json
Normal file
7
src/webui/www/public/lang/ltg.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Napareizs slāgvuords voi paroļs",
|
||||||
|
"Login": "Dasaslāgt",
|
||||||
|
"Password": "Paroļs",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Lītuotuojs"
|
||||||
|
}
|
7
src/webui/www/public/lang/lv_LV.json
Normal file
7
src/webui/www/public/lang/lv_LV.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nederīgs lietotājvārds vai parole.",
|
||||||
|
"Login": "Pierakstīties",
|
||||||
|
"Password": "Parole",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Lietotājvārds"
|
||||||
|
}
|
7
src/webui/www/public/lang/mn_MN.json
Normal file
7
src/webui/www/public/lang/mn_MN.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Хэрэглэгчийн нэр эсвэл нууц үг тохирохгүй байна.",
|
||||||
|
"Login": "Нэвтрэх",
|
||||||
|
"Password": "Нууц үг",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Хэрэглэгчийн нэр"
|
||||||
|
}
|
7
src/webui/www/public/lang/ms_MY.json
Normal file
7
src/webui/www/public/lang/ms_MY.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nama Pengguna atau Kata Laluan tidak sah.",
|
||||||
|
"Login": "Daftar Masuk",
|
||||||
|
"Password": "Kata Laluan",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nama pengguna"
|
||||||
|
}
|
7
src/webui/www/public/lang/nb.json
Normal file
7
src/webui/www/public/lang/nb.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Ugyldig brukernavn eller passord.",
|
||||||
|
"Login": "Logg inn",
|
||||||
|
"Password": "Passord",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Brukernavn"
|
||||||
|
}
|
7
src/webui/www/public/lang/nl.json
Normal file
7
src/webui/www/public/lang/nl.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Ongeldige gebruikersnaam of wachtwoord.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Wachtwoord",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Gebruikersnaam"
|
||||||
|
}
|
7
src/webui/www/public/lang/oc.json
Normal file
7
src/webui/www/public/lang/oc.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nom d'utilizaire o senhal invalid.",
|
||||||
|
"Login": "Identificant",
|
||||||
|
"Password": "Senhal",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": ""
|
||||||
|
}
|
7
src/webui/www/public/lang/pl.json
Normal file
7
src/webui/www/public/lang/pl.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nieprawidłowa nazwa użytkownika lub hasło.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Hasło",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nazwa użytkownika"
|
||||||
|
}
|
7
src/webui/www/public/lang/pt_BR.json
Normal file
7
src/webui/www/public/lang/pt_BR.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nome de usuário ou senha inválidos.",
|
||||||
|
"Login": "Login",
|
||||||
|
"Password": "Senha",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nome de usuário"
|
||||||
|
}
|
7
src/webui/www/public/lang/pt_PT.json
Normal file
7
src/webui/www/public/lang/pt_PT.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Palavra-passe ou nome de utilizador inválido.",
|
||||||
|
"Login": "Iniciar sessão",
|
||||||
|
"Password": "Palavra-passe",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nome de utilizador"
|
||||||
|
}
|
7
src/webui/www/public/lang/ro.json
Normal file
7
src/webui/www/public/lang/ro.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Numele de utilizator sau parola nu sunt valide.",
|
||||||
|
"Login": "Autentifică",
|
||||||
|
"Password": "Parolă",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Nume de utilizator"
|
||||||
|
}
|
7
src/webui/www/public/lang/ru.json
Normal file
7
src/webui/www/public/lang/ru.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Неверное имя пользователя или пароль.",
|
||||||
|
"Login": "Войти",
|
||||||
|
"Password": "Пароль",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Имя пользователя"
|
||||||
|
}
|
7
src/webui/www/public/lang/sk.json
Normal file
7
src/webui/www/public/lang/sk.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Nesprávne užívateľské meno alebo heslo.",
|
||||||
|
"Login": "Prihlasovacie meno",
|
||||||
|
"Password": "Heslo",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Meno používateľa"
|
||||||
|
}
|
7
src/webui/www/public/lang/sl.json
Normal file
7
src/webui/www/public/lang/sl.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Neveljavno uporabniško ime ali geslo.",
|
||||||
|
"Login": "Prijava",
|
||||||
|
"Password": "Geslo",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Uporabniško ime"
|
||||||
|
}
|
7
src/webui/www/public/lang/sr.json
Normal file
7
src/webui/www/public/lang/sr.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Неважеће корисничко име или шифра.",
|
||||||
|
"Login": "Логовање",
|
||||||
|
"Password": "Шифра",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Корисничко име"
|
||||||
|
}
|
7
src/webui/www/public/lang/sv.json
Normal file
7
src/webui/www/public/lang/sv.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Felaktigt användarnamn eller lösenord.",
|
||||||
|
"Login": "Inloggning",
|
||||||
|
"Password": "Lösenord",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Användarnamn"
|
||||||
|
}
|
7
src/webui/www/public/lang/th.json
Normal file
7
src/webui/www/public/lang/th.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง.",
|
||||||
|
"Login": "ล็อกอิน",
|
||||||
|
"Password": "รหัสผ่าน",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "ชื่อผู้ใช้"
|
||||||
|
}
|
7
src/webui/www/public/lang/tr.json
Normal file
7
src/webui/www/public/lang/tr.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Geçersiz Kullanıcı Adı veya Parola.",
|
||||||
|
"Login": "Oturum Aç",
|
||||||
|
"Password": "Parola",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Kullanıcı adı"
|
||||||
|
}
|
7
src/webui/www/public/lang/uk.json
Normal file
7
src/webui/www/public/lang/uk.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Неправильний логін або пароль.",
|
||||||
|
"Login": "Логін",
|
||||||
|
"Password": "Пароль",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Ім'я користувача"
|
||||||
|
}
|
7
src/webui/www/public/lang/uz@Latn.json
Normal file
7
src/webui/www/public/lang/uz@Latn.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Foydalanuvchi ismi yoki parol noto‘g‘ri.",
|
||||||
|
"Login": "Kirish",
|
||||||
|
"Password": "Parol",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": ""
|
||||||
|
}
|
7
src/webui/www/public/lang/vi.json
Normal file
7
src/webui/www/public/lang/vi.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "Tên tài khoản hoặc Mật khẩu không hợp lệ",
|
||||||
|
"Login": "Đăng nhập",
|
||||||
|
"Password": "Mật khẩu",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "Tên tài khoản"
|
||||||
|
}
|
7
src/webui/www/public/lang/zh_CN.json
Normal file
7
src/webui/www/public/lang/zh_CN.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "无效的用户名或密码。",
|
||||||
|
"Login": "登录",
|
||||||
|
"Password": "密码",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "用户名"
|
||||||
|
}
|
7
src/webui/www/public/lang/zh_HK.json
Normal file
7
src/webui/www/public/lang/zh_HK.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "無效用戶名稱或密碼。",
|
||||||
|
"Login": "登入",
|
||||||
|
"Password": "密碼",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "用戶名"
|
||||||
|
}
|
7
src/webui/www/public/lang/zh_TW.json
Normal file
7
src/webui/www/public/lang/zh_TW.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Invalid Username or Password.": "無效的使用者名稱或密碼。",
|
||||||
|
"Login": "登入",
|
||||||
|
"Password": "密碼",
|
||||||
|
"Unable to log in, server is probably unreachable.": "",
|
||||||
|
"Username": "使用者名稱"
|
||||||
|
}
|
1
src/webui/www/public/scripts/lib/i18next.min.js
vendored
Normal file
1
src/webui/www/public/scripts/lib/i18next.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2019 Mike Tzou (Chocobo1)
|
* Copyright (C) 2019-2024 Mike Tzou (Chocobo1)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -28,10 +28,64 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
async function setupI18n() {
|
||||||
document.getElementById('username').focus();
|
const languages = (() => {
|
||||||
document.getElementById('username').select();
|
const langs = new Set();
|
||||||
});
|
for (const lang of navigator.languages) {
|
||||||
|
langs.add(lang.replace('-', '_'));
|
||||||
|
|
||||||
|
const idx = lang.indexOf('-');
|
||||||
|
if (idx > 0)
|
||||||
|
langs.add(lang.slice(0, idx));
|
||||||
|
}
|
||||||
|
langs.add('en'); // fallback
|
||||||
|
return Array.from(langs);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// it is faster to fetch all translation files at once than one by one
|
||||||
|
const fetches = languages.map(lang => fetch(`lang/${lang}.json`));
|
||||||
|
const fetchResults = await Promise.allSettled(fetches);
|
||||||
|
const translations = fetchResults
|
||||||
|
.map((value, idx) => ({ lang: languages[idx], result: value }))
|
||||||
|
.filter(v => (v.result.value.status === 200));
|
||||||
|
const translation = {
|
||||||
|
lang: (translations.length > 0) ? translations[0].lang.replace('_', '-') : undefined,
|
||||||
|
data: (translations.length > 0) ? (await translations[0].result.value.json()) : {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// present it to i18next
|
||||||
|
const i18nextOptions = {
|
||||||
|
lng: translation.lang,
|
||||||
|
fallbackLng: false,
|
||||||
|
load: 'currentOnly',
|
||||||
|
resources: {
|
||||||
|
[translation.lang]: { translation: translation.data }
|
||||||
|
},
|
||||||
|
returnEmptyString: false
|
||||||
|
};
|
||||||
|
i18next.init(i18nextOptions, replaceI18nText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceI18nText() {
|
||||||
|
const tr = i18next.t; // workaround for warnings from i18next-parser
|
||||||
|
for (const element of document.getElementsByClassName('qbt-translatable')) {
|
||||||
|
const translationKey = element.getAttribute('data-i18n');
|
||||||
|
const translatedValue = tr(translationKey);
|
||||||
|
switch (element.nodeName) {
|
||||||
|
case 'INPUT':
|
||||||
|
element.value = translatedValue;
|
||||||
|
break;
|
||||||
|
case 'LABEL':
|
||||||
|
element.textContent = translatedValue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unhandled element: ${element}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.lang = i18next.language.split('-')[0];
|
||||||
|
}
|
||||||
|
|
||||||
function submitLoginForm(event) {
|
function submitLoginForm(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -40,18 +94,18 @@ function submitLoginForm(event) {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', 'api/v2/auth/login', true);
|
xhr.open('POST', 'api/v2/auth/login', true);
|
||||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||||
xhr.addEventListener('readystatechange', function() {
|
xhr.addEventListener('readystatechange', () => {
|
||||||
if (xhr.readyState === 4) { // DONE state
|
if (xhr.readyState === 4) { // DONE state
|
||||||
if ((xhr.status === 200) && (xhr.responseText === "Ok."))
|
if ((xhr.status === 200) && (xhr.responseText === "Ok."))
|
||||||
location.replace(location);
|
location.replace(location);
|
||||||
else
|
else
|
||||||
errorMsgElement.textContent = 'Invalid Username or Password.';
|
errorMsgElement.textContent = i18next.t('Invalid Username or Password.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
xhr.addEventListener('error', function() {
|
xhr.addEventListener('error', () => {
|
||||||
errorMsgElement.textContent = (xhr.responseText !== "")
|
errorMsgElement.textContent = (xhr.responseText !== "")
|
||||||
? xhr.responseText
|
? xhr.responseText
|
||||||
: 'Unable to log in, qBittorrent is probably unreachable.';
|
: i18next.t('Unable to log in, server is probably unreachable.');
|
||||||
});
|
});
|
||||||
|
|
||||||
const usernameElement = document.getElementById('username');
|
const usernameElement = document.getElementById('username');
|
||||||
|
@ -62,3 +116,11 @@ function submitLoginForm(event) {
|
||||||
// clear the field
|
// clear the field
|
||||||
passwordElement.value = '';
|
passwordElement.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const loginForm = document.getElementById('loginform');
|
||||||
|
loginForm.setAttribute('method', 'POST');
|
||||||
|
loginForm.addEventListener('submit', submitLoginForm);
|
||||||
|
|
||||||
|
setupI18n();
|
||||||
|
});
|
||||||
|
|
128
src/webui/www/split_merge_json.py
Executable file
128
src/webui/www/split_merge_json.py
Executable file
|
@ -0,0 +1,128 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# split_merge_json.py - script for splitting the JSON files from
|
||||||
|
# Transifex into their public/private parts and to merging the
|
||||||
|
# private/public en.json files to form the source file for Transifex
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 sledgehammer999 <hammered999@gmail.com>
|
||||||
|
#
|
||||||
|
# License: Public domain.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import PurePath
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def updateJson(json_file: str, source: dict[str, str]) -> None:
|
||||||
|
trimmed_path: str = json_file
|
||||||
|
path_parts = PurePath(json_file).parts
|
||||||
|
if len(path_parts) >= 3:
|
||||||
|
trimmed_path = f"{path_parts[-3]}/{path_parts[-2]}/{path_parts[-1]}"
|
||||||
|
|
||||||
|
if not os.path.exists(json_file):
|
||||||
|
print(f"\tWARNIG: {trimmed_path} doesn't exist")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\tUpdating {trimmed_path}")
|
||||||
|
with open(json_file, mode="r+", encoding='utf-8') as file:
|
||||||
|
target: dict[str, str] = json.load(file)
|
||||||
|
if not isinstance(target, dict):
|
||||||
|
print(f"\tWARNIG: {trimmed_path} is malormed")
|
||||||
|
return
|
||||||
|
|
||||||
|
for key in target.keys():
|
||||||
|
value = source.get(key)
|
||||||
|
if value:
|
||||||
|
target[key] = value
|
||||||
|
file.seek(0)
|
||||||
|
json.dump(target, file, ensure_ascii=False, indent=2, sort_keys=True)
|
||||||
|
file.write("\n")
|
||||||
|
file.truncate()
|
||||||
|
|
||||||
|
def splitJson(transifex_dir: str, json_public_dir: str, json_private_dir: str) -> None:
|
||||||
|
locales: list[str] = glob.glob("*.json", root_dir=transifex_dir)
|
||||||
|
locales = [x for x in locales if x != "en.json"]
|
||||||
|
for locale in locales:
|
||||||
|
print(f"Processing lang {locale}")
|
||||||
|
transifex_file: str = f"{transifex_dir}/{locale}"
|
||||||
|
public_file: str = f"{json_public_dir}/{locale}"
|
||||||
|
private_file: str = f"{json_private_dir}/{locale}"
|
||||||
|
|
||||||
|
transifex_json: dict[str, str] = {}
|
||||||
|
with open(transifex_file, mode="r", encoding='utf-8') as file:
|
||||||
|
transifex_json = json.load(file)
|
||||||
|
if not isinstance(transifex_json, dict):
|
||||||
|
trimmed_path: str = transifex_file
|
||||||
|
path_parts = PurePath(transifex_file).parts
|
||||||
|
if len(path_parts) >= 2:
|
||||||
|
trimmed_path = f"{path_parts[-2]}/{path_parts[-1]}"
|
||||||
|
print(f"\tERROR: {trimmed_path} is malformed.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
updateJson(public_file, transifex_json)
|
||||||
|
updateJson(private_file, transifex_json)
|
||||||
|
|
||||||
|
def mergeJson(transifex_dir: str, json_public_dir: str, json_private_dir: str) -> None:
|
||||||
|
transifex_en_file: str = f"{transifex_dir}/en.json"
|
||||||
|
public_en_file: str = f"{json_public_dir}/en.json"
|
||||||
|
private_en_file: str = f"{json_private_dir}/en.json"
|
||||||
|
|
||||||
|
transifex_en_json: dict[str, str] = {}
|
||||||
|
public_en_json: dict[str, str] = {}
|
||||||
|
private_en_json: dict[str, str] = {}
|
||||||
|
|
||||||
|
print("Merging source en.json file")
|
||||||
|
if os.path.exists(public_en_file):
|
||||||
|
with open(public_en_file, mode="r", encoding='utf-8') as file:
|
||||||
|
public_en_json = json.load(file)
|
||||||
|
if not isinstance(public_en_json, dict):
|
||||||
|
public_en_json = {}
|
||||||
|
|
||||||
|
if os.path.exists(private_en_file):
|
||||||
|
with open(private_en_file, mode="r", encoding='utf-8') as file:
|
||||||
|
private_en_json = json.load(file)
|
||||||
|
if not isinstance(private_en_json, dict):
|
||||||
|
private_en_json = {}
|
||||||
|
|
||||||
|
transifex_en_json = public_en_json | private_en_json
|
||||||
|
with open(transifex_en_file, mode="w", encoding='utf-8') as file:
|
||||||
|
json.dump(transifex_en_json, file, ensure_ascii=False, indent=2, sort_keys=True)
|
||||||
|
file.write("\n")
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
script_path: str = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
transifex_dir: str = f"{script_path}/transifex"
|
||||||
|
json_public_dir: str = f"{script_path}/public/lang"
|
||||||
|
json_private_dir: str = f"{script_path}/private/lang"
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--mode", required=True, choices=["split", "merge"], help="SPLIT the translations files from Transifex into their public/private JSON counterpart. MERGE to merge the public and private en.json files into the 'transifex/en.json' file")
|
||||||
|
parser.add_argument("--transifex-dir", default=transifex_dir, nargs=1, help=f"directory of WebUI transifex file (default: '{transifex_dir}')")
|
||||||
|
parser.add_argument("--json-public-dir", default=json_public_dir, nargs=1, help=f"directory of WebUI public JSON translation files(default: '{json_public_dir}')")
|
||||||
|
parser.add_argument("--json-private-dir", default=json_private_dir, nargs=1, help=f"directory of WebUI private JSON translation files(default: '{json_private_dir}')")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.isdir(args.transifex_dir):
|
||||||
|
raise RuntimeError(f"'{args.transifex_dir}' isn't a directory")
|
||||||
|
|
||||||
|
if not os.path.isdir(args.json_public_dir):
|
||||||
|
raise RuntimeError(f"'{args.json_public_dir}' isn't a directory")
|
||||||
|
|
||||||
|
if not os.path.isdir(args.json_private_dir):
|
||||||
|
raise RuntimeError(f"'{args.json_private_dir}' isn't a directory")
|
||||||
|
|
||||||
|
if args.mode == "merge":
|
||||||
|
mergeJson(transifex_dir, json_public_dir, json_private_dir)
|
||||||
|
else:
|
||||||
|
splitJson(transifex_dir, json_public_dir, json_private_dir)
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
101
src/webui/www/translations_qt2i18next.py
Executable file
101
src/webui/www/translations_qt2i18next.py
Executable file
|
@ -0,0 +1,101 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# translations_qt2i18next.py - script for migrating translations from Qt's .ts files
|
||||||
|
# to i18next compatible JSON files.
|
||||||
|
# Copyright (C) 2024 sledgehammer999 <hammered999@gmail.com>
|
||||||
|
#
|
||||||
|
# License: Public domain.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
def getTsStrings(ts_file: str, key: str) -> list[str]:
|
||||||
|
tr_strings: list[str] = []
|
||||||
|
tree = ET.parse(ts_file)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
for context in root.findall('context'):
|
||||||
|
for message in context.findall('message'):
|
||||||
|
source = message.find('source').text
|
||||||
|
if key != source:
|
||||||
|
continue
|
||||||
|
|
||||||
|
translation = message.find('translation').text
|
||||||
|
if not translation:
|
||||||
|
continue
|
||||||
|
if translation not in tr_strings:
|
||||||
|
tr_strings.append(translation)
|
||||||
|
|
||||||
|
return tr_strings
|
||||||
|
|
||||||
|
def migrate2Json(ts_dir: str, json_dir: str, locale: str) -> None:
|
||||||
|
ts_file: str = f"{ts_dir}/webui_{locale}.ts"
|
||||||
|
js_file: str = f"{json_dir}/{locale}.json"
|
||||||
|
|
||||||
|
print(f"Processing lang {locale}")
|
||||||
|
if not os.path.exists(ts_file):
|
||||||
|
print(f"\tERROR: {ts_file} doesn't exist.")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(js_file, mode="r+", encoding='utf-8') as file:
|
||||||
|
translations = json.load(file)
|
||||||
|
if not isinstance(translations, dict):
|
||||||
|
print(f"\tERROR: {js_file} is malformed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for key, value in translations.items():
|
||||||
|
if not (isinstance(key, str) and isinstance(value, str) and not value):
|
||||||
|
continue
|
||||||
|
|
||||||
|
ts_strings: list[str] = getTsStrings(ts_file, key)
|
||||||
|
ts_len: int = len(ts_strings)
|
||||||
|
if ts_len == 0:
|
||||||
|
print(f"\tWARNING: Translation for '{key}' not found.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ts_len > 1:
|
||||||
|
print(f"\tWARNING: Multiple translations for '{key}' found.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
translations[key] = ts_strings[0]
|
||||||
|
|
||||||
|
file.seek(0)
|
||||||
|
json.dump(translations, file, ensure_ascii=False, indent=2, sort_keys=True)
|
||||||
|
file.write("\n")
|
||||||
|
file.truncate()
|
||||||
|
|
||||||
|
print("\tFinished.")
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
script_path: str = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
ts_dir: str = f"{script_path}/translations"
|
||||||
|
json_dir: str = f"{script_path}/public/lang"
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--ts-dir", default=ts_dir, nargs=1, help=f"directory of WebUI .ts translation files (default: '{ts_dir}')")
|
||||||
|
parser.add_argument("--json-dir", default=json_dir, nargs=1, help=f"directory of WebUI .json translation files(default: '{json_dir}')")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.isdir(args.ts_dir):
|
||||||
|
raise RuntimeError(f"'{args.ts_dir}' isn't a directory")
|
||||||
|
|
||||||
|
if not os.path.isdir(args.json_dir):
|
||||||
|
raise RuntimeError(f"'{args.json_dir}' isn't a directory")
|
||||||
|
|
||||||
|
locales: list[str] = glob.glob("*.json", root_dir=json_dir)
|
||||||
|
locales = [x.removesuffix(".json") for x in locales if x != "en.json"]
|
||||||
|
for locale in locales:
|
||||||
|
migrate2Json(ts_dir, json_dir, locale)
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -423,6 +423,63 @@
|
||||||
<file>public/images/qbittorrent-tray.svg</file>
|
<file>public/images/qbittorrent-tray.svg</file>
|
||||||
<file>public/images/qbittorrent32.png</file>
|
<file>public/images/qbittorrent32.png</file>
|
||||||
<file>public/index.html</file>
|
<file>public/index.html</file>
|
||||||
|
<file>public/lang/ar.json</file>
|
||||||
|
<file>public/lang/az@latin.json</file>
|
||||||
|
<file>public/lang/be.json</file>
|
||||||
|
<file>public/lang/bg.json</file>
|
||||||
|
<file>public/lang/ca.json</file>
|
||||||
|
<file>public/lang/cs.json</file>
|
||||||
|
<file>public/lang/da.json</file>
|
||||||
|
<file>public/lang/de.json</file>
|
||||||
|
<file>public/lang/el.json</file>
|
||||||
|
<file>public/lang/en.json</file>
|
||||||
|
<file>public/lang/en_AU.json</file>
|
||||||
|
<file>public/lang/en_GB.json</file>
|
||||||
|
<file>public/lang/eo.json</file>
|
||||||
|
<file>public/lang/es.json</file>
|
||||||
|
<file>public/lang/et.json</file>
|
||||||
|
<file>public/lang/eu.json</file>
|
||||||
|
<file>public/lang/fa.json</file>
|
||||||
|
<file>public/lang/fi.json</file>
|
||||||
|
<file>public/lang/fr.json</file>
|
||||||
|
<file>public/lang/gl.json</file>
|
||||||
|
<file>public/lang/he.json</file>
|
||||||
|
<file>public/lang/hi_IN.json</file>
|
||||||
|
<file>public/lang/hr.json</file>
|
||||||
|
<file>public/lang/hu.json</file>
|
||||||
|
<file>public/lang/hy.json</file>
|
||||||
|
<file>public/lang/id.json</file>
|
||||||
|
<file>public/lang/is.json</file>
|
||||||
|
<file>public/lang/it.json</file>
|
||||||
|
<file>public/lang/ja.json</file>
|
||||||
|
<file>public/lang/ka.json</file>
|
||||||
|
<file>public/lang/ko.json</file>
|
||||||
|
<file>public/lang/lt.json</file>
|
||||||
|
<file>public/lang/ltg.json</file>
|
||||||
|
<file>public/lang/lv_LV.json</file>
|
||||||
|
<file>public/lang/mn_MN.json</file>
|
||||||
|
<file>public/lang/ms_MY.json</file>
|
||||||
|
<file>public/lang/nb.json</file>
|
||||||
|
<file>public/lang/nl.json</file>
|
||||||
|
<file>public/lang/oc.json</file>
|
||||||
|
<file>public/lang/pl.json</file>
|
||||||
|
<file>public/lang/pt_BR.json</file>
|
||||||
|
<file>public/lang/pt_PT.json</file>
|
||||||
|
<file>public/lang/ro.json</file>
|
||||||
|
<file>public/lang/ru.json</file>
|
||||||
|
<file>public/lang/sk.json</file>
|
||||||
|
<file>public/lang/sl.json</file>
|
||||||
|
<file>public/lang/sr.json</file>
|
||||||
|
<file>public/lang/sv.json</file>
|
||||||
|
<file>public/lang/th.json</file>
|
||||||
|
<file>public/lang/tr.json</file>
|
||||||
|
<file>public/lang/uk.json</file>
|
||||||
|
<file>public/lang/uz@Latn.json</file>
|
||||||
|
<file>public/lang/vi.json</file>
|
||||||
|
<file>public/lang/zh_CN.json</file>
|
||||||
|
<file>public/lang/zh_HK.json</file>
|
||||||
|
<file>public/lang/zh_TW.json</file>
|
||||||
|
<file>public/scripts/lib/i18next.min.js</file>
|
||||||
<file>public/scripts/login.js</file>
|
<file>public/scripts/login.js</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Loading…
Reference in a new issue