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 {
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 */
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 */
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
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: 'ادامه'
}
};
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
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 @@
🇨🇳
中文
+
+
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'