diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json index 782d6b51..034d0cbd 100644 --- a/src/locales/ar/translation.json +++ b/src/locales/ar/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "تم تسجيل الدخول بنجاح" }, "home": { - "featured": "مميز", "surprise_me": "مفاجئني", "no_results": "لم يتم العثور على نتائج", "start_typing": "ابدأ بالكتابة للبحث...", diff --git a/src/locales/be/translation.json b/src/locales/be/translation.json index c9d49626..8d67e693 100644 --- a/src/locales/be/translation.json +++ b/src/locales/be/translation.json @@ -1,7 +1,6 @@ { "language_name": "беларуская мова", "home": { - "featured": "Рэкамэндаванае", "surprise_me": "Здзіві мяне", "no_results": "Няма вынікаў" }, @@ -17,7 +16,6 @@ "home": "Галоўная", "favorites": "Улюбленыя" }, - "header": { "search": "Пошук", "home": "Галоўная", @@ -31,10 +29,7 @@ "downloading_metadata": "Сцягванне мэтаданых {{title}}…", "downloading": "Сцягванне {{title}}… ({{percentage}} скончана) - Канчатак {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Наступная старонка", - "previous_page": "Папярэдняя старонка" - }, + "catalogue": {}, "game_details": { "open_download_options": "Адкрыць варыянты сцягвання", "download_options_zero": "Няма варыянтаў сцягвання", diff --git a/src/locales/bg/translation.json b/src/locales/bg/translation.json index 458b9e36..3e289700 100644 --- a/src/locales/bg/translation.json +++ b/src/locales/bg/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Успешно влизане" }, "home": { - "featured": "Препоръчани", "surprise_me": "Изненадай ме", "no_results": "Няма намерени резултати", "start_typing": "Започнете да пишете за търсене...", diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index aa69001f..96eb67e2 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Has entrat correctament" }, "home": { - "featured": "Destacats", "surprise_me": "Sorprèn-me", "no_results": "No s'ha trobat res" }, @@ -25,7 +24,6 @@ }, "header": { "search": "Cerca jocs", - "home": "Inici", "catalogue": "Catàleg", "downloads": "Baixades", @@ -41,10 +39,7 @@ "calculating_eta": "Descarregant {{title}}… ({{percentage}} completat) - Calculant el temps restant…", "checking_files": "Comprovant els fitxers de {{title}}… ({{percentage}} completat)" }, - "catalogue": { - "next_page": "Pàgina següent", - "previous_page": "Pàgina anterior" - }, + "catalogue": {}, "game_details": { "open_download_options": "Obre les opcions de baixada", "download_options_zero": "No hi ha opcions de baixada", diff --git a/src/locales/cs/translation.json b/src/locales/cs/translation.json index 9b501b54..6bcc8944 100644 --- a/src/locales/cs/translation.json +++ b/src/locales/cs/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Úspěšně přihlášen" }, "home": { - "featured": "Doporučené", "surprise_me": "Překvap mě", "no_results": "Výsledek nenalezen", "start_typing": "Začni psát pro vyhledávání...", diff --git a/src/locales/da/translation.json b/src/locales/da/translation.json index 618f085c..21a92f72 100644 --- a/src/locales/da/translation.json +++ b/src/locales/da/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Loggede ind successfuldt" }, "home": { - "featured": "Anbefalet", "surprise_me": "Overrask mig", "no_results": "Ingen resultater fundet", "start_typing": "Begynd at skrive for at søge...", @@ -29,7 +28,6 @@ }, "header": { "search": "Søg efter spil", - "home": "Hjem", "catalogue": "Katalog", "downloads": "Downloads", @@ -45,10 +43,7 @@ "calculating_eta": "Downloader {{title}}… ({{percentage}} færdig) - Udregner resterende tid…", "checking_files": "Checker {{title}} filer… ({{percentage}} færdig)" }, - "catalogue": { - "next_page": "Næste side", - "previous_page": "Forrige side" - }, + "catalogue": {}, "game_details": { "open_download_options": "Åben download muligheder", "download_options_zero": "Ingen download mulighed", diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index 5101f459..fb285ee0 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Erfolgreich angemeldet" }, "home": { - "featured": "Empfohlen", "surprise_me": "Überrasche mich", "no_results": "Keine Ergebnisse gefunden", "start_typing": "Tippe, um zu suchen...", @@ -59,9 +58,7 @@ "download_sources": "Download-Quellen", "result_count": "{{resultCount}} Ergebnisse", "filter_count": "{{filterCount}} verfügbar", - "clear_filters": "{{filterCount}} ausgewählte löschen", - "next_page": "Nächste Seite", - "previous_page": "Vorherige Seite" + "clear_filters": "{{filterCount}} ausgewählte löschen" }, "game_details": { "open_download_options": "Download-Optionen öffnen", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index ce8b4de1..93fd5b0a 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -506,6 +506,8 @@ "user_profile": { "amount_hours": "{{amount}} hours", "amount_minutes": "{{amount}} minutes", + "amount_hours_short": "{{amount}}h", + "amount_minutes_short": "{{amount}}m", "last_time_played": "Last played {{period}}", "activity": "Recent Activity", "library": "Library", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 7f54925a..6f0fc9f1 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -1,34 +1,76 @@ { "language_name": "Español", "app": { - "successfully_signed_in": "Sesión iniciada exitosamente" + "successfully_signed_in": "Iniciaste sesión exitosamente" }, "home": { - "featured": "Destacado", "surprise_me": "¡Sorpréndeme!", - "no_results": "Sin resultados encontrados", - "start_typing": "Empieza a escribir para buscar...", - "hot": "Popular Ahora", + "no_results": "No se encontraron resultados", + "start_typing": "Empezá a escribir para buscar...", + "hot": "Tendencias", "weekly": "📅 Mejores juegos de la semana", - "achievements": "🏆 Juegos para completar" + "achievements": "🏆 Juegos para platinar" }, "sidebar": { "catalogue": "Catálogo", "downloads": "Descargas", "settings": "Ajustes", - "my_library": "Mi biblioteca", + "my_library": "Mi Librería", "downloading_metadata": "{{title}} (Descargando metadatos…)", "paused": "{{title}} (Pausado)", "downloading": "{{title}} ({{percentage}} - Descargando…)", - "filter": "Buscar en la biblioteca", + "filter": "Filtrar Librería", "home": "Inicio", "queued": "{{title}} (En cola)", "game_has_no_executable": "El juego no tiene un ejecutable seleccionado", - "sign_in": "Iniciar sesión", + "sign_in": "Iniciar Sesión", "friends": "Amigos", - "need_help": "¿Necesitas ayuda?", + "need_help": "¿Necesitás ayuda?", "favorites": "Favoritos", - "playable_button_title": "Mostrar solo juegos que puedes jugar ahora" + "playable_button_title": "Solo mostrar juegos que podés jugar en este momento", + "add_custom_game_tooltip": "Añadir juego personalizado", + "show_playable_only_tooltip": "Mostrar Solo Jugable", + "custom_game_modal": "Añadir juego personalizado", + "custom_game_modal_description": "Añadí un juego personalizado a tu librería seleccionando el ejecutable", + "custom_game_modal_executable_path": "Ruta del Ejecutable", + "custom_game_modal_select_executable": "Seleccionar archivo ejecutable", + "custom_game_modal_title": "Título", + "custom_game_modal_enter_title": "Ingresá el título", + "custom_game_modal_browse": "Buscar", + "custom_game_modal_cancel": "Cancelar", + "custom_game_modal_add": "Añadir juego", + "custom_game_modal_adding": "Añadiendo juego...", + "custom_game_modal_success": "Juego personalizado añadido exitosamente", + "custom_game_modal_failed": "Error al añadir juego personalizado", + "custom_game_modal_executable": "Ejecutable", + "edit_game_modal": "Personalizar recursos", + "edit_game_modal_description": "Personaliza los recursos y detalles del juego", + "edit_game_modal_title": "Título", + "edit_game_modal_enter_title": "Ingresá el título", + "edit_game_modal_image": "Imagen", + "edit_game_modal_select_image": "Seleccionar imagen", + "edit_game_modal_browse": "Navegar", + "edit_game_modal_image_preview": "Vista previa de imagen", + "edit_game_modal_icon": "Ícono", + "edit_game_modal_select_icon": "Seleccionar ícono", + "edit_game_modal_icon_preview": "Vista previa de ícono", + "edit_game_modal_logo": "Logo", + "edit_game_modal_select_logo": "Seleccionar logo", + "edit_game_modal_logo_preview": "Vista previa del logo", + "edit_game_modal_hero": "Library Hero", + "edit_game_modal_select_hero": "Seleccionar una imagen de Library Hero", + "edit_game_modal_hero_preview": "Vista previa de library hero", + "edit_game_modal_cancel": "Cancelar", + "edit_game_modal_update": "Actualizar", + "edit_game_modal_updating": "Actualizando...", + "edit_game_modal_fill_required": "Por favor rellená todos los espacios requeridos", + "edit_game_modal_success": "Recursos actualizados exitosamente", + "edit_game_modal_failed": "Error al actualizar los recursos", + "edit_game_modal_image_filter": "Imagen", + "edit_game_modal_icon_resolution": "Resolución recomendada: 256x256px", + "edit_game_modal_logo_resolution": "Resolución recomendada: 640x360px", + "edit_game_modal_hero_resolution": "Resolución recomendada: 1920x620px", + "edit_game_modal_assets": "Recursos" }, "header": { "search": "Buscar juegos", @@ -37,348 +79,409 @@ "downloads": "Descargas", "search_results": "Resultados de búsqueda", "settings": "Ajustes", - "version_available_install": "Versión {{version}} disponible. Presiona acá para descargar y reinstalar.", - "version_available_download": "Versión {{version}} disponible. Presiona aquí para descargar." + "version_available_install": "Versión {{version}} disponible. Presiona acá para reiniciar e instalar.", + "version_available_download": "Versión {{version}} disponible. Presiona acá para descargar." }, "bottom_panel": { "no_downloads_in_progress": "Sin descargas en progreso", "downloading_metadata": "Descargando metadatos de {{title}}…", - "downloading": "Descargando {{title}}… ({{percentage}} completado) - Finalizando {{eta}} - {{speed}}", - "calculating_eta": "Descargando {{title}}… ({{percentage}} completado) - Calculando tiempo restante…", - "installation_complete": "Instalación completada", - "installation_complete_message": "Common redistributables instalados exitosamente", + "downloading": "Descargando {{title}}… ({{percentage}} completado) - Restante {{eta}} - {{speed}}", + "calculating_eta": "Descargando {{title}}… ({{percentage}} completado) - Comprobando tiempo restante…", + "checking_files": "Revisando archivos de {{title}}… ({{percentage}} completado)", "installing_common_redist": "{{log}}…", - "checking_files": "Verificando archivos de {{title}}… ({{percentage}} completado)" + "installation_complete": "Instalación completada", + "installation_complete_message": "Common redistributables instalados correctamente" }, "catalogue": { "search": "Filtrar…", "developers": "Desarrolladores", "genres": "Géneros", - "tags": "Marcadores", + "tags": "Etiquetas", "publishers": "Editores", - "download_sources": "Fuentes de descarga", + "download_sources": "Descargando fuentes", "result_count": "{{resultCount}} resultados", - "filter_count": "{{filterCount}} disponibles", + "filter_count": "{{filterCount}} disponible", "clear_filters": "Limpiar {{filterCount}} seleccionados" }, "game_details": { - "open_download_options": "Ver opciones de descargas", - "automatically_extract_downloaded_files": "Extraer automáticamente archivos descargados", - "download_error_not_cached_on_hydra": "Esta descarga no está disponible en Nimbus.", - "download_options_zero": "No hay opciones de descargas disponibles", + "open_download_options": "Abrir opciones de descargas", + "download_options_zero": "Sin opciones de descargas", "download_options_one": "{{count}} opción de descarga", "download_options_other": "{{count}} opciones de descargas", - "updated_at": "Actualizado el: {{updated_at}}", + "updated_at": "Actualizado el {{updated_at}}", "install": "Instalar", - "resume": "Continuar", - "pause": "Pausa", + "resume": "Resumir", + "pause": "Pausar", "cancel": "Cancelar", - "remove": "Eliminar", - "space_left_on_disk": "{{space}} restantes en el disco", - "eta": "Tiempo restante: {{eta}}", + "remove": "Remover", + "space_left_on_disk": "{{space}} restante en el disco", + "eta": "Conclusión {{eta}}", "calculating_eta": "Calculando tiempo restante…", "downloading_metadata": "Descargando metadatos…", - "filter": "Buscar repacks", + "filter": "Filtrar repacks", "requirements": "Requisitos del Sistema", "minimum": "Mínimos", "recommended": "Recomendados", - "paused": "Pausado", - "release_date": "Fecha de lanzamiento: {{date}}", - "publisher": "Publicado por: {{publisher}}", + "paused": "En Pausa", + "release_date": "Lanzado el {{date}}", + "publisher": "Públicado por {{publisher}}", "hours": "horas", "minutes": "minutos", "amount_hours": "{{amount}} horas", "amount_minutes": "{{amount}} minutos", - "accuracy": "{{accuracy}}% precisión", - "add_to_library": "Agregar a la biblioteca", - "remove_from_library": "Eliminar de la biblioteca", - "no_downloads": "No hay descargas disponibles", - "play_time": "Has jugado {{amount}}", - "last_time_played": "Jugado por última vez: {{period}}", - "not_played_yet": "Aún no has jugado a {{title}}", + "accuracy": "{{accuracy}}% completista", + "add_to_library": "Añadir a la librería", + "already_in_library": "Ya está en la librería", + "remove_from_library": "Eliminar de la librería", + "no_downloads": "Sin descargas disponibles", + "play_time": "Jugado por {{amount}}", + "last_time_played": "Última vez jugado {{period}}", + "not_played_yet": "No has jugado a {{title}} todavía", "next_suggestion": "Siguiente sugerencia", "play": "Jugar", "deleting": "Eliminando instalador…", "close": "Cerrar", "playing_now": "Jugando ahora", "change": "Cambiar", - "repacks_modal_description": "Selecciona el repack que quieres descargar", - "select_folder_hint": "Para cambiar la carpeta predeterminada, ve a <0>Ajustes", + "repacks_modal_description": "Elegí el repack que querés descargar", + "select_folder_hint": "Si querés cambiar la carpeta por defecto, andá a <0>Ajustes", "download_now": "Descargar ahora", "no_shop_details": "No se pudieron obtener detalles de la tienda.", "download_options": "Opciones de descarga", "download_path": "Ruta de descarga", "previous_screenshot": "Anterior captura", "next_screenshot": "Siguiente captura", - "screenshot": "Captura {{number}}", - "open_screenshot": "Abrir captura {{number}}", - "download_settings": "Ajustes de descarga", - "downloader": "Método de descarga", + "screenshot": "Captura número {{number}}", + "open_screenshot": "Abrir captura número {{number}}", + "download_settings": "Descargar ajustes", + "downloader": "Descargador", "select_executable": "Seleccionar", - "no_executable_selected": "No se seleccionó un ejecutable", + "no_executable_selected": "Sin ejecutable seleccionado", "open_folder": "Abrir carpeta", "open_download_location": "Ver archivos descargados", - "create_shortcut": "Crear acceso directo en el escritorio", - "remove_files": "Eliminar archivos", + "create_shortcut": "Crear atajo en el escritorio", + "clear": "Limpiar", + "remove_files": "Remover archivos", "remove_from_library_title": "¿Estás seguro?", - "remove_from_library_description": "Esto eliminará {{game}} de tu biblioteca", + "remove_from_library_description": "Esto va eliminará {{game}} de tu librería", "options": "Opciones", "executable_section_title": "Ejecutable", - "executable_section_description": "Ruta del archivo que se ejecutará cuando se presione \"Jugar\"", + "executable_section_description": "Ruta del archivo que se ejecutará cuando presiones \"Jugar\"", "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)", + "downloads_section_description": "Revisar actualizaciones u otras versiones del juego", + "danger_zone_section_title": "Zona de Peligro", + "danger_zone_section_description": "Remover este juego de tu librería o los archivos descargados por Hydra", "download_in_progress": "Descarga en progreso", "download_paused": "Descarga pausada", + "last_downloaded_option": "Última opción de descarga", "create_steam_shortcut": "Crear atajo de Steam", - "last_downloaded_option": "Última opción descargada", "create_shortcut_success": "Atajo creado con éxito", - "you_might_need_to_restart_steam": "Es posible que necesites reiniciar Steam para ver los cambios", - "create_shortcut_error": "Error al crear un atajo", - "nsfw_content_title": "Este juego contiene contenido inapropiado.", - "nsfw_content_description": "{{title}} puede ser no adecuado para todas las edades por su contenido. \n¿Deseas continuar de igual forma?", + "you_might_need_to_restart_steam": "Probablemente necesités reiniciar Steam para ver cambios", + "create_shortcut_error": "Error al crear atajo", + "nsfw_content_title": "Este juego tiene contenido inapropiado", + "nsfw_content_description": "{{title}} tiene contenido no apto para todas las edades. ¿Querés continuar igualmente?", "allow_nsfw_content": "Continuar", - "refuse_nsfw_content": "No, gracias", + "refuse_nsfw_content": "Regresar", "stats": "Estadísticas", "download_count": "Descargas", - "player_count": "Jugadores activos", - "download_error": "Esta opción de descarga no está disponible.", + "player_count": "Jugadores activos", + "download_error": "Esta opción de descarga no está disponible", "download": "Descargar", - "executable_path_in_use": "El ejecutable se encuentra en uso por \"{{game}}\"", + "executable_path_in_use": "El ejecutable ya se está usando por \"{{game}}\"", "warning": "Advertencia:", - "hydra_needs_to_remain_open": "Para esta descarga, Hydra necesita mantenerse abierta hasta que concluya. En caso de que Hydra se cierre antes de que concluya, podrías perder todo el progreso.", + "hydra_needs_to_remain_open": "para esta descarga, Hydra necesita estar abierta hasta que termine. Si se cierra antes de completar, perderás todo el progreso.", "achievements": "Logros", "achievements_count": "Logros {{unlockedCount}}/{{achievementsCount}}", "cloud_save": "Guardado en la nube", - "cloud_save_description": "Guarda tu progreso en la nube y continúa jugando en cualquier dispositivo", - "backups": "Copias de Seguridad", + "cloud_save_description": "Guardá tu progreso en la nube y jugá en cualquier dispositivo", + "backups": "Copia de seguridad", "install_backup": "Instalar", "delete_backup": "Eliminar", - "create_backup": "Nueva Copia de Seguridad", - "last_backup_date": "Última copia de seguridad el {{date}}", - "no_backup_preview": "No se encontraron datos de guardados para este juego", + "create_backup": "Nueva copia de seguridad", + "last_backup_date": "Última copia de seguridad {{date}}", + "no_backup_preview": "No se han encotrado puntos de guardado para este juego", "restoring_backup": "Restaurando copia de seguridad ({{progress}} completado)…", "uploading_backup": "Subiendo copia de seguridad…", - "no_backups": "No has creado ninguna copia de seguridad para este juego aún", + "no_backups": "No has creado ninguna copia de seguridad para este juego todavía", "backup_uploaded": "Copia de seguridad subida", "backup_deleted": "Copia de seguridad eliminada", "backup_restored": "Copia de seguridad restaurada", "see_all_achievements": "Ver todos los logros", - "sign_in_to_see_achievements": "Inicia sesión para ver los logros", + "sign_in_to_see_achievements": "Iniciá sesión para ver los logros", "mapping_method_automatic": "Automático", "mapping_method_manual": "Manual", - "mapping_method_label": "Método de mapeo", - "files_automatically_mapped": "Archivos mapeados automáticamente", + "mapping_method_label": "Método de mapeado", + "files_automatically_mapped": "Archivos automáticamente mapeados", "no_backups_created": "Sin copias de seguridad creadas para este juego", - "manage_files": "Gestionar archivos", - "loading_save_preview": "Buscando datos de guardados de juegos…", + "manage_files": "Administrar archivos", + "loading_save_preview": "Buscando por guardado de juegos…", "wine_prefix": "Prefijo de Wine", - "wine_prefix_description": "El prefijo de Wine usado para ejecutar este juego", + "wine_prefix_description": "El prefijo de Wine usado para este juego", + "launch_options": "Opciones para iniciar", + "launch_options_description": "Los usuarios avanzados pueden ingresar sus modificaciones para el inicio de sus juegos (característica experimental)", + "launch_options_placeholder": "Sin parámetro específicado", "no_download_option_info": "Sin información disponible", - "backup_deletion_failed": "La eliminación de la copia de seguridad falló", - "max_number_of_artifacts_reached": "Número máximo de copias de seguridad de este juego alcanzadas", - "achievements_not_sync": "Tus logros no están sincronizados", - "manage_files_description": "Gestiona los archivos que serán respaldados y restaurados", + "backup_deletion_failed": "Error al eliminar copia de seguridad", + "max_number_of_artifacts_reached": "Máximo de copias de seguridad alcanzadas para este juego", + "achievements_not_sync": "Revisá como sincronizar tus logros'", + "manage_files_description": "Elegí que archivos se guardarán y restaurarán de la copia de seguridad", "select_folder": "Seleccionar carpeta", "backup_from": "Copia de seguridad de {{date}}", "automatic_backup_from": "Copia de seguridad automática de {{date}}", - "enable_automatic_cloud_sync": "Habilitar sincronización automática en la nube", - "custom_backup_location_set": "Se configuró la carpeta de copia de seguridad", - "clear": "Limpiar", - "no_directory_selected": "No se seleccionó un directorio", - "launch_options": "Opciones de Inicio", - "launch_options_description": "Los usuarios avanzados pueden introducir sus propias modificaciones de opciones de inicio (característica experimental)", - "launch_options_placeholder": "Sin parámetro específicado", - "no_write_permission": "No se puede descargar en este directorio. Presiona aquí para aprender más.", + "enable_automatic_cloud_sync": "Habilitar sincronización con la nube", + "custom_backup_location_set": "Ubicación de copia de seguridad personalizada", + "no_directory_selected": "Sin directorio seleccionado", + "no_write_permission": "No se puede descargar en este directorio. Presioná acá para más información.", "reset_achievements": "Reiniciar logros", - "reset_achievements_description": "Esto reiniciará todos los logros de {{game}}", - "reset_achievements_title": "¿Estás seguro?", - "reset_achievements_success": "Logros reiniciados exitosamente", - "reset_achievements_error": "Se produjo un error al reiniciar los logros", - "download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de Gofile. Por favor espera a que se reinicie la cuota.", - "download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nueva descargas. Por favor, revisa los ajustes de tu cuenta e intenta de nuevo.", - "download_error_not_cached_on_real_debrid": "Esta descarga no está disponible en Real-Debrid y el estado de descarga del sondeo de Real-Debrid aún no está disponible.", - "download_error_not_cached_on_torbox": "Esta descarga no está disponible en TorBox y aún no se puede verificar el estado de la descarga.", + "reset_achievements_description": "Esto va a reiniciar todos los logros para {{game}}", + "reset_achievements_title": "¿Querés continuar?", + "reset_achievements_success": "Logros reiniciados éxitosamente", + "reset_achievements_error": "Error al reiniciar logros", + "download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de GoFile. Esperá a que se reinice.", + "download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nuevas descargas. Revisá los ajustes de tu cuenta y probá de nuevo.", + "download_error_not_cached_on_real_debrid": "Esta descarga no está disponible en Real-Debrid y no está disponible el estado de descarga de sondeo todavía.", + "update_playtime_title": "Actualizar tiempo de juego", + "update_playtime_description": "Actualizar manualmente el tiempo de juego para {{game}}", + "update_playtime": "Actualizar tiempo de juego", + "update_playtime_success": "Tiempo de juego actualizado éxitosamente", + "update_playtime_error": "Error al actualizar el tiempo de juego", + "update_game_playtime": "Actualizar tu tiempo de juego", + "manual_playtime_warning": "Tus horas de juego se marcarán como actualizadas manualmente, y esto no se puede deshacer.", + "manual_playtime_tooltip": "Este tiempo de juego se ha actualizad manualmente", + "download_error_not_cached_on_torbox": "Esta descarga no está disponible en TorBox y no está disponible el estado de descarga de sondeo todavía.", + "download_error_not_cached_on_hydra": "Esta descarga no está disponible en Nimbus.", + "game_removed_from_favorites": "Juego eliminado de favoritos", "game_added_to_favorites": "Juego añadido a favoritos", - "game_removed_from_favorites": "Juego removido de favoritos", - "invalid_wine_prefix_path": "Ruta de prefijo de Wine inválida", - "invalid_wine_prefix_path_description": "La ruta del prefijo Wine es inválida. Por favor, checa la ruta y vuelve a intentarlo.", - "missing_wine_prefix": "Se requiere el prefijo Wine para crear una copia de seguridad en Linux" + "game_removed_from_pinned": "Juego removido de fijados", + "game_added_to_pinned": "Juego añadido a fijados", + "automatically_extract_downloaded_files": "Extraer automáticamente archivos descargados", + "create_start_menu_shortcut": "Crear un atajo en el Menú de Inicio", + "invalid_wine_prefix_path": "Ruta inválida del prefijo de Wine", + "invalid_wine_prefix_path_description": "La ruta al prefijo de Wine es inválida. Por favor revisá la ruta y probá de nuevo.", + "missing_wine_prefix": "EL prefijo de Wine es requerido para hacer una copia en Linux", + "artifact_renamed": "Copia de seguridad renombrada éxitosamente", + "rename_artifact": "Renombrar copia de seguridad", + "rename_artifact_description": "Renombrar copia de seguridad con un nombre más descriptivo", + "artifact_name_label": "Nombre de la copia de seguridad", + "artifact_name_placeholder": "Introducí un nombre para la copia de seguridad", + "save_changes": "Guardar cambios", + "required_field": "Este campo es requerido", + "max_length_field": "Este campo debe tener menos de {{length}} carácteres", + "freeze_backup": "Fíjalo así no se re-escríbira por copias de seguridad automáticas", + "unfreeze_backup": "Dejar de fijar", + "backup_frozen": "Copia de seguridad fijada", + "backup_unfrozen": "Copia de seguridad desfijada", + "backup_freeze_failed": "Error al congelar tu copia de seguridad", + "backup_freeze_failed_description": "Tenés que tener mínimo un espacio para copias de seguridad automáticas", + "edit_game_modal_button": "Personalizar recursos de juego", + "game_details": "Detalles del juego", + "currency_symbol": "$", + "currency_country": "us", + "prices": "Precios", + "no_prices_found": "No se encontraron precios", + "view_all_prices": "Presioná acá para ver todos los precios", + "retail_price": "Precio recomendado", + "keyshop_price": "Precio de tiendas de terceros", + "historical_retail": "Precio de tiendas", + "historical_keyshop": "Precio de tiendas de terceros", + "language": "Idioma", + "caption": "Subtítulo", + "audio": "Audio" }, "activation": { "title": "Activar Hydra", - "installation_id": "ID de la Instalación:", - "enter_activation_code": "Introduce tu código de activación", - "message": "Si no sabes donde obtener el código, no deberías de tener esto.", + "installation_id": "ID de Instalación:", + "enter_activation_code": "Introducí tu código de activación", + "message": "Si no sabes donde preguntar por esto, entonces no tenés que tener esto.", "activate": "Activar", "loading": "Cargando…" }, "downloads": { "resume": "Resumir", - "pause": "Pausa", - "eta": "Finalizando en {{eta}}", - "paused": "En Pausa", + "pause": "Pausar", + "eta": "Tiempo de finalizción {{eta}}", + "paused": "Pausado", "verifying": "Verificando…", "completed": "Completado", "removed": "No descargado", "cancel": "Cancelar", - "filter": "Buscar juegos descargados", - "remove": "Eliminar", + "filter": "Filtrar juegos descargados", + "remove": "Remover", "downloading_metadata": "Descargando metadatos…", - "deleting": "Eliminando instalador…", - "delete": "Eliminar instalador", - "delete_modal_title": "¿Estás seguro?", - "delete_modal_description": "Esto eliminará todos los archivos de la instalación del repack del juego de tu computadora. (Si ya instalaste el juego, puedes eliminar esto, no afectará al juego)", + "deleting": "Eliminado instalador…", + "delete": "Remover instalador", + "delete_modal_title": "¿Querés continuar?", + "delete_modal_description": "Esto eliminará todos los archivos del instalador de tu computadora", "install": "Instalar", "download_in_progress": "En progreso", "queued_downloads": "Descargas en cola", "downloads_completed": "Completado", "queued": "En cola", - "no_downloads_title": "Esto está tan... vacío", - "no_downloads_description": "No has descargado nada con Hydra... aún, ¡pero nunca es tarde para comenzar!.", - "checking_files": "Verificando archivos…", - "seeding": "Seeding", - "stop_seeding": "Detener seeding", - "resume_seeding": "Continuar seeding", + "no_downloads_title": "Esto está... tan, ¿vacío?", + "no_downloads_description": "No has descargado nada con Hydra, pero nunca es tarde para comenzar.", + "checking_files": "Revisando archivos…", + "seeding": "Sembrando", + "stop_seeding": "Dejar de sembrar", + "resume_seeding": "Continuar sembrando", + "options": "Administrar", "extract": "Extraer archivos", - "extracting": "Extrayendo archivos…", - "options": "Gestionar" + "extracting": "Extrayendo archivos…" }, "settings": { "downloads_path": "Ruta de descarga", - "common_redist": "Common redistributables", - "common_redist_description": "Las Common redistributables son requeridos para ejecutar algunos juegos. Es recomendado instalarlos para evitar problemas.", - "create_real_debrid_account": "Presiona acá si no tienes una cuenta de Real-Debrid aún", - "create_torbox_account": "Presiona acá si no tienes una cuenta de TorBox aún", - "install_common_redist": "Instalar", - "installing_common_redist": "Instalando…", - "show_download_speed_in_megabytes": "Mostrar velocidad de descargar en megabytes por segundo", - "change": "Cambiar", + "change": "Actualizar", "notifications": "Notificaciones", - "enable_download_notifications": "Cuando se completa una descarga", - "enable_repack_list_notifications": "Cuando se añade un repack nuevo", - "real_debrid_api_token_label": "Token API de Real-Debrid", - "quit_app_instead_hiding": "Salir de Hydra en vez de minimizar en la bandeja del sistema", - "launch_with_system": "Iniciar Hydra al inicio del sistema", + "enable_download_notifications": "Cuando una descarga se completa", + "enable_repack_list_notifications": "Cuando un nuevo repack se añade", + "real_debrid_api_token_label": "Real-Debrid API token", + "quit_app_instead_hiding": "No ocultar Hydra cuando se cierra", + "launch_with_system": "Iniciar Hydra con el sistema", "general": "General", - "behavior": "Otros", - "download_sources": "Fuentes de descarga", + "behavior": "Comportamiento", + "download_sources": "Fuentes de descargas", "language": "Idioma", - "api_token": "Token API", - "enable_real_debrid": "Activar Real-Debrid", - "real_debrid_description": "Real-Debrid es una forma de descargar sin restricciones archivos instantáneamente con la máxima velocidad de tu internet.", - "debrid_invalid_token": "Token de API inválido", - "debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí", - "real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratuita. Por favor, suscríbete a Real-Debrid", + "api_token": "API Token", + "enable_real_debrid": "Habilitar Real-Debrid", + "real_debrid_description": "Real-Debrid es un descargador que te permite descargar archivos más rápidos, solo límitado por la velocidad de tu internet.", + "debrid_invalid_token": "Token API inválido", + "debrid_api_token_hint": "Podés obtener la el token de tu API <0>acá", + "real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratis. Por favor suscribíte a Real-Debrid", "debrid_linked_message": "Cuenta \"{{username}}\" vinculada", "save_changes": "Guardar cambios", - "changes_saved": "Ajustes guardados exitosamente", - "download_sources_description": "Hydra buscará los enlaces de descarga de estas fuentes. La URL de origen debe ser un enlace directo a un archivo .json que contenga los enlaces de descarga", + "changes_saved": "Cambios guardados éxitosamente", + "download_sources_description": "Hydra va a recoger los links de descarga de cada fuente. La URL de origen debe ser un enlace .json que contenga los enlaces de descarga.", "validate_download_source": "Validar", - "remove_download_source": "Eliminar", - "add_download_source": "Añadir fuente de descarga", - "download_count_zero": "No hay descargas en la lista", - "download_count_one": "{{countFormatted}} descarga en la lista", - "download_count_other": "{{countFormatted}} descargas en la lista", - "download_source_url": "Descargar URL de origen", - "add_download_source_description": "Introduce la URL con el archivo .json", - "download_source_up_to_date": "Al día", + "remove_download_source": "Remover", + "add_download_source": "Añadir fuente", + "download_count_zero": "Sin opciones de descarga", + "download_count_one": "{{countFormatted}} opción de descarga", + "download_count_other": "{{countFormatted}} opciones de descarga", + "download_source_url": "Descargar fuente URL", + "add_download_source_description": "Introducí la URL del archivo .json", + "download_source_up_to_date": "Actualizado", "download_source_errored": "Error", "sync_download_sources": "Sincronizar fuentes", "removed_download_source": "Fuente de descarga eliminada", + "removed_download_sources": "Fuente de descarga eliminadas", "cancel_button_confirmation_delete_all_sources": "No", - "confirm_button_confirmation_delete_all_sources": "Sí, eliminar todo", - "description_confirmation_delete_all_sources": "Eliminarás todas las fuentes de descarga", + "confirm_button_confirmation_delete_all_sources": "Si, eliminar todo", "title_confirmation_delete_all_sources": "Eliminar todas las fuentes de descarga", - "removed_download_sources": "Fuentes de descarga eliminadas", - "button_delete_all_sources": "Eliminar todas las fuentes de descarga", - "added_download_source": "Fuente de descarga añadida", - "download_sources_synced": "Todas las fuentes de descargas están actualizadas.", - "insert_valid_json_url": "Introduce una URL JSON válida", - "found_download_option_zero": "No se encontró una opción de descarga", - "found_download_option_one": "Se encontró {{countFormatted}} opción de descarga", - "found_download_option_other": "Se encontraron {{countFormatted}} opciones de descarga", + "description_confirmation_delete_all_sources": "Vas a eliminar todas las fuentes de descargas", + "button_delete_all_sources": "Eliminar todo", + "added_download_source": "Añadir fuente de descarga", + "download_sources_synced": "Todas las fuentes de descarga están sincronizadas", + "insert_valid_json_url": "Introducí una URL de json válida", + "found_download_option_zero": "Sin opciones de descargas encontrada", + "found_download_option_one": "Encontrada {{countFormatted}} fuente de descarga", + "found_download_option_other": "Encontradas {{countFormatted}} opciones de descargas", "import": "Importar", "public": "Público", "private": "Privado", - "friends_only": "Solo amigos", + "friends_only": "Sólo amigos", "privacy": "Privacidad", "profile_visibility": "Visibilidad del perfil", - "profile_visibility_description": "Elige quién puede ver tu perfil y biblioteca", - "required_field": "Este campo es obligatorio", - "source_already_exists": "Esta fuente ya ha sido agregada.", - "must_be_valid_url": "La fuente debe ser una URL válida.", + "profile_visibility_description": "Elegí quién puede ver tú perfil y biblioteca", + "required_field": "Este campo es requerido", + "source_already_exists": "Esta fuente ya está añadida", + "must_be_valid_url": "La fuente debe ser una URL válida", "blocked_users": "Usuarios bloqueados", - "user_unblocked": "El usuario ha sido desbloqueado", - "enable_achievement_notifications": "Cuando un logro se desbloquea", + "user_unblocked": "Has desbloqueado a este usuario", + "enable_achievement_notifications": "Cuando desbloqueás un logro", "launch_minimized": "Iniciar Hydra minimizado", - "disable_nsfw_alert": "Desactivar alerta NSFW", - "seed_after_download_complete": "Realizar seeding después de que se completa la descarga", - "show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos", + "disable_nsfw_alert": "Deshabilitar alerta de NSFW", + "seed_after_download_complete": "Sembrar después de completar una descarga", + "show_hidden_achievement_description": "Mostrar logros ocultos antes de desbloquearlos", "account": "Cuenta", - "account_data_updated_successfully": "Datos de la cuenta actualizados", - "bill_sent_until": "Tú próxima factura se enviará el {{date}}", - "current_email": "Correo actual:", - "manage_subscription": "Gestionar suscripción", - "no_email_account": "No has configurado un correo aún", - "no_subscription": "Disfruta Hydra de la mejor manera", - "no_users_blocked": "No tienes usuarios bloqueados", - "renew_subscription": "Renovar Hydra Cloud", - "subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}", - "subscription_expired_at": "Tú suscripción expiró el {{date}}", - "subscription_renew_cancelled": "Está desactivada la renovación automática", - "subscription_renews_on": "Tú suscripción se renueva el {{date}}", + "no_users_blocked": "No has bloqueado a ningún usuario", + "subscription_active_until": "Tu Hydra Cloud está activo hasta {{date}}", + "manage_subscription": "Administrar suscripción", "update_email": "Actualizar correo", - "update_password": "Actualizar contraseña", - "appearance": "Apariencia", - "become_subscriber": "Sé Hydra Cloud", - "cancel": "Cancelar", - "clear_themes": "Limpiar", - "create_theme": "Crear", - "create_theme_modal_description": "Crea un nuevo tema para personalizar la apariencia de Hydra", - "create_theme_modal_title": "Crear tema personalizado", - "delete_all_themes": "Eliminar todos los temas", - "delete_all_themes_description": "Esto eliminará todos tus temas personalizados", - "delete_theme": "Eliminar tema", - "delete_theme_description": "Esto eliminará el tema {{theme}}", - "edit_theme": "Editar tema", + "update_password": "Cambiar contraseña", + "current_email": "Correo actual:", + "no_email_account": "No tenés ningún correo vinculado aún", + "account_data_updated_successfully": "Datos de la cuenta actualizados correctamente", + "renew_subscription": "Renovar Hydra Cloud", + "subscription_expired_at": "Tu suscripción expiró el {{date}}", + "no_subscription": "Disfrutá Hydra de la mejor forma", + "become_subscriber": "Sé parte de Hydra Cloud", + "subscription_renew_cancelled": "Renovación automática desactivada", + "subscription_renews_on": "Tu suscripción se renueva el {{date}}", + "bill_sent_until": "Tu próxima factura se enviará este día", + "no_themes": "Parece que no tenés ningún tema aún, pero no te preocupés, presiona acá para hacer tu primera obra maestra.", "editor_tab_code": "Código", "editor_tab_info": "Info", "editor_tab_save": "Guardar", - "enable_torbox": "Habilitar TorBox", - "error_importing_theme": "Error al importar el tema", + "web_store": "Tienda Web", + "clear_themes": "Limpiar", + "create_theme": "Crear", + "create_theme_modal_title": "Crear tema personalizado", + "create_theme_modal_description": "Crear un nuevo tema para personalizar el estilo de Hydra", + "theme_name": "Nombre", + "insert_theme_name": "Introducí un nombre del tema", + "set_theme": "Usar tema", + "unset_theme": "Dejar de usar tema", + "delete_theme": "Eliminar tema", + "edit_theme": "Editar tema", + "delete_all_themes": "Eliminar todos los temas", + "delete_all_themes_description": "Esto va a eliminar todos los temas personalizados", + "delete_theme_description": "Esto va a eliminar el tema {{theme}}", + "cancel": "Cancelar", + "appearance": "Apariencia", + "enable_torbox": "Activar TorBox", + "torbox_description": "TorBox es un servicio premium de seedbox que incluso rivaliza los mejores servidores.", + "torbox_account_linked": "Cuenta de TorBox vinculada", + "create_real_debrid_account": "Presioná acá si todavía no tenés una cuenta de Real-Debrid", + "create_torbox_account": "Presioná acá si todavía no tenés una cuenta de TorBox", + "real_debrid_account_linked": "Cuenta de Real-Debrid vinculada", + "name_min_length": "El nombre del tema debe tener mínimo 3 carácteres", "import_theme": "Importar tema", "import_theme_description": "Vas a importar el tema {{theme}} desde la tienda de temas", - "insert_theme_name": "Introducí el nombre del tema", - "name_min_length": "El tema tiene que tener 3 carácteres de largo mínimo", - "no_themes": "Parece que no tenés ningún tema aún, pero no te preocupes, presiona acá para crear tu primer tema.", - "real_debrid_account_linked": "Cuenta de Real-Debrid vinculada", - "set_theme": "Establecer tema", - "theme_imported": "Tema importado exitosamente", - "theme_name": "Nombre", - "torbox_account_linked": "Cuenta de TorBox vinculada", - "torbox_description": "TorBox es tu servicio premium de seedbox que rivaliza incluso a los mejores servidores del mercado.", - "unset_theme": "Desactivar tema", - "web_store": "Tienda Web", - "enable_friend_request_notifications": "Cuando se recibe una solicitud de amistad", - "enable_auto_install": "Descargar actualizaciones automáticamente" + "error_importing_theme": "Error al importar el tema", + "theme_imported": "Tema importado correctamente", + "enable_friend_request_notifications": "Cuando recibís una solicitud de amistad", + "enable_auto_install": "Descargar actualizaciones automáticamente", + "common_redist": "Common redistributables", + "common_redist_description": "Common redistributables son requeridos para algunos juegos. Es recomendable instalarlos para evitar algunos problemas.", + "install_common_redist": "Instalar", + "installing_common_redist": "Instalando…", + "show_download_speed_in_megabytes": "Mostrar velocidad de descarga en megabytes por segundo", + "extract_files_by_default": "Extraer archivos por defecto después de descargar", + "enable_steam_achievements": "Habilitar búsqueda de logros de Steam", + "achievement_custom_notification_position": "Posición de notificación de logros", + "top-left": "Superior Izquierda", + "top-center": "Superior Centro", + "top-right": "Superior Derecha", + "bottom-left": "Inferior Izquierda", + "bottom-center": "Inferior Centro", + "bottom-right": "Inferior Derecha", + "enable_achievement_custom_notifications": "Habilitar notificación personalizada de logros", + "alignment": "Centrado", + "variation": "Variación", + "default": "Defecto", + "rare": "Raro", + "platinum": "Platino", + "hidden": "Oculto", + "test_notification": "Probar notificación", + "notification_preview": "Probar notificación de logro", + "enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego" }, "notifications": { "download_complete": "Descarga completada", + "game_ready_to_install": "{{title}} está listo para instalar", + "repack_list_updated": "Lista de repacks actualizadas", + "repack_count_one": "{{count}} repack añadido", + "repack_count_other": "{{count}} repacks añadidos", + "new_update_available": "Versión {{version}} disponible", + "restart_to_install_update": "Reiniciá Hydra para instalar la actualización", + "notification_achievement_unlocked_title": "Logro desbloqueado para {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados", + "new_friend_request_description": "{{displayName}} te envió una solicitud de amistad", + "new_friend_request_title": "Nueva solicitud de amistad", "extraction_complete": "Extracción completada", "game_extracted": "{{title}} extraído exitosamente", - "game_ready_to_install": "{{title}} está listo para instalarse", - "repack_list_updated": "Lista de repacks actualizadas", - "repack_count_one": "{{count}} repack ha sido añadido", - "repack_count_other": "{{count}} repacks añadidos", - "new_update_available": "Version {{version}} disponible", - "restart_to_install_update": "Reinicia Hydra para instalar la actualización", - "notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}", - "notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados", - "new_friend_request_title": "Nueva solicitud de amistad", - "new_friend_request_description": "{{displayName}} te envió una solicitud de amistad", - "friend_started_playing_game": "{{displayName}} está jugando" + "friend_started_playing_game": "{{displayName}} empezó a jugar un juego", + "test_achievement_notification_title": "Esto es una notificación de prueba", + "test_achievement_notification_description": "Piola, ¿verdad?" }, "system_tray": { "open": "Abrir Hydra", @@ -387,15 +490,15 @@ "game_card": { "available_one": "Disponible", "available_other": "Disponibles", - "no_downloads": "No hay descargas disponibles" + "no_downloads": "Sin descargas disponibles" }, "binary_not_found_modal": { "title": "Programas no instalados", - "description": "Los ejecutables de Wine o Lutris no se encontraron en tu sistema", - "instructions": "Comprueba como instalar de forma correcta uno de los dos en tu distro de Linux para ejecutar el juego con normalidad" + "description": "Ejecutables de Wine o Lutris executables no encontrados en tu sistema", + "instructions": "Comprobá la forma correcta de instalar cualquiera de ellos en tu distribución de Linux para que el juego pueda ejecutarse con normalidad" }, "modal": { - "close": "Botón de cierre" + "close": "Botón de cerrar" }, "forms": { "toggle_password_visibility": "Cambiar visibilidad de contraseña" @@ -403,111 +506,120 @@ "user_profile": { "amount_hours": "{{amount}} horas", "amount_minutes": "{{amount}} minutos", - "last_time_played": "Última vez jugado: {{period}}", + "last_time_played": "Jugado por última vez el {{period}}", "activity": "Actividad reciente", - "library": "Biblioteca", - "total_play_time": "Has jugado", - "no_recent_activity_title": "Que raro, no hay nada por acá...", - "no_recent_activity_description": "No has jugado ningún juego recientemente, ¡vamos a cambiar eso ahora!", - "display_name": "Nombre en pantalla", + "library": "Librería", + "pinned": "Fijado", + "total_play_time": "Total de tiempo de juego", + "achievements_earned": "Logros conseguidos", + "played_recently": "Jugado recientemente", + "playtime": "Tiempo de juego", + "manual_playtime_tooltip": "Este tiempo de juego ha sido modificado manualmente", + "no_recent_activity_title": "Hmmm… nada por acá", + "no_recent_activity_description": "No has jugado nada recientemente. ¡Te toca cambiar eso!", + "display_name": "Nombre a mostar", "saving": "Guardando", - "save": "Guardar", + "save": "Guardado", "edit_profile": "Editar perfil", "saved_successfully": "Guardado exitosamente", - "try_again": "Por favor, intenta de nuevo", - "sign_out_modal_title": "¿Estás seguro?", + "try_again": "Por favor, intentá de nuevo", + "sign_out_modal_title": "¿Querés continuar?", "cancel": "Cancelar", - "successfully_signed_out": "Sesión cerrada exitosamente", + "successfully_signed_out": "Cerraste sesión exitosamente", "sign_out": "Cerrar sesión", - "playing_for": "Llevas jugando {{amount}}", - "sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?", - "add_friends": "Añadir amigos", + "playing_for": "Jugando por {{amount}}", + "sign_out_modal_text": "Tu librería está vinculada con esta cuenta. Cuando cerrés sesión, tu librería ya no será visible, y cualquier progreso no se guardará. ¿Querés continuar con el cierre de sesión?", + "add_friends": "Añadir amistades", "add": "Añadir", - "friend_code": "Código de amigo", + "friend_code": "Código de amistad", "see_profile": "Ver perfil", "sending": "Enviando", "friend_request_sent": "Solicitud de amistad enviada", - "friends": "Amigos", - "friends_list": "Lista de amigos", + "friends": "Amistades", + "friends_list": "Lista de amistades", "user_not_found": "Usuario no encontrado", "block_user": "Bloquear usuario", - "add_friend": "Añadir amigo", + "add_friend": "Añadir amistad", "request_sent": "Solicitud enviada", "request_received": "Solicitud recibida", "accept_request": "Aceptar solicitud", "ignore_request": "Ignorar solicitud", "cancel_request": "Cancelar solicitud", - "undo_friendship": "Eliminar amistad", + "undo_friendship": "Deshacer amistad", "request_accepted": "Solicitud aceptada", "user_blocked_successfully": "Usuario bloqueado exitosamente", "user_block_modal_text": "Esto va a bloquear a {{displayName}}", "blocked_users": "Usuarios bloqueados", "unblock": "Desbloquear", - "no_friends_added": "Todavía no tienes amigos añadidos", + "no_friends_added": "No tenés amistades añadidas", "pending": "Pendiente", - "no_pending_invites": "No tienes invitaciones pendientes", - "no_blocked_users": "No has bloqueado a ningún usuario", - "friend_code_copied": "Código de amigo copiado", - "undo_friendship_modal_text": "Esto deshará tu amistad con {{displayName}}", - "privacy_hint": "Para ajustar quién puede ver esto, ve a <0>Configuración.", + "no_pending_invites": "No tenés invitaciones pendientes", + "no_blocked_users": "No has bloqueado a nadie", + "friend_code_copied": "Código de amistad copiado", + "undo_friendship_modal_text": "Esto va a deshacer tu amistad con {{displayName}}", + "privacy_hint": "Para cambiar quién puede ver esto, andá a <0>Ajustes", "locked_profile": "Este perfil es privado", - "image_process_failure": "Error al procesar la imagen", - "required_field": "Este campo es obligatorio", - "displayname_min_length": "El nombre a mostrar debe tener al menos 3 caracteres", - "displayname_max_length": "El nombre a mostrar debe tener como máximo 50 caracteres", + "image_process_failure": "Errpr al procesar la imagen", + "required_field": "Este campo es requerido", + "displayname_min_length": "El nombre en pantalla debe tener mínimo 3 caracteres", + "displayname_max_length": "El nombre en pantalla debe tener máximo 50 caracteres", "report_profile": "Reportar este perfil", - "report_reason": "¿Cual es el motivo del reporte?", + "report_reason": "¿Porque estás reportando este perfil?", "report_description": "Información adicional", "report_description_placeholder": "Información adicional", "report": "Reportar", "report_reason_hate": "Discursos de odio", "report_reason_sexual_content": "Contenido sexual", "report_reason_violence": "Violencia", - "report_reason_spam": "Spam / Contenido no deseado", - "report_reason_other": "Otro", + "report_reason_spam": "Spam", + "report_reason_other": "Otros", "profile_reported": "Perfil reportado", - "your_friend_code": "Tu código de amigo:", - "upload_banner": "Subir un banner", + "your_friend_code": "Tu código de amistad:", + "upload_banner": "Subir banner", "uploading_banner": "Subiendo banner…", "background_image_updated": "Imagen de fondo actualizada", - "playing": "Jugando {{game}}", - "achievements": "logros", - "achievements_unlocked": "Logros desbloqueados", - "earned_points": "Puntos Obtenidos", - "show_achievements_on_profile": "Mostrar tus logros en tu perfil", - "show_points_on_profile": "Mostrar tus puntos obtenidos en tu perfil", - "games": "Juegos", - "ranking_updated_weekly": "El Ranking se actualiza semanalmente", "stats": "Estadísticas", - "top_percentile": "Top {{percentile}}%" + "achievements": "logros", + "games": "Juegos", + "top_percentile": "Top {{percentile}}%", + "ranking_updated_weekly": "El ranking se actualiza semanalmente", + "playing": "Jugando {{game}}", + "achievements_unlocked": "Logros desbloqueados", + "earned_points": "Puntos obtenidos", + "show_achievements_on_profile": "Mostrá tus logros en tu perfil", + "show_points_on_profile": "Mostrá los puntos obtenidos en tu perfil", + "error_adding_friend": "No se pudo enviar la solicitud de amistad. Por favor revisá el código", + "friend_code_length_error": "El código de amistad debe tener mínimo 8 caracteres", + "game_removed_from_pinned": "Juego removido de fijados", + "game_added_to_pinned": "Juego añadido a fijados" }, "achievement": { "achievement_unlocked": "Logro desbloqueado", "user_achievements": "Logros de {{displayName}}", - "your_achievements": "Tus Logros", + "your_achievements": "Tus logros", "unlocked_at": "Desbloqueado el: {{date}}", - "subscription_needed": "Se necesita una suscripción a Hydra Cloud necesita para ver este contenido", - "new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos", + "subscription_needed": "Se requiere una suscripción a Hydra Cloud para ver esto", + "new_achievements_unlocked": "Desbloqueaste {{achievementCount}} nuevos logros de {{gameCount}} juegos", "achievement_progress": "{{unlockedCount}}/{{totalCount}} logros", - "achievements_unlocked_for_game": "Se han desbloqueado {{achievementCount}} nuevos logros de {{gameTitle}}", + "achievements_unlocked_for_game": "Desbloqueaste {{achievementCount}} nuevos logros para {{gameTitle}}", "hidden_achievement_tooltip": "Este es un logro oculto", - "achievement_earn_points": "Obtén {{points}} puntos con este logro", + "achievement_earn_points": "Conseguí {{points}} puntos por este logro", "earned_points": "Puntos obtenidos:", "available_points": "Puntos disponibles:", - "how_to_earn_achievements_points": "¿Cómo obtener puntos de logros?" + "how_to_earn_achievements_points": "¿Como conseguir puntos por logros?" }, "hydra_cloud": { "subscription_tour_title": "Suscripción Hydra Cloud", - "debrid_description": "Descargas hasta x4 más rápidas con Nimbus", - "subscribe_now": "Suscribirse ahora", + "subscribe_now": "suscribíte ahora", "cloud_saving": "Guardado en la nube", - "cloud_achievements": "Guarda tus logros en la nube", - "animated_profile_picture": "Fotos de perfil animadas", + "cloud_achievements": "Guardá tus logros en la nube", + "animated_profile_picture": "Foto de perfil animada", "premium_support": "Soporte Premium", - "show_and_compare_achievements": "Muestra y compara tus logros con otros usuarios", - "animated_profile_banner": "Fondo de perfil animado", + "show_and_compare_achievements": "Mostrá y compará tus logros con otros usuarios", + "animated_profile_banner": "Banner de perfil animado", "hydra_cloud": "Hydra Cloud", - "hydra_cloud_feature_found": "¡Has descubierto una característica de Hydra Cloud!", - "learn_more": "Aprender más" + "hydra_cloud_feature_found": "¡Acabas de descubrir una característica de Hydra Cloud!", + "learn_more": "Descubrir más", + "debrid_description": "Descargas hasta x4 veces más rápidas con Nimbus" } } diff --git a/src/locales/et/translation.json b/src/locales/et/translation.json index 119e1aab..c5566eeb 100644 --- a/src/locales/et/translation.json +++ b/src/locales/et/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Edukalt sisse logitud" }, "home": { - "featured": "Esile toodud", "surprise_me": "Üllata mind", "no_results": "Tulemusi ei leitud", "start_typing": "Alusta otsimiseks kirjutamist...", @@ -45,10 +44,7 @@ "calculating_eta": "{{title}} allalaadimine… ({{percentage}} valmis) - Järelejäänud aja arvutamine…", "checking_files": "{{title}} failide kontrollimine… ({{percentage}} valmis)" }, - "catalogue": { - "next_page": "Järgmine leht", - "previous_page": "Eelmine leht" - }, + "catalogue": {}, "game_details": { "open_download_options": "Ava allalaadimise valikud", "download_options_zero": "Allalaadimise valikuid pole", diff --git a/src/locales/fa/translation.json b/src/locales/fa/translation.json index be18263a..69a49b79 100644 --- a/src/locales/fa/translation.json +++ b/src/locales/fa/translation.json @@ -1,7 +1,6 @@ { "language_name": "فارسی", "home": { - "featured": "پیشنهادی", "surprise_me": "سوپرایزم کن", "no_results": "اتمام‌ای پیدا نشد" }, @@ -17,7 +16,6 @@ "home": "خانه", "favorites": "علاقه‌مندی‌ها" }, - "header": { "search": "جستجوی بازی‌ها", "home": "خانه", @@ -31,10 +29,7 @@ "downloading_metadata": "درحال دانلود متادیتاهای {{title}}…", "downloading": "در حال دانلود {{title}}… ({{percentage}} تکمیل شده) - اتمام {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "صفحه‌ی بعدی", - "previous_page": "صفحه‌ی قبلی" - }, + "catalogue": {}, "game_details": { "open_download_options": "بازکردن آپشن‌های دانلود", "download_options_zero": "هیچ آپشن دانلودی وجود ندارد", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 1c129a64..8fc07722 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Connecté avec succès" }, "home": { - "featured": "En vedette", "surprise_me": "Surprenez-moi", "no_results": "Aucun résultat trouvé", "start_typing": "Commencez à taper pour rechercher...", diff --git a/src/locales/hu/translation.json b/src/locales/hu/translation.json index 4cf5ee45..2d38690c 100644 --- a/src/locales/hu/translation.json +++ b/src/locales/hu/translation.json @@ -92,6 +92,7 @@ "installation_complete": "Telepítés befejezve", "installation_complete_message": "A(z) Alapvető segédprogramok sikeresen telepítve" }, +<<<<<<< HEAD "catalogue": { "search": "Szűrés…", "developers": "Fejlesztők", @@ -103,6 +104,9 @@ "filter_count": "{{filterCount}} elérhető", "clear_filters": "{{filterCount}} kiválaszott szűrő törlése" }, +======= + "catalogue": {}, +>>>>>>> 4f5c345c421b7447ea4aa4a01706e485f5f641e7 "game_details": { "open_download_options": "Letöltési opciók megnyitása", "download_options_zero": "Nincs letöltési opció", diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index 4fa347fc..fc72fc51 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Berhasil masuk" }, "home": { - "featured": "Unggulan", "surprise_me": "Kejutkan saya", "no_results": "Tidak ada hasil ditemukan" }, @@ -25,7 +24,6 @@ }, "header": { "search": "Cari game", - "home": "Beranda", "catalogue": "Katalog", "downloads": "Unduhan", @@ -41,10 +39,7 @@ "calculating_eta": "Mengunduh {{title}}… ({{percentage}} selesai) - Menghitung waktu yang tersisa…", "checking_files": "Memeriksa file {{title}}… ({{percentage}} selesai)" }, - "catalogue": { - "next_page": "Halaman Berikutnya", - "previous_page": "Halaman Sebelumnya" - }, + "catalogue": {}, "game_details": { "open_download_options": "Buka opsi unduhan", "download_options_zero": "Tidak ada opsi unduhan", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index b23d1244..ac37ffe9 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -1,7 +1,6 @@ { "language_name": "Italiano", "home": { - "featured": "In primo piano", "surprise_me": "Sorprendimi", "no_results": "Nessun risultato trovato" }, @@ -20,7 +19,6 @@ }, "header": { "search": "Cerca", - "home": "Home", "catalogue": "Catalogo", "downloads": "Download", @@ -32,10 +30,7 @@ "downloading_metadata": "Scaricamento metadati di {{title}}…", "downloading": "Download di {{title}}… ({{percentage}} completato) - Conclusione {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Pagina successiva", - "previous_page": "Pagina precedente" - }, + "catalogue": {}, "game_details": { "open_download_options": "Apri opzioni di download", "download_options_zero": "Nessuna opzione di download", diff --git a/src/locales/kk/translation.json b/src/locales/kk/translation.json index bfb009a7..48fb8181 100644 --- a/src/locales/kk/translation.json +++ b/src/locales/kk/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Сәтті кіру" }, "home": { - "featured": "Ұсынылған", "surprise_me": "Таңқалдыр", "no_results": "Ештеңе табылмады" }, @@ -23,7 +22,6 @@ "sign_in": "Кіру", "favorites": "Таңдаулылар" }, - "header": { "search": "Іздеу", "home": "Басты бет", @@ -40,10 +38,7 @@ "downloading": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Аяқтау {{eta}} - {{speed}}", "calculating_eta": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Қалған уақытты есептеу…" }, - "catalogue": { - "next_page": "Келесі бет", - "previous_page": "Алдыңғы бет" - }, + "catalogue": {}, "game_details": { "open_download_options": "Жүктеу нұсқаларын ашу", "download_options_zero": "Жүктеу нұсқалары жоқ", diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json index 9ec389b1..a9b9c0e5 100644 --- a/src/locales/ko/translation.json +++ b/src/locales/ko/translation.json @@ -1,7 +1,6 @@ { "language_name": "한국어", "home": { - "featured": "추천", "surprise_me": "무작위 추천", "no_results": "결과 없음" }, @@ -17,7 +16,6 @@ "home": "홈", "favorites": "즐겨찾기" }, - "header": { "search": "게임 검색하기", "home": "홈", @@ -31,10 +29,7 @@ "downloading_metadata": "{{title}}의 메타데이터를 다운로드 중…", "downloading": "{{title}}의 파일들을 다운로드 중… ({{percentage}} 완료) - 완료까지 {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "다음 페이지", - "previous_page": "이전 페이지" - }, + "catalogue": {}, "game_details": { "open_download_options": "다운로드 선택지 열기", "download_options_zero": "다운로드 선택지 없음", diff --git a/src/locales/nb/translation.json b/src/locales/nb/translation.json index 8898ec7b..95bda8fe 100644 --- a/src/locales/nb/translation.json +++ b/src/locales/nb/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Logget inn vellykket" }, "home": { - "featured": "Anbefalinger", "surprise_me": "Overrask meg", "no_results": "Ingen resultater fundet", "start_typing": "Begynn å skrive for å søke...", @@ -29,7 +28,6 @@ }, "header": { "search": "Søk efter spill", - "home": "Hjem", "catalogue": "Katalog", "downloads": "Nedlastinger", @@ -45,10 +43,7 @@ "calculating_eta": "Laster ned {{title}}… ({{percentage}} ferdig) - Regner ut resterende tid…", "checking_files": "Sjekker {{title}} filer… ({{percentage}} ferdig)" }, - "catalogue": { - "next_page": "Neste side", - "previous_page": "Forrige side" - }, + "catalogue": {}, "game_details": { "open_download_options": "Åpne nedlastingsmuligheter", "download_options_zero": "Ingen nedlastingsmulighet", diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json index 72d20c74..baa6df6e 100644 --- a/src/locales/nl/translation.json +++ b/src/locales/nl/translation.json @@ -1,7 +1,6 @@ { "language_name": "Nederlands", "home": { - "featured": "Uitgelicht", "surprise_me": "Verrasing", "no_results": "Geen resultaten gevonden" }, @@ -19,7 +18,6 @@ }, "header": { "search": "Zoek spellen", - "home": "Home", "catalogue": "Bibliotheek", "downloads": "Downloads", @@ -31,10 +29,7 @@ "downloading_metadata": "Downloading {{title}} metadata…", "downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Volgende Pagina", - "previous_page": "Vorige Pagina" - }, + "catalogue": {}, "game_details": { "open_download_options": "Open download Instellingen", "download_options_zero": "Geen download Instellingen", diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json index 86751b0e..2e0d1696 100644 --- a/src/locales/pl/translation.json +++ b/src/locales/pl/translation.json @@ -1,7 +1,6 @@ { "language_name": "Polski", "home": { - "featured": "Wyróżnione", "surprise_me": "Zaskocz mnie", "no_results": "Nie znaleziono wyników" }, @@ -20,7 +19,6 @@ }, "header": { "search": "Szukaj", - "home": "Główna", "catalogue": "Katalog", "downloads": "Pobrane", @@ -32,10 +30,7 @@ "downloading_metadata": "Pobieranie {{title}} metadata…", "downloading": "Pobieranie {{title}}… (ukończone w {{percentage}}) - Podsumowanie {{eta}} - {{speed}}" }, - "catalogue": { - "next_page": "Następna strona", - "previous_page": "Poprzednia strona" - }, + "catalogue": {}, "game_details": { "open_download_options": "Otwórz opcje pobierania", "download_options_zero": "Brak opcji pobierania", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 7f7f8cc1..b88c38a5 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -26,7 +26,22 @@ "sign_in": "Login", "friends": "Amigos", "need_help": "Precisa de ajuda?", - "favorites": "Favoritos" + "favorites": "Favoritos", + "add_custom_game_tooltip": "Adicionar jogo personalizado", + "custom_game_modal": "Adicionar jogo personalizado", + "edit_game_modal_title": "Título", + "playable_button_title": "", + "custom_game_modal_add": "Adicionar Jogo", + "custom_game_modal_adding": "Adicionando...", + "custom_game_modal_browse": "Buscar", + "custom_game_modal_cancel": "Cancelar", + "edit_game_modal_assets": "Imagens", + "edit_game_modal_icon": "Ícone", + "edit_game_modal_browse": "Buscar", + "edit_game_modal_cancel": "Cancelar", + "edit_game_modal_enter_title": "Insira o título", + "edit_game_modal_logo": "Logo", + "edit_game_modal": "Personalizar detalhes" }, "header": { "search": "Buscar jogos", @@ -219,7 +234,18 @@ "historical_keyshop": "Preço histórico em keyshops", "language": "Idioma", "caption": "Legenda", - "audio": "Áudio" + "audio": "Áudio", + "edit_game_modal_button": "Alterar detalhes do jogo", + "game_added_to_pinned": "Jogo adicionado aos fixados", + "game_removed_from_pinned": "Jogo removido dos fixados", + "manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente", + "manual_playtime_warning": "As suas horas de jogo serão marcadas como atualizadas manualmente. Esta ação não pode ser desfeita.", + "missing_wine_prefix": "Um prefixo Wine é necessário para criar um backup no Linux", + "update_game_playtime": "Modificar tempo de jogo", + "update_playtime": "Modificar tempo de jogo", + "update_playtime_description": "Atualizar manualmente o tempo de jogo de {{game}}", + "update_playtime_error": "Falha ao atualizar tempo de jogo", + "update_playtime_title": "Atualizar tempo de jogo" }, "activation": { "title": "Ativação", @@ -394,7 +420,8 @@ "hidden": "Oculta", "test_notification": "Testar notificação", "notification_preview": "Prévia da Notificação de Conquistas", - "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" + "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo", + "editor_tab_code": "Código" }, "notifications": { "download_complete": "Download concluído", @@ -523,7 +550,15 @@ "show_achievements_on_profile": "Exiba suas conquistas no perfil", "show_points_on_profile": "Exiba seus pontos ganhos no perfil", "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", - "friend_code_length_error": "Código de amigo deve ter 8 caracteres" + "friend_code_length_error": "Código de amigo deve ter 8 caracteres", + "top_percentile": "Top {{percentile}}%", + "playtime": "Tempo de jogo", + "played_recently": "Jogado recentemente", + "pinned": "Fixado", + "amount_minutes_short": "{{amount}}h", + "amount_hours_short": "{{amount}}h", + "game_added_to_pinned": "Jogo adicionado aos fixados", + "achievements_earned": "Conquistas recebidas" }, "achievement": { "achievement_unlocked": "Conquista desbloqueada", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index 6c32b35b..654e94ec 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Sessão iniciada com sucesso" }, "home": { - "featured": "Destaques", "hot": "Populares", "weekly": "📅 Mais descarregados esta semana", "achievements": "🏆 Para completar", @@ -26,7 +25,8 @@ "game_has_no_executable": "O jogo não tem um executável selecionado", "sign_in": "Iniciar sessão", "friends": "Amigos", - "favorites": "Favoritos" + "favorites": "Favoritos", + "edit_game_modal_cancel": "Cancelar" }, "header": { "search": "Procurar jogos", @@ -247,9 +247,6 @@ "download_count_zero": "Sem downloads na lista", "download_count_one": "{{countFormatted}} download na lista", "download_count_other": "{{countFormatted}} downloads na lista", - "download_options_zero": "Sem downloads disponíveis", - "download_options_one": "{{countFormatted}} download disponível", - "download_options_other": "{{countFormatted}} downloads disponíveis", "download_source_url": "URL da fonte", "add_download_source_description": "Insere o URL que contém o ficheiro .json", "download_source_up_to_date": "Sincronizada", @@ -359,8 +356,6 @@ "instructions": "Verifica a forma correta de instalar algum deles na tua distribuição Linux, para garantir a execução normal do jogo" }, "catalogue": { - "next_page": "Página seguinte", - "previous_page": "Página anterior", "search": "Filtrar…", "developers": "Desenvolvedores", "genres": "Géneros", @@ -427,7 +422,6 @@ "friend_code_copied": "Código de amigo copiado", "undo_friendship_modal_text": "Isto vai remover a tua amizade com {{displayName}}", "privacy_hint": "Para controlar quem pode ver o teu perfil, acede às <0>Definições", - "profile_locked": "Este perfil é privado", "image_process_failure": "Falha ao processar a imagem", "required_field": "Este campo é obrigatório", "displayname_min_length": "O nome de apresentação deve ter pelo menos 3 caracteres", diff --git a/src/locales/ro/translation.json b/src/locales/ro/translation.json index c5a81881..8ed6fd39 100644 --- a/src/locales/ro/translation.json +++ b/src/locales/ro/translation.json @@ -1,7 +1,6 @@ { "language_name": "Română", "home": { - "featured": "Recomandate", "surprise_me": "Surprinde-mă", "no_results": "Niciun rezultat găsit" }, @@ -19,7 +18,6 @@ }, "header": { "search": "Caută jocuri", - "home": "Acasă", "catalogue": "Catalog", "downloads": "Descărcări", @@ -32,10 +30,7 @@ "downloading": "Se descarcă {{title}}... ({{percentage}} complet) - Concluzie {{eta}} - {{speed}}", "calculating_eta": "Se descarcă {{title}}... ({{percentage}} complet) - Calculare timp rămas..." }, - "catalogue": { - "next_page": "Pagina următoare", - "previous_page": "Pagina anterioară" - }, + "catalogue": {}, "game_details": { "open_download_options": "Deschide opțiunile de descărcare", "download_options_zero": "Nicio opțiune de descărcare", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 58235989..03413554 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -4,14 +4,12 @@ "successfully_signed_in": "Успешный вход" }, "home": { - "featured": "Рекомендации", "surprise_me": "Удиви меня", "no_results": "Ничего не найдено", "hot": "Сейчас популярно", "start_typing": "Начинаю вводить текст...", "weekly": "📅 Лучшие игры недели", - "achievements": "🏆 Игры с достижениями", - "already_in_library": "Уже в библиотеке" + "achievements": "🏆 Игры с достижениями" }, "sidebar": { "catalogue": "Каталог", @@ -486,6 +484,8 @@ "user_profile": { "amount_hours": "{{amount}} часов", "amount_minutes": "{{amount}} минут", + "amount_hours_short": "{{amount}}ч", + "amount_minutes_short": "{{amount}}м", "last_time_played": "Последняя игра {{period}}", "activity": "Недавняя активность", "library": "Библиотека", diff --git a/src/locales/sv/translation.json b/src/locales/sv/translation.json index 0972effa..901e4ca7 100644 --- a/src/locales/sv/translation.json +++ b/src/locales/sv/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Inloggningen lyckades" }, "home": { - "featured": "Utvalt", "surprise_me": "Överraska mig", "no_results": "Inga resultat hittades", "start_typing": "Börja skriva för att söka...", diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index c3fe2081..e8e1cb2b 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Başarıyla giriş yapıldı" }, "home": { - "featured": "Öne Çıkanlar", "surprise_me": "Beni Şaşırt", "no_results": "Sonuç bulunamadı", "start_typing": "Aramak için yazmaya başlayın...", diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index 48a3972d..26aa8aae 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Успішний вхід в систему" }, "home": { - "featured": "Рекомендоване", "surprise_me": "Здивуй мене", "no_results": "Результатів не знайдено", "start_typing": "Почніть набирати текст для пошуку...", diff --git a/src/locales/uz/translation.json b/src/locales/uz/translation.json index d20a9677..24e508af 100644 --- a/src/locales/uz/translation.json +++ b/src/locales/uz/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Kirish muvaffaqiyatli amalga oshirildi" }, "home": { - "featured": "Tavsiya etilgan", "surprise_me": "Hayratda qoldir", "no_results": "Natijalar topilmadi", "hot": "Eng mashhur", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 0323d991..7cdd0c92 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "已成功登录" }, "home": { - "featured": "特色推荐", "surprise_me": "向我推荐", "no_results": "没有找到结果", "start_typing": "键入以开始搜素...", @@ -51,8 +50,6 @@ "installing_common_redist": "{{log}}…" }, "catalogue": { - "next_page": "下一页", - "previous_page": "上一页", "clear_filters": "清除已选的 {{filterCount}} 项", "developers": "开发商", "download_sources": "下载源", diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index 6a33ffaf..fbb60ab2 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -1,7 +1,70 @@ import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; -import { gamesSublevel, levelKeys } from "@main/level"; -import type { GameShop } from "@types"; +import { HydraApi, logger } from "@main/services"; +import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; +import type { GameShop, Game } from "@types"; +import fs from "node:fs"; + +const collectAssetPathsToDelete = (game: Game): string[] => { + const assetPathsToDelete: string[] = []; + + const assetUrls = + game.shop === "custom" + ? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl] + : [game.customIconUrl, game.customLogoImageUrl, game.customHeroImageUrl]; + + for (const url of assetUrls) { + if (url?.startsWith("local:")) { + assetPathsToDelete.push(url.replace("local:", "")); + } + } + + return assetPathsToDelete; +}; + +const updateGameAsDeleted = async ( + game: Game, + gameKey: string +): Promise => { + const updatedGame = { + ...game, + isDeleted: true, + executablePath: null, + ...(game.shop !== "custom" && { + customIconUrl: null, + customLogoImageUrl: null, + customHeroImageUrl: null, + }), + }; + + await gamesSublevel.put(gameKey, updatedGame); +}; + +const resetShopAssets = async (gameKey: string): Promise => { + const existingAssets = await gamesShopAssetsSublevel.get(gameKey); + if (existingAssets) { + const resetAssets = { + ...existingAssets, + title: existingAssets.title, + }; + await gamesShopAssetsSublevel.put(gameKey, resetAssets); + } +}; + +const deleteAssetFiles = async ( + assetPathsToDelete: string[] +): Promise => { + if (assetPathsToDelete.length === 0) return; + + for (const assetPath of assetPathsToDelete) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + logger.warn(`Failed to delete asset ${assetPath}:`, error); + } + } +}; const removeGameFromLibrary = async ( _event: Electron.IpcMainInvokeEvent, @@ -11,17 +74,21 @@ const removeGameFromLibrary = async ( const gameKey = levelKeys.game(shop, objectId); const game = await gamesSublevel.get(gameKey); - if (game) { - await gamesSublevel.put(gameKey, { - ...game, - isDeleted: true, - executablePath: null, - }); + if (!game) return; - if (game?.remoteId) { - HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); - } + const assetPathsToDelete = collectAssetPathsToDelete(game); + + await updateGameAsDeleted(game, gameKey); + + if (game.shop !== "custom") { + await resetShopAssets(gameKey); } + + if (game?.remoteId) { + HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {}); + } + + await deleteAssetFiles(assetPathsToDelete); }; registerEvent("removeGameFromLibrary", removeGameFromLibrary); diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 6152c0df..8129fc57 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -1,16 +1,36 @@ import { registerEvent } from "../register-event"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop } from "@types"; +import fs from "node:fs"; +import { logger } from "@main/services"; + +interface UpdateCustomGameParams { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; +} const updateCustomGame = async ( _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string + params: UpdateCustomGameParams ) => { + const { + shop, + objectId, + title, + iconUrl, + logoImageUrl, + libraryHeroImageUrl, + originalIconPath, + originalLogoPath, + originalHeroPath, + } = params; const gameKey = levelKeys.game(shop, objectId); const existingGame = await gamesSublevel.get(gameKey); @@ -18,12 +38,29 @@ const updateCustomGame = async ( throw new Error("Game not found"); } + const oldAssetPaths: string[] = []; + + const assetPairs = [ + { existing: existingGame.iconUrl, new: iconUrl }, + { existing: existingGame.logoImageUrl, new: logoImageUrl }, + { existing: existingGame.libraryHeroImageUrl, new: libraryHeroImageUrl }, + ]; + + for (const { existing, new: newUrl } of assetPairs) { + if (existing?.startsWith("local:") && (!newUrl || existing !== newUrl)) { + oldAssetPaths.push(existing.replace("local:", "")); + } + } + const updatedGame = { ...existingGame, title, iconUrl: iconUrl || null, logoImageUrl: logoImageUrl || null, libraryHeroImageUrl: libraryHeroImageUrl || null, + originalIconPath: originalIconPath || existingGame.originalIconPath || null, + originalLogoPath: originalLogoPath || existingGame.originalLogoPath || null, + originalHeroPath: originalHeroPath || existingGame.originalHeroPath || null, }; await gamesSublevel.put(gameKey, updatedGame); @@ -43,6 +80,18 @@ const updateCustomGame = async ( await gamesShopAssetsSublevel.put(gameKey, updatedAssets); } + if (oldAssetPaths.length > 0) { + for (const assetPath of oldAssetPaths) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + logger.warn(`Failed to delete old asset ${assetPath}:`, error); + } + } + } + return updatedGame; }; diff --git a/src/main/events/library/update-game-custom-assets.ts b/src/main/events/library/update-game-custom-assets.ts index 866cd60e..1f912901 100644 --- a/src/main/events/library/update-game-custom-assets.ts +++ b/src/main/events/library/update-game-custom-assets.ts @@ -1,16 +1,131 @@ import { registerEvent } from "../register-event"; import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; -import type { GameShop } from "@types"; +import type { GameShop, Game } from "@types"; +import fs from "node:fs"; +import { logger } from "@main/services"; -const updateGameCustomAssets = async ( - _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string, - title: string, +const collectOldAssetPaths = ( + existingGame: Game, customIconUrl?: string | null, customLogoImageUrl?: string | null, customHeroImageUrl?: string | null +): string[] => { + const oldAssetPaths: string[] = []; + + const assetPairs = [ + { existing: existingGame.customIconUrl, new: customIconUrl }, + { existing: existingGame.customLogoImageUrl, new: customLogoImageUrl }, + { existing: existingGame.customHeroImageUrl, new: customHeroImageUrl }, + ]; + + for (const { existing, new: newUrl } of assetPairs) { + if ( + existing && + newUrl !== undefined && + existing !== newUrl && + existing.startsWith("local:") + ) { + oldAssetPaths.push(existing.replace("local:", "")); + } + } + + return oldAssetPaths; +}; + +interface UpdateGameDataParams { + gameKey: string; + existingGame: Game; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; +} + +const updateGameData = async (params: UpdateGameDataParams): Promise => { + const { + gameKey, + existingGame, + title, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath, + } = params; + const updatedGame = { + ...existingGame, + title, + ...(customIconUrl !== undefined && { customIconUrl }), + ...(customLogoImageUrl !== undefined && { customLogoImageUrl }), + ...(customHeroImageUrl !== undefined && { customHeroImageUrl }), + ...(customOriginalIconPath !== undefined && { customOriginalIconPath }), + ...(customOriginalLogoPath !== undefined && { customOriginalLogoPath }), + ...(customOriginalHeroPath !== undefined && { customOriginalHeroPath }), + }; + + await gamesSublevel.put(gameKey, updatedGame); + return updatedGame; +}; + +const updateShopAssets = async ( + gameKey: string, + title: string +): Promise => { + const existingAssets = await gamesShopAssetsSublevel.get(gameKey); + if (existingAssets) { + const updatedAssets = { + ...existingAssets, + title, + }; + await gamesShopAssetsSublevel.put(gameKey, updatedAssets); + } +}; + +const deleteOldAssetFiles = async (oldAssetPaths: string[]): Promise => { + if (oldAssetPaths.length === 0) return; + + for (const assetPath of oldAssetPaths) { + try { + if (fs.existsSync(assetPath)) { + await fs.promises.unlink(assetPath); + } + } catch (error) { + logger.warn(`Failed to delete old custom asset ${assetPath}:`, error); + } + } +}; + +interface UpdateGameCustomAssetsParams { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; +} + +const updateGameCustomAssets = async ( + _event: Electron.IpcMainInvokeEvent, + params: UpdateGameCustomAssetsParams ) => { + const { + shop, + objectId, + title, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath, + } = params; const gameKey = levelKeys.game(shop, objectId); const existingGame = await gamesSublevel.get(gameKey); @@ -18,26 +133,28 @@ const updateGameCustomAssets = async ( throw new Error("Game not found"); } - const updatedGame = { - ...existingGame, + const oldAssetPaths = collectOldAssetPaths( + existingGame, + customIconUrl, + customLogoImageUrl, + customHeroImageUrl + ); + + const updatedGame = await updateGameData({ + gameKey, + existingGame, title, - ...(customIconUrl !== undefined && { customIconUrl }), - ...(customLogoImageUrl !== undefined && { customLogoImageUrl }), - ...(customHeroImageUrl !== undefined && { customHeroImageUrl }), - }; + customIconUrl, + customLogoImageUrl, + customHeroImageUrl, + customOriginalIconPath, + customOriginalLogoPath, + customOriginalHeroPath, + }); - await gamesSublevel.put(gameKey, updatedGame); + await updateShopAssets(gameKey, title); - // Also update the shop assets for non-custom games - const existingAssets = await gamesShopAssetsSublevel.get(gameKey); - if (existingAssets) { - const updatedAssets = { - ...existingAssets, - title, // Update the title in shop assets as well - }; - - await gamesShopAssetsSublevel.put(gameKey, updatedAssets); - } + await deleteOldAssetFiles(oldAssetPaths); return updatedGame; }; diff --git a/src/preload/index.ts b/src/preload/index.ts index e536f8c7..17c1225f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -152,40 +152,28 @@ contextBridge.exposeInMainWorld("electron", { deleteTempFile: (filePath: string) => ipcRenderer.invoke("deleteTempFile", filePath), cleanupUnusedAssets: () => ipcRenderer.invoke("cleanupUnusedAssets"), - updateCustomGame: ( - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string - ) => - ipcRenderer.invoke( - "updateCustomGame", - shop, - objectId, - title, - iconUrl, - logoImageUrl, - libraryHeroImageUrl - ), - updateGameCustomAssets: ( - shop: GameShop, - objectId: string, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null - ) => - ipcRenderer.invoke( - "updateGameCustomAssets", - shop, - objectId, - title, - customIconUrl, - customLogoImageUrl, - customHeroImageUrl - ), + updateCustomGame: (params: { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; + }) => ipcRenderer.invoke("updateCustomGame", params), + updateGameCustomAssets: (params: { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; + }) => ipcRenderer.invoke("updateGameCustomAssets", params), createGameShortcut: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 81d18940..e6277888 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -119,14 +119,17 @@ declare global { logoImageUrl?: string, libraryHeroImageUrl?: string ) => Promise; - updateCustomGame: ( - shop: GameShop, - objectId: string, - title: string, - iconUrl?: string, - logoImageUrl?: string, - libraryHeroImageUrl?: string - ) => Promise; + updateCustomGame: (params: { + shop: GameShop; + objectId: string; + title: string; + iconUrl?: string; + logoImageUrl?: string; + libraryHeroImageUrl?: string; + originalIconPath?: string; + originalLogoPath?: string; + originalHeroPath?: string; + }) => Promise; copyCustomGameAsset: ( sourcePath: string, assetType: "icon" | "logo" | "hero" @@ -135,14 +138,17 @@ declare global { deletedCount: number; errors: string[]; }>; - updateGameCustomAssets: ( - shop: GameShop, - objectId: string, - title: string, - customIconUrl?: string | null, - customLogoImageUrl?: string | null, - customHeroImageUrl?: string | null - ) => Promise; + updateGameCustomAssets: (params: { + shop: GameShop; + objectId: string; + title: string; + customIconUrl?: string | null; + customLogoImageUrl?: string | null; + customHeroImageUrl?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; + }) => Promise; createGameShortcut: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/pages/game-details/description-header/description-header.scss b/src/renderer/src/pages/game-details/description-header/description-header.scss index 920e8068..8ca78eaa 100644 --- a/src/renderer/src/pages/game-details/description-header/description-header.scss +++ b/src/renderer/src/pages/game-details/description-header/description-header.scss @@ -2,7 +2,7 @@ .description-header { width: calc(100% - calc(globals.$spacing-unit * 2)); - margin: calc(globals.$spacing-unit * 1) auto; + margin: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 1); padding: calc(globals.$spacing-unit * 1.5); display: flex; justify-content: space-between; @@ -10,8 +10,8 @@ background-color: globals.$background-color; height: 72px; border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.03); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); &__info { display: flex; diff --git a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss index f66da32b..9483b50e 100644 --- a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss +++ b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss @@ -7,6 +7,15 @@ display: flex; flex-direction: column; align-items: center; + max-height: 80vh; + + @media (min-width: 1024px) { + max-height: 70vh; + } + + @media (min-width: 1280px) { + max-height: 60vh; + } } &__viewport { @@ -16,8 +25,19 @@ overflow: hidden; border-radius: 8px; + @media (min-width: 1024px) { + width: 80%; + max-height: 400px; + } + @media (min-width: 1280px) { width: 60%; + max-height: 500px; + } + + @media (min-width: 1536px) { + width: 50%; + max-height: 600px; } } @@ -52,10 +72,18 @@ overflow-y: hidden; gap: calc(globals.$spacing-unit / 2); + @media (min-width: 1024px) { + width: 80%; + } + @media (min-width: 1280px) { width: 60%; } + @media (min-width: 1536px) { + width: 50%; + } + &::-webkit-scrollbar-thumb { width: 20%; } @@ -79,6 +107,19 @@ border: solid 1px globals.$border-color; overflow: hidden; position: relative; + aspect-ratio: 16/9; + + @media (min-width: 1024px) { + width: 15%; + } + + @media (min-width: 1280px) { + width: 12%; + } + + @media (min-width: 1536px) { + width: 10%; + } &:hover { opacity: 0.8; diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index 347e5a1c..4e9ecf14 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -43,6 +43,29 @@ export function GameDetailsContent() { const $images = Array.from(document.querySelectorAll("img")); $images.forEach(($image) => { $image.loading = "lazy"; + // Remove any inline width/height styles that might cause overflow + $image.removeAttribute("width"); + $image.removeAttribute("height"); + $image.removeAttribute("style"); + // Set max-width to prevent overflow + $image.style.maxWidth = "100%"; + $image.style.width = "auto"; + $image.style.height = "auto"; + $image.style.boxSizing = "border-box"; + }); + + // Handle videos the same way + const $videos = Array.from(document.querySelectorAll("video")); + $videos.forEach(($video) => { + // Remove any inline width/height styles that might cause overflow + $video.removeAttribute("width"); + $video.removeAttribute("height"); + $video.removeAttribute("style"); + // Set max-width to prevent overflow + $video.style.maxWidth = "100%"; + $video.style.width = "auto"; + $video.style.height = "auto"; + $video.style.boxSizing = "border-box"; }); return document.body.outerHTML; diff --git a/src/renderer/src/pages/game-details/game-details.scss b/src/renderer/src/pages/game-details/game-details.scss index 786a8d30..6b02dde5 100644 --- a/src/renderer/src/pages/game-details/game-details.scss +++ b/src/renderer/src/pages/game-details/game-details.scss @@ -186,9 +186,10 @@ $hero-height: 300px; &__description-content { width: 100%; - height: 100%; + min-height: 100%; min-width: 0; flex: 1; + overflow-x: hidden; } &__description { @@ -199,6 +200,8 @@ $hero-height: 300px; width: 100%; margin-left: auto; margin-right: auto; + overflow-x: auto; + min-height: auto; @media (min-width: 768px) { padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); @@ -206,20 +209,30 @@ $hero-height: 300px; @media (min-width: 1024px) { padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2); + width: 80%; } @media (min-width: 1280px) { width: 60%; } - img { + @media (min-width: 1536px) { + width: 50%; + } + + img, + video { border-radius: 5px; margin-top: globals.$spacing-unit; margin-bottom: calc(globals.$spacing-unit * 3); - display: block; - width: 100%; - height: auto; - object-fit: cover; + display: block !important; + max-width: 100% !important; + width: auto !important; + height: auto !important; + object-fit: contain !important; + box-sizing: border-box !important; + word-wrap: break-word; + overflow-wrap: break-word; } a { @@ -247,12 +260,17 @@ $hero-height: 300px; @media (min-width: 1024px) { padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 2); + width: 80%; } @media (min-width: 1280px) { width: 60%; line-height: 22px; } + + @media (min-width: 1536px) { + width: 50%; + } } &__randomizer-button { diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 04a27779..0f6df95d 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { ImageIcon, XIcon } from "@primer/octicons-react"; @@ -29,16 +29,29 @@ export function EditGameModal({ const { showSuccessToast, showErrorToast } = useToast(); const [gameName, setGameName] = useState(""); - const [iconPath, setIconPath] = useState(""); - const [logoPath, setLogoPath] = useState(""); - const [heroPath, setHeroPath] = useState(""); + const [assetPaths, setAssetPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [assetDisplayPaths, setAssetDisplayPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [originalAssetPaths, setOriginalAssetPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [defaultUrls, setDefaultUrls] = useState({ + icon: null as string | null, + logo: null as string | null, + hero: null as string | null, + }); const [isUpdating, setIsUpdating] = useState(false); const [selectedAssetType, setSelectedAssetType] = useState("icon"); - const [defaultIconUrl, setDefaultIconUrl] = useState(null); - const [defaultLogoUrl, setDefaultLogoUrl] = useState(null); - const [defaultHeroUrl, setDefaultHeroUrl] = useState(null); - const isCustomGame = (game: LibraryGame | Game): boolean => { return game.shop === "custom"; }; @@ -48,26 +61,58 @@ export function EditGameModal({ }; const setCustomGameAssets = useCallback((game: LibraryGame | Game) => { - setIconPath(extractLocalPath(game.iconUrl)); - setLogoPath(extractLocalPath(game.logoImageUrl)); - setHeroPath(extractLocalPath(game.libraryHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); + setOriginalAssetPaths({ + icon: (game as any).originalIconPath || extractLocalPath(game.iconUrl), + logo: + (game as any).originalLogoPath || extractLocalPath(game.logoImageUrl), + hero: + (game as any).originalHeroPath || + extractLocalPath(game.libraryHeroImageUrl), + }); }, []); const setNonCustomGameAssets = useCallback( (game: LibraryGame) => { - setIconPath(extractLocalPath(game.customIconUrl)); - setLogoPath(extractLocalPath(game.customLogoImageUrl)); - setHeroPath(extractLocalPath(game.customHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); + setOriginalAssetPaths({ + icon: + (game as any).customOriginalIconPath || + extractLocalPath(game.customIconUrl), + logo: + (game as any).customOriginalLogoPath || + extractLocalPath(game.customLogoImageUrl), + hero: + (game as any).customOriginalHeroPath || + extractLocalPath(game.customHeroImageUrl), + }); - setDefaultIconUrl(shopDetails?.assets?.iconUrl || game.iconUrl || null); - setDefaultLogoUrl( - shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null - ); - setDefaultHeroUrl( - shopDetails?.assets?.libraryHeroImageUrl || + setDefaultUrls({ + icon: shopDetails?.assets?.iconUrl || game.iconUrl || null, + logo: shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null, + hero: + shopDetails?.assets?.libraryHeroImageUrl || game.libraryHeroImageUrl || - null - ); + null, + }); }, [shopDetails] ); @@ -93,39 +138,24 @@ export function EditGameModal({ }; const getAssetPath = (assetType: AssetType): string => { - switch (assetType) { - case "icon": - return iconPath; - case "logo": - return logoPath; - case "hero": - return heroPath; - } + return assetPaths[assetType]; + }; + + const getAssetDisplayPath = (assetType: AssetType): string => { + // Use original path if available, otherwise fall back to display path + return originalAssetPaths[assetType] || assetDisplayPaths[assetType]; }; const setAssetPath = (assetType: AssetType, path: string): void => { - switch (assetType) { - case "icon": - setIconPath(path); - break; - case "logo": - setLogoPath(path); - break; - case "hero": - setHeroPath(path); - break; - } + setAssetPaths((prev) => ({ ...prev, [assetType]: path })); + }; + + const setAssetDisplayPath = (assetType: AssetType, path: string): void => { + setAssetDisplayPaths((prev) => ({ ...prev, [assetType]: path })); }; const getDefaultUrl = (assetType: AssetType): string | null => { - switch (assetType) { - case "icon": - return defaultIconUrl; - case "logo": - return defaultLogoUrl; - case "hero": - return defaultHeroUrl; - } + return defaultUrls[assetType]; }; const handleSelectAsset = async (assetType: AssetType) => { @@ -140,23 +170,55 @@ export function EditGameModal({ }); if (filePaths && filePaths.length > 0) { + const originalPath = filePaths[0]; try { const copiedAssetUrl = await window.electron.copyCustomGameAsset( - filePaths[0], + originalPath, assetType ); setAssetPath(assetType, copiedAssetUrl.replace("local:", "")); + setAssetDisplayPath(assetType, originalPath); + // Store the original path for display purposes + setOriginalAssetPaths((prev) => ({ + ...prev, + [assetType]: originalPath, + })); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); - setAssetPath(assetType, filePaths[0]); + setAssetPath(assetType, originalPath); + setAssetDisplayPath(assetType, originalPath); + setOriginalAssetPaths((prev) => ({ + ...prev, + [assetType]: originalPath, + })); } } }; const handleRestoreDefault = (assetType: AssetType) => { setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); }; + const getOriginalTitle = (): string => { + if (!game) return ""; + + // For non-custom games, the original title is from shopDetails assets + return shopDetails?.assets?.title || game.title || ""; + }; + + const handleRestoreDefaultTitle = () => { + const originalTitle = getOriginalTitle(); + setGameName(originalTitle); + }; + + const isTitleChanged = useMemo((): boolean => { + if (!game || isCustomGame(game)) return false; + const originalTitle = getOriginalTitle(); + return gameName.trim() !== originalTitle.trim(); + }, [game, gameName, shopDetails]); + const [dragOverTarget, setDragOverTarget] = useState(null); const handleDragOver = (e: React.DragEvent) => { @@ -232,6 +294,7 @@ export function EditGameModal({ const assetPath = copiedAssetUrl.replace("local:", ""); setAssetPath(assetType, assetPath); + setAssetDisplayPath(assetType, filePath); showSuccessToast( `${assetType.charAt(0).toUpperCase() + assetType.slice(1)} updated successfully!` @@ -267,10 +330,12 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { - const iconUrl = iconPath ? `local:${iconPath}` : game.iconUrl; - const logoImageUrl = logoPath ? `local:${logoPath}` : game.logoImageUrl; - const libraryHeroImageUrl = heroPath - ? `local:${heroPath}` + const iconUrl = assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl; + const logoImageUrl = assetPaths.logo + ? `local:${assetPaths.logo}` + : game.logoImageUrl; + const libraryHeroImageUrl = assetPaths.hero + ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; return { iconUrl, logoImageUrl, libraryHeroImageUrl }; @@ -279,9 +344,9 @@ export function EditGameModal({ // Helper function to prepare non-custom game assets const prepareNonCustomGameAssets = () => { return { - customIconUrl: iconPath ? `local:${iconPath}` : null, - customLogoImageUrl: logoPath ? `local:${logoPath}` : null, - customHeroImageUrl: heroPath ? `local:${heroPath}` : null, + customIconUrl: assetPaths.icon ? `local:${assetPaths.icon}` : null, + customLogoImageUrl: assetPaths.logo ? `local:${assetPaths.logo}` : null, + customHeroImageUrl: assetPaths.hero ? `local:${assetPaths.hero}` : null, }; }; @@ -290,14 +355,17 @@ export function EditGameModal({ const { iconUrl, logoImageUrl, libraryHeroImageUrl } = prepareCustomGameAssets(game); - return window.electron.updateCustomGame( - game.shop, - game.objectId, - gameName.trim(), - iconUrl || undefined, - logoImageUrl || undefined, - libraryHeroImageUrl || undefined - ); + return window.electron.updateCustomGame({ + shop: game.shop, + objectId: game.objectId, + title: gameName.trim(), + iconUrl: iconUrl || undefined, + logoImageUrl: logoImageUrl || undefined, + libraryHeroImageUrl: libraryHeroImageUrl || undefined, + originalIconPath: originalAssetPaths.icon || undefined, + originalLogoPath: originalAssetPaths.logo || undefined, + originalHeroPath: originalAssetPaths.hero || undefined, + }); }; // Helper function to update non-custom game @@ -305,14 +373,17 @@ export function EditGameModal({ const { customIconUrl, customLogoImageUrl, customHeroImageUrl } = prepareNonCustomGameAssets(); - return window.electron.updateGameCustomAssets( - game.shop, - game.objectId, - gameName.trim(), + return window.electron.updateGameCustomAssets({ + shop: game.shop, + objectId: game.objectId, + title: gameName.trim(), customIconUrl, customLogoImageUrl, - customHeroImageUrl - ); + customHeroImageUrl, + customOriginalIconPath: originalAssetPaths.icon || undefined, + customOriginalLogoPath: originalAssetPaths.logo || undefined, + customOriginalHeroPath: originalAssetPaths.hero || undefined, + }); }; const handleUpdateGame = async () => { @@ -343,19 +414,24 @@ export function EditGameModal({ }; // Helper function to reset form to initial state - const resetFormToInitialState = (game: LibraryGame | Game) => { - setGameName(game.title || ""); + const resetFormToInitialState = useCallback( + (game: LibraryGame | Game) => { + setGameName(game.title || ""); - if (isCustomGame(game)) { - setCustomGameAssets(game); - // Clear default URLs for custom games - setDefaultIconUrl(null); - setDefaultLogoUrl(null); - setDefaultHeroUrl(null); - } else { - setNonCustomGameAssets(game as LibraryGame); - } - }; + if (isCustomGame(game)) { + setCustomGameAssets(game); + // Clear default URLs for custom games + setDefaultUrls({ + icon: null, + logo: null, + hero: null, + }); + } else { + setNonCustomGameAssets(game as LibraryGame); + } + }, + [setCustomGameAssets, setNonCustomGameAssets] + ); const handleClose = () => { if (!isUpdating && game) { @@ -378,6 +454,7 @@ export function EditGameModal({ const renderImageSection = (assetType: AssetType) => { const assetPath = getAssetPath(assetType); + const assetDisplayPath = getAssetDisplayPath(assetType); const defaultUrl = getDefaultUrl(assetType); const hasImage = assetPath || (game && !isCustomGame(game) && defaultUrl); const isDragOver = dragOverTarget === assetType; @@ -390,7 +467,7 @@ export function EditGameModal({
+ + + ) + } />
diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss index 8674b044..f86db399 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss @@ -1,6 +1,12 @@ @use "../../../scss/globals.scss"; .sidebar-section { + background-color: globals.$dark-background-color; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + overflow: hidden; + &__button { padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); display: flex; diff --git a/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx b/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx index 61c90389..9a29f150 100644 --- a/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/how-long-to-beat-section.tsx @@ -25,7 +25,7 @@ export function HowLongToBeatSection({ return `${value} ${t(durationTranslation[unit])}`; }; - if (!howLongToBeatData && !isLoading) return null; + if (!howLongToBeatData || !isLoading) return null; return ( diff --git a/src/renderer/src/pages/home/home.scss b/src/renderer/src/pages/home/home.scss index 497f074e..478e96a1 100644 --- a/src/renderer/src/pages/home/home.scss +++ b/src/renderer/src/pages/home/home.scss @@ -76,6 +76,15 @@ width: 24px; height: 24px; position: relative; + display: flex; + align-items: center; + justify-content: center; + } + + &__title-flame-icon { + width: 32px; + height: 32px; + object-fit: contain; } &__title { diff --git a/src/renderer/src/pages/home/home.tsx b/src/renderer/src/pages/home/home.tsx index e2f66283..0b762882 100644 --- a/src/renderer/src/pages/home/home.tsx +++ b/src/renderer/src/pages/home/home.tsx @@ -158,7 +158,7 @@ export default function Home() { Flame animation
)} diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index 8de16d3d..41b11ba3 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -5,7 +5,6 @@ import { useAppDispatch, useFormat } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; import { TelescopeIcon, ChevronRightIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; -import { UserGame } from "@types"; import { LockedProfile } from "./locked-profile"; import { ReportProfile } from "../report-profile/report-profile"; import { FriendsBox } from "./friends-box"; @@ -17,8 +16,6 @@ import { useSectionCollapse } from "@renderer/hooks/use-section-collapse"; import { motion, AnimatePresence } from "framer-motion"; import { sectionVariants, - gameCardVariants, - gameGridVariants, chevronVariants, GAME_STATS_ANIMATION_DURATION_IN_MS, } from "./profile-animations"; @@ -38,8 +35,6 @@ export function ProfileContent() { const [statsIndex, setStatsIndex] = useState(0); const [isAnimationRunning, setIsAnimationRunning] = useState(true); const [sortBy, setSortBy] = useState("playedRecently"); - const [prevLibraryGames, setPrevLibraryGames] = useState([]); - const [prevPinnedGames, setPrevPinnedGames] = useState([]); const statsAnimation = useRef(-1); const { toggleSection, isPinnedCollapsed } = useSectionCollapse(); @@ -92,27 +87,6 @@ export function ProfileContent() { const { numberFormatter } = useFormat(); - const gamesHaveChanged = ( - current: UserGame[], - previous: UserGame[] - ): boolean => { - if (current.length !== previous.length) return true; - return current.some( - (game, index) => game.objectId !== previous[index]?.objectId - ); - }; - - const shouldAnimateLibrary = gamesHaveChanged(libraryGames, prevLibraryGames); - const shouldAnimatePinned = gamesHaveChanged(pinnedGames, prevPinnedGames); - - useEffect(() => { - setPrevLibraryGames(libraryGames); - }, [libraryGames]); - - useEffect(() => { - setPrevPinnedGames(pinnedGames); - }, [pinnedGames]); - const usersAreFriends = useMemo(() => { return userProfile?.relation?.status === "ACCEPTED"; }, [userProfile]); @@ -192,57 +166,21 @@ export function ProfileContent() { exit="collapsed" layout > - - {shouldAnimatePinned ? ( - - {pinnedGames?.map((game, index) => ( - - - - ))} - - ) : ( - pinnedGames?.map((game) => ( -
  • - -
  • - )) - )} -
    +
      + {pinnedGames?.map((game) => ( +
    • + +
    • + ))} +
    )} @@ -262,54 +200,18 @@ export function ProfileContent() {
    - - {shouldAnimateLibrary ? ( - - {libraryGames?.map((game, index) => ( - - - - ))} - - ) : ( - libraryGames?.map((game) => ( -
  • - -
  • - )) - )} -
    +
      + {libraryGames?.map((game) => ( +
    • + +
    • + ))} +
    )} @@ -338,8 +240,6 @@ export function ProfileContent() { pinnedGames, isPinnedCollapsed, toggleSection, - shouldAnimateLibrary, - shouldAnimatePinned, sortBy, ]); diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index f072fdd5..5d0d7f2c 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -8,6 +8,7 @@ display: flex; transition: all ease 0.2s; cursor: grab; + container-type: inline-size; &:hover { transform: scale(1.05); @@ -86,32 +87,10 @@ top: 8px; right: 8px; display: flex; - gap: 6px; + gap: 4px; z-index: 2; } - &__favorite-icon { - color: rgba(255, 255, 255, 0.8); - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - border: solid 1px rgba(255, 255, 255, 0.15); - border-radius: 50%; - padding: 6px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - transition: all ease 0.2s; - - &:hover { - background: rgba(0, 0, 0, 0.5); - border-color: rgba(255, 255, 255, 0.25); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - } - } - &__pin-button { color: rgba(255, 255, 255, 0.8); background: rgba(0, 0, 0, 0.4); @@ -160,6 +139,27 @@ transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } + + &-long { + display: inline; + font-size: 12px; + } + + &-short { + display: none; + font-size: 12px; + } + + // When the card is narrow (less than 180px), show short format + @container (max-width: 140px) { + &-long { + display: none; + } + + &-short { + display: inline; + } + } } &__manual-playtime { color: globals.$warning-color; diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index 860c6758..eac0912d 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -13,7 +13,6 @@ import { ClockIcon, TrophyIcon, AlertFillIcon, - HeartFillIcon, PinIcon, PinSlashIcon, } from "@primer/octicons-react"; @@ -79,17 +78,22 @@ export function UserLibraryGameCard({ }; const formatPlayTime = useCallback( - (playTimeInSeconds = 0) => { + (playTimeInSeconds = 0, isShort = false) => { const minutes = playTimeInSeconds / 60; if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t("amount_minutes", { + return t(isShort ? "amount_minutes_short" : "amount_minutes", { amount: minutes.toFixed(0), }); } const hours = minutes / 60; - return t("amount_hours", { amount: numberFormatter.format(hours) }); + const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; + const hoursAmount = isShort + ? Math.floor(hours) + : numberFormatter.format(hours); + + return t(hoursKey, { amount: hoursAmount }); }, [numberFormatter, t] ); @@ -130,33 +134,26 @@ export function UserLibraryGameCard({ onClick={() => navigate(buildUserGameDetailsPath(game))} >
    - {(game.isFavorite || isMe) && ( + {isMe && (
    - {game.isFavorite && ( -
    - -
    - )} - {isMe && ( - - )} +
    )} - )} - {formatPlayTime(game.playTimeInSeconds)} - + + {formatPlayTime(game.playTimeInSeconds)} + + + {formatPlayTime(game.playTimeInSeconds, true)} + +
    {userProfile?.hasActiveSubscription && game.achievementCount > 0 && ( diff --git a/src/types/level.types.ts b/src/types/level.types.ts index 73fce370..8a6c56a0 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -38,6 +38,12 @@ export interface Game { customIconUrl?: string | null; customLogoImageUrl?: string | null; customHeroImageUrl?: string | null; + originalIconPath?: string | null; + originalLogoPath?: string | null; + originalHeroPath?: string | null; + customOriginalIconPath?: string | null; + customOriginalLogoPath?: string | null; + customOriginalHeroPath?: string | null; playTimeInMilliseconds: number; unsyncedDeltaPlayTimeInMilliseconds?: number; lastTimePlayed: Date | null;