mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-18 00:33:59 +00:00
ci: updating build to support ws url
This commit is contained in:
@@ -120,7 +120,7 @@
|
||||
"options": "خيارات",
|
||||
"executable_section_title": "ملف التشغيل",
|
||||
"executable_section_description": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"",
|
||||
"downloads_secion_title": "التنزيلات",
|
||||
"downloads_section_title": "التنزيلات",
|
||||
"downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى لهذه اللعبة",
|
||||
"danger_zone_section_title": "منطقة الخطر",
|
||||
"danger_zone_section_description": "إزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra",
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"options": "Опции",
|
||||
"executable_section_title": "Стартиращ файл",
|
||||
"executable_section_description": "Пътят на файла, който ще се изпълни, когато се щракне върху \"Пускане\"",
|
||||
"downloads_secion_title": "Свалени",
|
||||
"downloads_section_title": "Свалени",
|
||||
"downloads_section_description": "Вижте актуализации или други версии на тази игра",
|
||||
"danger_zone_section_title": "Опасна зона",
|
||||
"danger_zone_section_description": "Премахнете тази игра от библиотеката си или от файловете, изтеглени от Hydra",
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"options": "Opcions",
|
||||
"executable_section_title": "Executable",
|
||||
"executable_section_description": "Directori del fitxer des d'on s'executarà quan es cliqui a \"Executar\"",
|
||||
"downloads_secion_title": "Descàrregues",
|
||||
"downloads_section_title": "Descàrregues",
|
||||
"downloads_section_description": "Comprova actualitzacions o altres versions del videojoc",
|
||||
"danger_zone_section_title": "Zona de perill",
|
||||
"danger_zone_section_description": "Elimina aquest videojoc del teu catàleg o els fitxers descarregats per Hydra",
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"options": "Možnosti",
|
||||
"executable_section_title": "Spustitelné",
|
||||
"executable_section_description": "Umístění souboru který bude spuštěn při kliknutí na \"Hrát\"",
|
||||
"downloads_secion_title": "Stažené soubory",
|
||||
"downloads_section_title": "Stažené soubory",
|
||||
"downloads_section_description": "Zkontrolovat jestli není nová / odlišná verze hry",
|
||||
"danger_zone_section_title": "Nebezpečná zóna",
|
||||
"danger_zone_section_description": "Odebrat hru z knihovny / soubory stažené Hydrou",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
"options": "Valgmuligheder",
|
||||
"executable_section_title": "Eksekverbar fil",
|
||||
"executable_section_description": "Sti til filen som skal bruges når \"Spil\" bliver klikket",
|
||||
"downloads_secion_title": "Downloads",
|
||||
"downloads_section_title": "Downloads",
|
||||
"downloads_section_description": "Undersøg opdateringer eller andre versioner af dette spil",
|
||||
"danger_zone_section_title": "Farezonen",
|
||||
"danger_zone_section_description": "Fjern dette spil fra dit bibliotek eller filerne der er blevet downloadet af Hydra",
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"options": "Optionen",
|
||||
"executable_section_title": "Ausführbare Datei",
|
||||
"executable_section_description": "Pfad der Datei, die bei Klick auf \"Play\" ausgeführt wird",
|
||||
"downloads_secion_title": "Downloads",
|
||||
"downloads_section_title": "Downloads",
|
||||
"downloads_section_description": "Sieh dir Updates oder andere Versionen dieses Spiels an",
|
||||
"danger_zone_section_title": "Gefahrenzone",
|
||||
"danger_zone_section_description": "Entferne dieses Spiel aus deiner Bibliothek oder die von Hydra heruntergeladenen Dateien",
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"options": "Options",
|
||||
"executable_section_title": "Executable",
|
||||
"executable_section_description": "Path of the file that will be executed when \"Play\" is clicked",
|
||||
"downloads_secion_title": "Downloads",
|
||||
"downloads_section_title": "Downloads",
|
||||
"downloads_section_description": "Check out updates or other versions of this game",
|
||||
"danger_zone_section_title": "Danger zone",
|
||||
"danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra",
|
||||
@@ -198,7 +198,8 @@
|
||||
"download_error_not_cached_on_hydra": "This download is not available on Nimbus.",
|
||||
"game_removed_from_favorites": "Game removed from favorites",
|
||||
"game_added_to_favorites": "Game added to favorites",
|
||||
"automatically_extract_downloaded_files": "Automatically extract downloaded files"
|
||||
"automatically_extract_downloaded_files": "Automatically extract downloaded files",
|
||||
"create_start_menu_shortcut": "Create Start Menu shortcut"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activate Hydra",
|
||||
@@ -356,7 +357,8 @@
|
||||
"common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.",
|
||||
"install_common_redist": "Install",
|
||||
"installing_common_redist": "Installing…",
|
||||
"show_download_speed_in_megabytes": "Show download speed in megabytes per second"
|
||||
"show_download_speed_in_megabytes": "Show download speed in megabytes per second",
|
||||
"extract_files_by_default": "Extract files by default after download"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download complete",
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"options": "Opciones",
|
||||
"executable_section_title": "Ejecutable",
|
||||
"executable_section_description": "Ruta del archivo que se ejecutará cuando se presione \"Jugar\"",
|
||||
"downloads_secion_title": "Descargas",
|
||||
"downloads_section_title": "Descargas",
|
||||
"downloads_section_description": "Buscar actualizaciones u otras versiones de este juego",
|
||||
"danger_zone_section_title": "Opciones Avanzadas",
|
||||
"danger_zone_section_description": "Eliminar este juego de tu librería o los archivos descargados por Hydra (Esto solo eliminará los archivos de instalación y no el juego instalado)",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
"options": "Valikud",
|
||||
"executable_section_title": "Käivitusfail",
|
||||
"executable_section_description": "Faili tee, mida käivitatakse \"Mängi\" nupule vajutades",
|
||||
"downloads_secion_title": "Allalaadimised",
|
||||
"downloads_section_title": "Allalaadimised",
|
||||
"downloads_section_description": "Vaata uuendusi või selle mängu teisi versioone",
|
||||
"danger_zone_section_title": "Ohutsoon",
|
||||
"danger_zone_section_description": "Eemalda see mäng oma kogust või Hydra poolt allalaaditud failid",
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
{
|
||||
"language_name": "Français",
|
||||
"app": {
|
||||
"successfully_signed_in": "Connecté avec succès"
|
||||
},
|
||||
"home": {
|
||||
"featured": "En vedette",
|
||||
"surprise_me": "Surprenez-moi",
|
||||
"no_results": "Aucun résultat trouvé"
|
||||
"no_results": "Aucun résultat trouvé",
|
||||
"start_typing": "Commencez à taper pour rechercher...",
|
||||
"hot": "Tendance",
|
||||
"weekly": "📅 Meilleurs jeux de la semaine",
|
||||
"achievements": "🏆 Jeux à terminer"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Catalogue",
|
||||
@@ -12,24 +19,46 @@
|
||||
"my_library": "Ma bibliothèque",
|
||||
"downloading_metadata": "{{title}} (Téléchargement des métadonnées…)",
|
||||
"paused": "{{title}} (En pause)",
|
||||
"downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)",
|
||||
"downloading": "{{title}} ({{percentage}} - Téléchargement…)",
|
||||
"filter": "Filtrer la bibliothèque",
|
||||
"home": "Page d’accueil",
|
||||
"home": "Page d'accueil",
|
||||
"queued": "{{title}} (En file d'attente)",
|
||||
"game_has_no_executable": "Aucun exécutable sélectionné pour ce jeu",
|
||||
"sign_in": "Se connecter",
|
||||
"friends": "Amis",
|
||||
"need_help": "Besoin d'aide ?",
|
||||
"favorites": "Favoris"
|
||||
},
|
||||
"header": {
|
||||
"search": "Recherche",
|
||||
|
||||
"search": "Rechercher",
|
||||
"home": "Accueil",
|
||||
"catalogue": "Catalogue",
|
||||
"downloads": "Téléchargements",
|
||||
"search_results": "Résultats de la recherche",
|
||||
"settings": "Paramètres",
|
||||
"home": "Accueil"
|
||||
"version_available_install": "Version {{version}} disponible. Cliquez ici pour redémarrer et installer.",
|
||||
"version_available_download": "Version {{version}} disponible. Cliquez ici pour télécharger."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Aucun téléchargement en cours",
|
||||
"downloading_metadata": "Téléchargement des métadonnées de {{title}}…",
|
||||
"downloading": "Téléchargement de {{title}}… ({{percentage}} terminé) - Fin dans {{eta}} - {{speed}}"
|
||||
"downloading": "Téléchargement de {{title}}… ({{percentage}} terminé) - Fin dans {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Téléchargement de {{title}}… ({{percentage}} terminé) - Calcul du temps restant…",
|
||||
"checking_files": "Vérification des fichiers de {{title}}… ({{percentage}} terminé)",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"installation_complete": "Installation terminée",
|
||||
"installation_complete_message": "Redistribuables communs installés avec succès"
|
||||
},
|
||||
"catalogue": {
|
||||
"search": "Filtrer…",
|
||||
"developers": "Développeurs",
|
||||
"genres": "Genres",
|
||||
"tags": "Tags",
|
||||
"publishers": "Éditeurs",
|
||||
"download_sources": "Sources de téléchargement",
|
||||
"result_count": "{{resultCount}} résultats",
|
||||
"filter_count": "{{filterCount}} disponibles",
|
||||
"clear_filters": "Effacer {{filterCount}} sélectionnés"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Ouvrir les options de téléchargement",
|
||||
@@ -37,36 +66,139 @@
|
||||
"download_options_one": "{{count}} option de téléchargement",
|
||||
"download_options_other": "{{count}} options de téléchargement",
|
||||
"updated_at": "Mis à jour le {{updated_at}}",
|
||||
"install": "Installer",
|
||||
"resume": "Reprendre",
|
||||
"pause": "Pause",
|
||||
"cancel": "Annuler",
|
||||
"remove": "Supprimer",
|
||||
"space_left_on_disk": "{{space}} restant sur le disque",
|
||||
"space_left_on_disk": "{{space}} restants sur le disque",
|
||||
"eta": "Fin dans {{eta}}",
|
||||
"downloading_metadata": "Téléchargement des métadonnées en cours…",
|
||||
"calculating_eta": "Calcul du temps restant…",
|
||||
"downloading_metadata": "Téléchargement des métadonnées…",
|
||||
"filter": "Filtrer les repacks",
|
||||
"requirements": "Configuration requise",
|
||||
"minimum": "Minimum",
|
||||
"recommended": "Recommandée",
|
||||
"paused": "En pause",
|
||||
"release_date": "Sorti le {{date}}",
|
||||
"publisher": "Édité par {{publisher}}",
|
||||
"publisher": "Publié par {{publisher}}",
|
||||
"hours": "heures",
|
||||
"minutes": "minutes",
|
||||
"amount_hours": "{{amount}} heures",
|
||||
"amount_minutes": "{{amount}} minutes",
|
||||
"accuracy": "{{accuracy}}% précision",
|
||||
"add_to_library": "Ajouter à la bibliothèque",
|
||||
"remove_from_library": "Supprimer de la bibliothèque",
|
||||
"remove_from_library": "Retirer de la bibliothèque",
|
||||
"no_downloads": "Aucun téléchargement disponible",
|
||||
"next_suggestion": "Suggestion suivante",
|
||||
"play_time": "Joué pour {{amount}}",
|
||||
"install": "Installer",
|
||||
"play": "Jouer",
|
||||
"play_time": "{{amount}} de temps de jeu",
|
||||
"last_time_played": "Dernière partie {{period}}",
|
||||
"not_played_yet": "Vous n'avez pas encore joué à {{title}}",
|
||||
"next_suggestion": "Suggestion suivante",
|
||||
"play": "Jouer",
|
||||
"deleting": "Suppression de l'installateur…",
|
||||
"close": "Fermer",
|
||||
"deleting": "Suppression du programme d'installation…",
|
||||
"playing_now": "Jeu en cours",
|
||||
"last_time_played": "Dernièrement joué {{period}}"
|
||||
"playing_now": "En cours de jeu",
|
||||
"change": "Changer",
|
||||
"repacks_modal_description": "Choisissez le repack que vous souhaitez télécharger",
|
||||
"select_folder_hint": "Pour changer le dossier par défaut, allez dans les <0>Paramètres</0>",
|
||||
"download_now": "Télécharger maintenant",
|
||||
"no_shop_details": "Impossible d'obtenir les détails du magasin.",
|
||||
"download_options": "Options de téléchargement",
|
||||
"download_path": "Emplacement de téléchargement",
|
||||
"previous_screenshot": "Capture précédente",
|
||||
"next_screenshot": "Capture suivante",
|
||||
"screenshot": "Capture d'écran {{number}}",
|
||||
"open_screenshot": "Ouvrir la capture {{number}}",
|
||||
"download_settings": "Paramètres de téléchargement",
|
||||
"downloader": "Téléchargeur",
|
||||
"select_executable": "Sélectionner",
|
||||
"no_executable_selected": "Aucun exécutable sélectionné",
|
||||
"open_folder": "Ouvrir le dossier",
|
||||
"open_download_location": "Voir les fichiers téléchargés",
|
||||
"create_shortcut": "Créer un raccourci sur le bureau",
|
||||
"clear": "Effacer",
|
||||
"remove_files": "Supprimer les fichiers",
|
||||
"remove_from_library_title": "Êtes-vous sûr ?",
|
||||
"remove_from_library_description": "Ceci supprimera {{game}} de votre bibliothèque",
|
||||
"options": "Options",
|
||||
"executable_section_title": "Exécutable",
|
||||
"executable_section_description": "Chemin du fichier lancé quand \"Jouer\" est cliqué",
|
||||
"downloads_section_title": "Téléchargements",
|
||||
"downloads_section_description": "Découvrez les mises à jour ou autres versions de ce jeu",
|
||||
"danger_zone_section_title": "Zone de danger",
|
||||
"danger_zone_section_description": "Supprimez ce jeu de votre bibliothèque ou les fichiers téléchargés par Hydra",
|
||||
"download_in_progress": "Téléchargement en cours",
|
||||
"download_paused": "Téléchargement en pause",
|
||||
"last_downloaded_option": "Dernière option téléchargée",
|
||||
"create_shortcut_success": "Raccourci créé avec succès",
|
||||
"create_shortcut_error": "Erreur lors de la création du raccourci",
|
||||
"nsfw_content_title": "Ce jeu contient du contenu inapproprié",
|
||||
"nsfw_content_description": "{{title}} contient du contenu pouvant ne pas convenir à tous les âges. Voulez-vous continuer ?",
|
||||
"allow_nsfw_content": "Continuer",
|
||||
"refuse_nsfw_content": "Retourner",
|
||||
"stats": "Statistiques",
|
||||
"download_count": "Téléchargements",
|
||||
"player_count": "Joueurs actifs",
|
||||
"download_error": "Cette option de téléchargement n'est pas disponible",
|
||||
"download": "Télécharger",
|
||||
"executable_path_in_use": "Exécutable déjà utilisé par \"{{game}}\"",
|
||||
"warning": "Attention :",
|
||||
"hydra_needs_to_remain_open": "Pour ce téléchargement, Hydra doit rester ouvert jusqu'à la fin. Si Hydra se ferme avant, la progression sera perdue.",
|
||||
"achievements": "Succès",
|
||||
"achievements_count": "Succès {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Sauvegarde Cloud",
|
||||
"cloud_save_description": "Sauvegardez vos progrès dans le cloud et continuez à jouer sur n'importe quel appareil",
|
||||
"backups": "Sauvegardes",
|
||||
"install_backup": "Restaurer",
|
||||
"delete_backup": "Supprimer",
|
||||
"create_backup": "Nouvelle sauvegarde",
|
||||
"last_backup_date": "Dernière sauvegarde le {{date}}",
|
||||
"no_backup_preview": "Aucune sauvegarde trouvée pour ce titre",
|
||||
"restoring_backup": "Restauration de la sauvegarde ({{progress}} terminé)…",
|
||||
"uploading_backup": "Envoi de la sauvegarde…",
|
||||
"no_backups": "Vous n'avez pas encore créé de sauvegarde pour ce jeu",
|
||||
"backup_uploaded": "Sauvegarde envoyée",
|
||||
"backup_deleted": "Sauvegarde supprimée",
|
||||
"backup_restored": "Sauvegarde restaurée",
|
||||
"see_all_achievements": "Voir tous les succès",
|
||||
"sign_in_to_see_achievements": "Connectez-vous pour voir les succès",
|
||||
"mapping_method_automatic": "Automatique",
|
||||
"mapping_method_manual": "Manuel",
|
||||
"mapping_method_label": "Méthode de mappage",
|
||||
"files_automatically_mapped": "Fichiers mappés automatiquement",
|
||||
"no_backups_created": "Aucune sauvegarde créée pour ce jeu",
|
||||
"manage_files": "Gérer les fichiers",
|
||||
"loading_save_preview": "Recherche de jeux sauvegardés…",
|
||||
"wine_prefix": "Wine Prefix",
|
||||
"wine_prefix_description": "Le préfixe Wine utilisé pour lancer ce jeu",
|
||||
"launch_options": "Options de lancement",
|
||||
"launch_options_description": "Les utilisateurs avancés peuvent modifier les options de lancement (fonction expérimentale)",
|
||||
"launch_options_placeholder": "Aucun paramètre spécifié",
|
||||
"no_download_option_info": "Pas d'information disponible",
|
||||
"backup_deletion_failed": "Échec de la suppression de la sauvegarde",
|
||||
"max_number_of_artifacts_reached": "Nombre maximal de sauvegardes atteint pour ce jeu",
|
||||
"achievements_not_sync": "Voir comment synchroniser vos succès",
|
||||
"manage_files_description": "Gérer les fichiers qui seront sauvegardés et restaurés",
|
||||
"select_folder": "Sélectionner un dossier",
|
||||
"backup_from": "Sauvegarde du {{date}}",
|
||||
"automatic_backup_from": "Sauvegarde automatique du {{date}}",
|
||||
"enable_automatic_cloud_sync": "Activer la synchronisation cloud automatique",
|
||||
"custom_backup_location_set": "Emplacement de sauvegarde personnalisé défini",
|
||||
"no_directory_selected": "Aucun dossier sélectionné",
|
||||
"no_write_permission": "Impossible de télécharger dans ce dossier. Cliquez ici pour en savoir plus.",
|
||||
"reset_achievements": "Réinitialiser les succès",
|
||||
"reset_achievements_description": "Ceci réinitialisera tous les succès pour {{game}}",
|
||||
"reset_achievements_title": "Êtes-vous sûr ?",
|
||||
"reset_achievements_success": "Succès réinitialisés avec succès",
|
||||
"reset_achievements_error": "Échec de la réinitialisation des succès",
|
||||
"download_error_gofile_quota_exceeded": "Vous avez dépassé votre quota mensuel Gofile. Attendez la remise à zéro du quota.",
|
||||
"download_error_real_debrid_account_not_authorized": "Votre compte Real-Debrid n'est pas autorisé à effectuer de nouveaux téléchargements. Veuillez vérifier les paramètres de votre compte et réessayer.",
|
||||
"download_error_not_cached_on_real_debrid": "Ce téléchargement n'est pas disponible sur Real-Debrid, et le suivi n'est pas encore disponible.",
|
||||
"download_error_not_cached_on_torbox": "Ce téléchargement n'est pas disponible sur TorBox, et le suivi n'est pas encore disponible.",
|
||||
"download_error_not_cached_on_hydra": "Ce téléchargement n'est pas disponible sur Nimbus.",
|
||||
"game_removed_from_favorites": "Jeu retiré des favoris",
|
||||
"game_added_to_favorites": "Jeu ajouté aux favoris",
|
||||
"automatically_extract_downloaded_files": "Extraire automatiquement les fichiers téléchargés"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activer Hydra",
|
||||
@@ -83,51 +215,292 @@
|
||||
"paused": "En pause",
|
||||
"verifying": "Vérification en cours…",
|
||||
"completed": "Terminé",
|
||||
"removed": "Non téléchargé",
|
||||
"cancel": "Annuler",
|
||||
"filter": "Filtrer les jeux téléchargés",
|
||||
"remove": "Supprimer",
|
||||
"downloading_metadata": "Téléchargement des métadonnées en cours…",
|
||||
"delete": "Supprimer le programme d'installation",
|
||||
"delete_modal_description": "Cela supprimera tous les fichiers d'installation de votre ordinateur",
|
||||
"delete_modal_title": "Es-tu sûr?",
|
||||
"deleting": "Suppression du programme d'installation…",
|
||||
"install": "Installer"
|
||||
"delete": "Supprimer le programme d'installation",
|
||||
"delete_modal_title": "Êtes-vous sûr ?",
|
||||
"delete_modal_description": "Cela supprimera tous les fichiers d'installation de votre ordinateur",
|
||||
"install": "Installer",
|
||||
"download_in_progress": "En cours",
|
||||
"queued_downloads": "Téléchargements en attente",
|
||||
"downloads_completed": "Terminés",
|
||||
"queued": "En attente",
|
||||
"no_downloads_title": "Tellement vide",
|
||||
"no_downloads_description": "Vous n'avez encore rien téléchargé avec Hydra, mais il n'est jamais trop tard pour commencer.",
|
||||
"checking_files": "Vérification des fichiers…",
|
||||
"seeding": "Partage",
|
||||
"stop_seeding": "Arrêter le partage",
|
||||
"resume_seeding": "Reprendre le partage",
|
||||
"options": "Gérer",
|
||||
"extract": "Extraire les fichiers",
|
||||
"extracting": "Extraction des fichiers…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Chemin des téléchargements",
|
||||
"change": "Mettre à jour",
|
||||
"notifications": "Notifications",
|
||||
"enable_download_notifications": "Quand un téléchargement est terminé",
|
||||
"enable_repack_list_notifications": "Quand un nouveau repack est ajouté",
|
||||
"enable_download_notifications": "Lorsqu'un téléchargement est terminé",
|
||||
"enable_repack_list_notifications": "Lorsqu'un nouveau repack est ajouté",
|
||||
"real_debrid_api_token_label": "Jeton API Real-Debrid",
|
||||
"quit_app_instead_hiding": "Ne pas masquer Hydra à la fermeture",
|
||||
"launch_with_system": "Lancer Hydra au démarrage du système",
|
||||
"general": "Général",
|
||||
"behavior": "Comportement",
|
||||
"download_sources": "Sources de téléchargement",
|
||||
"language": "Langue",
|
||||
"api_token": "Jeton API",
|
||||
"enable_real_debrid": "Activer Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid est un téléchargeur sans restriction qui vous permet de télécharger rapidement des fichiers, uniquement limités par votre vitesse Internet.",
|
||||
"debrid_invalid_token": "Jeton API invalide",
|
||||
"debrid_api_token_hint": "Vous pouvez obtenir votre jeton API <0>ici</0>",
|
||||
"real_debrid_free_account_error": "Le compte \"{{username}}\" est un compte gratuit. Veuillez vous abonner à Real-Debrid",
|
||||
"debrid_linked_message": "Compte \"{{username}}\" lié",
|
||||
"save_changes": "Enregistrer les modifications",
|
||||
"changes_saved": "Modifications enregistrées avec succès",
|
||||
"download_sources_description": "Hydra récupère les liens de téléchargement à partir de ces sources. L'URL source doit être un lien direct vers un fichier .json contenant les liens de téléchargement.",
|
||||
"validate_download_source": "Valider",
|
||||
"remove_download_source": "Supprimer",
|
||||
"add_download_source": "Ajouter une source",
|
||||
"download_count_zero": "Aucune option de téléchargement",
|
||||
"download_count_one": "{{countFormatted}} option de téléchargement",
|
||||
"download_count_other": "{{countFormatted}} options de téléchargement",
|
||||
"download_source_url": "URL de la source",
|
||||
"add_download_source_description": "Insérez l'URL du fichier .json",
|
||||
"download_source_up_to_date": "À jour",
|
||||
"download_source_errored": "Erreur",
|
||||
"sync_download_sources": "Synchroniser les sources",
|
||||
"removed_download_source": "Source de téléchargement supprimée",
|
||||
"removed_download_sources": "Sources de téléchargement supprimées",
|
||||
"cancel_button_confirmation_delete_all_sources": "Non",
|
||||
"confirm_button_confirmation_delete_all_sources": "Oui, tout supprimer",
|
||||
"description_confirmation_delete_all_sources": "Vous supprimerez toutes les sources de téléchargement",
|
||||
"title_confirmation_delete_all_sources": "Supprimer toutes les sources de téléchargement",
|
||||
"removed_download_sources": "Sources de téléchargement supprimées",
|
||||
"button_delete_all_sources": "Supprimer toutes les sources de téléchargement"
|
||||
"description_confirmation_delete_all_sources": "Vous supprimerez toutes les sources de téléchargement",
|
||||
"button_delete_all_sources": "Tout supprimer",
|
||||
"added_download_source": "Source de téléchargement ajoutée",
|
||||
"download_sources_synced": "Toutes les sources de téléchargement sont synchronisées",
|
||||
"insert_valid_json_url": "Insérez une URL JSON valide",
|
||||
"found_download_option_zero": "Aucune option de téléchargement trouvée",
|
||||
"found_download_option_one": "{{countFormatted}} option trouvée",
|
||||
"found_download_option_other": "{{countFormatted}} options trouvées",
|
||||
"import": "Importer",
|
||||
"public": "Publique",
|
||||
"private": "Privé",
|
||||
"friends_only": "Amis uniquement",
|
||||
"privacy": "Confidentialité",
|
||||
"profile_visibility": "Visibilité du profil",
|
||||
"profile_visibility_description": "Choisissez qui peut voir votre profil et bibliothèque",
|
||||
"required_field": "Ce champ est requis",
|
||||
"source_already_exists": "Cette source a déjà été ajoutée",
|
||||
"must_be_valid_url": "La source doit être une URL valide",
|
||||
"blocked_users": "Utilisateurs bloqués",
|
||||
"user_unblocked": "Utilisateur débloqué",
|
||||
"enable_achievement_notifications": "Quand un succès est débloqué",
|
||||
"launch_minimized": "Lancer Hydra minimisé",
|
||||
"disable_nsfw_alert": "Désactiver l'alerte NSFW",
|
||||
"seed_after_download_complete": "Partager après téléchargement",
|
||||
"show_hidden_achievement_description": "Afficher la description des succès cachés avant de les débloquer",
|
||||
"account": "Compte",
|
||||
"no_users_blocked": "Aucun utilisateur bloqué",
|
||||
"subscription_active_until": "Votre Hydra Cloud est actif jusqu'au {{date}}",
|
||||
"manage_subscription": "Gérer l'abonnement",
|
||||
"update_email": "Modifier l'email",
|
||||
"update_password": "Modifier le mot de passe",
|
||||
"current_email": "Email actuel :",
|
||||
"no_email_account": "Vous n'avez pas encore défini d'email",
|
||||
"account_data_updated_successfully": "Informations du compte mises à jour",
|
||||
"renew_subscription": "Renouveler Hydra Cloud",
|
||||
"subscription_expired_at": "Votre abonnement a expiré le {{date}}",
|
||||
"no_subscription": "Profitez d'Hydra de la meilleure façon possible",
|
||||
"become_subscriber": "Devenir membre Hydra Cloud",
|
||||
"subscription_renew_cancelled": "Le renouvellement automatique est désactivé",
|
||||
"subscription_renews_on": "Votre abonnement sera renouvelé le {{date}}",
|
||||
"bill_sent_until": "Votre prochaine facture sera envoyée à cette date",
|
||||
"no_themes": "Vous n'avez pas encore de thèmes, cliquez ici pour créer votre premier chef-d'œuvre.",
|
||||
"editor_tab_code": "Code",
|
||||
"editor_tab_info": "Info",
|
||||
"editor_tab_save": "Enregistrer",
|
||||
"web_store": "Web store",
|
||||
"clear_themes": "Effacer",
|
||||
"create_theme": "Créer",
|
||||
"create_theme_modal_title": "Créer un thème personnalisé",
|
||||
"create_theme_modal_description": "Créer un nouveau thème pour personnaliser l'apparence d'Hydra",
|
||||
"theme_name": "Nom du thème",
|
||||
"insert_theme_name": "Entrez le nom du thème",
|
||||
"set_theme": "Activer le thème",
|
||||
"unset_theme": "Désactiver le thème",
|
||||
"delete_theme": "Supprimer le thème",
|
||||
"edit_theme": "Modifier le thème",
|
||||
"delete_all_themes": "Supprimer tous les thèmes",
|
||||
"delete_all_themes_description": "Cela supprimera tous vos thèmes personnalisés",
|
||||
"delete_theme_description": "Cela supprimera le thème {{theme}}",
|
||||
"cancel": "Annuler",
|
||||
"appearance": "Apparence",
|
||||
"enable_torbox": "Activer TorBox",
|
||||
"torbox_description": "TorBox est votre service de seedbox premium qui rivalise avec les meilleurs serveurs du marché.",
|
||||
"torbox_account_linked": "Compte TorBox lié",
|
||||
"create_real_debrid_account": "Cliquez ici pour créer un compte Real-Debrid",
|
||||
"create_torbox_account": "Cliquez ici pour créer un compte TorBox",
|
||||
"real_debrid_account_linked": "Compte Real-Debrid lié",
|
||||
"name_min_length": "Le nom du thème doit comporter au moins 3 caractères",
|
||||
"import_theme": "Importer un thème",
|
||||
"import_theme_description": "Vous allez importer {{theme}} du magasin de thèmes",
|
||||
"error_importing_theme": "Erreur lors de l'importation du thème",
|
||||
"theme_imported": "Thème importé avec succès",
|
||||
"enable_friend_request_notifications": "Lors de réception de demande d'ami",
|
||||
"enable_auto_install": "Télécharger les mises à jour automatiquement",
|
||||
"common_redist": "Redistribuables communs",
|
||||
"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"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Téléchargement terminé",
|
||||
"game_ready_to_install": "{{title}} est prêt à être installé",
|
||||
"repack_list_updated": "Liste de repacks mise à jour",
|
||||
"repack_count_one": "{{count}} repack ajouté",
|
||||
"repack_count_other": "{{count}} repacks ajoutés"
|
||||
"repack_count_other": "{{count}} repacks ajoutés",
|
||||
"new_update_available": "Version {{version}} disponible",
|
||||
"restart_to_install_update": "Redémarrez Hydra pour installer la mise à jour",
|
||||
"notification_achievement_unlocked_title": "Succès débloqué pour {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} et {{count}} autre(s) débloqués",
|
||||
"new_friend_request_description": "Vous avez reçu une nouvelle demande d'ami",
|
||||
"new_friend_request_title": "Nouvelle demande d'ami",
|
||||
"extraction_complete": "Extraction terminée",
|
||||
"game_extracted": "{{title}} extrait avec succès"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Ouvrir Hydra",
|
||||
"quit": "Quitter"
|
||||
},
|
||||
"game_card": {
|
||||
"available_one": "Disponible",
|
||||
"available_other": "Disponibles",
|
||||
"no_downloads": "Aucun téléchargement disponible"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programmes non installés",
|
||||
"description": "Les exécutables Wine ou Lutris sont introuvables sur votre système",
|
||||
"instructions": "Vérifiez la bonne façon d'installer l'un d'entre eux sur votre distribution Linux afin que le jeu puisse fonctionner normalement",
|
||||
"title": "Programmes non installés"
|
||||
"instructions": "Vérifiez la bonne façon d'installer l'un d'entre eux sur votre distribution Linux afin que le jeu puisse fonctionner normalement"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Page suivante",
|
||||
"previous_page": "Page précédente"
|
||||
"modal": {
|
||||
"close": "Fermer la fenêtre"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Afficher/Masquer le mot de passe"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} heures",
|
||||
"amount_minutes": "{{amount}} minutes",
|
||||
"last_time_played": "Dernière partie {{period}}",
|
||||
"activity": "Activité récente",
|
||||
"library": "Bibliothèque",
|
||||
"total_play_time": "Temps de jeu total",
|
||||
"no_recent_activity_title": "Hmm… rien ici",
|
||||
"no_recent_activity_description": "Vous n'avez pas joué récemment. Il est temps d'y remédier !",
|
||||
"display_name": "Nom d'affichage",
|
||||
"saving": "Enregistrement",
|
||||
"save": "Enregistrer",
|
||||
"edit_profile": "Modifier le profil",
|
||||
"saved_successfully": "Enregistré avec succès",
|
||||
"try_again": "Veuillez réessayer",
|
||||
"sign_out_modal_title": "Êtes-vous sûr ?",
|
||||
"cancel": "Annuler",
|
||||
"successfully_signed_out": "Déconnecté avec succès",
|
||||
"sign_out": "Se déconnecter",
|
||||
"playing_for": "En jeu depuis {{amount}}",
|
||||
"sign_out_modal_text": "Votre bibliothèque est liée à ce compte. Si vous vous déconnectez, elle ne sera plus visible et la progression ne sera pas sauvegardée. Continuer ?",
|
||||
"add_friends": "Ajouter des amis",
|
||||
"add": "Ajouter",
|
||||
"friend_code": "Code ami",
|
||||
"see_profile": "Voir le profil",
|
||||
"sending": "Envoi",
|
||||
"friend_request_sent": "Demande d'ami envoyée",
|
||||
"friends": "Amis",
|
||||
"friends_list": "Liste d'amis",
|
||||
"user_not_found": "Utilisateur introuvable",
|
||||
"block_user": "Bloquer l'utilisateur",
|
||||
"add_friend": "Ajouter un ami",
|
||||
"request_sent": "Demande envoyée",
|
||||
"request_received": "Demande reçue",
|
||||
"accept_request": "Accepter la demande",
|
||||
"ignore_request": "Ignorer la demande",
|
||||
"cancel_request": "Annuler la demande",
|
||||
"undo_friendship": "Retirer de la liste d'amis",
|
||||
"request_accepted": "Demande acceptée",
|
||||
"user_blocked_successfully": "Utilisateur bloqué avec succès",
|
||||
"user_block_modal_text": "Vous allez bloquer {{displayName}}",
|
||||
"blocked_users": "Utilisateurs bloqués",
|
||||
"unblock": "Débloquer",
|
||||
"no_friends_added": "Vous n'avez pas encore d'amis",
|
||||
"pending": "En attente",
|
||||
"no_pending_invites": "Aucune invitation en attente",
|
||||
"no_blocked_users": "Aucun utilisateur bloqué",
|
||||
"friend_code_copied": "Code ami copié",
|
||||
"undo_friendship_modal_text": "Vous allez retirer {{displayName}} de vos amis",
|
||||
"privacy_hint": "Pour changer qui voit ceci, allez dans les <0>Paramètres</0>",
|
||||
"locked_profile": "Ce profil est privé",
|
||||
"image_process_failure": "Erreur lors du traitement de l'image",
|
||||
"required_field": "Ce champ est requis",
|
||||
"displayname_min_length": "Le nom doit contenir au moins 3 caractères",
|
||||
"displayname_max_length": "Le nom doit contenir au maximum 50 caractères",
|
||||
"report_profile": "Signaler ce profil",
|
||||
"report_reason": "Pourquoi signaler ce profil ?",
|
||||
"report_description": "Informations supplémentaires",
|
||||
"report_description_placeholder": "Infos en plus",
|
||||
"report": "Signaler",
|
||||
"report_reason_hate": "Discours de haine",
|
||||
"report_reason_sexual_content": "Contenu sexuel",
|
||||
"report_reason_violence": "Violence",
|
||||
"report_reason_spam": "Spam",
|
||||
"report_reason_other": "Autre",
|
||||
"profile_reported": "Profil signalé",
|
||||
"your_friend_code": "Votre code ami :",
|
||||
"upload_banner": "Télécharger une bannière",
|
||||
"uploading_banner": "Téléversement de la bannière…",
|
||||
"background_image_updated": "Image de fond mise à jour",
|
||||
"stats": "Statistiques",
|
||||
"achievements": "Succès",
|
||||
"games": "Jeux",
|
||||
"top_percentile": "Top {{percentile}}%",
|
||||
"ranking_updated_weekly": "Classement mis à jour chaque semaine",
|
||||
"playing": "En train de jouer à {{game}}",
|
||||
"achievements_unlocked": "Succès débloqués",
|
||||
"earned_points": "Points gagnés",
|
||||
"show_achievements_on_profile": "Afficher vos succès sur votre profil",
|
||||
"show_points_on_profile": "Afficher vos points sur votre profil"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Succès débloqué",
|
||||
"user_achievements": "Succès de {{displayName}}",
|
||||
"your_achievements": "Vos succès",
|
||||
"unlocked_at": "Débloqué le : {{date}}",
|
||||
"subscription_needed": "Un abonnement Hydra Cloud est requis",
|
||||
"new_achievements_unlocked": "{{achievementCount}} nouveaux succès débloqués sur {{gameCount}} jeux",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} succès",
|
||||
"achievements_unlocked_for_game": "{{achievementCount}} nouveaux succès débloqués sur {{gameTitle}}",
|
||||
"hidden_achievement_tooltip": "Ce succès est caché",
|
||||
"achievement_earn_points": "Gagnez {{points}} points avec ce succès",
|
||||
"earned_points": "Points gagnés :",
|
||||
"available_points": "Points disponibles :",
|
||||
"how_to_earn_achievements_points": "Comment gagner des points de succès ?"
|
||||
},
|
||||
"hydra_cloud": {
|
||||
"subscription_tour_title": "Abonnement Hydra Cloud",
|
||||
"subscribe_now": "S'abonner",
|
||||
"cloud_saving": "Sauvegarde Cloud",
|
||||
"cloud_achievements": "Sauvegardez vos succès dans le cloud",
|
||||
"animated_profile_picture": "Photo de profil animée",
|
||||
"premium_support": "Support premium",
|
||||
"show_and_compare_achievements": "Montrez et comparez vos succès avec d'autres utilisateurs",
|
||||
"animated_profile_banner": "Bannière de profil animée",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"hydra_cloud_feature_found": "Vous avez découvert une fonctionnalité Hydra Cloud !",
|
||||
"learn_more": "En savoir plus",
|
||||
"debrid_description": "Téléchargez jusqu'à 4x plus vite avec Nimbus"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"options": "Opsi",
|
||||
"executable_section_title": "Eksekusi",
|
||||
"executable_section_description": "Path file eksekusi saat \"Main\" diklik",
|
||||
"downloads_secion_title": "Unduhan",
|
||||
"downloads_section_title": "Unduhan",
|
||||
"downloads_section_description": "Cek update atau versi lain dari game ini",
|
||||
"danger_zone_section_title": "Zona Berbahaya",
|
||||
"danger_zone_section_description": "Hapus game ini dari perpustakaan kamu atau file yang diunduh oleh Hydra",
|
||||
|
||||
@@ -25,6 +25,7 @@ import cs from "./cs/translation.json";
|
||||
import nb from "./nb/translation.json";
|
||||
import et from "./et/translation.json";
|
||||
import bg from "./bg/translation.json";
|
||||
import uz from "./uz/translation.json";
|
||||
|
||||
export default {
|
||||
"pt-BR": ptBR,
|
||||
@@ -54,4 +55,5 @@ export default {
|
||||
cs,
|
||||
nb,
|
||||
et,
|
||||
uz,
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
"options": "Параметрлер",
|
||||
"executable_section_title": "Файл",
|
||||
"executable_section_description": "\"Ойнау\" батырмасын басқанда іске қосылатын файл жолы",
|
||||
"downloads_secion_title": "Жүктеулер",
|
||||
"downloads_section_title": "Жүктеулер",
|
||||
"downloads_section_description": "Ойынның жаңартулары немесе басқа нұсқалары бар-жоғын тексеру",
|
||||
"danger_zone_section_title": "Қауіпті аймақ",
|
||||
"danger_zone_section_description": "Осы ойынды кітапханаңыздан жою немесе Hydra жүктеген файлдарды жою",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
"options": "Valgmuligheter",
|
||||
"executable_section_title": "Kjørbar fil",
|
||||
"executable_section_description": "Sti til filen som skal brukes når det trykkes på \"Spill\"",
|
||||
"downloads_secion_title": "Nedlastinger",
|
||||
"downloads_section_title": "Nedlastinger",
|
||||
"downloads_section_description": "Sjekk for oppdateringer eller andre versjoner af dette spillet",
|
||||
"danger_zone_section_title": "Faresonen",
|
||||
"danger_zone_section_description": "Fjern dette spillet fra biblioteket ditt eller filene som har blitt lastet ned av Hydra",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
"remove_from_library_title": "Tem certeza?",
|
||||
"executable_section_title": "Executável",
|
||||
"executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"",
|
||||
"downloads_secion_title": "Downloads",
|
||||
"downloads_section_title": "Downloads",
|
||||
"downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título",
|
||||
"danger_zone_section_title": "Zona de perigo",
|
||||
"danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra",
|
||||
@@ -187,7 +187,8 @@
|
||||
"download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.",
|
||||
"game_removed_from_favorites": "Jogo removido dos favoritos",
|
||||
"game_added_to_favorites": "Jogo adicionado aos favoritos",
|
||||
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados"
|
||||
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados",
|
||||
"create_start_menu_shortcut": "Criar atalho no Menu Iniciar"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Ativação",
|
||||
@@ -343,7 +344,8 @@
|
||||
"common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.",
|
||||
"install_common_redist": "Instalar",
|
||||
"installing_common_redist": "Instalando…",
|
||||
"show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo"
|
||||
"show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo",
|
||||
"extract_files_by_default": "Extrair arquivos automaticamente após o download"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download concluído",
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"remove_from_library_title": "Tens a certeza?",
|
||||
"executable_section_title": "Executável",
|
||||
"executable_section_description": "O caminho do ficheiro que vai ser executado ao clicar em \"Jogar\"",
|
||||
"downloads_secion_title": "Transferências",
|
||||
"downloads_section_title": "Transferências",
|
||||
"downloads_section_description": "Encontra atualizações ou versões diferentes para este mesmo título",
|
||||
"danger_zone_section_title": "Zona de perigo",
|
||||
"danger_zone_section_description": "Remove o jogo da tua biblioteca ou os ficheiros que foram transferidos pelo Hydra",
|
||||
@@ -178,7 +178,8 @@
|
||||
"download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
|
||||
"download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.",
|
||||
"game_removed_from_favorites": "Jogo removido dos favoritos",
|
||||
"game_added_to_favorites": "Jogo adicionado aos favoritos"
|
||||
"game_added_to_favorites": "Jogo adicionado aos favoritos",
|
||||
"create_start_menu_shortcut": "Criar atalho no Menu Iniciar"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Ativação",
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"options": "Настройки",
|
||||
"executable_section_title": "Файл",
|
||||
"executable_section_description": "Путь к файлу, который будет запущен при нажатии на \"Play\"",
|
||||
"downloads_secion_title": "Загрузки",
|
||||
"downloads_section_title": "Загрузки",
|
||||
"downloads_section_description": "Проверить наличие обновлений или других версий игры",
|
||||
"danger_zone_section_title": "Опасная зона",
|
||||
"danger_zone_section_description": "Вы можете удалить эту игру из вашей библиотеки или файлы скачанные из Hydra",
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"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_secion_title": "İndirmeler",
|
||||
"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.",
|
||||
@@ -195,6 +195,7 @@
|
||||
"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",
|
||||
"game_added_to_favorites": "Oyun favorilere eklendi",
|
||||
"automatically_extract_downloaded_files": "Yüklenmiş dosyaları otomatik olarak çıkart"
|
||||
@@ -354,7 +355,8 @@
|
||||
"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…"
|
||||
"installing_common_redist": "Yükleniyor…",
|
||||
"show_download_speed_in_megabytes": "İndirme hızını megabayt/saniye (MB/s) cinsinden göster"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "İndirme tamamlandı",
|
||||
@@ -498,6 +500,7 @@
|
||||
"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"
|
||||
"learn_more": "Daha Fazla Bilgi Edinin",
|
||||
"debrid_description": "Nimbus ile 4 kata kadar daha hızlı indirin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
"home": {
|
||||
"featured": "Рекомендоване",
|
||||
"surprise_me": "Здивуй мене",
|
||||
"no_results": "Результатів не знайдено"
|
||||
"no_results": "Результатів не знайдено",
|
||||
"start_typing": "Почніть набирати текст для пошуку...",
|
||||
"hot": "Гарячі новинки",
|
||||
"weekly": "📅 Найкращі ігри цього тижня",
|
||||
"achievements": "🏆 Ігри на проходження"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Каталог",
|
||||
@@ -21,11 +25,12 @@
|
||||
"game_has_no_executable": "Не було вибрано файл для запуску гри",
|
||||
"queued": "{{title}} в черзі",
|
||||
"sign_in": "Увійти",
|
||||
"favorites": "Улюблені"
|
||||
"favorites": "Улюблені",
|
||||
"friends": "Друзі",
|
||||
"need_help": "Потрібна допомога?"
|
||||
},
|
||||
"header": {
|
||||
"search": "Пошук",
|
||||
|
||||
"home": "Головна",
|
||||
"catalogue": "Каталог",
|
||||
"downloads": "Завантаження",
|
||||
@@ -38,11 +43,22 @@
|
||||
"no_downloads_in_progress": "Немає активних завантажень",
|
||||
"downloading_metadata": "Завантаження метаданих {{title}}…",
|
||||
"downloading": "Завантаження {{title}}… ({{percentage}} завершено) - Закінчення {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Завантаження {{title}}… ({{percentage}} завершено) - Обчислення залишкового часу…"
|
||||
"calculating_eta": "Завантаження {{title}}… ({{percentage}} завершено) - Обчислення залишкового часу…",
|
||||
"checking_files": "Перевірка файлів {{title}} ({{percentage}} виповнено)",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"installation_complete": "Встановлення завершено",
|
||||
"installation_complete_message": "Загальні розповсюджувані файли успішно встановлено"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Наступна сторінка",
|
||||
"previous_page": "Попередня сторінка"
|
||||
"search": "Фільтрувати…",
|
||||
"developers": "Розробники",
|
||||
"genres": "Жанри",
|
||||
"tags": "Теги",
|
||||
"publishers": "Видавці",
|
||||
"download_sources": "Джерела",
|
||||
"result_count": "{{resultCount}} результатів",
|
||||
"filter_count": "{{filterCount}} доступно",
|
||||
"clear_filters": "Очистити {{filterCount}} вибрані"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Відкрити варіанти завантаження",
|
||||
@@ -86,18 +102,31 @@
|
||||
"download_now": "Завантажити зараз",
|
||||
"calculating_eta": "Обчислення залишкового часу…",
|
||||
"create_shortcut": "Створити ярлик на робочому столі",
|
||||
"create_shortcut_success": "Ярлик успішно створено",
|
||||
"create_shortcut_error": "Виникла помилка під час створення ярлику",
|
||||
"nsfw_content_title": "Ця гра містить неприйнятний контент",
|
||||
"nsfw_content_description": "{{title}} містить вміст, який може бути непридатним для всіх вікових груп. Ви впевнені, що хочете продовжити?",
|
||||
"allow_nsfw_content": "Продовжити",
|
||||
"refuse_nsfw_content": "Вернутись назад",
|
||||
"stats": "Статистика",
|
||||
"clear": "Очистити",
|
||||
"danger_zone_section_description": "Видалити цю гру з вашої бібліотеки або файли скачані Hydra",
|
||||
"danger_zone_section_title": "Небезпечна зона",
|
||||
"player_count": "Грають зараз",
|
||||
"download": "Завантажити",
|
||||
"download_count": "Завантажень",
|
||||
"download_in_progress": "Триває завантаження.",
|
||||
"download_options": "Варіантів завантаження",
|
||||
"download_path": "Тека для завантажень",
|
||||
"download_paused": "Завантаження призупинено",
|
||||
"download_settings": "Налаштування завантаження",
|
||||
"download_error": "Цей варіант завантаження не доступний",
|
||||
"downloader": "Завантажувач",
|
||||
"downloads_secion_title": "Завантаження",
|
||||
"downloads_section_title": "Завантаження",
|
||||
"downloads_section_description": "Перевірити наявність оновлень або інших версій гри",
|
||||
"executable_section_description": "Шлях до файлу, який буде запущений при натисканні на кнопку \"Play\"",
|
||||
"executable_section_title": "Файл",
|
||||
"executable_path_in_use": "Виконуваний файл наразі використовується \"{{game}}\"",
|
||||
"last_downloaded_option": "Останній варіант завантаження",
|
||||
"next_screenshot": "Наступний скрішнот",
|
||||
"no_executable_selected": "Файл не вибрано",
|
||||
@@ -112,7 +141,64 @@
|
||||
"remove_from_library_description": "{{game}} буде видалено з вашої бібліотеки",
|
||||
"remove_from_library_title": "Ви впевнені?",
|
||||
"screenshot": "Скріншот",
|
||||
"select_executable": "Обрати"
|
||||
"select_executable": "Обрати",
|
||||
"warning": "Попередження:",
|
||||
"hydra_needs_to_remain_open": "Hydra повинна залишатись відкритою до завершення завантаження. Якщо Hydra закриється до завершення, ви втратите прогрес.",
|
||||
"achievements": "Досягнення",
|
||||
"achievements_count": "Досягнень {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Хмарне Збереження",
|
||||
"cloud_save_description": "Збережіть свій ігровий процесс у хмарі та продовжіть грати з любого пристрою",
|
||||
"backups": "Резервні копії",
|
||||
"install_backup": "Встановити",
|
||||
"delete_backup": "Видалити",
|
||||
"create_backup": "Нова резервна копія",
|
||||
"last_backup_date": "Остання резервна копія {{date}}",
|
||||
"no_backup_preview": "Не було знайдено жодних збережень для цієї гри.",
|
||||
"restoring_backup": "Відновлення резервної копії ({{progress}} виконано)…",
|
||||
"uploading_backup": "Вивантаження резервної копії…",
|
||||
"no_backups": "Ви ще не створили жодних резеврних копій для цієї гри",
|
||||
"backup_uploaded": "Резервна копія вивантажена",
|
||||
"backup_deleted": "Резервна копія видалена",
|
||||
"backup_restored": "Резервна копія відновлена",
|
||||
"see_all_achievements": "Переглянути всі досягнення",
|
||||
"sign_in_to_see_achievements": "Увійдіть, що б переглянути усі досягнення",
|
||||
"mapping_method_automatic": "Автоматичний",
|
||||
"mapping_method_manual": "Ручний",
|
||||
"mapping_method_label": "Спосіб визначення файлів для резервного копіювання",
|
||||
"files_automatically_mapped": "Hydra автоматично вибере файли для резервного копіювання",
|
||||
"no_backups_created": "Для цієї гри не було створено резервних копій",
|
||||
"manage_files": "Керувати файлами",
|
||||
"loading_save_preview": "Виконується пошук збережень гри...",
|
||||
"wine_prefix": "Префікс Wine",
|
||||
"wine_prefix_description": "Префікс Wine використовувався для запуску цієї гри",
|
||||
"launch_options": "Параметри загрузки",
|
||||
"launch_options_description": "Досвідчені користувачі можуть ввести власні модифікації до параметрів запуску (експериментальна функція).",
|
||||
"launch_options_placeholder": "Параметри не вказано",
|
||||
"no_download_option_info": "Немає інформації",
|
||||
"backup_deletion_failed": "Виникла помилка при видалені резервної копії",
|
||||
"max_number_of_artifacts_reached": "Максимальну кількість резервних копій для цієї гри досягнуто",
|
||||
"achievements_not_sync": "Дізнайтеся, як синхронізувати свої досягнення",
|
||||
"manage_files_description": "Керуйте файлами, які буде збережено та відновлено",
|
||||
"select_folder": "Обрати папку",
|
||||
"backup_from": "Резервна копія від {{date}}",
|
||||
"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, і перевірка статусу завантаження з Real-Debrid наразі недоступна.",
|
||||
"download_error_not_cached_on_torbox": "Це завантаження недоступне на TorBox, і перевірка статусу завантаження з TorBox наразі недоступна.",
|
||||
"download_error_not_cached_on_hydra": "Це завантаження недоступне через Nimbus.",
|
||||
"game_removed_from_favorites": "Гра видалена з улюбленних",
|
||||
"game_added_to_favorites": "Гра була добавлена у улюблені",
|
||||
"automatically_extract_downloaded_files": "Автоматично розархівувати завантаженні файли"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Активувати Hydra",
|
||||
@@ -140,11 +226,18 @@
|
||||
"install": "Встановити",
|
||||
"download_in_progress": "В процесі",
|
||||
"downloads_completed": "Завершено",
|
||||
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати.",
|
||||
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати!",
|
||||
"no_downloads_title": "Тут так пусто...",
|
||||
"queued": "В черзі",
|
||||
"queued_downloads": "Завантаження в черзі",
|
||||
"removed": "Не завантажено"
|
||||
"removed": "Не завантажено",
|
||||
"checking_files": "Перевірка файлів…",
|
||||
"seeding": "Сідінг",
|
||||
"stop_seeding": "Зупинити сідінг",
|
||||
"resume_seeding": "Продовжити сідінг",
|
||||
"options": "Налаштування",
|
||||
"extract": "Розархівувати файли",
|
||||
"extracting": "Розархівовування файлів…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Тека завантажень",
|
||||
@@ -181,7 +274,7 @@
|
||||
"description_confirmation_delete_all_sources": "Ви видалите всі джерела завантаження",
|
||||
"title_confirmation_delete_all_sources": "Видалити всі джерела завантаження",
|
||||
"removed_download_sources": "Джерела завантажень видалено",
|
||||
"button_delete_all_sources": "Видаліть усі джерела завантаження",
|
||||
"button_delete_all_sources": "Видалити усі джерела завантаження",
|
||||
"api_token": "API-токен",
|
||||
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
|
||||
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
||||
@@ -193,21 +286,102 @@
|
||||
"removed_download_source": "Джерело завантажень було видалено",
|
||||
"save_changes": "Зберегти зміни",
|
||||
"sync_download_sources": "Синхронізувати джерела",
|
||||
"validate_download_source": "Перевірити"
|
||||
"validate_download_source": "Перевірити",
|
||||
"public": "Публічний",
|
||||
"private": "Приватний",
|
||||
"friends_only": "Тільки для друзів",
|
||||
"privacy": "Приватність",
|
||||
"profile_visibility": "Видимість профілю",
|
||||
"profile_visibility_description": "Оберіть хто може бачити ваш профіль та бібліотеку",
|
||||
"required_field": "Це поле обов'язкове",
|
||||
"source_already_exists": "Це джерело уже додано",
|
||||
"must_be_valid_url": "Джерело має бути дійсною URL-адресою",
|
||||
"blocked_users": "Заблоковані користувачі",
|
||||
"user_unblocked": "Користувача було розблоковано",
|
||||
"enable_achievement_notifications": "Коли отримано нове досягнення",
|
||||
"launch_minimized": "Запустити Hydra у згорнутому вигляді",
|
||||
"disable_nsfw_alert": "Вимкнути сповіщення про NSFW",
|
||||
"seed_after_download_complete": "Cідувати по завершенню завантажень",
|
||||
"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 — це ваш преміум-сервіс для сідінгу, що конкурує навіть з найкращими серверами на ринку.",
|
||||
"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": "Показувати швидкість завантаження в мегабайтах на секунду",
|
||||
"extract_files_by_default": "Розпаковувати файли після завантаження"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Завантаження завершено",
|
||||
"game_ready_to_install": "{{title}} готова до встановлення",
|
||||
"repack_list_updated": "Список репаків оновлено",
|
||||
"repack_count_one": "{{count}} репак додано",
|
||||
"repack_count_other": "{{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}} було розблоковано",
|
||||
"new_friend_request_description": "Ви отримали новий запит на дружбу",
|
||||
"new_friend_request_title": "Новий запит на дружбу",
|
||||
"extraction_complete": "Витягування завершено",
|
||||
"game_extracted": "{{title}} успішно витягнуто"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Відкрити Hydra",
|
||||
"quit": "Вийти"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "Немає доступних завантажень"
|
||||
"no_downloads": "Немає доступних завантажень",
|
||||
"available_one": "Доступний",
|
||||
"available_other": "Доступні"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Програми не встановлені",
|
||||
@@ -240,6 +414,94 @@
|
||||
"sign_out_modal_title": "Ви впевнені?",
|
||||
"successfully_signed_out": "Успішний вихід з акаунту",
|
||||
"total_play_time": "Всього зіграно",
|
||||
"try_again": "Будь ласка, попробуйте ще раз"
|
||||
"try_again": "Будь ласка, попробуйте ще раз",
|
||||
"add_friends": "Добавити друзів",
|
||||
"add": "Добавити",
|
||||
"friend_code": "Код друга",
|
||||
"see_profile": "Переглянути профіль",
|
||||
"sending": "Надсилання",
|
||||
"friend_request_sent": "Запит на дружбу було надіслано",
|
||||
"friends": "Друзі",
|
||||
"friends_list": "Список друзів",
|
||||
"user_not_found": "Користувача не найдено",
|
||||
"block_user": "Заблокувати користувача",
|
||||
"add_friend": "Добавити друга",
|
||||
"request_sent": "надіслано запит на дружбу",
|
||||
"request_received": "Отримано запит на дружбу",
|
||||
"accept_request": "Прийняти запит",
|
||||
"ignore_request": "Ігнорувати запит",
|
||||
"cancel_request": "Скасувати запит",
|
||||
"undo_friendship": "Видалити з друзів",
|
||||
"request_accepted": "Запит прийнято",
|
||||
"user_blocked_successfully": "Користувач успішно заблокований",
|
||||
"user_block_modal_text": "Це заблокує {{displayName}}",
|
||||
"blocked_users": "Заблоковані користувачі",
|
||||
"unblock": "Розблокувати",
|
||||
"no_friends_added": "У вас немає доданих друзів",
|
||||
"pending": "Очікування",
|
||||
"no_pending_invites": "У вас немає запитів, що очікують на підтвердження",
|
||||
"no_blocked_users": "У вас немає заблокованих користувачів",
|
||||
"friend_code_copied": "Код друга скопійовано",
|
||||
"undo_friendship_modal_text": "Це видалить з друзів {{displayName}}",
|
||||
"privacy_hint": "Щоб налаштувати, хто може це бачити, перейдіть до <0>Settings</0>",
|
||||
"locked_profile": "Цей профіль приватний",
|
||||
"image_process_failure": "Помилка при обробці зображення",
|
||||
"required_field": "Це поле обов'язкове",
|
||||
"displayname_min_length": "Ім'я користувача повинно містити не менше 3 символів",
|
||||
"displayname_max_length": "Ім'я користувача повинно містити не більше 50 символів",
|
||||
"report_profile": "Повідомити про цей профіль",
|
||||
"report_reason": "Чому ви повідомляєте про цей профіль?",
|
||||
"report_description": "Додаткова інформація",
|
||||
"report_description_placeholder": "Додаткова інформація",
|
||||
"report": "Повідомити",
|
||||
"report_reason_hate": "Мова ненависті",
|
||||
"report_reason_sexual_content": "Сексуальний контент",
|
||||
"report_reason_violence": "Насильство",
|
||||
"report_reason_spam": "Спам",
|
||||
"report_reason_other": "Інше",
|
||||
"profile_reported": "Профіль повідомлено",
|
||||
"your_friend_code": "Ваш код друга:",
|
||||
"upload_banner": "Завантажити банер",
|
||||
"uploading_banner": "Завантаження банеру...",
|
||||
"background_image_updated": "Фонове зображення оновлено",
|
||||
"stats": "Статистика",
|
||||
"achievements": "Досягнення",
|
||||
"games": "Ігри",
|
||||
"top_percentile": "Топ {{percentile}}%",
|
||||
"ranking_updated_weekly": "Рейтинг оновлюється щотижня",
|
||||
"playing": "Грає у {{game}}",
|
||||
"achievements_unlocked": "Досягнень розблоковано",
|
||||
"earned_points": "Отримано балів",
|
||||
"show_achievements_on_profile": "Покажіть свої досягнення у своєму профілі",
|
||||
"show_points_on_profile": "Покажіть ваші отриманні бали у своєму профілі"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Досягнення розблоковано",
|
||||
"user_achievements": "Досягнення користувача {{displayName}}",
|
||||
"your_achievements": "Ваші досягнення",
|
||||
"unlocked_at": "Розблоковано: {{date}}",
|
||||
"subscription_needed": "Для перегляду цього контенту потрібна підписка на Hydra Cloud",
|
||||
"new_achievements_unlocked": "Розблоковано {{achievementCount}} нових досягнень з {{gameCount}} ігор",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} досягнень",
|
||||
"achievements_unlocked_for_game": "Розблоковано {{achievementCount}} нових досягнень у грі {{gameTitle}}",
|
||||
"hidden_achievement_tooltip": "Це приховане досягнення",
|
||||
"achievement_earn_points": " Отримайте {{points}} балів за це досягнення",
|
||||
"earned_points": "Отримано балів:",
|
||||
"available_points": "Доступно балів:",
|
||||
"how_to_earn_achievements_points": "Як заробляти бали за досягнення?"
|
||||
},
|
||||
"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": "Дізнатися більше",
|
||||
"debrid_description": "Завантажуйте до 4 разів швидше з Nimbus"
|
||||
}
|
||||
}
|
||||
|
||||
476
src/locales/uz/translation.json
Normal file
476
src/locales/uz/translation.json
Normal file
@@ -0,0 +1,476 @@
|
||||
{
|
||||
"language_name": "Uzbek",
|
||||
"app": {
|
||||
"successfully_signed_in": "Kirish muvaffaqiyatli amalga oshirildi"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Tavsiya etilgan",
|
||||
"surprise_me": "Hayratda qoldir",
|
||||
"no_results": "Natijalar topilmadi",
|
||||
"hot": "Eng mashhur",
|
||||
"start_typing": "Yozishni boshlayapman ...",
|
||||
"weekly": "📅 Haftaning eng yaxshi o'yinlari",
|
||||
"achievements": "🏆 Yutuqlar bilan o'yinlar"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Yuklab olishlar",
|
||||
"settings": "Sozlamalar",
|
||||
"my_library": "Mening kutubxonam",
|
||||
"downloading_metadata": "{{title}} (Metamaʼlumotlar yuklanmoqda…)",
|
||||
"paused": "{{title}} (To'xtatildi)",
|
||||
"downloading": "{{title}} ({{percentage}} - Yuklanmoqda…)",
|
||||
"filter": "Qidiruv",
|
||||
"home": "Asosiy",
|
||||
"queued": "{{title}} (Navbatda)",
|
||||
"game_has_no_executable": "Oʻyinni ishga tushirish fayli tanlanmagan",
|
||||
"sign_in": "Kirish",
|
||||
"friends": "Do'stlar",
|
||||
"need_help": "Yordam kerakmi?",
|
||||
"favorites": "Sevimlilar"
|
||||
},
|
||||
"header": {
|
||||
"search": "Qidirish",
|
||||
"home": "Asosiy",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Yuklab olishlar",
|
||||
"search_results": "Qidiruv natijalari",
|
||||
"settings": "Sozlamalar",
|
||||
"version_available_install": "{{version}} versiyasi mavjud. Oʻrnatish uchun shu yerni bosing.",
|
||||
"version_available_download": "{{version}} versiyasi mavjud. Yuklab olish uchun shu yerni bosing."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Faol yuklab olishlar yo'q",
|
||||
"downloading_metadata": "Metamaʼlumotlar yuklanmoqda {{title}}…",
|
||||
"downloading": "Yuklanmoqda {{title}}… ({{percentage}} yakunlandi) - Tugash vaqti {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Yuklanmoqda {{title}}… ({{percentage}} yakunlandi) - Qolgan vaqt hisoblanmoqda…",
|
||||
"checking_files": "Fayllar tekshirilmoqda {{title}}… ({{percentage}} yakunlandi)",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"installation_complete": "O'rnatish yakunlandi",
|
||||
"installation_complete_message": "Kutubxonalar muvaffaqiyatli o'rnatildi"
|
||||
},
|
||||
"catalogue": {
|
||||
"search": "Filter…",
|
||||
"developers": "Ishlab chiquvchilar",
|
||||
"genres": "Janrlar",
|
||||
"tags": "Teglar",
|
||||
"publishers": "Nashriyotlar",
|
||||
"download_sources": "Yuklab olish manbalari",
|
||||
"result_count": "{{resultCount}} natija",
|
||||
"filter_count": "{{filterCount}} mavjud",
|
||||
"clear_filters": "{{filterCount}} tanlangan filtrni tozalash"
|
||||
},
|
||||
"game_details": {
|
||||
"play_time": "O'ynalgan vaqt {{amount}}",
|
||||
"last_time_played": "Oxirgi ishga tushirilgan {{period}}",
|
||||
"not_played_yet": "Siz hali {{title}}ni o'ynamagansiz",
|
||||
"next_suggestion": "Keyingi taklif",
|
||||
"play": "O'ynash",
|
||||
"deleting": "O'rnatuvchi o'chirilmoqda…",
|
||||
"close": "Yopish",
|
||||
"playing_now": "Hozir o'ynalmoqda",
|
||||
"change": "O'zgartirish",
|
||||
"repacks_modal_description": "Yuklab olish uchun repakni tanlang",
|
||||
"select_folder_hint": "Standart yuklab olish jildini o'zgartirish uchun <0>Sozlamalar</0>ni oching",
|
||||
"download_now": "Hozir yuklab olish",
|
||||
"no_shop_details": "Tavsif olib bo'lmadi",
|
||||
"download_options": "Manbalar",
|
||||
"download_path": "Yuklab olish yo'li",
|
||||
"previous_screenshot": "Oldingi skrinshot",
|
||||
"next_screenshot": "Keyingi skrinshot",
|
||||
"screenshot": "Skrinshot {{number}}",
|
||||
"open_screenshot": "{{number}}-skrinshotni ochish",
|
||||
"download_settings": "Yuklab olish sozlamalari",
|
||||
"downloader": "Yuklovchi",
|
||||
"select_executable": "Tanlash",
|
||||
"no_executable_selected": "Fayl tanlanmagan",
|
||||
"open_folder": "Jildni ochish",
|
||||
"open_download_location": "Yuklab olish jildini ko'rish",
|
||||
"create_shortcut": "Ish stoliga yorliq yaratish",
|
||||
"clear": "Tozalash",
|
||||
"remove_files": "Fayllarni o'chirish",
|
||||
"remove_from_library_title": "Ishonchingiz komilmi?",
|
||||
"remove_from_library_description": "{{game}} kutubxonangizdan o'chiriladi.",
|
||||
"options": "Sozlamalar",
|
||||
"executable_section_title": "Fayl",
|
||||
"executable_section_description": "\"O'ynash\" tugmasi bosilganda ishga tushiriladigan fayl yo'li",
|
||||
"downloads_section_title": "Yuklab olishlar",
|
||||
"downloads_section_description": "Yangilanishlar yoki o'yinning boshqa versiyalarini tekshirish",
|
||||
"danger_zone_section_title": "Xavfli zona",
|
||||
"danger_zone_section_description": "Bu o'yinni kutubxonangizdan yoki Hydradan yuklab olingan fayllarni o'chirishingiz mumkin",
|
||||
"download_in_progress": "Yuklab olish davom etmoqda",
|
||||
"download_paused": "Yuklab olish to'xtatilgan",
|
||||
"last_downloaded_option": "Oxirgi yuklab olish varianti",
|
||||
"create_shortcut_success": "Yorliq yaratildi",
|
||||
"create_shortcut_error": "Yorliq yaratib bo'lmadi",
|
||||
"allow_nsfw_content": "Davom etish",
|
||||
"download": "Yuklab olish",
|
||||
"download_count": "Yuklab olishlar",
|
||||
"download_error": "Bu yuklab olish varianti mavjud emas",
|
||||
"executable_path_in_use": "Bajarish fayli allaqachon \"{{game}}\" tomonidan ishlatilmoqda",
|
||||
"nsfw_content_description": "{{title}} barcha yoshdagilar uchun mos bo'lmasligi mumkin bo'lgan tarkibni o'z ichiga oladi. \nDavom etishni xohlaysizmi?",
|
||||
"nsfw_content_title": "Bu o'yin nomunosib tarkibga ega",
|
||||
"refuse_nsfw_content": "Orqaga",
|
||||
"stats": "Statistika",
|
||||
"player_count": "Faol o'yinchilar",
|
||||
"warning": "Ogohlantirish:",
|
||||
"hydra_needs_to_remain_open": "Bu yuklab olish uchun Hydra tugaguncha ochiq qolishi kerak. Agar Hydra tugashidan oldin yopilsa, jarayonni yo'qotasiz.",
|
||||
"achievements": "Yutuqlar",
|
||||
"achievements_count": "Yutuqlar {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Bulutli saqlash",
|
||||
"cloud_save_description": "O'yin jarayoningizni bulutda saqlang va istalgan qurilmada o'ynashni davom ettiring",
|
||||
"backups": "Zaxira nusxalar",
|
||||
"install_backup": "O'rnatish",
|
||||
"delete_backup": "O'chirish",
|
||||
"create_backup": "Yangi zaxira nusxa yaratish",
|
||||
"last_backup_date": "Oxirgi zaxira nusxa {{date}} dan",
|
||||
"no_backup_preview": "Bu sarlavha uchun saqlashlar topilmadi",
|
||||
"restoring_backup": "Zaxira nusxa tiklanmoqda ({{progress}} yakunlandi)…",
|
||||
"uploading_backup": "Zaxira nusxa yuklanmoqda…",
|
||||
"no_backups": "Siz hali bu o'yin uchun zaxira nusxa yaratmagansiz",
|
||||
"backup_uploaded": "Zaxira nusxa yuklandi",
|
||||
"backup_deleted": "Zaxira nusxa o'chirildi",
|
||||
"backup_restored": "Zaxira nusxa tiklandi",
|
||||
"see_all_achievements": "Barcha yutuqlarni ko'rish",
|
||||
"sign_in_to_see_achievements": "Yutuqlarni ko'rish uchun tizimga kiring",
|
||||
"mapping_method_automatic": "Avtomatik",
|
||||
"mapping_method_manual": "Qo'lda",
|
||||
"mapping_method_label": "Moslashtirish usuli",
|
||||
"files_automatically_mapped": "Fayllar avtomatik moslashtirildi",
|
||||
"no_backups_created": "Bu o'yin uchun zaxira nusxalar yaratilmagan",
|
||||
"manage_files": "Fayllarni boshqarish",
|
||||
"loading_save_preview": "Saqlashlar qidirilmoqda…",
|
||||
"wine_prefix": "Wine prefiksi",
|
||||
"wine_prefix_description": "Bu o'yinni ishga tushirish uchun ishlatiladigan Wine prefiksi",
|
||||
"launch_options": "Ishga tushirish parametrlari",
|
||||
"launch_options_description": "Tajribali foydalanuvchilar ishga tushirish parametrlarini o'zgartirishi mumkin",
|
||||
"launch_options_placeholder": "Parametr ko'rsatilmagan",
|
||||
"no_download_option_info": "Ma'lumot mavjud emas",
|
||||
"backup_deletion_failed": "Zaxira nusxani o'chirib bo'lmadi",
|
||||
"max_number_of_artifacts_reached": "Bu o'yin uchun maksimal zaxira nusxalar soniga yetildi",
|
||||
"achievements_not_sync": "Yutuqlaringiz sinxronlanmagan",
|
||||
"manage_files_description": "Saqlanishi va tiklanishi kerak bo'lgan fayllarni boshqaring",
|
||||
"select_folder": "Jildni tanlash",
|
||||
"backup_from": "{{date}} dan zaxira nusxa",
|
||||
"automatic_backup_from": "{{date}} dan avtomatik zaxira nusxa",
|
||||
"enable_automatic_cloud_sync": "Avtomatik bulutli sinxronlashni yoqish",
|
||||
"custom_backup_location_set": "Maxsus zaxira nusxa joylashuvi o'rnatildi",
|
||||
"no_directory_selected": "Katalog tanlanmagan",
|
||||
"no_write_permission": "Bu katalogga yuklab bo'lmaydi. Ko'proq ma'lumot olish uchun bu yerga bosing.",
|
||||
"reset_achievements": "Yutuqlarni tiklash",
|
||||
"reset_achievements_description": "Bu {{game}} uchun barcha yutuqlarni tiklaydi",
|
||||
"reset_achievements_title": "Ishonchingiz komilmi?",
|
||||
"reset_achievements_success": "Yutuqlar muvaffaqiyatli tiklandi",
|
||||
"reset_achievements_error": "Yutuqlarni tiklab bo'lmadi",
|
||||
"download_error_gofile_quota_exceeded": "Siz Gofile oylik kvotangizni oshirib yubordingiz. Iltimos, kvota tiklanguncha kuting.",
|
||||
"download_error_real_debrid_account_not_authorized": "Sizning Real-Debrid hisobingiz yangi yuklab olishlar uchun avtorizatsiya qilinmagan. Iltimos, hisob sozlamalarini tekshiring va qaytadan urinib ko'ring.",
|
||||
"download_error_not_cached_on_real_debrid": "Bu yuklab olish Real-Debrid'da mavjud emas, va Real-Debrid'dan yuklab olish holatini olish hozircha mavjud emas.",
|
||||
"download_error_not_cached_on_torbox": "Bu yuklab olish TorBox'da mavjud emas, va TorBox'dan yuklab olish holatini olish hozircha mumkin emas.",
|
||||
"game_added_to_favorites": "O'yin sevimlilarga qo'shildi",
|
||||
"game_removed_from_favorites": "O'yin sevimlilardan olib tashlandi",
|
||||
"automatically_extract_downloaded_files": "Yuklab olingan fayllarni avtomatik chiqarish"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Hydra'ni faollashtirish",
|
||||
"installation_id": "O'rnatish ID'si:",
|
||||
"enter_activation_code": "Faollashtirish kodini kiriting",
|
||||
"message": "Agar qayerdan so'rash kerakligini bilmasangiz, u sizda bo'lmasligi kerak.",
|
||||
"activate": "Faollashtirish",
|
||||
"loading": "Yuklanmoqda…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Davom ettirish",
|
||||
"pause": "To'xtatib turish",
|
||||
"eta": "Tugash vaqti {{eta}}",
|
||||
"paused": "To'xtatilgan",
|
||||
"verifying": "Tekshirilmoqda…",
|
||||
"completed": "Yakunlandi",
|
||||
"removed": "Yuklab olinmagan",
|
||||
"cancel": "Bekor qilish",
|
||||
"filter": "Yuklab olingan o'yinlarni qidirish",
|
||||
"remove": "O'chirish",
|
||||
"downloading_metadata": "Metamaʼlumotlar yuklanmoqda…",
|
||||
"deleting": "O'rnatuvchi o'chirilmoqda…",
|
||||
"delete": "O'rnatuvchini o'chirish",
|
||||
"delete_modal_title": "Ishonchingiz komilmi?",
|
||||
"delete_modal_description": "Bu kompyuteringizdan barcha o'rnatuvchilarni o'chiradi",
|
||||
"install": "O'rnatish",
|
||||
"download_in_progress": "Jarayonda",
|
||||
"queued_downloads": "Navbatdagi yuklab olishlar",
|
||||
"downloads_completed": "Yakunlangan",
|
||||
"queued": "Navbatda",
|
||||
"no_downloads_title": "Bu yer juda bo'sh...",
|
||||
"no_downloads_description": "Siz hali Hydra orqali hech narsa yuklab olmadingiz, lekin boshlash uchun hech qachon kech emas.",
|
||||
"checking_files": "Fayllar tekshirilmoqda…",
|
||||
"seeding": "Ulashish",
|
||||
"stop_seeding": "Ulashishni to'xtatish",
|
||||
"resume_seeding": "Ulashishni davom ettirish",
|
||||
"options": "Boshqarish",
|
||||
"extract": "Fayllarni chiqarish",
|
||||
"extracting": "Fayllar chiqarilmoqda…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Yuklab olish yo'li",
|
||||
"change": "O'zgartirish",
|
||||
"notifications": "Bildirishnomalar",
|
||||
"enable_download_notifications": "Yuklab olish tugaganda",
|
||||
"enable_achievement_notifications": "Yutuq ochilganda",
|
||||
"enable_repack_list_notifications": "Yangi repak qo'shilganda",
|
||||
"real_debrid_api_token_label": "Real-Debrid API-tokeni",
|
||||
"quit_app_instead_hiding": "Ilovani trayga yig'ish o'rniga yopish",
|
||||
"launch_with_system": "Hydra'ni tizim bilan birga ishga tushirish",
|
||||
"launch_minimized": "Hydra'ni yig'ilgan holda ishga tushirish",
|
||||
"disable_nsfw_alert": "Noqulay kontent haqida ogohlantirishni o'chirish",
|
||||
"general": "Asosiy",
|
||||
"behavior": "Xatti-harakat",
|
||||
"download_sources": "Yuklab olish manbalari",
|
||||
"language": "Til",
|
||||
"api_token": "API kalit",
|
||||
"enable_real_debrid": "Real-Debrid'ni yoqish",
|
||||
"real_debrid_description": "Real-Debrid - bu cheksiz yuklab oluvchi bo'lib, internetda joylashtirilgan fayllarni tezda yuklab olishga yoki ularni xususiy tarmoq orqali pleerga zudlik bilan o'tkazishga imkon beradi, bu esa har qanday blokirovkalarni chetlab o'tishga imkon beradi.",
|
||||
"debrid_invalid_token": "Noto'g'ri API kalit",
|
||||
"debrid_api_token_hint": "API kalitni <0>bu yerda</0> olish mumkin",
|
||||
"real_debrid_free_account_error": "\"{{username}}\" hisobi - obunaga ega emas. Iltimos, Real-Debrid obunasini rasmiylashtiring",
|
||||
"debrid_linked_message": "\"{{username}}\" hisobi bog'langan",
|
||||
"save_changes": "O'zgarishlarni saqlash",
|
||||
"changes_saved": "O'zgarishlar muvaffaqiyatli saqlandi",
|
||||
"download_sources_description": "Hydra yuklab olish havolalarini ushbu manbalardan oladi. URL yuklab olish uchun havolalar bilan .json-fayliga to'g'ridan-to'g'ri havolani o'z ichiga olishi kerak.",
|
||||
"validate_download_source": "Tekshirish",
|
||||
"remove_download_source": "O'chirish",
|
||||
"add_download_source": "Manba qo'shish",
|
||||
"download_count_zero": "Ro'yxatda yuklab olishlar yo'q",
|
||||
"download_count_one": "Ro'yxatda {{countFormatted}} ta yuklab olish",
|
||||
"download_count_other": "Ro'yxatda {{countFormatted}} ta yuklab olish",
|
||||
"download_source_url": "Manba havolasi",
|
||||
"add_download_source_description": ".json-fayliga havolani joylang",
|
||||
"download_source_up_to_date": "Yangilangan",
|
||||
"download_source_errored": "Xato",
|
||||
"sync_download_sources": "Manbalarni yangilash",
|
||||
"removed_download_source": "Manba o'chirildi",
|
||||
"cancel_button_confirmation_delete_all_sources": "Yo'q",
|
||||
"confirm_button_confirmation_delete_all_sources": "Ha, barchasini o'chirish",
|
||||
"description_confirmation_delete_all_sources": "Siz barcha manbalarni o'chirasiz",
|
||||
"title_confirmation_delete_all_sources": "Barcha manbalarni o'chirish",
|
||||
"removed_download_sources": "Manbalar o'chirildi",
|
||||
"button_delete_all_sources": "Barcha manbalarni o'chirish",
|
||||
"added_download_source": "Manba qo'shildi",
|
||||
"download_sources_synced": "Barcha manbalar yangilandi",
|
||||
"insert_valid_json_url": "Amaldagi JSON-fayl URL'ini kiriting",
|
||||
"found_download_option_zero": "Yuklab olish variantlari topilmadi",
|
||||
"found_download_option_one": "{{countFormatted}} yuklab olish varianti topildi",
|
||||
"found_download_option_other": "{{countFormatted}} yuklab olish varianti topildi",
|
||||
"import": "Import qilish",
|
||||
"blocked_users": "Bloklangan foydalanuvchilar",
|
||||
"friends_only": "Faqat do'stlar uchun",
|
||||
"must_be_valid_url": "Manbada to'g'ri URL bo'lishi kerak",
|
||||
"privacy": "Maxfiylik",
|
||||
"private": "Shaxsiy",
|
||||
"profile_visibility": "Profil ko'rinuvchanligi",
|
||||
"profile_visibility_description": "Kim sizning profilingiz va kutubxonangizni ko'ra olishini tanlang",
|
||||
"public": "Ommaviy",
|
||||
"required_field": "Bu maydon to'ldirilishi shart",
|
||||
"source_already_exists": "Bu manba allaqachon qo'shilgan",
|
||||
"user_unblocked": "Foydalanuvchi blokdan chiqarildi",
|
||||
"seed_after_download_complete": "Yuklab olish tugagandan so'ng ulashish",
|
||||
"show_hidden_achievement_description": "Yashirin yutuqlarning tavsifini ularni olishdan oldin ko'rsatish",
|
||||
"account": "Hisob",
|
||||
"no_users_blocked": "Sizda bloklangan foydalanuvchilar yo'q",
|
||||
"subscription_active_until": "Hydra Cloud obunangiz {{date}} ga qadar faol",
|
||||
"manage_subscription": "Obunani boshqarish",
|
||||
"update_email": "Elektron pochtani yangilash",
|
||||
"update_password": "Parolni yangilash",
|
||||
"current_email": "Joriy elektron pochta:",
|
||||
"no_email_account": "Siz hali elektron pochta o'rnatmagansiz",
|
||||
"account_data_updated_successfully": "Hisob ma'lumotlari muvaffaqiyatli yangilandi",
|
||||
"renew_subscription": "Hydra Cloud obunasini yangilash",
|
||||
"subscription_expired_at": "Obunangiz muddati {{date}} da tugagan",
|
||||
"no_subscription": "Hydra'dan maksimal darajada bahramand bo'ling",
|
||||
"become_subscriber": "Hydra Cloud egasiga aylaning",
|
||||
"subscription_renew_cancelled": "Avtomatik yangilash o'chirilgan",
|
||||
"subscription_renews_on": "Obunangiz {{date}} da yangilanadi",
|
||||
"bill_sent_until": "Keyingi hisobingiz shu kungacha yuboriladi",
|
||||
"no_themes": "Sizda hali mavzular yo'qqa o'xshaydi, lekin tashvishlanmang, birinchi shoh asaringizni yaratish uchun shu yerni bosing",
|
||||
"editor_tab_code": "Kod",
|
||||
"editor_tab_info": "Ma'lumot",
|
||||
"editor_tab_save": "Saqlash",
|
||||
"web_store": "Veb-do'kon",
|
||||
"clear_themes": "Tozalash",
|
||||
"create_theme": "Yaratish",
|
||||
"create_theme_modal_title": "Maxsus mavzu yaratish",
|
||||
"create_theme_modal_description": "Hydra ko'rinishini sozlash uchun yangi mavzu yaratish",
|
||||
"theme_name": "Nomi",
|
||||
"insert_theme_name": "Mavzu nomini kiriting",
|
||||
"set_theme": "Mavzuni o'rnatish",
|
||||
"unset_theme": "Mavzuni olib tashlash",
|
||||
"delete_theme": "Mavzuni o'chirish",
|
||||
"edit_theme": "Mavzuni tahrirlash",
|
||||
"delete_all_themes": "Barcha mavzularni o'chirish",
|
||||
"delete_all_themes_description": "Bu barcha maxsus mavzularingizni o'chiradi",
|
||||
"delete_theme_description": "Bu {{theme}} mavzusini o'chirishga olib keladi",
|
||||
"cancel": "Bekor qilish",
|
||||
"appearance": "Tashqi ko'rinish",
|
||||
"enable_torbox": "TorBox'ni yoqish",
|
||||
"torbox_description": "TorBox - bu bozordagi eng yaxshi serverlar bilan ham raqobatlashadigan premium xizmatingiz.",
|
||||
"torbox_account_linked": "TorBox hisobi bog'langan",
|
||||
"real_debrid_account_linked": "Real-Debrid hisobi bog'langan",
|
||||
"create_real_debrid_account": "Agar sizda hali Real-Debrid hisobi bo'lmasa, shu yerni bosing",
|
||||
"create_torbox_account": "Agar sizda hali TorBox hisobi bo'lmasa, shu yerni bosing",
|
||||
"name_min_length": "Mavzu nomi kamida 3 ta belgi bo'lishi kerak",
|
||||
"import_theme": "Mavzuni import qilish",
|
||||
"import_theme_description": "Siz {{theme}} mavzusini mavzular do'konidan import qilmoqdasiz",
|
||||
"error_importing_theme": "Mavzuni import qilishda xato",
|
||||
"theme_imported": "Mavzu muvaffaqiyatli import qilindi",
|
||||
"enable_friend_request_notifications": "Do'stlar so'rovi olinganda",
|
||||
"enable_auto_install": "Yangilanishlarni avtomatik yuklab olish",
|
||||
"common_redist": "Kutubxonalar",
|
||||
"common_redist_description": "Ba'zi o'yinlarni ishga tushirish uchun kutubxonalar talab qilinadi. Muammolarning oldini olish uchun ularni o'rnatish tavsiya etiladi.",
|
||||
"install_common_redist": "O'rnatish",
|
||||
"installing_common_redist": "O'rnatilmoqda…",
|
||||
"show_download_speed_in_megabytes": "Yuklab olish tezligini sekundiga megabaytlarda ko'rsatish"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Yuklab olish yakunlandi",
|
||||
"game_ready_to_install": "{{title}} o'rnatishga tayyor",
|
||||
"repack_list_updated": "Repaklar ro'yxati yangilandi",
|
||||
"repack_count_one": "{{count}} repak qo'shildi",
|
||||
"repack_count_other": "{{count}} repak qo'shildi",
|
||||
"new_update_available": "Yangi {{version}} versiyasi mavjud",
|
||||
"restart_to_install_update": "Yangilanishni o'rnatish uchun Hydra'ni qayta ishga tushiring",
|
||||
"notification_achievement_unlocked_title": "{{game}} uchun yutuq ochildi",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} va boshqa {{count}} ta yutuq ochildi",
|
||||
"new_friend_request_title": "Yangi do'stlik so'rovi",
|
||||
"new_friend_request_description": "Siz yangi do'stlik so'rovini oldingiz",
|
||||
"extraction_complete": "Arxivdan chiqarish yakunlandi",
|
||||
"game_extracted": "{{title}} muvaffaqiyatli arxivdan chiqarildi"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Hydra'ni ochish",
|
||||
"quit": "Chiqish"
|
||||
},
|
||||
"game_card": {
|
||||
"available_one": "Mavjud",
|
||||
"available_other": "Mavjud",
|
||||
"no_downloads": "Mavjud manbalar yo'q"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Dasturlar o'rnatilmagan",
|
||||
"description": "Wine yoki Lutris topilmadi",
|
||||
"instructions": "O'yin to'g'ri ishlashi uchun Linux distributivingizga ulardan birini o'rnatishning to'g'ri usulini bilib oling"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Yopish"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Parolni ko'rsatish"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} soat",
|
||||
"amount_minutes": "{{amount}} daqiqa",
|
||||
"last_time_played": "Oxirgi o'yin {{period}}",
|
||||
"activity": "So'nggi faollik",
|
||||
"library": "Kutubxona",
|
||||
"total_play_time": "Jami o'ynalgan vaqt",
|
||||
"no_recent_activity_title": "Hmm... Bu yerda hech narsa yo'q",
|
||||
"no_recent_activity_description": "Siz uzoq vaqtdan beri o'ynamagansiz. Buni o'zgartirish vaqti keldi!",
|
||||
"display_name": "Ko'rsatiladigan ism",
|
||||
"saving": "Saqlanmoqda",
|
||||
"save": "Saqlash",
|
||||
"edit_profile": "Profilni tahrirlash",
|
||||
"saved_successfully": "Muvaffaqiyatli saqlandi",
|
||||
"try_again": "Iltimos, qayta urinib ko'ring",
|
||||
"sign_out_modal_title": "Ishonchingiz komilmi?",
|
||||
"cancel": "Bekor qilish",
|
||||
"successfully_signed_out": "Tizimdan muvaffaqiyatli chiqdingiz",
|
||||
"sign_out": "Chiqish",
|
||||
"playing_for": "{{amount}} o'ynalgan",
|
||||
"sign_out_modal_text": "Sizning kutubxonangiz joriy hisob qaydnomangizga bog'langan. Tizimdan chiqsangiz, kutubxonangiz mavjud bo'lmaydi va progress saqlanmaydi. Chiqasizmi?",
|
||||
"add_friends": "Do'stlar qo'shish",
|
||||
"add": "Qo'shish",
|
||||
"friend_code": "Do'st kodi",
|
||||
"see_profile": "Profilni ko'rish",
|
||||
"sending": "Yuborilmoqda",
|
||||
"friend_request_sent": "Do'stlik so'rovi yuborildi",
|
||||
"friends": "Do'stlar",
|
||||
"friends_list": "Do'stlar ro'yxati",
|
||||
"user_not_found": "Foydalanuvchi topilmadi",
|
||||
"block_user": "Foydalanuvchini bloklash",
|
||||
"add_friend": "Do'st qo'shish",
|
||||
"request_sent": "So'rov yuborildi",
|
||||
"request_received": "So'rov qabul qilindi",
|
||||
"accept_request": "So'rovni qabul qilish",
|
||||
"ignore_request": "So'rovni e'tiborsiz qoldirish",
|
||||
"cancel_request": "So'rovni bekor qilish",
|
||||
"undo_friendship": "Do'stni o'chirish",
|
||||
"request_accepted": "So'rov qabul qilindi",
|
||||
"user_blocked_successfully": "Foydalanuvchi muvaffaqiyatli bloklandi",
|
||||
"user_block_modal_text": "{{displayName}} bloklanadi",
|
||||
"blocked_users": "Bloklangan foydalanuvchilar",
|
||||
"unblock": "Blokdan chiqarish",
|
||||
"no_friends_added": "Siz hali hech qanday do'st qo'shmagansiz",
|
||||
"pending": "Kutilmoqda",
|
||||
"no_pending_invites": "Sizda javob kutayotgan so'rovlar yo'q",
|
||||
"no_blocked_users": "Siz hech kimni bloklamagansiz",
|
||||
"friend_code_copied": "Do'st kodi nusxalandi",
|
||||
"displayname_max_length": "Ko'rsatiladigan ism 50 ta belgidan oshmasligi kerak",
|
||||
"displayname_min_length": "Ko'rsatiladigan ism kamida 3 ta belgidan iborat bo'lishi kerak",
|
||||
"image_process_failure": "Rasmni qayta ishlashda xatolik yuz berdi",
|
||||
"locked_profile": "Bu profil shaxsiy",
|
||||
"privacy_hint": "Uni kimlar ko'rishi mumkinligini belgilash uchun <0>Sozlamalar</0>ga o'ting",
|
||||
"profile_reported": "Profil haqida xabar berildi",
|
||||
"report": "Xabar berish",
|
||||
"report_description": "Qo'shimcha ma'lumot",
|
||||
"report_description_placeholder": "Qo'shimcha ma'lumot",
|
||||
"report_profile": "Bu profil haqida shikoyat qilish",
|
||||
"report_reason": "Nega bu profil haqida shikoyat qilyapsiz?",
|
||||
"report_reason_hate": "Nafrat qo'zg'atish",
|
||||
"report_reason_other": "Boshqa",
|
||||
"report_reason_sexual_content": "Jinsiy tarkib",
|
||||
"report_reason_spam": "Spam",
|
||||
"report_reason_violence": "Zo'ravonlik",
|
||||
"required_field": "Bu maydon to'ldirilishi shart",
|
||||
"undo_friendship_modal_text": "Bu {{displayName}} bilan do'stligingizni bekor qiladi",
|
||||
"your_friend_code": "Sizning do'st kodingiz:",
|
||||
"upload_banner": "Banner yuklash",
|
||||
"uploading_banner": "Banner yuklanmoqda...",
|
||||
"background_image_updated": "Fon rasmi yangilandi",
|
||||
"stats": "Statistika",
|
||||
"achievements": "Yutuqlar",
|
||||
"games": "O'yinlar",
|
||||
"top_percentile": "Yuqori {{percentile}}%",
|
||||
"ranking_updated_weekly": "Reyting har hafta yangilanadi",
|
||||
"playing": "{{game}}ni o'ynayapti",
|
||||
"achievements_unlocked": "Yutuqlar ochildi",
|
||||
"earned_points": "To'plangan ballar:",
|
||||
"show_achievements_on_profile": "Yutuqlaringizni profilingizda ko'rsating",
|
||||
"show_points_on_profile": "To'plangan ballarni profilingizda ko'rsating"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Yutuq ochildi",
|
||||
"user_achievements": "{{displayName}}ning yutuqlari",
|
||||
"your_achievements": "Sizning yutuqlaringiz",
|
||||
"unlocked_at": "Ochilgan sana: {{date}}",
|
||||
"subscription_needed": "Bu kontentni ko'rish uchun Hydra Cloud obunasi kerak",
|
||||
"new_achievements_unlocked": "{{gameCount}} o'yindan {{achievementCount}} ta yangi yutuq ochildi",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} yutuq",
|
||||
"achievements_unlocked_for_game": "{{gameTitle}} uchun {{achievementCount}} ta yangi yutuq ochildi",
|
||||
"hidden_achievement_tooltip": "Bu yashirin yutuq",
|
||||
"achievement_earn_points": "Bu yutuq bilan {{points}} ball to'plang",
|
||||
"earned_points": "To'plangan ballar:",
|
||||
"available_points": "Mavjud ballar:",
|
||||
"how_to_earn_achievements_points": "Yutuq ballarini qanday to'plash mumkin?"
|
||||
},
|
||||
"hydra_cloud": {
|
||||
"subscription_tour_title": "Hydra Cloud obunasi",
|
||||
"subscribe_now": "Hoziroq obuna bo'ling",
|
||||
"cloud_saving": "Bulutli saqlash",
|
||||
"cloud_achievements": "Yutuqlaringizni bulutda saqlang",
|
||||
"animated_profile_picture": "Animatsiyali profil rasmi",
|
||||
"premium_support": "Premium qo'llab-quvvatlash",
|
||||
"show_and_compare_achievements": "Yutuqlaringizni boshqa foydalanuvchilarning yutuqlari bilan solishtiring va ko'rsating",
|
||||
"animated_profile_banner": "Animatsiyali profil banneri",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"hydra_cloud_feature_found": "Siz hozirgina Hydra Cloud funksiyasini kashf etdingiz!",
|
||||
"learn_more": "Batafsil ma'lumot",
|
||||
"debrid_description": "Nimbus bilan 4 barobar tezroq yuklab oling"
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@
|
||||
"options": "选项",
|
||||
"executable_section_title": "可执行文件",
|
||||
"executable_section_description": "点击 \"Play\" 时将执行的文件的路径",
|
||||
"downloads_secion_title": "下载",
|
||||
"downloads_section_title": "下载",
|
||||
"downloads_section_description": "查看此游戏的更新或其他版本",
|
||||
"danger_zone_section_title": "危险操作",
|
||||
"danger_zone_section_description": "从您的库或Hydra下载的文件中删除此游戏",
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import { app } from "electron";
|
||||
import path from "node:path";
|
||||
import { SystemPath } from "./services/system-path";
|
||||
|
||||
export const LUDUSAVI_MANIFEST_URL = "https://cdn.losbroxas.org/manifest.yaml";
|
||||
|
||||
export const defaultDownloadsPath = app.getPath("downloads");
|
||||
export const defaultDownloadsPath = SystemPath.getPath("downloads");
|
||||
|
||||
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
|
||||
|
||||
export const windowsStartMenuPath = path.join(
|
||||
SystemPath.getPath("appData"),
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"Start Menu",
|
||||
"Programs"
|
||||
);
|
||||
|
||||
export const levelDatabasePath = path.join(
|
||||
app.getPath("userData"),
|
||||
SystemPath.getPath("userData"),
|
||||
`hydra-db${isStaging ? "-staging" : ""}`
|
||||
);
|
||||
|
||||
export const commonRedistPath = path.join(
|
||||
app.getPath("userData"),
|
||||
SystemPath.getPath("userData"),
|
||||
"CommonRedist"
|
||||
);
|
||||
|
||||
export const logsPath = path.join(app.getPath("userData"), "logs");
|
||||
export const logsPath = path.join(SystemPath.getPath("userData"), "logs");
|
||||
|
||||
export const seedsPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "seeds")
|
||||
@@ -27,7 +36,7 @@ export const achievementSoundPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "achievement.wav")
|
||||
: path.join(__dirname, "..", "..", "resources", "achievement.wav");
|
||||
|
||||
export const backupsPath = path.join(app.getPath("userData"), "Backups");
|
||||
export const backupsPath = path.join(SystemPath.getPath("userData"), "Backups");
|
||||
|
||||
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { TrendingGame } from "@types";
|
||||
const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf-8",
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.then((language) => language || "en");
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import * as tar from "tar";
|
||||
import { registerEvent } from "../register-event";
|
||||
import axios from "axios";
|
||||
import os from "node:os";
|
||||
import { app } from "electron";
|
||||
import path from "node:path";
|
||||
import { backupsPath } from "@main/constants";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
import YAML from "yaml";
|
||||
import { normalizePath } from "@main/helpers";
|
||||
import { SystemPath } from "@main/services/system-path";
|
||||
|
||||
export interface LudusaviBackup {
|
||||
files: {
|
||||
@@ -35,7 +35,7 @@ const replaceLudusaviBackupWithCurrentUser = (
|
||||
drives: Record<string, string>;
|
||||
};
|
||||
|
||||
const currentHomeDir = normalizePath(app.getPath("home"));
|
||||
const currentHomeDir = normalizePath(SystemPath.getPath("home"));
|
||||
|
||||
/* Renaming logic */
|
||||
if (os.platform() === "win32") {
|
||||
@@ -84,7 +84,7 @@ const downloadGameArtifact = async (
|
||||
homeDir: string;
|
||||
}>(`/profile/games/artifacts/${gameArtifactId}/download`);
|
||||
|
||||
const zipLocation = path.join(app.getPath("userData"), objectKey);
|
||||
const zipLocation = path.join(SystemPath.getPath("userData"), objectKey);
|
||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||
|
||||
if (fs.existsSync(backupPath)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
import type { Game, GameShop } from "@types";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
import { createGame } from "@main/services/library-sync";
|
||||
@@ -15,15 +15,14 @@ const addGameToLibrary = async (
|
||||
title: string
|
||||
) => {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
let game = await gamesSublevel.get(gameKey);
|
||||
|
||||
if (game) {
|
||||
await downloadsSublevel.del(gameKey);
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
isDeleted: false,
|
||||
});
|
||||
game.isDeleted = false;
|
||||
|
||||
await gamesSublevel.put(gameKey, game);
|
||||
} else {
|
||||
const steamGame = await steamGamesWorker.run(Number(objectId), {
|
||||
name: "getById",
|
||||
@@ -33,7 +32,7 @@ const addGameToLibrary = async (
|
||||
? steamUrlBuilder.icon(objectId, steamGame.clientIcon)
|
||||
: null;
|
||||
|
||||
const game: Game = {
|
||||
game = {
|
||||
title,
|
||||
iconUrl,
|
||||
objectId,
|
||||
@@ -44,12 +43,12 @@ const addGameToLibrary = async (
|
||||
lastTimePlayed: null,
|
||||
};
|
||||
|
||||
await gamesSublevel.put(levelKeys.game(shop, objectId), game);
|
||||
|
||||
await createGame(game).catch(() => {});
|
||||
|
||||
updateLocalUnlockedAchievements(game);
|
||||
await gamesSublevel.put(gameKey, game);
|
||||
}
|
||||
|
||||
await createGame(game).catch(() => {});
|
||||
|
||||
updateLocalUnlockedAchievements(game);
|
||||
};
|
||||
|
||||
registerEvent("addGameToLibrary", addGameToLibrary);
|
||||
|
||||
@@ -3,13 +3,16 @@ import createDesktopShortcut from "create-desktop-shortcuts";
|
||||
import path from "node:path";
|
||||
import { app } from "electron";
|
||||
import { removeSymbolsFromName } from "@shared";
|
||||
import { GameShop } from "@types";
|
||||
import { GameShop, ShortcutLocation } from "@types";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import { SystemPath } from "@main/services/system-path";
|
||||
import { windowsStartMenuPath } from "@main/constants";
|
||||
|
||||
const createGameShortcut = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string
|
||||
objectId: string,
|
||||
location: ShortcutLocation
|
||||
): Promise<boolean> => {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
@@ -24,7 +27,10 @@ const createGameShortcut = async (
|
||||
const options = {
|
||||
filePath,
|
||||
name: removeSymbolsFromName(game.title),
|
||||
outputPath: app.getPath("desktop"),
|
||||
outputPath:
|
||||
location === "desktop"
|
||||
? SystemPath.getPath("desktop")
|
||||
: windowsStartMenuPath,
|
||||
};
|
||||
|
||||
return createDesktopShortcut({
|
||||
|
||||
@@ -6,7 +6,7 @@ import { db, levelKeys } from "@main/level";
|
||||
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf-8",
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.then((language) => language || "en");
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi, WindowManager } from "@main/services";
|
||||
import { publishNewFriendRequestNotification } from "@main/services/notifications";
|
||||
@@ -10,14 +9,12 @@ interface SyncState {
|
||||
tick: number;
|
||||
}
|
||||
|
||||
const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes
|
||||
|
||||
const syncState: SyncState = {
|
||||
friendRequestCount: null,
|
||||
tick: 0,
|
||||
};
|
||||
|
||||
const syncFriendRequests = async () => {
|
||||
export const syncFriendRequests = async () => {
|
||||
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`)
|
||||
.then((res) => {
|
||||
if (
|
||||
@@ -44,16 +41,4 @@ const syncFriendRequests = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const syncFriendRequestsEvent = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
return syncFriendRequests();
|
||||
};
|
||||
|
||||
export const watchFriendRequests = async () => {
|
||||
if (syncState.tick % ticksToUpdate === 0) {
|
||||
await syncFriendRequests();
|
||||
}
|
||||
|
||||
syncState.tick++;
|
||||
};
|
||||
|
||||
registerEvent("syncFriendRequests", syncFriendRequestsEvent);
|
||||
registerEvent("syncFriendRequests", syncFriendRequests);
|
||||
|
||||
@@ -40,7 +40,7 @@ const startGameDownload = async (
|
||||
/* Delete any previous download */
|
||||
await downloadsSublevel.del(gameKey);
|
||||
|
||||
if (game?.isDeleted) {
|
||||
if (game) {
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
isDeleted: false,
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import AutoLaunch from "auto-launch";
|
||||
import { app } from "electron";
|
||||
import path from "path";
|
||||
import fs from "node:fs";
|
||||
import { logger } from "@main/services";
|
||||
|
||||
const windowsStartupPath = path.join(
|
||||
app.getPath("appData"),
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"Start Menu",
|
||||
"Programs",
|
||||
"Startup"
|
||||
);
|
||||
|
||||
const autoLaunch = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
autoLaunchProps: { enabled: boolean; minimized: boolean }
|
||||
@@ -30,10 +19,6 @@ const autoLaunch = async (
|
||||
logger.error(err);
|
||||
});
|
||||
} else {
|
||||
if (process.platform == "win32") {
|
||||
fs.rm(path.join(windowsStartupPath, "Hydra.vbs"), () => {});
|
||||
}
|
||||
|
||||
appLauncher.disable().catch((err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ const updateUserPreferences = async (
|
||||
|
||||
if (preferences.language) {
|
||||
await db.put<string, string>(levelKeys.language, preferences.language, {
|
||||
valueEncoding: "utf-8",
|
||||
valueEncoding: "utf8",
|
||||
});
|
||||
|
||||
i18next.changeLanguage(preferences.language);
|
||||
|
||||
242
src/main/generated/envelope.ts
Normal file
242
src/main/generated/envelope.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
// @generated by protobuf-ts 2.10.0
|
||||
// @generated from protobuf file "envelope.proto" (syntax proto3)
|
||||
// tslint:disable
|
||||
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
|
||||
import type { IBinaryWriter } from "@protobuf-ts/runtime";
|
||||
import { WireType } from "@protobuf-ts/runtime";
|
||||
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
|
||||
import type { IBinaryReader } from "@protobuf-ts/runtime";
|
||||
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
|
||||
import type { PartialMessage } from "@protobuf-ts/runtime";
|
||||
import { reflectionMergePartial } from "@protobuf-ts/runtime";
|
||||
import { MessageType } from "@protobuf-ts/runtime";
|
||||
/**
|
||||
* @generated from protobuf message FriendRequest
|
||||
*/
|
||||
export interface FriendRequest {
|
||||
/**
|
||||
* @generated from protobuf field: int32 friend_request_count = 1;
|
||||
*/
|
||||
friendRequestCount: number;
|
||||
/**
|
||||
* @generated from protobuf field: optional string sender_id = 2;
|
||||
*/
|
||||
senderId?: string;
|
||||
}
|
||||
/**
|
||||
* @generated from protobuf message UpdateGamePlaytime
|
||||
*/
|
||||
export interface UpdateGamePlaytime {
|
||||
/**
|
||||
* @generated from protobuf field: int64 playtime_delta_in_seconds = 1;
|
||||
*/
|
||||
playtimeDeltaInSeconds: bigint;
|
||||
/**
|
||||
* @generated from protobuf field: string last_time_played = 2;
|
||||
*/
|
||||
lastTimePlayed: string;
|
||||
/**
|
||||
* @generated from protobuf field: string game_id = 3;
|
||||
*/
|
||||
gameId: string;
|
||||
}
|
||||
/**
|
||||
* @generated from protobuf message Envelope
|
||||
*/
|
||||
export interface Envelope {
|
||||
/**
|
||||
* @generated from protobuf oneof: payload
|
||||
*/
|
||||
payload: {
|
||||
oneofKind: "friendRequest";
|
||||
/**
|
||||
* @generated from protobuf field: FriendRequest friend_request = 1;
|
||||
*/
|
||||
friendRequest: FriendRequest;
|
||||
} | {
|
||||
oneofKind: "updateGamePlaytime";
|
||||
/**
|
||||
* @generated from protobuf field: UpdateGamePlaytime update_game_playtime = 2;
|
||||
*/
|
||||
updateGamePlaytime: UpdateGamePlaytime;
|
||||
} | {
|
||||
oneofKind: undefined;
|
||||
};
|
||||
}
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class FriendRequest$Type extends MessageType<FriendRequest> {
|
||||
constructor() {
|
||||
super("FriendRequest", [
|
||||
{ no: 1, name: "friend_request_count", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 2, name: "sender_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<FriendRequest>): FriendRequest {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.friendRequestCount = 0;
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<FriendRequest>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FriendRequest): FriendRequest {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* int32 friend_request_count */ 1:
|
||||
message.friendRequestCount = reader.int32();
|
||||
break;
|
||||
case /* optional string sender_id */ 2:
|
||||
message.senderId = reader.string();
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: FriendRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* int32 friend_request_count = 1; */
|
||||
if (message.friendRequestCount !== 0)
|
||||
writer.tag(1, WireType.Varint).int32(message.friendRequestCount);
|
||||
/* optional string sender_id = 2; */
|
||||
if (message.senderId !== undefined)
|
||||
writer.tag(2, WireType.LengthDelimited).string(message.senderId);
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message FriendRequest
|
||||
*/
|
||||
export const FriendRequest = new FriendRequest$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class UpdateGamePlaytime$Type extends MessageType<UpdateGamePlaytime> {
|
||||
constructor() {
|
||||
super("UpdateGamePlaytime", [
|
||||
{ no: 1, name: "playtime_delta_in_seconds", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
|
||||
{ no: 2, name: "last_time_played", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 3, name: "game_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<UpdateGamePlaytime>): UpdateGamePlaytime {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.playtimeDeltaInSeconds = 0n;
|
||||
message.lastTimePlayed = "";
|
||||
message.gameId = "";
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<UpdateGamePlaytime>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UpdateGamePlaytime): UpdateGamePlaytime {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* int64 playtime_delta_in_seconds */ 1:
|
||||
message.playtimeDeltaInSeconds = reader.int64().toBigInt();
|
||||
break;
|
||||
case /* string last_time_played */ 2:
|
||||
message.lastTimePlayed = reader.string();
|
||||
break;
|
||||
case /* string game_id */ 3:
|
||||
message.gameId = reader.string();
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: UpdateGamePlaytime, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* int64 playtime_delta_in_seconds = 1; */
|
||||
if (message.playtimeDeltaInSeconds !== 0n)
|
||||
writer.tag(1, WireType.Varint).int64(message.playtimeDeltaInSeconds);
|
||||
/* string last_time_played = 2; */
|
||||
if (message.lastTimePlayed !== "")
|
||||
writer.tag(2, WireType.LengthDelimited).string(message.lastTimePlayed);
|
||||
/* string game_id = 3; */
|
||||
if (message.gameId !== "")
|
||||
writer.tag(3, WireType.LengthDelimited).string(message.gameId);
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message UpdateGamePlaytime
|
||||
*/
|
||||
export const UpdateGamePlaytime = new UpdateGamePlaytime$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class Envelope$Type extends MessageType<Envelope> {
|
||||
constructor() {
|
||||
super("Envelope", [
|
||||
{ no: 1, name: "friend_request", kind: "message", oneof: "payload", T: () => FriendRequest },
|
||||
{ no: 2, name: "update_game_playtime", kind: "message", oneof: "payload", T: () => UpdateGamePlaytime }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<Envelope>): Envelope {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.payload = { oneofKind: undefined };
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<Envelope>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Envelope): Envelope {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* FriendRequest friend_request */ 1:
|
||||
message.payload = {
|
||||
oneofKind: "friendRequest",
|
||||
friendRequest: FriendRequest.internalBinaryRead(reader, reader.uint32(), options, (message.payload as any).friendRequest)
|
||||
};
|
||||
break;
|
||||
case /* UpdateGamePlaytime update_game_playtime */ 2:
|
||||
message.payload = {
|
||||
oneofKind: "updateGamePlaytime",
|
||||
updateGamePlaytime: UpdateGamePlaytime.internalBinaryRead(reader, reader.uint32(), options, (message.payload as any).updateGamePlaytime)
|
||||
};
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: Envelope, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* FriendRequest friend_request = 1; */
|
||||
if (message.payload.oneofKind === "friendRequest")
|
||||
FriendRequest.internalBinaryWrite(message.payload.friendRequest, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
|
||||
/* UpdateGamePlaytime update_game_playtime = 2; */
|
||||
if (message.payload.oneofKind === "updateGamePlaytime")
|
||||
UpdateGamePlaytime.internalBinaryWrite(message.payload.updateGamePlaytime, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message Envelope
|
||||
*/
|
||||
export const Envelope = new Envelope$Type();
|
||||
@@ -59,9 +59,11 @@ app.whenReady().then(async () => {
|
||||
|
||||
await loadState();
|
||||
|
||||
const language = await db.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf-8",
|
||||
});
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.catch(() => "en");
|
||||
|
||||
if (language) i18n.changeLanguage(language);
|
||||
|
||||
|
||||
@@ -14,4 +14,5 @@ export const levelKeys = {
|
||||
userPreferences: "userPreferences",
|
||||
language: "language",
|
||||
screenState: "screenState",
|
||||
rpcPassword: "rpcPassword",
|
||||
};
|
||||
|
||||
@@ -10,8 +10,11 @@ import type { UserPreferences } from "@types";
|
||||
import { TorBoxClient } from "./services/download/torbox";
|
||||
import { CommonRedistManager } from "./services/common-redist-manager";
|
||||
import { WSManager } from "./services/ws-manager";
|
||||
import { SystemPath } from "./services/system-path";
|
||||
|
||||
export const loadState = async () => {
|
||||
SystemPath.checkIfPathsAreAvailable();
|
||||
|
||||
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||
levelKeys.userPreferences,
|
||||
{
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { app } from "electron";
|
||||
import type { Game, AchievementFile } from "@types";
|
||||
import { Cracker } from "@shared";
|
||||
import { achievementsLogger } from "../logger";
|
||||
import { SystemPath } from "../system-path";
|
||||
|
||||
const getAppDataPath = () => {
|
||||
if (process.platform === "win32") {
|
||||
return app.getPath("appData");
|
||||
return SystemPath.getPath("appData");
|
||||
}
|
||||
|
||||
const user = app.getPath("home").split("/").pop();
|
||||
const user = SystemPath.getPath("home").split("/").pop();
|
||||
|
||||
return path.join("drive_c", "users", user || "", "AppData", "Roaming");
|
||||
};
|
||||
|
||||
const getDocumentsPath = () => {
|
||||
if (process.platform === "win32") {
|
||||
return app.getPath("documents");
|
||||
return SystemPath.getPath("documents");
|
||||
}
|
||||
|
||||
const user = app.getPath("home").split("/").pop();
|
||||
const user = SystemPath.getPath("home").split("/").pop();
|
||||
|
||||
return path.join("drive_c", "users", user || "", "Documents");
|
||||
};
|
||||
@@ -38,7 +38,7 @@ const getLocalAppDataPath = () => {
|
||||
return path.join(appData, "..", "Local");
|
||||
}
|
||||
|
||||
const user = app.getPath("home").split("/").pop();
|
||||
const user = SystemPath.getPath("home").split("/").pop();
|
||||
|
||||
return path.join("drive_c", "users", user || "", "AppData", "Local");
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export const getGameAchievementData = async (
|
||||
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf-8",
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.then((language) => language || "en");
|
||||
|
||||
|
||||
@@ -208,6 +208,19 @@ const processSkidrow = (unlockedAchievements: any): UnlockedAchievement[] => {
|
||||
const processGoldberg = (unlockedAchievements: any): UnlockedAchievement[] => {
|
||||
const newUnlockedAchievements: UnlockedAchievement[] = [];
|
||||
|
||||
if (Array.isArray(unlockedAchievements)) {
|
||||
for (const achievement of unlockedAchievements) {
|
||||
if (achievement?.earned) {
|
||||
newUnlockedAchievements.push({
|
||||
name: achievement.name,
|
||||
unlockTime: achievement.earned_time * 1000,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newUnlockedAchievements;
|
||||
}
|
||||
|
||||
for (const achievement of Object.keys(unlockedAchievements)) {
|
||||
const unlockedAchievement = unlockedAchievements[achievement];
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { levelKeys, gamesSublevel, db } from "@main/level";
|
||||
import { app } from "electron";
|
||||
import path from "node:path";
|
||||
import * as tar from "tar";
|
||||
import crypto from "node:crypto";
|
||||
@@ -15,6 +14,7 @@ import axios from "axios";
|
||||
import { Ludusavi } from "./ludusavi";
|
||||
import { formatDate, SubscriptionRequiredError } from "@shared";
|
||||
import i18next, { t } from "i18next";
|
||||
import { SystemPath } from "./system-path";
|
||||
|
||||
export class CloudSync {
|
||||
public static getBackupLabel(automatic: boolean) {
|
||||
@@ -102,7 +102,7 @@ export class CloudSync {
|
||||
shop,
|
||||
objectId,
|
||||
hostname: os.hostname(),
|
||||
homeDir: normalizePath(app.getPath("home")),
|
||||
homeDir: normalizePath(SystemPath.getPath("home")),
|
||||
downloadOptionTitle,
|
||||
platform: os.platform(),
|
||||
label,
|
||||
|
||||
@@ -4,24 +4,21 @@ import fs from "node:fs";
|
||||
import cp from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { logger } from "./logger";
|
||||
import { app } from "electron";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { SystemPath } from "./system-path";
|
||||
|
||||
export class CommonRedistManager {
|
||||
private static readonly redistributables = [
|
||||
"dotNetFx40_Full_setup.exe",
|
||||
"dxwebsetup.exe",
|
||||
"directx_Jun2010_redist.exe",
|
||||
"oalinst.exe",
|
||||
"install.bat",
|
||||
"vcredist_2015-2019_x64.exe",
|
||||
"vcredist_2015-2019_x86.exe",
|
||||
"vcredist_x64.exe",
|
||||
"vcredist_x86.exe",
|
||||
"xnafx40_redist.msi",
|
||||
"VisualCppRedist_AIO_x86_x64.exe",
|
||||
];
|
||||
private static readonly installationTimeout = 1000 * 60 * 5; // 5 minutes
|
||||
private static readonly installationLog = path.join(
|
||||
app.getPath("temp"),
|
||||
SystemPath.getPath("temp"),
|
||||
"common_redist_install.log"
|
||||
);
|
||||
|
||||
@@ -47,6 +44,8 @@ export class CommonRedistManager {
|
||||
fs.readFile(this.installationLog, "utf-8", (err, data) => {
|
||||
if (err) return logger.error("Error reading log file:", err);
|
||||
|
||||
logger.log("Redist log file updated:", data);
|
||||
|
||||
const tail = data.split("\n").at(-2)?.trim();
|
||||
|
||||
if (tail?.includes(installationCompleteMessage)) {
|
||||
@@ -92,7 +91,7 @@ export class CommonRedistManager {
|
||||
for (const redist of this.redistributables) {
|
||||
const filePath = path.join(commonRedistPath, redist);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
if (fs.existsSync(filePath) && redist !== "install.bat") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getUserData } from "./user/get-user-data";
|
||||
import { db } from "@main/level";
|
||||
import { levelKeys } from "@main/level/sublevels";
|
||||
import type { Auth, User } from "@types";
|
||||
import { WSManager } from "./ws-manager";
|
||||
|
||||
interface HydraApiOptions {
|
||||
needsAuth?: boolean;
|
||||
@@ -101,6 +102,8 @@ export class HydraApi {
|
||||
WindowManager.mainWindow.webContents.send("on-signin");
|
||||
await clearGamesRemoteIds();
|
||||
uploadGamesBatch();
|
||||
WSManager.close();
|
||||
WSManager.connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { gamesSublevel, levelKeys } from "@main/level";
|
||||
export const createGame = async (game: Game) => {
|
||||
return HydraApi.post(`/profile/games`, {
|
||||
objectId: game.objectId,
|
||||
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
|
||||
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds ?? 0),
|
||||
shop: game.shop,
|
||||
lastTimePlayed: game.lastTimePlayed,
|
||||
}).then((response) => {
|
||||
|
||||
@@ -8,9 +8,13 @@ import YAML from "yaml";
|
||||
|
||||
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
|
||||
import { LUDUSAVI_MANIFEST_URL } from "@main/constants";
|
||||
import { SystemPath } from "./system-path";
|
||||
|
||||
export class Ludusavi {
|
||||
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
|
||||
private static ludusaviPath = path.join(
|
||||
SystemPath.getPath("appData"),
|
||||
"ludusavi"
|
||||
);
|
||||
private static ludusaviConfigPath = path.join(
|
||||
this.ludusaviPath,
|
||||
"config.yaml"
|
||||
|
||||
@@ -3,7 +3,6 @@ import { DownloadManager } from "./download";
|
||||
import { watchProcesses } from "./process-watcher";
|
||||
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
|
||||
import { UpdateManager } from "./update-manager";
|
||||
import { watchFriendRequests } from "@main/events/profile/sync-friend-requests";
|
||||
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||
|
||||
export const startMainLoop = async () => {
|
||||
@@ -11,7 +10,6 @@ export const startMainLoop = async () => {
|
||||
while (true) {
|
||||
await Promise.allSettled([
|
||||
watchProcesses(),
|
||||
watchFriendRequests(),
|
||||
DownloadManager.watchDownloads(),
|
||||
AchievementWatcherManager.watchAchievements(),
|
||||
DownloadManager.getSeedStatus(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Notification, app } from "electron";
|
||||
import { Notification } from "electron";
|
||||
import { t } from "i18next";
|
||||
import trayIcon from "@resources/tray-icon.png?asset";
|
||||
import fs from "node:fs";
|
||||
@@ -13,13 +13,14 @@ import { WindowManager } from "../window-manager";
|
||||
import type { Game, UserPreferences } from "@types";
|
||||
import { db, levelKeys } from "@main/level";
|
||||
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
||||
import { SystemPath } from "../system-path";
|
||||
|
||||
async function downloadImage(url: string | null) {
|
||||
if (!url) return undefined;
|
||||
if (!url.startsWith("http")) return undefined;
|
||||
|
||||
const fileName = url.split("/").pop()!;
|
||||
const outputPath = path.join(app.getPath("temp"), fileName);
|
||||
const outputPath = path.join(SystemPath.getPath("temp"), fileName);
|
||||
const writer = fs.createWriteStream(outputPath);
|
||||
|
||||
const response = await axios.get(url, {
|
||||
@@ -80,7 +81,9 @@ export const publishNotificationUpdateReadyToInstall = async (
|
||||
.show();
|
||||
};
|
||||
|
||||
export const publishNewFriendRequestNotification = async () => {
|
||||
export const publishNewFriendRequestNotification = async (
|
||||
senderProfileImageUrl?: string
|
||||
) => {
|
||||
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||
levelKeys.userPreferences,
|
||||
{
|
||||
@@ -97,7 +100,9 @@ export const publishNewFriendRequestNotification = async () => {
|
||||
body: t("new_friend_request_description", {
|
||||
ns: "notifications",
|
||||
}),
|
||||
icon: trayIcon,
|
||||
icon: senderProfileImageUrl
|
||||
? await downloadImage(senderProfileImageUrl)
|
||||
: trayIcon,
|
||||
}).show();
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import crypto from "node:crypto";
|
||||
|
||||
import { pythonRpcLogger } from "./logger";
|
||||
import { Readable } from "node:stream";
|
||||
import { app, dialog } from "electron";
|
||||
import { app, dialog, safeStorage } from "electron";
|
||||
import { db, levelKeys } from "@main/level";
|
||||
|
||||
interface GamePayload {
|
||||
game_id: string;
|
||||
@@ -30,17 +31,12 @@ const rustBinaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
|
||||
export class PythonRPC {
|
||||
public static readonly BITTORRENT_PORT = "5881";
|
||||
public static readonly RPC_PORT = "8084";
|
||||
private static readonly RPC_PASSWORD = crypto.randomBytes(32).toString("hex");
|
||||
|
||||
private static pythonProcess: cp.ChildProcess | null = null;
|
||||
|
||||
public static readonly rpc = axios.create({
|
||||
baseURL: `http://localhost:${this.RPC_PORT}`,
|
||||
headers: {
|
||||
"x-hydra-rpc-password": this.RPC_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
private static pythonProcess: cp.ChildProcess | null = null;
|
||||
|
||||
private static logStderr(readable: Readable | null) {
|
||||
if (!readable) return;
|
||||
|
||||
@@ -48,14 +44,37 @@ export class PythonRPC {
|
||||
readable.on("data", pythonRpcLogger.log);
|
||||
}
|
||||
|
||||
public static spawn(
|
||||
private static async getRPCPassword() {
|
||||
const existingPassword = await db.get(levelKeys.rpcPassword, {
|
||||
valueEncoding: "utf8",
|
||||
});
|
||||
|
||||
if (existingPassword)
|
||||
return safeStorage.decryptString(Buffer.from(existingPassword, "hex"));
|
||||
|
||||
const newPassword = crypto.randomBytes(32).toString("hex");
|
||||
|
||||
await db.put(
|
||||
levelKeys.rpcPassword,
|
||||
safeStorage.encryptString(newPassword).toString("hex"),
|
||||
{
|
||||
valueEncoding: "utf8",
|
||||
}
|
||||
);
|
||||
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
public static async spawn(
|
||||
initialDownload?: GamePayload,
|
||||
initialSeeding?: GamePayload[]
|
||||
) {
|
||||
const rpcPassword = await this.getRPCPassword();
|
||||
|
||||
const commonArgs = [
|
||||
this.BITTORRENT_PORT,
|
||||
this.RPC_PORT,
|
||||
this.RPC_PASSWORD,
|
||||
rpcPassword,
|
||||
initialDownload ? JSON.stringify(initialDownload) : "",
|
||||
initialSeeding ? JSON.stringify(initialSeeding) : "",
|
||||
app.isPackaged
|
||||
@@ -116,6 +135,8 @@ export class PythonRPC {
|
||||
|
||||
this.pythonProcess = childProcess;
|
||||
}
|
||||
|
||||
this.rpc.defaults.headers.common["x-hydra-rpc-password"] = rpcPassword;
|
||||
}
|
||||
|
||||
public static kill() {
|
||||
|
||||
45
src/main/services/system-path.ts
Normal file
45
src/main/services/system-path.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { app, dialog } from "electron";
|
||||
import { logger } from "./logger";
|
||||
|
||||
export class SystemPath {
|
||||
static readonly paths = {
|
||||
userData: "userData",
|
||||
downloads: "downloads",
|
||||
documents: "documents",
|
||||
desktop: "desktop",
|
||||
home: "home",
|
||||
appData: "appData",
|
||||
temp: "temp",
|
||||
};
|
||||
|
||||
static checkIfPathsAreAvailable() {
|
||||
const paths = Object.keys(SystemPath.paths) as Array<
|
||||
keyof typeof SystemPath.paths
|
||||
>;
|
||||
|
||||
paths.forEach((pathName) => {
|
||||
try {
|
||||
app.getPath(pathName);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting path ${pathName}`);
|
||||
if (error instanceof Error) {
|
||||
logger.error(error.message, error.stack);
|
||||
}
|
||||
|
||||
dialog.showErrorBox(
|
||||
`Hydra was not able to find path for '${pathName}' system folder`,
|
||||
`Some functionalities may not work as expected.\nPlease check your system settings.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static getPath(pathName: keyof typeof SystemPath.paths): string {
|
||||
try {
|
||||
return app.getPath(pathName);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting path: ${error}`);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import trayIcon from "@resources/tray-icon.png?asset";
|
||||
import { HydraApi } from "./hydra-api";
|
||||
import UserAgent from "user-agents";
|
||||
import { db, gamesSublevel, levelKeys } from "@main/level";
|
||||
import { slice, sortBy } from "lodash-es";
|
||||
import { orderBy, slice } from "lodash-es";
|
||||
import type { ScreenState, UserPreferences } from "@types";
|
||||
import { AuthPage } from "@shared";
|
||||
import { isStaging } from "@main/constants";
|
||||
@@ -370,14 +370,14 @@ export class WindowManager {
|
||||
!game.isDeleted && game.executablePath && game.lastTimePlayed
|
||||
);
|
||||
|
||||
const sortedGames = sortBy(filteredGames, "lastTimePlayed", "DESC");
|
||||
const sortedGames = orderBy(filteredGames, "lastTimePlayed", "desc");
|
||||
|
||||
return slice(sortedGames, 5);
|
||||
return slice(sortedGames, 0, 6);
|
||||
});
|
||||
|
||||
const recentlyPlayedGames: Array<MenuItemConstructorOptions | MenuItem> =
|
||||
games.map(({ title, executablePath }) => ({
|
||||
label: title.length > 15 ? `${title.slice(0, 15)}…` : title,
|
||||
label: title.length > 18 ? `${title.slice(0, 18)}…` : title,
|
||||
type: "normal",
|
||||
click: async () => {
|
||||
if (!executablePath) return;
|
||||
@@ -418,7 +418,10 @@ export class WindowManager {
|
||||
},
|
||||
]);
|
||||
|
||||
tray.setContextMenu(contextMenu);
|
||||
if (process.platform === "linux") {
|
||||
tray.setContextMenu(contextMenu);
|
||||
}
|
||||
|
||||
return contextMenu;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { WebSocket } from "ws";
|
||||
import { HydraApi } from "./hydra-api";
|
||||
import { Envelope } from "@main/generated/envelope";
|
||||
import { logger } from "./logger";
|
||||
import { WindowManager } from "./window-manager";
|
||||
|
||||
export class WSManager {
|
||||
private static ws: WebSocket;
|
||||
private static ws: WebSocket | null = null;
|
||||
private static reconnectInterval = 1000;
|
||||
private static maxReconnectInterval = 30000;
|
||||
private static reconnectAttempts = 0;
|
||||
private static reconnecting = false;
|
||||
|
||||
static async connect() {
|
||||
const { token } = await HydraApi.post<{ token: string }>("/auth/ws");
|
||||
|
||||
console.log("WS TOKEN", token);
|
||||
|
||||
this.ws = new WebSocket(import.meta.env.MAIN_VITE_WS_URL, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -16,15 +21,55 @@ export class WSManager {
|
||||
});
|
||||
|
||||
this.ws.on("open", () => {
|
||||
console.log("open");
|
||||
});
|
||||
|
||||
this.ws.on("error", (error) => {
|
||||
console.error(error);
|
||||
logger.info("WS connected");
|
||||
this.reconnectInterval = 1000;
|
||||
this.reconnecting = false;
|
||||
});
|
||||
|
||||
this.ws.on("message", (message) => {
|
||||
console.log(message);
|
||||
const envelope = Envelope.fromBinary(
|
||||
new Uint8Array(Buffer.from(message.toString()))
|
||||
);
|
||||
|
||||
if (envelope.payload.oneofKind === "friendRequest") {
|
||||
WindowManager.mainWindow?.webContents.send("on-sync-friend-requests", {
|
||||
friendRequestCount: envelope.payload.friendRequest.friendRequestCount,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on("close", () => {
|
||||
logger.warn("WS closed. Attempting reconnect...");
|
||||
this.tryReconnect();
|
||||
});
|
||||
|
||||
this.ws.on("error", (err) => {
|
||||
logger.error("WS error:", err);
|
||||
this.tryReconnect();
|
||||
});
|
||||
}
|
||||
|
||||
private static async tryReconnect() {
|
||||
if (this.reconnecting) return;
|
||||
|
||||
this.reconnecting = true;
|
||||
this.reconnectAttempts++;
|
||||
|
||||
const waitTime = Math.min(
|
||||
this.reconnectInterval * 2 ** this.reconnectAttempts,
|
||||
this.maxReconnectInterval
|
||||
);
|
||||
logger.info(`Reconnecting in ${waitTime / 1000}s...`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, waitTime);
|
||||
}
|
||||
|
||||
public static async close() {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
GameAchievement,
|
||||
Theme,
|
||||
FriendRequestSync,
|
||||
ShortcutLocation,
|
||||
} from "@types";
|
||||
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||
import type { AxiosProgressEvent } from "axios";
|
||||
@@ -122,8 +123,11 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
),
|
||||
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
|
||||
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
|
||||
createGameShortcut: (shop: GameShop, objectId: string) =>
|
||||
ipcRenderer.invoke("createGameShortcut", shop, objectId),
|
||||
createGameShortcut: (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
location: ShortcutLocation
|
||||
) => ipcRenderer.invoke("createGameShortcut", shop, objectId, location),
|
||||
updateExecutablePath: (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
setUserDetails,
|
||||
setProfileBackground,
|
||||
setGameRunning,
|
||||
setFriendRequestCount,
|
||||
} from "@renderer/features";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
||||
@@ -155,16 +154,6 @@ export function App() {
|
||||
});
|
||||
}, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
|
||||
dispatch(setFriendRequestCount(result.friendRequestCount));
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
|
||||
if (gamesRunning.length) {
|
||||
|
||||
@@ -85,5 +85,6 @@
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
line-height: 1.15;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import { sortBy } from "lodash-es";
|
||||
import cn from "classnames";
|
||||
import { CommentDiscussionIcon } from "@primer/octicons-react";
|
||||
import { SidebarGameItem } from "./sidebar-game-item";
|
||||
import { setFriendRequestCount } from "@renderer/features/user-details-slice";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
const SIDEBAR_MIN_WIDTH = 200;
|
||||
const SIDEBAR_INITIAL_WIDTH = 250;
|
||||
@@ -33,6 +35,8 @@ const initialSidebarWidth = window.localStorage.getItem("sidebarWidth");
|
||||
export function Sidebar() {
|
||||
const filterRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { t } = useTranslation("sidebar");
|
||||
const { library, updateLibrary } = useLibrary();
|
||||
const navigate = useNavigate();
|
||||
@@ -60,6 +64,16 @@ export function Sidebar() {
|
||||
updateLibrary();
|
||||
}, [lastPacket?.gameId, updateLibrary]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
|
||||
dispatch(setFriendRequestCount(result.friendRequestCount));
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const sidebarRef = useRef<HTMLElement>(null);
|
||||
|
||||
const cursorPos = useRef({ x: 0 });
|
||||
|
||||
7
src/renderer/src/declaration.d.ts
vendored
7
src/renderer/src/declaration.d.ts
vendored
@@ -32,6 +32,7 @@ import type {
|
||||
Theme,
|
||||
Badge,
|
||||
Auth,
|
||||
ShortcutLocation,
|
||||
} from "@types";
|
||||
import type { AxiosProgressEvent } from "axios";
|
||||
import type disk from "diskusage";
|
||||
@@ -101,7 +102,11 @@ declare global {
|
||||
objectId: string,
|
||||
title: string
|
||||
) => Promise<void>;
|
||||
createGameShortcut: (shop: GameShop, objectId: string) => Promise<boolean>;
|
||||
createGameShortcut: (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
location: ShortcutLocation
|
||||
) => Promise<boolean>;
|
||||
updateExecutablePath: (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const toastSlice = createSlice({
|
||||
state.title = action.payload.title;
|
||||
state.message = action.payload.message;
|
||||
state.type = action.payload.type;
|
||||
state.duration = action.payload.duration ?? 2000;
|
||||
state.duration = action.payload.duration ?? 3000;
|
||||
state.visible = true;
|
||||
},
|
||||
closeToast: (state) => {
|
||||
|
||||
@@ -34,13 +34,18 @@ export function DownloadSettingsModal({
|
||||
}: Readonly<DownloadSettingsModalProps>) {
|
||||
const { t } = useTranslation("game_details");
|
||||
|
||||
const userPreferences = useAppSelector(
|
||||
(state) => state.userPreferences.value
|
||||
);
|
||||
|
||||
const { showErrorToast } = useToast();
|
||||
|
||||
const [diskFreeSpace, setDiskFreeSpace] = useState<number | null>(null);
|
||||
const [selectedPath, setSelectedPath] = useState("");
|
||||
const [downloadStarting, setDownloadStarting] = useState(false);
|
||||
const [automaticExtractionEnabled, setAutomaticExtractionEnabled] =
|
||||
useState(true);
|
||||
const [automaticExtractionEnabled, setAutomaticExtractionEnabled] = useState(
|
||||
userPreferences?.extractFilesByDefault ?? true
|
||||
);
|
||||
const [selectedDownloader, setSelectedDownloader] =
|
||||
useState<Downloader | null>(null);
|
||||
const [hasWritePermission, setHasWritePermission] = useState<boolean | null>(
|
||||
@@ -49,10 +54,6 @@ export function DownloadSettingsModal({
|
||||
|
||||
const { isFeatureEnabled, Feature } = useFeature();
|
||||
|
||||
const userPreferences = useAppSelector(
|
||||
(state) => state.userPreferences.value
|
||||
);
|
||||
|
||||
const getDiskFreeSpace = async (path: string) => {
|
||||
const result = await window.electron.getDiskFreeSpace(path);
|
||||
setDiskFreeSpace(result.free);
|
||||
@@ -83,6 +84,8 @@ export function DownloadSettingsModal({
|
||||
|
||||
const getDefaultDownloader = useCallback(
|
||||
(availableDownloaders: Downloader[]) => {
|
||||
if (availableDownloaders.length === 0) return null;
|
||||
|
||||
if (availableDownloaders.includes(Downloader.Hydra)) {
|
||||
return Downloader.Hydra;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useContext, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button, CheckboxField, Modal, TextField } from "@renderer/components";
|
||||
import type { LibraryGame } from "@types";
|
||||
import type { LibraryGame, ShortcutLocation } from "@types";
|
||||
import { gameDetailsContext } from "@renderer/context";
|
||||
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
||||
import { useDownload, useToast, useUserDetails } from "@renderer/hooks";
|
||||
@@ -107,15 +107,18 @@ export function GameOptionsModal({
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateShortcut = async () => {
|
||||
const handleCreateShortcut = async (location: ShortcutLocation) => {
|
||||
window.electron
|
||||
.createGameShortcut(game.shop, game.objectId)
|
||||
.createGameShortcut(game.shop, game.objectId, location)
|
||||
.then((success) => {
|
||||
if (success) {
|
||||
showSuccessToast(t("create_shortcut_success"));
|
||||
} else {
|
||||
showErrorToast(t("create_shortcut_error"));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
showErrorToast(t("create_shortcut_error"));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -176,6 +179,9 @@ export function GameOptionsModal({
|
||||
const shouldShowWinePrefixConfiguration =
|
||||
window.electron.platform === "linux";
|
||||
|
||||
const shouldShowCreateStartMenuShortcut =
|
||||
window.electron.platform === "win32";
|
||||
|
||||
const handleResetAchievements = async () => {
|
||||
setIsDeletingAchievements(true);
|
||||
try {
|
||||
@@ -278,9 +284,20 @@ export function GameOptionsModal({
|
||||
>
|
||||
{t("open_folder")}
|
||||
</Button>
|
||||
<Button onClick={handleCreateShortcut} theme="outline">
|
||||
<Button
|
||||
onClick={() => handleCreateShortcut("desktop")}
|
||||
theme="outline"
|
||||
>
|
||||
{t("create_shortcut")}
|
||||
</Button>
|
||||
{shouldShowCreateStartMenuShortcut && (
|
||||
<Button
|
||||
onClick={() => handleCreateShortcut("start_menu")}
|
||||
theme="outline"
|
||||
>
|
||||
{t("create_start_menu_shortcut")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -362,7 +379,7 @@ export function GameOptionsModal({
|
||||
|
||||
<div className="game-options-modal__downloads">
|
||||
<div className="game-options-modal__header">
|
||||
<h2>{t("downloads_secion_title")}</h2>
|
||||
<h2>{t("downloads_section_title")}</h2>
|
||||
<h4 className="game-options-modal__header-description">
|
||||
{t("downloads_section_description")}
|
||||
</h4>
|
||||
|
||||
@@ -24,6 +24,7 @@ export function SettingsBehavior() {
|
||||
seedAfterDownloadComplete: false,
|
||||
showHiddenAchievementsDescription: false,
|
||||
showDownloadSpeedInMegabytes: false,
|
||||
extractFilesByDefault: true,
|
||||
});
|
||||
|
||||
const { t } = useTranslation("settings");
|
||||
@@ -43,6 +44,7 @@ export function SettingsBehavior() {
|
||||
userPreferences.showHiddenAchievementsDescription ?? false,
|
||||
showDownloadSpeedInMegabytes:
|
||||
userPreferences.showDownloadSpeedInMegabytes ?? false,
|
||||
extractFilesByDefault: userPreferences.extractFilesByDefault ?? true,
|
||||
});
|
||||
}
|
||||
}, [userPreferences]);
|
||||
@@ -152,6 +154,16 @@ export function SettingsBehavior() {
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("extract_files_by_default")}
|
||||
checked={form.extractFilesByDefault}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
extractFilesByDefault: !form.extractFilesByDefault,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export type GameShop = "steam" | "epic";
|
||||
|
||||
export type ShortcutLocation = "desktop" | "start_menu";
|
||||
|
||||
export interface UnlockedAchievement {
|
||||
name: string;
|
||||
unlockTime: number;
|
||||
|
||||
@@ -88,6 +88,7 @@ export interface UserPreferences {
|
||||
achievementNotificationsEnabled?: boolean;
|
||||
friendRequestNotificationsEnabled?: boolean;
|
||||
showDownloadSpeedInMegabytes?: boolean;
|
||||
extractFilesByDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface ScreenState {
|
||||
|
||||
Reference in New Issue
Block a user