diff --git a/.gitignore b/.gitignore index 36e620fc..f9f32977 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ aria2/ # Sentry Config File .env.sentry-build-plugin + +*storybook.log diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json index 35a64d2d..15a24a7d 100644 --- a/src/locales/ar/translation.json +++ b/src/locales/ar/translation.json @@ -1,417 +1,439 @@ { - "language_name": "اَلْعَرَبِيَّةُ", + "language_name": "العربية", "app": { "successfully_signed_in": "تم تسجيل الدخول بنجاح" }, "home": { - "featured": "مُتَمَيِّز", - "surprise_me": "فَاجِئْنِي", - "no_results": "لَمْ يُعْثَرْ عَلَى نَتائِج", - "start_typing": "اِبْدَأْ بِالْكِتَابَةِ لِلْبَحْثِ...", - "hot": "اَلْأَكْثَرُ شُيُوعًا الْآن", - "weekly": "📅 أَفْضَلُ أَلْعَابِ الْأُسْبُوعِ", - "achievements": "🏆 أَلْعَابٌ لِلتَّغَلُّبِ عَلَيْهَا" + "featured": "مميز", + "surprise_me": "مفاجئني", + "no_results": "لم يتم العثور على نتائج", + "start_typing": "ابدأ الكتابة للبحث...", + "hot": "الأكثر شيوعًا الآن", + "weekly": "📅 أفضل ألعاب الأسبوع", + "achievements": "🏆 ألعاب للتغلب عليها" }, "sidebar": { - "catalogue": "الْفِهْرِسُ", - "downloads": "التَّنْزِيلَاتُ", - "settings": "الإعْدَادَاتُ", - "my_library": "مَكْتَبَتِي", - "downloading_metadata": "{{title}} (جَارٍ تَنْزِيلُ الْبَيَانَاتِ الْوَصْفِيَّةِ...)", - "paused": "{{title}} (مُوْقَفٌ)", - "downloading": "{{title}} ({{percentage}} - جَارٍ التَّنْزِيلُ...)", - "filter": "تَصْفِيَةُ الْمَكْتَبَةِ", - "home": "الرَّئِيسِيَّةُ", - "queued": "{{title}} (فِي الْانْتِظَارِ)", - "game_has_no_executable": "اللُّعْبَةُ لَيْسَ لَدَيْهَا مِلَفٌّ تَنْفِيذِيٌّ مُحَدَّدٌ", - "sign_in": "تَسْجِيلُ الدُّخُولِ", - "friends": "الْأَصْدِقَاءُ", - "need_help": "هَلْ تَحْتَاجُ إِلَى مُسَاعَدَةٍ؟" + "catalogue": "الكـتالوج", + "downloads": "التنزيلات", + "settings": "الإعدادات", + "my_library": "مكتبتي", + "downloading_metadata": "{{title}} (جارٍ تنزيل البيانات الوصفية...)", + "paused": "{{title}} (معلّق)", + "downloading": "{{title}} ({{percentage}} - جاري التنزيل...)", + "filter": "تصفية المكتبة", + "home": "الرئيسية", + "queued": "{{title}} (في قائمة الانتظار)", + "game_has_no_executable": "اللعبة لا تحتوي على ملف تشغيل", + "sign_in": "تسجيل الدخول", + "friends": "الأصدقاء", + "need_help": "تحتاج مساعدة؟" }, "header": { - "search": "بَحْثُ الْأَلْعَابِ", - "home": "الرَّئِيسِيَّةُ", - "catalogue": "الْفِهْرِسُ", - "downloads": "التَّنْزِيلَاتُ", - "search_results": "نَتائِجُ الْبَحْثِ", - "settings": "الإعْدَادَاتُ", - "version_available_install": "الْإِصْدَارُ {{version}} مَتَوَفِّرٌ. انْقُرْ هُنَا لِإِعَادَةِ التَّشْغِيلِ وَالتَّثْبِيتِ.", - "version_available_download": "الْإِصْدَارُ {{version}} مَتَوَفِّرٌ. انْقُرْ هُنَا لِلتَّنْزِيلِ." + "search": "ابحث عن الألعاب", + "home": "الرئيسية", + "catalogue": "الكـتالوج", + "downloads": "التنزيلات", + "search_results": "نتائج البحث", + "settings": "الإعدادات", + "version_available_install": "الإصدار {{version}} متوفر. انقر هنا لإعادة التشغيل والتثبيت.", + "version_available_download": "الإصدار {{version}} متوفر. انقر هنا للتنزيل." }, "bottom_panel": { - "no_downloads_in_progress": "لَا تَوْجَدُ تَنْزِيلَاتٌ جَارِيَةٌ", - "downloading_metadata": "جَارٍ تَنْزِيلُ الْبَيَانَاتِ الْوَصْفِيَّةِ لِـ {{title}}...", - "downloading": "جَارٍ تَنْزِيلُ {{title}}... ({{percentage}} مَكْتُومٌ) - الِاكْتِمَالُ {{eta}} - {{speed}}", - "calculating_eta": "جَارٍ تَنْزِيلُ {{title}}... ({{percentage}} مَكْتُومٌ) - جَارٍ حِسَابُ الْوَقْتِ الْمُتَبَقِّي...", - "checking_files": "جَارٍ التَّحَقُّقُ مِنْ مَلَفَّاتِ {{title}}... ({{percentage}} مَكْتُومٌ)" + "no_downloads_in_progress": "لا توجد تنزيلات قيد التقدم", + "downloading_metadata": "جارٍ تنزيل البيانات الوصفية لـ {{title}}...", + "downloading": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - الوقت المتبقي {{eta}} - السرعة {{speed}}", + "calculating_eta": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - جاري حساب الوقت المتبقي...", + "checking_files": "جارٍ فحص ملفات {{title}}... ({{percentage}} اكتمال)" }, "catalogue": { - "search": "تَصْفِيَةٌ...", - "developers": "الْمُطَوِّرُونَ", - "genres": "الْأَنْوَاعُ", - "tags": "الْعَلَامَاتُ", - "publishers": "النَّاشِرُونَ", - "download_sources": "مَصَادِرُ التَّنْزِيلِ", - "result_count": "{{resultCount}} نَتائِجُ", - "filter_count": "{{filterCount}} مَتَوَفِّرٌ", - "clear_filters": "مَسْحُ {{filterCount}} الْمُخْتَارَةِ" + "search": "تصفية...", + "developers": "المطورون", + "genres": "الأنواع", + "tags": "العلامات", + "publishers": "الناشرون", + "download_sources": "مصادر التنزيل", + "result_count": "{{resultCount}} نتيجة", + "filter_count": "{{filterCount}} متاح", + "clear_filters": "مسح {{filterCount}} المحددة" }, "game_details": { - "open_download_options": "فَتْحُ خِيَارَاتِ التَّنْزِيلِ", - "download_options_zero": "لَا تَوْجَدُ خِيَارَاتُ تَنْزِيلٍ", - "download_options_one": "{{count}} خِيَارُ تَنْزِيلٍ", - "download_options_other": "{{count}} خِيَارَاتُ تَنْزِيلٍ", - "updated_at": "تَمَّ التَّحْدِيثُ فِي {{updated_at}}", - "install": "تَثْبِيتٌ", - "resume": "اسْتِئْنَافٌ", - "pause": "إِيقَافٌ", - "cancel": "إِلْغَاءٌ", - "remove": "إِزَالَةٌ", - "space_left_on_disk": "{{space}} مُتَبَقٍّ عَلَى الْقُرْصِ", - "eta": "الِاكْتِمَالُ {{eta}}", - "calculating_eta": "جَارٍ حِسَابُ الْوَقْتِ الْمُتَبَقِّي...", - "downloading_metadata": "جَارٍ تَنْزِيلُ الْبَيَانَاتِ الْوَصْفِيَّةِ...", - "filter": "تَصْفِيَةُ الْإِصْدَارَاتِ الْمُعَادِ تَغْلِيفُهَا", - "requirements": "مُتَطَلَّبَاتُ النِّظَامِ", - "minimum": "الْأَدْنَى", - "recommended": "الْمُوَصَّى بِهِ", - "paused": "مُوْقَفٌ", - "release_date": "تَمَّ الْإِصْدَارُ فِي {{date}}", - "publisher": "نُشِرَ بِوَاسِطَةِ {{publisher}}", - "hours": "سَاعَاتٌ", - "minutes": "دَقَائِقُ", - "amount_hours": "{{amount}} سَاعَاتٌ", - "amount_minutes": "{{amount}} دَقَائِقُ", - "accuracy": "دِقَّةٌ {{accuracy}}%", - "add_to_library": "إِضَافَةٌ إِلَى الْمَكْتَبَةِ", - "remove_from_library": "إِزَالَةٌ مِنَ الْمَكْتَبَةِ", - "no_downloads": "لَا تَوْجَدُ تَنْزِيلَاتٌ مَتَوَفِّرَةٌ", - "play_time": "لُعِبَ لِمُدَّةِ {{amount}}", - "last_time_played": "آخِرُ مَرَّةٍ لُعِبَتْ {{period}}", - "not_played_yet": "لَمْ تَلْعَبْ {{title}} بَعْدُ", - "next_suggestion": "الِاقْتِرَاحُ التَّالِي", - "play": "لَعِبٌ", - "deleting": "جَارٍ حَذْفُ الْمُثَبِّتِ...", - "close": "إِغْلَاقٌ", - "playing_now": "جَارِي اللَّعِبُ الْآن", - "change": "تَغْيِيرٌ", - "repacks_modal_description": "اخْتَرِ الْإِصْدَارَ الْمُعَادَ تَغْلِيفُهُ الَّذِي تُرِيدُ تَنْزِيلَهُ", - "select_folder_hint": "لِتَغْيِيرِ الْمَجَلَّدِ الافْتِرَاضِيِّ، اذْهَبْ إِلَى <0>الإعْدَادَاتِ", - "download_now": "تَنْزِيلٌ الْآن", - "no_shop_details": "لَمْ يَتَمَكَّنْ مِنْ اسْتِرْدَادِ تَفَاصِيلِ الْمَتْجَرِ.", - "download_options": "خِيَارَاتُ التَّنْزِيلِ", - "download_path": "مَسَارُ التَّنْزِيلِ", - "previous_screenshot": "لَقْطَةُ الشَّاشَةِ السَّابِقَةُ", - "next_screenshot": "لَقْطَةُ الشَّاشَةِ التَّالِيَةُ", - "screenshot": "لَقْطَةُ الشَّاشَةِ {{number}}", - "open_screenshot": "فَتْحُ لَقْطَةِ الشَّاشَةِ {{number}}", - "download_settings": "إعْدَادَاتُ التَّنْزِيلِ", - "downloader": "الْمُنَزِّلُ", - "select_executable": "تَحْدِيدٌ", - "no_executable_selected": "لَمْ يُحَدَّدْ مِلَفٌّ تَنْفِيذِيٌّ", - "open_folder": "فَتْحُ الْمَجَلَّدِ", - "open_download_location": "مُشَاهَدَةُ الْمَلَفَّاتِ الْمُنَزَّلَةِ", - "create_shortcut": "إِنْشَاءُ طَرِيقٍ مُخْتَصَرٍ عَلَى سَطْحِ الْمَكْتَبِ", - "clear": "مَسْحٌ", - "remove_files": "إِزَالَةُ الْمَلَفَّاتِ", - "remove_from_library_title": "هَلْ أَنْتَ مُتَأَكِّدٌ؟", - "remove_from_library_description": "سَيُؤَدِّي هَذَا إِلَى إِزَالَةِ {{game}} مِنْ مَكْتَبَتِكَ", - "options": "خِيَارَاتٌ", - "executable_section_title": "الْمِلَفُّ التَّنْفِيذِيُّ", - "executable_section_description": "مَسَارُ الْمِلَفِّ الَّذِي سَيَتِمُّ تَنْفِيذُهُ عِنْدَ النَّقْرِ عَلَى \"لَعِبٌ\"", - "downloads_secion_title": "التَّنْزِيلَاتُ", - "downloads_section_description": "تَحَقَّقْ مِنَ التَّحْدِيثَاتِ أَوِ الْإِصْدَارَاتِ الْأُخْرَى لِهَذِهِ اللُّعْبَةِ", - "danger_zone_section_title": "مِنْطَقَةُ الْخَطَرِ", - "danger_zone_section_description": "إِزَالَةُ هَذِهِ اللُّعْبَةِ مِنْ مَكْتَبَتِكَ أَوِ الْمَلَفَّاتِ الْمُنَزَّلَةِ بِوَاسِطَةِ Hydra", - "download_in_progress": "جَارٍ التَّنْزِيلُ", - "download_paused": "التَّنْزِيلُ مُوْقَفٌ", - "last_downloaded_option": "خِيَارُ التَّنْزِيلِ الْأَخِيرُ", - "create_shortcut_success": "تَمَّ إِنْشَاءُ الطَّرِيقِ الْمُخْتَصَرِ بِنَجَاحٍ", - "create_shortcut_error": "خَطَأٌ فِي إِنْشَاءِ الطَّرِيقِ الْمُخْتَصَرِ", - "nsfw_content_title": "هَذِهِ اللُّعْبَةُ تَحْتَوِي عَلَى مُحْتَوًى غَيْرِ لَائِقٍ", - "nsfw_content_description": "{{title}} تَحْتَوِي عَلَى مُحْتَوًى قَدْ لَا يَكُونُ مُنَاسِبًا لِجَمِيعِ الْأَعْمَارِ. هَلْ أَنْتَ مُتَأَكِّدٌ مِنْ أَنَّكَ تُرِيدُ الْمُتَابَعَةَ؟", - "allow_nsfw_content": "الْمُتَابَعَةُ", - "refuse_nsfw_content": "الرُّجُوعُ", - "stats": "الإحْصَائِيَّاتُ", - "download_count": "التَّنْزِيلَاتُ", - "player_count": "اللَّاعِبُونَ النَّشِطُونَ", - "download_error": "هَذَا خِيَارُ التَّنْزِيلِ غَيْرُ مَتَوَفِّرٍ", - "download": "تَنْزِيلٌ", - "executable_path_in_use": "الْمِلَفُّ التَّنْفِيذِيُّ مُسْتَخْدَمٌ بِوَاسِطَةِ \"{{game}}\"", - "warning": "تَنْبِيهٌ:", - "hydra_needs_to_remain_open": "لِهَذَا التَّنْزِيلِ، يَجِبُ أَنْ يَبْقَى Hydra مَفْتُوحًا حَتَّى يَتِمَّ الِاكْتِمَالُ. إِذَا أُغْلِقَ Hydra قَبْلَ الِاكْتِمَالِ، سَتَفْقِدُ تَقَدُّمَكَ.", - "achievements": "الإِنْجَازَاتُ", - "achievements_count": "الإِنْجَازَاتُ {{unlockedCount}}/{{achievementsCount}}", - "cloud_save": "حِفْظٌ سَحَابِيٌّ", - "cloud_save_description": "احْفَظْ تَقَدُّمَكَ فِي السَّحَابَةِ وَاسْتَمِرَّ فِي اللَّعِبِ عَلَى أَيِّ جِهَازٍ", - "backups": "الْنُسَخُ الِاحْتِيَاطِيَّةُ", - "install_backup": "تَثْبِيتٌ", - "delete_backup": "حَذْفٌ", - "create_backup": "نُسْخَةٌ احْتِيَاطِيَّةٌ جَدِيدَةٌ", - "last_backup_date": "آخِرُ نُسْخَةٍ احْتِيَاطِيَّةٍ فِي {{date}}", - "no_backup_preview": "لَمْ يُعْثَرْ عَلَى أَيِّ أَلْعَابٍ مَحْفُوظَةٍ لِهَذَا الْعُنْوَانِ", - "restoring_backup": "جَارٍ اسْتِعَادَةُ النُّسْخَةِ الِاحْتِيَاطِيَّةِ ({{progress}} مَكْتُومٌ)...", - "uploading_backup": "جَارٍ رَفْعُ النُّسْخَةِ الِاحْتِيَاطِيَّةِ...", - "no_backups": "لَمْ تَقُمْ بِإِنْشَاءِ أَيِّ نُسَخٍ احْتِيَاطِيَّةٍ لِهَذِهِ اللُّعْبَةِ بَعْدُ", - "backup_uploaded": "تَمَّ رَفْعُ النُّسْخَةِ الِاحْتِيَاطِيَّةِ", - "backup_deleted": "تَمَّ حَذْفُ النُّسْخَةِ الِاحْتِيَاطِيَّةِ", - "backup_restored": "تَمَّ اسْتِعَادَةُ النُّسْخَةِ الِاحْتِيَاطِيَّةِ", - "see_all_achievements": "عَرْضُ جَمِيعِ الإِنْجَازَاتِ", - "sign_in_to_see_achievements": "سَجِّلِ الدُّخُولَ لِعَرْضِ الإِنْجَازَاتِ", - "mapping_method_automatic": "آلِيٌّ", - "mapping_method_manual": "يَدَوِيٌّ", - "mapping_method_label": "طَرِيقَةُ التَّحْدِيدِ", - "files_automatically_mapped": "تَمَّ تَحْدِيدُ الْمَلَفَّاتِ تِلْقَائِيًّا", - "no_backups_created": "لَمْ تُنْشَأْ أَيُّ نُسَخٍ احْتِيَاطِيَّةٍ لِهَذِهِ اللُّعْبَةِ", - "manage_files": "إِدَارَةُ الْمَلَفَّاتِ", - "loading_save_preview": "جَارٍ الْبَحْثُ عَنْ أَلْعَابٍ مَحْفُوظَةٍ...", - "wine_prefix": "بَادِئَةُ Wine", - "wine_prefix_description": "بَادِئَةُ Wine الْمُسْتَخْدَمَةُ لِتَشْغِيلِ هَذِهِ اللُّعْبَةِ", - "launch_options": "خِيَارَاتُ الْإِطْلَاقِ", - "launch_options_description": "يُمْكِنُ لِلْمُسْتَخْدِمِينَ الْمُتَقَدِّمِينَ إِدْخَالُ تَعْدِيلَاتٍ عَلَى خِيَارَاتِ الْإِطْلَاقِ", - "launch_options_placeholder": "لَمْ يُحَدَّدْ أَيُّ مُعَامِلٍ", - "no_download_option_info": "لَا تَوْجَدُ مَعْلُومَاتٌ مَتَوَفِّرَةٌ", - "backup_deletion_failed": "فَشَلَ فِي حَذْفِ النُّسْخَةِ الِاحْتِيَاطِيَّةِ", - "max_number_of_artifacts_reached": "تَمَّ بَلُوغُ الْعَدَدِ الْأَقْصَى لِلنُّسَخِ الِاحْتِيَاطِيَّةِ لِهَذِهِ اللُّعْبَةِ", - "achievements_not_sync": "تَعَرَّفْ عَلَى كَيْفِيَّةِ مَزْجِ إِنْجَازَاتِكَ", - "manage_files_description": "إِدَارَةُ الْمَلَفَّاتِ الَّتِي سَيَتِمُّ نَسْخُهَا احْتِيَاطِيًّا وَاسْتِعَادَتُهَا", - "select_folder": "تَحْدِيدُ الْمَجَلَّدِ", - "backup_from": "نُسْخَةٌ احْتِيَاطِيَّةٌ مِنْ {{date}}", - "custom_backup_location_set": "تَمَّ تَحْدِيدُ مَوْقِعِ النُّسْخَةِ الِاحْتِيَاطِيَّةِ الْمُخَصَّصِ", - "no_directory_selected": "لَمْ يُحَدَّدْ أَيُّ دَلِيلٍ" + "open_download_options": "فتح خيارات التنزيل", + "download_options_zero": "لا توجد خيارات تنزيل", + "download_options_one": "خيار تنزيل واحد", + "download_options_other": "{{count}} خيارات تنزيل", + "updated_at": "تم التحديث في {{updated_at}}", + "install": "تثبيت", + "resume": "استئناف", + "pause": "إيقاف مؤقت", + "cancel": "إلغاء", + "remove": "إزالة", + "space_left_on_disk": "{{space}} متبقي على القرص", + "eta": "الانتهاء {{eta}}", + "calculating_eta": "جارٍ حساب الوقت المتبقي...", + "downloading_metadata": "جارٍ تنزيل البيانات الوصفية...", + "filter": "تصفية الحزم المعاد تعبئتها", + "requirements": "متطلبات النظام", + "minimum": "الحد الأدنى", + "recommended": "مُوصى به", + "paused": "معلّق", + "release_date": "تاريخ الإصدار {{date}}", + "publisher": "نشر بواسطة {{publisher}}", + "hours": "ساعات", + "minutes": "دقائق", + "amount_hours": "{{amount}} ساعات", + "amount_minutes": "{{amount}} دقائق", + "accuracy": "دقة {{accuracy}}%", + "add_to_library": "إضافة إلى المكتبة", + "remove_from_library": "إزالة من المكتبة", + "no_downloads": "لا توجد تنزيلات متاحة", + "play_time": "لعب لمدة {{amount}}", + "last_time_played": "آخر تشغيل {{period}}", + "not_played_yet": "لم تلعب {{title}} بعد", + "next_suggestion": "الاقتراح التالي", + "play": "تشغيل", + "deleting": "جارٍ حذف المثبت...", + "close": "إغلاق", + "playing_now": "يتم التشغيل الآن", + "change": "تغيير", + "repacks_modal_description": "اختر الحزمة المعاد تعبئتها التي تريد تنزيلها", + "select_folder_hint": "لتغيير المجلد الافتراضي، انتقل إلى <0>الإعدادات", + "download_now": "تنزيل الآن", + "no_shop_details": "تعذر الحصول على تفاصيل المتجر.", + "download_options": "خيارات التنزيل", + "download_path": "مسار التنزيل", + "previous_screenshot": "لقطة الشاشة السابقة", + "next_screenshot": "لقطة الشاشة التالية", + "screenshot": "لقطة الشاشة {{number}}", + "open_screenshot": "فتح لقطة الشاشة {{number}}", + "download_settings": "إعدادات التنزيل", + "downloader": "أداة التنزيل", + "select_executable": "تحديد", + "no_executable_selected": "لم يتم تحديد ملف تشغيل", + "open_folder": "فتح المجلد", + "open_download_location": "عرض الملفات المحملة", + "create_shortcut": "إنشاء اختصار على سطح المكتب", + "clear": "مسح", + "remove_files": "إزالة الملفات", + "remove_from_library_title": "هل أنت متأكد؟", + "remove_from_library_description": "سيؤدي هذا إلى إزالة {{game}} من مكتبتك", + "options": "خيارات", + "executable_section_title": "ملف التشغيل", + "executable_section_description": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"", + "downloads_secion_title": "التنزيلات", + "downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى لهذه اللعبة", + "danger_zone_section_title": "منطقة الخطر", + "danger_zone_section_description": "إزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra", + "download_in_progress": "تنزيل قيد التقدم", + "download_paused": "التنزيل معلق", + "last_downloaded_option": "خيار التنزيل الأخير", + "create_shortcut_success": "تم إنشاء الاختصار بنجاح", + "create_shortcut_error": "خطأ في إنشاء الاختصار", + "nsfw_content_title": "هذه اللعبة تحتوي على محتوى غير لائق", + "nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يناسب جميع الأعمار. هل تريد المتابعة؟", + "allow_nsfw_content": "متابعة", + "refuse_nsfw_content": "رجوع", + "stats": "الإحصائيات", + "download_count": "مرات التنزيل", + "player_count": "اللاعبون النشطون", + "download_error": "خيار التنزيل هذا غير متاح", + "download": "تنزيل", + "executable_path_in_use": "مسار التشغيل مستخدم بالفعل بواسطة \"{{game}}\"", + "warning": "تحذير:", + "hydra_needs_to_remain_open": "لهذا التنزيل، يجب أن يبقى Hydra مفتوحًا حتى اكتماله. إذا أغلق Hydra قبل الاكتمال، ستفقد تقدمك.", + "achievements": "الإنجازات", + "achievements_count": "الإنجازات {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "حفظ سحابي", + "cloud_save_description": "احفظ تقدمك على السحابة واستمر في اللعب من أي جهاز", + "backups": "النسخ الاحتياطية", + "install_backup": "تثبيت", + "delete_backup": "حذف", + "create_backup": "نسخة احتياطية جديدة", + "last_backup_date": "آخر نسخة احتياطية في {{date}}", + "no_backup_preview": "لم يتم العثور على حفظات لهذا العنوان", + "restoring_backup": "جارٍ استعادة النسخة الاحتياطية ({{progress}} اكتمال)...", + "uploading_backup": "جارٍ رفع النسخة الاحتياطية...", + "no_backups": "لم تقم بإنشاء أي نسخ احتياطية لهذه اللعبة بعد", + "backup_uploaded": "تم رفع النسخة الاحتياطية", + "backup_deleted": "تم حذف النسخة الاحتياطية", + "backup_restored": "تم استعادة النسخة الاحتياطية", + "see_all_achievements": "عرض جميع الإنجازات", + "sign_in_to_see_achievements": "سجل الدخول لعرض الإنجازات", + "mapping_method_automatic": "تلقائي", + "mapping_method_manual": "يدوي", + "mapping_method_label": "طريقة التعيين", + "files_automatically_mapped": "تم تعيين الملفات تلقائيًا", + "no_backups_created": "لم يتم إنشاء نسخ احتياطية لهذه اللعبة", + "manage_files": "إدارة الملفات", + "loading_save_preview": "جارٍ البحث عن حفظات الألعاب...", + "wine_prefix": "بادئة Wine", + "wine_prefix_description": "بادئة Wine المستخدمة لتشغيل هذه اللعبة", + "launch_options": "خيارات التشغيل", + "launch_options_description": "يمكن للمستخدمين المتقدمين إدخال تعديلات على خيارات التشغيل (ميزة تجريبية)", + "launch_options_placeholder": "لم يتم تحديد أي معاملات", + "no_download_option_info": "لا توجد معلومات متاحة", + "backup_deletion_failed": "فشل حذف النسخة الاحتياطية", + "max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى لعدد النسخ الاحتياطية لهذه اللعبة", + "achievements_not_sync": "تعرف على كيفية مزامنة إنجازاتك", + "manage_files_description": "إدارة الملفات التي سيتم نسخها احتياطيًا واستعادتها", + "select_folder": "حدد المجلد", + "backup_from": "نسخة احتياطية من {{date}}", + "custom_backup_location_set": "تم تعيين موقع نسخ احتياطي مخصص", + "no_directory_selected": "لم يتم تحديد مجلد", + "no_write_permission": "لا يمكن التنزيل إلى هذا المجلد. انقر هنا لمعرفة المزيد.", + "reset_achievements": "إعادة تعيين الإنجازات", + "reset_achievements_description": "سيؤدي هذا إلى إعادة تعيين جميع إنجازات {{game}}", + "reset_achievements_title": "هل أنت متأكد؟", + "reset_achievements_success": "تم إعادة تعيين الإنجازات بنجاح", + "reset_achievements_error": "فشل إعادة تعيين الإنجازات" }, "activation": { - "title": "تَفْعِيلُ Hydra", - "installation_id": "مُعَرِّفُ التَّثْبِيتِ:", - "enter_activation_code": "أَدْخِلْ رَمْزَ التَّفْعِيلِ", - "message": "إِذَا كُنْتَ لَا تَعْرِفُ أَيْنَ تَطْلُبُ هَذَا، فَلا يَجِبُ أَنْ تَكُونَ لَدَيْكَ.", - "activate": "تَفْعِيلٌ", - "loading": "جَارٍ التَّحْمِيلُ..." + "title": "تفعيل Hydra", + "installation_id": "معرف التثبيت:", + "enter_activation_code": "أدخل رمز التفعيل الخاص بك", + "message": "إذا كنت لا تعرف أين تطلب هذا، فلا يجب أن يكون لديك هذا.", + "activate": "تفعيل", + "loading": "جارٍ التحميل..." }, "downloads": { - "resume": "اسْتِئْنَافٌ", - "pause": "إِيقَافٌ", - "eta": "الِاكْتِمَالُ {{eta}}", - "paused": "مُوْقَفٌ", - "verifying": "جَارٍ التَّحَقُّقُ...", - "completed": "مَكْتُومٌ", - "removed": "لَمْ يُنَزَّلْ", - "cancel": "إِلْغَاءٌ", - "filter": "تَصْفِيَةُ الْأَلْعَابِ الْمُنَزَّلَةِ", - "remove": "إِزَالَةٌ", - "downloading_metadata": "جَارٍ تَنْزِيلُ الْبَيَانَاتِ الْوَصْفِيَّةِ...", - "deleting": "جَارٍ حَذْفُ الْمُثَبِّتِ...", - "delete": "حَذْفُ الْمُثَبِّتِ", - "delete_modal_title": "هَلْ أَنْتَ مُتَأَكِّدٌ؟", - "delete_modal_description": "سَيُؤَدِّي هَذَا إِلَى إِزَالَةِ جَمِيعِ مَلَفَّاتِ التَّثْبِيتِ مِنْ حَاسُوبِكَ", - "install": "تَثْبِيتٌ", - "download_in_progress": "جَارٍ التَّنْفِيذُ", - "queued_downloads": "التَّنْزِيلَاتُ فِي الْانْتِظَارِ", - "downloads_completed": "مَكْتُومٌ", - "queued": "فِي الْانْتِظَارِ", - "no_downloads_title": "فَرَاغٌ تَامٌ", - "no_downloads_description": "لَمْ تَقُمْ بِتَنْزِيلِ أَيِّ شَيْءٍ بِاسْتِخْدَامِ Hydra بَعْدُ، لَكِنَّهُ لَيْسَ مُتَأَخِّرًا لِلْبَدْءِ.", - "checking_files": "جَارٍ التَّحَقُّقُ مِنَ الْمَلَفَّاتِ...", - "seeding": "الْبَذْرُ", - "stop_seeding": "إِيقَافُ الْبَذْرِ", - "resume_seeding": "اسْتِئْنَافُ الْبَذْرِ", - "options": "إِدَارَةٌ" + "resume": "استئناف", + "pause": "إيقاف مؤقت", + "eta": "الانتهاء {{eta}}", + "paused": "معلّق", + "verifying": "جارٍ التحقق...", + "completed": "مكتمل", + "removed": "غير محمل", + "cancel": "إلغاء", + "filter": "تصفية الألعاب المحملة", + "remove": "إزالة", + "downloading_metadata": "جارٍ تنزيل البيانات الوصفية...", + "deleting": "جارٍ حذف المثبت...", + "delete": "إزالة المثبت", + "delete_modal_title": "هل أنت متأكد؟", + "delete_modal_description": "سيؤدي هذا إلى إزالة جميع ملفات التثبيت من جهازك", + "install": "تثبيت", + "download_in_progress": "قيد التقدم", + "queued_downloads": "التنزيلات في قائمة الانتظار", + "downloads_completed": "مكتمل", + "queued": "في قائمة الانتظار", + "no_downloads_title": "فارغ جدًا", + "no_downloads_description": "لم تقم بتنزيل أي شيء باستخدام Hydra بعد، ولكن لم يفت الأوان للبدء.", + "checking_files": "جارٍ فحص الملفات...", + "seeding": "التوزيع", + "stop_seeding": "إيقاف التوزيع", + "resume_seeding": "استئناف التوزيع", + "options": "إدارة" }, "settings": { - "downloads_path": "مَسَارُ التَّنْزِيلَاتِ", - "change": "تَحْدِيثٌ", - "notifications": "الإِشْعَارَاتُ", - "enable_download_notifications": "عِنْدَ اكْتِمَالِ التَّنْزِيلِ", - "enable_repack_list_notifications": "عِنْدَ إِضَافَةِ إِصْدَارٍ مُعَادٍ تَغْلِيفِهِ جَدِيدٍ", - "real_debrid_api_token_label": "رَمْزُ واجهة برمجة التطبيقات Real-Debrid", - "quit_app_instead_hiding": "لا تُخْفِ Hydra عِنْدَ الإِغْلَاقِ", - "launch_with_system": "تَشْغِيلُ Hydra عِنْدَ بَدْءِ النِّظَامِ", - "general": "عَامٌ", - "behavior": "سُلُوكٌ", - "download_sources": "مَصَادِرُ التَّنْزِيلِ", - "language": "اللُّغَةُ", - "real_debrid_api_token": "رَمْزُ واجهة برمجة التطبيقات", - "enable_real_debrid": "تَمْكِينُ Real-Debrid", - "real_debrid_description": "Real-Debrid هُوَ مُنَزِّلٌ غَيْرُ مَقْيُودٍ يَتِيحُ لَكَ تَنْزِيلَ الْمَلَفَّاتِ بِسُرْعَةٍ، مَحْدُودٌ فَقَطْ بِسُرْعَةِ الْإِنْتَرْنِتِ لَدَيْكَ.", - "real_debrid_invalid_token": "رَمْزُ واجهة برمجة التطبيقات غَيْرُ صَالِحٍ", - "real_debrid_api_token_hint": "يُمْكِنُكَ الْحُصُولُ عَلَى رَمْزِ واجهة برمجة التطبيقات <0>هُنَا", - "real_debrid_free_account_error": "الْحِسَابُ \"{{username}}\" هُوَ حِسَابٌ مَجَّانِيٌّ. يَرْجَى الِاشْتِرَاكُ فِي Real-Debrid", - "real_debrid_linked_message": "تَمَّ رَبْطُ الْحِسَابِ \"{{username}}\"", - "save_changes": "حِفْظُ التَّغْيِيرَاتِ", - "changes_saved": "تَمَّ حِفْظُ التَّغْيِيرَاتِ بِنَجَاحٍ", - "download_sources_description": "سَتَقُومُ Hydra بِجَلْبِ رَوَابِطِ التَّنْزِيلِ مِنْ هَذِهِ الْمَصَادِرِ. يَجِبُ أَنْ يَكُونَ عُنْوَانُ URL لِلْمَصْدَرِ رَابِطًا مُبَاشِرًا إِلَى مِلَفٍّ .json يَحْتَوِي عَلَى رَوَابِطِ التَّنْزِيلِ.", - "validate_download_source": "تَصْدِيقٌ", - "remove_download_source": "إِزَالَةٌ", - "add_download_source": "إِضَافَةُ مَصْدَرٍ", - "download_count_zero": "لَا تَوْجَدُ خِيَارَاتُ تَنْزِيلٍ", - "download_count_one": "{{countFormatted}} خِيَارُ تَنْزِيلٍ", - "download_count_other": "{{countFormatted}} خِيَارَاتُ تَنْزِيلٍ", - "download_source_url": "عُنْوَانُ مَصْدَرِ التَّنْزِيلِ", - "add_download_source_description": "أَدْخِلْ عُنْوَانَ URL لِمِلَفٍّ .json", - "download_source_up_to_date": "مُحَدَّثٌ", - "download_source_errored": "خَطَأٌ", - "sync_download_sources": "مَزْجُ الْمَصَادِرِ", - "removed_download_source": "تَمَّ إِزَالَةُ مَصْدَرِ التَّنْزِيلِ", - "added_download_source": "تَمَّتْ إِضَافَةُ مَصْدَرِ التَّنْزِيلِ", - "download_sources_synced": "تَمَّ مَزْجُ جَمِيعِ مَصَادِرِ التَّنْزِيلِ", - "insert_valid_json_url": "أَدْخِلْ عُنْوَانَ JSON صَالِحًا", - "found_download_option_zero": "لَمْ يُعْثَرْ عَلَى خِيَارِ تَنْزِيلٍ", - "found_download_option_one": "عُثِرَ عَلَى {{countFormatted}} خِيَارِ تَنْزِيلٍ", - "found_download_option_other": "عُثِرَ عَلَى {{countFormatted}} خِيَارَاتِ تَنْزِيلٍ", - "import": "اسْتِيرَادٌ", - "public": "عَامٌ", - "private": "خَاصٌ", - "friends_only": "الْأَصْدِقَاءُ فَقَطْ", - "privacy": "الْخُصُوصِيَّةُ", - "profile_visibility": "رُؤْيَةُ الْمَلَفِّ الشَّخْصِيِّ", - "profile_visibility_description": "اخْتَرْ مَنْ يُمْكِنُهُ رُؤْيَةُ مَلَفِّكَ الشَّخْصِيِّ وَمَكْتَبَتِكَ", - "required_field": "هَذَا الْحَقْلُ مَطْلُوبٌ", - "source_already_exists": "تَمَّتْ إِضَافَةُ هَذَا الْمَصْدَرِ مِنْ قَبْلُ", - "must_be_valid_url": "يَجِبُ أَنْ يَكُونَ الْمَصْدَرُ عُنْوَانَ URL صَالِحًا", - "blocked_users": "الْمُسْتَخْدِمُونَ الْمَحْظُورُونَ", - "user_unblocked": "تَمَّ إِزَالَةُ حَظْرِ الْمُسْتَخْدِمِ", - "enable_achievement_notifications": "عِنْدَ فَتْحِ إِنْجَازٍ", - "launch_minimized": "تَشْغِيلُ Hydra مُصَغَّرًا", - "disable_nsfw_alert": "تَعْطِيلُ تَنْبِيهِ الْمُحْتَوَى غَيْرِ اللَّائِقِ", - "seed_after_download_complete": "الْبَذْرُ بَعْدَ اكْتِمَالِ التَّنْزِيلِ", - "show_hidden_achievement_description": "إِظْهَارُ وَصْفِ الإِنْجَازَاتِ الْمَخْفِيَّةِ قَبْلَ فَتْحِهَا" + "downloads_path": "مسار التنزيلات", + "change": "تحديث", + "notifications": "الإشعارات", + "enable_download_notifications": "عند اكتمال التنزيل", + "enable_repack_list_notifications": "عند إضافة حزمة معاد تعبئتها جديدة", + "real_debrid_api_token_label": "رمز واجهة برمجة تطبيقات Real-Debrid", + "quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق", + "launch_with_system": "تشغيل Hydra مع بدء النظام", + "general": "عام", + "behavior": "السلوك", + "download_sources": "مصادر التنزيل", + "language": "اللغة", + "real_debrid_api_token": "رمز API", + "enable_real_debrid": "تفعيل Real-Debrid", + "real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، مقيدة فقط بسرعة الإنترنت لديك.", + "real_debrid_invalid_token": "رمز API غير صالح", + "real_debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا", + "real_debrid_free_account_error": "الحساب \"{{username}}\" هو حساب مجاني. يرجى الاشتراك في Real-Debrid", + "real_debrid_linked_message": "تم ربط الحساب \"{{username}}\"", + "save_changes": "حفظ التغييرات", + "changes_saved": "تم حفظ التغييرات بنجاح", + "download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL المصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.", + "validate_download_source": "تحقق", + "remove_download_source": "إزالة", + "add_download_source": "إضافة مصدر", + "download_count_zero": "لا توجد خيارات تنزيل", + "download_count_one": "{{countFormatted}} خيار تنزيل", + "download_count_other": "{{countFormatted}} خيارات تنزيل", + "download_source_url": "عنوان URL لمصدر التنزيل", + "add_download_source_description": "أدخل عنوان URL لملف .json", + "download_source_up_to_date": "محدث", + "download_source_errored": "خطأ", + "sync_download_sources": "مزامنة المصادر", + "removed_download_source": "تمت إزالة مصدر التنزيل", + "added_download_source": "تمت إضافة مصدر التنزيل", + "download_sources_synced": "تمت مزامنة جميع مصادر التنزيل", + "insert_valid_json_url": "أدخل عنوان JSON صالح", + "found_download_option_zero": "لم يتم العثور على خيارات تنزيل", + "found_download_option_one": "تم العثور على {{countFormatted}} خيار تنزيل", + "found_download_option_other": "تم العثور على {{countFormatted}} خيارات تنزيل", + "import": "استيراد", + "public": "عام", + "private": "خاص", + "friends_only": "الأصدقاء فقط", + "privacy": "الخصوصية", + "profile_visibility": "رؤية الملف الشخصي", + "profile_visibility_description": "اختر من يمكنه رؤية ملفك الشخصي ومكتبتك", + "required_field": "هذا الحقل مطلوب", + "source_already_exists": "تمت إضافة هذا المصدر مسبقًا", + "must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالحًا", + "blocked_users": "المستخدمون المحظورون", + "user_unblocked": "تم إلغاء حظر المستخدم", + "enable_achievement_notifications": "عند فتح إنجاز", + "launch_minimized": "تشغيل Hydra مصغرًا", + "disable_nsfw_alert": "تعطيل تنبيه المحتوى غير اللائق", + "seed_after_download_complete": "التوزيع بعد اكتمال التنزيل", + "show_hidden_achievement_description": "عرض وصف الإنجازات المخفية قبل فتحها", + "account": "الحساب", + "no_users_blocked": "لا يوجد مستخدمون محظورون", + "subscription_active_until": "اشتراك Hydra Cloud نشط حتى {{date}}", + "manage_subscription": "إدارة الاشتراك", + "update_email": "تحديث البريد الإلكتروني", + "update_password": "تحديث كلمة المرور", + "current_email": "البريد الإلكتروني الحالي:", + "no_email_account": "لم تقم بتعيين بريد إلكتروني بعد", + "account_data_updated_successfully": "تم تحديث بيانات الحساب بنجاح", + "renew_subscription": "تجديد اشتراك Hydra Cloud", + "subscription_expired_at": "انتهى اشتراكك في {{date}}", + "no_subscription": "استمتع بـ Hydra بأفضل طريقة ممكنة", + "become_subscriber": "كن مشتركًا في Hydra Cloud", + "subscription_renew_cancelled": "تم تعطيل التجديد التلقائي", + "subscription_renews_on": "سيتم تجديد اشتراكك في {{date}}", + "bill_sent_until": "سيتم إرسال فاتورتك التالية حتى هذا اليوم" }, "notifications": { - "download_complete": "اكْتِمَالُ التَّنْزِيلِ", - "game_ready_to_install": "{{title}} جَاهِزٌ لِلتَّثْبِيتِ", - "repack_list_updated": "تَمَّ تَحْدِيثُ قَائِمَةِ الإِصْدَارَاتِ الْمُعَادَةِ تَغْلِيفُهَا", - "repack_count_one": "{{count}} إِصْدَارٌ مُعَادٌ تَغْلِيفُهُ أُضِيفَ", - "repack_count_other": "{{count}} إِصْدَارَاتٌ مُعَادَةٌ تَغْلِيفُهَا أُضِيفَتْ", - "new_update_available": "الْإِصْدَارُ {{version}} مَتَوَفِّرٌ", - "restart_to_install_update": "أَعِدْ تَشْغِيلَ Hydra لِتَثْبِيتِ التَّحْدِيثِ", - "notification_achievement_unlocked_title": "تَمَّ فَتْحُ إِنْجَازٍ لِـ {{game}}", - "notification_achievement_unlocked_body": "{{achievement}} وَ{{count}} أُخْرَى تَمَّ فَتْحُهَا" + "download_complete": "اكتمل التنزيل", + "game_ready_to_install": "{{title}} جاهز للتثبيت", + "repack_list_updated": "تم تحديث قائمة الحزم المعاد تعبئتها", + "repack_count_one": "تمت إضافة {{count}} حزمة معاد تعبئتها", + "repack_count_other": "تمت إضافة {{count}} حزم معاد تعبئتها", + "new_update_available": "الإصدار {{version}} متوفر", + "restart_to_install_update": "أعد تشغيل Hydra لتثبيت التحديث", + "notification_achievement_unlocked_title": "تم فتح إنجاز لـ {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} و {{count}} آخرين تم فتحهم" }, "system_tray": { - "open": "فَتْحُ Hydra", - "quit": "الْخُرُوجُ" + "open": "فتح Hydra", + "quit": "خروج" }, "game_card": { - "no_downloads": "لَا تَوْجَدُ تَنْزِيلَاتٌ مَتَوَفِّرَةٌ" + "no_downloads": "لا توجد تنزيلات متاحة" }, "binary_not_found_modal": { - "title": "الْبَرَامِجُ غَيْرُ مُثَبَّتَةٍ", - "description": "لَمْ يُعْثَرْ عَلَى مَلَفَّاتٍ تَنْفِيذِيَّةٍ لِـ Wine أَوْ Lutris عَلَى نِظَامِكَ", - "instructions": "تَحَقَّقْ مِنَ الطَّرِيقَةِ الصَّحِيحَةِ لِتَثْبِيتِ أَيٍّ مِنْهُمَا عَلَى تَوْزِيعَةِ Linux لَدَيْكَ لِتَعْمَلَ اللُّعْبَةُ بِشَكْلٍ طَبِيعِيٍّ" + "title": "البرامج غير مثبتة", + "description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك", + "instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة لينكس الخاصة بك حتى تعمل اللعبة بشكل طبيعي" }, "modal": { - "close": "زِرُّ الإِغْلَاقِ" + "close": "زر الإغلاق" }, "forms": { - "toggle_password_visibility": "تَبْدِيلُ رُؤْيَةِ كَلِمَةِ الْمَرُورِ" + "toggle_password_visibility": "تبديل رؤية كلمة المرور" }, "user_profile": { - "amount_hours": "{{amount}} سَاعَاتٌ", - "amount_minutes": "{{amount}} دَقَائِقُ", - "last_time_played": "آخِرُ مَرَّةٍ لُعِبَتْ {{period}}", - "activity": "النَّشَاطُ الْأَخِيرُ", - "library": "الْمَكْتَبَةُ", - "total_play_time": "إِجْمَالِيُّ وَقْتِ اللَّعِبِ", - "no_recent_activity_title": "هَمَمْ... لَا شَيْءَ هُنَا", - "no_recent_activity_description": "لَمْ تَلْعَبْ أَيَّ أَلْعَابٍ مُؤَخَّرًا. حَانَ الْوَقْتُ لِتَغْيِيرِ ذَلِكَ!", - "display_name": "اسْمُ الْعَرْضِ", - "saving": "جَارٍ الْحِفْظُ", - "save": "حِفْظٌ", - "edit_profile": "تَحْرِيرُ الْمَلَفِّ الشَّخْصِيِّ", - "saved_successfully": "تَمَّ الْحِفْظُ بِنَجَاحٍ", - "try_again": "الرَّجَاءُ الْمُحَاوَلَةُ مَرَّةً أُخْرَى", - "sign_out_modal_title": "هَلْ أَنْتَ مُتَأَكِّدٌ؟", - "cancel": "إِلْغَاءٌ", - "successfully_signed_out": "تَمَّ تَسْجِيلُ الْخُرُوجِ بِنَجَاحٍ", - "sign_out": "تَسْجِيلُ الْخُرُوجِ", - "playing_for": "جَارِي اللَّعِبُ لِمُدَّةِ {{amount}}", - "sign_out_modal_text": "مَكْتَبَتُكَ مُرْتَبِطَةٌ بِحِسَابِكَ الْحَالِيِّ. عِنْدَ تَسْجِيلِ الْخُرُوجِ، لَنْ تَكُونَ مَكْتَبَتُكَ مَرْئِيَّةً بَعْدَ الْآنِ، وَلَنْ يَتِمَّ حِفْظُ أَيِّ تَقَدُّمٍ. هَلْ تُرِيدُ الْمُتَابَعَةَ مَعَ تَسْجِيلِ الْخُرُوجِ؟", - "add_friends": "إِضَافَةُ الْأَصْدِقَاءِ", - "add": "إِضَافَةٌ", - "friend_code": "رَمْزُ الصَّدِيقِ", - "see_profile": "رُؤْيَةُ الْمَلَفِّ الشَّخْصِيِّ", - "sending": "جَارٍ الْإِرْسَالُ", - "friend_request_sent": "تَمَّ إِرْسَالُ طَلَبِ الصَّدَاقَةِ", - "friends": "الْأَصْدِقَاءُ", - "friends_list": "قَائِمَةُ الْأَصْدِقَاءِ", - "user_not_found": "الْمُسْتَخْدِمُ غَيْرُ مَوْجُودٍ", - "block_user": "حَظْرُ الْمُسْتَخْدِمِ", - "add_friend": "إِضَافَةُ صَدِيقٍ", - "request_sent": "تَمَّ إِرْسَالُ الطَّلَبِ", - "request_received": "تَمَّ اسْتِقْبَالُ الطَّلَبِ", - "accept_request": "قَبُولُ الطَّلَبِ", - "ignore_request": "تَجَاهُلُ الطَّلَبِ", - "cancel_request": "إِلْغَاءُ الطَّلَبِ", - "undo_friendship": "إِلْغَاءُ الصَّدَاقَةِ", - "request_accepted": "تَمَّ قَبُولُ الطَّلَبِ", - "user_blocked_successfully": "تَمَّ حَظْرُ الْمُسْتَخْدِمِ بِنَجَاحٍ", - "user_block_modal_text": "سَيُؤَدِّي هَذَا إِلَى حَظْرِ {{displayName}}", - "blocked_users": "الْمُسْتَخْدِمُونَ الْمَحْظُورُونَ", - "unblock": "إِزَالَةُ الْحَظْرِ", - "no_friends_added": "لَيْسَ لَدَيْكَ أَصْدِقَاءٌ مُضَافُونَ", - "pending": "قَيْدُ الْانْتِظَارِ", - "no_pending_invites": "لَيْسَ لَدَيْكَ دَعَوَاتٌ قَيْدُ الْانْتِظَارِ", - "no_blocked_users": "لَيْسَ لَدَيْكَ مُسْتَخْدِمُونَ مَحْظُورُونَ", - "friend_code_copied": "تَمَّ نَسْخُ رَمْزِ الصَّدِيقِ", - "undo_friendship_modal_text": "سَيُؤَدِّي هَذَا إِلَى إِلْغَاءِ صَدَاقَتِكَ مَعَ {{displayName}}", - "privacy_hint": "لِتَعْدِيلِ مَنْ يُمْكِنُهُ رُؤْيَةُ هَذَا، اذْهَبْ إِلَى <0>الإعْدَادَاتِ", - "locked_profile": "هَذَا الْمَلَفُّ الشَّخْصِيُّ خَاصٌّ", - "image_process_failure": "فَشَلَ أَثْنَاءَ مُعَالَجَةِ الصُّورَةِ", - "required_field": "هَذَا الْحَقْلُ مَطْلُوبٌ", - "displayname_min_length": "يَجِبُ أَنْ يَكُونَ اسْمُ الْعَرْضِ عَلَى الْأَقَلِّ 3 أَحْرُفٍ", - "displayname_max_length": "يَجِبُ أَنْ يَكُونَ اسْمُ الْعَرْضِ عَلَى الْأَكْثَرِ 50 حَرْفًا", - "report_profile": "تَقْرِيرٌ عَنْ هَذَا الْمَلَفِّ الشَّخْصِيِّ", - "report_reason": "لِمَاذَا تُقَدِّمُ تَقْرِيرًا عَنْ هَذَا الْمَلَفِّ الشَّخْصِيِّ؟", - "report_description": "مَعْلُومَاتٌ إِضَافِيَّةٌ", - "report_description_placeholder": "مَعْلُومَاتٌ إِضَافِيَّةٌ", - "report": "تَقْرِيرٌ", - "report_reason_hate": "خِطَابُ الْكُرْهِ", - "report_reason_sexual_content": "مُحْتَوًى جِنْسِيٌّ", - "report_reason_violence": "عُنْفٌ", - "report_reason_spam": "رَاسِلَةٌ عَشْوَائِيَّةٌ", - "report_reason_other": "آخَرُ", - "profile_reported": "تَمَّ تَقْرِيرُ الْمَلَفِّ الشَّخْصِيِّ", - "your_friend_code": "رَمْزُ صَدِيقِكَ:", - "upload_banner": "رَفْعُ لَافِتَةٍ", - "uploading_banner": "جَارٍ رَفْعُ اللَّافِتَةِ...", - "background_image_updated": "تَمَّ تَحْدِيثُ صُورَةِ الْخَلْفِيَّةِ", - "stats": "الإحْصَائِيَّاتُ", - "achievements": "الإِنْجَازَاتُ", - "games": "الْأَلْعَابُ", - "top_percentile": "الْأَفْضَلُ {{percentile}}%", - "ranking_updated_weekly": "التَّرْتِيبُ يُحَدَّثُ أُسْبُوعِيًّا", - "playing": "جَارِي اللَّعِبُ {{game}}", - "achievements_unlocked": "الإِنْجَازَاتُ الْمَفْتُوحَةُ", - "earned_points": "النَّقَاطُ الْمَكْسُوبَةُ", - "show_achievements_on_profile": "عَرْضُ إِنْجَازَاتِكَ عَلَى مَلَفِّكَ الشَّخْصِيِّ", - "show_points_on_profile": "عَرْضُ النَّقَاطِ الْمَكْسُوبَةِ عَلَى مَلَفِّكَ الشَّخْصِيِّ" + "amount_hours": "{{amount}} ساعات", + "amount_minutes": "{{amount}} دقائق", + "last_time_played": "آخر تشغيل {{period}}", + "activity": "النشاط الأخير", + "library": "المكتبة", + "total_play_time": "إجمالي وقت اللعب", + "no_recent_activity_title": "همم... لا شيء هنا", + "no_recent_activity_description": "لم تلعب أي ألعاب مؤخرًا. حان الوقت لتغيير ذلك!", + "display_name": "اسم العرض", + "saving": "جارٍ الحفظ", + "save": "حفظ", + "edit_profile": "تعديل الملف الشخصي", + "saved_successfully": "تم الحفظ بنجاح", + "try_again": "يرجى المحاولة مرة أخرى", + "sign_out_modal_title": "هل أنت متأكد؟", + "cancel": "إلغاء", + "successfully_signed_out": "تم تسجيل الخروج بنجاح", + "sign_out": "تسجيل الخروج", + "playing_for": "يلعب لمدة {{amount}}", + "sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. عند تسجيل الخروج، لن تكون مكتبتك مرئية بعد الآن، ولن يتم حفظ أي تقدم. هل تتابع تسجيل الخروج؟", + "add_friends": "إضافة أصدقاء", + "add": "إضافة", + "friend_code": "رمز الصديق", + "see_profile": "عرض الملف الشخصي", + "sending": "جارٍ الإرسال", + "friend_request_sent": "تم إرسال طلب الصداقة", + "friends": "الأصدقاء", + "friends_list": "قائمة الأصدقاء", + "user_not_found": "المستخدم غير موجود", + "block_user": "حظر المستخدم", + "add_friend": "إضافة صديق", + "request_sent": "تم إرسال الطلب", + "request_received": "تم استلام الطلب", + "accept_request": "قبول الطلب", + "ignore_request": "تجاهل الطلب", + "cancel_request": "إلغاء الطلب", + "undo_friendship": "إلغاء الصداقة", + "request_accepted": "تم قبول الطلب", + "user_blocked_successfully": "تم حظر المستخدم بنجاح", + "user_block_modal_text": "سيؤدي هذا إلى حظر {{displayName}}", + "blocked_users": "المستخدمون المحظورون", + "unblock": "إلغاء الحظر", + "no_friends_added": "ليس لديك أصدقاء مضافون", + "pending": "قيد الانتظار", + "no_pending_invites": "ليس لديك دعوات معلقة", + "no_blocked_users": "ليس لديك مستخدمون محظورون", + "friend_code_copied": "تم نسخ رمز الصديق", + "undo_friendship_modal_text": "سيؤدي هذا إلى إلغاء صداقتك مع {{displayName}}", + "privacy_hint": "لضبط من يمكنه رؤية هذا، انتقل إلى <0>الإعدادات", + "locked_profile": "هذا الملف الشخصي خاص", + "image_process_failure": "فشل معالجة الصورة", + "required_field": "هذا الحقل مطلوب", + "displayname_min_length": "يجب أن يكون اسم العرض على الأقل 3 أحرف", + "displayname_max_length": "يجب ألا يتجاوز اسم العرض 50 حرفًا", + "report_profile": "الإبلاغ عن هذا الملف الشخصي", + "report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف الشخصي؟", + "report_description": "معلومات إضافية", + "report_description_placeholder": "معلومات إضافية", + "report": "الإبلاغ", + "report_reason_hate": "خطاب كراهية", + "report_reason_sexual_content": "محتوى جنسي", + "report_reason_violence": "عنف", + "report_reason_spam": "بريد عشوائي", + "report_reason_other": "أخرى", + "profile_reported": "تم الإبلاغ عن الملف الشخصي", + "your_friend_code": "رمز صديقك:", + "upload_banner": "تحميل بانر", + "uploading_banner": "جارٍ تحميل البانر...", + "background_image_updated": "تم تحديث صورة الخلفية", + "stats": "الإحصائيات", + "achievements": "إنجازات", + "games": "الألعاب", + "top_percentile": "ال{{percentile}}% الأعلى", + "ranking_updated_weekly": "يتم تحديث التصنيف أسبوعيًا", + "playing": "يلعب {{game}}", + "achievements_unlocked": "الإنجازات المفتوحة", + "earned_points": "النقاط المكتسبة", + "show_achievements_on_profile": "عرض إنجازاتك على ملفك الشخصي", + "show_points_on_profile": "عرض نقاطك المكتسبة على ملفك الشخصي" }, "achievement": { - "achievement_unlocked": "إِنْجَازٌ مَفْتُوحٌ", - "user_achievements": "إِنْجَازَاتُ {{displayName}}", - "your_achievements": "إِنْجَازَاتُكَ", - "unlocked_at": "تَمَّ الْفَتْحُ فِي: {{date}}", - "subscription_needed": "يَحْتَاجُ اشْتِرَاكُ Hydra Cloud لِرُؤْيَةِ هَذَا الْمُحْتَوَى", - "new_achievements_unlocked": "تَمَّ فَتْحُ {{achievementCount}} إِنْجَازَاتٍ جَدِيدَةٍ مِنْ {{gameCount}} أَلْعَابٍ", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} إِنْجَازَاتٍ", - "achievements_unlocked_for_game": "تَمَّ فَتْحُ {{achievementCount}} إِنْجَازَاتٍ جَدِيدَةٍ لِـ {{gameTitle}}", - "hidden_achievement_tooltip": "هَذَا إِنْجَازٌ مَخْفِيٌّ", - "achievement_earn_points": "اكْسِبْ {{points}} نَقَاطًا بِهَذَا الإِنْجَازِ", - "earned_points": "النَّقَاطُ الْمَكْسُوبَةُ:", - "available_points": "النَّقَاطُ الْمُتَوَفِّرَةُ:", - "how_to_earn_achievements_points": "كَيْفَ تَكْسِبُ نَقَاطَ الإِنْجَازَاتِ؟" + "achievement_unlocked": "تم فتح الإنجاز", + "user_achievements": "إنجازات {{displayName}}", + "your_achievements": "إنجازاتك", + "unlocked_at": "تم الفتح في: {{date}}", + "subscription_needed": "يحتاج إلى اشتراك Hydra Cloud لرؤية هذا المحتوى", + "new_achievements_unlocked": "تم فتح {{achievementCount}} إنجازات جديدة من {{gameCount}} ألعاب", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} إنجازات", + "achievements_unlocked_for_game": "تم فتح {{achievementCount}} إنجازات جديدة لـ {{gameTitle}}", + "hidden_achievement_tooltip": "هذا إنجاز مخفي", + "achievement_earn_points": "اكسب {{points}} نقطة مع هذا الإنجاز", + "earned_points": "النقاط المكتسبة:", + "available_points": "النقاط المتاحة:", + "how_to_earn_achievements_points": "كيفية كسب نقاط الإنجازات؟" }, "hydra_cloud": { - "subscription_tour_title": "اشْتِرَاكُ Hydra Cloud", - "subscribe_now": "اشْتَرِكِ الْآنَ", - "cloud_saving": "حِفْظٌ سَحَابِيٌّ", - "cloud_achievements": "حِفْظُ إِنْجَازَاتِكَ فِي السَّحَابَةِ", - "animated_profile_picture": "صُورُ الْمَلَفِّ الشَّخْصِيِّ الْمُتَحَرِّكَةِ", - "premium_support": "الدَّعْمُ الْمُتَقَدِّمُ", - "show_and_compare_achievements": "عَرْضٌ وَمُقَارَنَةُ إِنْجَازَاتِكَ مَعَ مُسْتَخْدِمِينَ آخَرِينَ", - "animated_profile_banner": "لَافِتَةُ الْمَلَفِّ الشَّخْصِيِّ الْمُتَحَرِّكَةِ", + "subscription_tour_title": "اشتراك Hydra Cloud", + "subscribe_now": "اشترك الآن", + "cloud_saving": "حفظ سحابي", + "cloud_achievements": "احفظ إنجازاتك على السحابة", + "animated_profile_picture": "صورة ملف شخصي متحركة", + "premium_support": "دعم ممتاز", + "show_and_compare_achievements": "اعرض وقارن إنجازاتك مع المستخدمين الآخرين", + "animated_profile_banner": "بانر ملف شخصي متحرك", "hydra_cloud": "Hydra Cloud", - "hydra_cloud_feature_found": "لَقَدْ اكْتَشَفْتَ مِيزَةً مِنْ Hydra Cloud!", - "learn_more": "تَعَلَّمْ أَكْثَرَ" + "hydra_cloud_feature_found": "لقد اكتشفت ميزة Hydra Cloud!", + "learn_more": "معرفة المزيد" } } diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 931ee058..c8667d6c 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -175,7 +175,16 @@ "backup_from": "Copia de seguridad de {{date}}", "custom_backup_location_set": "Se configuró la carpeta de copia de seguridad", "clear": "Limpiar", - "no_directory_selected": "No se seleccionó un directorio" + "no_directory_selected": "No se seleccionó un directorio", + "launch_options": "Opciones de Inicio", + "launch_options_description": "Los usuarios avanzados pueden introducir sus propias modificaciones de opciones de inicio (característica experimental)", + "launch_options_placeholder": "Sin parámetro específicado", + "no_write_permission": "No se puede descargar en este directorio. Presiona aquí para aprender más.", + "reset_achievements": "Reiniciar logros", + "reset_achievements_description": "Esto reiniciará todos los logros de {{game}}", + "reset_achievements_title": "¿Estás seguro?", + "reset_achievements_success": "Logros reiniciados exitosamente", + "reset_achievements_error": "Se produjo un error al reiniciar los logros" }, "activation": { "title": "Activar Hydra", @@ -271,7 +280,23 @@ "launch_minimized": "Iniciar Hydra minimizado", "disable_nsfw_alert": "Desactivar alerta NSFW", "seed_after_download_complete": "Realizar seeding después de que se completa la descarga", - "show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos" + "show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos", + "account": "Cuenta", + "account_data_updated_successfully": "Datos de la cuenta actualizados", + "bill_sent_until": "Tú próxima factura se enviará el {{date}}", + "current_email": "Correo actual:", + "manage_subscription": "Gestionar suscripción", + "no_email_account": "No has configurado un correo aún", + "no_subscription": "Disfruta Hydra de la mejor manera", + "no_users_blocked": "No tienes usuarios bloqueados", + "notifications": "Notificaciones", + "renew_subscription": "Renovar Hydra Cloud", + "subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}", + "subscription_expired_at": "Tú suscripción expiró el {{date}}", + "subscription_renew_cancelled": "Está desactivada la renovación automática", + "subscription_renews_on": "Tú suscripción se renueva el {{date}}", + "update_email": "Actualizar correo", + "update_password": "Actualizar contraseña" }, "notifications": { "download_complete": "Descarga completada", diff --git a/src/main/events/helpers/get-downloads-path.ts b/src/main/events/helpers/get-downloads-path.ts index 782ea599..0403095f 100644 --- a/src/main/events/helpers/get-downloads-path.ts +++ b/src/main/events/helpers/get-downloads-path.ts @@ -3,15 +3,14 @@ import { db, levelKeys } from "@main/level"; import type { UserPreferences } from "@types"; export const getDownloadsPath = async () => { - const userPreferences = await db.get( + const userPreferences = await db.get( levelKeys.userPreferences, { valueEncoding: "json", } ); - if (userPreferences && userPreferences.downloadsPath) - return userPreferences.downloadsPath; + if (userPreferences?.downloadsPath) return userPreferences.downloadsPath; return defaultDownloadsPath; }; diff --git a/src/main/events/notifications/publish-new-repacks-notification.ts b/src/main/events/notifications/publish-new-repacks-notification.ts index 1f23eeb1..356b1b16 100644 --- a/src/main/events/notifications/publish-new-repacks-notification.ts +++ b/src/main/events/notifications/publish-new-repacks-notification.ts @@ -10,7 +10,7 @@ const publishNewRepacksNotification = async ( ) => { if (newRepacksCount < 1) return; - const userPreferences = await db.get( + const userPreferences = await db.get( levelKeys.userPreferences, { valueEncoding: "json", diff --git a/src/main/events/user-preferences/get-user-preferences.ts b/src/main/events/user-preferences/get-user-preferences.ts index 19458496..b40d6780 100644 --- a/src/main/events/user-preferences/get-user-preferences.ts +++ b/src/main/events/user-preferences/get-user-preferences.ts @@ -5,11 +5,11 @@ import type { UserPreferences } from "@types"; const getUserPreferences = async () => db - .get(levelKeys.userPreferences, { + .get(levelKeys.userPreferences, { valueEncoding: "json", }) .then((userPreferences) => { - if (userPreferences.realDebridApiToken) { + if (userPreferences?.realDebridApiToken) { userPreferences.realDebridApiToken = Crypto.decrypt( userPreferences.realDebridApiToken ); diff --git a/src/main/events/user-preferences/update-user-preferences.ts b/src/main/events/user-preferences/update-user-preferences.ts index caec78a1..31193558 100644 --- a/src/main/events/user-preferences/update-user-preferences.ts +++ b/src/main/events/user-preferences/update-user-preferences.ts @@ -3,13 +3,14 @@ import { registerEvent } from "../register-event"; import type { UserPreferences } from "@types"; import i18next from "i18next"; import { db, levelKeys } from "@main/level"; +import { Crypto } from "@main/services"; import { patchUserProfile } from "../profile/update-profile"; const updateUserPreferences = async ( _event: Electron.IpcMainInvokeEvent, preferences: Partial ) => { - const userPreferences = await db.get( + const userPreferences = await db.get( levelKeys.userPreferences, { valueEncoding: "json" } ); @@ -23,6 +24,16 @@ const updateUserPreferences = async ( patchUserProfile({ language: preferences.language }).catch(() => {}); } + if (preferences.realDebridApiToken) { + preferences.realDebridApiToken = Crypto.encrypt( + preferences.realDebridApiToken + ); + } + + if (!preferences.downloadsPath) { + preferences.downloadsPath = null; + } + await db.put( levelKeys.userPreferences, { diff --git a/src/main/events/user/get-compared-unlocked-achievements.ts b/src/main/events/user/get-compared-unlocked-achievements.ts index 33b37584..697ad716 100644 --- a/src/main/events/user/get-compared-unlocked-achievements.ts +++ b/src/main/events/user/get-compared-unlocked-achievements.ts @@ -10,7 +10,7 @@ const getComparedUnlockedAchievements = async ( shop: GameShop, userId: string ) => { - const userPreferences = await db.get( + const userPreferences = await db.get( levelKeys.userPreferences, { valueEncoding: "json", @@ -25,7 +25,7 @@ const getComparedUnlockedAchievements = async ( { shop, objectId, - language: userPreferences?.language || "en", + language: userPreferences?.language ?? "en", } ).then((achievements) => { const sortedAchievements = achievements.achievements diff --git a/src/main/events/user/get-unlocked-achievements.ts b/src/main/events/user/get-unlocked-achievements.ts index 9cb44423..6deecbad 100644 --- a/src/main/events/user/get-unlocked-achievements.ts +++ b/src/main/events/user/get-unlocked-achievements.ts @@ -12,7 +12,7 @@ export const getUnlockedAchievements = async ( levelKeys.game(shop, objectId) ); - const userPreferences = await db.get( + const userPreferences = await db.get( levelKeys.userPreferences, { valueEncoding: "json", diff --git a/src/main/main.ts b/src/main/main.ts index d5d23cdb..0777a28e 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -27,7 +27,7 @@ export const loadState = async () => { valueEncoding: "json", }); - return db.get(levelKeys.userPreferences, { + return db.get(levelKeys.userPreferences, { valueEncoding: "json", }); }); @@ -114,24 +114,29 @@ const migrateFromSqlite = async () => { if (userPreferences.length > 0) { const { realDebridApiToken, ...rest } = userPreferences[0]; - await db.put(levelKeys.userPreferences, { - ...rest, - realDebridApiToken: realDebridApiToken - ? Crypto.encrypt(realDebridApiToken) - : null, - preferQuitInsteadOfHiding: rest.preferQuitInsteadOfHiding === 1, - runAtStartup: rest.runAtStartup === 1, - startMinimized: rest.startMinimized === 1, - disableNsfwAlert: rest.disableNsfwAlert === 1, - seedAfterDownloadComplete: rest.seedAfterDownloadComplete === 1, - showHiddenAchievementsDescription: - rest.showHiddenAchievementsDescription === 1, - downloadNotificationsEnabled: rest.downloadNotificationsEnabled === 1, - repackUpdatesNotificationsEnabled: - rest.repackUpdatesNotificationsEnabled === 1, - achievementNotificationsEnabled: - rest.achievementNotificationsEnabled === 1, - }); + await db.put( + levelKeys.userPreferences, + { + ...rest, + realDebridApiToken: realDebridApiToken + ? Crypto.encrypt(realDebridApiToken) + : null, + preferQuitInsteadOfHiding: rest.preferQuitInsteadOfHiding === 1, + runAtStartup: rest.runAtStartup === 1, + startMinimized: rest.startMinimized === 1, + disableNsfwAlert: rest.disableNsfwAlert === 1, + seedAfterDownloadComplete: rest.seedAfterDownloadComplete === 1, + showHiddenAchievementsDescription: + rest.showHiddenAchievementsDescription === 1, + downloadNotificationsEnabled: + rest.downloadNotificationsEnabled === 1, + repackUpdatesNotificationsEnabled: + rest.repackUpdatesNotificationsEnabled === 1, + achievementNotificationsEnabled: + rest.achievementNotificationsEnabled === 1, + }, + { valueEncoding: "json" } + ); if (rest.language) { await db.put(levelKeys.language, rest.language); diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index c0145b41..cc289067 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -27,17 +27,20 @@ export class DownloadManager { ) { PythonRPC.spawn( download?.status === "active" - ? await this.getDownloadPayload(download).catch(() => undefined) + ? await this.getDownloadPayload(download).catch((err) => { + logger.error("Error getting download payload", err); + return undefined; + }) : undefined, downloadsToSeed?.map((download) => ({ - game_id: `${download.shop}-${download.objectId}`, + game_id: levelKeys.game(download.shop, download.objectId), url: download.uri, save_path: download.downloadPath, })) ); if (download) { - this.downloadingGameId = `${download.shop}-${download.objectId}`; + this.downloadingGameId = levelKeys.game(download.shop, download.objectId); } } @@ -280,7 +283,7 @@ export class DownloadManager { return { action: "start", game_id: downloadId, - url: `https://pixeldrain.com/api/file/${id}?download`, + url: `https://cdn.pd5-gamedriveorg.workers.dev/api/file/${id}`, save_path: download.downloadPath, out: name, }; diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 411e2c04..00a1d399 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -29,6 +29,7 @@ import { downloadSourcesWorker } from "./workers"; import { downloadSourcesTable } from "./dexie"; import { useSubscription } from "./hooks/use-subscription"; import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-modal"; +import { SPACING_UNIT } from "./theme.css"; export interface AppProps { children: React.ReactNode; @@ -212,22 +213,22 @@ export function App() { const id = crypto.randomUUID(); const channel = new BroadcastChannel(`download_sources:sync:${id}`); - channel.onmessage = (event: MessageEvent) => { + channel.onmessage = async (event: MessageEvent) => { const newRepacksCount = event.data; window.electron.publishNewRepacksNotification(newRepacksCount); updateRepacks(); - downloadSourcesTable.toArray().then((downloadSources) => { - downloadSources - .filter((source) => !source.fingerprint) - .forEach((downloadSource) => { - window.electron - .putDownloadSource(downloadSource.objectIds) - .then(({ fingerprint }) => { - downloadSourcesTable.update(downloadSource.id, { fingerprint }); - }); - }); - }); + const downloadSources = await downloadSourcesTable.toArray(); + + downloadSources + .filter((source) => !source.fingerprint) + .forEach(async (downloadSource) => { + const { fingerprint } = await window.electron.putDownloadSource( + downloadSource.objectIds + ); + + downloadSourcesTable.update(downloadSource.id, { fingerprint }); + }); }; downloadSourcesWorker.postMessage(["SYNC_DOWNLOAD_SOURCES", id]); @@ -255,7 +256,7 @@ export function App() { return ( <> - {/* {window.electron.platform === "win32" && ( + {window.electron.platform === "win32" && (

Hydra @@ -264,22 +265,25 @@ export function App() { )}

- )} */} -
-

- Hydra - {hasActiveSubscription && ( - Cloud - )} -

-
+ )} - +
+ +
-
-
-
    +
    +
    +
      {routes.map(({ nameKey, path, render }) => (
    -
    - {t("my_library")} +
    + {t("my_library")} -
      +
        {filteredLibrary.map((game) => (
      • @@ -257,10 +265,10 @@ export function Sidebar() { {hasActiveSubscription && ( +
        + {type === "success" && ( + + )} + + {type === "error" && ( + + )} + + {type === "warning" && ( + + )} + + {title} + + +
        + + {message &&

        {message}

        } +
- +
); } diff --git a/src/renderer/src/features/toast-slice.ts b/src/renderer/src/features/toast-slice.ts index e27480fa..44abf53a 100644 --- a/src/renderer/src/features/toast-slice.ts +++ b/src/renderer/src/features/toast-slice.ts @@ -3,12 +3,14 @@ import type { PayloadAction } from "@reduxjs/toolkit"; import { ToastProps } from "@renderer/components/toast/toast"; export interface ToastState { - message: string; + title: string; + message?: string; type: ToastProps["type"]; visible: boolean; } const initialState: ToastState = { + title: "", message: "", type: "success", visible: false, @@ -19,6 +21,7 @@ export const toastSlice = createSlice({ initialState, reducers: { showToast: (state, action: PayloadAction>) => { + state.title = action.payload.title; state.message = action.payload.message; state.type = action.payload.type; state.visible = true; diff --git a/src/renderer/src/hooks/use-toast.ts b/src/renderer/src/hooks/use-toast.ts index 485470f0..5e08a7ab 100644 --- a/src/renderer/src/hooks/use-toast.ts +++ b/src/renderer/src/hooks/use-toast.ts @@ -6,9 +6,10 @@ export function useToast() { const dispatch = useAppDispatch(); const showSuccessToast = useCallback( - (message: string) => { + (title: string, message?: string) => { dispatch( showToast({ + title, message, type: "success", }) @@ -18,9 +19,10 @@ export function useToast() { ); const showErrorToast = useCallback( - (message: string) => { + (title: string, message?: string) => { dispatch( showToast({ + title, message, type: "error", }) @@ -30,9 +32,10 @@ export function useToast() { ); const showWarningToast = useCallback( - (message: string) => { + (title: string, message?: string) => { dispatch( showToast({ + title, message, type: "warning", }) diff --git a/src/renderer/src/pages/achievements/achievement-list.tsx b/src/renderer/src/pages/achievements/achievement-list.tsx index 6066f241..8862ce48 100644 --- a/src/renderer/src/pages/achievements/achievement-list.tsx +++ b/src/renderer/src/pages/achievements/achievement-list.tsx @@ -1,43 +1,38 @@ import { useDate } from "@renderer/hooks"; import type { UserAchievement } from "@types"; import { useTranslation } from "react-i18next"; -import * as styles from "./achievements.css"; +import "./achievements.scss"; import { EyeClosedIcon } from "@primer/octicons-react"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; import { useSubscription } from "@renderer/hooks/use-subscription"; -import { vars } from "@renderer/theme.css"; interface AchievementListProps { achievements: UserAchievement[]; } -export function AchievementList({ achievements }: AchievementListProps) { +export function AchievementList({ + achievements, +}: Readonly) { const { t } = useTranslation("achievement"); const { showHydraCloudModal } = useSubscription(); const { formatDateTime } = useDate(); return ( -
    +
      {achievements.map((achievement) => ( -
    • +
    • {achievement.displayName} -
      -

      +
      +

      {achievement.hidden && ( @@ -47,48 +42,36 @@ export function AchievementList({ achievements }: AchievementListProps) {

      {achievement.description}

      -
      + +
      {achievement.points != undefined ? (
      - -

      {achievement.points}

      + +

      + {achievement.points} +

      ) : ( )} {achievement.unlockTime != null && (
      {formatDateTime(achievement.unlockTime)}
      diff --git a/src/renderer/src/pages/achievements/achievements.scss b/src/renderer/src/pages/achievements/achievements.scss new file mode 100644 index 00000000..5a5de8e6 --- /dev/null +++ b/src/renderer/src/pages/achievements/achievements.scss @@ -0,0 +1,262 @@ +@use "../../scss/globals.scss"; +@use "sass:math"; + +$hero-height: 150px; +$logo-height: 100px; +$logo-max-width: 200px; + +.achievements { + display: flex; + flex-direction: column; + overflow: hidden; + width: 100%; + height: 100%; + transition: all ease 0.3s; + + &__hero { + width: 100%; + height: $hero-height; + min-height: $hero-height; + display: flex; + flex-direction: column; + position: relative; + transition: all ease 0.2s; + + &-content { + padding: globals.$spacing-unit * 2; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + } + + &-logo-backdrop { + width: 100%; + height: 100%; + position: absolute; + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + &-image-skeleton { + height: 150px; + } + } + + &__game-logo { + width: $logo-max-width; + height: $logo-height; + object-fit: contain; + transition: all ease 0.2s; + + &:hover { + transform: scale(1.05); + } + } + + &__container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + overflow: auto; + z-index: 1; + } + + &__table-header { + width: 100%; + background-color: var(--color-dark-background); + transition: all ease 0.2s; + border-bottom: solid 1px var(--color-border); + position: sticky; + top: 0; + z-index: 1; + + &--stuck { + box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.8); + } + } + + &__list { + list-style: none; + margin: 0; + display: flex; + flex-direction: column; + gap: globals.$spacing-unit * 2; + padding: globals.$spacing-unit * 2; + width: 100%; + background-color: var(--color-background); + } + + &__item { + display: flex; + transition: all ease 0.1s; + color: var(--color-muted); + width: 100%; + overflow: hidden; + border-radius: 4px; + padding: globals.$spacing-unit globals.$spacing-unit; + gap: globals.$spacing-unit * 2; + align-items: center; + text-align: left; + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + text-decoration: none; + } + + &-image { + width: 54px; + height: 54px; + border-radius: 4px; + object-fit: cover; + + &--locked { + filter: grayscale(100%); + } + } + + &-content { + flex: 1; + } + + &-title { + display: flex; + align-items: center; + gap: 4px; + } + + &-hidden-icon { + display: flex; + color: var(--color-warning); + opacity: 0.8; + + &:hover { + opacity: 1; + } + + svg { + width: 12px; + height: 12px; + } + } + + &-eye-closed { + width: 12px; + height: 12px; + color: globals.$warning-color; + scale: 4; + } + + &-meta { + display: flex; + flex-direction: column; + gap: 8px; + } + + &-points { + display: flex; + align-items: center; + gap: 4px; + margin-right: 4px; + font-weight: 600; + + &--locked { + cursor: pointer; + color: var(--color-warning); + } + + &-icon { + width: 18px; + height: 18px; + } + + &-value { + font-size: 1.1em; + } + } + + &-unlock-time { + white-space: nowrap; + gap: 4px; + display: flex; + } + + &-compared { + display: grid; + grid-template-columns: 3fr 1fr 1fr; + + &--no-owner { + grid-template-columns: 3fr 2fr; + } + } + + &-main { + display: flex; + flex-direction: row; + align-items: center; + gap: globals.$spacing-unit; + } + + &-status { + display: flex; + padding: globals.$spacing-unit; + justify-content: center; + + &--unlocked { + white-space: nowrap; + flex-direction: row; + gap: globals.$spacing-unit; + padding: 0; + } + } + } + + &__progress-bar { + width: 100%; + height: 8px; + transition: all ease 0.2s; + + &::-webkit-progress-bar { + background-color: rgba(255, 255, 255, 0.15); + border-radius: 4px; + } + + &::-webkit-progress-value { + background-color: var(--color-muted); + border-radius: 4px; + } + } + + &__profile-avatar { + height: 54px; + width: 54px; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + background-color: var(--color-background); + position: relative; + object-fit: cover; + + &--small { + height: 32px; + width: 32px; + } + } + + &__subscription-button { + text-decoration: none; + display: flex; + justify-content: center; + width: 100%; + gap: math.div(globals.$spacing-unit, 2); + color: var(--color-body); + cursor: pointer; + + &:hover { + text-decoration: underline; + } + } +} diff --git a/src/renderer/src/pages/downloads/download-group.css.ts b/src/renderer/src/pages/downloads/download-group.css.ts deleted file mode 100644 index cbbb4f8e..00000000 --- a/src/renderer/src/pages/downloads/download-group.css.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { style } from "@vanilla-extract/css"; - -import { SPACING_UNIT, vars } from "../../theme.css"; - -export const downloadTitleWrapper = style({ - display: "flex", - alignItems: "center", - marginBottom: `${SPACING_UNIT}px`, - gap: `${SPACING_UNIT}px`, -}); - -export const downloadTitle = style({ - fontWeight: "bold", - cursor: "pointer", - color: vars.color.body, - textAlign: "left", - fontSize: "16px", - display: "block", - ":hover": { - textDecoration: "underline", - }, -}); - -export const downloads = style({ - width: "100%", - gap: `${SPACING_UNIT * 2}px`, - display: "flex", - flexDirection: "column", - margin: "0", - padding: "0", - marginTop: `${SPACING_UNIT}px`, -}); - -export const downloadCover = style({ - width: "280px", - minWidth: "280px", - height: "auto", - borderRight: `solid 1px ${vars.color.border}`, - position: "relative", - zIndex: "1", -}); - -export const downloadCoverContent = style({ - width: "100%", - height: "100%", - padding: `${SPACING_UNIT}px`, - display: "flex", - alignItems: "flex-end", - justifyContent: "flex-end", -}); - -export const downloadCoverBackdrop = style({ - width: "100%", - height: "100%", - background: "linear-gradient(0deg, rgba(0, 0, 0, 0.8) 5%, transparent 100%)", - display: "flex", - overflow: "hidden", - zIndex: "1", -}); - -export const downloadCoverImage = style({ - width: "100%", - height: "100%", - position: "absolute", - zIndex: "-1", -}); - -export const download = style({ - width: "100%", - backgroundColor: vars.color.background, - display: "flex", - borderRadius: "8px", - border: `solid 1px ${vars.color.border}`, - overflow: "hidden", - boxShadow: "0px 0px 5px 0px #000000", - transition: "all ease 0.2s", - height: "140px", - minHeight: "140px", - maxHeight: "140px", -}); - -export const downloadDetails = style({ - display: "flex", - flexDirection: "column", - flex: "1", - justifyContent: "center", - gap: `${SPACING_UNIT / 2}px`, - fontSize: "14px", -}); - -export const downloadRightContent = style({ - display: "flex", - padding: `${SPACING_UNIT * 2}px`, - flex: "1", - gap: `${SPACING_UNIT}px`, - background: "linear-gradient(90deg, transparent 20%, rgb(0 0 0 / 20%) 100%)", -}); - -export const downloadActions = style({ - display: "flex", - alignItems: "center", - gap: `${SPACING_UNIT}px`, -}); - -export const downloadGroup = style({ - display: "flex", - flexDirection: "column", - gap: `${SPACING_UNIT * 2}px`, -}); diff --git a/src/renderer/src/pages/downloads/download-group.scss b/src/renderer/src/pages/downloads/download-group.scss new file mode 100644 index 00000000..2c5e9701 --- /dev/null +++ b/src/renderer/src/pages/downloads/download-group.scss @@ -0,0 +1,140 @@ +@use "../../scss/globals.scss"; + +.download-group { + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: calc(globals.$spacing-unit * 2); + + &-divider { + flex: 1; + background-color: globals.$border-color; + height: 1px; + } + + &-count { + font-weight: 400; + } + } + + &__title-wrapper { + display: flex; + align-items: center; + margin-bottom: globals.$spacing-unit; + gap: globals.$spacing-unit; + } + + &__title { + font-weight: bold; + cursor: pointer; + color: globals.$body-color; + text-align: left; + font-size: 16px; + display: block; + + &:hover { + text-decoration: underline; + } + } + + &__downloads { + width: 100%; + gap: calc(globals.$spacing-unit * 2); + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + margin-top: globals.$spacing-unit; + } + + &__item { + width: 100%; + background-color: globals.$background-color; + display: flex; + border-radius: 8px; + border: solid 1px globals.$border-color; + overflow: hidden; + box-shadow: 0px 0px 5px 0px #000000; + transition: all ease 0.2s; + height: 140px; + min-height: 140px; + max-height: 140px; + position: relative; + } + + &__cover { + width: 280px; + min-width: 280px; + height: auto; + border-right: solid 1px globals.$border-color; + position: relative; + z-index: 1; + + &-content { + width: 100%; + height: 100%; + padding: globals.$spacing-unit; + display: flex; + align-items: flex-end; + justify-content: flex-end; + } + + &-backdrop { + width: 100%; + height: 100%; + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.8) 5%, + transparent 100% + ); + display: flex; + overflow: hidden; + z-index: 1; + } + + &-image { + width: 100%; + height: 100%; + position: absolute; + z-index: -1; + } + } + + &__right-content { + display: flex; + padding: calc(globals.$spacing-unit * 2); + flex: 1; + gap: globals.$spacing-unit; + background: linear-gradient(90deg, transparent 20%, rgb(0 0 0 / 20%) 100%); + } + + &__details { + display: flex; + flex-direction: column; + flex: 1; + justify-content: center; + gap: calc(globals.$spacing-unit / 2); + font-size: 14px; + } + + &__actions { + display: flex; + align-items: center; + gap: globals.$spacing-unit; + } + + &__menu-button { + position: absolute; + top: 12px; + right: 12px; + border-radius: 50%; + border: none; + padding: 8px; + min-height: unset; + } +} diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 68794650..0bcbbdbe 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -12,9 +12,8 @@ import { Downloader, formatBytes, steamUrlBuilder } from "@shared"; import { DOWNLOADER_NAME } from "@renderer/constants"; import { useAppSelector, useDownload } from "@renderer/hooks"; -import * as styles from "./download-group.css"; +import "./download-group.scss"; import { useTranslation } from "react-i18next"; -import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { useMemo } from "react"; import { DropdownMenu, @@ -262,44 +261,26 @@ export function DownloadGroup({ if (!library.length) return null; return ( -
      -
      +
      +

      {title}

      - -
      -

      {library.length}

      +
      +

      {library.length}

      -
        +
          {library.map((game) => { return ( -
        • -
          -
          +
        • +
          +
          {game.title} -
          +
          {game.download?.downloader === Downloader.TorBox ? (
          -
          -
          -
          +
          +
          +
          )} - {achievements.slice(0, 4).map((achievement, index) => ( -
        • + {achievements.slice(0, 4).map((achievement) => ( +
        • {achievement.displayName} @@ -226,17 +223,17 @@ export function Sidebar() { {stats && ( -
          -
          -

          +

          +
          +

          {t("download_count")}

          {numberFormatter.format(stats?.downloadCount)}

          -
          -

          +

          +

          {t("player_count")}

          @@ -252,9 +249,9 @@ export function Sidebar() { /> -
          +
          { if (userPreferences) { setForm({ - preferQuitInsteadOfHiding: userPreferences.preferQuitInsteadOfHiding, - runAtStartup: userPreferences.runAtStartup, - startMinimized: userPreferences.startMinimized, - disableNsfwAlert: userPreferences.disableNsfwAlert, - seedAfterDownloadComplete: userPreferences.seedAfterDownloadComplete, + preferQuitInsteadOfHiding: + userPreferences.preferQuitInsteadOfHiding ?? false, + runAtStartup: userPreferences.runAtStartup ?? false, + startMinimized: userPreferences.startMinimized ?? false, + disableNsfwAlert: userPreferences.disableNsfwAlert ?? false, + seedAfterDownloadComplete: + userPreferences.seedAfterDownloadComplete ?? false, showHiddenAchievementsDescription: - userPreferences.showHiddenAchievementsDescription, + userPreferences.showHiddenAchievementsDescription ?? false, }); } }, [userPreferences]); diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index ba0411a5..52d47abf 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -65,18 +65,20 @@ export function SettingsGeneral() { (language) => language === userPreferences.language ) ?? languageKeys.find((language) => { - return language.startsWith(userPreferences.language.split("-")[0]); + return language.startsWith( + userPreferences.language?.split("-")[0] ?? "en" + ); }); setForm((prev) => ({ ...prev, downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath, downloadNotificationsEnabled: - userPreferences.downloadNotificationsEnabled, + userPreferences.downloadNotificationsEnabled ?? false, repackUpdatesNotificationsEnabled: - userPreferences.repackUpdatesNotificationsEnabled, + userPreferences.repackUpdatesNotificationsEnabled ?? false, achievementNotificationsEnabled: - userPreferences.achievementNotificationsEnabled, + userPreferences.achievementNotificationsEnabled ?? false, language: language ?? "en", })); } diff --git a/src/renderer/src/scss/globals.scss b/src/renderer/src/scss/globals.scss index 792abf86..12bad37c 100644 --- a/src/renderer/src/scss/globals.scss +++ b/src/renderer/src/scss/globals.scss @@ -16,6 +16,11 @@ $spacing-unit: 8px; $toast-z-index: 5; $bottom-panel-z-index: 3; -$title-bar-z-index: 1900000001; +$title-bar-z-index: 4; $backdrop-z-index: 4; $modal-z-index: 5; + +$body-font-size: 14px; +$small-font-size: 12px; + +$app-container: app-container; diff --git a/src/shared/index.ts b/src/shared/index.ts index 4d4b6af4..99a2cff6 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -39,7 +39,7 @@ export const pipe = fns.reduce((prev, fn) => fn(prev), arg); export const removeReleaseYearFromName = (name: string) => - name.replace(/\([0-9]{4}\)/g, ""); + name.replace(/\(\d{4}\)/g, ""); export const removeSymbolsFromName = (name: string) => name.replace(/[^A-Za-z 0-9]/g, ""); diff --git a/src/types/level.types.ts b/src/types/level.types.ts index 06fc79e3..28d8b0b6 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -66,16 +66,16 @@ export interface GameAchievement { } export interface UserPreferences { - downloadsPath: string | null; - language: string; - realDebridApiToken: string | null; - preferQuitInsteadOfHiding: boolean; - runAtStartup: boolean; - startMinimized: boolean; - disableNsfwAlert: boolean; - seedAfterDownloadComplete: boolean; - showHiddenAchievementsDescription: boolean; - downloadNotificationsEnabled: boolean; - repackUpdatesNotificationsEnabled: boolean; - achievementNotificationsEnabled: boolean; + downloadsPath?: string | null; + language?: string; + realDebridApiToken?: string | null; + preferQuitInsteadOfHiding?: boolean; + runAtStartup?: boolean; + startMinimized?: boolean; + disableNsfwAlert?: boolean; + seedAfterDownloadComplete?: boolean; + showHiddenAchievementsDescription?: boolean; + downloadNotificationsEnabled?: boolean; + repackUpdatesNotificationsEnabled?: boolean; + achievementNotificationsEnabled?: boolean; } diff --git a/tsconfig.web.json b/tsconfig.web.json index ca29bd89..6dc0c4ab 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -6,7 +6,9 @@ "src/renderer/src/**/*.tsx", "src/preload/*.d.ts", "src/locales/index.ts", - "src/shared/**/*" + "src/shared/**/*", + "src/stories/**/*", + ".storybook/**/*" ], "compilerOptions": { "composite": true,