Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions public/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2932,3 +2932,107 @@ body {
html[data-theme] {
visibility: visible;
}

/* RTL Support */
[dir="rtl"] body {
text-align: right;
}

[dir="rtl"] .nav-logo-wordmark {
left: auto;
right: 0;
}

[dir="rtl"] .nav-links {
margin-left: var(--spacing-lg);
margin-right: auto;
}

[dir="rtl"] .nav-actions {
justify-content: flex-start;
}

[dir="rtl"] .hero-buttons,
[dir="rtl"] .hero-content {
text-align: center;
}

[dir="rtl"] .feature-card,
[dir="rtl"] .community-card,
[dir="rtl"] .testimonial-card,
[dir="rtl"] .faq-answer,
[dir="rtl"] .app-page-desc {
text-align: right;
}

[dir="rtl"] .faq-toggle-icon {
margin-right: auto;
margin-left: 0;
}

[dir="rtl"] .faq-number {
margin-right: 0;
margin-left: var(--spacing-md);
}

[dir="rtl"] .scroll-to-top {
right: auto;
left: var(--spacing-xl);
}

[dir="rtl"] .footer-column {
text-align: right;
}

[dir="rtl"] .toggle-slider::before {
left: auto;
right: 2px;
}

[dir="rtl"] .filter-toggle input:checked + .toggle-slider::before {
transform: translateX(-20px);
}

[dir="rtl"] .version-card {
border-left: 1px solid var(--border);
border-right: 4px solid var(--primary-blue);
}

[dir="rtl"] .version-card[data-type="patches"] {
border-right-color: var(--primary-teal);
}

[dir="rtl"] .feature-slide-icon {
margin-left: auto;
margin-right: 0;
}

[dir="rtl"] .carousel-button.prev {
left: auto;
right: var(--spacing-md);
}

[dir="rtl"] .carousel-button.next {
right: auto;
left: var(--spacing-md);
}

[dir="rtl"] .carousel-button svg {
transform: rotate(180deg);
}

@media (max-width: 768px) {
[dir="rtl"] .nav-links {
margin: 0;
}

[dir="rtl"] .mobile-menu {
left: 0;
right: auto;
transform: translateX(-100%);
}

[dir="rtl"] .mobile-menu.active {
transform: translateX(0);
}
}
20 changes: 17 additions & 3 deletions public/js/app-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ function appGradient(appId) {
tab.setAttribute('aria-selected', i === currentIndex);
});

// Handle RTL direction
const isRTL = document.documentElement.dir === 'rtl';
const translateX = isRTL ? currentIndex * 100 : -currentIndex * 100;

panel.style.transition = animate
? 'transform 0.45s cubic-bezier(0.4, 0, 0.2, 1)'
: 'none';
panel.style.transform = `translateX(-${currentIndex * 100}%)`;
panel.style.transform = `translateX(${translateX}%)`;

// Apply per-app theme after transition settles
const appId = pages[currentIndex]?.dataset.app;
Expand Down Expand Up @@ -136,7 +140,12 @@ function appGradient(appId) {
if (!isDragging) return;
isDragging = false;
const delta = dragStartX - e.changedTouches[0].clientX;
if (Math.abs(delta) > 40) delta > 0 ? next() : prev();

// Handle RTL swipe direction
const isRTL = document.documentElement.dir === 'rtl';
const normalizedDelta = isRTL ? -delta : delta;

if (Math.abs(normalizedDelta) > 40) normalizedDelta > 0 ? next() : prev();
startAutoplay();
});

Expand All @@ -151,7 +160,12 @@ function appGradient(appId) {
if (!isDragging) return;
isDragging = false;
const delta = dragStartX - e.clientX;
if (Math.abs(delta) > 40) delta > 0 ? next() : prev();

// Handle RTL swipe direction
const isRTL = document.documentElement.dir === 'rtl';
const normalizedDelta = isRTL ? -delta : delta;

if (Math.abs(normalizedDelta) > 40) normalizedDelta > 0 ? next() : prev();
startAutoplay();
});

Expand Down
10 changes: 10 additions & 0 deletions public/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
// Apply translations after everything is ready
this.applyTranslations();

// Set direction (RTL/LTR)
this.applyDirection();

// Remove loading class to show content
document.documentElement.classList.remove('i18n-loading');

Expand Down Expand Up @@ -168,6 +171,7 @@
localStorage.setItem(I18N_KEY, lang);
await this.loadTranslations(lang);
this.applyTranslations();
this.applyDirection();

// Reload testimonials with current language
if (typeof window.reloadTestimonials === 'function') {
Expand Down Expand Up @@ -310,6 +314,12 @@
document.documentElement.lang = this.currentLang;
}

applyDirection() {
const rtlLanguages = ['ar', 'he', 'fa', 'ur'];
const lang = this.currentLang.split('-')[0];
document.documentElement.dir = rtlLanguages.includes(lang) ? 'rtl' : 'ltr';
}

// Close all open lang menus
closeAllMenus() {
document.querySelectorAll('.lang-menu').forEach(m => m.classList.remove('open'));
Expand Down
7 changes: 6 additions & 1 deletion public/js/lang-preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
(function() {
'use strict';

const SUPPORTED_LOCALES = ['en', 'cs-CZ', 'de-DE', 'es-ES', 'fr-FR', 'it-IT', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sk-SK', 'vi-VN', 'tr-TR', 'uk-UA', 'ja-JP', 'ko-KR', 'zh-CN'];
const SUPPORTED_LOCALES = ['en', 'cs-CZ', 'de-DE', 'es-ES', 'fr-FR', 'it-IT', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sk-SK', 'vi-VN', 'tr-TR', 'uk-UA', 'ar', 'ja-JP', 'ko-KR', 'zh-CN'];

const STORAGE_KEY = 'morphe-language';

Expand Down Expand Up @@ -38,6 +38,11 @@
// Set language attribute immediately
document.documentElement.lang = lang;

// Set direction (RTL/LTR)
const rtlLanguages = ['ar', 'he', 'fa', 'ur'];
const baseLang = lang.split('-')[0];
document.documentElement.dir = rtlLanguages.includes(baseLang) ? 'rtl' : 'ltr';

// Hide content until i18n loads
document.documentElement.classList.add('i18n-loading');

Expand Down
42 changes: 38 additions & 4 deletions public/js/testimonials.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@
const cardWidth = cards[0].offsetWidth;
const gap = parseInt(getComputedStyle(grid).gap) || 16;
const offset = currentIndex * (cardWidth + gap);
grid.style.transform = `translateX(-${offset}px)`;

// Handle RTL direction
const isRTL = document.documentElement.dir === 'rtl';
const translateX = isRTL ? offset : -offset;

grid.style.transform = `translateX(${translateX}px)`;
grid.style.transition = 'transform 0.3s ease-out';
}

Expand All @@ -147,7 +152,12 @@
return;
}

if (deltaX > 0) {
// In RTL, swiping right (negative deltaX) should move to next index
// and swiping left (positive deltaX) should move to previous index
const isRTL = document.documentElement.dir === 'rtl';
const normalizedDeltaX = isRTL ? -deltaX : deltaX;

if (normalizedDeltaX > 0) {
currentIndex = currentIndex >= maxIndex ? 0 : currentIndex + 1;
} else {
currentIndex = currentIndex <= 0 ? maxIndex : currentIndex - 1;
Expand Down Expand Up @@ -193,15 +203,39 @@
document.addEventListener('mouseup', mouseUpHandler);

// Button controls
const isRTL = document.documentElement.dir === 'rtl';

// In RTL: swap arrow icons and reorder buttons so ‹ is on the right, › on the left
if (isRTL) {
const prevIcon = prevBtn.querySelector('.material-symbols-rounded');
const nextIcon = nextBtn.querySelector('.material-symbols-rounded');
if (prevIcon) prevIcon.textContent = 'chevron_right';
if (nextIcon) nextIcon.textContent = 'chevron_left';
// Move nextBtn before prevBtn in DOM so › appears on the left
if (prevBtn.parentNode) {
prevBtn.parentNode.insertBefore(nextBtn, prevBtn);
}
}

const nextClick = (e) => {
e.preventDefault();
currentIndex = currentIndex >= maxIndex ? 0 : currentIndex + 1;
// In RTL, the visual "next" button (chevron pointing left) has class .prev
// so we invert the index direction
if (isRTL) {
currentIndex = currentIndex <= 0 ? maxIndex : currentIndex - 1;
} else {
currentIndex = currentIndex >= maxIndex ? 0 : currentIndex + 1;
}
updateCarousel();
};

const prevClick = (e) => {
e.preventDefault();
currentIndex = currentIndex <= 0 ? maxIndex : currentIndex - 1;
if (isRTL) {
currentIndex = currentIndex >= maxIndex ? 0 : currentIndex + 1;
} else {
currentIndex = currentIndex <= 0 ? maxIndex : currentIndex - 1;
}
updateCarousel();
};

Expand Down
Loading