Merge pull request #70 from Goudarz/feature/bidirectional-support
Add RTL Support with Automatic HTML Attributes
This commit is contained in:
@@ -60,6 +60,10 @@
|
|||||||
.auth-form {
|
.auth-form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-input-group {
|
.auth-input-group {
|
||||||
|
|||||||
@@ -15,6 +15,11 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
left: 10px;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.favorite-indicator:hover {
|
.favorite-indicator:hover {
|
||||||
@@ -34,6 +39,11 @@
|
|||||||
/* Ajustes para la vista de cuadrícula */
|
/* Ajustes para la vista de cuadrícula */
|
||||||
.file-card.favorite-item {
|
.file-card.favorite-item {
|
||||||
border-left: 3px solid #ffc107;
|
border-left: 3px solid #ffc107;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
border-right: 3px solid #ffc107;
|
||||||
|
border-left: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ajustes para la vista de lista */
|
/* Ajustes para la vista de lista */
|
||||||
|
|||||||
@@ -13,6 +13,11 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
left: 10px;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos para los elementos de la vista de recientes */
|
/* Estilos para los elementos de la vista de recientes */
|
||||||
@@ -23,6 +28,11 @@
|
|||||||
/* Ajustes para la vista de cuadrícula */
|
/* Ajustes para la vista de cuadrícula */
|
||||||
.file-card.recent-item {
|
.file-card.recent-item {
|
||||||
border-left: 3px solid #6c757d;
|
border-left: 3px solid #6c757d;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
border-right: 3px solid #6c757d;
|
||||||
|
border-left: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ajustes para la vista de lista */
|
/* Ajustes para la vista de lista */
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ select:focus {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo svg {
|
.logo svg {
|
||||||
@@ -137,6 +142,11 @@ select:focus {
|
|||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Storage indicator */
|
/* Storage indicator */
|
||||||
@@ -202,6 +212,11 @@ select:focus {
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container input {
|
.search-container input {
|
||||||
@@ -241,6 +256,11 @@ select:focus {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
right: 16px;
|
||||||
|
left: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container input:focus + .search-icon,
|
.search-container input:focus + .search-icon,
|
||||||
@@ -265,6 +285,11 @@ select:focus {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
box-shadow: 0 2px 8px rgba(255, 94, 58, 0.3);
|
box-shadow: 0 2px 8px rgba(255, 94, 58, 0.3);
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
left: 6px;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-button:hover {
|
.search-button:hover {
|
||||||
@@ -1058,6 +1083,11 @@ select:focus {
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dialog */
|
/* Dialog */
|
||||||
@@ -1153,6 +1183,13 @@ select:focus {
|
|||||||
border-left: 4px solid #ff5e3a;
|
border-left: 4px solid #ff5e3a;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
left: 20px;
|
||||||
|
border-right: 4px solid #ff5e3a;
|
||||||
|
right: unset;
|
||||||
|
border-left: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-title {
|
.notification-title {
|
||||||
@@ -1221,6 +1258,11 @@ select:focus {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 14px;
|
margin-right: 14px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 14px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-icon i {
|
.modal-icon i {
|
||||||
@@ -1251,6 +1293,11 @@ select:focus {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
left: 16px;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-close-btn:hover {
|
.modal-close-btn:hover {
|
||||||
@@ -1571,6 +1618,10 @@ header {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 10px center;
|
background-position: right 10px center;
|
||||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
background-position: left 10px center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-group select:hover {
|
.filter-group select:hover {
|
||||||
@@ -1990,11 +2041,21 @@ header {
|
|||||||
.folder-select-item.selected {
|
.folder-select-item.selected {
|
||||||
background-color: #e6f7ff;
|
background-color: #e6f7ff;
|
||||||
border-left: 3px solid #1890ff;
|
border-left: 3px solid #1890ff;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
border-right: 3px solid #1890ff;
|
||||||
|
border-left: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-select-item i {
|
.folder-select-item i {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
color: #ffc107;
|
color: #ffc107;
|
||||||
|
|
||||||
|
[dir='rtl'] & {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles for trash */
|
/* Styles for trash */
|
||||||
@@ -2241,3 +2302,12 @@ header {
|
|||||||
.button.primary:hover {
|
.button.primary:hover {
|
||||||
background-color: #e64a29;
|
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);
|
||||||
|
}
|
||||||
@@ -34,6 +34,11 @@ const LANGUAGE_TEXTS = {
|
|||||||
title: '欢迎使用 OxiCloud',
|
title: '欢迎使用 OxiCloud',
|
||||||
subtitle: '请选择您的语言',
|
subtitle: '请选择您的语言',
|
||||||
continue: '继续'
|
continue: '继续'
|
||||||
|
},
|
||||||
|
fa: {
|
||||||
|
title: 'به OxiCloud خوش آمدید',
|
||||||
|
subtitle: 'لطفا زبان خود را انتخاب کنید',
|
||||||
|
continue: 'ادامه'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,24 @@ const languages = [
|
|||||||
{ code: 'fa', name: 'فارسی', flag: '🦁' }
|
{ 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
|
* Creates and initializes a custom language selector component
|
||||||
* @param {string} containerId - ID of the container element
|
* @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 currentLocale = window.i18n ? window.i18n.getCurrentLocale() : 'en';
|
||||||
const currentLang = languages.find(l => l.code === currentLocale) || languages[0];
|
const currentLang = languages.find(l => l.code === currentLocale) || languages[0];
|
||||||
|
|
||||||
|
// Set initial HTML attributes
|
||||||
|
updateHtmlAttributes(currentLocale);
|
||||||
|
|
||||||
// Create toggle button
|
// Create toggle button
|
||||||
const toggle = document.createElement('div');
|
const toggle = document.createElement('div');
|
||||||
toggle.className = 'language-selector-toggle';
|
toggle.className = 'language-selector-toggle';
|
||||||
@@ -150,6 +171,9 @@ async function selectLanguage(langCode, container) {
|
|||||||
await window.i18n.setLocale(langCode);
|
await window.i18n.setLocale(langCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update HTML lang attribute and dir for RTL languages
|
||||||
|
updateHtmlAttributes(langCode);
|
||||||
|
|
||||||
updateSelectedLanguage(langCode, container);
|
updateSelectedLanguage(langCode, container);
|
||||||
closeDropdown(container);
|
closeDropdown(container);
|
||||||
}
|
}
|
||||||
@@ -178,4 +202,4 @@ function updateSelectedLanguage(langCode, container) {
|
|||||||
// Create language selector when DOM is ready
|
// Create language selector when DOM is ready
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
createLanguageSelector();
|
createLanguageSelector();
|
||||||
});
|
});
|
||||||
@@ -52,6 +52,13 @@
|
|||||||
<span class="language-flag">🇨🇳</span>
|
<span class="language-flag">🇨🇳</span>
|
||||||
<span class="language-name">中文</span>
|
<span class="language-name">中文</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="language-option" data-lang="fa">
|
||||||
|
<input type="radio" name="language" value="fa">
|
||||||
|
<span class="language-radio"></span>
|
||||||
|
<span class="language-flag">🦁</span>
|
||||||
|
<span class="language-name">فارسی</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="auth-button" id="language-continue" disabled>Continue</button>
|
<button type="button" class="auth-button" id="language-continue" disabled>Continue</button>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const ASSETS_TO_CACHE = [
|
|||||||
'/js/languageSelector.js',
|
'/js/languageSelector.js',
|
||||||
'/locales/en.json',
|
'/locales/en.json',
|
||||||
'/locales/es.json',
|
'/locales/es.json',
|
||||||
|
'/locales/fa.json',
|
||||||
'/favicon.ico',
|
'/favicon.ico',
|
||||||
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css',
|
'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'
|
'https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js'
|
||||||
|
|||||||
Reference in New Issue
Block a user