chore: add support for rtl languages (#331)

This commit is contained in:
Miguel Ribeiro 2024-05-18 17:41:01 +02:00 committed by GitHub
parent 38a2ba9804
commit f1815d7335
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 219 additions and 118 deletions

36
admin.php Normal file
View File

@ -0,0 +1,36 @@
<?php
require_once 'includes/header.php';
$settings = [];
$settings['registrations'] = false;
?>
<section class="contain settings">
<section class="account-section">
<header>
<h2><?= translate('backup_and_restore', $i18n) ?></h2>
</header>
<div class="form-group-inline">
<div>
<input type="button" class="button thin" value="<?= translate('backup', $i18n) ?>" id="backupDB" onClick="backupDB()"/>
</div>
<div>
<input type="button" class="secondary-button thin" value="<?= translate('restore', $i18n) ?>" id="restoreDB" onClick="openRestoreDBFileSelect()" />
<input type="file" name="restoreDBFile" id="restoreDBFile" style="display: none;" onChange="restoreDB()" accept=".zip">
</div>
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('restore_info', $i18n) ?>
</p>
</div>
</section>
</section>
<script src="scripts/admin.js?<?= $version ?>"></script>
<?php
require_once 'includes/footer.php';
?>

View File

@ -27,9 +27,12 @@
if (isset($settings['color_theme'])) {
$colorTheme = $settings['color_theme'];
}
$isAdmin = $_SESSION['userId'] == 1;
?>
<!DOCTYPE html>
<html>
<html dir="<?= $languages[$lang]['dir'] ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@ -76,7 +79,7 @@
<script type="text/javascript" src="scripts/i18n/<?= $lang ?>.js?<?= $version ?>"></script>
<script type="text/javascript" src="scripts/i18n/getlang.js?<?= $version ?>"></script>
</head>
<body class="<?= $theme ?>">
<body class="<?= $theme ?> <?= $languages[$lang]['dir'] ?>">
<header>
<div class="contain">
<div class="logo">
@ -94,6 +97,9 @@
<a href="."><i class="fa-solid fa-list"></i><?= translate('subscriptions', $i18n) ?></a>
<a href="stats.php"><i class="fa-solid fa-chart-simple"></i><?= translate('stats', $i18n) ?></a>
<a href="settings.php"><i class="fa-solid fa-gear"></i><?= translate('settings', $i18n) ?></a>
<?php if ($isAdmin): ?>
<a href="admin.php"><i class="fa-solid fa-user-tie"></i><?= translate('admin', $i18n) ?></a>
<?php endif; ?>
<a href="about.php"><i class="fa-solid fa-info-circle"></i><?= translate('about', $i18n) ?></a>
<a href="logout.php"><i class="fa-solid fa-arrow-right-from-bracket"></i><?= translate('logout', $i18n) ?></a>
</div>

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Abonnements",
'stats' => "Statistiken",
'settings' => "Einstellungen",
'admin' => "Admin",
'about' => "Über",
'logout' => "Logout",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Συνδρομές",
'stats' => "Στατιστικές",
'settings' => "Ρυθμίσεις",
'admin' => "Διαχείριση",
'about' => "Για εμάς",
'logout' => "Αποσύνδεση",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Subscriptions",
'stats' => "Statistics",
'settings' => "Settings",
'admin' => "Admin",
'about' => "About",
'logout' => "Logout",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Suscripciones",
'stats' => "Estadísticas",
'settings' => "Configuración",
'admin' => "Admin",
'about' => "Acerca de",
'logout' => "Cerrar Sesión",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Abonnements",
'stats' => "Statistiques",
'settings' => "Paramètres",
'admin' => "Admin",
'about' => "À propos",
'logout' => "Déconnexion",
// Page d'abonnements

View File

@ -24,6 +24,7 @@ $i18n = [
'subscriptions' => 'Abbonamenti',
'stats' => 'Statistiche',
'settings' => 'Impostazioni',
'admin' => 'Amministrazione',
'about' => 'Informazioni',
'logout' => 'Esci',

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "定期購入",
'stats' => "統計",
'settings' => "設定",
'admin' => "管理者",
'about' => "About",
'logout' => "ログアウト",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "구독",
'stats' => "통계",
'settings' => "설정",
'admin' => "관리자",
'about' => "정보",
'logout' => "로그아웃",
// Subscriptions page

View File

@ -1,26 +1,25 @@
<?php
// File Name => Language Name
// File Name => Language Name
$languages = [
// English first
"en" => "English",
"en" => ["name" => "English", "dir" => "ltr"],
// Remaining sorted alphabetically by language code
"de" => "Deutsch",
"el" => "Ελληνικά",
"es" => "Español",
"fr" => "Français",
"it" => "Italiano",
"jp" => "日本語",
"ko" => "한국어",
"pl" => "Polski",
"pt" => "Português",
"pt_br" => "Português Brasileiro",
"ru" => "Русский",
"sr_lat" => "Srpski",
"sr" => "Српски",
"tr" => "Türkçe",
"zh_cn" => "简体中文",
"zh_tw" => "繁體中文",
"de" => ["name" => "Deutsch", "dir" => "ltr"],
"el" => ["name" => "Ελληνικά", "dir" => "ltr"],
"es" => ["name" => "Español", "dir" => "ltr"],
"fr" => ["name" => "Français", "dir" => "ltr"],
"it" => ["name" => "Italiano", "dir" => "ltr"],
"jp" => ["name" => "日本語", "dir" => "ltr"],
"ko" => ["name" => "한국어", "dir" => "ltr"],
"pl" => ["name" => "Polski", "dir" => "ltr"],
"pt" => ["name" => "Português", "dir" => "ltr"],
"pt_br" => ["name" => "Português Brasileiro", "dir" => "ltr"],
"ru" => ["name" => "Русский", "dir" => "ltr"],
"sr_lat" => ["name" => "Srpski", "dir" => "ltr"],
"sr" => ["name" => "Српски", "dir" => "ltr"],
"tr" => ["name" => "Türkçe", "dir" => "ltr"],
"zh_cn" => ["name" => "简体中文", "dir" => "ltr"],
"zh_tw" => ["name" => "繁體中文", "dir" => "ltr"],
]
?>
?>

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Subskrypcje",
'stats' => "Statystyki",
'settings' => "Ustawienia",
'admin' => "Admin",
'about' => "O aplikacji",
'logout' => "Wyloguj się",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Subscrições",
'stats' => "Estatísticas",
'settings' => "Definições",
'admin' => "Administração",
'about' => "Sobre",
'logout' => "Terminar Sessão",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Assinaturas",
'stats' => "Estatísticas",
'settings' => "Configurações",
'admin' => "Admin",
'about' => "Sobre",
'logout' => "Sair",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Подписки",
'stats' => "Статистика",
'settings' => "Настройки",
'admin' => "Администратор",
'about' => "О программе",
'logout' => "Выйти",
// Subscriptions page

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Претплате",
'stats' => "Статистике",
'settings' => "Подешавања",
'admin' => "Админ",
'about' => "О апликацији",
'logout' => "Одјава",
// Страница са претплатама

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Pretplate",
'stats' => "Statistike",
'settings' => "Podešavanja",
'admin' => "Admin",
'about' => "O aplikaciji",
'logout' => "Odjava",
// Stranica sa pretplatama

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "Abonelikler",
'stats' => "İstatistikler",
'settings' => "Ayarlar",
'admin' => "Yönetici",
'about' => "Hakkında",
'logout' => "Çıkış Yap",
// Subscriptions page

View File

@ -24,6 +24,7 @@ $i18n = [
'subscriptions' => "订阅",
'stats' => "统计",
'settings' => "设置",
'admin' => "管理员",
'about' => "关于",
'logout' => "登出",

View File

@ -22,6 +22,7 @@ $i18n = [
'subscriptions' => "訂閱",
'stats' => "統計",
'settings' => "設定",
'admin' => "管理員",
'about' => "關於",
'logout' => "登出",
// 訂閱頁面

View File

@ -77,7 +77,7 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
}
?>
<!DOCTYPE html>
<html>
<html dir="<?= $languages[$lang]['dir'] ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@ -94,7 +94,7 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
<link rel="stylesheet" href="styles/barlow.css">
<link rel="stylesheet" href="styles/login-dark-theme.css?<?= $version ?>" id="dark-theme" <?= $theme == "light" ? "disabled" : "" ?>>
</head>
<body>
<body class="<?= $languages[$lang]['dir'] ?>">
<div class="content">
<section class="container">
<header>

View File

@ -91,7 +91,7 @@ if (isset($_POST['username'])) {
}
?>
<!DOCTYPE html>
<html>
<html dir="<?= $languages[$lang]['dir'] ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@ -109,7 +109,7 @@ if (isset($_POST['username'])) {
<link rel="stylesheet" href="styles/barlow.css">
<script type="text/javascript" src="scripts/registration.js?<?= $version ?>"></script>
</head>
<body>
<body class="<?= $languages[$lang]['dir'] ?>">
<div class="content">
<section class="container">
<header>
@ -157,10 +157,10 @@ if (isset($_POST['username'])) {
<label for="language"><?= translate('language', $i18n) ?>:</label>
<select id="language" name="language" placeholder="Language" onchange="changeLanguage(this.value)">
<?php
foreach ($languages as $code => $name) {
foreach ($languages as $code => $language) {
$selected = ($code === $lang) ? 'selected' : '';
?>
<option value="<?= $code ?>" <?= $selected ?>><?= $name ?></option>
<option value="<?= $code ?>" <?= $selected ?>><?= $language['name'] ?></option>
<?php
}
?>

59
scripts/admin.js Normal file
View File

@ -0,0 +1,59 @@
function backupDB() {
const button = document.getElementById("backupDB");
button.disabled = true;
fetch('endpoints/db/backup.php')
.then(response => response.json())
.then(data => {
if (data.success) {
const link = document.createElement('a');
const filename = data.file;
link.href = '.tmp/' + filename;
link.download = 'backup.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
button.disabled = false;
} else {
showErrorMessage(data.errorMessage);
button.disabled = false;
}
})
.catch(error => {
showErrorMessage(error);
button.disabled = false;
});
}
function openRestoreDBFileSelect() {
document.getElementById('restoreDBFile').click();
};
function restoreDB() {
const input = document.getElementById('restoreDBFile');
const file = input.files[0];
if (!file) {
console.error('No file selected');
return;
}
const formData = new FormData();
formData.append('file', file);
fetch('endpoints/db/restore.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSuccessMessage(data.message)
window.location.href = 'logout.php';
} else {
showErrorMessage(data.message);
}
})
.catch(error => showErrorMessage('Error:', error));
}

View File

@ -949,66 +949,6 @@ function setHideDisabled() {
storeSettingsOnDB('hide_disabled', value);
}
function backupDB() {
const button = document.getElementById("backupDB");
button.disabled = true;
fetch('endpoints/db/backup.php')
.then(response => response.json())
.then(data => {
if (data.success) {
const link = document.createElement('a');
const filename = data.file;
link.href = '.tmp/' + filename;
link.download = 'backup.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
button.disabled = false;
} else {
showErrorMessage(data.errorMessage);
button.disabled = false;
}
})
.catch(error => {
showErrorMessage(error);
button.disabled = false;
});
}
function openRestoreDBFileSelect() {
document.getElementById('restoreDBFile').click();
};
function restoreDB() {
const input = document.getElementById('restoreDBFile');
const file = input.files[0];
if (!file) {
console.error('No file selected');
return;
}
const formData = new FormData();
formData.append('file', file);
fetch('endpoints/db/restore.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSuccessMessage(data.message)
window.location.href = 'logout.php';
} else {
showErrorMessage(data.message);
}
})
.catch(error => showErrorMessage('Error:', error));
}
function saveCategorySorting() {
const categories = document.getElementById('categories');
const categoryIds = Array.from(categories.children).map(category => category.dataset.categoryid);

View File

@ -97,10 +97,10 @@
<label for="language"><?= translate('language', $i18n) ?>:</label>
<select id="language" name="language" placeholder="Language">
<?php
foreach ($languages as $code => $name) {
foreach ($languages as $code => $language) {
$selected = ($code === $lang) ? 'selected' : '';
?>
<option value="<?= $code ?>" <?= $selected ?>><?= $name ?></option>
<option value="<?= $code ?>" <?= $selected ?>><?= $language['name'] ?></option>
<?php
}
?>
@ -992,27 +992,6 @@
</div>
</section>
<section class="account-section">
<header>
<h2><?= translate('backup_and_restore', $i18n) ?></h2>
</header>
<div class="form-group-inline">
<div>
<input type="button" class="button thin" value="<?= translate('backup', $i18n) ?>" id="backupDB" onClick="backupDB()"/>
</div>
<div>
<input type="button" class="secondary-button thin" value="<?= translate('restore', $i18n) ?>" id="restoreDB" onClick="openRestoreDBFileSelect()" />
<input type="file" name="restoreDBFile" id="restoreDBFile" style="display: none;" onChange="restoreDB()" accept=".zip">
</div>
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('restore_info', $i18n) ?>
</p>
</div>
</section>
</section>
<script src="scripts/settings.js?<?= $version ?>"></script>
<script src="scripts/notifications.js?<?= $version ?>"></script>

View File

@ -74,6 +74,11 @@ label {
cursor: pointer;
}
.rtl .form-group-inline label {
margin-right: 8px;
margin-left: 0px;
}
input {
box-sizing: border-box;
}

View File

@ -123,11 +123,9 @@ header .logo .logo-image {
color: black;
padding: 14px 18px;
text-decoration: none;
display: block;
}
.dropdown-content a > i {
margin-right: 12px;
display: flex;
flex-direction: row;
gap: 12px;
}
.dropdown-content a:hover {
@ -233,6 +231,11 @@ main > .contain {
font-size: 20px;
}
.rtl .top-actions > .search > .search-icon {
float: left;
right: -15px;
}
.subscriptions {
display: flex;
flex-direction: column;
@ -340,6 +343,12 @@ main > .contain {
margin-right: 12px;
}
.rtl .subscription .price img {
margin-right: 0px;
margin-left: 12px;
}
.subscription .actions {
flex-basis: auto;
}
@ -359,6 +368,7 @@ main > .contain {
.subscription-secondary > span {
justify-content: flex-start;
flex-basis: 33%;
gap: 10px;
}
.subscription-secondary > .url {
@ -367,10 +377,16 @@ main > .contain {
cursor: pointer;
}
.rtl .subscription-secondary > .url {
margin-left: 0px;
margin-right: auto;
}
.subscription-notes > span {
display: flex;
align-items: center;
font-size: 14px;
gap: 10px;
}
@media (max-width: 768px) {
@ -403,7 +419,6 @@ main > .contain {
.subscription-secondary img,
.subscription-notes img {
height: 20px;
margin-right: 10px;
}
.subscription-secondary .url img {
@ -812,6 +827,12 @@ header #avatar {
color: var(--accent-color);
}
.rtl .credits-list > p > span > a,
.rtl .settings-notes > p > span > a {
margin-left: 0px;
margin-right: 5px;
}
.credits-list > p > span > a:visited,
.settings-notes > p > span > a:visited {
color: var(--accent-color);
@ -823,6 +844,12 @@ header #avatar {
margin-right: 5px;
}
.rtl .settings-notes > p > i,
.rtl .account-section .notes > p > i {
margin-right: 0px;
margin-left: 5px;
}
.form-group {
margin-bottom: 20px;
}
@ -1249,6 +1276,11 @@ textarea.thin {
color: gray;
}
.rtl .close-form {
right: auto;
left: 15px;
}
.sort-container {
position: relative;
}
@ -1270,6 +1302,11 @@ textarea.thin {
z-index: 2;
}
.rtl .sort-options {
left: 0px;
right: auto;
}
@media (max-width: 380px) {
.sort-container {
flex-grow: 1;
@ -1293,6 +1330,10 @@ textarea.thin {
cursor: pointer;
}
.rtl .sort-options > ul > li {
padding: 14px 18px 14px 35px;
}
.sort-options > ul > li:last-of-type {
border-bottom: none;
}
@ -1308,6 +1349,10 @@ textarea.thin {
background-position: center right 10px;
}
.rtl .sort-options > ul > li.selected {
background-position: center left 10px;
}
.subscription-list-title {
font-size: 18px;
padding: 4px;
@ -1594,6 +1639,12 @@ textarea.thin {
margin-top: 6px;
}
.rtl .filtermenu-content {
left: 0;
right: auto;
}
@media (max-width: 354px) {
.on-dashboard .filtermenu-content {
right: -94px;
@ -1647,6 +1698,10 @@ textarea.thin {
background-position: center right 10px;
}
.rtl .filtermenu-content .filter-item.selected {
background-position: center left 10px;
}
.filtermenu-content .filter-title.filter-clear {
color: var(--hover-color);
font-weight: normal;
@ -1657,6 +1712,11 @@ textarea.thin {
margin-right: 8px;
}
.rtl .filtermenu-content .filter-title.filter-clear > i {
margin-left: 8px;
margin-right: 0px;
}
.filtermenu-content .filter-item:hover,
.filtermenu-content .filter-title:hover {
background-color: #f1f1f1;