diff --git a/css/public-rtl.css b/css/public-rtl.css new file mode 100644 index 000000000..c07c95251 --- /dev/null +++ b/css/public-rtl.css @@ -0,0 +1,209 @@ +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * RTL (Right-to-Left) adjustments for Hebrew, Arabic, Farsi, and other RTL languages + * This file is automatically loaded by Nextcloud when ?forceLanguage=he (or other RTL language) + * is present in the URL and a RTL language is detected via IL10N->isRTL() + */ + +/* ============================================ + Global RTL Direction + ============================================ */ + +/* Set global direction to RTL for main containers */ +body, +html, +#content, +#content-vue { + direction: rtl; + text-align: right; +} + +/* ============================================ + Form Header and Title + ============================================ */ + +/* Form title and description */ +.form-title { + text-align: right; + direction: rtl; +} + +.form-desc, +.info-message { + text-align: right; + direction: rtl; +} + +/* ============================================ + Form Elements and Inputs + ============================================ */ + +/* Text inputs and textareas */ +input[type="text"], +input[type="email"], +input[type="number"], +input[type="tel"], +input[type="url"], +textarea { + direction: rtl; + text-align: right; +} + +/* Question containers */ +.question { + direction: rtl; +} + +.question__header, +.question__text { + text-align: right; + direction: rtl; +} + +.question__content { + direction: rtl; +} + +/* ============================================ + Buttons and Actions + ============================================ */ + +/* Form action buttons */ +.form-buttons { + justify-content: flex-end !important; + flex-direction: row-reverse; +} + +.form-buttons .submit-button { + margin-right: 35px !important; +} + +.submit-button { + margin-left: 0 !important; + margin-right: 5px !important; +} + +.submit-button:first-child { + margin-right: 0 !important; +} + +/* Button icons - flip to the left side */ +.button-vue { + flex-direction: row-reverse; +} + +/* ============================================ + Lists and Options + ============================================ */ + +/* Checkbox and radio button options */ +.question__item { + direction: rtl; + text-align: right; +} + +/* Answer options text */ +.answer-text, +.option-text { + text-align: right; + direction: rtl; +} + +/* ============================================ + Empty Content / Messages + ============================================ */ + +/* Empty content messages */ +.empty-content, +.forms-emptycontent { + direction: rtl; +} + +.empty-content__name, +.empty-content__description { + text-align: right; +} + +.submission-message { + text-align: right !important; + direction: rtl; +} + +/* ============================================ + Date Picker + ============================================ */ + +/* Date picker inputs */ +.mx-datepicker { + direction: rtl; +} + +.mx-input { + text-align: right; + direction: rtl; +} + +/* ============================================ + Multiselect Dropdown + ============================================ */ + +/* Multiselect component */ +.multiselect { + direction: rtl; + text-align: right; +} + +.multiselect__tags { + text-align: right; +} + +.multiselect__option { + text-align: right; +} + +.multiselect__single { + text-align: right; +} + +/* ============================================ + Modal Dialogs + ============================================ */ + +/* Dialog windows */ +.modal-container, +.dialog { + direction: rtl; +} + +.modal-container__content, +.dialog__content { + text-align: right; +} + +/* ============================================ + Icons and Visual Elements + ============================================ */ + +/* Required asterisk positioning */ +.asterisk, +.required-asterisk { + margin-left: 0; + margin-right: 4px; +} + +/* ============================================ + Responsive and Mobile + ============================================ */ + +/* Ensure RTL on all screen sizes */ +@media (max-width: 1024px) { + body, + #content, + #content-vue { + direction: rtl; + } +} diff --git a/l10n/he.js b/l10n/he.js index ac1a15a7f..2477125b1 100644 --- a/l10n/he.js +++ b/l10n/he.js @@ -82,6 +82,11 @@ OC.L10N.register( "Abort" : "להפסיק", "Submit" : "שליחה", "Clear" : "פינוי", + "Clear form" : "ניקוי הטופס", + "Enter your answer" : "הזנת תשובתך", + "Add new file as answer" : "הוספת קובץ חדש כתשובה", + "Some answers are not valid" : "חלק מהתשובות אינן תקפות", + "There was an error submitting the form: {message}" : "אירעה שגיאה בשליחת הטופס: {message}", "There was an error submitting the form" : "עלתה שגיאה בעת הגשת הטופס", "Submit form" : "הגשת הטופס", "Thank you for completing the form!" : "תודה לך על שהשלמת את הטופס!", diff --git a/l10n/he.json b/l10n/he.json index a27bc89ce..df92a855f 100644 --- a/l10n/he.json +++ b/l10n/he.json @@ -80,6 +80,11 @@ "Abort" : "להפסיק", "Submit" : "שליחה", "Clear" : "פינוי", + "Clear form" : "ניקוי הטופס", + "Enter your answer" : "הזנת תשובתך", + "Add new file as answer" : "הוספת קובץ חדש כתשובה", + "Some answers are not valid" : "חלק מהתשובות אינן תקפות", + "There was an error submitting the form: {message}" : "אירעה שגיאה בשליחת הטופס: {message}", "There was an error submitting the form" : "עלתה שגיאה בעת הגשת הטופס", "Submit form" : "הגשת הטופס", "Thank you for completing the form!" : "תודה לך על שהשלמת את הטופס!", diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 43db2366a..283036c50 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -221,6 +221,17 @@ protected function provideEmptyContent(string $renderAs, ?Form $form = null): Te return $this->provideTemplate(self::TEMPLATE_MAIN, $form); } + /** + * Check if current language is RTL (Right-to-Left) + * + * @return bool + */ + private function isRTL(): bool { + $rtlLanguages = ['ar', 'he', 'fa', 'ur', 'ps', 'ku', 'sd', 'yi']; + $currentLanguage = $this->l10n->getLanguageCode(); + return in_array($currentLanguage, $rtlLanguages, true); + } + /** * Helper function to create a template response from a form * @param string $template @@ -229,9 +240,16 @@ protected function provideEmptyContent(string $renderAs, ?Form $form = null): Te */ protected function provideTemplate(string $template, ?Form $form = null, array $options = []): TemplateResponse { Util::addStyle($this->appName, 'forms-style'); + // If not logged in, use PublicTemplate if (!$this->userSession->isLoggedIn()) { Util::addStyle($this->appName, 'public'); + + // Load RTL styles for public pages if language is RTL + if ($this->isRTL()) { + Util::addStyle($this->appName, 'public-rtl'); + } + $response = new PublicTemplateResponse($this->appName, $template, array_merge([ 'id-app-content' => '#app-content-vue', 'id-app-navigation' => null,