From 30929ee0da5a9c64e65869d6157bd705db3b80f0 Mon Sep 17 00:00:00 2001 From: Finix Date: Sun, 22 Feb 2026 19:25:01 +0000 Subject: [PATCH] Arabic and RTL support added --- GUI/index.html | 3 +- GUI/js/i18n.js | 32 ++++- GUI/locales/ar-AR.json | 257 +++++++++++++++++++++++++++++++++++++++++ GUI/style-RTL.css | 209 +++++++++++++++++++++++++++++++++ 4 files changed, 498 insertions(+), 3 deletions(-) create mode 100644 GUI/locales/ar-AR.json create mode 100644 GUI/style-RTL.css diff --git a/GUI/index.html b/GUI/index.html index 5f9c09a..7ed1028 100644 --- a/GUI/index.html +++ b/GUI/index.html @@ -8,9 +8,10 @@ + diff --git a/GUI/js/i18n.js b/GUI/js/i18n.js index 2c4158c..ed85a6a 100644 --- a/GUI/js/i18n.js +++ b/GUI/js/i18n.js @@ -12,9 +12,18 @@ const i18n = (() => { { code: 'ru-RU', name: 'Russian (Russia)' }, { code: 'sv-SE', name: 'Swedish (Sweden)' }, { code: 'tr-TR', name: 'Turkish (Turkey)' }, - { code: 'id-ID', name: 'Indonesian (Indonesia)' } + { code: 'id-ID', name: 'Indonesian (Indonesia)' }, + { code: 'ar-AR', name: 'Arabic (Saudi Arabia)' } ]; + // RTL languages + const rtlLanguages = ['ar-AR']; + + // Check if current language is RTL + function isRTL() { + return rtlLanguages.includes(currentLang); + } + // Load single language file async function loadLanguage(lang) { if (translations[lang]) return true; @@ -73,6 +82,24 @@ const i18n = (() => { const key = el.getAttribute('data-i18n-title'); el.title = t(key); }); + // Update RTL layout + updateRTL(); + } + + // Update RTL layout + function updateRTL() { + const html = document.documentElement; + const body = document.body; + + if (isRTL()) { + html.setAttribute('dir', 'rtl'); + html.setAttribute('lang', currentLang); + body.classList.add('rtl'); + } else { + html.removeAttribute('dir'); + html.setAttribute('lang', currentLang); + body.classList.remove('rtl'); + } } // Initialize - load saved language only @@ -88,7 +115,8 @@ const i18n = (() => { t, setLanguage, getAvailableLanguages: () => availableLanguages, - getCurrentLanguage: () => currentLang + getCurrentLanguage: () => currentLang, + isRTL }; })(); diff --git a/GUI/locales/ar-AR.json b/GUI/locales/ar-AR.json new file mode 100644 index 0000000..3ae57bb --- /dev/null +++ b/GUI/locales/ar-AR.json @@ -0,0 +1,257 @@ +{ + "nav": { + "play": "لعب", + "mods": "المودات", + "news": "الأخبار", + "chat": "دردشة اللاعبين", + "settings": "الإعدادات" + }, + "header": { + "playersLabel": "اللاعبون:", + "manageProfiles": "إدارة الملفات الشخصية", + "defaultProfile": "الافتراضي" + }, + "install": { + "title": "مشغل اللعب المجاني", + "playerName": "اسم اللاعب", + "playerNamePlaceholder": "أدخل اسمك", + "gameBranch": "إصدار اللعبة", + "releaseVersion": "إصدار نهائي (مستقر)", + "preReleaseVersion": "إصدار تجريبي (تجريبي)", + "customInstallation": "تثبيت مخصص", + "installationFolder": "مجلد التثبيت", + "pathPlaceholder": "الموقع الافتراضي", + "browse": "تصفح", + "installButton": "تثبيت HYTALE", + "installing": "جاري التثبيت..." + }, + "play": { + "ready": "جاهز للعب", + "subtitle": "شغل Hytale وابدأ المغامرة", + "playButton": "لعب HYTALE", + "latestNews": "آخر الأخبار", + "viewAll": "عرض الكل", + "checking": "جاري التحقق...", + "play": "بدء" + }, + "mods": { + "searchPlaceholder": "البحث عن مودات...", + "myMods": "موداتي", + "previous": "السابق", + "next": "التالي", + "page": "صفحة", + "of": "من", + "modalTitle": "موداتي", + "noModsFound": "لم يتم العثور على مودات", + "noModsFoundDesc": "حاول تعديل معايير البحث", + "noModsInstalled": "لا توجد مودات مثبتة", + "noModsInstalledDesc": "أضف مودات من CurseForge أو استورد ملفات محلية", + "view": "عرض", + "install": "تثبيت", + "installed": "مثبت", + "enable": "تفعيل", + "disable": "تعطيل", + "active": "نشط", + "disabled": "معطل", + "delete": "حذف المود", + "noDescription": "لا يوجد وصف متاح", + "confirmDelete": "هل أنت متأكد أنك تريد حذف \"{name}\"؟", + "confirmDeleteDesc": "لا يمكن التراجع عن هذا الإجراء.", + "confirmDeletion": "تأكيد الحذف", + "apiKeyRequired": "مطلوب مفتاح API", + "apiKeyRequiredDesc": "مطلوب مفتاح CurseForge API لتصفح المودات" + }, + "news": { + "title": "كل الأخبار", + "readMore": "اقرأ المزيد" + }, + "chat": { + "title": "دردشة اللاعبين", + "pickColor": "اللون", + "inputPlaceholder": "اكتب رسالتك...", + "send": "إرسال", + "online": "متصل", + "charCounter": "{current}/{max}", + "secureChat": "دردشة آمنة - الروابط محجوبة", + "joinChat": "انضمام للدردشة", + "chooseUsername": "اختر اسم مستخدم للانضمام إلى دردشة اللاعبين", + "username": "اسم المستخدم", + "usernamePlaceholder": "أدخل اسم المستخدم...", + "usernameHint": "3-20 حرفاً، حروف، أرقام، و - و _ فقط", + "joinButton": "انضمام", + "colorModal": { + "title": "تخصيص لون اسم المستخدم", + "chooseSolid": "اختر لوناً ثابتاً:", + "customColor": "لون مخصص:", + "preview": "معاينة:", + "previewUsername": "اسم المستخدم", + "apply": "تطبيق اللون" + } + }, + "settings": { + "title": "الإعدادات", + "java": "بيئة تشغيل جافا", + "useCustomJava": "استخدام مسار جافا مخصص", + "javaDescription": "تجاوز بيئة جافا المرفقة واستخدام تثبيت خاص بك", + "javaPath": "مسار ملف جافا التنفيذي", + "javaPathPlaceholder": "اختر مسار جافا...", + "javaBrowse": "تصفح", + "javaHint": "اختر مجلد تثبيت جافا (يدعم ويندوز، ماك، ولينكس)", + "discord": "تكامل ديسكورد", + "enableRPC": "تفعيل نشاط ديسكورد (Rich Presence)", + "discordDescription": "إظهار نشاط المشغل الخاص بك على ديسكورد", + "game": "خيارات اللعبة", + "playerName": "اسم اللاعب", + "playerNamePlaceholder": "أدخل اسم اللاعب", + "playerNameHint": "سيتم استخدام هذا الاسم داخل اللعبة (1-16 حرفاً)", + "openGameLocation": "فتح موقع اللعبة", + "openGameLocationDesc": "فتح مجلد تثبيت اللعبة", + "account": "إدارة UUID اللاعب", + "currentUUID": "الـ UUID الحالي", + "uuidPlaceholder": "جاري تحميل UUID...", + "copyUUID": "نسخ UUID", + "regenerateUUID": "إعادة إنشاء UUID", + "uuidHint": "معرف اللاعب الفريد الخاص بك لهذا الاسم", + "manageUUIDs": "إدارة جميع الـ UUIDs", + "manageUUIDsDesc": "عرض وإدارة جميع معرفات اللاعبين", + "language": "اللغة", + "selectLanguage": "اختر اللغة", + "repairGame": "إصلاح اللعبة", + "reinstallGame": "إعادة تثبيت ملفات اللعبة (يحفظ البيانات)", + "gpuPreference": "تفضيل معالج الرسوميات (GPU)", + "gpuHint": "ميزة للمحمول فقط؛ اضبطها على Integrated إذا كنت تستخدم كمبيوتر مكتبي", + "gpuAuto": "تلقائي", + "gpuIntegrated": "مدمج", + "gpuDedicated": "منفصل", + "logs": "سجلات النظام", + "logsCopy": "نسخ", + "logsRefresh": "تحديث", + "logsFolder": "فتح المجلد", + "logsLoading": "جاري تحميل السجلات...", + "closeLauncher": "سلوك المشغل", + "closeOnStart": "إغلاق المشغل عند بدء اللعبة", + "closeOnStartDescription": "إغلاق المشغل تلقائياً بعد تشغيل Hytale", + "hwAccel": "تسريع الأجهزة (Hardware Acceleration)", + "hwAccelDescription": "تفعيل تسريع الأجهزة للمشغل", + "gameBranch": "فرع اللعبة", + "branchRelease": "إصدار نهائي", + "branchPreRelease": "إصدار تجريبي", + "branchHint": "التبديل بين الإصدار المستقر والإصدار التجريبي", + "branchWarning": "تغيير الفرع سيؤدي إلى تحميل وتثبيت نسخة مختلفة من اللعبة", + "branchSwitching": "جاري التبديل إلى {branch}...", + "branchSwitched": "تم التبديل إلى {branch} بنجاح!", + "installRequired": "التثبيت مطلوب", + "branchInstallConfirm": "سيتم تثبيت اللعبة لفرع {branch}. هل تريد الاستمرار؟" + }, + "uuid": { + "modalTitle": "إدارة UUID", + "currentUserUUID": "UUID المستخدم الحالي", + "allPlayerUUIDs": "جميع معرفات UUID للاعبين", + "generateNew": "إنشاء UUID جديد", + "loadingUUIDs": "جاري تحميل الـ UUIDs...", + "setCustomUUID": "تعيين UUID مخصص", + "customPlaceholder": "أدخل UUID مخصص (الصيغة: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", + "setUUID": "تعيين UUID", + "warning": "تحذير: تعيين UUID مخصص سيغير هوية اللاعب الحالية", + "copyTooltip": "نسخ UUID", + "regenerateTooltip": "إنشاء UUID جديد" + }, + "profiles": { + "modalTitle": "إدارة الملفات الشخصية", + "newProfilePlaceholder": "اسم الملف الشخصي الجديد", + "createProfile": "إنشاء ملف شخصي" + }, + "discord": { + "notificationText": "انضم إلى مجتمعنا على ديسكورد!", + "joinButton": "انضم إلى ديسكورد" + }, + "common": { + "confirm": "تأكيد", + "cancel": "إلغاء", + "save": "حفظ", + "close": "إغلاق", + "delete": "حذف", + "edit": "تعديل", + "loading": "جاري التحميل...", + "apply": "تطبيق", + "install": "تثبيت" + }, + "notifications": { + "gameDataNotFound": "خطأ: لم يتم العثور على بيانات اللعبة", + "gameUpdatedSuccess": "تم تحديث اللعبة بنجاح! 🎉", + "updateFailed": "فشل التحديث: {error}", + "updateError": "خطأ في التحديث: {error}", + "discordEnabled": "تم تفعيل نشاط ديسكورد", + "discordDisabled": "تم تعطيل نشاط ديسكورد", + "discordSaveFailed": "فشل حفظ إعدادات ديسكورد", + "playerNameRequired": "يرجى إدخال اسم لاعب صالح", + "playerNameSaved": "تم حفظ اسم اللاعب بنجاح", + "playerNameSaveFailed": "فشل حفظ اسم اللاعب", + "uuidCopied": "تم نسخ الـ UUID إلى الحافظة!", + "uuidCopyFailed": "فشل نسخ الـ UUID", + "uuidRegenNotAvailable": "إعادة إنشاء UUID غير متاحة", + "uuidRegenFailed": "فشل إعادة إنشاء الـ UUID", + "uuidGenerated": "تم إنشاء UUID جديد بنجاح!", + "uuidGeneratedShort": "تم إنشاء UUID جديد!", + "uuidGenerateFailed": "فشل إنشاء UUID جديد", + "uuidRequired": "يرجى إدخال UUID", + "uuidInvalidFormat": "صيغة UUID غير صالحة", + "uuidSetFailed": "فشل تعيين الـ UUID المخصص", + "uuidSetSuccess": "تم تعيين الـ UUID المخصص بنجاح!", + "uuidDeleteFailed": "فشل حذف الـ UUID", + "uuidDeleteSuccess": "تم حذف الـ UUID بنجاح!", + "modsDownloading": "جاري تحميل {name}...", + "modsTogglingMod": "جاري تبديل حالة المود...", + "modsDeletingMod": "جاري حذف المود...", + "modsLoadingMods": "جاري تحميل المودات من CurseForge...", + "modsInstalledSuccess": "تم تثبيت {name} بنجاح! 🎉", + "modsDeletedSuccess": "تم حذف {name} بنجاح", + "modsDownloadFailed": "فشل تحميل المود: {error}", + "modsToggleFailed": "فشل تبديل المود: {error}", + "modsDeleteFailed": "فشل حذف المود: {error}", + "modsModNotFound": "لم يتم العثور على معلومات المود", + "hwAccelSaved": "تم حفظ إعداد تسريع الأجهزة", + "hwAccelSaveFailed": "فشل حفظ إعداد تسريع الأجهزة", + "noUsername": "لم يتم تهيئة اسم مستخدم. يرجى حفظ اسم المستخدم أولاً.", + "switchUsernameSuccess": "تم التبديل إلى المستخدم \"{username}\" بنجاح!", + "switchUsernameFailed": "فشل تبديل اسم المستخدم", + "playerNameTooLong": "يجب أن يكون اسم اللاعب 16 حرفاً أو أقل" + }, + "confirm": { + "defaultTitle": "تأكيد الإجراء", + "regenerateUuidTitle": "إنشاء UUID جديد", + "regenerateUuidMessage": "هل أنت متأكد أنك تريد إنشاء UUID جديد؟ سيؤدي ذلك إلى تغيير هوية اللاعب الخاصة بك.", + "regenerateUuidButton": "إنشاء", + "setCustomUuidTitle": "تعيين UUID مخصص", + "setCustomUuidMessage": "هل أنت متأكد أنك تريد تعيين هذا الـ UUID المخصص؟ سيؤدي ذلك إلى تغيير هوية اللاعب الخاصة بك.", + "setCustomUuidButton": "تعيين UUID", + "deleteUuidTitle": "حذف UUID", + "deleteUuidMessage": "هل أنت متأكد أنك تريد حذف الـ UUID الخاص بـ \"{username}\"؟ لا يمكن التراجع عن هذا الإجراء.", + "deleteUuidButton": "حذف", + "uninstallGameTitle": "إلغاء تثبيت اللعبة", + "uninstallGameMessage": "هل أنت متأكد أنك تريد إلغاء تثبيت Hytale؟ سيتم حذف جميع ملفات اللعبة.", + "uninstallGameButton": "إلغاء التثبيت", + "switchUsernameTitle": "تبديل الهوية", + "switchUsernameMessage": "التبديل إلى اسم المستخدم \"{username}\"؟ سيؤدي هذا إلى تغيير هوية اللاعب الحالية.", + "switchUsernameButton": "تبديل" + }, + "progress": { + "initializing": "جاري التهيئة...", + "downloading": "جاري التحميل...", + "installing": "جاري التثبيت...", + "extracting": "جاري الاستخراج...", + "verifying": "جاري التحقق...", + "switchingProfile": "جاري تبديل الملف الشخصي...", + "profileSwitched": "تم تبديل الملف الشخصي!", + "startingGame": "جاري بدء اللعبة...", + "launching": "جاري التشغيل...", + "uninstallingGame": "جاري إلغاء تثبيت اللعبة...", + "gameUninstalled": "تم إلغاء تثبيت اللعبة بنجاح!", + "uninstallFailed": "فشل إلغاء التثبيت: {error}", + "startingUpdate": "جاري بدء تحديث اللعبة الإجباري...", + "installationComplete": "تم اكتمال التثبيت بنجاح!", + "installationFailed": "فشل التثبيت: {error}", + "installingGameFiles": "جاري تثبيت ملفات اللعبة...", + "installComplete": "اكتمل التثبيت!" + } +} \ No newline at end of file diff --git a/GUI/style-RTL.css b/GUI/style-RTL.css new file mode 100644 index 0000000..48bd820 --- /dev/null +++ b/GUI/style-RTL.css @@ -0,0 +1,209 @@ +body.rtl { + direction: rtl; + font-family: 'Noto Sans Arabic', 'Space Grotesk', sans-serif; +} + +body.rtl .sidebar { + right: 0; + left: auto; + border-right: none; + border-left: 1px solid rgba(255, 255, 255, 0.1); +} + +body.rtl .nav-item.active::before { + right: -8px; + left: auto; + border-radius: 4px 0 0 4px; +} + +body.rtl .nav-tooltip { + right: 100%; + left: auto; + margin-right: 0.5rem; + margin-left: 0; +} + +body.rtl .nav-item:hover .nav-tooltip { + transform: translateX(-8px); +} + + +body.rtl .main-content { + margin-right: 80px; + margin-left: 0; +} + +/* Header Layout*/ + +body.rtl .players-counter { + order: 2; + margin-left: 1.5rem; + margin-right: 0; +} + +body.rtl .profile-selector { + order: -1; +} + +body.rtl .window-controls { + order: 3; + flex-direction: row; +} + +body.rtl .profile-dropdown { + right: auto; + left: 0; +} + + + +body.rtl .form-group { + text-align: right; +} + +body.rtl .radio-label, +body.rtl .checkbox-group { + flex-direction: row-reverse; +} + +body.rtl .form-input { + border-radius: 0 8px 8px 0; +} + + +body.rtl .mods-pagination { + flex-direction: row-reverse; +} + +body.rtl .pagination-btn:first-child i { + transform: rotate(180deg); +} + +body.rtl .pagination-btn:last-child i { + transform: rotate(180deg); +} + +/* UUID Display */ + +body.rtl .uuid-display-container { + flex-direction: row-reverse; +} + +body.rtl .uuid-btn { + border-radius: 0 8px 8px 0; +} + +body.rtl .uuid-input { + border-radius: 8px 0 0 8px; +} + + +body.rtl .segmented-control { + flex-direction: row-reverse; +} + +/* Mod Grid Layout */ +body.rtl .mods-search { + text-align: right; +} + +body.rtl .mods-search-container { + flex-direction: row-reverse; +} + +body.rtl .mods-actions { + order: -1; +} + +body.rtl .mod-card { + direction: rtl; +} + +body.rtl .installed-mod-card { + direction: rtl; +} + +body.rtl .installed-mod-card .mod-info { + text-align: right; +} + +body.rtl .mods-header { + flex-direction: row-reverse; +} + +body.rtl .news-section news-header{ + flex-direction: row-reverse; +} + +/* Settings Layout */ + +body.rtl .settings-option { + text-align: right; +} + +body.rtl .settings-input-group { + text-align: right; +} + +body.rtl .settings-input { + border-radius: 0 8px 8px 0; +} + +body.rtl .settings-section-title i { + margin-right: 0; + margin-left: 0.5rem; +} + +body.rtl .settings-hint i { + margin-right: 0; + margin-left: 0.5rem; +} + +body.rtl .custom-options, +body.rtl .custom-java-options { + text-align: right; +} + +body.rtl .checkbox-content { + margin-right: 2rem; + margin-left: 0; +} + +body.rtl .btn-content { + text-align: right; +} + +/* Icons & Transformations */ + +body.rtl .news-title i { + padding-left: 0.5rem; +} +body.rtl .uuid-modal-title i { + padding-left: 0.5rem; +} +body.rtl .mods-modal-title i { + padding-left: 0.5rem; +} + +body.rtl .view-all-btn i, +body.rtl .sidebar-nav div i, +body.rtl .logs-header i, +body.rtl .home-play-button i { + transform: scaleX(-1); +} + +body.rtl .play-title i { + transform: scaleX(-1); + padding-right: 0.5rem; +} + + +body.rtl .logs-terminal { + direction: ltr; + text-align: left; +} + +body.rtl .version-display-bottom { + right: auto; + left: 1rem; +} \ No newline at end of file