diff --git a/binaries/aria2c.exe b/binaries/aria2c.exe index cf6411fe..5004e103 100755 Binary files a/binaries/aria2c.exe and b/binaries/aria2c.exe differ diff --git a/package.json b/package.json index 9bd5ba29..b16e792f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydralauncher", - "version": "3.4.10", + "version": "3.5.2", "description": "Hydra", "main": "./out/main/index.js", "author": "Los Broxas", @@ -56,20 +56,21 @@ "diskusage": "^1.2.0", "electron-log": "^5.2.4", "electron-updater": "^6.6.2", - "file-type": "^19.6.0", + "file-type": "^20.5.0", "i18next": "^23.11.2", "i18next-browser-languagedetector": "^7.2.1", "jsdom": "^24.0.0", "jsonwebtoken": "^9.0.2", "lodash-es": "^4.17.21", - "parse-torrent": "^11.0.17", - "rc-virtual-list": "^3.16.1", + "parse-torrent": "^11.0.18", + "rc-virtual-list": "^3.18.3", "react-hook-form": "^7.53.0", "react-i18next": "^14.1.0", "react-loading-skeleton": "^3.4.0", "react-redux": "^9.1.1", "react-router-dom": "^6.22.3", - "react-tooltip": "^5.28.0", + "react-shadow": "^20.6.0", + "react-tooltip": "^5.28.1", "sound-play": "^1.1.0", "steam-shortcut-editor": "https://github.com/hydralauncher/steam-shortcut-editor", "sudo-prompt": "^9.2.1", @@ -105,9 +106,9 @@ "@types/winreg": "^1.2.36", "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^4.2.1", - "electron": "^31.7.7", + "electron": "^32.3.3", "electron-builder": "^26.0.12", - "electron-vite": "^2.3.0", + "electron-vite": "^3.0.0", "eslint": "^8.56.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.4", @@ -121,5 +122,6 @@ "typescript": "^5.3.3", "vite": "^5.0.12", "vite-plugin-svgr": "^4.2.0" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/locales/bg/translation.json b/src/locales/bg/translation.json index 4d91aa92..458b9e36 100644 --- a/src/locales/bg/translation.json +++ b/src/locales/bg/translation.json @@ -1,88 +1,88 @@ { "language_name": "Български", "app": { - "successfully_signed_in": "Успешно вписване" + "successfully_signed_in": "Успешно влизане" }, "home": { "featured": "Препоръчани", "surprise_me": "Изненадай ме", - "no_results": "Не са намерени резултати", - "start_typing": "Търсене...", - "hot": "Актуално сега", - "weekly": "📅 Най-доброто от седмицата", - "achievements": "🏆 Игри, които да победите" + "no_results": "Няма намерени резултати", + "start_typing": "Започнете да пишете за търсене...", + "hot": "Горещи сега", + "weekly": "📅 Топ игри на седмицата", + "achievements": "🏆 Игри които да победите" }, "sidebar": { "catalogue": "Каталог", "downloads": "Изтегляния", "settings": "Настройки", "my_library": "Моята библиотека", - "downloading_metadata": "{{title}} (Сваляне на метаданни…)", - "paused": "{{title}} (Пауза)", + "downloading_metadata": "{{title}} (Изтегляне на метаданни…)", + "paused": "{{title}} (На пауза)", "downloading": "{{title}} ({{percentage}} - Изтегляне…)", - "filter": "Търсене по име", + "filter": "Филтрирай библиотеката", "home": "Начало", - "queued": "{{title}} (Опашка)", - "game_has_no_executable": "Играта няма избран изпълним файл", - "sign_in": "Вписване", + "queued": "{{title}} (В опашката)", + "game_has_no_executable": "Няма избран изпълним файл за играта", + "sign_in": "Вход", "friends": "Приятели", - "need_help": "Имате нужда от помощ??", - "favorites": "Любими игри" + "need_help": "Нужда от помощ?", + "favorites": "Любими" }, "header": { - "search": "Търсене", + "search": "Търси игри", "home": "Начало", "catalogue": "Каталог", "downloads": "Изтегляния", - "search_results": "Резултати от търсене", + "search_results": "Резултати от търсенето", "settings": "Настройки", - "version_available_install": "Версия {{version}} е налична. Кликни тук, за да рестартирате и инсталирате.", - "version_available_download": "Версия {{version}} е налична. Кликни тук за изтегляне." + "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}} завършено)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Инсталацията завършена", + "installation_complete_message": "Общите компоненти са инсталирани успешно" }, "catalogue": { - "search": "Филтър…", + "search": "Филтрирай…", "developers": "Разработчици", "genres": "Жанрове", "tags": "Тагове", "publishers": "Издатели", "download_sources": "Източници за изтегляне", - "result_count": "{{resultCount}} резултати", + "result_count": "{{resultCount}} резултата", "filter_count": "{{filterCount}} налични", "clear_filters": "Изчисти {{filterCount}} избрани" }, "game_details": { - "launch_options": "Опции за стартиране", - "launch_options_description": "Напредналите потребители могат да въведат модификации на своите опции за стартиране (экспериментальный)", - "launch_options_placeholder": "Няма зададен параметър", - "open_download_options": "Варианти за изтегляне", - "download_options_zero": "Няма варианти за изтегляне", - "download_options_one": "{{count}} варианти за изтегляне", - "download_options_other": "{{count}} варианти за изтегляне", + "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": "Филтрирай repacks", + "space_left_on_disk": "{{space}} свободно на диска", + "eta": "Завършване {{eta}}", + "calculating_eta": "Изчисляване на оставащо време…", + "downloading_metadata": "Изтегляне на метаданни…", + "filter": "Филтрирай репаковки", "requirements": "Системни изисквания", "minimum": "Минимални", "recommended": "Препоръчителни", - "paused": "Паузирано", - "release_date": "Издадено на {{date}}", - "publisher": "Публикувано от {{publisher}}", - "hours": "часове", + "paused": "На пауза", + "release_date": "Издадена на {{date}}", + "publisher": "Издател: {{publisher}}", + "hours": "часа", "minutes": "минути", "amount_hours": "{{amount}} часа", "amount_minutes": "{{amount}} минути", @@ -90,333 +90,425 @@ "add_to_library": "Добави в библиотеката", "remove_from_library": "Премахни от библиотеката", "no_downloads": "Няма налични изтегляния", - "play_time": "Игрално време {{amount}}", - "last_time_played": "Последно пускане {{period}}", - "not_played_yet": "Не сте играли {{title}} все още", + "play_time": "Играно: {{amount}}", + "last_time_played": "Последно играно: {{period}}", + "not_played_yet": "Все още не сте играли {{title}}", "next_suggestion": "Следващо предложение", - "play": "Пускане", - "deleting": "Изтриване на инсталация…", + "play": "Играй", + "deleting": "Изтриване на инсталатора…", "close": "Затвори", - "playing_now": "Играй сега", - "change": "Промяна", - "repacks_modal_description": "Избери repack който искаш да изтеглиш", - "select_folder_hint": "За да промените стандартната папка отидете в <0>Настройки", + "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": "Downloader", + "no_shop_details": "Неуспешно извличане на детайли от магазина.", + "download_options": "Опции за изтегляне", + "download_path": "Път за изтегляне", + "previous_screenshot": "Предишен скрийншот", + "next_screenshot": "Следващ скрийншот", + "screenshot": "Скрийншот {{number}}", + "open_screenshot": "Отвори скрийншот {{number}}", + "download_settings": "Настройки за изтегляне", + "downloader": "Изтегляч", "select_executable": "Избери", - "no_executable_selected": "Няма избран стартиращ файл", + "no_executable_selected": "Няма избран изпълним файл", "open_folder": "Отвори папка", - "open_download_location": "Виж свалените файлове", - "create_shortcut": "Пряк път на Десктопа", + "open_download_location": "Виж изтеглените файлове", + "create_shortcut": "Създай пряк път на работния плот", + "clear": "Изчисти", "remove_files": "Премахни файловете", - "remove_from_library_title": "Сигурен ли си?", - "remove_from_library_description": "Това ще премахне {{game}} от Библиотеката", + "remove_from_library_title": "Сигурни ли сте?", + "remove_from_library_description": "Това ще премахне {{game}} от вашата библиотека", "options": "Опции", - "executable_section_title": "Стартиращ файл", - "executable_section_description": "Пътят на файла, който ще се изпълни, когато се щракне върху \"Пускане\"", - "downloads_section_title": "Свалени", - "downloads_section_description": "Вижте актуализации или други версии на тази игра", + "executable_section_title": "Изпълним файл", + "executable_section_description": "Пътят на файла, който ще се изпълни при \"Играй\"", + "downloads_section_title": "Изтегляния", + "downloads_section_description": "Вижте обновления или други версии на тази игра", "danger_zone_section_title": "Опасна зона", - "danger_zone_section_description": "Премахнете тази игра от библиотеката си или от файловете, изтеглени от Hydra", - "download_in_progress": "Изтегляне в ход", - "download_paused": "Изтеглянето е паузирано", - "last_downloaded_option": "Опция от последно изтегляне", + "danger_zone_section_description": "Премахнете тази игра от библиотеката или файловете, изтеглени от Hydra", + "download_in_progress": "Изтеглянето е в ход", + "download_paused": "Изтеглянето е на пауза", + "last_downloaded_option": "Последно изтеглена опция", + "create_steam_shortcut": "Създай пряк път за Steam", "create_shortcut_success": "Прекият път е създаден успешно", - "create_shortcut_error": "Грешка при създаването на пряк път", + "you_might_need_to_restart_steam": "Може да е необходимо да рестартирате Steam, за да видите промените", + "create_shortcut_error": "Грешка при създаване на пряк път", "nsfw_content_title": "Тази игра съдържа неподходящо съдържание", "nsfw_content_description": "{{title}} съдържа съдържание, което може да не е подходящо за всички възрасти. Сигурни ли сте, че искате да продължите?", "allow_nsfw_content": "Продължи", - "refuse_nsfw_content": "Назад", + "refuse_nsfw_content": "Върни се", "stats": "Статистики", - "download_count": "Сваляния", + "download_count": "Изтегляния", "player_count": "Активни играчи", "download_error": "Тази опция за изтегляне не е налична", - "download": "Свали", + "download": "Изтегли", "executable_path_in_use": "Изпълнимият файл вече се използва от \"{{game}}\"", "warning": "Внимание:", - "hydra_needs_to_remain_open": "за това изтегляне, Hydra трябва да остане отворена, когато е завършено. Ако Hydra се затвори преди завършването, ще загубите напредъка си..", + "hydra_needs_to_remain_open": "за това изтегляне, Hydra трябва да остане отворена до завършване. Ако затворите преди завършване, ще загубите прогреса.", "achievements": "Постижения", "achievements_count": "Постижения {{unlockedCount}}/{{achievementsCount}}", - "cloud_save": "Запазване в облака", - "cloud_save_description": "Запазете напредъка си в облака и продължете да играете на всяко устройство", - "backups": "Резервни копия", + "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": "Вижте всички постижения", + "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": "Управление на файлове", + "mapping_method_label": "Метод на съпоставяне", + "files_automatically_mapped": "Файловете са съпоставени автоматично", + "no_backups_created": "Няма създадени архиви за тази игра", + "manage_files": "Управлявай файлове", "loading_save_preview": "Търсене на запазени игри…", - "wine_prefix": "Wine Префикс", - "wine_prefix_description": "Wine prefix използван за тази игра", - "no_download_option_info": "Няма налични данни", - "backup_deletion_failed": "Неуспешно изтриване на резервно копие", - "max_number_of_artifacts_reached": "Достигнат максимален брой резервни копия за тази игра", - "achievements_not_sync": "Постиженията не са синхронизирани", - "manage_files_description": "Управлявайте кои файлове ще бъдат архивирани и възстановени", + "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": "Задаване на персонализирано местоположение за архивиране" + "backup_from": "Архив от {{date}}", + "automatic_backup_from": "Автоматичен архив от {{date}}", + "enable_automatic_cloud_sync": "Включи автоматична синхронизация с облака", + "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": "Неуспешно нулиране на постиженията", + "download_error_gofile_quota_exceeded": "Превишихте месечната си квота в Gofile. Моля, изчакайте тя да се възстанови.", + "download_error_real_debrid_account_not_authorized": "Вашият Real-Debrid акаунт не е упълномощен за нови изтегляния. Моля, проверете настройките на акаунта и опитайте отново.", + "download_error_not_cached_on_real_debrid": "Това изтегляне не е налично в Real-Debrid и не може да се следи статуса.", + "download_error_not_cached_on_torbox": "Това изтегляне не е налично в TorBox и не може да се следи статуса.", + "download_error_not_cached_on_hydra": "Това изтегляне не е налично в Nimbus.", + "game_removed_from_favorites": "Играта е премахната от любими", + "game_added_to_favorites": "Играта е добавена в любими", + "automatically_extract_downloaded_files": "Автоматично извличане на изтеглени файлове", + "create_start_menu_shortcut": "Създай пряк път в старт менюто", + "invalid_wine_prefix_path": "Невалиден път до Wine префикса", + "invalid_wine_prefix_path_description": "Пътят до Wine префикса е невалиден. Моля, проверете го и опитайте отново.", + "missing_wine_prefix": "Wine префикс е необходим за създаване на архив в Linux" }, "activation": { "title": "Активирай Hydra", - "installation_id": "Идентификатор на инсталацията:", - "enter_activation_code": "Въведете кода за активиране", - "message": "Ако не знаете къде да попитате за това, значи не трябва да го имате..", + "installation_id": "Инсталационен ID:", + "enter_activation_code": "Въведете активационен код", + "message": "Ако не знаете къде да попитате за това, не бива да го имате.", "activate": "Активирай", "loading": "Зареждане…" }, "downloads": { - "seeding": "Сийдване", - "stop_seeding": "Спри сийдването", - "resume_seeding": "Продължи сийдването", - "options": "Управление", "resume": "Продължи", "pause": "Пауза", - "eta": "Conclusion {{eta}}", - "paused": "Паузирано", + "eta": "Завършване {{eta}}", + "paused": "На пауза", "verifying": "Проверка…", - "completed": "Готово", - "removed": "Не е изтеглен", + "completed": "Завършено", + "removed": "Не е изтеглено", "cancel": "Отказ", - "filter": "Филтриране на изтеглени игри", + "filter": "Филтрирай изтеглените игри", "remove": "Премахни", "downloading_metadata": "Изтегляне на метаданни…", "deleting": "Изтриване на инсталатора…", - "delete": "Премахване на инсталатора", - "delete_modal_title": "Сигурени ли сте?", - "delete_modal_description": "Това ще премахне всички инсталационни файлове от компютъра ви.", + "delete": "Премахни инсталатора", + "delete_modal_title": "Сигурни ли сте?", + "delete_modal_description": "Това ще премахне всички инсталационни файлове от компютъра ви", "install": "Инсталирай", - "download_in_progress": "В процес на изпълнение", - "queued_downloads": "Изтеглени файлове в опашката", - "downloads_completed": "Приключени", - "queued": "В опашка", + "download_in_progress": "В процес", + "queued_downloads": "Изтегляния на опашка", + "downloads_completed": "Завършени", + "queued": "В опашката", "no_downloads_title": "Толкова е празно", - "no_downloads_description": "Все още не сте изтеглили нищо с Hydra, но никога не е късно да започнете...", - "checking_files": "Проверка на файлове…" + "no_downloads_description": "Все още не сте изтеглили нищо с Hydra, но никога не е късно да започнете.", + "checking_files": "Проверка на файлове…", + "seeding": "Сийдване", + "stop_seeding": "Спри сийдването", + "resume_seeding": "Продължи сийдването", + "options": "Управлявай", + "extract": "Извлечи файловете", + "extracting": "Извличане на файловете…" }, "settings": { - "seed_after_download_complete": "Сийд след завършване на изтеглянето", - "show_hidden_achievement_description": "Показвай описанието на скритите постижения преди отключването им", - "downloads_path": "Инсталационен път", - "change": "Актуализиране", + "downloads_path": "Път за изтегляния", + "change": "Обнови", "notifications": "Известия", - "enable_download_notifications": "Когато изтеглянето е завършено", - "enable_repack_list_notifications": "Когато се добави нов repack", + "enable_download_notifications": "Когато изтеглянето приключи", + "enable_repack_list_notifications": "Когато бъде добавен нов репак", "real_debrid_api_token_label": "Real-Debrid API токен", - "quit_app_instead_hiding": "Не скривайте Hydra при затваряне", - "launch_with_system": "Стартиране на Hydra при стартиране на системата", + "quit_app_instead_hiding": "Не скривай Hydra при затваряне", + "launch_with_system": "Стартирай Hydra при стартиране на системата", "general": "Общи", "behavior": "Поведение", "download_sources": "Източници за изтегляне", "language": "Език", - "api_token": "API Токен", + "api_token": "API токен", "enable_real_debrid": "Включи Real-Debrid", - "real_debrid_description": "Real-Debrid е неограничен даунлоудър, който ви позволява бързо да изтегляте файлове, ограничени само от скоростта на интернет..", + "real_debrid_description": "Real-Debrid е неограничен изтегляч, който ви позволява да теглите бързо, ограничено само от интернет връзката ви.", "debrid_invalid_token": "Невалиден API токен", - "debrid_api_token_hint": "Вземете своя API токен <0>тук", - "real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен акаунт. Моля абонирай се за Real-Debrid", - "debrid_linked_message": "Акаунтът \"{{username}}\" е свързан", + "debrid_api_token_hint": "Може да получите вашия API токен <0>тук", + "real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен. Моля, абонирайте се за Real-Debrid", + "debrid_linked_message": "Акаунт \"{{username}}\" е свързан", "save_changes": "Запази промените", - "changes_saved": "Промените са успешно запазни", - "download_sources_description": "Hydra ще извлича връзките за изтегляне от тези източници. URL адресът на източника трябва да е директна връзка към .json файл, съдържащ връзките за изтегляне.", - "validate_download_source": "Валидиране", + "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": "Източника за сваляне е премахнат", - "cancel_button_confirmation_delete_all_sources": "не", - "confirm_button_confirmation_delete_all_sources": "Да, удалить все", - "description_confirmation_delete_all_sources": "Вы удалите все источники загрузки", - "title_confirmation_delete_all_sources": "Удалить все источники загрузки", - "removed_download_sources": "Шрифты удалены", - "button_delete_all_sources": "Удалить все источники загрузки", - "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": "Личен", + "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": "Източникът е премахнат", + "removed_download_sources": "Източниците са премахнати", + "cancel_button_confirmation_delete_all_sources": "Не", + "confirm_button_confirmation_delete_all_sources": "Да, изтрий всичко", + "title_confirmation_delete_all_sources": "Изтрий всички източници", + "description_confirmation_delete_all_sources": "Ще изтриете всички източници", + "button_delete_all_sources": "Премахни всички", + "added_download_source": "Източникът е добавен", + "download_sources_synced": "Всички източници са синхронизирани", + "insert_valid_json_url": "Въведете валиден JSON url", + "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 адрес.", + "must_be_valid_url": "Източникът трябва да е валиден URL", "blocked_users": "Блокирани потребители", - "user_unblocked": "Потребителят е бил деблокиран", - "enable_achievement_notifications": "Когато е отключено постижение", - "launch_minimized": "Стартиране на Hydra минимизирано", - "disable_nsfw_alert": "Деактивиране на предупреждението NSFW" + "user_unblocked": "Потребителят е деблокиран", + "enable_achievement_notifications": "Когато бъде отключено постижение", + "launch_minimized": "Стартирай Hydra минимизирано", + "disable_nsfw_alert": "Изключи NSFW предупреждението", + "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": "Следващата фактура ще бъде изпратена до този ден", + "no_themes": "Изглежда, че все още нямате теми. Кликнете тук, за да създадете първата си.", + "editor_tab_code": "Код", + "editor_tab_info": "Информация", + "editor_tab_save": "Запази", + "web_store": "Уеб магазин", + "clear_themes": "Изчисти", + "create_theme": "Създай", + "create_theme_modal_title": "Създай персонализирана тема", + "create_theme_modal_description": "Създайте нова тема за персонализиране на външния вид на Hydra", + "theme_name": "Име", + "insert_theme_name": "Въведете име на тема", + "set_theme": "Задай тема", + "unset_theme": "Премахни тема", + "delete_theme": "Изтрий тема", + "edit_theme": "Редактирай тема", + "delete_all_themes": "Изтрий всички теми", + "delete_all_themes_description": "Това ще изтрие всички ваши персонализирани теми", + "delete_theme_description": "Това ще изтрие темата {{theme}}", + "cancel": "Отказ", + "appearance": "Външен вид", + "enable_torbox": "Включи TorBox", + "torbox_description": "TorBox е вашият премиум seedbox, съперничещ на най-добрите сървъри на пазара.", + "torbox_account_linked": "TorBox акаунтът е свързан", + "create_real_debrid_account": "Кликнете тук, ако все още нямате Real-Debrid акаунт", + "create_torbox_account": "Кликнете тук, ако все още нямате TorBox акаунт", + "real_debrid_account_linked": "Real-Debrid акаунтът е свързан", + "name_min_length": "Името на темата трябва да е поне 3 символа", + "import_theme": "Импортирай тема", + "import_theme_description": "Ще импортирате {{theme}} от магазина с теми", + "error_importing_theme": "Грешка при импортиране на тема", + "theme_imported": "Темата е импортирана успешно", + "enable_friend_request_notifications": "Когато получите заявка за приятелство", + "enable_auto_install": "Автоматично изтегляй обновления", + "common_redist": "Общи компоненти", + "common_redist_description": "Общите компоненти са нужни за някои игри. Препоръчва се инсталация.", + "install_common_redist": "Инсталирай", + "installing_common_redist": "Инсталиране…", + "show_download_speed_in_megabytes": "Показвай скоростта на изтегляне в MB/s", + "extract_files_by_default": "Извличай файловете по подразбиране след изтегляне" }, "notifications": { - "download_complete": "Изтеглянето е завършено", - "game_ready_to_install": "{{title}} е готово за инсталиране", - "repack_list_updated": "Repack лист е обновен", - "repack_count_one": "{{count}} repack е добавен", - "repack_count_other": "{{count}} repacks добавени", - "new_update_available": "Версия {{version}} е налична", - "restart_to_install_update": "Рестартирайте Hydra, за да инсталирате актуализацията", + "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}} са отклщчени" + "notification_achievement_unlocked_body": "{{achievement}} и още {{count}} бяха отключени", + "new_friend_request_description": "{{displayName}} ви изпрати заявка за приятелство", + "new_friend_request_title": "Нова заявка за приятелство", + "extraction_complete": "Извличането завърши", + "game_extracted": "{{title}} е извлечена успешно", + "friend_started_playing_game": "{{displayName}} започна да играе игра" }, "system_tray": { "open": "Отвори Hydra", "quit": "Изход" }, "game_card": { + "available_one": "Налично", + "available_other": "Налично", "no_downloads": "Няма налични изтегляния" }, "binary_not_found_modal": { - "title": "Не инсталирани програми", - "description": "Wine или Lutris изпълними файлове не бяха открити на вашата система", - "instructions": "Проверете правилния начин за инсталиране на някоя от тях на вашата дистрибуция на Linux, за да може играта да работи нормално" + "title": "Програмите не са инсталирани", + "description": "Wine или Lutris не са открити на вашата система", + "instructions": "Проверете как да инсталирате някоя от тях за вашата Linux дистрибуция, за да може играта да работи." }, "modal": { "close": "Бутон за затваряне" }, "forms": { - "toggle_password_visibility": "Превключване на видимостта на паролата" + "toggle_password_visibility": "Показване/скриване на паролата" }, "user_profile": { - "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_hours": "{{amount}} часа", "amount_minutes": "{{amount}} минути", - "last_time_played": "Последно играно {{period}}", - "activity": "Скорошна активност", + "last_time_played": "Последно играно: {{period}}", + "activity": "Последна активност", "library": "Библиотека", "total_play_time": "Общо време за игра", - "no_recent_activity_title": "Хмм… няма нищо тук", - "no_recent_activity_description": "Не сте играли игри напоследък. Време е да промените това.!", - "display_name": "Показване на името", + "no_recent_activity_title": "Хммм… няма нищо тук", + "no_recent_activity_description": "Не сте играли игри наскоро. Време е да го промените!", + "display_name": "Показвано име", "saving": "Запазване", - "save": "Запис", - "edit_profile": "Редактиране на профила", - "saved_successfully": "Запазено успешно", - "try_again": "Моля, опитайте пак", + "save": "Запази", + "edit_profile": "Редактирай профил", + "saved_successfully": "Успешно запазено", + "try_again": "Моля, опитайте отново", "sign_out_modal_title": "Сигурни ли сте?", "cancel": "Отказ", - "successfully_signed_out": "Успешно се отписахте", - "sign_out": "Отписване", - "playing_for": "В игра от {{amount}}", - "sign_out_modal_text": "Вашата библиотека е свързана с текущата ви сметка. Когато се отпишете, библиотеката ви вече няма да е видима и напредъкът няма да бъде запазен. Продължете с отписването?", + "successfully_signed_out": "Успешно излязохте", + "sign_out": "Изход", + "playing_for": "Играе се от {{amount}}", + "sign_out_modal_text": "Библиотеката ви е свързана с този акаунт. При изход, тя няма да е видима, а прогресът няма да се запази. Продължавате ли?", "add_friends": "Добави приятели", "add": "Добави", - "friend_code": "Приятелски код", + "friend_code": "Код за приятелство", "see_profile": "Виж профила", "sending": "Изпращане", - "friend_request_sent": "Изпратена покана за приятелство", + "friend_request_sent": "Заявката е изпратена", "friends": "Приятели", "friends_list": "Списък с приятели", - "user_not_found": "Не е намерен потребител", + "user_not_found": "Потребителят не е намерен", "block_user": "Блокирай потребител", "add_friend": "Добави приятел", - "request_sent": "Изпратена покана", - "request_received": "Получена покана", - "accept_request": "Приеми поканата", - "ignore_request": "Игнирирай поканата", - "cancel_request": "Откажи поканата", - "undo_friendship": "Отмяна на приятелството", - "request_accepted": "Поканата е приета", + "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": "Не сте добавили приятели", + "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": "Грешка при обработката на изображението", + "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 символа.", + "displayname_min_length": "Показваното име трябва да съдържа поне 3 символа", + "displayname_max_length": "Показваното име трябва да съдържа най-много 50 символа", "report_profile": "Докладвай този профил", "report_reason": "Защо докладвате този профил?", "report_description": "Допълнителна информация", "report_description_placeholder": "Допълнителна информация", "report": "Докладвай", - "report_reason_hate": "Омразна реч", + "report_reason_hate": "Реч на омразата", "report_reason_sexual_content": "Сексуално съдържание", - "report_reason_violence": "Насилия", + "report_reason_violence": "Насилие", "report_reason_spam": "Спам", "report_reason_other": "Друго", "profile_reported": "Профилът е докладван", - "your_friend_code": "Вашия приятелски код:", + "your_friend_code": "Вашият код за приятелство:", "upload_banner": "Качи банер", - "uploading_banner": "Качване на банер…", - "background_image_updated": "Обновено фоново изображение" + "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}} точки с това постижение", + "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}}" + "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": "Анимирани профилни банери", "hydra_cloud": "Hydra Cloud", "hydra_cloud_feature_found": "Открихте функция на Hydra Cloud!", "learn_more": "Научете повече", - "subscription_tour_title": "Hydra Cloud Абонамент", - "subscribe_now": "Абонирай се сега", - "cloud_saving": "Запазване в облака", - "cloud_achievements": "Запазете постиженията си в облака", - "animated_profile_picture": "Анимирана профилна снимка", - "premium_support": "Премиум поддръжка", - "show_and_compare_achievements": "Показвайте и сравнявайте постиженията си с тези на други потребители", - "animated_profile_banner": "Анимиран профилен банер" + "debrid_description": "Изтегляйте до 4 пъти по-бързо с Nimbus" } } diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index dad29fa8..2b1fb9f3 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -21,7 +21,8 @@ "queued": "{{title}} (In Warteschlange)", "game_has_no_executable": "Spiel hat keine ausführbare Datei gewählt", "sign_in": "Anmelden", - "favorites": "Favoriten" + "favorites": "Favoriten", + "playable_button_title": "Nur Spiele anzeigen, die du jetzt spielen kannst" }, "header": { "search": "Spiele suchen", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 2902218e..ee4254e2 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -27,7 +27,8 @@ "sign_in": "Sign in", "friends": "Friends", "need_help": "Need help?", - "favorites": "Favorites" + "favorites": "Favorites", + "playable_button_title": "Show only games you can play now" }, "header": { "search": "Search games", @@ -363,7 +364,24 @@ "install_common_redist": "Install", "installing_common_redist": "Installing…", "show_download_speed_in_megabytes": "Show download speed in megabytes per second", - "extract_files_by_default": "Extract files by default after download" + "extract_files_by_default": "Extract files by default after download", + "achievement_custom_notification_position": "Achievement custom notification position", + "top-left": "Top left", + "top-center": "Top center", + "top-right": "Top right", + "bottom-left": "Bottom left", + "bottom-center": "Bottom center", + "bottom-right": "Bottom right", + "enable_achievement_custom_notifications": "Enable achievement custom notifications", + "alignment": "Alignment", + "variation": "Variation", + "default": "Default", + "rare": "Rare", + "platinum": "Platinum", + "hidden": "Hidden", + "test_notification": "Test notification", + "notification_preview": "Achievement Notification Preview", + "enable_friend_start_game_notifications": "When a friend starts playing a game" }, "notifications": { "download_complete": "Download complete", @@ -379,7 +397,9 @@ "new_friend_request_title": "New friend request", "extraction_complete": "Extraction complete", "game_extracted": "{{title}} extracted successfully", - "friend_started_playing_game": "{{displayName}} started playing a game" + "friend_started_playing_game": "{{displayName}} started playing a game", + "test_achievement_notification_title": "This is a test notification", + "test_achievement_notification_description": "Pretty cool, huh?" }, "system_tray": { "open": "Open Hydra", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 716f7b33..f91a9809 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -27,7 +27,8 @@ "sign_in": "Iniciar sesión", "friends": "Amigos", "need_help": "¿Necesitas ayuda?", - "favorites": "Favoritos" + "favorites": "Favoritos", + "playable_button_title": "Mostrar solo juegos que puedes jugar ahora" }, "header": { "search": "Buscar juegos", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index aab6e019..1c129a64 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -27,7 +27,8 @@ "sign_in": "Se connecter", "friends": "Amis", "need_help": "Besoin d'aide ?", - "favorites": "Favoris" + "favorites": "Favoris", + "playable_button_title": "Afficher uniquement les jeux que vous pouvez jouer maintenant" }, "header": { "search": "Rechercher", @@ -356,7 +357,17 @@ "common_redist_description": "Certains jeux nécessitent les redistribuables communs. L'installation est recommandée.", "install_common_redist": "Installer", "installing_common_redist": "Installation…", - "show_download_speed_in_megabytes": "Afficher la vitesse de téléchargement en mégaoctets par seconde" + "show_download_speed_in_megabytes": "Afficher la vitesse de téléchargement en mégaoctets par seconde", + "extract_files_by_default": "Extraire les fichiers par défaut après le téléchargement", + "enable_achievement_custom_notifications": "Activer les notifications personnalisées de succès", + "achievement_custom_notification_position": "Position de la notification personnalisée de succès", + "top-left": "En haut à gauche", + "top-center": "En haut au centre", + "top-right": "En haut à droite", + "bottom-left": "En bas à gauche", + "bottom-center": "En bas au centre", + "bottom-right": "En bas à droite", + "enable_friend_start_game_notifications": "Quand un ami commence à jouer à un jeu" }, "notifications": { "download_complete": "Téléchargement terminé", diff --git a/src/locales/index.ts b/src/locales/index.ts index 24ab202c..f71e8f0e 100644 --- a/src/locales/index.ts +++ b/src/locales/index.ts @@ -26,6 +26,7 @@ import nb from "./nb/translation.json"; import et from "./et/translation.json"; import bg from "./bg/translation.json"; import uz from "./uz/translation.json"; +import sv from "./sv/translation.json"; export default { "pt-BR": ptBR, @@ -56,4 +57,5 @@ export default { nb, et, uz, + sv, }; diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index 5e69a733..b23d1244 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -15,7 +15,8 @@ "downloading": "{{title}} ({{percentage}} - Download…)", "filter": "Filtra libreria", "home": "Home", - "favorites": "Preferiti" + "favorites": "Preferiti", + "playable_button_title": "Mostra solo i giochi che puoi giocare ora" }, "header": { "search": "Cerca", diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json index 1d55099e..86751b0e 100644 --- a/src/locales/pl/translation.json +++ b/src/locales/pl/translation.json @@ -13,9 +13,10 @@ "downloading_metadata": "{{title}} (Pobieranie metadata…)", "paused": "{{title}} (Zatrzymano)", "downloading": "{{title}} ({{percentage}} - Pobieranie…)", - "filter": "Filtruj biblioteke", + "filter": "Filtruj bibliotekę", "home": "Główna", - "favorites": "Ulubione" + "favorites": "Ulubione", + "playable_button_title": "Pokaż tylko gry, w które możesz grać teraz" }, "header": { "search": "Szukaj", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 5509e07b..6b9546ec 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -349,7 +349,24 @@ "install_common_redist": "Instalar", "installing_common_redist": "Instalando…", "show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo", - "extract_files_by_default": "Extrair arquivos automaticamente após o download" + "extract_files_by_default": "Extrair arquivos automaticamente após o download", + "enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas", + "top-left": "Superior esquerdo", + "top-center": "Superior central", + "top-right": "Superior direito", + "bottom-left": "Inferior esquerdo", + "bottom-right": "Inferior direito", + "bottom-center": "Inferior central", + "achievement_custom_notification_position": "Posição das notificações customizadas de conquista", + "alignment": "Alinhamento", + "variation": "Variação", + "default": "Padrão", + "rare": "Rara", + "platinum": "Platina", + "hidden": "Oculta", + "test_notification": "Testar notificação", + "notification_preview": "Prévia da Notificação de Conquistas", + "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" }, "notifications": { "download_complete": "Download concluído", @@ -363,7 +380,9 @@ "new_friend_request_description": "{{displayName}} te enviou um pedido de amizade", "extraction_complete": "Extração concluída", "game_extracted": "{{title}} extraído com sucesso", - "friend_started_playing_game": "{{displayName}} começou a jogar" + "friend_started_playing_game": "{{displayName}} começou a jogar", + "test_achievement_notification_title": "Esta é uma notificação de teste", + "test_achievement_notification_description": "Bem legal, né?" }, "system_tray": { "open": "Abrir Hydra", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 00b7e5af..7724e10d 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -27,7 +27,8 @@ "sign_in": "Войти", "friends": "Друзья", "need_help": "Нужна помощь?", - "favorites": "Избранное" + "favorites": "Избранное", + "playable_button_title": "Показать только игры, в которые можно играть сейчас" }, "header": { "search": "Поиск", @@ -197,7 +198,14 @@ "download_error_not_cached_on_torbox": "Эта загрузка недоступна на TorBox, и получить статус загрузки с TorBox пока невозможно.", "game_added_to_favorites": "Игра добавлена в избранное", "game_removed_from_favorites": "Игра удалена из избранного", - "automatically_extract_downloaded_files": "Автоматическая распаковка загруженных файлов" + "automatically_extract_downloaded_files": "Автоматическая распаковка загруженных файлов", + "create_steam_shortcut": "Создать ярлык Steam", + "you_might_need_to_restart_steam": "Возможно, вам потребуется перезапустить Steam, чтобы увидеть изменения", + "create_start_menu_shortcut": "Создать ярлык в меню «Пуск»", + "invalid_wine_prefix_path": "Недопустимый путь префикса Wine", + "invalid_wine_prefix_path_description": "Путь к префиксу Wine недействителен. Пожалуйста, проверьте путь и попробуйте снова.", + "missing_wine_prefix": "Префикс Wine необходим для создания резервной копии в Linux", + "download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus." }, "activation": { "title": "Активировать Hydra", @@ -355,7 +363,25 @@ "common_redist_description": "Для запуска некоторых игр требуются библиотеки. Во избежание проблем рекомендуется установить их.", "install_common_redist": "Установить", "installing_common_redist": "Установка…", - "show_download_speed_in_megabytes": "Показать скорость загрузки в мегабайтах в секунду" + "show_download_speed_in_megabytes": "Показать скорость загрузки в мегабайтах в секунду", + "extract_files_by_default": "Извлекать файлы по умолчанию после загрузки", + "achievement_custom_notification_position": "Позиция настраиваемых уведомлений о достижениях", + "top-left": "Верхний левый угол", + "top-center": "Верхний центр", + "top-right": "Верхний правый угол", + "bottom-left": "Нижний левый угол", + "bottom-center": "Нижний центр", + "bottom-right": "Нижний правый угол", + "enable_achievement_custom_notifications": "Включить настраиваемые уведомления о достижениях", + "alignment": "Выравнивание", + "variation": "Вариация", + "default": "По умолчанию", + "rare": "Редкое", + "platinum": "Платиновый", + "hidden": "Скрытый", + "test_notification": "Тестовое уведомление", + "notification_preview": "Предварительный просмотр уведомления о достижении", + "enable_friend_start_game_notifications": "Когда друг начинает играть в игру" }, "notifications": { "download_complete": "Загрузка завершена", @@ -370,7 +396,10 @@ "new_friend_request_title": "Новый запрос на добавление в друзья", "new_friend_request_description": "Вы получили новый запрос на добавление в друзья", "extraction_complete": "Распаковка завершена", - "game_extracted": "{{title}} успешно распакован" + "game_extracted": "{{title}} успешно распакован", + "friend_started_playing_game": "{{displayName}} начал играть в игру", + "test_achievement_notification_title": "Это тестовое уведомление", + "test_achievement_notification_description": "Довольно круто, да?" }, "system_tray": { "open": "Открыть Hydra", diff --git a/src/locales/sv/translation.json b/src/locales/sv/translation.json new file mode 100644 index 00000000..0972effa --- /dev/null +++ b/src/locales/sv/translation.json @@ -0,0 +1,533 @@ +{ + "language_name": "Svenska", + "app": { + "successfully_signed_in": "Inloggningen lyckades" + }, + "home": { + "featured": "Utvalt", + "surprise_me": "Överraska mig", + "no_results": "Inga resultat hittades", + "start_typing": "Börja skriva för att söka...", + "hot": "Hetast just nu", + "weekly": "📅 Veckans topplista", + "achievements": "🏆 Spel att klara av" + }, + "sidebar": { + "catalogue": "Katalog", + "downloads": "Nedladdningar", + "settings": "Inställningar", + "my_library": "Mitt bibliotek", + "downloading_metadata": "{{title}} (Hämtar metadata…)", + "paused": "{{title}} (Pausad)", + "downloading": "{{title}} ({{percentage}} - Hämtar…)", + "filter": "Filtrera bibliotek", + "home": "Hem", + "queued": "{{title}} (I kö)", + "game_has_no_executable": "Spelet har ingen vald körbar fil", + "sign_in": "Logga in", + "friends": "Vänner", + "need_help": "Behöver du hjälp?", + "favorites": "Favoriter" + }, + "header": { + "search": "Sök spel", + "home": "Hem", + "catalogue": "Katalog", + "downloads": "Nedladdningar", + "search_results": "Sökresultat", + "settings": "Inställningar", + "version_available_install": "Version {{version}} är tillgänglig. Klicka här för att starta om och installera.", + "version_available_download": "Version {{version}} är tillgänglig. Klicka här för att ladda ner." + }, + "bottom_panel": { + "no_downloads_in_progress": "Inga nedladdningar pågår", + "downloading_metadata": "Laddar ner metadata för {{title}}…", + "downloading": "Laddar ner {{title}}… ({{percentage}} klart) - Klart om {{eta}} - {{speed}}", + "calculating_eta": "Laddar ner {{title}}… ({{percentage}} klart) - Beräknar återstående tid…", + "checking_files": "Kontrollerar filer för {{title}}… ({{percentage}} klart)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Installation klar", + "installation_complete_message": "Nödvändiga systemkomponenter installerade framgångsrikt" + }, + "catalogue": { + "search": "Filter…", + "developers": "Utvecklare", + "genres": "Genrer", + "tags": "Taggar", + "publishers": "Utgivare", + "download_sources": "Nedladdningskällor", + "result_count": "{{resultCount}} resultat", + "filter_count": "{{filterCount}} tillgängliga", + "clear_filters": "Rensa {{filterCount}} valda" + }, + "game_details": { + "open_download_options": "Öppna nedladdningsalternativ", + "download_options_zero": "Inget nedladdningsalternativ", + "download_options_one": "{{count}} nedladdningsalternativ", + "download_options_other": "{{count}} nedladdningsalternativ", + "updated_at": "Uppdaterad {{updated_at}}", + "install": "Installera", + "resume": "Återuppta", + "pause": "Pausa", + "cancel": "Avbryt", + "remove": "Ta bort", + "space_left_on_disk": "{{space}} ledigt på disken", + "eta": "Klart om {{eta}}", + "calculating_eta": "Beräknar återstående tid…", + "downloading_metadata": "Laddar ner metadata…", + "filter": "Filtrera repacks", + "requirements": "Systemkrav", + "minimum": "Minimum", + "recommended": "Rekommenderat", + "paused": "Pausat", + "release_date": "Släpptes den {{date}}", + "publisher": "Utgiven av {{publisher}}", + "hours": "timmar", + "minutes": "minuter", + "amount_hours": "{{amount}} timmar", + "amount_minutes": "{{amount}} minuter", + "accuracy": "{{accuracy}}% träffsäkerhet", + "add_to_library": "Lägg till i biblioteket", + "remove_from_library": "Ta bort från biblioteket", + "no_downloads": "Inga nedladdningar tillgängliga", + "play_time": "Spelad i {{amount}}", + "last_time_played": "Senast spelad {{period}}", + "not_played_yet": "Du har inte spelat {{title}} än", + "next_suggestion": "Nästa förslag", + "play": "Spela", + "deleting": "Tar bort installationsfil…", + "close": "Stäng", + "playing_now": "Spelar nu", + "change": "Byt", + "repacks_modal_description": "Välj den repack du vill ladda ner", + "select_folder_hint": "För att ändra standardmappen, gå till <0>Inställningar", + "download_now": "Ladda ner nu", + "no_shop_details": "Kunde inte hämta butikens information.", + "download_options": "Nedladdningsalternativ", + "download_path": "Nedladdningsplats", + "previous_screenshot": "Föregående skärmdump", + "next_screenshot": "Nästa skärmdump", + "screenshot": "Skärmdump {{number}}", + "open_screenshot": "Öppna skärmdump {{number}}", + "download_settings": "Nedladdningsinställningar", + "downloader": "Nedladdare", + "select_executable": "Välj", + "no_executable_selected": "Ingen körbar fil vald", + "open_folder": "Öppna mapp", + "open_download_location": "Visa nedladdade filer", + "create_shortcut": "Skapa genväg på skrivbordet", + "clear": "Rensa", + "remove_files": "Ta bort filer", + "remove_from_library_title": "Är du säker?", + "remove_from_library_description": "Detta kommer ta bort {{game}} från ditt bibliotek", + "options": "Alternativ", + "executable_section_title": "Körbar fil", + "executable_section_description": "Sökväg till filen som körs när du klickar på \"Spela\"", + "downloads_section_title": "Nedladdningar", + "downloads_section_description": "Kolla uppdateringar eller andra versioner av detta spel", + "danger_zone_section_title": "Danger zone", + "danger_zone_section_description": "Ta bort detta spel från ditt bibliotek eller filer nedladdade av Hydra", + "download_in_progress": "Nedladdning pågår", + "download_paused": "Nedladdning pausad", + "last_downloaded_option": "Senast nedladdade alternativ", + "create_steam_shortcut": "Skapa Steam-genväg", + "create_shortcut_success": "Genväg skapad", + "you_might_need_to_restart_steam": "Du kan behöva starta om Steam för att se ändringarna", + "create_shortcut_error": "Fel vid skapande av genväg", + "nsfw_content_title": "Det här spelet innehåller olämpligt innehåll", + "nsfw_content_description": "{{title}} innehåller innehåll som kanske inte är lämpligt för alla åldrar. Vill du fortsätta?", + "allow_nsfw_content": "Fortsätt", + "refuse_nsfw_content": "Gå tillbaka", + "stats": "Statistik", + "download_count": "Nedladdningar", + "player_count": "Aktiva spelare", + "download_error": "Det här nedladdningsalternativet är inte tillgängligt", + "download": "Ladda ner", + "executable_path_in_use": "Körbar fil används redan av \"{{game}}\"", + "warning": "Varning:", + "hydra_needs_to_remain_open": "för denna nedladdning behöver Hydra vara öppen tills den är klar. Om Hydra stängs innan nedladdningen är klar förlorar du dina framsteg.", + "achievements": "Prestationer", + "achievements_count": "Prestationer {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "Molnspara", + "cloud_save_description": "Spara dina framsteg i molnet och fortsätt spela på vilken enhet som helst", + "backups": "Säkerhetskopior", + "install_backup": "Installera", + "delete_backup": "Ta bort", + "create_backup": "Ny säkerhetskopia", + "last_backup_date": "Senaste säkerhetskopia {{date}}", + "no_backup_preview": "Inga sparfiler hittades för detta spel", + "restoring_backup": "Återställer säkerhetskopia ({{progress}} klart)…", + "uploading_backup": "Laddar upp säkerhetskopia…", + "no_backups": "Du har inte skapat några säkerhetskopior för detta spel än", + "backup_uploaded": "Säkerhetskopia uppladdad", + "backup_deleted": "Säkerhetskopia borttagen", + "backup_restored": "Säkerhetskopia återställd", + "see_all_achievements": "Se alla prestationer", + "sign_in_to_see_achievements": "Logga in för att se prestationer", + "mapping_method_automatic": "Automatisk", + "mapping_method_manual": "Manuell", + "mapping_method_label": "Kartläggningsmetod", + "files_automatically_mapped": "Filer kartlagda automatiskt", + "no_backups_created": "Inga säkerhetskopior skapade för detta spel", + "manage_files": "Hantera filer", + "loading_save_preview": "Söker efter sparfiler…", + "wine_prefix": "Wine-prefix", + "wine_prefix_description": "Wine-prefixet som används för att köra detta spel", + "launch_options": "Startalternativ", + "launch_options_description": "Avancerade användare kan lägga till modifieringar till sina startalternativ (experimentell funktion)", + "launch_options_placeholder": "Inga parametrar angivna", + "no_download_option_info": "Ingen information tillgänglig", + "backup_deletion_failed": "Misslyckades med att ta bort säkerhetskopian", + "max_number_of_artifacts_reached": "Maximalt antal säkerhetskopior nått för detta spel", + "achievements_not_sync": "Se hur du synkroniserar dina prestationer", + "manage_files_description": "Hantera vilka filer som ska säkerhetskopieras och återställas", + "select_folder": "Välj mapp", + "backup_from": "Säkerhetskopia från {{date}}", + "automatic_backup_from": "Automatisk säkerhetskopia från {{date}}", + "enable_automatic_cloud_sync": "Aktivera automatisk molnsynkronisering", + "custom_backup_location_set": "Anpassad plats för säkerhetskopior inställd", + "no_directory_selected": "Ingen mapp vald", + "no_write_permission": "Kan inte ladda ner till denna mapp. Klicka här för att läsa mer.", + "reset_achievements": "Återställ prestationer", + "reset_achievements_description": "Detta kommer att återställa alla prestationer för {{game}}", + "reset_achievements_title": "Är du säker?", + "reset_achievements_success": "Prestationer återställda", + "reset_achievements_error": "Misslyckades med att återställa prestationer", + "download_error_gofile_quota_exceeded": "Du har överskridit din månadsgräns för Gofile. Vänta tills kvoten återställs.", + "download_error_real_debrid_account_not_authorized": "Ditt Real-Debrid-konto är inte auktoriserat att göra nya nedladdningar. Kontrollera dina kontoinställningar och försök igen.", + "download_error_not_cached_on_real_debrid": "Denna nedladdning finns inte på Real-Debrid och statusövervakning från Real-Debrid är ännu inte tillgänglig.", + "download_error_not_cached_on_torbox": "Denna nedladdning finns inte på TorBox och statusövervakning från TorBox är ännu inte tillgänglig.", + "download_error_not_cached_on_hydra": "Denna nedladdning finns inte på Nimbus.", + "game_removed_from_favorites": "Spelet togs bort från favoriter", + "game_added_to_favorites": "Spelet lades till i favoriter", + "automatically_extract_downloaded_files": "Extrahera nedladdade filer automatiskt", + "create_start_menu_shortcut": "Skapa genväg i Startmenyn", + "invalid_wine_prefix_path": "Ogiltig sökväg för Wine-prefix", + "invalid_wine_prefix_path_description": "Sökvägen till Wine-prefixet är ogiltig. Kontrollera sökvägen och försök igen.", + "missing_wine_prefix": "Wine-prefix krävs för att skapa en säkerhetskopia på Linux" + }, + "activation": { + "title": "Aktivera Hydra", + "installation_id": "Installations ID:", + "enter_activation_code": "Ange din aktiveringskod", + "message": "Om du inte vet var du ska fråga efter denna, borde du inte ha den.", + "activate": "Aktivera", + "loading": "Laddar…" + }, + "downloads": { + "resume": "Fortsätt", + "pause": "Pausa", + "eta": "Slutförs {{eta}}", + "paused": "Pausad", + "verifying": "Verifierar…", + "completed": "Slutförd", + "removed": "Ej nedladdad", + "cancel": "Avbryt", + "filter": "Filtrera nedladdade spel", + "remove": "Ta bort", + "downloading_metadata": "Laddar metadata…", + "deleting": "Tar bort installationsfil…", + "delete": "Ta bort installationsfil", + "delete_modal_title": "Är du säker?", + "delete_modal_description": "Detta tar bort alla installationsfiler från din dator", + "install": "Installera", + "download_in_progress": "Pågår", + "queued_downloads": "Köade nedladdningar", + "downloads_completed": "Klart", + "queued": "I kö", + "no_downloads_title": "Så tomt", + "no_downloads_description": "Du har inte laddat ner något med Hydra än, men det är aldrig för sent att börja.", + "checking_files": "Kontrollerar filer…", + "seeding": "Seedar", + "stop_seeding": "Sluta seeda", + "resume_seeding": "Fortsätt seeda", + "options": "Hantera", + "extract": "Packa upp filer", + "extracting": "Packar upp filer…" + }, + "settings": { + "downloads_path": "Nedladdningssökväg", + "change": "Uppdatera", + "notifications": "Aviseringar", + "enable_download_notifications": "När en nedladdning är klar", + "enable_repack_list_notifications": "När en ny repack läggs till", + "real_debrid_api_token_label": "Real-Debrid API-token", + "quit_app_instead_hiding": "Stäng Hydra istället för att minimera", + "launch_with_system": "Starta Hydra vid systemstart", + "general": "Allmänt", + "behavior": "Beteende", + "download_sources": "Nedladdningskällor", + "language": "Språk", + "api_token": "API-token", + "enable_real_debrid": "Aktivera Real-Debrid", + "real_debrid_description": "Real-Debrid är en obegränsad nedladdningstjänst som låter dig ladda ner filer snabbt, endast begränsad av din internetanslutning.", + "debrid_invalid_token": "Ogiltig API-token", + "debrid_api_token_hint": "Du kan hämta din API-token <0>här", + "real_debrid_free_account_error": "Kontot \"{{username}}\" är ett gratiskonto. Prenumerera på Real-Debrid", + "debrid_linked_message": "Kontot \"{{username}}\" kopplat", + "save_changes": "Spara ändringar", + "changes_saved": "Ändringar sparades", + "download_sources_description": "Hydra hämtar nedladdningslänkar från dessa källor. Källans URL måste vara en direktlänk till en .json-fil med nedladdningslänkar.", + "validate_download_source": "Validera", + "remove_download_source": "Ta bort", + "add_download_source": "Lägg till källa", + "download_count_zero": "Inga nedladdningsalternativ", + "download_count_one": "{{countFormatted}} nedladdningsalternativ", + "download_count_other": "{{countFormatted}} nedladdningsalternativ", + "download_source_url": "URL till nedladdningskälla", + "add_download_source_description": "Ange URL:en till .json-filen", + "download_source_up_to_date": "Uppdaterad", + "download_source_errored": "Fel uppstod", + "sync_download_sources": "Synkronisera källor", + "removed_download_source": "Nedladdningskälla borttagen", + "removed_download_sources": "Nedladdningskällor borttagna", + "cancel_button_confirmation_delete_all_sources": "Nej", + "confirm_button_confirmation_delete_all_sources": "Ja, ta bort allt", + "title_confirmation_delete_all_sources": "Ta bort alla nedladdningskällor", + "description_confirmation_delete_all_sources": "Du kommer att ta bort alla nedladdningskällor", + "button_delete_all_sources": "Ta bort alla", + "added_download_source": "Nedladdningskälla tillagd", + "download_sources_synced": "Alla nedladdningskällor är synkroniserade", + "insert_valid_json_url": "Ange en giltig JSON-URL", + "found_download_option_zero": "Inga nedladdningsalternativ hittades", + "found_download_option_one": "Hittade {{countFormatted}} nedladdningsalternativ", + "found_download_option_other": "Hittade {{countFormatted}} nedladdningsalternativ", + "import": "Importera", + "public": "Offentlig", + "private": "Privat", + "friends_only": "Endast vänner", + "privacy": "Integritet", + "profile_visibility": "Profilens synlighet", + "profile_visibility_description": "Välj vem som kan se din profil och ditt bibliotek", + "required_field": "Detta fält är obligatoriskt", + "source_already_exists": "Denna källa har redan lagts till", + "must_be_valid_url": "Källan måste vara en giltig URL", + "blocked_users": "Blockerade användare", + "user_unblocked": "Användaren har avblockerats", + "enable_achievement_notifications": "När en prestation låses upp", + "launch_minimized": "Starta Hydra minimerad", + "disable_nsfw_alert": "Inaktivera NSFW-varning", + "seed_after_download_complete": "Seeda efter att nedladdningen är klar", + "show_hidden_achievement_description": "Visa beskrivning av dolda prestationer innan de låses upp", + "account": "Konto", + "no_users_blocked": "Du har inga blockerade användare", + "subscription_active_until": "Ditt Hydra Cloud är aktivt till {{date}}", + "manage_subscription": "Hantera prenumeration", + "update_email": "Uppdatera e-postadress", + "update_password": "Uppdatera lösenord", + "current_email": "Nuvarande e-postadress:", + "no_email_account": "Du har ännu inte angett någon e-postadress", + "account_data_updated_successfully": "Kontoinformationen har uppdaterats", + "renew_subscription": "Förnya Hydra Cloud", + "subscription_expired_at": "Din prenumeration gick ut den {{date}}", + "no_subscription": "Njut av Hydra på bästa möjliga sätt", + "become_subscriber": "Bli Hydra Cloud-prenumerant", + "subscription_renew_cancelled": "Automatisk förnyelse är inaktiverad", + "subscription_renews_on": "Din prenumeration förnyas den {{date}}", + "bill_sent_until": "Din nästa faktura skickas senast detta datum", + "no_themes": "Det verkar som att du inte har några teman ännu, men ingen fara – klicka här för att skapa ditt första mästerverk.", + "editor_tab_code": "Kod", + "editor_tab_info": "Info", + "editor_tab_save": "Spara", + "web_store": "Webbutik", + "clear_themes": "Rensa", + "create_theme": "Skapa", + "create_theme_modal_title": "Skapa eget tema", + "create_theme_modal_description": "Skapa ett nytt tema för att anpassa Hydras utseende", + "theme_name": "Namn", + "insert_theme_name": "Ange temats namn", + "set_theme": "Aktivera tema", + "unset_theme": "Avaktivera tema", + "delete_theme": "Ta bort tema", + "edit_theme": "Redigera tema", + "delete_all_themes": "Ta bort alla teman", + "delete_all_themes_description": "Detta kommer att ta bort alla dina egna teman", + "delete_theme_description": "Detta kommer att ta bort temat {{theme}}", + "cancel": "Avbryt", + "appearance": "Utseende", + "enable_torbox": "Aktivera TorBox", + "torbox_description": "TorBox är din premium seedbox-tjänst som konkurrerar med de bästa servrarna på marknaden.", + "torbox_account_linked": "TorBox-konto kopplat", + "create_real_debrid_account": "Klicka här om du ännu inte har ett Real-Debrid-konto", + "create_torbox_account": "Klicka här om du ännu inte har ett TorBox-konto", + "real_debrid_account_linked": "Real-Debrid-konto kopplat", + "name_min_length": "Temanamnet måste innehålla minst 3 tecken", + "import_theme": "Importera tema", + "import_theme_description": "Du kommer att importera {{theme}} från temabutiken", + "error_importing_theme": "Fel vid import av tema", + "theme_imported": "Temat har importerats", + "enable_friend_request_notifications": "När en vänförfrågan tas emot", + "enable_auto_install": "Ladda ner uppdateringar automatiskt", + "common_redist": "Nödvändiga systemkomponenter", + "common_redist_description": "Nödvändiga systemkomponenter krävs för att vissa spel ska fungera. Det rekommenderas att installera dem för att undvika problem.", + "install_common_redist": "Installera", + "installing_common_redist": "Installerar…", + "show_download_speed_in_megabytes": "Visa nedladdningshastighet i megabyte per sekund", + "extract_files_by_default": "Extrahera filer automatiskt efter nedladdning", + "achievement_custom_notification_position": "Anpassad position för prestationmeddelande", + "top-left": "Övre vänster", + "top-center": "Övre mitten", + "top-right": "Övre höger", + "bottom-left": "Nedre vänster", + "bottom-center": "Nedre mitten", + "bottom-right": "Nedre höger", + "enable_achievement_custom_notifications": "Aktivera anpassade prestationmeddelanden", + "alignment": "Justering", + "variation": "Variation", + "default": "Standard", + "rare": "Sällsynt", + "platinum": "Platina", + "hidden": "Dold", + "test_notification": "Testa meddelande", + "notification_preview": "Förhandsvisning av prestationmeddelande", + "enable_friend_start_game_notifications": "När en vän börjar spela ett spel" + }, + "notifications": { + "download_complete": "Nedladdning klar", + "game_ready_to_install": "{{title}} är redo att installeras", + "repack_list_updated": "Repack-listan har uppdaterats", + "repack_count_one": "{{count}} repack tillagd", + "repack_count_other": "{{count}} repacks tillagda", + "new_update_available": "Version {{version}} tillgänglig", + "restart_to_install_update": "Starta om Hydra för att installera uppdateringen", + "notification_achievement_unlocked_title": "Prestation upplåst för {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} och {{count}} andra har låsts upp", + "new_friend_request_description": "{{displayName}} har skickat en vänförfrågan", + "new_friend_request_title": "Ny vänförfrågan", + "extraction_complete": "Extrahering slutförd", + "game_extracted": "{{title}} har extraherats", + "friend_started_playing_game": "{{displayName}} började spela ett spel", + "test_achievement_notification_title": "Detta är ett testmeddelande", + "test_achievement_notification_description": "Ganska coolt, eller hur?" + }, + "system_tray": { + "open": "Öppna Hydra", + "quit": "Avsluta" + }, + "game_card": { + "available_one": "Tillgänglig", + "available_other": "Tillgänglig", + "no_downloads": "Inga nedladdningar tillgängliga" + }, + "binary_not_found_modal": { + "title": "Program inte installerade", + "description": "Wine- eller Lutris-körbara filer hittades inte på ditt system", + "instructions": "Kontrollera hur du installerar dem korrekt på din Linux-distribution så att spelet kan köras normalt" + }, + "modal": { + "close": "Stäng-knapp" + }, + "forms": { + "toggle_password_visibility": "Visa/dölj lösenord" + }, + "user_profile": { + "amount_hours": "{{amount}} timmar", + "amount_minutes": "{{amount}} minuter", + "last_time_played": "Senast spelad {{period}}", + "activity": "Senaste aktivitet", + "library": "Bibliotek", + "total_play_time": "Total speltid", + "no_recent_activity_title": "Hmmm… ingenting här", + "no_recent_activity_description": "Du har inte spelat några spel nyligen. Dags att ändra på det!", + "display_name": "Visningsnamn", + "saving": "Sparar", + "save": "Spara", + "edit_profile": "Redigera profil", + "saved_successfully": "Sparat", + "try_again": "Försök igen", + "sign_out_modal_title": "Är du säker?", + "cancel": "Avbryt", + "successfully_signed_out": "Utloggningen lyckades", + "sign_out": "Logga ut", + "playing_for": "Spelar sedan {{amount}}", + "sign_out_modal_text": "Ditt bibliotek är kopplat till det aktuella kontot. När du loggar ut kommer biblioteket inte längre vara synligt, och framstegen kommer inte att sparas. Vill du fortsätta logga ut?", + "add_friends": "Lägg till vänner", + "add": "Lägg till", + "friend_code": "Vänkod", + "see_profile": "Visa profil", + "sending": "Skickar", + "friend_request_sent": "Vänförfrågan skickad", + "friends": "Vänner", + "friends_list": "Vänlista", + "user_not_found": "Användare hittades inte", + "block_user": "Blockera användare", + "add_friend": "Lägg till vän", + "request_sent": "Förfrågan skickad", + "request_received": "Förfrågan mottagen", + "accept_request": "Acceptera förfrågan", + "ignore_request": "Ignorera förfrågan", + "cancel_request": "Avbryt förfrågan", + "undo_friendship": "Ta bort vänskap", + "request_accepted": "Förfrågan accepterad", + "user_blocked_successfully": "Användaren har blockerats", + "user_block_modal_text": "Detta kommer att blockera {{displayName}}", + "blocked_users": "Blockerade användare", + "unblock": "Avblockera", + "no_friends_added": "Du har inte lagt till några vänner", + "pending": "Väntande", + "no_pending_invites": "Du har inga väntande inbjudningar", + "no_blocked_users": "Du har inga blockerade användare", + "friend_code_copied": "Vänkod kopierad", + "undo_friendship_modal_text": "Detta kommer att ta bort din vänskap med {{displayName}}", + "privacy_hint": "För att justera vem som kan se detta, gå till <0>Inställningar", + "locked_profile": "Denna profil är privat", + "image_process_failure": "Fel vid bildbehandling", + "required_field": "Detta fält är obligatoriskt", + "displayname_min_length": "Visningsnamnet måste vara minst 3 tecken långt", + "displayname_max_length": "Visningsnamnet får vara högst 50 tecken långt", + "report_profile": "Anmäl denna profil", + "report_reason": "Varför anmäler du denna profil?", + "report_description": "Ytterligare information", + "report_description_placeholder": "Ytterligare information", + "report": "Anmäl", + "report_reason_hate": "Hatretorik", + "report_reason_sexual_content": "Sexuellt innehåll", + "report_reason_violence": "Våld", + "report_reason_spam": "Spam", + "report_reason_other": "Annat", + "profile_reported": "Profil anmäld", + "your_friend_code": "Din vänkod:", + "upload_banner": "Ladda upp banner", + "uploading_banner": "Laddar upp banner…", + "background_image_updated": "Bakgrundsbild uppdaterad", + "stats": "Statistik", + "achievements": "prestationer", + "games": "Spel", + "top_percentile": "Topp {{percentile}}%", + "ranking_updated_weekly": "Rankingen uppdateras varje vecka", + "playing": "Spelar {{game}}", + "achievements_unlocked": "Prestationer upplåsta", + "earned_points": "Intjänade poäng", + "show_achievements_on_profile": "Visa dina prestationer på profilen", + "show_points_on_profile": "Visa dina intjänade poäng på din profil" + }, + "achievement": { + "achievement_unlocked": "Prestationer upplåst", + "user_achievements": "Prestationer för {{displayName}}", + "your_achievements": "Dina prestationer", + "unlocked_at": "Upplåst den: {{date}}", + "subscription_needed": "Ett Hydra Cloud-abonnemang krävs för att se detta innehåll", + "new_achievements_unlocked": "Upplåste {{achievementCount}} nya prestationer från {{gameCount}} spel", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} prestationer", + "achievements_unlocked_for_game": "Upplåste {{achievementCount}} nya prestationer för {{gameTitle}}", + "hidden_achievement_tooltip": "Detta är en dold prestation", + "achievement_earn_points": "Tjäna {{points}} poäng med denna prestation", + "earned_points": "Tjänade poäng:", + "available_points": "Tillgängliga poäng:", + "how_to_earn_achievements_points": "Hur tjänar man poäng på prestationer?" + }, + "hydra_cloud": { + "subscription_tour_title": "Hydra Cloud-abonnemang", + "subscribe_now": "Prenumerera nu", + "cloud_saving": "Spara i molnet", + "cloud_achievements": "Spara dina prestationer i molnet", + "animated_profile_picture": "Animerade profilbilder", + "premium_support": "Premium-support", + "show_and_compare_achievements": "Visa och jämför dina prestationer med andra användare", + "animated_profile_banner": "Animerad profilbanner", + "hydra_cloud": "Hydra Cloud", + "hydra_cloud_feature_found": "Du har just upptäckt en Hydra Cloud-funktion!", + "learn_more": "Läs mer", + "debrid_description": "Ladda ner upp till 4x snabbare med Nimbus" + } +} diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index 76496c5d..c3fe2081 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -8,46 +8,46 @@ "surprise_me": "Beni Şaşırt", "no_results": "Sonuç bulunamadı", "start_typing": "Aramak için yazmaya başlayın...", - "hot": "Şu anda popüler", - "weekly": "📅 Haftanın en iyi oyunları", - "achievements": "🏆 Tamamlanacak oyunlar" + "hot": "Şu anda Popüler", + "weekly": "📅 Haftanın En İyi Oyunları", + "achievements": "🏆 Bitirilecek Oyunlar" }, "sidebar": { "catalogue": "Katalog", "downloads": "İndirilenler", "settings": "Ayarlar", - "my_library": "Kütüphane", + "my_library": "Kütüphanem", "downloading_metadata": "{{title}} (Meta verileri indiriliyor…)", - "paused": "{{title}} (Durduruldu)", - "downloading": "{{title}} ({{percentage}} - İndiriliyor…)", - "filter": "Kütüphaneyi filtrele", + "paused": "{{title}} (Duraklatıldı)", + "downloading": "{{title}} (%{{percentage}} - İndiriliyor…)", + "filter": "Kütüphanede filtrele", "home": "Ana Sayfa", "queued": "{{title}} (Sırada)", - "game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi", - "sign_in": "Giriş yap", + "game_has_no_executable": "Bu oyun için çalıştırılabilir dosya seçilmedi", + "sign_in": "Giriş Yap", "friends": "Arkadaşlar", "need_help": "Yardıma mı ihtiyacınız var?", "favorites": "Favoriler" }, "header": { - "search": "Oyunları ara", + "search": "Oyunlarda Ara", "home": "Ana Sayfa", "catalogue": "Katalog", "downloads": "İndirilenler", - "search_results": "Arama sonuçları", + "search_results": "Arama Sonuçları", "settings": "Ayarlar", - "version_available_install": "{{version}} sürümü mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.", - "version_available_download": "{{version}} sürümü mevcut. İndirmek için buraya tıklayın." + "version_available_install": "{{version}} sürümü mevcut. Yeniden başlatıp yüklemek için tıklayın.", + "version_available_download": "{{version}} sürümü mevcut. İndirmek için tıklayın." }, "bottom_panel": { "no_downloads_in_progress": "Devam eden indirme yok", "downloading_metadata": "{{title}} meta verileri indiriliyor…", - "downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlanma: {{eta}} - Hız: {{speed}}", - "calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…", - "checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)", + "downloading": "{{title}} indiriliyor… (%{{percentage}} tamamlandı) - Bitiş: {{eta}} - Hız: {{speed}}", + "calculating_eta": "{{title}} indiriliyor… (%{{percentage}} tamamlandı) - Kalan süre hesaplanıyor…", + "checking_files": "{{title}} dosyaları kontrol ediliyor… (%{{percentage}} tamamlandı)", "installing_common_redist": "{{log}}…", - "installation_complete": "İndirme tamamlandı", - "installation_complete_message": "Genel bağımlılıklar başarıyla yüklendi." + "installation_complete": "Kurulum tamamlandı", + "installation_complete_message": "Gerekli paketler başarıyla yüklendi" }, "catalogue": { "search": "Filtrele…", @@ -58,7 +58,7 @@ "download_sources": "İndirme kaynakları", "result_count": "{{resultCount}} sonuç", "filter_count": "{{filterCount}} mevcut", - "clear_filters": "{{filterCount}} seçili filtreyi temizle" + "clear_filters": "{{filterCount}} seçiliyi temizle" }, "game_details": { "open_download_options": "İndirme seçeneklerini aç", @@ -67,32 +67,32 @@ "download_options_other": "{{count}} indirme seçeneği", "updated_at": "{{updated_at}} tarihinde güncellendi", "install": "Yükle", - "resume": "Devam et", - "pause": "Durdur", - "cancel": "İptal et", + "resume": "Devam Et", + "pause": "Duraklat", + "cancel": "İptal Et", "remove": "Kaldır", "space_left_on_disk": "Diskte {{space}} boş alan kaldı", - "eta": "{{eta}} tahmini bitiş", + "eta": "Bitiş: {{eta}}", "calculating_eta": "Kalan süre hesaplanıyor…", "downloading_metadata": "Meta veriler indiriliyor…", "filter": "Paketleri filtrele", - "requirements": "Sistem gereksinimleri", + "requirements": "Sistem Gereksinimleri", "minimum": "Minimum", "recommended": "Önerilen", - "paused": "Durduruldu", + "paused": "Duraklatıldı", "release_date": "{{date}} tarihinde yayımlandı", "publisher": "{{publisher}} tarafından yayımlandı", "hours": "saat", "minutes": "dakika", "amount_hours": "{{amount}} saat", "amount_minutes": "{{amount}} dakika", - "accuracy": "{{accuracy}}% doğruluk", + "accuracy": "%{{accuracy}} doğruluk", "add_to_library": "Kütüphaneye ekle", "remove_from_library": "Kütüphaneden kaldır", - "no_downloads": "İndirilebilir içerik yok", - "play_time": "{{amount}} süre oynandı", - "last_time_played": "Son oynama {{period}} önce", - "not_played_yet": "{{title}} henüz oynanmadı", + "no_downloads": "İndirme mevcut değil", + "play_time": "{{amount}} oynandı", + "last_time_played": "Son oynanma: {{period}}", + "not_played_yet": "{{title}} oyununu henüz oynamadınız", "next_suggestion": "Sonraki öneri", "play": "Oyna", "deleting": "Yükleyici siliniyor…", @@ -107,134 +107,140 @@ "download_path": "İndirme yolu", "previous_screenshot": "Önceki ekran görüntüsü", "next_screenshot": "Sonraki ekran görüntüsü", - "screenshot": "{{number}} ekran görüntüsü", - "open_screenshot": "{{number}} ekran görüntüsünü aç", + "screenshot": "Ekran görüntüsü {{number}}", + "open_screenshot": "Ekran görüntüsünü aç ({{number}})", "download_settings": "İndirme ayarları", "downloader": "İndirici", "select_executable": "Seç", - "no_executable_selected": "Hiçbir çalıştırılabilir dosya seçilmedi", + "no_executable_selected": "Çalıştırılabilir dosya seçilmedi", "open_folder": "Klasörü aç", - "open_download_location": "İndirilen dosyaları gör", + "open_download_location": "İndirilen dosyaları görüntüle", "create_shortcut": "Masaüstü kısayolu oluştur", "clear": "Temizle", "remove_files": "Dosyaları kaldır", "remove_from_library_title": "Emin misiniz?", - "remove_from_library_description": "Bu işlem sonrasında {{game}} oyunu kütüphanenizden kaldıracaktır", + "remove_from_library_description": "{{game}} oyununu kütüphanenizden kaldıracaktır", "options": "Seçenekler", "executable_section_title": "Çalıştırılabilir dosya", - "executable_section_description": "\"Oyna\" butonuna tıklandığında çalıştırılacak dosyanın yolu", - "downloads_section_title": "İndirmeler", - "downloads_section_description": "Bu oyun için güncellemeleri veya diğer sürümleri kontrol edin", - "danger_zone_section_title": "Tehlike bölgesi", - "danger_zone_section_description": "Bu oyunu kütüphanenizden kaldırın veya Hydra tarafından indirilen dosyaları silin.", - "download_in_progress": "İndirme devam ediyor", - "download_paused": "İndirme durduruldu", + "executable_section_description": "\"Oyna\" seçildiğinde çalışacak dosyanın yolu", + "downloads_section_title": "İndirilenler", + "downloads_section_description": "Bu oyunun güncelleme veya diğer sürümlerine göz atın", + "danger_zone_section_title": "Tehlikeli Alan", + "danger_zone_section_description": "Bu oyunu kütüphanenizden veya Hydra tarafından indirilen dosyalardan kaldırın", + "download_in_progress": "İndirme sürüyor", + "download_paused": "İndirme duraklatıldı", "last_downloaded_option": "Son indirilen seçenek", + "create_steam_shortcut": "Steam kısayolu oluştur", "create_shortcut_success": "Kısayol başarıyla oluşturuldu", + "you_might_need_to_restart_steam": "Değişiklikleri görmek için Steam'i yeniden başlatmanız gerekebilir", "create_shortcut_error": "Kısayol oluşturulurken hata oluştu", - "nsfw_content_title": "Bu oyun uygunsuz içerik içeriyor", - "nsfw_content_description": "{{title}} her yaş için uygun olmayabilecek içeriklere sahiptir. Devam etmek istediğinizden emin misiniz?", + "nsfw_content_title": "Bu oyun uygunsuz içerik barındırıyor", + "nsfw_content_description": "{{title}} bazı kullanıcılar için uygun olmayabilecek içerik barındırıyor. Devam etmek istediğinizden emin misiniz?", "allow_nsfw_content": "Devam et", "refuse_nsfw_content": "Geri dön", "stats": "İstatistikler", - "download_count": "İndirme sayısı", + "download_count": "İndirme", "player_count": "Aktif oyuncular", - "download_error": "Bu indirme seçeneği mevcut değil", + "download_error": "Bu indirme seçeneği kullanılamıyor", "download": "İndir", - "executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya", + "executable_path_in_use": "Çalıştırılabilir dosya zaten \"{{game}}\" tarafından kullanılıyor", "warning": "Uyarı:", - "hydra_needs_to_remain_open": "Bu indirmenin tamamlanması için Hydra açık kalmalıdır. Eğer Hydra kapanırsa, ilerleme kaydedilmez.", + "hydra_needs_to_remain_open": "Bu indirme için, Hydra programının tamamlanana kadar açık kalması gerekir. Hydra kapanırsa, ilerlemeniz kaybolacaktır.", "achievements": "Başarımlar", "achievements_count": "Başarımlar {{unlockedCount}}/{{achievementsCount}}", - "cloud_save": "Bulut kaydı", - "cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin", + "cloud_save": "Bulut Kaydı", + "cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazdan devam edin", "backups": "Yedekler", "install_backup": "Yükle", "delete_backup": "Sil", - "create_backup": "Yeni yedek oluştur", - "last_backup_date": "{{date}} tarihindeki son yedek", - "no_backup_preview": "Bu oyun için bir kayıt dosyası bulunamadı", - "restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…", + "create_backup": "Yeni Yedek", + "last_backup_date": "Son yedekleme: {{date}}", + "no_backup_preview": "Bu başlık için kayıtlı oyun bulunamadı", + "restoring_backup": "Yedek geri yükleniyor (%{{progress}} tamamlandı)…", "uploading_backup": "Yedek yükleniyor…", - "no_backups": "Bu oyun için henüz bir yedek oluşturmadınız", + "no_backups": "Bu oyun için henüz yedek oluşturmadınız", "backup_uploaded": "Yedek yüklendi", "backup_deleted": "Yedek silindi", "backup_restored": "Yedek geri yüklendi", - "see_all_achievements": "Tüm başarımları gör", - "sign_in_to_see_achievements": "Başarımları görmek için oturum açın", + "see_all_achievements": "Tüm başarımları görüntüle", + "sign_in_to_see_achievements": "Başarımları görmek için giriş yapın", "mapping_method_automatic": "Otomatik", "mapping_method_manual": "Manuel", - "mapping_method_label": "Eşleme yöntemi", - "files_automatically_mapped": "Dosyalar otomatik olarak eşlendi", - "no_backups_created": "Bu oyun için yedek oluşturulmadı", + "mapping_method_label": "Eşleme metodu", + "files_automatically_mapped": "Dosyalar otomatik eşlendi", + "no_backups_created": "Bu oyun için oluşturulmuş yedek yok", "manage_files": "Dosyaları yönet", "loading_save_preview": "Kayıtlı oyunlar aranıyor…", - "wine_prefix": "Wine Prefix", - "wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix", + "wine_prefix": "Wine Ön Ek", + "wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine ön eki", "launch_options": "Başlatma Seçenekleri", - "launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine parametreler girebilir (deneysel özellik)", - "launch_options_placeholder": "Belirtilen bir parametre yok", + "launch_options_description": "Gelişmiş kullanıcılar için başlatma parametreleri tanımlayın (deneysel özellik)", + "launch_options_placeholder": "Parametre belirtilmedi", "no_download_option_info": "Bilgi mevcut değil", - "backup_deletion_failed": "Yedek silinemedi", - "max_number_of_artifacts_reached": "Bu oyun için maksimum yedek sayısına ulaşıldı", - "achievements_not_sync": "Başarımlarınızı senkronize etmeyi öğrenin", - "manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin", + "backup_deletion_failed": "Yedek silme işlemi başarısız oldu", + "max_number_of_artifacts_reached": "Bu oyun için azami yedekleme sayısına ulaşıldı", + "achievements_not_sync": "Başarımlarını eşitlemeyi öğren", + "manage_files_description": "Hangi dosyaların yedekleneceğini ve geri yükleneceğini yönetin", "select_folder": "Klasör seç", - "backup_from": "{{date}} tarihinden yedek", - "automatic_backup_from": "{{date}} tarihinden otomatik kayıt", - "enable_automatic_cloud_sync": "Otomatik bulut kaydı senkronizasyonunu aktifleştir", - "custom_backup_location_set": "Özel yedekleme konumu ayarlandı", - "no_directory_selected": "Bir dizin seçilmedi", - "no_write_permission": "Bu dizine indirme yapılamaz. Daha fazla bilgi için buraya tıklayın.", + "backup_from": "{{date}} tarihli yedek", + "automatic_backup_from": "{{date}} tarihli otomatik yedek", + "enable_automatic_cloud_sync": "Otomatik bulut eşitlemesini etkinleştir", + "custom_backup_location_set": "Özel yedekleme konumu belirlendi", + "no_directory_selected": "Klasör seçilmedi", + "no_write_permission": "Bu klasöre indirme yapılamıyor. Detaylar için buraya tıklayın.", "reset_achievements": "Başarımları sıfırla", - "reset_achievements_description": "Bu işlem {{game}} için tüm başarımları sıfırlar", + "reset_achievements_description": "{{game}} için tüm başarımlar sıfırlanacak", "reset_achievements_title": "Emin misiniz?", "reset_achievements_success": "Başarımlar başarıyla sıfırlandı", "reset_achievements_error": "Başarımlar sıfırlanamadı", - "download_error_gofile_quota_exceeded": "Gofile aylık kotanızı doldurdunuz. Kotanın yenilenmesini bekleyin.", - "download_error_real_debrid_account_not_authorized": "Real-Debrid hesabınız yeni indirme işlemleri yapmak için yetkilendirilmemiş. Lütfen hesap ayarlarınızı kontrol edip tekrar deneyin.", - "download_error_not_cached_on_real_debrid": "Bu indirme Real-Debrid üzerinde mevcut değil ve Real-Debrid'den indirme durumu henüz sorgulanamıyor.", - "download_error_not_cached_on_torbox": "Bu indirme TorBox'ta mevcut değil ve TorBox'tan indirme durumu henüz sorgulanamıyor.", - "download_error_not_cached_on_hydra": "Bu indirme Nimbus'ta mevcut değil.", - "game_removed_from_favorites": "Oyun favorilerden silindi", + "download_error_gofile_quota_exceeded": "Gofile aylık kotanızı aştınız. Lütfen kotanın sıfırlanmasını bekleyin.", + "download_error_real_debrid_account_not_authorized": "Real-Debrid hesabınız yeni indirmeler için yetkili değil. Hesap ayarlarınızı kontrol edip tekrar deneyin.", + "download_error_not_cached_on_real_debrid": "Bu indirme Real-Debrid üzerinde mevcut değil ve durum sorgulanamıyor.", + "download_error_not_cached_on_torbox": "Bu indirme TorBox üzerinde mevcut değil ve durum sorgulanamıyor.", + "download_error_not_cached_on_hydra": "Bu indirme Nimbus üzerinde mevcut değil.", + "game_removed_from_favorites": "Oyun favorilerden kaldırıldı", "game_added_to_favorites": "Oyun favorilere eklendi", - "automatically_extract_downloaded_files": "Yüklenmiş dosyaları otomatik olarak çıkart" + "automatically_extract_downloaded_files": "İndirilen dosyaları otomatik çıkart", + "create_start_menu_shortcut": "Başlat Menüsüne kısayol oluştur", + "invalid_wine_prefix_path": "Geçersiz Wine ön ek yolu", + "invalid_wine_prefix_path_description": "Wine ön ek yolu hatalı. Lütfen yolu kontrol edin ve tekrar deneyin.", + "missing_wine_prefix": "Linux'ta yedekleme oluşturmak için Wine ön eki gereklidir" }, "activation": { - "title": "Hydra'yı Aktive Et", - "installation_id": "Kurulum Kimliği:", - "enter_activation_code": "Aktivasyon kodunuzu girin", - "message": "Bunu nasıl edineceğini bilmiyorsan, buna sahip olmamalısın.", - "activate": "Aktive Et", + "title": "Hydra'yı Etkinleştir", + "installation_id": "Kurulum ID:", + "enter_activation_code": "Etkinleştirme kodunu girin", + "message": "Bu kodun nereden alınacağını bilmiyorsanız, zaten bu kodu kullanmamanız gerekir.", + "activate": "Etkinleştir", "loading": "Yükleniyor…" }, "downloads": { "resume": "Devam Et", "pause": "Duraklat", - "eta": "Tamamlama {{eta}}", + "eta": "Bitiş: {{eta}}", "paused": "Duraklatıldı", "verifying": "Doğrulanıyor…", "completed": "Tamamlandı", "removed": "İndirilmedi", "cancel": "İptal Et", - "filter": "İndirilen oyunları filtrele", + "filter": "İndirilen oyunlarda filtrele", "remove": "Kaldır", "downloading_metadata": "Meta verileri indiriliyor…", "deleting": "Yükleyici siliniyor…", "delete": "Yükleyiciyi kaldır", "delete_modal_title": "Emin misiniz?", - "delete_modal_description": "Bu işlem, tüm kurulum dosyalarını bilgisayarınızdan kaldıracaktır", - "install": "Kur", + "delete_modal_description": "Tüm kurulum dosyaları bilgisayarınızdan kaldırılacaktır", + "install": "Yükle", "download_in_progress": "Devam ediyor", "queued_downloads": "Sıradaki indirmeler", "downloads_completed": "Tamamlananlar", "queued": "Sırada", - "no_downloads_title": "Bomboş", - "no_downloads_description": "Henüz Hydra ile hiçbir şey indirmediniz, ancak başlamak için asla geç değil.", + "no_downloads_title": "Çok boş görünüyor", + "no_downloads_description": "Hydra ile henüz bir şey indirmediniz, başlamak için asla geç değildir.", "checking_files": "Dosyalar kontrol ediliyor…", - "seeding": "Paylaşılıyor", - "stop_seeding": "Paylaşımı durdur", - "resume_seeding": "Paylaşımı sürdür", + "seeding": "Seed yapılıyor", + "stop_seeding": "Seed yapmayı durdur", + "resume_seeding": "Seed yapmaya devam et", "options": "Yönet", "extract": "Dosyaları çıkart", "extracting": "Dosyalar çıkartılıyor…" @@ -243,181 +249,202 @@ "downloads_path": "İndirme yolu", "change": "Güncelle", "notifications": "Bildirimler", - "enable_download_notifications": "Bir indirme tamamlandığında", - "enable_repack_list_notifications": "Yeni bir repack eklendiğinde", + "enable_download_notifications": "İndirme tamamlandığında", + "enable_repack_list_notifications": "Yeni bir paket eklendiğinde", "real_debrid_api_token_label": "Real-Debrid API anahtarı", - "quit_app_instead_hiding": "Hydra'yı kapatınca sistem tepsisine gitmesin", - "launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır", + "quit_app_instead_hiding": "Hydra kapatıldığında gizleme", + "launch_with_system": "Sistem başlatıldığında Hydra'yı aç", "general": "Genel", "behavior": "Davranış", "download_sources": "İndirme kaynakları", "language": "Dil", "api_token": "API Anahtarı", - "enable_real_debrid": "Real-Debrid'i Etkinleştir", - "real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak hızlı dosya indirmenizi sağlayan sınırsız bir indirici.", + "enable_real_debrid": "Real-Debrid’i etkinleştir", + "real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak dosyaları hızlı indirmenizi sağlayan sınırsız bir indirme servisidir.", "debrid_invalid_token": "Geçersiz API anahtarı", "debrid_api_token_hint": "API anahtarınızı <0>buradan alabilirsiniz", - "real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsiz bir hesaptır. Lütfen Real-Debrid abonesi olun", + "real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsizdir. Lütfen Real-Debrid’e abone olun", "debrid_linked_message": "\"{{username}}\" hesabı bağlandı", "save_changes": "Değişiklikleri Kaydet", "changes_saved": "Değişiklikler başarıyla kaydedildi", - "download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacak. Kaynak URL, indirme bağlantılarını içeren bir .json dosyasına doğrudan bir bağlantı olmalıdır.", + "download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacaktır. Kaynak URL’si, bağlantıların bulunduğu bir .json dosyasına doğrudan bağlantı olmalıdır.", "validate_download_source": "Doğrula", "remove_download_source": "Kaldır", "add_download_source": "Kaynak ekle", - "cancel_button_confirmation_delete_all_sources": "Hayır", - "confirm_button_confirmation_delete_all_sources": "Evet, her şeyi sil", - "description_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sileceksiniz", - "title_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sil", - "removed_download_sources": "Yazı tipleri kaldırıldı", - "button_delete_all_sources": "Tüm indirme kaynaklarını kaldır", "download_count_zero": "İndirme seçeneği yok", "download_count_one": "{{countFormatted}} indirme seçeneği", "download_count_other": "{{countFormatted}} indirme seçeneği", "download_source_url": "İndirme kaynağı URL'si", - "add_download_source_description": ".json dosyasının URL'sini girin", + "add_download_source_description": ".json dosyasının URL’sini girin", "download_source_up_to_date": "Güncel", "download_source_errored": "Hatalı", - "sync_download_sources": "Kaynakları senkronize et", + "sync_download_sources": "Kaynakları eşitle", "removed_download_source": "İndirme kaynağı kaldırıldı", + "removed_download_sources": "İndirme kaynakları kaldırıldı", + "cancel_button_confirmation_delete_all_sources": "Hayır", + "confirm_button_confirmation_delete_all_sources": "Evet, hepsini sil", + "title_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sil", + "description_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sileceksiniz", + "button_delete_all_sources": "Tümünü kaldır", "added_download_source": "İndirme kaynağı eklendi", - "download_sources_synced": "Tüm indirme kaynakları senkronize edildi", - "insert_valid_json_url": "Geçerli bir JSON URL'si girin", - "found_download_option_zero": "Hiçbir indirme seçeneği bulunamadı", + "download_sources_synced": "Tüm indirme kaynakları eşitlendi", + "insert_valid_json_url": "Geçerli bir JSON URL’si girin", + "found_download_option_zero": "İndirme seçeneği bulunamadı", "found_download_option_one": "{{countFormatted}} indirme seçeneği bulundu", "found_download_option_other": "{{countFormatted}} indirme seçeneği bulundu", - "import": "İçe aktar", - "public": "Herkese açık", + "import": "İçe Aktar", + "public": "Herkese Açık", "private": "Gizli", - "friends_only": "Sadece arkadaşlar", + "friends_only": "Yalnızca Arkadaşlar", "privacy": "Gizlilik", - "profile_visibility": "Profil görünürlüğü", + "profile_visibility": "Profil Görünürlüğü", "profile_visibility_description": "Profilinizi ve kütüphanenizi kimlerin görebileceğini seçin", "required_field": "Bu alan gereklidir", - "source_already_exists": "Bu kaynak zaten eklenmiş", - "must_be_valid_url": "Kaynak geçerli bir URL olmalıdır", + "source_already_exists": "Bu kaynak zaten eklendi", + "must_be_valid_url": "Kaynak geçerli bir URL olmalı", "blocked_users": "Engellenen kullanıcılar", - "user_unblocked": "Kullanıcının engeli kaldırıldı", - "enable_achievement_notifications": "Bir başarım kilidi açıldığında", - "launch_minimized": "Hydra'yı küçültülmüş başlat", + "user_unblocked": "Kullanıcı engeli kaldırıldı", + "enable_achievement_notifications": "Bir başarı açıldığında", + "launch_minimized": "Hydra'yı küçük aç", "disable_nsfw_alert": "NSFW uyarısını devre dışı bırak", - "seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş", - "show_hidden_achievement_description": "Gizli başarım açıklamalarını kilitlenmeden önce göster", + "seed_after_download_complete": "İndirme sonrası seed yap", + "show_hidden_achievement_description": "Açılmadan önce gizli başarı açıklamasını göster", "account": "Hesap", "no_users_blocked": "Hiçbir kullanıcıyı engellemediniz", - "subscription_active_until": "Hydra Cloud'unuz {{date}} tarihine kadar aktif", + "subscription_active_until": "Hydra Cloud üyeliğiniz {{date}} tarihine kadar aktif", "manage_subscription": "Aboneliği yönet", - "update_email": "E-posta'yı güncelle", + "update_email": "E-postayı güncelle", "update_password": "Şifreyi güncelle", - "current_email": "Aktif e-posta'nız", - "no_email_account": "Henüz ayarlanmış bir e-postanız yok", - "account_data_updated_successfully": "Hesap bilgileri başarıyla güncellendi", + "current_email": "Mevcut e-posta:", + "no_email_account": "Henüz bir e-posta tanımlanmadı", + "account_data_updated_successfully": "Hesap verileri başarıyla güncellendi", "renew_subscription": "Hydra Cloud'u yenile", "subscription_expired_at": "Aboneliğiniz {{date}} tarihinde sona erdi", - "no_subscription": "Hydra'yı en iyi şekilde deneyimleyin", - "become_subscriber": "Hydra Cloud'lu ol", - "subscription_renew_cancelled": "Otomatik yenileme devre dışı", + "no_subscription": "Hydra'yı en iyi şekilde kullanın", + "become_subscriber": "Hydra Cloud Ol", + "subscription_renew_cancelled": "Otomatik yenileme devre dışı bırakıldı", "subscription_renews_on": "Aboneliğiniz {{date}} tarihinde yenilenecek", - "bill_sent_until": "Bir sonraki faturanız bu tarihe kadar gönderilecek", - "no_themes": "Henüz bir temanız yok gibi görünüyor, ama endişelenmeyin, ilk şaheserinizi oluşturmak için buraya tıklayın.", + "bill_sent_until": "Sonraki fatura bu güne kadar gönderilecek", + "no_themes": "Henüz bir temanız yok gibi görünüyor, endişelenmeyin, ilk şaheserinizi oluşturmak için buraya tıklayın.", "editor_tab_code": "Kod", "editor_tab_info": "Bilgi", "editor_tab_save": "Kaydet", - "web_store": "İnternet mağazası", + "web_store": "Web Mağaza", "clear_themes": "Temizle", "create_theme": "Oluştur", - "create_theme_modal_title": "Tema oluştur", - "create_theme_modal_description": "Hydra'nın görünümünü özelleştirmek için yeni bir tema oluştur", - "theme_name": "İsim", - "insert_theme_name": "Tema ismini gir", - "set_theme": "Temayı seç", - "unset_theme": "Tema seçimini kaldır", + "create_theme_modal_title": "Özel tema oluştur", + "create_theme_modal_description": "Hydra’nın görünümünü özelleştirmek için yeni bir tema oluşturun", + "theme_name": "Tema adı", + "insert_theme_name": "Tema adı girin", + "set_theme": "Temayı ayarla", + "unset_theme": "Temayı kaldır", "delete_theme": "Temayı sil", "edit_theme": "Temayı düzenle", "delete_all_themes": "Tüm temaları sil", - "delete_all_themes_description": "Bu tüm temalarınızı silecektir", - "delete_theme_description": "Bu {{theme}} temasını silecektir", + "delete_all_themes_description": "Tüm özel temalarınız silinecek", + "delete_theme_description": "{{theme}} teması silinecek", "cancel": "İptal", "appearance": "Görünüm", - "enable_torbox": "TorBox'u etkinleştir", - "torbox_description": "TorBox, piyasadaki en iyi sunucularla bile rekabet edebilen premium seedbox hizmetinizdir.", + "enable_torbox": "TorBox'u Etkinleştir", + "torbox_description": "TorBox, piyasadaki en iyi sunucularla yarışan premium seedbox hizmetinizdir.", "torbox_account_linked": "TorBox hesabı bağlandı", - "create_real_debrid_account": "Henüz bir Real-Debrid hesabınız yoksa buraya tıklayın", - "create_torbox_account": "Henüz bir TorBox hesabınız yoksa buraya tıklayın", + "create_real_debrid_account": "Henüz Real Debrid hesabınız yoksa buraya tıklayın", + "create_torbox_account": "Henüz TorBox hesabınız yoksa buraya tıklayın", "real_debrid_account_linked": "Real-Debrid hesabı bağlandı", - "name_min_length": "Tema ismi en az 3 karakter uzunluğunda olmalıdır", - "import_theme": "Temayı içe aktar", - "import_theme_description": "{{theme}} teması, tema mağazasından içeri aktarılacak", - "error_importing_theme": "Temayı içe aktarmada bir sorun oluştu", + "name_min_length": "Tema adı en az 3 karakter olmalıdır", + "import_theme": "Tema içe aktar", + "import_theme_description": "{{theme}} temasını tema mağazasından içe aktaracaksınız", + "error_importing_theme": "Tema içe aktarılırken hata oluştu", "theme_imported": "Tema başarıyla içe aktarıldı", "enable_friend_request_notifications": "Bir arkadaşlık isteği alındığında", - "enable_auto_install": "Güncellemeleri otomatik yükle", - "common_redist": "Ortak bağımlılıklar", - "common_redist_description": "Bazı oyunların çalışabilmesi için genel bağımlılıklar gereklidir. Sorun yaşamamak için bunların yüklenmesi önerilir.", - "install_common_redist": "Yükle", - "installing_common_redist": "Yükleniyor…", - "show_download_speed_in_megabytes": "İndirme hızını megabayt/saniye (MB/s) cinsinden göster" + "enable_auto_install": "Güncellemeleri otomatik indir", + "common_redist": "Gereksinim Paketleri", + "common_redist_description": "Bazı oyunların çalışması için gereksinim paketleri gerekir. Sorun yaşamamak için kurulması önerilir.", + "install_common_redist": "Kur", + "installing_common_redist": "Kuruluyor…", + "show_download_speed_in_megabytes": "İndirme hızını megabayt cinsinden göster", + "extract_files_by_default": "İndirme sonrası varsayılan olarak dosyaları çıkar", + "achievement_custom_notification_position": "Başarı özel bildirim konumu", + "top-left": "Sol üst", + "top-center": "Üst orta", + "top-right": "Sağ üst", + "bottom-left": "Sol alt", + "bottom-center": "Alt orta", + "bottom-right": "Sağ alt", + "enable_achievement_custom_notifications": "Başarı özel bildirimlerini etkinleştir", + "alignment": "Hizalama", + "variation": "Çeşit", + "default": "Varsayılan", + "rare": "Nadir", + "platinum": "Platin", + "hidden": "Gizli", + "test_notification": "Test bildirimi", + "notification_preview": "Başarı Bildirimi Önizlemesi", + "enable_friend_start_game_notifications": "Bir arkadaşınız oyun oynamaya başladığında" }, "notifications": { "download_complete": "İndirme tamamlandı", - "game_ready_to_install": "{{title}} kurulmaya hazır", - "repack_list_updated": "Repack listesi güncellendi", - "repack_count_one": "{{count}} repack eklendi", - "repack_count_other": "{{count}} repack eklendi", + "game_ready_to_install": "{{title}} yüklenmeye hazır", + "repack_list_updated": "Paket listesi güncellendi", + "repack_count_one": "{{count}} paket eklendi", + "repack_count_other": "{{count}} paket eklendi", "new_update_available": "{{version}} sürümü mevcut", - "restart_to_install_update": "Güncellemeyi yüklemek için Hydra'yı yeniden başlatın", - "notification_achievement_unlocked_title": "{{game}} için başarım kilidi açıldı", - "notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarım açıldı", - "new_friend_request_description": "Yeni bir arkadaşlık isteğin var", + "restart_to_install_update": "Güncellemeyi yüklemek için Hydra’yı yeniden başlatın", + "notification_achievement_unlocked_title": "{{game}} için başarı açıldı", + "notification_achievement_unlocked_body": "{{achievement}} ve {{count}} diğer başarı açıldı", + "new_friend_request_description": "{{displayName}} size bir arkadaşlık isteği gönderdi", "new_friend_request_title": "Yeni arkadaşlık isteği", - "extraction_complete": "Çıkartma tamamlandı", - "game_extracted": "{{title}} başarıyla çıkartıldı" + "extraction_complete": "Çıkarma tamamlandı", + "game_extracted": "{{title}} başarıyla çıkarıldı", + "friend_started_playing_game": "{{displayName}} bir oyun oynamaya başladı", + "test_achievement_notification_title": "Bu bir test bildirimi", + "test_achievement_notification_description": "Oldukça havalı, değil mi?" }, "system_tray": { "open": "Hydra'yı Aç", "quit": "Çık" }, "game_card": { - "no_downloads": "İndirilebilir içerik bulunmuyor", "available_one": "Mevcut", - "available_other": "Mevcut" + "available_other": "Mevcut", + "no_downloads": "İndirme mevcut değil" }, "binary_not_found_modal": { "title": "Programlar Yüklü Değil", - "description": "Wine veya Lutris çalıştırılabilir dosyaları sisteminizde bulunamadı", - "instructions": "Oyunun normal çalışabilmesi için bunlardan herhangi birini Linux dağıtımınıza uygun şekilde nasıl kuracağınızı kontrol edin" + "description": "Sisteminizde Wine veya Lutris çalıştırılabilir dosyaları bulunamadı", + "instructions": "Oyunun sorunsuz çalışması için Linux dağıtımınızda bunların nasıl kurulacağını kontrol edin" }, "modal": { "close": "Kapat düğmesi" }, "forms": { - "toggle_password_visibility": "Şifre görünürlüğünü değiştir" + "toggle_password_visibility": "Şifreyi göster/gizle" }, "user_profile": { "amount_hours": "{{amount}} saat", "amount_minutes": "{{amount}} dakika", - "last_time_played": "Son oynanma {{period}}", + "last_time_played": "Son oynanma: {{period}}", "activity": "Son Etkinlik", "library": "Kütüphane", "total_play_time": "Toplam oynama süresi", "no_recent_activity_title": "Hmmm… burada bir şey yok", - "no_recent_activity_description": "Son zamanlarda hiç oyun oynamamışsınız. Bunu değiştirmenin zamanı geldi!", - "display_name": "Görünen isim", + "no_recent_activity_description": "Son zamanlarda hiç oyun oynamadınız. Bunu değiştirmenin zamanı geldi!", + "display_name": "Kullanıcı adı", "saving": "Kaydediliyor", "save": "Kaydet", "edit_profile": "Profili Düzenle", "saved_successfully": "Başarıyla kaydedildi", "try_again": "Lütfen tekrar deneyin", - "sign_out_modal_title": "Emin misiniz?", + "sign_out_modal_title": "Çıkmak istediğinizden emin misiniz?", "cancel": "İptal", "successfully_signed_out": "Başarıyla çıkış yapıldı", "sign_out": "Çıkış yap", "playing_for": "{{amount}} oynanıyor", - "sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Oturumu kapattığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Oturumu kapatmaya devam etmek istiyor musunuz?", + "sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Çıkış yaparsanız, kütüphaneniz görünmeyecek ve ilerlemeniz kaydedilmeyecek. Yine de çıkış yapılsın mı?", "add_friends": "Arkadaş Ekle", "add": "Ekle", "friend_code": "Arkadaş kodu", - "see_profile": "Profili gör", + "see_profile": "Profili Görüntüle", "sending": "Gönderiliyor", "friend_request_sent": "Arkadaşlık isteği gönderildi", "friends": "Arkadaşlar", @@ -428,79 +455,79 @@ "request_sent": "İstek gönderildi", "request_received": "İstek alındı", "accept_request": "İsteği kabul et", - "ignore_request": "İsteği yok say", + "ignore_request": "İsteği görmezden gel", "cancel_request": "İsteği iptal et", - "undo_friendship": "Arkadaşlığı sonlandır", + "undo_friendship": "Arkadaşlığı kaldır", "request_accepted": "İstek kabul edildi", "user_blocked_successfully": "Kullanıcı başarıyla engellendi", - "user_block_modal_text": "Bu işlem {{displayName}} adlı kullanıcıyı engelleyecek", + "user_block_modal_text": "{{displayName}} engellenecek", "blocked_users": "Engellenen kullanıcılar", "unblock": "Engeli kaldır", - "no_friends_added": "Hiç arkadaş eklemediniz", - "pending": "Bekliyor", + "no_friends_added": "Hiç arkadaşınız yok", + "pending": "Bekleyen", "no_pending_invites": "Bekleyen davetiniz yok", - "no_blocked_users": "Engellenmiş kullanıcı yok", + "no_blocked_users": "Engellenen kullanıcı yok", "friend_code_copied": "Arkadaş kodu kopyalandı", - "undo_friendship_modal_text": "Bu işlem {{displayName}} ile arkadaşlığınızı sonlandıracak", - "privacy_hint": "Bunu kimin görebileceğini ayarlamak için <0>Ayarlar bölümüne gidin", + "undo_friendship_modal_text": "Bu işlemle {{displayName}} ile arkadaşlığınız kaldırılacak", + "privacy_hint": "Bunu kimlerin görebileceğini <0>Ayarlar bölümünden değiştirebilirsiniz", "locked_profile": "Bu profil gizli", - "image_process_failure": "Görüntü işleme başarısız oldu", - "required_field": "Bu alan gerekli", - "displayname_min_length": "Görünen isim en az 3 karakter uzunluğunda olmalıdır", - "displayname_max_length": "Görünen isim en fazla 50 karakter uzunluğunda olabilir", - "report_profile": "Bu profili bildir", - "report_reason": "Bu profili neden bildiriyorsunuz?", + "image_process_failure": "Resim işlenirken hata oluştu", + "required_field": "Bu alan gereklidir", + "displayname_min_length": "Kullanıcı adı en az 3 karakter olmalıdır", + "displayname_max_length": "Kullanıcı adı en fazla 50 karakter olmalıdır", + "report_profile": "Bu profili şikayet et", + "report_reason": "Bu profili neden şikayet ediyorsunuz?", "report_description": "Ek bilgi", "report_description_placeholder": "Ek bilgi", - "report": "Bildir", + "report": "Şikayet et", "report_reason_hate": "Nefret söylemi", "report_reason_sexual_content": "Cinsel içerik", "report_reason_violence": "Şiddet", "report_reason_spam": "Spam", "report_reason_other": "Diğer", - "profile_reported": "Profil bildirildi", + "profile_reported": "Profil şikayet edildi", "your_friend_code": "Arkadaş kodunuz:", - "upload_banner": "Afiş yükle", - "uploading_banner": "Afiş yükleniyor…", - "background_image_updated": "Arka plan görüntüsü güncellendi", + "upload_banner": "Banner yükle", + "uploading_banner": "Banner yükleniyor…", + "background_image_updated": "Arka plan resmi güncellendi", "stats": "İstatistikler", "achievements": "Başarımlar", "games": "Oyunlar", - "top_percentile": "En üst {{percentile}}%", - "ranking_updated_weekly": "Sıralama haftalık olarak güncellenir", + "top_percentile": "En iyi %{{percentile}}", + "ranking_updated_weekly": "Sıralama haftalık güncellenir", "playing": "{{game}} oynanıyor", - "achievements_unlocked": "Başarımlar açıldı", + "achievements_unlocked": "Açılan başarımlar", "earned_points": "Kazanılan puanlar", - "show_achievements_on_profile": "Başarımlarınızı profilinizde gösterin", - "show_points_on_profile": "Kazandığınız puanları profilinizde gösterin" + "show_achievements_on_profile": "Başarımlarını profilinde göster", + "show_points_on_profile": "Kazanılan puanlarını profilinde göster" }, "achievement": { "achievement_unlocked": "Başarım açıldı", - "user_achievements": "{{displayName}} oyununun Başarımları", + "user_achievements": "{{displayName}}'nın Başarımları", "your_achievements": "Başarımlarınız", - "unlocked_at": "Açılma zamanı: {{date}}", - "subscription_needed": "Bu içeriği görmek için bir Hydra Cloud aboneliği gereklidir", - "new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarım açıldı", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} başarım", - "achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarım açıldı", - "hidden_achievement_tooltip": "Bu gizli bir başarımdır", - "achievement_earn_points": "Bu başarım ile {{points}} puan kazanın", + "unlocked_at": "Açıldığı tarih: {{date}}", + "subscription_needed": "Bu içeriği görmek için Hydra Cloud aboneliği gereklidir", + "new_achievements_unlocked": "{{gameCount}} oyunda {{achievementCount}} yeni başarı açıldı", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} başarı", + "achievements_unlocked_for_game": "{{gameTitle}} için {{achievementCount}} yeni başarı açıldı", + "hidden_achievement_tooltip": "Bu gizli bir başarıdır", + "achievement_earn_points": "Bu başarı ile {{points}} puan kazan", "earned_points": "Kazanılan puanlar:", "available_points": "Mevcut puanlar:", - "how_to_earn_achievements_points": "Başarım puanları nasıl kazanılır?" + "how_to_earn_achievements_points": "Başarı puanları nasıl kazanılır?" }, "hydra_cloud": { "subscription_tour_title": "Hydra Cloud Aboneliği", - "subscribe_now": "Şimdi abone olun", - "cloud_saving": "Bulut kaydetme", - "cloud_achievements": "Başarımlarınızı buluta kaydedin", + "subscribe_now": "Şimdi abone ol", + "cloud_saving": "Bulut kaydı", + "cloud_achievements": "Başarımlarınızı bulutta saklayın", "animated_profile_picture": "Animasyonlu profil resimleri", - "premium_support": "Premium Destek", + "premium_support": "Öncelikli Destek", "show_and_compare_achievements": "Başarımlarınızı diğer kullanıcılarla karşılaştırın ve gösterin", "animated_profile_banner": "Animasyonlu profil afişi", "hydra_cloud": "Hydra Cloud", - "hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!", - "learn_more": "Daha Fazla Bilgi Edinin", + "hydra_cloud_feature_found": "Bir Hydra Cloud özelliğini keşfettiniz!", + "learn_more": "Daha fazla bilgi al", "debrid_description": "Nimbus ile 4 kata kadar daha hızlı indirin" } } diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index bed8a167..48a3972d 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -27,7 +27,8 @@ "sign_in": "Увійти", "favorites": "Улюблені", "friends": "Друзі", - "need_help": "Потрібна допомога?" + "need_help": "Потрібна допомога?", + "playable_button_title": "Показати лише ігри, які можна грати зараз" }, "header": { "search": "Пошук", diff --git a/src/main/constants.ts b/src/main/constants.ts index 243891e7..16642d50 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -26,11 +26,10 @@ export const commonRedistPath = path.join( "CommonRedist" ); -export const logsPath = path.join(SystemPath.getPath("userData"), "logs"); - -export const seedsPath = app.isPackaged - ? path.join(process.resourcesPath, "seeds") - : path.join(__dirname, "..", "..", "seeds"); +export const logsPath = path.join( + SystemPath.getPath("userData"), + `logs${isStaging ? "-staging" : ""}` +); export const achievementSoundPath = app.isPackaged ? path.join(process.resourcesPath, "achievement.wav") diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 94520467..28ea70a9 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -20,6 +20,7 @@ import "./library/create-game-shortcut"; import "./library/close-game"; import "./library/delete-game-folder"; import "./library/get-game-by-object-id"; +import "./library/sync-game-by-object-id"; import "./library/get-library"; import "./library/extract-game-download"; import "./library/open-game"; @@ -88,6 +89,8 @@ import "./cloud-save/delete-game-artifact"; import "./cloud-save/select-game-backup-path"; import "./cloud-save/toggle-artifact-freeze"; import "./notifications/publish-new-repacks-notification"; +import "./notifications/update-achievement-notification-window"; +import "./notifications/show-achievement-test-notification"; import "./themes/add-custom-theme"; import "./themes/delete-custom-theme"; import "./themes/get-all-custom-themes"; diff --git a/src/main/events/library/open-game-installer-path.ts b/src/main/events/library/open-game-installer-path.ts index b61246fa..0bbe96de 100644 --- a/src/main/events/library/open-game-installer-path.ts +++ b/src/main/events/library/open-game-installer-path.ts @@ -12,16 +12,14 @@ const openGameInstallerPath = async ( ) => { const download = await downloadsSublevel.get(levelKeys.game(shop, objectId)); - if (!download || !download.folderName || !download.downloadPath) return true; + if (!download?.folderName || !download.downloadPath) return; const gamePath = path.join( download.downloadPath ?? (await getDownloadsPath()), - download.folderName! + download.folderName ); shell.showItemInFolder(gamePath); - - return true; }; registerEvent("openGameInstallerPath", openGameInstallerPath); diff --git a/src/main/events/library/sync-game-by-object-id.ts b/src/main/events/library/sync-game-by-object-id.ts new file mode 100644 index 00000000..6235a09c --- /dev/null +++ b/src/main/events/library/sync-game-by-object-id.ts @@ -0,0 +1,28 @@ +import { registerEvent } from "../register-event"; +import { gamesSublevel, levelKeys } from "@main/level"; +import { HydraApi } from "@main/services"; +import type { GameShop, UserGameDetails } from "@types"; + +const syncGameByObjectId = async ( + _event: Electron.IpcMainInvokeEvent, + shop: GameShop, + objectId: string +) => { + return HydraApi.get( + `/profile/games/${shop}/${objectId}` + ).then(async (res) => { + const { id, playTimeInSeconds, ...rest } = res; + + const gameKey = levelKeys.game(shop, objectId); + + await gamesSublevel.put(gameKey, { + ...rest, + remoteId: id, + playTimeInMilliseconds: playTimeInSeconds * 1000, + }); + + return res; + }); +}; + +registerEvent("syncGameByObjectId", syncGameByObjectId); diff --git a/src/main/events/notifications/show-achievement-test-notification.ts b/src/main/events/notifications/show-achievement-test-notification.ts new file mode 100644 index 00000000..b6f425f3 --- /dev/null +++ b/src/main/events/notifications/show-achievement-test-notification.ts @@ -0,0 +1,15 @@ +import { registerEvent } from "../register-event"; +import { WindowManager } from "@main/services"; + +const showAchievementTestNotification = async ( + _event: Electron.IpcMainInvokeEvent +) => { + setTimeout(() => { + WindowManager.showAchievementTestNotification(); + }, 1000); +}; + +registerEvent( + "showAchievementTestNotification", + showAchievementTestNotification +); diff --git a/src/main/events/notifications/update-achievement-notification-window.ts b/src/main/events/notifications/update-achievement-notification-window.ts new file mode 100644 index 00000000..02dd45b1 --- /dev/null +++ b/src/main/events/notifications/update-achievement-notification-window.ts @@ -0,0 +1,29 @@ +import { db, levelKeys } from "@main/level"; +import { registerEvent } from "../register-event"; +import { WindowManager } from "@main/services"; +import { UserPreferences } from "@types"; + +const updateAchievementCustomNotificationWindow = async ( + _event: Electron.IpcMainInvokeEvent +) => { + const userPreferences = await db.get( + levelKeys.userPreferences, + { + valueEncoding: "json", + } + ); + + WindowManager.closeNotificationWindow(); + + if ( + userPreferences.achievementNotificationsEnabled !== false && + userPreferences.achievementCustomNotificationsEnabled !== false + ) { + WindowManager.createNotificationWindow(); + } +}; + +registerEvent( + "updateAchievementCustomNotificationWindow", + updateAchievementCustomNotificationWindow +); diff --git a/src/main/events/themes/toggle-custom-theme.ts b/src/main/events/themes/toggle-custom-theme.ts index 50440551..59f66793 100644 --- a/src/main/events/themes/toggle-custom-theme.ts +++ b/src/main/events/themes/toggle-custom-theme.ts @@ -1,5 +1,6 @@ import { themesSublevel } from "@main/level"; import { registerEvent } from "../register-event"; +import { WindowManager } from "@main/services"; const toggleCustomTheme = async ( _event: Electron.IpcMainInvokeEvent, @@ -17,6 +18,8 @@ const toggleCustomTheme = async ( isActive, updatedAt: new Date(), }); + + WindowManager.notificationWindow?.webContents.send("on-custom-theme-updated"); }; registerEvent("toggleCustomTheme", toggleCustomTheme); diff --git a/src/main/events/themes/update-custom-theme.ts b/src/main/events/themes/update-custom-theme.ts index b9a8e048..f71c50da 100644 --- a/src/main/events/themes/update-custom-theme.ts +++ b/src/main/events/themes/update-custom-theme.ts @@ -20,7 +20,10 @@ const updateCustomTheme = async ( }); if (theme.isActive) { - WindowManager.mainWindow?.webContents.send("css-injected", code); + WindowManager.mainWindow?.webContents.send("on-custom-theme-updated"); + WindowManager.notificationWindow?.webContents.send( + "on-custom-theme-updated" + ); } }; diff --git a/src/main/index.ts b/src/main/index.ts index 3b223299..de88a548 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,7 +4,7 @@ import i18n from "i18next"; import path from "node:path"; import url from "node:url"; import { electronApp, optimizer } from "@electron-toolkit/utils"; -import { logger, WindowManager } from "@main/services"; +import { logger, clearGamesPlaytime, WindowManager } from "@main/services"; import resources from "@locales"; import { PythonRPC } from "./services/python-rpc"; import { db, levelKeys } from "./level"; @@ -73,6 +73,7 @@ app.whenReady().then(async () => { WindowManager.createMainWindow(); } + WindowManager.createNotificationWindow(); WindowManager.createSystemTray(language || "en"); }); @@ -142,9 +143,17 @@ app.on("window-all-closed", () => { WindowManager.mainWindow = null; }); -app.on("before-quit", () => { - /* Disconnects libtorrent */ - PythonRPC.kill(); +let canAppBeClosed = false; + +app.on("before-quit", async (e) => { + if (!canAppBeClosed) { + e.preventDefault(); + /* Disconnects libtorrent */ + PythonRPC.kill(); + await clearGamesPlaytime(); + canAppBeClosed = true; + app.quit(); + } }); app.on("activate", () => { diff --git a/src/main/main.ts b/src/main/main.ts index a080eec2..54191b9f 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -40,6 +40,7 @@ export const loadState = async () => { } Ludusavi.copyConfigFileToUserData(); + Ludusavi.copyBinaryToUserData(); await HydraApi.setupApi().then(() => { uploadGamesBatch(); diff --git a/src/main/services/achievements/achievement-watcher-manager.ts b/src/main/services/achievements/achievement-watcher-manager.ts index 8b076d9e..5cf09d4f 100644 --- a/src/main/services/achievements/achievement-watcher-manager.ts +++ b/src/main/services/achievements/achievement-watcher-manager.ts @@ -7,11 +7,18 @@ import { findAllAchievementFiles, getAlternativeObjectIds, } from "./find-achivement-files"; -import type { AchievementFile, Game, UnlockedAchievement } from "@types"; +import type { + AchievementFile, + Game, + UnlockedAchievement, + UserPreferences, +} from "@types"; import { achievementsLogger } from "../logger"; import { Cracker } from "@shared"; import { publishCombinedNewAchievementNotification } from "../notifications"; -import { gamesSublevel } from "@main/level"; +import { db, gamesSublevel, levelKeys } from "@main/level"; +import { WindowManager } from "../window-manager"; +import { sleep } from "@main/helpers"; const fileStats: Map = new Map(); const fltFiles: Map> = new Map(); @@ -184,7 +191,7 @@ export class AchievementWatcherManager { return mergeAchievements(game, unlockedAchievements, false); } - private static preSearchAchievementsWindows = async () => { + private static async getGameAchievementFilesWindows() { const games = await gamesSublevel .values() .all() @@ -194,24 +201,24 @@ export class AchievementWatcherManager { return Promise.all( games.map((game) => { - const gameAchievementFiles: AchievementFile[] = []; + const achievementFiles: AchievementFile[] = []; for (const objectId of getAlternativeObjectIds(game.objectId)) { - gameAchievementFiles.push( + achievementFiles.push( ...(gameAchievementFilesMap.get(objectId) || []) ); - gameAchievementFiles.push( + achievementFiles.push( ...findAchievementFileInExecutableDirectory(game) ); } - return this.preProcessGameAchievementFiles(game, gameAchievementFiles); + return { game, achievementFiles }; }) ); - }; + } - private static preSearchAchievementsWithWine = async () => { + private static async getGameAchievementFilesLinux() { const games = await gamesSublevel .values() .all() @@ -219,37 +226,70 @@ export class AchievementWatcherManager { return Promise.all( games.map((game) => { - const gameAchievementFiles = findAchievementFiles(game); + const achievementFiles = findAchievementFiles(game); const achievementFileInsideDirectory = findAchievementFileInExecutableDirectory(game); - gameAchievementFiles.push(...achievementFileInsideDirectory); + achievementFiles.push(...achievementFileInsideDirectory); - return this.preProcessGameAchievementFiles(game, gameAchievementFiles); + return { game, achievementFiles }; }) ); - }; + } public static async preSearchAchievements() { + await sleep(2000); + try { - const newAchievementsCount = + const gameAchievementFiles = process.platform === "win32" - ? await this.preSearchAchievementsWindows() - : await this.preSearchAchievementsWithWine(); + ? await this.getGameAchievementFilesWindows() + : await this.getGameAchievementFilesLinux(); + + const newAchievementsCount: number[] = []; + + for (const { game, achievementFiles } of gameAchievementFiles) { + const result = await this.preProcessGameAchievementFiles( + game, + achievementFiles + ); + + newAchievementsCount.push(result); + } const totalNewGamesWithAchievements = newAchievementsCount.filter( (achievements) => achievements ).length; + const totalNewAchievements = newAchievementsCount.reduce( (acc, val) => acc + val, 0 ); if (totalNewAchievements > 0) { - publishCombinedNewAchievementNotification( - totalNewAchievements, - totalNewGamesWithAchievements + const userPreferences = await db.get( + levelKeys.userPreferences, + { + valueEncoding: "json", + } ); + + if (userPreferences.achievementNotificationsEnabled !== false) { + if (userPreferences.achievementCustomNotificationsEnabled !== false) { + WindowManager.notificationWindow?.webContents.send( + "on-combined-achievements-unlocked", + totalNewGamesWithAchievements, + totalNewAchievements, + userPreferences.achievementCustomNotificationPosition ?? + "top-left" + ); + } else { + publishCombinedNewAchievementNotification( + totalNewAchievements, + totalNewGamesWithAchievements + ); + } + } } } catch (err) { achievementsLogger.error("Error on preSearchAchievements", err); diff --git a/src/main/services/achievements/get-game-achievement-data.ts b/src/main/services/achievements/get-game-achievement-data.ts index 0f351dcb..f4d66b6a 100644 --- a/src/main/services/achievements/get-game-achievement-data.ts +++ b/src/main/services/achievements/get-game-achievement-data.ts @@ -38,7 +38,9 @@ export const getGameAchievementData = async ( await gameAchievementsSublevel.put(levelKeys.game(shop, objectId), { unlockedAchievements: cachedAchievements?.unlockedAchievements ?? [], achievements, - cacheExpiresTimestamp: Date.now() + 1000 * 60 * 30, // 30 minutes + cacheExpiresTimestamp: achievements.length + ? Date.now() + 1000 * 60 * 30 // 30 minutes + : undefined, }); return achievements; diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index 0385f2fc..2674e451 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -1,4 +1,5 @@ import type { + AchievementNotificationInfo, Game, GameShop, UnlockedAchievement, @@ -12,6 +13,13 @@ import { publishNewAchievementNotification } from "../notifications"; import { SubscriptionRequiredError } from "@shared"; import { achievementsLogger } from "../logger"; import { db, gameAchievementsSublevel, levelKeys } from "@main/level"; +import { getGameAchievementData } from "./get-game-achievement-data"; + +const isRareAchievement = (points: number) => { + const rawPercentage = (50 - Math.sqrt(points)) * 2; + + return rawPercentage < 10; +}; const saveAchievementsOnLocal = async ( objectId: string, @@ -48,12 +56,22 @@ export const mergeAchievements = async ( achievements: UnlockedAchievement[], publishNotification: boolean ) => { - const [localGameAchievement, userPreferences] = await Promise.all([ - gameAchievementsSublevel.get(levelKeys.game(game.shop, game.objectId)), - db.get(levelKeys.userPreferences, { + let localGameAchievement = await gameAchievementsSublevel.get( + levelKeys.game(game.shop, game.objectId) + ); + const userPreferences = await db.get( + levelKeys.userPreferences, + { valueEncoding: "json", - }), - ]); + } + ); + + if (!localGameAchievement) { + await getGameAchievementData(game.objectId, game.shop, true); + localGameAchievement = await gameAchievementsSublevel.get( + levelKeys.game(game.shop, game.objectId) + ); + } const achievementsData = localGameAchievement?.achievements ?? []; const unlockedAchievements = localGameAchievement?.unlockedAchievements ?? []; @@ -84,9 +102,9 @@ export const mergeAchievements = async ( if ( newAchievements.length && publishNotification && - userPreferences?.achievementNotificationsEnabled + userPreferences.achievementNotificationsEnabled !== false ) { - const achievementsInfo = newAchievements + const filteredAchievements = newAchievements .toSorted((a, b) => { return a.unlockTime - b.unlockTime; }) @@ -98,21 +116,41 @@ export const mergeAchievements = async ( ); }); }) - .filter((achievement) => Boolean(achievement)) - .map((achievement) => { + .filter((achievement) => !!achievement); + + const achievementsInfo: AchievementNotificationInfo[] = + filteredAchievements.map((achievement, index) => { return { - displayName: achievement!.displayName, - iconUrl: achievement!.icon, + title: achievement.displayName, + description: achievement.description, + points: achievement.points, + isHidden: achievement.hidden, + isRare: achievement.points + ? isRareAchievement(achievement.points) + : false, + isPlatinum: + index === filteredAchievements.length - 1 && + newAchievements.length + unlockedAchievements.length === + achievementsData.length, + iconUrl: achievement.icon, }; }); - publishNewAchievementNotification({ - achievements: achievementsInfo, - unlockedAchievementCount: mergedLocalAchievements.length, - totalAchievementCount: achievementsData.length, - gameTitle: game.title, - gameIcon: game.iconUrl, - }); + if (userPreferences.achievementCustomNotificationsEnabled !== false) { + WindowManager.notificationWindow?.webContents.send( + "on-achievement-unlocked", + userPreferences.achievementCustomNotificationPosition ?? "top-left", + achievementsInfo + ); + } else { + publishNewAchievementNotification({ + achievements: achievementsInfo, + unlockedAchievementCount: mergedLocalAchievements.length, + totalAchievementCount: achievementsData.length, + gameTitle: game.title, + gameIcon: game.iconUrl, + }); + } } if (game.remoteId) { diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 122960c7..0f5a4d21 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -42,7 +42,7 @@ export class HydraApi { subscription: null, }; - private static isLoggedIn() { + public static isLoggedIn() { return this.userAuth.authToken !== ""; } diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index 3593ac11..4294e389 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -33,7 +33,9 @@ export const uploadGamesBatch = async () => { await mergeWithRemoteGames(); - AchievementWatcherManager.preSearchAchievements(); + if (HydraApi.isLoggedIn()) { + AchievementWatcherManager.preSearchAchievements(); + } if (WindowManager.mainWindow) WindowManager.mainWindow.webContents.send("on-library-batch-complete"); diff --git a/src/main/services/ludusavi.ts b/src/main/services/ludusavi.ts index 0f060ccb..133f764e 100644 --- a/src/main/services/ludusavi.ts +++ b/src/main/services/ludusavi.ts @@ -8,19 +8,19 @@ import cp from "node:child_process"; import { SystemPath } from "./system-path"; export class Ludusavi { - private static ludusaviPath = app.isPackaged + private static ludusaviResourcesPath = app.isPackaged ? path.join(process.resourcesPath, "ludusavi") : path.join(__dirname, "..", "..", "ludusavi"); - private static binaryPath = path.join(this.ludusaviPath, "ludusavi"); private static configPath = path.join( SystemPath.getPath("userData"), "ludusavi" ); + private static binaryPath = path.join(this.configPath, "ludusavi"); public static async getConfig() { const config = YAML.parse( - fs.readFileSync(path.join(this.ludusaviPath, "config.yaml"), "utf-8") + fs.readFileSync(path.join(this.configPath, "config.yaml"), "utf-8") ) as LudusaviConfig; return config; @@ -29,13 +29,23 @@ export class Ludusavi { public static async copyConfigFileToUserData() { if (!fs.existsSync(this.configPath)) { fs.mkdirSync(this.configPath, { recursive: true }); + fs.cpSync( - path.join(this.ludusaviPath, "config.yaml"), + path.join(this.ludusaviResourcesPath, "config.yaml"), path.join(this.configPath, "config.yaml") ); } } + public static async copyBinaryToUserData() { + if (!fs.existsSync(this.binaryPath)) { + fs.cpSync( + path.join(this.ludusaviResourcesPath, "ludusavi"), + this.binaryPath + ); + } + } + public static async backupGame( _shop: GameShop, objectId: string, diff --git a/src/main/services/notifications/index.ts b/src/main/services/notifications/index.ts index 77866a47..fa9ac593 100644 --- a/src/main/services/notifications/index.ts +++ b/src/main/services/notifications/index.ts @@ -162,7 +162,7 @@ export const publishExtractionCompleteNotification = async (game: Game) => { }; export const publishNewAchievementNotification = async (info: { - achievements: { displayName: string; iconUrl: string }[]; + achievements: { title: string; iconUrl: string }[]; unlockedAchievementCount: number; totalAchievementCount: number; gameTitle: string; @@ -176,12 +176,12 @@ export const publishNewAchievementNotification = async (info: { gameTitle: info.gameTitle, achievementCount: info.achievements.length, }), - body: info.achievements.map((a) => a.displayName).join(", "), + body: info.achievements.map((a) => a.title).join(", "), icon: (await downloadImage(info.gameIcon)) ?? icon, } : { title: t("achievement_unlocked", { ns: "achievement" }), - body: info.achievements[0].displayName, + body: info.achievements[0].title, icon: (await downloadImage(info.achievements[0].iconUrl)) ?? icon, }; diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index fd3987df..deb93f4e 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -28,7 +28,7 @@ interface GameExecutables { [key: string]: ExecutableInfo[]; } -const TICKS_TO_UPDATE_API = 120; +const TICKS_TO_UPDATE_API = 80; let currentTick = 1; const isWindowsPlatform = process.platform === "win32"; @@ -225,7 +225,18 @@ function onOpenGame(game: Game) { }); if (game.remoteId) { - updateGamePlaytime(game, 0, new Date()).catch(() => {}); + updateGamePlaytime( + game, + game.unsyncedDeltaPlayTimeInMilliseconds ?? 0, + new Date() + ) + .then(() => { + gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { + ...game, + unsyncedDeltaPlayTimeInMilliseconds: 0, + }); + }) + .catch(() => {}); if (game.automaticCloudSync) { CloudSync.uploadSaveGame( @@ -250,13 +261,7 @@ function onTickGame(game: Game) { gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { ...game, - playTimeInMilliseconds: game.playTimeInMilliseconds + delta, - lastTimePlayed: new Date(), - }); - - gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { - ...game, - playTimeInMilliseconds: game.playTimeInMilliseconds + delta, + playTimeInMilliseconds: (game.playTimeInMilliseconds ?? 0) + delta, lastTimePlayed: new Date(), }); @@ -266,22 +271,34 @@ function onTickGame(game: Game) { }); if (currentTick % TICKS_TO_UPDATE_API === 0) { + const deltaToSync = + now - + gamePlaytime.lastSyncTick + + (game.unsyncedDeltaPlayTimeInMilliseconds ?? 0); + const gamePromise = game.remoteId - ? updateGamePlaytime( - game, - now - gamePlaytime.lastSyncTick, - game.lastTimePlayed! - ) + ? updateGamePlaytime(game, deltaToSync, game.lastTimePlayed!) : createGame(game); gamePromise .then(() => { + gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { + ...game, + unsyncedDeltaPlayTimeInMilliseconds: 0, + }); + }) + .catch(() => { + gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { + ...game, + unsyncedDeltaPlayTimeInMilliseconds: deltaToSync, + }); + }) + .finally(() => { gamesPlaytime.set(levelKeys.game(game.shop, game.objectId), { ...gamePlaytime, lastSyncTick: now, }); - }) - .catch(() => {}); + }); } } @@ -292,12 +309,6 @@ const onCloseGame = (game: Game) => { gamesPlaytime.delete(levelKeys.game(game.shop, game.objectId)); if (game.remoteId) { - updateGamePlaytime( - game, - performance.now() - gamePlaytime.lastSyncTick, - game.lastTimePlayed! - ).catch(() => {}); - if (game.automaticCloudSync) { CloudSync.uploadSaveGame( game.objectId, @@ -306,7 +317,38 @@ const onCloseGame = (game: Game) => { CloudSync.getBackupLabel(true) ); } + + const deltaToSync = + performance.now() - + gamePlaytime.lastSyncTick + + (game.unsyncedDeltaPlayTimeInMilliseconds ?? 0); + + return updateGamePlaytime(game, deltaToSync, game.lastTimePlayed!) + .then(() => { + return gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { + ...game, + unsyncedDeltaPlayTimeInMilliseconds: 0, + }); + }) + .catch(() => { + return gamesSublevel.put(levelKeys.game(game.shop, game.objectId), { + ...game, + unsyncedDeltaPlayTimeInMilliseconds: deltaToSync, + }); + }); } else { - createGame(game).catch(() => {}); + return createGame(game).catch(() => {}); } }; + +export const clearGamesPlaytime = async () => { + for (const game of gamesPlaytime.keys()) { + const gameData = await gamesSublevel.get(game); + + if (gameData) { + await onCloseGame(gameData); + } + } + + gamesPlaytime.clear(); +}; diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 9841eb6e..3d84d6f3 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -6,6 +6,7 @@ import { Tray, app, nativeImage, + screen, shell, } from "electron"; import { is } from "@electron-toolkit/utils"; @@ -17,12 +18,17 @@ import { HydraApi } from "./hydra-api"; import UserAgent from "user-agents"; import { db, gamesSublevel, levelKeys } from "@main/level"; import { orderBy, slice } from "lodash-es"; -import type { ScreenState, UserPreferences } from "@types"; -import { AuthPage } from "@shared"; +import type { + AchievementCustomNotificationPosition, + ScreenState, + UserPreferences, +} from "@types"; +import { AuthPage, generateAchievementCustomNotificationTest } from "@shared"; import { isStaging } from "@main/constants"; export class WindowManager { public static mainWindow: Electron.BrowserWindow | null = null; + public static notificationWindow: Electron.BrowserWindow | null = null; private static readonly editorWindows: Map = new Map(); @@ -259,6 +265,156 @@ export class WindowManager { } } + private static loadNotificationWindowURL() { + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + this.notificationWindow?.loadURL( + `${process.env["ELECTRON_RENDERER_URL"]}#/achievement-notification` + ); + } else { + this.notificationWindow?.loadFile( + path.join(__dirname, "../renderer/index.html"), + { + hash: "achievement-notification", + } + ); + } + } + + private static readonly NOTIFICATION_WINDOW_WIDTH = 360; + private static readonly NOTIFICATION_WINDOW_HEIGHT = 140; + + private static async getNotificationWindowPosition( + position: AchievementCustomNotificationPosition | undefined + ) { + const display = screen.getPrimaryDisplay(); + const { width, height } = display.workAreaSize; + + if (position === "bottom-left") { + return { + x: 0, + y: height - this.NOTIFICATION_WINDOW_HEIGHT, + }; + } + + if (position === "bottom-center") { + return { + x: (width - this.NOTIFICATION_WINDOW_WIDTH) / 2, + y: height - this.NOTIFICATION_WINDOW_HEIGHT, + }; + } + + if (position === "bottom-right") { + return { + x: width - this.NOTIFICATION_WINDOW_WIDTH, + y: height - this.NOTIFICATION_WINDOW_HEIGHT, + }; + } + + if (position === "top-center") { + return { + x: (width - this.NOTIFICATION_WINDOW_WIDTH) / 2, + y: 0, + }; + } + + if (position === "top-right") { + return { + x: width - this.NOTIFICATION_WINDOW_WIDTH, + y: 0, + }; + } + + return { + x: 0, + y: 0, + }; + } + + public static async createNotificationWindow() { + if (this.notificationWindow) return; + + const userPreferences = await db.get( + levelKeys.userPreferences, + { + valueEncoding: "json", + } + ); + + if ( + userPreferences?.achievementNotificationsEnabled === false || + userPreferences?.achievementCustomNotificationsEnabled === false + ) { + return; + } + + const { x, y } = await this.getNotificationWindowPosition( + userPreferences?.achievementCustomNotificationPosition + ); + + this.notificationWindow = new BrowserWindow({ + transparent: true, + maximizable: false, + autoHideMenuBar: true, + minimizable: false, + backgroundColor: "#00000000", + focusable: false, + skipTaskbar: true, + frame: false, + width: this.NOTIFICATION_WINDOW_WIDTH, + height: this.NOTIFICATION_WINDOW_HEIGHT, + x, + y, + webPreferences: { + preload: path.join(__dirname, "../preload/index.mjs"), + sandbox: false, + }, + }); + this.notificationWindow.setIgnoreMouseEvents(true); + // this.notificationWindow.setVisibleOnAllWorkspaces(true, { + // visibleOnFullScreen: true, + // }); + + this.notificationWindow.setAlwaysOnTop(true, "screen-saver", 1); + this.loadNotificationWindowURL(); + + if (isStaging) { + this.notificationWindow.webContents.openDevTools(); + } + } + + public static async showAchievementTestNotification() { + const userPreferences = await db.get( + levelKeys.userPreferences, + { + valueEncoding: "json", + } + ); + + const language = userPreferences.language ?? "en"; + + this.notificationWindow?.webContents.send( + "on-achievement-unlocked", + userPreferences.achievementCustomNotificationPosition ?? "top-left", + [ + generateAchievementCustomNotificationTest(t, language), + generateAchievementCustomNotificationTest(t, language, { + isRare: true, + isHidden: true, + }), + generateAchievementCustomNotificationTest(t, language, { + isPlatinum: true, + }), + ] + ); + } + + public static async closeNotificationWindow() { + if (this.notificationWindow) { + this.notificationWindow.close(); + this.notificationWindow = null; + } + } + public static openEditorWindow(themeId: string) { if (this.mainWindow) { const existingWindow = this.editorWindows.get(themeId); @@ -271,13 +427,13 @@ export class WindowManager { } const editorWindow = new BrowserWindow({ - width: 600, + width: 720, height: 720, minWidth: 600, minHeight: 540, backgroundColor: "#1c1c1c", titleBarStyle: process.platform === "linux" ? "default" : "hidden", - ...(process.platform === "linux" ? { icon } : {}), + icon, trafficLightPosition: { x: 16, y: 16 }, titleBarOverlay: { symbolColor: "#DADBE1", @@ -313,9 +469,8 @@ export class WindowManager { } }); - editorWindow.webContents.on("before-input-event", (event, input) => { + editorWindow.webContents.on("before-input-event", (_event, input) => { if (input.key === "F12") { - event.preventDefault(); this.mainWindow?.webContents.toggleDevTools(); } }); diff --git a/src/main/services/ws/events/friend-game-session.ts b/src/main/services/ws/events/friend-game-session.ts index 930b4885..47d8164e 100644 --- a/src/main/services/ws/events/friend-game-session.ts +++ b/src/main/services/ws/events/friend-game-session.ts @@ -1,15 +1,25 @@ import type { FriendGameSession } from "@main/generated/envelope"; +import { db, levelKeys } from "@main/level"; import { HydraApi } from "@main/services/hydra-api"; import { publishFriendStartedPlayingGameNotification } from "@main/services/notifications"; -import { GameStats } from "@types"; +import type { GameStats, UserPreferences, UserProfile } from "@types"; export const friendGameSessionEvent = async (payload: FriendGameSession) => { + const userPreferences = await db.get( + levelKeys.userPreferences, + { + valueEncoding: "json", + } + ); + + if (userPreferences?.friendStartGameNotificationsEnabled === false) return; + const [friend, gameStats] = await Promise.all([ - HydraApi.get(`/users/${payload.friendId}`), + HydraApi.get(`/users/${payload.friendId}`), HydraApi.get( `/games/stats?objectId=${payload.objectId}&shop=steam` ), - ]); + ]).catch(() => [null, null]); if (friend && gameStats) { publishFriendStartedPlayingGameNotification(friend, gameStats); diff --git a/src/main/services/ws/ws-client.ts b/src/main/services/ws/ws-client.ts index 8a6487de..e2e9d550 100644 --- a/src/main/services/ws/ws-client.ts +++ b/src/main/services/ws/ws-client.ts @@ -34,6 +34,10 @@ export class WSClient { }); this.ws.on("message", (message) => { + if (message.toString() === "PONG") { + return; + } + const envelope = Envelope.fromBinary( new Uint8Array(Buffer.from(message.toString())) ); diff --git a/src/preload/index.ts b/src/preload/index.ts index 8cf2492d..b9834676 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -18,6 +18,8 @@ import type { FriendRequestSync, ShortcutLocation, ShopAssets, + AchievementCustomNotificationPosition, + AchievementNotificationInfo, } from "@types"; import type { AuthPage, CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -185,6 +187,8 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("deleteGameFolder", shop, objectId), getGameByObjectId: (shop: GameShop, objectId: string) => ipcRenderer.invoke("getGameByObjectId", shop, objectId), + syncGameByObjectId: (shop: GameShop, objectId: string) => + ipcRenderer.invoke("syncGameByObjectId", shop, objectId), resetGameAchievements: (shop: GameShop, objectId: string) => ipcRenderer.invoke("resetGameAchievements", shop, objectId), extractGameDownload: (shop: GameShop, objectId: string) => @@ -209,12 +213,6 @@ contextBridge.exposeInMainWorld("electron", { return () => ipcRenderer.removeListener("on-library-batch-complete", listener); }, - onAchievementUnlocked: (cb: () => void) => { - const listener = (_event: Electron.IpcRendererEvent) => cb(); - ipcRenderer.on("on-achievement-unlocked", listener); - return () => - ipcRenderer.removeListener("on-achievement-unlocked", listener); - }, onExtractionComplete: (cb: (shop: GameShop, objectId: string) => void) => { const listener = ( _event: Electron.IpcRendererEvent, @@ -414,6 +412,42 @@ contextBridge.exposeInMainWorld("electron", { /* Notifications */ publishNewRepacksNotification: (newRepacksCount: number) => ipcRenderer.invoke("publishNewRepacksNotification", newRepacksCount), + onAchievementUnlocked: ( + cb: ( + position?: AchievementCustomNotificationPosition, + achievements?: AchievementNotificationInfo[] + ) => void + ) => { + const listener = ( + _event: Electron.IpcRendererEvent, + position?: AchievementCustomNotificationPosition, + achievements?: AchievementNotificationInfo[] + ) => cb(position, achievements); + ipcRenderer.on("on-achievement-unlocked", listener); + return () => + ipcRenderer.removeListener("on-achievement-unlocked", listener); + }, + onCombinedAchievementsUnlocked: ( + cb: ( + gameCount: number, + achievementsCount: number, + position: AchievementCustomNotificationPosition + ) => void + ) => { + const listener = ( + _event: Electron.IpcRendererEvent, + gameCount: number, + achievementCount: number, + position: AchievementCustomNotificationPosition + ) => cb(gameCount, achievementCount, position); + ipcRenderer.on("on-combined-achievements-unlocked", listener); + return () => + ipcRenderer.removeListener("on-combined-achievements-unlocked", listener); + }, + updateAchievementCustomNotificationWindow: () => + ipcRenderer.invoke("updateAchievementCustomNotificationWindow"), + showAchievementTestNotification: () => + ipcRenderer.invoke("showAchievementTestNotification"), /* Themes */ addCustomTheme: (theme: Theme) => ipcRenderer.invoke("addCustomTheme", theme), @@ -432,11 +466,11 @@ contextBridge.exposeInMainWorld("electron", { /* Editor */ openEditorWindow: (themeId: string) => ipcRenderer.invoke("openEditorWindow", themeId), - onCssInjected: (cb: (cssString: string) => void) => { - const listener = (_event: Electron.IpcRendererEvent, cssString: string) => - cb(cssString); - ipcRenderer.on("css-injected", listener); - return () => ipcRenderer.removeListener("css-injected", listener); + onCustomThemeUpdated: (cb: () => void) => { + const listener = (_event: Electron.IpcRendererEvent) => cb(); + ipcRenderer.on("on-custom-theme-updated", listener); + return () => + ipcRenderer.removeListener("on-custom-theme-updated", listener); }, closeEditorWindow: (themeId?: string) => ipcRenderer.invoke("closeEditorWindow", themeId), diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index f22e0361..7cd6eaa4 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -28,7 +28,7 @@ import { downloadSourcesTable } from "./dexie"; import { useSubscription } from "./hooks/use-subscription"; import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-modal"; -import { injectCustomCss } from "./helpers"; +import { injectCustomCss, removeCustomCss } from "./helpers"; import "./app.scss"; export interface AppProps { @@ -246,17 +246,27 @@ export function App() { }; }, [updateRepacks]); - useEffect(() => { - const loadAndApplyTheme = async () => { - const activeTheme = await window.electron.getActiveCustomTheme(); - - if (activeTheme?.code) { - injectCustomCss(activeTheme.code); - } - }; - loadAndApplyTheme(); + const loadAndApplyTheme = useCallback(async () => { + const activeTheme = await window.electron.getActiveCustomTheme(); + if (activeTheme?.code) { + injectCustomCss(activeTheme.code); + } else { + removeCustomCss(); + } }, []); + useEffect(() => { + loadAndApplyTheme(); + }, [loadAndApplyTheme]); + + useEffect(() => { + const unsubscribe = window.electron.onCustomThemeUpdated(() => { + loadAndApplyTheme(); + }); + + return () => unsubscribe(); + }, [loadAndApplyTheme]); + const playAudio = useCallback(() => { const audio = new Audio(achievementSound); audio.volume = 0.2; @@ -273,14 +283,6 @@ export function App() { }; }, [playAudio]); - useEffect(() => { - const unsubscribe = window.electron.onCssInjected((cssString) => { - injectCustomCss(cssString); - }); - - return () => unsubscribe(); - }, []); - const handleToastClose = useCallback(() => { dispatch(closeToast()); }, [dispatch]); diff --git a/src/renderer/src/assets/icons/ellipses.png b/src/renderer/src/assets/icons/ellipses.png new file mode 100644 index 00000000..9c8eca1d Binary files /dev/null and b/src/renderer/src/assets/icons/ellipses.png differ diff --git a/src/renderer/src/assets/icons/trophy.svg b/src/renderer/src/assets/icons/trophy.svg new file mode 100644 index 00000000..7be58814 --- /dev/null +++ b/src/renderer/src/assets/icons/trophy.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/renderer/src/components/achievements/notification/achievement-notification.scss b/src/renderer/src/components/achievements/notification/achievement-notification.scss new file mode 100644 index 00000000..0a41782e --- /dev/null +++ b/src/renderer/src/components/achievements/notification/achievement-notification.scss @@ -0,0 +1,519 @@ +@use "../../../scss/globals.scss"; + +$margin-horizontal: 40px; +$margin-top: 52px; +$margin-bottom: 28px; + +@keyframes content-in { + 0% { + width: 80px; + opacity: 0; + transform: scale(0); + } + 100% { + width: 80px; + opacity: 1; + transform: scale(1); + } +} + +@keyframes content-wait { + 0% { + width: 80px; + } + 100% { + width: 80px; + } +} + +@keyframes trophy-out { + 0% { + opacity: 1; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +@keyframes ellipses-stand-by { + 0% { + opacity: 1; + } + 100% { + opacity: 1; + } +} +@keyframes ellipses-out { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + scale: 1.5; + } +} + +@keyframes content-expand { + 0% { + width: 80px; + } + 100% { + width: calc(360px - $margin-horizontal); + } +} + +@keyframes chip-stand-by { + 0% { + opacity: 0; + } + 100% { + opacity: 0; + } +} + +@keyframes chip-in { + 0% { + transform: translateY(20px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes title-in { + 0% { + transform: translateY(10px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes description-in { + 0% { + transform: translateY(20px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes dark-overlay { + 0% { + opacity: 0.7; + } + 50% { + opacity: 0.7; + } + 100% { + opacity: 0; + } +} + +@keyframes content-out { + 0% { + transform: translateY(0); + opacity: 1; + } + 100% { + transform: translateY(-20px); + opacity: 0; + } +} + +@keyframes shine { + from { + transform: translateX(0px) rotate(36deg); + } + to { + transform: translateX(420px) rotate(36deg); + } +} + +.achievement-notification { + width: 360px; + height: 140px; + display: flex; + + &--top-left { + align-items: start; + } + + &--top-center { + align-items: start; + } + + &--top-right { + justify-content: end; + align-items: start; + } + + &--bottom-left { + align-items: end; + } + + &--bottom-center { + align-items: end; + } + + &--bottom-right { + justify-content: end; + align-items: end; + } + + &__outer-container { + position: relative; + display: grid; + width: calc(360px - $margin-horizontal); + overflow: clip; + border: 1px solid #ffffff1a; + animation: + content-in 450ms ease-in-out, + content-wait 450ms ease-in-out 450ms, + content-expand 450ms ease-in-out 900ms; + box-shadow: 0px 2px 16px 0px rgba(0, 0, 0, 0.25); + } + + &--top-left &__outer-container { + margin: $margin-top 0 0 $margin-horizontal; + } + + &--top-center &__outer-container { + margin: $margin-top 0 0 $margin-horizontal; + } + + &--top-right &__outer-container { + margin: $margin-top $margin-horizontal 0 0; + } + + &--bottom-left &__outer-container { + margin: 0 0 $margin-bottom $margin-horizontal; + } + + &--bottom-center &__outer-container { + margin: 0 0 $margin-bottom $margin-horizontal; + } + + &--bottom-right &__outer-container { + margin: 0 $margin-horizontal $margin-bottom 0; + } + + &--closing .achievement-notification__outer-container { + animation: content-out 450ms ease-in-out; + animation-fill-mode: forwards; + } + + &__container { + width: calc(360px - $margin-horizontal); + display: flex; + padding: 8px 16px 8px 8px; + background: globals.$background-color; + } + + &--platinum &__container { + background: linear-gradient(94deg, #1c1c1c -25%, #044838 100%); + } + + &--rare &__container { + &::before { + content: ""; + position: absolute; + top: -50%; + left: -60px; + width: 29px; + height: 134px; + transform: translateX(0px) rotate(36deg); + opacity: 0.2; + background: #d9d9d9; + filter: blur(8px); + animation: shine 450ms ease-in-out 1350ms; + } + } + + &__content { + display: flex; + flex-direction: row; + gap: 8px; + align-items: center; + width: 100%; + z-index: 1; + } + + &__icon { + box-sizing: border-box; + min-width: 64px; + min-height: 64px; + width: 64px; + height: 64px; + border-radius: 2px; + flex: 1; + } + + &--rare &__icon { + outline: 1px solid #f4a510; + box-shadow: 0px 0px 12px 0px rgba(244, 165, 16, 0.25); + } + + &--platinum &__icon { + outline: 1px solid #0cf1ca; + box-shadow: 0px 0px 12px 0px rgba(12, 241, 202, 0.25); + } + + &__additional-overlay { + position: absolute; + top: 0; + left: 0; + width: 80px; + height: 80px; + } + + &__dark-overlay { + position: absolute; + top: 8px; + left: 8px; + width: 64px; + height: 64px; + background: #000; + opacity: 0; + z-index: 1; + animation: dark-overlay 900ms ease-in-out; + } + + &__trophy-overlay { + position: absolute; + mask-image: url("/src/assets/icons/trophy.svg"); + top: 22px; + left: 22px; + width: 36px; + height: 36px; + opacity: 0; + z-index: 1; + animation: trophy-out 900ms ease-in-out; + background: #fff; + } + + &--rare &__trophy-overlay { + background: linear-gradient( + 118deg, + #e8ad15 18.96%, + #d5900f 26.41%, + #e8ad15 29.99%, + #e4aa15 38.89%, + #ca890e 42.43%, + #ca880e 46.59%, + #ecbe1a 50.08%, + #ecbd1a 53.48%, + #b3790d 57.39%, + #66470a 75.64%, + #a37a13 78.2%, + #987112 79.28%, + #503808 83.6%, + #3e2d08 85.77% + ), + #fff; + } + + &--platinum &__trophy-overlay { + background: linear-gradient( + 118deg, + #15e8d6 18.96%, + #0fd5a7 26.41%, + #15e8b7 29.99%, + #15e4b4 38.89%, + #0eca7f 42.43%, + #0eca9e 46.59%, + #1aecbb 50.08%, + #1aecb0 53.48%, + #0db392 57.39%, + #0a6648 75.64%, + #13a38b 78.2%, + #129862 79.28%, + #085042 83.6%, + #083e31 85.77% + ); + } + + &__ellipses-overlay { + position: absolute; + top: 8px; + left: 8px; + width: 64px; + height: 64px; + z-index: 2; + opacity: 0; + animation: ellipses-out 900ms ease-in-out; + } + + &__text-container { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + overflow: hidden; + } + + &__title { + font-size: 14px; + font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: globals.$muted-color; + animation: title-in 450ms ease-in-out 900ms; + } + + &__hidden-icon { + margin-right: 4px; + opacity: 0.5; + } + + &__description { + font-size: 14px; + font-weight: 400; + overflow: hidden; + -webkit-line-clamp: 2; /* number of lines to show */ + line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; + color: globals.$body-color; + animation: description-in 450ms ease-in-out 900ms; + } + + &--closing &__chip { + animation: content-out 450ms ease-in-out; + animation-fill-mode: forwards; + } + + &__chip { + position: absolute; + right: 8px; + display: flex; + gap: 4px; + padding: 0 8px; + border-radius: 300px; + align-items: center; + background: globals.$muted-color; + height: 24px; + animation: + chip-stand-by 900ms ease-in-out, + chip-in 450ms ease-in-out 900ms; + z-index: 2; + + &__icon { + width: 16px; + height: 16px; + path { + fill: globals.$background-color; + } + } + + &__label { + color: globals.$background-color; + font-weight: 700; + } + } + + &--top-left &__chip { + top: -12px; + margin: $margin-top 0 0 $margin-horizontal; + } + + &--top-center &__chip { + top: -12px; + margin: $margin-top 0 0 $margin-horizontal; + } + + &--top-right &__chip { + top: -12px; + margin: $margin-top $margin-horizontal 0 0; + } + + &--bottom-left &__chip { + bottom: 70px; + margin: 0 0 $margin-bottom $margin-horizontal; + } + + &--bottom-center &__chip { + bottom: 70px; + margin: 0 0 $margin-bottom $margin-horizontal; + } + + &--bottom-right &__chip { + bottom: 70px; + margin: 0 $margin-horizontal $margin-bottom 0; + } + + &--rare &__chip { + background: linear-gradient( + 160deg, + #e8ad15 18.96%, + #d5900f 26.41%, + #e8ad15 29.99%, + #e4aa15 38.89%, + #ca890e 42.43%, + #ca880e 46.59%, + #ecbe1a 50.08%, + #ecbd1a 53.48%, + #b3790d 57.39%, + #66470a 75.64%, + #a37a13 78.2%, + #987112 79.28%, + #503808 83.6%, + #3e2d08 85.77% + ); + &__icon { + path { + fill: #fff; + } + } + &__label { + color: #fff; + } + } + + &--platinum &__chip { + background: linear-gradient( + 118deg, + #15e8d6 18.96%, + #0fd5a7 26.41%, + #15e8b7 29.99%, + #15e4b4 38.89%, + #0eca7f 42.43%, + #0eca9e 46.59%, + #1aecbb 50.08%, + #1aecb0 53.48%, + #0db392 57.39%, + #0a6648 75.64%, + #13a38b 78.2%, + #129862 79.28%, + #085042 83.6%, + #083e31 85.77% + ); + &__icon { + path { + fill: #fff; + } + } + &__label { + color: #fff; + } + } + + &--closing * { + animation: none; + } + + &--closing *::before, + &--closing *::after { + animation: none !important; + } +} diff --git a/src/renderer/src/components/achievements/notification/achievement-notification.tsx b/src/renderer/src/components/achievements/notification/achievement-notification.tsx new file mode 100644 index 00000000..bcc22047 --- /dev/null +++ b/src/renderer/src/components/achievements/notification/achievement-notification.tsx @@ -0,0 +1,79 @@ +import { + AchievementCustomNotificationPosition, + AchievementNotificationInfo, +} from "@types"; +import cn from "classnames"; +import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; +import { EyeClosedIcon } from "@primer/octicons-react"; +import Ellipses from "@renderer/assets/icons/ellipses.png"; +import "./achievement-notification.scss"; + +interface AchievementNotificationProps { + position: AchievementCustomNotificationPosition; + achievement: AchievementNotificationInfo; + isClosing: boolean; +} + +export function AchievementNotificationItem({ + position, + achievement, + isClosing, +}: Readonly) { + const baseClassName = "achievement-notification"; + + return ( +
+ {achievement.points !== undefined && ( +
+ + + +{achievement.points} + +
+ )} + +
+
+
+ {achievement.title} +
+

+ {achievement.isHidden && ( + + + + )} + {achievement.title} +

+

+ {achievement.description} +

+
+
+ +
+
+ Ellipses effect +
+
+
+
+
+ ); +} diff --git a/src/renderer/src/components/collapsed-menu/collapsed-menu.scss b/src/renderer/src/components/collapsed-menu/collapsed-menu.scss new file mode 100644 index 00000000..c209940e --- /dev/null +++ b/src/renderer/src/components/collapsed-menu/collapsed-menu.scss @@ -0,0 +1,40 @@ +@use "../../scss/globals.scss"; + +.collapsed-menu { + &__button { + height: 72px; + padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 2); + display: flex; + align-items: center; + background-color: globals.$background-color; + color: globals.$muted-color; + width: 100%; + cursor: pointer; + transition: all ease 0.2s; + gap: globals.$spacing-unit; + font-size: globals.$body-font-size; + font-weight: bold; + + &:hover { + background-color: rgba(255, 255, 255, 0.05); + } + + &:active { + opacity: globals.$active-opacity; + } + } + + &__chevron { + transition: transform ease 0.2s; + + &--open { + transform: rotate(180deg); + } + } + + &__content { + overflow: hidden; + transition: max-height 0.4s cubic-bezier(0, 1, 0, 1); + position: relative; + } +} diff --git a/src/renderer/src/components/collapsed-menu/collapsed-menu.tsx b/src/renderer/src/components/collapsed-menu/collapsed-menu.tsx new file mode 100644 index 00000000..67058598 --- /dev/null +++ b/src/renderer/src/components/collapsed-menu/collapsed-menu.tsx @@ -0,0 +1,52 @@ +import { useEffect, useRef, useState } from "react"; +import { ChevronDownIcon } from "@primer/octicons-react"; +import "./collapsed-menu.scss"; + +export interface CollapsedMenuProps { + title: string; + children: React.ReactNode; +} + +export function CollapsedMenu({ + title, + children, +}: Readonly) { + const content = useRef(null); + const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState(0); + + useEffect(() => { + if (content.current && content.current.scrollHeight !== height) { + setHeight(isOpen ? content.current.scrollHeight : 0); + } else if (!isOpen) { + setHeight(0); + } + }, [isOpen, children, height]); + + return ( +
+ + +
+ {children} +
+
+ ); +} diff --git a/src/renderer/src/components/select-field/select-field.tsx b/src/renderer/src/components/select-field/select-field.tsx index 69e3d74b..08334b9f 100644 --- a/src/renderer/src/components/select-field/select-field.tsx +++ b/src/renderer/src/components/select-field/select-field.tsx @@ -18,12 +18,13 @@ export function SelectField({ options = [{ key: "-", value: value?.toString() || "-", label: "-" }], theme = "primary", onChange, -}: SelectProps) { + className, +}: Readonly) { const [isFocused, setIsFocused] = useState(false); const id = useId(); return ( -
+
{label && (