From ad48b865f231e38d1a05f3f9a4e15588922ada22 Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 09:56:11 +0330 Subject: [PATCH 1/8] feat: add automatic HTML lang and dir attributes - Add updateHtmlAttributes() function to set lang attribute on HTML element - Automatically add dir=rtl for RTL languages (fa, ar) and remove for LTR - Initialize attributes on component load with current locale - Listen for locale changes to update attributes dynamically - Add rtlLanguages configuration array for maintainability --- static/js/languageSelector.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/static/js/languageSelector.js b/static/js/languageSelector.js index 4f418f81..7ff3fcaa 100644 --- a/static/js/languageSelector.js +++ b/static/js/languageSelector.js @@ -11,6 +11,24 @@ const languages = [ { code: 'fa', name: 'فارسی', flag: '🦁' } ]; +// RTL languages +const rtlLanguages = ['fa']; // ['fa', 'ar'] + +// Update HTML lang attribute and dir for RTL languages +function updateHtmlAttributes(langCode) { + const htmlElement = document.documentElement; + + // Set lang attribute + htmlElement.setAttribute('lang', langCode); + + // Set dir attribute for RTL languages + if (rtlLanguages.includes(langCode)) { + htmlElement.setAttribute('dir', 'rtl'); + } else { + htmlElement.removeAttribute('dir'); + } +} + /** * Creates and initializes a custom language selector component * @param {string} containerId - ID of the container element @@ -32,6 +50,9 @@ function createLanguageSelector(containerId = 'language-selector') { const currentLocale = window.i18n ? window.i18n.getCurrentLocale() : 'en'; const currentLang = languages.find(l => l.code === currentLocale) || languages[0]; + // Set initial HTML attributes + updateHtmlAttributes(currentLocale); + // Create toggle button const toggle = document.createElement('div'); toggle.className = 'language-selector-toggle'; @@ -150,6 +171,9 @@ async function selectLanguage(langCode, container) { await window.i18n.setLocale(langCode); } + // Update HTML lang attribute and dir for RTL languages + updateHtmlAttributes(langCode); + updateSelectedLanguage(langCode, container); closeDropdown(container); } @@ -178,4 +202,4 @@ function updateSelectedLanguage(langCode, container) { // Create language selector when DOM is ready document.addEventListener('DOMContentLoaded', () => { createLanguageSelector(); -}); +}); \ No newline at end of file From a3b2192b0e6c36c7909d17308169ed5d40a448e5 Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 09:57:11 +0330 Subject: [PATCH 2/8] feat(i18n): add Persian(fa) to language list --- static/js/auth.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/static/js/auth.js b/static/js/auth.js index 33a743be..cb0920ad 100644 --- a/static/js/auth.js +++ b/static/js/auth.js @@ -34,6 +34,11 @@ const LANGUAGE_TEXTS = { title: '欢迎使用 OxiCloud', subtitle: '请选择您的语言', continue: '继续' + }, + fa: { + title: 'به OxiCloud خوش آمدید', + subtitle: 'لطفا زبان خود را انتخاب کنید', + continue: 'ادامه' } }; From 00a8e0c36166e930319d28f32aedd9606604b7fa Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 09:57:38 +0330 Subject: [PATCH 3/8] feat(i18n): add Persian(fa) to language list --- static/login.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/static/login.html b/static/login.html index ef759a40..1dcfd042 100644 --- a/static/login.html +++ b/static/login.html @@ -52,6 +52,13 @@ 🇨🇳 中文 + + From 31ad2f878a200a646c5549dc96d6ab2561d02f69 Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 09:57:55 +0330 Subject: [PATCH 4/8] feat(i18n): add Persian(fa) to language list --- static/sw.js | 1 + 1 file changed, 1 insertion(+) diff --git a/static/sw.js b/static/sw.js index cccb8c24..0be22ed5 100644 --- a/static/sw.js +++ b/static/sw.js @@ -7,6 +7,7 @@ const ASSETS_TO_CACHE = [ '/js/languageSelector.js', '/locales/en.json', '/locales/es.json', + '/locales/fa.json', '/favicon.ico', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css', 'https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js' From 0096d2ee95f2200bf1a5842ed3324b0746357fce Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 10:04:37 +0330 Subject: [PATCH 5/8] feat: add RTL-specific CSS for bidirectional layout --- static/css/auth.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static/css/auth.css b/static/css/auth.css index c7a94967..5c2ff2ce 100644 --- a/static/css/auth.css +++ b/static/css/auth.css @@ -60,6 +60,10 @@ .auth-form { width: 100%; text-align: left; + + [dir='rtl'] & { + text-align: right; + } } .auth-input-group { From d05485c459cab1b7976351e7358e6569078fdabc Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 10:04:49 +0330 Subject: [PATCH 6/8] feat: add RTL-specific CSS for bidirectional layout --- static/css/favorites.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/static/css/favorites.css b/static/css/favorites.css index f3ef4d2c..d1832ca7 100644 --- a/static/css/favorites.css +++ b/static/css/favorites.css @@ -15,6 +15,11 @@ cursor: pointer; z-index: 5; transition: all 0.2s ease; + + [dir='rtl'] & { + left: 10px; + right: unset; + } } .favorite-indicator:hover { @@ -34,6 +39,11 @@ /* Ajustes para la vista de cuadrícula */ .file-card.favorite-item { border-left: 3px solid #ffc107; + + [dir='rtl'] & { + border-right: 3px solid #ffc107; + border-left: unset; + } } /* Ajustes para la vista de lista */ From 449ac7a9810962527e8aca3ae059483d33d24321 Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 10:04:58 +0330 Subject: [PATCH 7/8] feat: add RTL-specific CSS for bidirectional layout --- static/css/recent.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/static/css/recent.css b/static/css/recent.css index eeaf01f6..a54ec54c 100644 --- a/static/css/recent.css +++ b/static/css/recent.css @@ -13,6 +13,11 @@ border-radius: 50%; color: #6c757d; z-index: 5; + + [dir='rtl'] & { + left: 10px; + right: unset; + } } /* Estilos para los elementos de la vista de recientes */ @@ -23,6 +28,11 @@ /* Ajustes para la vista de cuadrícula */ .file-card.recent-item { border-left: 3px solid #6c757d; + + [dir='rtl'] & { + border-right: 3px solid #6c757d; + border-left: unset; + } } /* Ajustes para la vista de lista */ From 67ade90d95254326f3b558df8967aaf50608c5ca Mon Sep 17 00:00:00 2001 From: Goudarz Jafari Date: Sun, 8 Feb 2026 10:05:04 +0330 Subject: [PATCH 8/8] feat: add RTL-specific CSS for bidirectional layout --- static/css/style.css | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/static/css/style.css b/static/css/style.css index d8e13692..af6bad6f 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -93,6 +93,11 @@ select:focus { align-items: center; justify-content: center; margin-right: 10px; + + [dir='rtl'] & { + margin-left: 10px; + margin-right: unset; + } } .logo svg { @@ -137,6 +142,11 @@ select:focus { margin-right: 15px; width: 20px; text-align: center; + + [dir='rtl'] & { + margin-left: 15px; + margin-right: unset; + } } /* Storage indicator */ @@ -202,6 +212,11 @@ select:focus { margin-right: 20px; display: flex; align-items: center; + + [dir='rtl'] & { + margin-left: 20px; + margin-right: unset; + } } .search-container input { @@ -241,6 +256,11 @@ select:focus { font-size: 16px; pointer-events: none; transition: color 0.2s ease; + + [dir='rtl'] & { + right: 16px; + left: unset; + } } .search-container input:focus + .search-icon, @@ -265,6 +285,11 @@ select:focus { justify-content: center; transition: all 0.2s ease; box-shadow: 0 2px 8px rgba(255, 94, 58, 0.3); + + [dir='rtl'] & { + left: 6px; + right: unset; + } } .search-button:hover { @@ -1058,6 +1083,11 @@ select:focus { margin-right: 8px; width: 16px; text-align: center; + + [dir='rtl'] & { + margin-left: 8px; + margin-right: unset; + } } /* Dialog */ @@ -1153,6 +1183,13 @@ select:focus { border-left: 4px solid #ff5e3a; z-index: 1000; display: none; + + [dir='rtl'] & { + left: 20px; + border-right: 4px solid #ff5e3a; + right: unset; + border-left: unset; + } } .notification-title { @@ -1221,6 +1258,11 @@ select:focus { justify-content: center; margin-right: 14px; flex-shrink: 0; + + [dir='rtl'] & { + margin-left: 14px; + margin-right: unset; + } } .modal-icon i { @@ -1251,6 +1293,11 @@ select:focus { justify-content: center; color: #64748b; transition: all 0.15s ease; + + [dir='rtl'] & { + left: 16px; + right: unset; + } } .modal-close-btn:hover { @@ -1571,6 +1618,10 @@ header { background-repeat: no-repeat; background-position: right 10px center; transition: border-color 0.15s ease, box-shadow 0.15s ease; + + [dir='rtl'] & { + background-position: left 10px center; + } } .filter-group select:hover { @@ -1990,11 +2041,21 @@ header { .folder-select-item.selected { background-color: #e6f7ff; border-left: 3px solid #1890ff; + + [dir='rtl'] & { + border-right: 3px solid #1890ff; + border-left: unset; + } } .folder-select-item i { margin-right: 8px; color: #ffc107; + + [dir='rtl'] & { + margin-left: 8px; + margin-right: unset; + } } /* Styles for trash */ @@ -2241,3 +2302,12 @@ header { .button.primary:hover { background-color: #e64a29; } + +html[dir='rtl'] .fa-arrow-left::before { + content: "\f061"; +} + +html[dir='rtl'] .fa-sign-out-alt { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); +} \ No newline at end of file