Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-06 12:35:57 +03:00
parent 49127d64dd
commit b58be5612e
22 changed files with 2423 additions and 151 deletions

View file

@ -306,6 +306,7 @@
<string name="common_hour">Stunde</string>
<string name="common_import">Importieren</string>
<string name="common_in_progress">In Arbeit</string>
<string name="common_insufficient_balance">Unzureichende Mittel</string>
<string name="common_later">Später</string>
<string name="common_learn_more">Mehr erfahren</string>
<string name="common_left">%1$s übrig</string>
@ -421,6 +422,7 @@
<string name="custom_token_creation_error_token_already_exist_title">Token existiert bereits</string>
<string name="custom_token_creation_error_wrong_decimals">Dezimalzahl muss eine gültige Ganzzahl sein, bis zu %d</string>
<string name="custom_token_custom_derivation">Benutzerdefinierte Ableitung(derivation)</string>
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">Die Funktion \"Dynamische Adressen\" ist aktiviert. Benutzerdefinierte \\"change\\" und \\"index\\" sind nicht verfügbar.</string>
<string name="custom_token_custom_derivation_placeholder">E. g. m/00\'/0000\'/0\'/0/0</string>
<string name="custom_token_custom_derivation_title">Benutzerdefinierte Ableitung (Derivation) eingeben</string>
<string name="custom_token_decimals_input_title">Dezimalstellen</string>
@ -473,6 +475,19 @@
<string name="domain_receive_assets_onboarding_description">Das Senden von Vermögenswerten in anderen Netzwerken führt zu dauerhaftem Verlust.</string>
<string name="domain_receive_assets_onboarding_network_name">%s Netzwerk</string>
<string name="domain_receive_assets_onboarding_title">Sende Geld nur mit</string>
<string name="dynamic_addresses">Dynamische Adressen</string>
<string name="dynamic_addresses_custom_token_error_on_addition">Dynamische Adressen ist aktiviert. Die benutzerdefinierten Funktionen \"Ändern\" und \"Index\" sind nicht verfügbar.</string>
<string name="dynamic_addresses_enabled_toast_title">Einige Adressen fehlen</string>
<string name="dynamic_addresses_enter_features_privacy_description">Verwenden Sie für jede Transaktion eine neue Adresse, um die Rückverfolgbarkeit zu verringern und den Datenschutz in der Kette zu verbessern.</string>
<string name="dynamic_addresses_enter_features_privacy_title">Verbesserter Datenschutz</string>
<string name="dynamic_addresses_enter_features_receving_description">Einfacher Geldempfang in UTXO-basierten Netzwerken mit automatischer Adressgenerierung - keine manuelle Adressverwaltung erforderlich.</string>
<string name="dynamic_addresses_enter_features_receving_title">Nahtloser Empfang</string>
<string name="dynamic_addresses_enter_main_button_title">Dynamische Adressen aktivieren</string>
<string name="dynamic_addresses_enter_subtitle">Bei dynamischen Adressen wird jedes Mal eine neue Adresse erstellt, um den Datenschutz zu gewährleisten - Ihr Gesamtguthaben bleibt gleich. </string>
<string name="dynamic_addresses_error_has_custom_token_description">Dynamische Adressen können nicht aktiviert werden, da einige benutzerdefinierte Adressen/Tokens einen geänderten Ableitungspfad verwenden, der die erforderlichen Kriterien nicht erfüllt.</string>
<string name="dynamic_addresses_error_has_custom_token_title">Nicht verfügbar</string>
<string name="dynamic_addresses_error_service_unavailable_description">Wir können im Moment keine Verbindung zum Provider herstellen. Bitte versuchen Sie es später noch einmal.</string>
<string name="dynamic_addresses_error_service_unavailable_title">Der Dienst ist nicht verfügbar. Bitte versuchen Sie es erneut.</string>
<string name="earn_best_opportunities">Beste Gelegenheiten</string>
<string name="earn_clear_filter">Filter löschen</string>
<string name="earn_empty">Die Liste ist vorübergehend leer, da sie gerade aktualisiert wird. Schauen Sie in Kürze wieder rein.</string>
@ -749,12 +764,18 @@
<string name="markets_insights_info_description_message">Die Daten dieses Abschnitts stammen aus den folgenden Netzwerken: %s</string>
<string name="markets_loading_error_title">Die Daten konnten nicht geladen werden...</string>
<string name="markets_loading_no_data_title">Keine Daten</string>
<string name="markets_portfolio_block_add_token_title">**Hinzufügen zu Ihrem Portfolio**, um mit dem Kauf, Tausch oder Erhalt dieses Vermögenswerts zu beginnen</string>
<string name="markets_portfolio_block_subtitle">Zum Portfolio hinzufügen</string>
<string name="markets_pulse_common_title">Marktimpuls</string>
<string name="markets_quick_actions">Schnelle Aktionen</string>
<string name="markets_search_clear_all_hints">Alles löschen</string>
<string name="markets_search_header_title">Markt durchsuchen</string>
<string name="markets_search_hint_header">Neueste</string>
<string name="markets_search_portfolio_header">In Ihrem Portfolio</string>
<string name="markets_search_result_title">Ergebnis</string>
<string name="markets_search_see_tokens_under_100k">Token unter 100k USD Marktkapitalisierung anzeigen</string>
<string name="markets_search_show_tokens">Token anzeigen</string>
<string name="markets_search_title_placeholder">Krypto, Nachrichten und mehr</string>
<string name="markets_search_token_no_result_title">Kein Ergebnis</string>
<string name="markets_select_network">Netzwerk auswählen</string>
<string name="markets_select_wallet">Wallet auswählen</string>
@ -827,9 +848,11 @@
<string name="markets_token_details_market_rating_description">Position im Krypto-Rating zwischen allen Coins basierend auf der Marktkapitalisierung</string>
<string name="markets_token_details_market_rating_full">Marktposition</string>
<string name="markets_token_details_max_supply">Maximale Versorgung</string>
<string name="markets_token_details_max_supply_and_circulation_full">Zirkulation und maximale Versorgung</string>
<string name="markets_token_details_max_supply_description">Die maximale Anzahl von Coins oder Tokens, die jemals für eine bestimmte Kryptowährung existieren können</string>
<string name="markets_token_details_max_supply_full">Maximale Versorgung</string>
<string name="markets_token_details_metrics">Metriken</string>
<string name="markets_token_details_metrics_no_limited">Nicht begrenzt</string>
<string name="markets_token_details_official_links">Offizielle Links</string>
<string name="markets_token_details_price_performance">Preisleistung</string>
<string name="markets_token_details_repository">Aufbewahrungsort</string>
@ -1104,10 +1127,14 @@
<string name="qr_scanner_camera_denied_settings_button">Einstellungen</string>
<string name="qr_scanner_camera_denied_text">Du hast keinen Zugriff auf deine Kamera gewährt</string>
<string name="qr_scanner_camera_denied_title">Kamerazugriff verweigert</string>
<string name="qr_scanner_error_token_not_added_message">Der angeforderte Token wurde Ihrer Wallet nicht hinzugefügt. Bitte fügen Sie ihn hinzu und versuchen Sie es erneut.</string>
<string name="qr_scanner_error_token_not_added_title">Token nicht hinzugefügt</string>
<string name="qr_scanner_error_unrecognized_message">Dieser QR-Code konnte leider nicht erkannt werden.</string>
<string name="qr_scanner_error_unrecognized_title">Unerkannter QR-Code</string>
<string name="qr_scanner_error_unsupported_network_message">Dieses Netzwerk wird von keinem der von Ihnen hinzugefügten Token unterstützt. Fügen Sie einen unterstützten Token hinzu, um Krypto zu senden.</string>
<string name="qr_scanner_error_unsupported_network_title">Keine unterstützten Token gefunden</string>
<string name="qr_scanner_warning_unknown_parameters_message">Dieser QR-Code enthält Parameter, die nicht erkannt werden: %s. Einige Zahlungsdetails können verloren gehen, wenn Sie fortfahren.</string>
<string name="qr_scanner_warning_unknown_parameters_title">Unbekannte Parameter</string>
<string name="receive_bottom_sheet_no_memo_required_message">Kein Memo erforderlich</string>
<string name="receive_bottom_sheet_warning_message">%1$s ( %2$s ) im %3$s Netzwerk</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s im %2$s Netzwerk</string>
@ -1222,6 +1249,10 @@
<string name="send_memo">Memo: %s</string>
<string name="send_memo_destination_tag_error">Ungültiges Memo</string>
<string name="send_network_fee_warning_title">Abdeckung der Netzgebühren</string>
<plurals name="send_network_selection_hidden_tokens">
<item quantity="one">%d Token ist mit dieser Adresse nicht kompatibel</item>
<item quantity="other">%d -Tokens sind mit dieser Adresse nicht kompatibel</item>
</plurals>
<string name="send_nonce">Nonce</string>
<string name="send_nonce_footer">Eindeutige Nummer für jede Transaktion. Verwende diese Nummer, um eine ausstehende Transaktion erneut zu senden oder abzubrechen.</string>
<string name="send_nonce_hint">Nonce eingeben…</string>
@ -1489,14 +1520,18 @@
<string name="swapping_fee_estimation_error_text">Fehler bei der Gebührenschätzung. Bitte sende dein Feedback an den Support.</string>
<string name="swapping_from_title">Du wechselst</string>
<string name="swapping_high_price_impact_description">Der Tausch dieser Menge ausgewählter Token hat erhebliche Auswirkungen auf den Preis und verringert dein Ergebnis.</string>
<string name="swapping_high_price_impact_text">Aufgrund geringer Liquidität erhalten Sie möglicherweise deutlich weniger. Versuchen Sie es mit einem kleineren Betrag oder einem anderen Anbieter.</string>
<string name="swapping_high_price_impact_title">Hoher Einfluss auf den Preis</string>
<string name="swapping_insufficient_funds">Unzureichende Mittel</string>
<string name="swapping_insufficient_funds_description">Nicht genügend Geldmittel, um diese Transaktion abzuschließen. Verringern Sie den zu erhaltenden Betrag oder fügen Sie weitere Mittel hinzu.</string>
<string name="swapping_permission_header">Erlaubnis erteilen</string>
<string name="swapping_swap_action">Tauschen</string>
<string name="swapping_swap_action_in_progress">Tauschen...</string>
<string name="swapping_to_title">Du erhältst</string>
<string name="swapping_token_list_title">Token auswählen</string>
<string name="swapping_token_not_available">Nicht verfügbar</string>
<string name="swapping_trade_too_large_text">Nicht genug Liquidität für diesen Handel. Reduzieren Sie den Betrag oder wählen Sie einen anderen Anbieter.</string>
<string name="swapping_trade_too_large_title">Handel zu groß</string>
<string name="tangem_pay_beta_notification_subtitle">Wir freuen uns über Ihr Feedback</string>
<string name="tangem_pay_beta_notification_title">Tangem Pay jetzt in der Beta</string>
<string name="tangem_pay_card_frozen">Karte eingefroren</string>
@ -1567,7 +1602,7 @@
<string name="tangempay_card_details_receive_error_title">Empfangen ist jetzt nicht verfügbar</string>
<string name="tangempay_card_details_reveal_text">Aufdecken</string>
<string name="tangempay_card_details_show_details">Details anzeigen</string>
<string name="tangempay_card_details_swap_description">Tausche beliebige Vermögenswerte in Deinem Portfolio gegen eine Karte.</string>
<string name="tangempay_card_details_swap_description">Tausche beliebige Vermögenswerte in Deinem Portfolio für deine Karte.</string>
<string name="tangempay_card_details_title">Kartendetails</string>
<string name="tangempay_card_details_unfreeze_card">Karte entsperren</string>
<string name="tangempay_card_details_view_pin_code_description">Komm zurück zur App, falls du es vergisst.</string>
@ -1657,6 +1692,7 @@
<string name="token_details_token_type_subtitle_no_standard">Token in %%image%% %1$s Netzwerk</string>
<string name="token_details_unable_hide_alert_message">Der %1$s (%2$s) Token ist die Hauptwährung im %3$s Netzwerk und kann nicht versteckt werden, solange du andere Token dieses Netzwerks in der Liste aktiv hast.</string>
<string name="token_details_unable_hide_alert_title">%s kann nicht ausgeblendet werden</string>
<string name="token_market_metrics_no_data">N / A</string>
<string name="token_receive_show_qr_code_title">QR-Code anzeigen</string>
<string name="token_swap_changelly_promotion_message">Tausche diesen Token gegen einen anderen zu %1$s Servicegebühren von Februar %2$s-%3$s.</string>
<string name="token_swap_changelly_promotion_title">Tausche mit Changelly, %s Gebühren</string>
@ -1699,6 +1735,9 @@
<string name="universal_error">Es ist ein Fehler aufgetreten. Fehlercode: %s. Bitte kontaktiere unseren Support.</string>
<string name="unlock_wallet_description_full">Verwende %s oder scanne eine Karte oder Ring, um Zugriff auf deine Wallet zu erhalten.</string>
<string name="unsupported_wc_version">Verbindung fehlgeschlagen: Diese dApp verwendet Wallet Connect Version 1.0, die nicht unterstützt wird. Bitte stelle sicher, dass die dApp Wallet Connect Version 2.0 unterstützt, um eine erfolgreiche Verbindung herzustellen.</string>
<string name="update_approval_permission_fee_note">Die vorherige Genehmigung wird widerrufen und eine neue erteilt. Das Netzwerk erhebt eine Token-Genehmigungsgebühr für jede dieser Aktionen. Als Beweis für den Widerruf wird in der Historie eine Transaktion mit einem Betrag von Null angezeigt.</string>
<string name="update_approval_permission_subtitle">Die Transaktion übersteigt den zuvor genehmigten Betrag.\nErlaubnis zum Fortfahren aktualisieren</string>
<string name="update_approval_permission_title">Erlaubnis zur Aktualisierung</string>
<string name="upgrade_to_hardware_wallet_button_title">Upgrade auf Hardware-Wallet</string>
<string name="user_push_notification_agreement_argument_one">Bleib auf dem Laufenden mit den neuesten Funktionen und Neuigkeiten</string>
<string name="user_push_notification_agreement_argument_one_subtitle">Echtzeit-Warnungen für Transaktionen, Umtausch und wichtige Aktualisierungen.</string>

View file

@ -161,7 +161,7 @@
<string name="biometric_updated_warning_title">Se requiere atención</string>
<string name="bitcoin_promo_activation_error">Se produjo un error al procesar tu código promocional. Inténtalo de nuevo más tarde.</string>
<string name="bitcoin_promo_activation_error_title">Error de activación</string>
<string name="bitcoin_promo_activation_success">Tu código promocional se activó correctamente. Una recompensa se acreditará en tu cuenta dentro de 14 días.</string>
<string name="bitcoin_promo_activation_success">Su código promocional se activó correctamente. Se acreditará la recompensa en su cuenta dentro de 14 días.</string>
<string name="bitcoin_promo_activation_success_title">Código promocional activado</string>
<string name="bitcoin_promo_already_activated">Este código promocional ya ha sido utilizado y no puede activarse de nuevo.</string>
<string name="bitcoin_promo_already_activated_title">Código no disponible</string>
@ -306,6 +306,7 @@
<string name="common_hour">hora</string>
<string name="common_import">Importe</string>
<string name="common_in_progress">En progreso</string>
<string name="common_insufficient_balance">Fondos insuficientes</string>
<string name="common_later">Más tarde</string>
<string name="common_learn_more">Más información</string>
<string name="common_left">%1$s quedan</string>
@ -326,6 +327,7 @@
<string name="common_nft">NFT</string>
<string name="common_no">No</string>
<string name="common_no_address">Ninguna dirección</string>
<string name="common_no_results">Sin resultados</string>
<string name="common_not_added">No agregadas</string>
<string name="common_not_available">No disponible</string>
<string name="common_not_now">Ahora no</string>
@ -384,6 +386,7 @@
<string name="common_to">A</string>
<string name="common_to_wallet_name">A %s</string>
<string name="common_today">Hoy</string>
<string name="common_token_send">Token para enviar</string>
<plurals name="common_tokens_count">
<item quantity="one">%d token</item>
<item quantity="other">%d tokens</item>
@ -396,6 +399,7 @@
<string name="common_understand">Entiendo</string>
<string name="common_understand_continue">Entiendo, continuar</string>
<string name="common_unknown_error">Hubo un error. Por favor inténtelo de nuevo.</string>
<string name="common_unlock">Desbloquear</string>
<string name="common_unreachable">Inaccesible</string>
<string name="common_unstake">Termine el staking</string>
<string name="common_utxo_validate_withdrawal_message_warning">Debido a limitaciones sobre %1$s, solo %2$d UTXO pueden caber en una sola transacción. Esto significa que solo puedes enviar %3$s o menos. Debe reducir la cantidad.</string>
@ -405,7 +409,7 @@
<string name="common_week">semana</string>
<string name="common_with">con</string>
<string name="common_yes"></string>
<string name="common_yield_mode">Modo de rendimiento</string>
<string name="common_yield_mode">Modo de Rendimiento</string>
<string name="contract_address_copied_message">Dirección de contrato copiada</string>
<string name="currency_subtitle_expanded">Redes disponibles</string>
<string name="custom_token_another_account_dialog_description">La derivación de su token coincide con la derivación de %1$s. Su token se añadirá a esta cuenta.</string>
@ -418,6 +422,7 @@
<string name="custom_token_creation_error_token_already_exist_title">Este token ya existe</string>
<string name="custom_token_creation_error_wrong_decimals">Los decimales deben ser un número entero válido, hasta %d</string>
<string name="custom_token_custom_derivation">Derivación personalizada</string>
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">La opción Direcciones dinámicas está habilitada. Las opciones personalizadas \\"cambiar\\" e \\"índice\\" no están disponibles.</string>
<string name="custom_token_custom_derivation_placeholder">Por ejemplo m/00\'/0000\'/0\'/0/0</string>
<string name="custom_token_custom_derivation_title">Introduzca una derivación personalizada</string>
<string name="custom_token_decimals_input_title">Decimales</string>
@ -460,7 +465,7 @@
<string name="details_row_title_signed_hashes">Firmado</string>
<string name="details_send_feedback">Enviar comentarios</string>
<string name="details_title">Detalles</string>
<string name="details_wallets_section_description">Puedes tener solo una billetera móvil a la vez. Puede actualizarse a una billetera fría Tangem o usarse junto con una nueva billetera fría.</string>
<string name="details_wallets_section_description">Puede tener solo una billetera móvil. Puede actualizarse a una billetera fría Tangem o añadir una billetera de hardware nueva.</string>
<string name="disclaimer_error_loading">Compruebe su conexión a internet o cambia a una red diferente</string>
<string name="disclaimer_title">Condiciones de uso</string>
<string name="domain_receive_assets_default_address">Dirección por defecto</string>
@ -470,6 +475,19 @@
<string name="domain_receive_assets_onboarding_description">Enviar activos a otras redes resultará en una pérdida permanente.</string>
<string name="domain_receive_assets_onboarding_network_name">Red de %s</string>
<string name="domain_receive_assets_onboarding_title">Envíe fondos utilizando solo</string>
<string name="dynamic_addresses">Direcciones dinámicas</string>
<string name="dynamic_addresses_custom_token_error_on_addition">La opción Direcciones dinámicas está habilitada. Las opciones personalizadas \"cambiar\" e \"índice\" no están disponibles.</string>
<string name="dynamic_addresses_enabled_toast_title">Direcciones dinámicas habilitadas</string>
<string name="dynamic_addresses_enter_features_privacy_description">Utilice una nueva dirección para cada transacción para reducir la trazabilidad y mejorar la privacidad on-chain.</string>
<string name="dynamic_addresses_enter_features_privacy_title">Privacidad mejorada</string>
<string name="dynamic_addresses_enter_features_receving_description">Reciba fondos fácilmente en redes basadas en UTXO con generación automática de direcciones, sin necesidad de gestión manual de direcciones.</string>
<string name="dynamic_addresses_enter_features_receving_title">Recepción sin problemas</string>
<string name="dynamic_addresses_enter_main_button_title">Habilitar direcciones dinámicas</string>
<string name="dynamic_addresses_enter_subtitle">Las direcciones dinámicas crean una nueva cada vez para mayor privacidad - su saldo total no varía. </string>
<string name="dynamic_addresses_error_has_custom_token_description">Las direcciones dinámicas no pueden activarse porque algunas direcciones/tokens personalizadas utilizan una ruta de derivación modificada, que no cumple los criterios requeridos.</string>
<string name="dynamic_addresses_error_has_custom_token_title">Direcciones dinámicas no disponibles</string>
<string name="dynamic_addresses_error_service_unavailable_description">No podemos conectar con el proveedor en este momento. Vuelva a intentarlo más tarde.</string>
<string name="dynamic_addresses_error_service_unavailable_title">Servicio no disponible. Por favor, inténtelo de nuevo.</string>
<string name="earn_best_opportunities">Las mejores oportunidades</string>
<string name="earn_clear_filter">Limpiar filtro</string>
<string name="earn_empty">La lista está temporalmente vacía porque se está actualizando. Vuelva a consultarla en un momento.</string>
@ -478,9 +496,9 @@
<string name="earn_filter_by">Filtrar por</string>
<string name="earn_filter_my_networks">Mis redes</string>
<string name="earn_filter_networks">Redes</string>
<string name="earn_mostly_used">Mayormente usado</string>
<string name="earn_mostly_used">Popular</string>
<string name="earn_no_results">Sin resultados</string>
<string name="earn_title">Ganar</string>
<string name="earn_title">Modo Staking &amp; Rendimiento</string>
<string name="email_preface_wc_error">Hola equipo de soporte, he encontrado un error con el código: %s</string>
<string name="email_subject_wc_error">Error de WalletConnect</string>
<string name="error_wrong_wallet_tapped">Ha usado una tarjeta de otra billetera. Toque la tarjeta asociada con esta billetera</string>
@ -653,7 +671,7 @@
<string name="hw_remove_wallet_confirmation_title">¿Está seguro que desea hacer esto?</string>
<string name="hw_remove_wallet_nav_title">Olvidar billetera</string>
<string name="hw_remove_wallet_notification_action_backup_go">Ir a copia de seguridad</string>
<string name="hw_remove_wallet_notification_action_backup_view">Ver copia de seguridad</string>
<string name="hw_remove_wallet_notification_action_backup_view">Ver frase de recuperación</string>
<string name="hw_remove_wallet_notification_action_forget">Olvidar la billetera</string>
<string name="hw_remove_wallet_notification_action_forget_anyway">Olvidar de todos modos</string>
<string name="hw_remove_wallet_notification_description_has_backup">Esta billetera tiene una copia de seguridad. Asegúrese de poder recuperarla antes de olvidarla.</string>
@ -689,6 +707,9 @@
<string name="initial_message_scan_header">Acerque para escanear</string>
<string name="initial_message_sign_header">Toque para firmar</string>
<string name="initial_message_tap_header">Coloque la tarjeta o el anillo</string>
<string name="initial_wallet_sync_banner_description">Su billetera está sincronizada y lista. \n Faltan algunos tokens?</string>
<string name="initial_wallet_sync_banner_title">Billetera importada correctamente</string>
<string name="initial_wallet_sync_restore_progress">Restaurando %d%%</string>
<string name="key_invalidated_warning_description">Ha actualizado la biometría, escanee su tarjeta o anillo para entrar</string>
<string name="koinos_insufficient_balance_to_send_koin_description">Su saldo debe ser mayor que el valor de la tarifa para hacer una transferencia</string>
<string name="koinos_insufficient_balance_to_send_koin_title">Saldo insuficiente</string>
@ -700,6 +721,7 @@
<string name="koinos_mana_level_title">Nivel de Mana</string>
<string name="main_empty_tokens_list_message">Para hacer un seguimiento de sus criptomonedas y transacciones, agregue tokens</string>
<string name="main_manage_tokens">Gestionar tokens</string>
<string name="main_qr_scan_hint">Escanee el código QR para enviar fondos o conectarse a una aplicación</string>
<string name="main_scan_card_warning_view_subtitle">Para acceder a todas las redes necesita escanear la tarjeta</string>
<string name="main_scan_card_warning_view_title">Escanee su tarjeta o anillo</string>
<string name="main_swap_changelly_promotion_message">Disfrute de %1$s comisiones de servicio en los swaps a través de Changelly a partir de febrero %2$s-%3$s</string>
@ -734,15 +756,22 @@
<string name="markets_apy_placeholder">APY %s</string>
<string name="markets_common_my_portfolio">Mi portafolio</string>
<string name="markets_common_title">Mercado</string>
<string name="markets_earn_common_title">Gane con Tangem</string>
<string name="markets_earn_common_title">Modo Staking &amp; Rendimiento</string>
<string name="markets_generate_addresses_notification">Para generar direcciones para las redes seleccionadas, debe escanear su tarjeta Tangem</string>
<string name="markets_hint">Para agregar tokens, abra esta página o pulse sobre la barra de búsqueda</string>
<string name="markets_hint_part_one">Deslice el dedo hacia arriba para explorar el mercado</string>
<string name="markets_hint_part_two">Descubre nuevas gemas ocultas</string>
<string name="markets_insights_info_description_message">Los datos de este apartado proceden de las siguientes redes: %s</string>
<string name="markets_loading_error_title">No se pueden cargar los datos…</string>
<string name="markets_loading_no_data_title">Sin datos</string>
<string name="markets_portfolio_block_add_token_title">**Añadir a su portafolio** para empezar a comprar, intercambiar o recibir este activo</string>
<string name="markets_portfolio_block_subtitle">En su portafolio</string>
<string name="markets_pulse_common_title">Análisis del Mercado</string>
<string name="markets_quick_actions">Acciones rápidas</string>
<string name="markets_search_clear_all_hints">Borrar todo</string>
<string name="markets_search_header_title">Buscar tokens</string>
<string name="markets_search_hint_header">Recientes</string>
<string name="markets_search_portfolio_header">En su portafolio</string>
<string name="markets_search_result_title">Resultado</string>
<string name="markets_search_see_tokens_under_100k">Ver tokens con marketcap inferior a 100.000$</string>
<string name="markets_search_show_tokens">Mostrar tokens</string>
@ -799,6 +828,10 @@
<string name="markets_token_details_holders">Titulares</string>
<string name="markets_token_details_holders_description">El cambio en el número de poseedores de tokens dentro de un período de tiempo específico</string>
<string name="markets_token_details_holders_full">Titulares</string>
<string name="markets_token_details_insight_day_timeline">D</string>
<string name="markets_token_details_insight_month_timeline">M</string>
<string name="markets_token_details_insight_week_timeline">S</string>
<string name="markets_token_details_insight_year_timeline">A</string>
<string name="markets_token_details_insights">Ideas</string>
<string name="markets_token_details_links">Enlaces</string>
<string name="markets_token_details_liquidity">Liquidez</string>
@ -814,9 +847,11 @@
<string name="markets_token_details_market_rating_description">Posición en la clasificación de criptomonedas entre todas las monedas según la capitalización de mercado</string>
<string name="markets_token_details_market_rating_full">Evaluación de mercado</string>
<string name="markets_token_details_max_supply">Suministro máximo</string>
<string name="markets_token_details_max_supply_and_circulation_full">Suministro circulante y máximo</string>
<string name="markets_token_details_max_supply_description">La cantidad máxima de monedas o tokens que pueden existir para una criptomoneda en particular</string>
<string name="markets_token_details_max_supply_full">Suministro máximo</string>
<string name="markets_token_details_metrics">Métrica</string>
<string name="markets_token_details_metrics_no_limited">Sin limitación</string>
<string name="markets_token_details_official_links">Enlaces oficiales</string>
<string name="markets_token_details_price_performance">Rendimiento de precios</string>
<string name="markets_token_details_repository">Repositorio</string>
@ -826,9 +861,11 @@
<string name="markets_token_details_total_supply">Suministro total</string>
<string name="markets_token_details_total_supply_description">La cantidad máxima de monedas o tokens que pueden existir para una criptomoneda en particular</string>
<string name="markets_token_details_total_supply_full">Suministro total</string>
<string name="markets_token_details_trading_interval">24h</string>
<string name="markets_token_details_trading_volume">Volumen de operaciones (24 horas)</string>
<string name="markets_token_details_trading_volume_24h_description">La cantidad total de una criptomoneda que se ha negociado en las últimas 24 horas, lo que indica el nivel de actividad y liquidez en el mercado.</string>
<string name="markets_token_details_trading_volume_full">Volumen de operaciones (24 horas)</string>
<string name="markets_token_details_valuation_value_in_total">%s en total</string>
<string name="markets_token_details_volume">Volumen</string>
<string name="markets_tooltip_message">Tire hacia arriba o toque la barra de búsqueda para agregar tokens directamente desde el mercado</string>
<string name="markets_tooltip_title">Agregar tokens</string>
@ -852,6 +889,7 @@
<string name="news_related_tokens">Tokens relacionados</string>
<string name="news_sources">Noticias relacionadas</string>
<string name="news_stay_in_the_loop">Manténgase informado</string>
<string name="news_trending_score">Puntuación de tendencia</string>
<string name="nfc_error_unavailable">NFC no está disponible en su dispositivo</string>
<string name="nft_about_title">Acerca de NFT</string>
<string name="nft_asset">Activo NFT</string>
@ -909,8 +947,8 @@
<string name="notification_black_friday_text">Obtén $10 en BTC con cada billetera \n ¡Date prisa!</string>
<string name="notification_black_friday_title">Black Friday: hasta 30% DESCUENTO</string>
<string name="notification_one_plus_one_button">Vamos</string>
<string name="notification_one_plus_one_text">Crea el par perfecto de packs de Tangem. Por tiempo limitado.</string>
<string name="notification_one_plus_one_title">1+1: Compra una billetera y obtén un 50% dto. en la segunda</string>
<string name="notification_one_plus_one_text">¡Tiempo limitado!</string>
<string name="notification_one_plus_one_title">1+1: Compra una billetera y obtenga un 50% dto. en la segunda</string>
<string name="notification_referral_promo_button">Únase ahora</string>
<string name="notification_referral_promo_text">Comparta su código y gane 5 USDT por venta. Su amigo obtiene un 10% de descuento.</string>
<string name="notification_referral_promo_title">¡Obtenga RECOMPENSAS por cada amigo!</string>
@ -1088,6 +1126,14 @@
<string name="qr_scanner_camera_denied_settings_button">Ajustes</string>
<string name="qr_scanner_camera_denied_text">No ha dado acceso a su cámara</string>
<string name="qr_scanner_camera_denied_title">Acceso a la cámara denegado</string>
<string name="qr_scanner_error_token_not_added_message">El token solicitado no se ha añadido a su billetera. Por favor, añádalo e inténtelo de nuevo.</string>
<string name="qr_scanner_error_token_not_added_title">Token no añadido</string>
<string name="qr_scanner_error_unrecognized_message">Lo sentimos, no se ha podido reconocer este código QR.</string>
<string name="qr_scanner_error_unrecognized_title">Código QR no reconocido</string>
<string name="qr_scanner_error_unsupported_network_message">Esta red no es compatible con ninguno de sus tokens añadidos. Añada un token compatible para enviar cripto.</string>
<string name="qr_scanner_error_unsupported_network_title">No se encontraron tokens compatibles</string>
<string name="qr_scanner_warning_unknown_parameters_message">Este código QR contiene parámetros que no son reconocidos: %s. Si continúa, es posible que se pierdan algunos detalles de pago.</string>
<string name="qr_scanner_warning_unknown_parameters_title">Parámetros desconocidos</string>
<string name="receive_bottom_sheet_no_memo_required_message">No se requiere nota</string>
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) en la red %3$s</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s en la red %2$s</string>
@ -1164,7 +1210,7 @@
<string name="send_alert_fee_too_low_text">Ha especificado una comisión inferior a la cantidad recomendada, lo que podría provocar un retraso en su transacción. ¿Continuar?</string>
<string name="send_alert_transaction_failed_text">Razón: %1$s\nCódigo: %2$s</string>
<string name="send_alert_transaction_failed_title">La transacción está incompleta</string>
<string name="send_amount_convert_to_another_token">Convertir a otro token</string>
<string name="send_amount_convert_to_another_token">Cambiar a otro token o red</string>
<string name="send_amount_label">Montante</string>
<string name="send_amount_receive_token_subtitle">Se enviará al destinatario</string>
<string name="send_bitcoin_custom_fee_footer">Puede establecer su tarifa de transacción ajustando el valor en el campo Satoshi por vByte.</string>
@ -1202,6 +1248,10 @@
<string name="send_memo">Memo: %s</string>
<string name="send_memo_destination_tag_error">Memo no válido</string>
<string name="send_network_fee_warning_title">Cobertura de tarifa de red</string>
<plurals name="send_network_selection_hidden_tokens">
<item quantity="one">El token %d no es compatible con esta dirección</item>
<item quantity="other">Los tokens %d no son compatibles con esta dirección</item>
</plurals>
<string name="send_nonce">Nonce</string>
<string name="send_nonce_footer">Número único para cada transacción. Utilícelo para reenviar o cancelar una transacción pendiente.</string>
<string name="send_nonce_hint">Introduzca nonce...</string>
@ -1229,6 +1279,11 @@
<string name="send_notification_transaction_limit_title">Limitación de transacciones</string>
<string name="send_optional_field">Opcional</string>
<string name="send_qrcode_scan_info">Por favor, alinee su código QR con el cuadrado para escanearlo. Asegúrese de escanear la dirección de la red %s.</string>
<string name="send_rate_fixed_info_description">Al utilizar un tipo de cambio fijo, el importe que recibe queda garantizado en el momento de la operación. Esto le protege de las fluctuaciones de precio durante la transacción.</string>
<string name="send_rate_fixed_info_title">Tasa fija</string>
<string name="send_rate_floating_info_description">Un tipo variable significa que la cantidad final que recibe puede variar ligeramente en función de las condiciones del mercado entre el momento en que inicia y finaliza el swap.</string>
<string name="send_rate_floating_info_title">Tasa flotante</string>
<string name="send_rate_is_fixed">La tasa es fija</string>
<string name="send_recent_transactions">Reciente</string>
<string name="send_recipient">Destinatario</string>
<string name="send_recipient_address_error">No es una dirección válida</string>
@ -1272,6 +1327,7 @@
<string name="send_with_swap_recipient_get_amount">El destinatario recibe %s</string>
<string name="send_with_swap_remove_convert_alert_message">¿Seguro que desea cancelar la conversión? Se borrarán sus datos anteriores.</string>
<string name="send_with_swap_remove_convert_alert_title">Eliminar conversión</string>
<string name="send_with_swap_something_went_wrong">Algo salió mal. Inténtelo de nuevo.</string>
<string name="send_with_swap_title">Enviar con swap</string>
<string name="sent_transaction_sent_title">Transacción enviada</string>
<string name="settings_card_settings_footer">Escanee la tarjeta/anillo que quiere configurar</string>
@ -1463,14 +1519,18 @@
<string name="swapping_fee_estimation_error_text">Error en la estimación de la tarifa. Envíe sus comentarios al servicio de asistencia.</string>
<string name="swapping_from_title">Usted intercambia</string>
<string name="swapping_high_price_impact_description">Hacer un intercambio de esta cantidad del token seleccionado causará un impacto significativo en el precio y reducirá su resultado.</string>
<string name="swapping_high_price_impact_text">Es posible que reciba una cantidad significativamente menor debido a la baja liquidez. Pruebe con una cantidad menor o con otro proveedor.</string>
<string name="swapping_high_price_impact_title">Alto impacto en los precios</string>
<string name="swapping_insufficient_funds">Fondos insuficientes</string>
<string name="swapping_insufficient_funds_description">No hay fondos suficientes para completar esta transacción. Reduzca el importe a recibir o añada más fondos.</string>
<string name="swapping_permission_header">Dar autorización</string>
<string name="swapping_swap_action">Intercambiar</string>
<string name="swapping_swap_action_in_progress">Intercambiando...</string>
<string name="swapping_to_title">Usted recibe</string>
<string name="swapping_token_list_title">Elige token</string>
<string name="swapping_token_not_available">no disponible</string>
<string name="swapping_trade_too_large_text">No hay liquidez suficiente para esta operación. Reduzca el importe o elija otro proveedor.</string>
<string name="swapping_trade_too_large_title">Operación demasiado grande</string>
<string name="tangem_pay_beta_notification_subtitle">Nos encantaría recibir tus comentarios</string>
<string name="tangem_pay_beta_notification_title">Tangem Pay ya está en beta</string>
<string name="tangem_pay_card_frozen">Tarjeta congelada</string>
@ -1509,6 +1569,7 @@
<string name="tangempay_cancel_kyc">Ocultar verificación de la pantalla</string>
<string name="tangempay_card_details_add_funds">Agregar fondos</string>
<string name="tangempay_card_details_add_funds_subtitle">Opciones de recarga</string>
<string name="tangempay_card_details_add_to_wallet_button_text">Añadir a Google Wallet</string>
<string name="tangempay_card_details_card_number">Número de tarjeta</string>
<string name="tangempay_card_details_change_pin">Modificar PIN</string>
<string name="tangempay_card_details_change_pin_success_description">La tarjeta está completamente lista para pagos.</string>
@ -1596,7 +1657,7 @@
<string name="tangempay_sync_needed">Sesión expirada</string>
<string name="tangempay_sync_needed_restore_access">Restablecer acceso</string>
<string name="tangempay_tangem_visa_card">Usa USDC para pagos cotidianos</string>
<string name="tangempay_temporarily_unavailable">Tangem Pay temporalmente no disponible</string>
<string name="tangempay_temporarily_unavailable">Tangem Pay no está disponible temporalmente.</string>
<string name="tangempay_title">Tangem Pay</string>
<string name="tangempay_use_tangem_device_to_restore_payment_account">Haga clic en el botón de abajo para restaurar el acceso</string>
<string name="tangempay_withdrawal_note_description">Tu saldo USDC Polygon on-chain difiere de tu saldo de tarjeta y se actualiza dentro de 2 días hábiles después de una compra. Los fondos de compras reembolsadas no se devolverán a tu saldo on-chain ni estarán disponibles para retiro, pero permanecerán en tu saldo de tarjeta para compras.</string>
@ -1630,6 +1691,7 @@
<string name="token_details_token_type_subtitle_no_standard">Token en la red %%image%% %1$s</string>
<string name="token_details_unable_hide_alert_message">El token %1$s (%2$s) es la moneda principal en la red %3$s y no se puede ocultar mientras tengas otros tokens de esta red en la lista</string>
<string name="token_details_unable_hide_alert_title">No se puede ocultar %s</string>
<string name="token_market_metrics_no_data">N/A</string>
<string name="token_receive_show_qr_code_title">Mostrar código QR</string>
<string name="token_swap_changelly_promotion_message">Cambie este token por otro por una tarifa de servicio de %1$s del %2$s al %3$s de febrero.</string>
<string name="token_swap_changelly_promotion_title">Intercambie con Changelly, %s comisiones</string>
@ -1672,6 +1734,9 @@
<string name="universal_error">Hemos encontrado un error. Código de error: %s. Póngase en contacto con nuestro servicio de soporte.</string>
<string name="unlock_wallet_description_full">Use %s o escanee una tarjeta/anillo para tener acceso a su billetera</string>
<string name="unsupported_wc_version">Error de conexión: Esta dApp utiliza la versión 1.0 de Wallet Connect, que no es compatible. Asegúrese de que la dApp sea compatible con la versión 2.0 de Wallet Connect para conectarse correctamente.</string>
<string name="update_approval_permission_fee_note">Se revocará el permiso anterior y se emitirá uno nuevo. La red cobrará una tarifa de aprobación de tokens por cada una de estas acciones. Verá una transacción de importe cero en el historial como comprobante de la revocación.</string>
<string name="update_approval_permission_subtitle">La transacción excede el límite de permisos otorgados previamente.\nActualice los permisos para continuar.</string>
<string name="update_approval_permission_title">Actualizar permisos</string>
<string name="upgrade_to_hardware_wallet_button_title">Actualización a billetera de hardware</string>
<string name="user_push_notification_agreement_argument_one">Manténgase actualizado con las últimas funciones y noticias</string>
<string name="user_push_notification_agreement_argument_one_subtitle">Alertas en tiempo real de transacciones, intercambios y actualizaciones críticas.</string>
@ -1842,7 +1907,7 @@
<string name="wallet_settings_access_code_description">Este código de acceso protege su billetera y se utiliza para iniciar sesión y firmar transacciones.</string>
<string name="wallet_settings_access_code_title">Establecer/Cambiar código de acceso</string>
<string name="wallet_settings_change_access_code_title">Cambiar código de acceso</string>
<string name="wallet_settings_push_notifications_description">Manténgase informado sobre las transacciones entrantes de la billetera y las actualizaciones de Tangem.</string>
<string name="wallet_settings_push_notifications_description">Reciba notificaciones push para las transacciones entrantes en su billetera.</string>
<string name="wallet_settings_push_notifications_huawei_warning">Es posible que las notificaciones push no funcionen actualmente en dispositivos Huawei. Estamos trabajando activamente en una solución y la publicaremos en una próxima actualización. ¡Gracias por su comprensión!</string>
<string name="wallet_settings_push_notifications_title">Notificaciones de transacciones</string>
<string name="wallet_settings_set_access_code_title">Establecer código de acceso</string>
@ -1870,7 +1935,7 @@
<string name="warning_clore_migration_message">Según la documentación oficial de Clore, todas las monedas recibidas antes del 21 de diciembre serán migradas a Clore (token ERC-20); las monedas recibidas después de esa fecha no lo serán. Se está desarrollando una solución de transferencia — mantente atento.</string>
<string name="warning_clore_migration_message_label">Mensaje</string>
<string name="warning_clore_migration_open_portal_button">Abrir el portal de reclamaciones</string>
<string name="warning_clore_migration_sheet_description">Para mantener el acceso a sus fondos, comience la migración de acuerdo con las pautas oficiales de Clore.</string>
<string name="warning_clore_migration_sheet_description">Para continuar utilizando sus tokens de Clore, debe completar la migración de tokens de acuerdo con la información del Portal de Reclamación.</string>
<string name="warning_clore_migration_sheet_title">Migración de la red Clore</string>
<string name="warning_clore_migration_sign_button">Firmar</string>
<string name="warning_clore_migration_signature_label">Firma</string>
@ -1925,7 +1990,7 @@
<string name="warning_network_unreachable_message">La red no está disponible actualmente. Por favor, inténtalo de nuevo más tarde.</string>
<string name="warning_network_unreachable_title">La red no está disponible</string>
<string name="warning_no_account_title">Recargue su billetera</string>
<string name="warning_no_backup_message">Su billetera no ha sido respaldado. Realice este procedimiento para proteger sus activos ahora.</string>
<string name="warning_no_backup_message">Su billetera no tiene aún una copia de seguridad. Hágala ahora para proteger sus activos.</string>
<string name="warning_no_backup_title">Falta backup</string>
<string name="warning_number_of_signed_hashes_incorrect_message">Esta tarjeta se ha utilizado previamente para transacciones. Si la recibió de una fuente no confiable, considere retirar todos los fondos. Si es su tarjeta, no se requiere ninguna acción.</string>
<string name="warning_number_of_signed_hashes_incorrect_title">La tarjeta ya ha firmado transacciones</string>
@ -2099,7 +2164,7 @@
<string name="yield_module_earn_sheet_high_fee_description">La tasa de red es actualmente demasiado alta para ejecutar préstamos. Los fondos se suministrarán una vez que baje a %1$s o menos.</string>
<string name="yield_module_earn_sheet_my_funds_title">Mis fondos</string>
<string name="yield_module_earn_sheet_provider_description">Su %1$s está ahora depositado en Aave y devenga intereses. Tiene un token%2$s, que representa su saldo y crece con el tiempo. Cuando deposite más fondos, estos se suministrarán a Aave para ganar intereses, menos una comisión por transacción.</string>
<string name="yield_module_earn_sheet_title">Modo de rendimiento</string>
<string name="yield_module_earn_sheet_title">Modo Rendimiento</string>
<string name="yield_module_earn_sheet_total_earnings_title">Ganancias totales</string>
<string name="yield_module_earn_sheet_transfers_title">Transferencias a Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">Explore Aave</string>
@ -2111,7 +2176,7 @@
<string name="yield_module_fee_policy_sheet_max_fee_title">Tarifa máxima</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">El monto mínimo se calcula en función de la comisión de la red actual para que no supere el 4%% del importe de la recarga, lo que hace que el mínimo sea %1$s (%2$s).</string>
<string name="yield_module_fee_policy_sheet_min_amount_title">Recarga mínima</string>
<string name="yield_module_fee_policy_sheet_title">Política de tarifas</string>
<string name="yield_module_fee_policy_sheet_title">Política de tarifas por recarga</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem también cobra una comisión de servicio del 15% sobre el rendimiento obtenido.</string>
<string name="yield_module_high_fee_error">Sus fondos se suministrarán automáticamente a Aave una vez que las comisiones de red sean más bajas o su saldo alcance el importe mínimo requerido.</string>
<string name="yield_module_high_network_fees_notification_description">Las comisiones son más altas de lo habitual debido a la alta actividad del mercado. Puede continuar ahora o volver a consultar más tarde cuando las comisiones sean más bajas.</string>
@ -2131,7 +2196,7 @@
<string name="yield_module_promo_screen_self_custodial_subtitle">Aave es un protocolo on-chain para crear mercados de liquidez no custodiales y ganar intereses a tasa variable.</string>
<string name="yield_module_promo_screen_self_custodial_title">Descentralizado y autocustodiado</string>
<string name="yield_module_promo_screen_terms_disclaimer">Al utilizar este servicio, usted acepta que el proveedor\n%1$s y %2$s</string>
<string name="yield_module_promo_screen_title">Conectar Aave</string>
<string name="yield_module_promo_screen_title">Conectar con Aave</string>
<string name="yield_module_promo_screen_title_v2">Consiga un %1$s%% APY\nen su saldo</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s%% - Tipo de interés variable</string>
<string name="yield_module_promo_screen_variable_rate_info_v2">Tipo de interés variable</string>
@ -2144,7 +2209,7 @@
<string name="yield_module_receive_sheet_description">Cuando recargue, sus fondos se enviarán automáticamente a Aave para comenzar a ganar intereses. %s se deducirá para cubrir la tarifa de transacción.</string>
<string name="yield_module_start_earning">Suministro de activos</string>
<string name="yield_module_start_earning_sheet_description">Su %s se suministrará a Aave, pero seguirá siendo gestionable.</string>
<string name="yield_module_start_earning_sheet_fee_policy">Ver política de tarifas</string>
<string name="yield_module_start_earning_sheet_fee_policy">Ver política de tarifas por recarga</string>
<string name="yield_module_start_earning_sheet_next_deposits">Sus próximas recargas se suministrarán automáticamente a Aave.</string>
<string name="yield_module_start_earning_sheet_next_deposits_v2">Todos tus futuros depósitos %1$s se suministrarán automáticamente a Aave.</string>
<string name="yield_module_status_active">Activo</string>
@ -2158,8 +2223,8 @@
<string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">Los intereses se devengan automáticamente</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Los intereses se devengan automáticamente</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Modo de rendimiento</string>
<string name="yield_module_token_details_earn_notification_processing">Procesando su depósito</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Modo Rendimiento</string>
<string name="yield_module_token_details_earn_notification_processing">Activando Modo Rendimiento</string>
<string name="yield_module_token_details_earn_notification_title">Modo Rendimiento</string>
<string name="yield_module_transaction_deploy_contract">Implementación del contrato del Modo Rendimiento</string>
<string name="yield_module_transaction_enter">Modo Rendimiento activado</string>

View file

@ -1086,6 +1086,14 @@
<string name="qr_scanner_camera_denied_settings_button">Paramètres</string>
<string name="qr_scanner_camera_denied_text">Vous n\'avez pas donné accès à votre caméra</string>
<string name="qr_scanner_camera_denied_title">Accès à la caméra refusé</string>
<string name="qr_scanner_error_token_not_added_message">Le jeton demandé n\'est pas ajouté à votre portefeuille. Veuillez l\'ajouter, puis réessayez.</string>
<string name="qr_scanner_error_token_not_added_title">Le jeton n\'a pas été ajouté</string>
<string name="qr_scanner_error_unrecognized_message">Désolé, ce code QR n\'a pas pu être reconnu.</string>
<string name="qr_scanner_error_unrecognized_title">Code QR non reconnu</string>
<string name="qr_scanner_error_unsupported_network_message">Ce réseau n\'est pris en charge par aucun des jetons que vous avez ajoutés. Ajoutez un jeton pris en charge pour envoyer des cryptomonnaies.</string>
<string name="qr_scanner_error_unsupported_network_title">Aucun jeton pris en charge n\'a été trouvé</string>
<string name="qr_scanner_warning_unknown_parameters_message">Ce code QR contient des paramètres non reconnus : %s. Si vous continuez, certaines informations de paiement risquent d\'être perdues.</string>
<string name="qr_scanner_warning_unknown_parameters_title">Paramètres inconnus</string>
<string name="receive_bottom_sheet_no_memo_required_message">Aucun mémo requis</string>
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) sur le réseau %3$s</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s sur le réseau %2$s</string>

View file

@ -415,6 +415,7 @@
<string name="custom_token_creation_error_token_already_exist_title">トークンはすでに存在します</string>
<string name="custom_token_creation_error_wrong_decimals">小数は%dまでの有効な整数である必要があります</string>
<string name="custom_token_custom_derivation">カスタム派生パス</string>
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">動的アドレスが有効です。カスタムの「change」と「index」は利用できません。</string>
<string name="custom_token_custom_derivation_placeholder">m/00\'/0000\'/0\'/0/0</string>
<string name="custom_token_custom_derivation_title">カスタム派生パスを入力</string>
<string name="custom_token_decimals_input_title">小数</string>
@ -756,11 +757,13 @@
<string name="markets_insights_info_description_message">このセクションのデータは、次のネットワークから取得されています: %s</string>
<string name="markets_loading_error_title">データを読み込めません…</string>
<string name="markets_loading_no_data_title">データなし</string>
<string name="markets_portfolio_block_add_token_title">**ポートフォリオに追加して**、この資産の買付・交換・受け取りを始めましょう</string>
<string name="markets_portfolio_block_subtitle">ポートフォリオ内</string>
<string name="markets_pulse_common_title">マーケット動向</string>
<string name="markets_quick_actions">クイックアクション</string>
<string name="markets_search_clear_all_hints">すべてクリア</string>
<string name="markets_search_header_title">トークンを探す</string>
<string name="markets_search_hint_header">最近</string>
<string name="markets_search_hint_header">最近の検索</string>
<string name="markets_search_portfolio_header">ポートフォリオ内</string>
<string name="markets_search_result_title">結果</string>
<string name="markets_search_see_tokens_under_100k">時価総額10万ドル以下のトークンを見る</string>
@ -1712,6 +1715,9 @@
<string name="universal_error">エラーが発生しました。エラー コード:%s。サポートにお問い合わせください。</string>
<string name="unlock_wallet_description_full">%sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください</string>
<string name="unsupported_wc_version">接続に失敗しましたこのdAppは、サポートされていないWallet Connectバージョン1.0を使用しています。正常に接続するには、dAppがWallet Connectバージョン2.0をサポートしていることを確認してください。</string>
<string name="update_approval_permission_fee_note">以前の承認は取り消され、新しい承認が発行されます。これらの各操作には、ネットワークによるトークン承認手数料がかかります。履歴には、取り消しの証拠として金額0の取引が表示されます。</string>
<string name="update_approval_permission_subtitle">取引金額が、以前に許可された承認額を超えています。\n続行するには、承認内容を更新してください。</string>
<string name="update_approval_permission_title">承認を更新</string>
<string name="upgrade_to_hardware_wallet_button_title">ハードウェアウォレットにアップグレード</string>
<string name="user_push_notification_agreement_argument_one">最新の機能とニュースをお届けします</string>
<string name="user_push_notification_agreement_argument_one_subtitle">取引・スワップ・重要な更新に関するリアルタイム通知。</string>

View file

@ -422,6 +422,7 @@
<string name="custom_token_creation_error_token_already_exist_title">O token já existe.</string>
<string name="custom_token_creation_error_wrong_decimals">O valor decimal deve ser um número inteiro válido, até um limite de %d</string>
<string name="custom_token_custom_derivation">Derivação personalizada</string>
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">O recurso de Endereços Dinâmicos está ativado. As opções personalizadas \"alterar\" e \"indexar\" não estão disponíveis.</string>
<string name="custom_token_custom_derivation_placeholder">Exemplo: m/00\'/0000\'/0\'/0/0</string>
<string name="custom_token_custom_derivation_title">Insira uma derivação personalizada</string>
<string name="custom_token_decimals_input_title">Decimais</string>
@ -474,6 +475,19 @@
<string name="domain_receive_assets_onboarding_description">O envio de ativos por outras redes resultará em perda permanente.</string>
<string name="domain_receive_assets_onboarding_network_name">%s rede</string>
<string name="domain_receive_assets_onboarding_title">Envie fundos usando apenas</string>
<string name="dynamic_addresses">Endereços dinâmicos</string>
<string name="dynamic_addresses_custom_token_error_on_addition">O recurso de Endereços Dinâmicos está ativado. As opções personalizadas \"alterar\" e \"indexar\" não estão disponíveis.</string>
<string name="dynamic_addresses_enabled_toast_title">Endereços dinâmicos ativados</string>
<string name="dynamic_addresses_enter_features_privacy_description">Use um novo endereço para cada transação para reduzir a rastreabilidade e melhorar a privacidade na blockchain.</string>
<string name="dynamic_addresses_enter_features_privacy_title">Privacidade aprimorada</string>
<string name="dynamic_addresses_enter_features_receving_description">Receba fundos facilmente em redes baseadas em UTXO com geração automática de endereços — sem necessidade de gerenciamento manual de endereços.</string>
<string name="dynamic_addresses_enter_features_receving_title">Recebimento perfeito</string>
<string name="dynamic_addresses_enter_main_button_title">Habilitar endereços dinâmicos</string>
<string name="dynamic_addresses_enter_subtitle">Endereços dinâmicos criam um novo a cada vez para maior privacidade — seu saldo total permanece o mesmo. </string>
<string name="dynamic_addresses_error_has_custom_token_description">Não é possível ativar os endereços dinâmicos porque alguns endereços/tokens personalizados usam um caminho de derivação modificado, que não atende aos critérios necessários.</string>
<string name="dynamic_addresses_error_has_custom_token_title">Endereços dinâmicos indisponíveis</string>
<string name="dynamic_addresses_error_service_unavailable_description">Não foi possível conectar-se ao provedor neste momento. Tente novamente mais tarde.</string>
<string name="dynamic_addresses_error_service_unavailable_title">Serviço indisponível. Por favor, tente novamente.</string>
<string name="earn_best_opportunities">Melhores oportunidades</string>
<string name="earn_clear_filter">Limpar filtro</string>
<string name="earn_empty">A lista está temporariamente vazia, pois está sendo atualizada. Volte daqui a pouco.</string>
@ -750,9 +764,14 @@
<string name="markets_insights_info_description_message">Os dados desta seção são provenientes das seguintes redes: %s</string>
<string name="markets_loading_error_title">Não foi possível carregar os dados…</string>
<string name="markets_loading_no_data_title">Sem dados</string>
<string name="markets_portfolio_block_add_token_title">**Adicione ao seu portfólio** para começar a comprar, trocar ou receber este ativo.</string>
<string name="markets_portfolio_block_subtitle">Em seu portfólio</string>
<string name="markets_pulse_common_title">Pulso do mercado</string>
<string name="markets_quick_actions">Ações rápidas</string>
<string name="markets_search_clear_all_hints">Limpar tudo</string>
<string name="markets_search_header_title">Pesquisar tokens</string>
<string name="markets_search_hint_header">Recentes</string>
<string name="markets_search_portfolio_header">Em seu portfólio</string>
<string name="markets_search_result_title">Resultado</string>
<string name="markets_search_see_tokens_under_100k">Veja tokens com capitalização de mercado inferior a 100 mil dólares.</string>
<string name="markets_search_show_tokens">Mostrar tokens</string>
@ -1114,6 +1133,8 @@
<string name="qr_scanner_error_unrecognized_title">Código QR não reconhecido</string>
<string name="qr_scanner_error_unsupported_network_message">Esta rede não é compatível com nenhum dos tokens adicionados. Adicione um token compatível para enviar criptomoedas.</string>
<string name="qr_scanner_error_unsupported_network_title">Nenhum token compatível encontrado</string>
<string name="qr_scanner_warning_unknown_parameters_message">Este código QR contém parâmetros que não são reconhecidos: %sAlgumas informações de pagamento podem ser perdidas se você continuar.</string>
<string name="qr_scanner_warning_unknown_parameters_title">Parâmetros desconhecidos</string>
<string name="receive_bottom_sheet_no_memo_required_message">Não é necessário memorando</string>
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) sobre %3$s rede</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s sobre %2$s rede</string>
@ -1499,6 +1520,7 @@
<string name="swapping_fee_estimation_error_text">Erro na estimativa de custos. Por favor, envie seu feedback para o suporte.</string>
<string name="swapping_from_title">Você troca</string>
<string name="swapping_high_price_impact_description">Trocar essa quantidade de tokens selecionados causará um impacto significativo no preço e reduzirá seu resultado.</string>
<string name="swapping_high_price_impact_text">Você pode receber um valor significativamente menor devido à baixa liquidez. Tente um valor menor ou outro provedor.</string>
<string name="swapping_high_price_impact_title">Alto impacto nos preços</string>
<string name="swapping_insufficient_funds">Fundos insuficientes</string>
<string name="swapping_insufficient_funds_description">Não há fundos suficientes para concluir esta transação. Reduza o valor a receber ou adicione mais fundos.</string>
@ -1508,6 +1530,8 @@
<string name="swapping_to_title">Você recebe</string>
<string name="swapping_token_list_title">Escolha o token</string>
<string name="swapping_token_not_available">não disponível</string>
<string name="swapping_trade_too_large_text">Liquidez insuficiente para esta transação. \n o valor ou escolha outro provedor.</string>
<string name="swapping_trade_too_large_title">Negociação em grande escala</string>
<string name="tangem_pay_beta_notification_subtitle">Teremos todo o prazer em receber seu feedback.</string>
<string name="tangem_pay_beta_notification_title">O Tangem Pay agora está em versão beta.</string>
<string name="tangem_pay_card_frozen">Cartão bloqueado</string>
@ -1711,6 +1735,9 @@
<string name="universal_error">Ocorreu um erro. Código do erro: %sPor favor, entre em contato com nosso suporte.</string>
<string name="unlock_wallet_description_full">Usar %s ou escaneie um cartão/anel para acessar sua carteira.</string>
<string name="unsupported_wc_version">Falha na conexão: Este aplicativo descentralizado (dApp) usa a versão 1.0 do Wallet Connect, que não é compatível. Certifique-se de que o dApp seja compatível com a versão 2.0 do Wallet Connect para que a conexão seja estabelecida com sucesso.</string>
<string name="update_approval_permission_fee_note">A permissão anterior será revogada e uma nova será emitida. A rede cobrará uma taxa de aprovação de tokens para cada uma dessas ações. Você verá uma transação de valor zero no histórico como comprovante de revogação.</string>
<string name="update_approval_permission_subtitle">A transação excede o limite de permissão concedido anteriormente.\nAtualize a permissão para prosseguir.</string>
<string name="update_approval_permission_title">Atualizar permissão</string>
<string name="upgrade_to_hardware_wallet_button_title">Faça upgrade para uma carteira de hardware.</string>
<string name="user_push_notification_agreement_argument_one">Fique por dentro das últimas novidades e recursos.</string>
<string name="user_push_notification_agreement_argument_one_subtitle">Alertas em tempo real para transações, câmbio e atualizações críticas.</string>

View file

@ -1124,6 +1124,14 @@
<string name="qr_scanner_camera_denied_settings_button">Налаштування</string>
<string name="qr_scanner_camera_denied_text">Ви не надали доступ до своєї камери</string>
<string name="qr_scanner_camera_denied_title">Доступ до камери заборонено</string>
<string name="qr_scanner_error_token_not_added_message">Запитуваний токен не додано до вашого гаманця. Будь ласка, додайте його та спробуйте ще раз.</string>
<string name="qr_scanner_error_token_not_added_title">Токен не додано</string>
<string name="qr_scanner_error_unrecognized_message">На жаль, цей QR-код не вдалося розпізнати.</string>
<string name="qr_scanner_error_unrecognized_title">Нерозпізнаний QR-код</string>
<string name="qr_scanner_error_unsupported_network_message">Ця мережа не підтримується жодним із ваших доданих токенів. Додайте підтримуваний токен, щоб надіслати криптовалюту.</string>
<string name="qr_scanner_error_unsupported_network_title">Підтримуваних токенів не знайдено</string>
<string name="qr_scanner_warning_unknown_parameters_message">Цей QR-код містить нерозпізнані параметри: %s. Деякі деталі платежу можуть бути втрачені, якщо ви продовжите.</string>
<string name="qr_scanner_warning_unknown_parameters_title">Невідомі параметри</string>
<string name="receive_bottom_sheet_no_memo_required_message">Memo не вимагається</string>
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) у мережі %3$s</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s у мережі %2$s</string>

View file

@ -1,19 +1,650 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="common_add_tokens">添加代幣</string>
<string name="common_no_results">沒有結果</string>
<string name="common_token_send">要發送的代幣</string>
<string name="common_unlock">解鎖</string>
<string name="initial_wallet_sync_banner_description">您的錢包已同步並準備就緒。\n缺少某些代幣</string>
<string name="initial_wallet_sync_banner_title">錢包導入成功</string>
<string name="initial_wallet_sync_restore_progress">恢復%d%%</string>
<string name="access_code_alert_validation_cancel">更改代码</string>
<string name="access_code_alert_validation_description">您的访问代码可解锁和保护您的钱包访问权限</string>
<string name="access_code_alert_validation_ok">无论如何都要用</string>
<string name="access_code_alert_validation_title">这个访问码很容易猜到。</string>
<string name="account_add_limit_dialog_description">您不能创建超过 %1$s 账户。存档一个账户以添加新账户。</string>
<string name="account_add_limit_dialog_title">无法添加新帐户</string>
<string name="account_archive_success_message">账户已存档</string>
<string name="account_archived_accounts">已存档账户</string>
<string name="account_archived_recover_dialog_description">您即将恢复 \"%1$s\"。</string>
<string name="account_archived_recover_dialog_title">恢复账户</string>
<string name="account_archived_recover_error_message">您已达到20个活跃账户的上限。请归档一个账户以恢复此账户。</string>
<string name="account_archived_recover_error_title">无法恢复帐户</string>
<string name="account_could_not_archive">我们无法存档账户。请稍后再试。</string>
<string name="account_could_not_archive_referral_program_message">此账户正在参与推荐计划。</string>
<string name="account_could_not_archive_referral_program_title">此帐户无法存档</string>
<string name="account_could_not_create">我们无法创建账户。请稍后再试。</string>
<string name="account_create_success_message">账户已创建</string>
<string name="account_details_archiving">存档...</string>
<string name="account_edit_failure_dialog_title">无法编辑帐户</string>
<string name="account_edit_success_message">账户已保存</string>
<string name="account_for_rewards">奖励账户</string>
<string name="account_form_name_already_exist_error_description">已存在使用此名称的帐户。请选择其他名称。</string>
<string name="account_form_name_already_exist_error_title">帐户名已被使用</string>
<string name="account_generic_error_dialog_message">请稍后再试。如果问题仍然存在,请联系客服,我们将协助您解决问题。</string>
<string name="account_label_tokens_info">%1$s 在 %2$s</string>
<string name="account_main_account_title">主账户</string>
<string name="account_recover_limit_dialog_description">您已超过 %1$s 活动账户的限制。存档一个以恢复</string>
<string name="account_recover_limit_dialog_title">无法恢复帐户</string>
<string name="account_recover_success_message">账户已恢复</string>
<string name="account_reorder_description">长按账户可重新排序</string>
<string name="accounts_migration_alert_message">一些自定义代币将自动从 \"%1$s\" 移至 \"%2$s\",因为它们的派生词属于该账户。</string>
<string name="accounts_migration_alert_title">部分自定义代币将自动移动</string>
<string name="alert_authentication_error_message">生物识别出现问题。请尝试重置设备上的生物识别技术或联系技术支持。</string>
<string name="alert_authentication_error_title">验证错误</string>
<string name="app_settings_access_code_warning">设置访问码以启用生物识别功能</string>
<string name="app_settings_off_require_access_code_alert_message">稍后会要求您提供访问代码以进行安全存储。</string>
<string name="app_settings_on_require_access_code_alert_message">这将删除所有已保存的钱包访问码。您需要重新输入访问码才能使用钱包。</string>
<string name="biometric_disabled_warning_description">您的设备已关闭生物识别功能,因此无法使用此功能解锁钱包。请在设备设置中启用生物识别功能,即可再次使用此方法。</string>
<string name="biometric_disabled_warning_title">生物识别认证已禁用</string>
<string name="biometric_lockout_permanent_warning_description_2">您的生物识别尝试次数已达上限。请使用卡片/指环解锁钱包,或输入您的访问码。</string>
<string name="biometric_lockout_permanent_warning_title">生物识别认证已锁定</string>
<string name="biometric_lockout_warning_description_2">生物识别登录暂时锁定。请30秒后重试或轻触设备或输入访问码解锁您的钱包。</string>
<string name="biometric_updated_warning_description">您设备上的生物识别信息已更新。请选择您的钱包并输入其访问码以重新启用生物识别登录。</string>
<string name="biometric_updated_warning_title">需要注意</string>
<string name="bitcoin_promo_activation_error">处理您的优惠码时出错,请稍后再试。</string>
<string name="bitcoin_promo_activation_error_title">激活错误</string>
<string name="bitcoin_promo_activation_success">您的优惠码已成功激活。奖励将在14天内计入您的比特币账户。</string>
<string name="bitcoin_promo_activation_success_title">促销代码已激活</string>
<string name="bitcoin_promo_already_activated">此优惠码已被使用,无法再次激活。</string>
<string name="bitcoin_promo_already_activated_title">代码不可用</string>
<string name="bitcoin_promo_invalid_code">此优惠码无效,无法激活。</string>
<string name="bitcoin_promo_invalid_code_title">无效代码</string>
<string name="bitcoin_promo_no_address">您需要提供比特币地址才能领取奖励。请将比特币地址添加到您的钱包,然后重试激活。</string>
<string name="bitcoin_promo_no_address_title">需要比特币地址</string>
<string name="card_reset_alert_continue_message">请重置下一个设备以继续。</string>
<string name="card_reset_alert_continue_title">钱包重置</string>
<string name="card_reset_alert_finish_message">所有 Tangem 设备均已重置。您现在可以继续升级钱包。</string>
<string name="card_reset_alert_finish_ok_button">再次升级</string>
<string name="card_reset_alert_finish_title">重置完成</string>
<string name="card_reset_alert_incomplete_message">我们建议在此钱包中完成所有 Tangem 设备的重置过程。</string>
<string name="card_reset_alert_incomplete_title">部分 Tangem 设备仍需重置。</string>
<string name="common_account">账户</string>
<string name="common_accounts">账户</string>
<string name="common_activate">激活</string>
<string name="common_add">添加</string>
<string name="common_add_tokens">添加代币</string>
<string name="common_added">已添加</string>
<string name="common_and"></string>
<string name="common_choose_account">选择账户</string>
<string name="common_contact_support">联系客服</string>
<string name="common_estimated_fee">充值网络费</string>
<string name="common_forget">忘记</string>
<string name="common_from"></string>
<string name="common_from_wallet_name">从 %s</string>
<string name="common_get_started">开始</string>
<string name="common_get_token">获取代币</string>
<string name="common_hold_to">保持到 %s</string>
<string name="common_insufficient_balance">余额不足</string>
<string name="common_learn_more">了解更多</string>
<string name="common_legacy_bitcoin_address">传统比特币</string>
<string name="common_locked_wallets">锁定的钱包</string>
<plurals name="common_networks_count">
<item quantity="other">%d网络</item>
</plurals>
<string name="common_new_address">新地址</string>
<string name="common_news">新闻</string>
<string name="common_no_results">无结果</string>
<string name="common_not_available">不可用</string>
<string name="common_or">或者</string>
<string name="common_recommended">推荐</string>
<string name="common_reset">重置</string>
<string name="common_something_went_wrong">出问题了</string>
<string name="common_tangem">Tangem</string>
<string name="common_tap_and_hold_hint">点击并按住</string>
<string name="common_to"></string>
<string name="common_to_wallet_name">到 %s</string>
<string name="common_token_send">要发送的代币</string>
<string name="common_unable_to_load">无法加载数据……</string>
<string name="common_understand_continue">我明白,请继续</string>
<string name="common_unlock">解锁</string>
<string name="common_wallets">钱包</string>
<string name="common_yield_mode">收益模式</string>
<string name="custom_token_another_account_dialog_description">您的代币派生信息与 %1$s的派生信息一致。 您的代币将被添加到该账户。</string>
<string name="custom_token_another_account_dialog_title">派生属于另一个账户。</string>
<string name="custom_token_another_account_snackbar_text">代币已添加到 %1$s 帐户</string>
<string name="custom_token_custom_derivation_dynamic_addresses_enabled_error">动态地址已启用。自定义\“更改”\和\“索引”\不可用。</string>
<string name="details_mobile_wallet_upgrade_action_title">升级到硬件钱包</string>
<string name="details_wallets_section_description">您只能拥有一个手机钱包。将其升级为 Tangem 硬件钱包,或添加一个新的硬件钱包。</string>
<string name="domain_receive_assets_default_address">默认地址</string>
<string name="domain_receive_assets_legacy_address">传统 %s 地址</string>
<string name="domain_receive_assets_network_name_address">%s 地址</string>
<string name="dynamic_addresses">动态地址</string>
<string name="dynamic_addresses_custom_token_error_on_addition">动态地址已启用。自定义“更改”和“索引”功能不可用。</string>
<string name="dynamic_addresses_enabled_toast_title">已启用动态地址</string>
<string name="dynamic_addresses_enter_features_privacy_description">每次交易都使用一个新地址,以减少可追溯性并提高链上隐私性。</string>
<string name="dynamic_addresses_enter_features_privacy_title">增强隐私</string>
<string name="dynamic_addresses_enter_features_receving_description">通过自动生成地址轻松在基于UTXO的网络中接收资金——无需手动管理地址。</string>
<string name="dynamic_addresses_enter_features_receving_title">无缝接收</string>
<string name="dynamic_addresses_enter_main_button_title">启用动态地址</string>
<string name="dynamic_addresses_enter_subtitle">动态地址每次都会创建一个新地址,以增强隐私保护——您的总余额保持不变。 </string>
<string name="dynamic_addresses_error_has_custom_token_description">无法启用动态地址,因为某些自定义地址/代币使用的是修改后的派生路径,不符合所需的标准。</string>
<string name="dynamic_addresses_error_has_custom_token_title">动态地址不可用</string>
<string name="dynamic_addresses_error_service_unavailable_description">我们目前无法连接到服务提供商,请稍后再试。</string>
<string name="dynamic_addresses_error_service_unavailable_title">服务不可用,请稍后再试。</string>
<string name="earn_best_opportunities">最佳机会</string>
<string name="earn_clear_filter">清除筛选</string>
<string name="earn_empty">列表正在刷新,暂时为空。请稍后再查看。</string>
<string name="earn_filter_all_networks">所有网络</string>
<string name="earn_filter_all_types">所有类型</string>
<string name="earn_filter_by">筛选方式</string>
<string name="earn_filter_my_networks">我的网络</string>
<string name="earn_filter_networks">网络</string>
<string name="earn_mostly_used">热门</string>
<string name="earn_no_results">无结果</string>
<string name="earn_title">质押与收益模式</string>
<string name="exchange_token_description">将您投资组合中的任何资产交换为该代币</string>
<string name="express_provider_great_rate">有竞争力的费率</string>
<string name="fee_selector_choose_speed_description">速度越快,确认速度越快,但网络费用也越高。 %s</string>
<string name="fee_selector_choose_speed_title">选择速度</string>
<string name="fee_selector_choose_token_description">选择用于支付网络费用的代币。 %s</string>
<string name="fee_selector_choose_token_title">选择代币</string>
<string name="feed_market_and_news">市场与新闻</string>
<string name="feed_tangem_ai">Tangem AI</string>
<string name="feed_trending_now">当下热门</string>
<string name="feedback_subject_backup_problem">备份问题</string>
<string name="gasless_not_enough_funds_to_cover_token_fee">资金不足</string>
<string name="gasless_transaction_fee">转账费</string>
<string name="hardware_wallet_backup_feature_description">地址直接在您的 Tangem 硬件钱包上生成,随时可用,并受到全面保护。</string>
<string name="hardware_wallet_backup_feature_title">新地址</string>
<string name="hardware_wallet_create_title">添加 Tangem 钱包</string>
<string name="hardware_wallet_key_feature_description">您的私钥将直接在 Tangem 卡内生成,并且永远不会离开该卡。</string>
<string name="hardware_wallet_key_feature_title">密钥生成</string>
<string name="hardware_wallet_security_feature_description">所有加密操作都在安全芯片内部进行,该芯片经过认证,可防止克隆和物理篡改。</string>
<string name="hardware_wallet_security_feature_title">硬件级安全</string>
<string name="hot_access_code_set_biometric_ask">您是否允许“Tangem”使用生物识别认证来确认您的身份并打开应用程序</string>
<string name="hw_backup_banner_description">让您的加密货币安全离线存储。轻薄如信用卡,安全胜过银行金库。</string>
<string name="hw_backup_hardware_create_description">创建一个安全的钱包并转移资金,以加强保护。</string>
<string name="hw_backup_hardware_create_title">创建新钱包</string>
<string name="hw_backup_hardware_description">使用卓越的 Tangem 硬件钱包,提升您的安全保障。</string>
<string name="hw_backup_hardware_title">硬件钱包</string>
<string name="hw_backup_hardware_upgrade_description">将您当前的手机钱包转换为 Tangem 冷钱包。</string>
<string name="hw_backup_hardware_upgrade_title">升级当前钱包</string>
<string name="hw_backup_need_finish_first">先完成备份</string>
<string name="hw_backup_section_other_title">其他方法</string>
<string name="hw_backup_seed_code_description">将您的恢复短语保存在安全的地方,保持其私密性以保护您的资金,并设置访问密码以提高安全性。</string>
<string name="hw_backup_to_secure_description">要使用访问码保护您的钱包,请完成备份过程。</string>
<string name="hw_backup_to_upgrade_description">要升级到硬件钱包,请完成备份过程。</string>
<string name="hw_backup_upgrade_description">将您的手机钱包升级到 Tangem 硬件钱包,享受最高级别的安全性。导入您的钱包或将资金转移到新钱包。</string>
<string name="hw_backup_upgrade_title">升级到冷钱包</string>
<string name="hw_create_upgrade_description">随时安全地将手机钱包转移到 Tangem 卡片或指环上。</string>
<string name="hw_create_upgrade_title">升级到硬件钱包</string>
<string name="hw_import_existing_wallet">导入现有钱包</string>
<string name="hw_import_seed_phrase_already_imported">此恢复短语已导入</string>
<string name="hw_remove_wallet_action_forget_title">忘记钱包</string>
<string name="hw_remove_wallet_attention_description">此钱包将从您的设备中永久移除。</string>
<string name="hw_remove_wallet_confirmation_title">你确定要这么做吗?</string>
<string name="hw_remove_wallet_nav_title">忘记钱包</string>
<string name="hw_remove_wallet_notification_action_backup_go">转到备份</string>
<string name="hw_remove_wallet_notification_action_backup_view">查看恢复短语</string>
<string name="hw_remove_wallet_notification_action_forget">忘记钱包</string>
<string name="hw_remove_wallet_notification_action_forget_anyway">确定忘记</string>
<string name="hw_remove_wallet_notification_description_has_backup">这个钱包有备份。在忘记钱包之前,请确保你能找回备份。</string>
<string name="hw_remove_wallet_notification_description_without_backup">如果您忘记了这个钱包而又没有备份,您将永远无法使用您的资金。</string>
<string name="hw_remove_wallet_notification_title">你确定要忘记这个钱包吗?</string>
<string name="hw_remove_wallet_warning_access">我知道,如果我在移除钱包之前没有备份钱包,我将无法访问钱包。</string>
<string name="hw_remove_wallet_warning_device">我知道移除钱包并不会将其删除,只会将其从我的设备中移除。</string>
<string name="hw_upgrade">升级</string>
<string name="hw_upgrade_backup_description">当您将手机钱包升级为 Tangem 卡或指环时,您的所有地址和余额都可以完全访问。</string>
<string name="hw_upgrade_backup_title">相同地址</string>
<string name="hw_upgrade_error_card_already_has_wallet">无法升级。此设备上已有一个钱包。</string>
<string name="hw_upgrade_error_card_key_import">请换一台设备。这台设备不能用于升级。</string>
<string name="hw_upgrade_error_wallet2_card_required">操作过程中发生错误。</string>
<string name="hw_upgrade_funds_access_description">在此过程中,您的资金将保持安全且完全可用。</string>
<string name="hw_upgrade_funds_access_title">获得资金</string>
<string name="hw_upgrade_general_security_description">升级后,您的移动钱包将从应用程序中移除并存储在您的 Tangem 硬件钱包中。您的助记词将保留在您身边。</string>
<string name="hw_upgrade_general_security_title">一般安全</string>
<string name="hw_upgrade_key_migration_description">私钥将从应用程序转移到您的 Tangem 硬件钱包。</string>
<string name="hw_upgrade_key_migration_title">私钥迁移</string>
<string name="hw_upgrade_scan_device">扫描设备</string>
<string name="hw_upgrade_start_action">开始升级</string>
<string name="hw_upgrade_start_description">您即将升级到我们的硬件钱包。它会将您的资产安全地存储在冷存储中。</string>
<string name="hw_upgrade_start_title">Tangem钱包</string>
<string name="hw_upgrade_title">升级到我们的硬件钱包</string>
<string name="hw_upgrade_to_cold_banner_description">使用 Tangem 的顶级硬件钱包,确保您的加密货币安全。</string>
<string name="hw_upgrade_to_cold_banner_title">将您的钱包升级到硬件安全级别</string>
<string name="initial_wallet_sync_banner_description">您的钱包已同步并准备就绪。\n缺少某些代币</string>
<string name="initial_wallet_sync_banner_title">钱包已成功导入</string>
<string name="initial_wallet_sync_restore_progress">恢复 %d%%</string>
<string name="main_qr_scan_hint">扫描二维码即可发送资金或连接到应用程序</string>
<string name="markets_about_coin_header">关于硬币</string>
<string name="markets_earn_common_title">质押与收益模式</string>
<string name="markets_hint_part_one">向上轻扫,探索市场</string>
<string name="markets_hint_part_two">寻找新的隐藏宝石</string>
<string name="markets_portfolio_block_add_token_title">**添加到您的投资组合**,即可开始购买、交换或接收此资产</string>
<string name="markets_portfolio_block_subtitle">在您的投资组合中</string>
<string name="markets_pulse_common_title">市场脉搏</string>
<string name="markets_search_clear_all_hints">全部清除</string>
<string name="markets_search_hint_header">最近的</string>
<string name="markets_search_portfolio_header">在您的投资组合中</string>
<string name="markets_search_title_placeholder">加密货币、新闻及更多</string>
<string name="markets_sort_by_yield_mode_title">收益模式</string>
<string name="markets_token_added">代币已添加</string>
<string name="markets_token_details_insight_day_timeline"></string>
<string name="markets_token_details_insight_month_timeline"></string>
<string name="markets_token_details_insight_week_timeline"></string>
<string name="markets_token_details_insight_week_timeline"></string>
<string name="markets_token_details_insight_year_timeline"></string>
<string name="send_rate_fixed_info_description">使用固定利率時,您在置換交易時收到的金額將被鎖定,這可以保護您免受交易過程中價格波動的影響。</string>
<string name="markets_token_details_max_supply_and_circulation_full">循环和最大供应</string>
<string name="markets_token_details_metrics_no_limited">无限制</string>
<string name="markets_token_details_trading_interval">24小时</string>
<string name="markets_token_details_valuation_value_in_total">%s 总共</string>
<string name="markets_tooltip_v2_title">添加更多代币</string>
<string name="markets_yield_supply_banner_description">增强资产性能,并使其能够即时访问。 %s</string>
<string name="markets_yield_supply_banner_title">激活收益模式</string>
<string name="mobile_wallet_requires_min_os_warning_body">创建手机钱包前必须更新至 %1$s</string>
<string name="mobile_wallet_requires_min_os_warning_title">手机钱包需要 %1$s 或更高版本</string>
<string name="news_all_news">所有新闻</string>
<string name="news_like">喜欢</string>
<plurals name="news_published_hours_ago">
<item quantity="other">小时之前</item>
</plurals>
<plurals name="news_published_minutes_ago">
<item quantity="other">分钟之前</item>
</plurals>
<string name="news_quick_recap">快速回顾</string>
<string name="news_related_news">新闻</string>
<string name="news_related_tokens">相关代币</string>
<string name="news_sources">相关新闻</string>
<string name="news_stay_in_the_loop">随时了解最新动态</string>
<string name="news_trending_score">趋势得分</string>
<string name="notification_black_friday_text">每个钱包均可获得价值 10 美元的 BTC \n快来领取</string>
<string name="notification_black_friday_title">黑色星期五:最多可省 30 美元% </string>
<string name="notification_one_plus_one_button">我们出发吧</string>
<string name="notification_one_plus_one_text">限时优惠!</string>
<string name="notification_one_plus_one_title">1+1买一个钱包优惠 50%</string>
<string name="notification_sepa_button">购买加密货币</string>
<string name="notification_sepa_text">通过 SEPA 转账购买加密货币时,可享受 ** 0% 费用**。</string>
<string name="notification_sepa_title">使用 SEPA 购买加密货币</string>
<string name="notification_visa_waitlist_promo_text">加入候补名单,即可获得一张与众不同的支付卡。</string>
<string name="notification_visa_waitlist_promo_title">Tangem Visa卡</string>
<string name="notification_yield_promo_button">条款和条件</string>
<string name="notification_yield_promo_text">存款 100 美元以上,持有 30 天,即可获得 10 美元</string>
<string name="notification_yield_promo_title">加入 \"收益模式 \"活动</string>
<string name="onboarding_navbar_upgrade_wallet_biometrics">生物识别</string>
<string name="only_one_mobile_wallet_explanation">您只能拥有一个手机钱包。将其升级为 Tangem 硬件钱包,或添加一个新的硬件钱包。</string>
<string name="onramp_all_offers_button_title">所有优惠</string>
<string name="onramp_error_transaction_already_processed">此交易已处理完毕,无需进一步操作。</string>
<string name="onramp_fetching_best_rates">获得最佳利率...</string>
<string name="onramp_instant_status">即时</string>
<string name="onramp_legal_text">服务由外部供应商提供。\nTangem对此不承担任何责任。</string>
<string name="onramp_offer_type_fastet">最快处理</string>
<string name="onramp_payment_method_subtitle">付款方式</string>
<plurals name="onramp_providers_count">
<item quantity="other">提供者</item>
</plurals>
<string name="onramp_providers_subtitle">提供商</string>
<string name="onramp_recently_used_title">最近使用过</string>
<string name="onramp_recommended_title">推荐</string>
<plurals name="onramp_timing_days">
<item quantity="other">至多%d天</item>
</plurals>
<string name="onramp_timing_minutes">%s 分钟</string>
<string name="onramp_title_available_from">可从</string>
<string name="onramp_title_available_up_to">最高可提供</string>
<string name="onramp_title_you_get">你得到</string>
<string name="onramp_tos_external_providers">服务由外部供应商提供。\nTangem 不承担任何责任。</string>
<string name="onramp_up_to_rate">至多</string>
<string name="onramp_via_provider">通过 %s</string>
<string name="onramp_you_will_pay_title">您将支付</string>
<string name="provider_name_support">%s 支持</string>
<string name="qr_scanner_camera_denied_gallery_button">从图库中选择</string>
<string name="qr_scanner_camera_denied_settings_button">设置</string>
<string name="qr_scanner_camera_denied_text">您尚未授予摄像头访问权限</string>
<string name="qr_scanner_camera_denied_title">摄像头访问权限被拒绝</string>
<string name="qr_scanner_error_token_not_added_message">您请求的代币未添加到您的钱包。请添加后重试。</string>
<string name="qr_scanner_error_token_not_added_title">代币未添加</string>
<string name="qr_scanner_error_unrecognized_message">抱歉,无法识别此二维码。</string>
<string name="qr_scanner_error_unrecognized_title">无法识别的二维码</string>
<string name="qr_scanner_error_unsupported_network_message">您添加的所有代币均不支持此网络。请添加受支持的代币以发送加密货币。</string>
<string name="qr_scanner_error_unsupported_network_title">未找到支持的代币</string>
<string name="qr_scanner_warning_unknown_parameters_message">此二维码包含无法识别的参数: %s如果您继续操作部分支付信息可能会丢失。</string>
<string name="qr_scanner_warning_unknown_parameters_title">未知参数</string>
<string name="referral_address_for_rewards">奖励地址</string>
<string name="reset_card_to_factory_condition_3">我明白,我将完全失去对 Tangem Pay 卡及其上所有资金的访问权,且无法挽回</string>
<string name="reset_cards_dialog_complete_description">所有 Tangem 设备均已重置。</string>
<string name="reset_cards_dialog_first_description">激活过程中出了问题。请逐一重置卡片。</string>
<string name="reset_cards_dialog_first_title">卡片验证失败</string>
<string name="reset_cards_dialog_next_device_description">请重置下一个设备以继续</string>
<string name="root_detected_warning_description">拥有 root 权限的设备安全性较低。您的数据可能面临额外的风险。</string>
<string name="root_detected_warning_title">检测到 root 访问</string>
<string name="send_from_title">您发送</string>
<plurals name="send_network_selection_hidden_tokens">
<item quantity="other">此地址与代币不兼容</item>
</plurals>
<string name="send_rate_fixed_info_description">使用固定利率时,您在互换交易时收到的金额将被锁定。这可以保护您免受交易过程中价格波动的影响。</string>
<string name="send_rate_fixed_info_title">固定利率</string>
<string name="send_rate_floating_info_description">浮動利率意味著您最終收到的金額可能會根據您開始和完成置換之間的市場情況略有變化。</string>
<string name="send_rate_floating_info_title">浮動利率</string>
<string name="send_rate_floating_info_description">动利率意味着您最终收到的金额可能会根据您开始和完成互换之间的市场情况略有变化。</string>
<string name="send_rate_floating_info_title">利率</string>
<string name="send_rate_is_fixed">利率固定</string>
<string name="send_with_swap_something_went_wrong">出错了。请再试一次。</string>
<string name="settings_upgrade_banner_advantage_feature_title">简单易用</string>
<string name="settings_upgrade_banner_description">让您的加密货币安全离线存储。轻薄如信用卡,安全胜过银行金库。</string>
<string name="settings_upgrade_banner_seed_phrase_feature_title">无助记词</string>
<string name="settings_upgrade_banner_superiority_feature_title">一流的硬件钱包</string>
<string name="settings_upgrade_banner_title">Tangem 冷钱包</string>
<string name="staking_account_initialization_footer">网络费用是处理和确认您在区块链上的交易时所需的小额费用。</string>
<string name="staking_account_initialization_message">要开始质押,您必须通过交易充值 1 TON 来激活您的 TON 账户。资金会保留在您的账户中,因为此步骤仅用于激活账户以进行质押。</string>
<string name="staking_account_initialization_title">激活账户</string>
<string name="staking_alert_network_fee_updated_message">网络费用已更改。请在继续操作前查看新金额。</string>
<string name="staking_alert_network_fee_updated_title">网络费用已更新</string>
<string name="staking_apr_earn_badge">年利率 %1$s%%</string>
<string name="staking_cardano_details_rewards_info_text">您的质押奖励将在 5 个周期(约 25 天)后开始发放,在此期间您的委托将由网络注册和统计。</string>
<string name="staking_details_annual_percentage_yield">年收益率</string>
<string name="staking_details_annual_percentage_yield_info">年利率 (APY):显示您一年内通过复利计算可获得的总利息。复利是指您获得的利息会添加到您的本金中,因此您还可以获得该利息的利息。</string>
<string name="staking_details_apy">APY</string>
<string name="staking_details_autocompound_rewards_earned">奖励将累积到您的质押余额中。已赚取资金: %s</string>
<string name="staking_max_amount_requirement_error">最高金额: %s</string>
<string name="staking_notification_ton_account_initialization_message">要开始质押,您需要先激活您的 TON 账户。</string>
<string name="staking_notification_ton_account_initialization_title">激活账户</string>
<string name="staking_solana_details_auto_claiming_rewards_daily_text">Solana 的奖励会自动添加到您的质押余额中,无法单独显示。</string>
<string name="swap_approve_description">批准后,您即允许智能合约在未来的交易中使用您的代币。</string>
<string name="swap_search_suggestion_hint">还在寻找其他代币?\n尝试搜索或探索其他加密货币</string>
<string name="swap_search_tooltip_description">搜索任何代币,即使它还不在你的列表中。</string>
<string name="swap_search_tooltip_title">使用搜索查找所需内容</string>
<string name="swap_your_assets_title">您的资产</string>
<string name="swapping_high_price_impact_text">由于流动性低,您收到的资金可能会大大减少。请尝试较小的金额或另一个提供商。</string>
<string name="swapping_insufficient_funds_description">账户余额不足,无法完成此交易。请减少收款金额或增加余额。</string>
<string name="swapping_swap_action_in_progress">互换...</string>
<string name="swapping_trade_too_large_text">此交易流动性不足,请减少金额或选择其他供应商。</string>
<string name="swapping_trade_too_large_title">交易额过大</string>
<string name="tangem_pay_beta_notification_subtitle">我们非常乐意收到您的反馈。</string>
<string name="tangem_pay_beta_notification_title">Tangem Pay 现已进入测试阶段</string>
<string name="tangem_pay_card_frozen">卡片已冻结</string>
<string name="tangem_pay_card_payment">卡片支付</string>
<string name="tangem_pay_deposit">存款</string>
<string name="tangem_pay_dispute">争议</string>
<string name="tangem_pay_explore_transaction">探索交易</string>
<string name="tangem_pay_fee_subtitle">服务费</string>
<string name="tangem_pay_fee_title">费用</string>
<string name="tangem_pay_freeze_card_alert_body">确保您的资金安全。您可以随时解冻。</string>
<string name="tangem_pay_freeze_card_alert_title">冻结您的卡片?</string>
<string name="tangem_pay_freeze_card_failed">冻结卡片失败,请稍后再试。</string>
<string name="tangem_pay_freeze_card_freeze">冻结</string>
<string name="tangem_pay_freeze_card_success">您的卡片已被冻结。</string>
<string name="tangem_pay_get_help">获取帮助</string>
<string name="tangem_pay_history_item_spend_mc_declined_reason">原因: %s</string>
<string name="tangem_pay_history_item_spend_mc_title_format" formatted="false">%s · %s</string>
<string name="tangem_pay_history_item_spend_mcc">MCC %s</string>
<string name="tangem_pay_other">其他</string>
<string name="tangem_pay_rooted_device_subtitle">无法在已 root 的设备上使用</string>
<string name="tangem_pay_status_completed">已完成</string>
<string name="tangem_pay_status_declined">拒绝</string>
<string name="tangem_pay_status_pending">待定</string>
<string name="tangem_pay_status_reversed">已反转</string>
<string name="tangem_pay_terms_fees_limits">条款、费用和限制</string>
<string name="tangem_pay_terms_limits">条款和限制</string>
<string name="tangem_pay_transaction_declined_notification_text">银行拒绝了这项交易请求。</string>
<string name="tangem_pay_transaction_fee_notification_text">这笔费用用于支付您办理转账时的费用。</string>
<string name="tangem_pay_transaction_reversed_notification_text">商家部分或全部撤销了交易</string>
<string name="tangem_pay_unfreeze_card_alert_body">继续使用您的资金。您可以随时冻结资金。</string>
<string name="tangem_pay_unfreeze_card_alert_title">要解冻您的卡片?</string>
<string name="tangem_pay_unfreeze_card_failed">卡片解冻失败,请稍后再试。</string>
<string name="tangem_pay_unfreeze_card_success">您的卡片已解冻。</string>
<string name="tangem_pay_withdrawal">提款</string>
<string name="tangempay_account_unable_to_use_rooted">无法在已root的设备上使用</string>
<string name="tangempay_cancel_kyc">从主屏幕隐藏 KYC 页面</string>
<string name="tangempay_card_details_add_funds">增加资金</string>
<string name="tangempay_card_details_add_funds_subtitle">充值选项</string>
<string name="tangempay_card_details_add_to_wallet_button_text">添加到 Google 钱包</string>
<string name="tangempay_card_details_card_number">卡号</string>
<string name="tangempay_card_details_change_pin">更改PIN码</string>
<string name="tangempay_card_details_change_pin_success_description">该卡已完全准备好用于支付。</string>
<string name="tangempay_card_details_change_pin_success_title">已创建 PIN 码</string>
<string name="tangempay_card_details_cvc">CVC</string>
<string name="tangempay_card_details_error_text">数据加载失败,请稍后再试。</string>
<string name="tangempay_card_details_expiry">到期</string>
<string name="tangempay_card_details_freeze_card">冻结卡片</string>
<string name="tangempay_card_details_hide_details">隐藏详情</string>
<string name="tangempay_card_details_hide_text">隐藏</string>
<string name="tangempay_card_details_open_wallet_button">打开 Google 钱包</string>
<string name="tangempay_card_details_open_wallet_notification_subtitle">只需轻点几下即可设置 Tangem Pay然后即可开始使用 Google Pay 付款。</string>
<string name="tangempay_card_details_open_wallet_notification_subtitle_apple">只需轻点几下即可设置 Tangem Pay然后即可开始使用 Apple Pay 付款。</string>
<string name="tangempay_card_details_open_wallet_notification_title">将您的卡片添加到 Google Pay</string>
<string name="tangempay_card_details_open_wallet_notification_title_apple">将您的卡片添加到 Apple Pay</string>
<string name="tangempay_card_details_open_wallet_step_1">打开 Google 钱包</string>
<string name="tangempay_card_details_open_wallet_step_1_5_apple">点击右上角的“+”按钮</string>
<string name="tangempay_card_details_open_wallet_step_1_apple">打开 Apple 钱包</string>
<string name="tangempay_card_details_open_wallet_step_2">点击“添加卡片”</string>
<string name="tangempay_card_details_open_wallet_step_2_apple">点击“借记卡或信用卡”</string>
<string name="tangempay_card_details_open_wallet_step_3">手动输入卡片信息</string>
<string name="tangempay_card_details_open_wallet_step_4">使用发送到您设备上的 OTP 来验证卡片。</string>
<string name="tangempay_card_details_open_wallet_step_5">一切就绪!您的卡已可以使用。</string>
<string name="tangempay_card_details_open_wallet_title">将卡片添加到 Google Pay</string>
<string name="tangempay_card_details_open_wallet_title_apple">将卡片添加到 Apple Pay</string>
<string name="tangempay_card_details_pin_code">PIN码</string>
<string name="tangempay_card_details_receive_description">分享您的地址或出示二维码</string>
<string name="tangempay_card_details_receive_error_description">检测到技术问题。请稍后再试或联系技术支持。</string>
<string name="tangempay_card_details_receive_error_title">目前无法接收</string>
<string name="tangempay_card_details_reveal_text">显示</string>
<string name="tangempay_card_details_show_details">显示详情</string>
<string name="tangempay_card_details_swap_description">将您投资组合中的任何资产互换到卡片</string>
<string name="tangempay_card_details_title">卡片详情</string>
<string name="tangempay_card_details_unfreeze_card">解冻卡片</string>
<string name="tangempay_card_details_view_pin_code_description">如果忘记了,请返回应用查看。</string>
<string name="tangempay_card_details_view_pin_code_title">您的PIN码</string>
<string name="tangempay_card_details_withdraw">提款</string>
<string name="tangempay_card_details_withdraw_error_title">目前无法提款</string>
<string name="tangempay_card_details_withdraw_in_progress_description">当前提款交易完成之前,您无法发起新的提款交易或互换交易。</string>
<string name="tangempay_card_details_withdraw_in_progress_title">提款进行中</string>
<string name="tangempay_card_page_settings_title">卡片设置</string>
<string name="tangempay_change_pin_code">更改PIN码</string>
<string name="tangempay_come_back_if_forget_pin">如果忘记了,请返回应用程序。</string>
<string name="tangempay_factory_settings_warning_title">我明白,我将完全失去对 Tangem Pay 卡及其上所有资金的访问权,且无法挽回</string>
<string name="tangempay_failed_to_issue_card">发卡失败</string>
<string name="tangempay_failed_to_issue_card_retry_description">发生技术故障,请点击下方按钮重试。</string>
<string name="tangempay_failed_to_issue_card_support_description">发生技术故障,请联系技术支持。</string>
<string name="tangempay_get_banner_description">免费领取您的 Tangem Visa 虚拟卡</string>
<string name="tangempay_get_tangem_pay">获取 Tangem Pay</string>
<string name="tangempay_go_to_support">前往支持页面</string>
<string name="tangempay_issue_card_notification_description">通常需要最多15分钟</string>
<string name="tangempay_issue_card_notification_title">设置您的 Tangem 卡</string>
<string name="tangempay_issuing_your_card">正在发放您的卡片</string>
<string name="tangempay_issuing_your_card_description">该卡通常会在 5 分钟内自动发放。极少数情况下,如果需要人工审核,则可能需要长达 48 小时。</string>
<string name="tangempay_kyc_card_ready_notification_title">Tangem Pay</string>
<string name="tangempay_kyc_confirm_cancellation_alert_title">确认取消</string>
<string name="tangempay_kyc_confirm_cancellation_description">您确定要停止 KYC 流程吗?您可以随时返回继续。</string>
<string name="tangempay_kyc_failed_description">我们无法验证您的个人资料。如有任何疑问,请联系客服。</string>
<string name="tangempay_kyc_failed_title">很抱歉,我们无法验证您的身份。 </string>
<string name="tangempay_kyc_has_failed">KYC 被拒</string>
<string name="tangempay_kyc_in_progress">KYC正在进行中</string>
<string name="tangempay_kyc_in_progress_notification_button">查看状态</string>
<string name="tangempay_kyc_in_progress_notification_title">Tangem Pay 的 KYC 流程正在进行中</string>
<string name="tangempay_kyc_in_progress_popup_description">文件通常会在 5 分钟内自动验证。极少数情况下,如果需要人工审核,则可能需要长达 48 小时。</string>
<string name="tangempay_kyc_rejected">已拒绝</string>
<string name="tangempay_kyc_rejected_button_text">隐藏 KYC 块</string>
<string name="tangempay_kyc_rejected_description">抱歉,我们无法验证</string>
<string name="tangempay_kyc_rejected_description_span">您的个人资料。</string>
<string name="tangempay_onboarding_banner_description">免费领取您的 Tangem Visa 虚拟卡</string>
<string name="tangempay_onboarding_banner_title">使用 USDC 进行日常支付</string>
<string name="tangempay_onboarding_get_card_button_text">获取卡片</string>
<string name="tangempay_onboarding_pay_description">数字卡可与 Apple Pay 和 Google Pay 一起使用</string>
<string name="tangempay_onboarding_pay_title">随意支配您的资产</string>
<string name="tangempay_onboarding_purchases_description">购买无需支付任何额外费用</string>
<string name="tangempay_onboarding_purchases_title">实际支付金额与所示金额一致</string>
<string name="tangempay_onboarding_security_description">将在不透露您的地址和资产的情况下创建一个单独的付款账户</string>
<string name="tangempay_onboarding_security_title">无与伦比的隐私保护</string>
<string name="tangempay_onboarding_title">几分钟内即可获得免费的 Tangem Pay 卡</string>
<string name="tangempay_payment_account">支付账户</string>
<string name="tangempay_payment_account_sync_needed">支付账户未同步</string>
<string name="tangempay_pin_validation_error_message">无效PIN码请避免使用连续或重复的密码。</string>
<string name="tangempay_service_unavailable_description">我们正在修复技术问题,请稍后再试。</string>
<string name="tangempay_service_unavailable_title">服务暂时不可用</string>
<string name="tangempay_service_unreachable_try_later">无法显示详细信息。但刷卡支付功能仍然可用。</string>
<string name="tangempay_set_pin_code">设置 PIN 码</string>
<string name="tangempay_sync_needed">会话已过期</string>
<string name="tangempay_sync_needed_restore_access">恢复访问权限</string>
<string name="tangempay_tangem_visa_card">使用 USDC 进行日常支付</string>
<string name="tangempay_temporarily_unavailable">Tangem Pay暂时无法使用。</string>
<string name="tangempay_title">Tangem Pay</string>
<string name="tangempay_use_tangem_device_to_restore_payment_account">点击下方按钮恢复访问权限</string>
<string name="tangempay_withdrawal_note_description">您的Polygon链上 USDC 余额与您的卡片余额不同,并在购买后 2 个工作日内更新。购物退款的资金不会退还至您的链上余额,也不能提现,但会保留在您的卡片余额中用于购物。</string>
<string name="tangempay_withdrawal_note_title">请注意</string>
<string name="tangempay_your_pin_code">您的PIN码</string>
<string name="token_button_unavailability_reason_yield_supply_approval">授权已被撤销。您的资金仍处于收益模式。如需执行操作,请进入收益模式并重新授予权限。</string>
<string name="token_market_metrics_no_data">不适用</string>
<string name="token_receive_show_qr_code_title">显示二维码</string>
<string name="transaction_history_transaction_for_address">为 %s</string>
<string name="update_approval_permission_fee_note">之前的授权将被撤销,并颁发新的授权。网络将对每次此类操作收取代币批准费。您将在交易记录中看到一笔金额为零的交易,作为撤销授权的证据。</string>
<string name="update_approval_permission_subtitle">交易金额超过先前授权的金额。\n请更新授权以继续</string>
<string name="update_approval_permission_title">更新权限</string>
<string name="upgrade_to_hardware_wallet_button_title">升级到硬件钱包</string>
<string name="user_push_notification_agreement_argument_one_subtitle">实时提醒交易、兑换和重要更新。</string>
<string name="user_push_notification_agreement_argument_one_title">交易提醒</string>
<string name="user_push_notification_agreement_argument_two_subtitle">抢先体验最新功能和专属优惠。</string>
<string name="user_push_notification_agreement_argument_two_title">专题报道和新闻更新</string>
<string name="user_push_notification_banner_subtitle">启用推送通知,即可在资金到账时收到提醒。</string>
<string name="user_push_notification_banner_title">不要错过任何一笔交易</string>
<string name="user_wallet_list_delete_hw_prompt">如果您在没有备份的情况下删除此钱包,您将永久失去对您的资金的访问权限。</string>
<string name="wallet_add_common_title">选择您的钱包类型</string>
<string name="wallet_add_hardware_description">扫描您的 Tangem 卡或指环以恢复卡号或从其他钱包导入卡号。</string>
<string name="wallet_add_hardware_info_create">创建硬件钱包</string>
<string name="wallet_add_hardware_purchase">想购买 Tangem 钱包吗?</string>
<string name="wallet_add_import_seed_phrase">导入助记词</string>
<string name="wallet_add_mobile_description">在手机上恢复您的钱包或从其他应用程序导入—方便,但安全性不如 Tangem 卡。</string>
<string name="wallet_add_support_title">选择哪个?</string>
<string name="wallet_connect_multiple_transactions">多部分交易</string>
<string name="wallet_connect_multiple_transactions_description">为了顺利完成交易,您的交易将被拆分成多个部分。您需要多次刷卡才能完成交易。</string>
<string name="wallet_connect_sending_multiple_explanation">交易正在处理中。请多次用卡轻触以完成交易。</string>
<string name="wallet_connect_sending_multiple_tx">交易进行中</string>
<string name="wallet_create_common_title">创建 Tangem 钱包</string>
<string name="warning_clore_migration_button">开始迁移</string>
<string name="warning_clore_migration_copy_button">复制</string>
<string name="warning_clore_migration_description">要继续使用您的资金,请根据 Clore 官方指南开始迁移。</string>
<string name="warning_clore_migration_error_signing_not_supported">该网络不支持信息签名</string>
<string name="warning_clore_migration_error_wallet_manager_not_found">无法签署信息。请重试。</string>
<string name="warning_clore_migration_message">根据 Clore 的官方文档,所有在 12 月 21 日之前收到的 Clore 代币ERC-20 代币)都将迁移到 Clore此日期之后收到的代币则不会迁移。迁移方案即将推出敬请期待。</string>
<string name="warning_clore_migration_message_label">消息</string>
<string name="warning_clore_migration_open_portal_button">打开索赔通道</string>
<string name="warning_clore_migration_sheet_description">要继续使用您的 Clore 代币,您必须根据索赔通道上的信息完成代币迁移。</string>
<string name="warning_clore_migration_sheet_title">Clore 网络迁移</string>
<string name="warning_clore_migration_sign_button">签署</string>
<string name="warning_clore_migration_signature_label">签名</string>
<string name="warning_clore_migration_title">Clore 网络迁移</string>
<string name="warning_express_unsupported_pair_description">此交易对不支持互换。请选择其他代币重试。</string>
<string name="warning_express_unsupported_pair_title">不支持的互换对</string>
<string name="wc_alert_add_network_to_portfolio_description">所需区块链 %s 尚未添加到您的投资组合中。请先添加,然后再进行连接。</string>
<string name="wc_alert_add_network_to_portfolio_title">将区块链添加到投资组合</string>
<string name="wc_alert_network_not_connected_description">要继续,请将您的 dApp 会话重新连接到所需的区块链网络 %s。</string>
<string name="wc_alert_network_not_connected_title">区块链网络未连接</string>
<string name="wc_alert_request_timeout_description">检查您的区块链连接</string>
<string name="wc_alert_request_timeout_title">请求超时</string>
<string name="wc_alert_unsupported_network_description">该区块链 %s Tangem Wallet 不支持,无法连接。</string>
<string name="wc_alert_unsupported_network_title">不支持的区块链</string>
<string name="wc_approve_description">通过批准,您允许 dApp 或智能合约在未来的交易中使用代币。</string>
<string name="welcome_create_wallet_already_have">已经拥有 Tangem 钱包?</string>
<string name="welcome_create_wallet_feature_assets">数千种资产</string>
<string name="welcome_create_wallet_feature_class">顶级硬件钱包</string>
<string name="welcome_create_wallet_feature_delivery">快速发货</string>
<string name="welcome_create_wallet_feature_one_tap">一触即开始</string>
<string name="welcome_create_wallet_feature_seamless">无缝且安全</string>
<string name="welcome_create_wallet_feature_seedphrase">无助记词</string>
<string name="welcome_create_wallet_feature_use">简单易用</string>
<string name="welcome_create_wallet_hardware_description">用 Tangem 创建硬件钱包。像银行卡一样纤薄,像银行保险库一样安全。</string>
<string name="welcome_create_wallet_mobile_description">创建或导入软件钱包</string>
<string name="welcome_create_wallet_mobile_description_full">在您的手机上创建或导入软件钱包。</string>
<string name="welcome_create_wallet_mobile_title">从手机钱包开始</string>
<string name="welcome_create_wallet_other_method">其他方法</string>
<string name="welcome_create_wallet_use_hardware_description">使用 Tangem 硬件钱包</string>
<string name="welcome_create_wallet_use_hardware_title">了解更多并购买</string>
<string name="yield_module_alert_description">启用收益模式后,所有未来充值到此地址的资金都将转入 Aave。您仍然可以自由管理您的资金。</string>
<string name="yield_module_alert_title">你的 %s 提供给 Aave</string>
<string name="yield_module_amount_not_transfered_to_aave_title">供应 %1$s %2$s 到 Aave</string>
<string name="yield_module_approve_needed_notification_cta">批准</string>
<string name="yield_module_approve_needed_notification_description">您的代币授权已被撤销。请重新授权代币以恢复服务功能。</string>
<string name="yield_module_approve_needed_notification_title">需要批准</string>
<string name="yield_module_approve_sheet_fee_note">费用将被扣除,您的资产将被重新提供。</string>
<string name="yield_module_approve_sheet_subtitle">要继续产生收益,需要获得批准。</string>
<string name="yield_module_approve_sheet_title">确认批准</string>
<string name="yield_module_balance_info_sheet_subtitle">您的资金目前已提供给 Aave 协议,但您可以随时对其进行管理。</string>
<string name="yield_module_balance_info_sheet_title">你的 %s 提供给 Aave</string>
<string name="yield_module_chart_loading_error">无法加载图表...</string>
<string name="yield_module_deposit_error_notification_title">供应 %1$s %2$s 到 Aave。</string>
<string name="yield_module_disable_button">禁用收益模式</string>
<string name="yield_module_earn_badge">APY %1$s%%</string>
<string name="yield_module_earn_sheet_available_title">可用</string>
<string name="yield_module_earn_sheet_current_apy_title">当前年收益率</string>
<string name="yield_module_earn_sheet_fee_description">充值贷款时,将从余额中扣除不超过 %1$s 的网络费。</string>
<string name="yield_module_earn_sheet_high_fee_description">目前网络费用过高,无法执行借贷。一旦降至 %1$s 或以下,将立即提供资金。</string>
<string name="yield_module_earn_sheet_my_funds_title">我的资金</string>
<string name="yield_module_earn_sheet_provider_description">您的 %1$s 现已部署到 Aave 并产生收益。您持有 %2$s 这些代币代表您的余额,并会自动累积收益。当您充值时,扣除手续费后,资金将提供给 Aave 以产生更多收益。</string>
<string name="yield_module_earn_sheet_title">收益模式</string>
<string name="yield_module_earn_sheet_total_earnings_title">总收益</string>
<string name="yield_module_earn_sheet_transfers_title">转至Aave</string>
<string name="yield_module_explore_sheet_explore_aave_button_title">探索 Aave</string>
<string name="yield_module_fee_policy_sheet_current_fee_note">这是 %s上的当前供应费。实际费用将显示在激活选项卡上。</string>
<string name="yield_module_fee_policy_sheet_current_fee_title">当前费用</string>
<string name="yield_module_fee_policy_sheet_description">在扣除交易费后,今后所有 %s 充值都将自动提供给 Aave。</string>
<string name="yield_module_fee_policy_sheet_fee_note">以后每次充值都会扣除大约 %1$s (%2$s) 的网络费,但不会超过 %3$s (%4$s) 的限额。</string>
<string name="yield_module_fee_policy_sheet_max_fee_note">如果网络费用超过最高限额,交易将无法完成,直到网络费用降低为止。您可以稍后更改此限额。</string>
<string name="yield_module_fee_policy_sheet_max_fee_title">最高费用</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">最低金额根据当前网络费用计算,确保不超过充值金额的 4%% ,相当于最低 %1$s (%2$s)。</string>
<string name="yield_module_fee_policy_sheet_min_amount_title">最低充值金额</string>
<string name="yield_module_fee_policy_sheet_title">充值费用政策</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem 还从产生的收益中提取 15% 服务费。</string>
<string name="yield_module_high_fee_error">一旦网络费用降低或您的余额达到最低金额要求,您的资金将自动转入 Aave。</string>
<string name="yield_module_high_network_fees_notification_description">由于市场活跃,手续费高于平时。您可以立即进行交易,也可以稍后手续费降低时再来查看。</string>
<string name="yield_module_high_network_fees_notification_title">高昂的网络费用</string>
<string name="yield_module_historical_returns">历史回报</string>
<string name="yield_module_main_screen_promo_banner_message">启动 %1$s%% 您余额的APY</string>
<string name="yield_module_main_view_approve_notification_description">您的代币在收益模式下的授权已被撤销。请打开代币以重新授予权限。</string>
<string name="yield_module_main_view_approve_notification_title">需要代币批准</string>
<string name="yield_module_network_fee_unreachable_notification_description">检查您的网络连接</string>
<string name="yield_module_network_fee_unreachable_notification_title">网络费用信息无法获取</string>
<string name="yield_module_promo_screen_auto_balance_subtitle">每次充值都将自动提供给 Aave。</string>
<string name="yield_module_promo_screen_auto_balance_subtitle_v2">您账户上的所有 %1$s 都将自动供应给 Aave。</string>
<string name="yield_module_promo_screen_auto_balance_title">自动供应至Aave</string>
<string name="yield_module_promo_screen_cash_out_subtitle">您可以随时随地发送、兑换或出售您的资金。</string>
<string name="yield_module_promo_screen_cash_out_title">无锁仓</string>
<string name="yield_module_promo_screen_how_it_works_button_title">它是如何运作的?</string>
<string name="yield_module_promo_screen_self_custodial_subtitle">Aave 是一个链上协议,提供非托管流动性市场,使用户能够以可变利率累积收益。</string>
<string name="yield_module_promo_screen_self_custodial_title">去中心化和自托管</string>
<string name="yield_module_promo_screen_terms_disclaimer">使用此服务即表示您同意服务提供商的条款\n%1$s 和 %2$s</string>
<string name="yield_module_promo_screen_title">连接到 Aave</string>
<string name="yield_module_promo_screen_title_v2">启动 %1$s%% APY\非您的余额</string>
<string name="yield_module_promo_screen_variable_rate_info">Aave %1$s%% - 可变利率</string>
<string name="yield_module_promo_screen_variable_rate_info_v2">浮动利率</string>
<string name="yield_module_provider">Aave</string>
<string name="yield_module_rate_info_sheet_chart_average">平均值 %s</string>
<string name="yield_module_rate_info_sheet_chart_title">去年的收益</string>
<string name="yield_module_rate_info_sheet_description">当前利率始终是浮动的,由 Aave 的链上智能合约根据实时供求关系自动计算。</string>
<string name="yield_module_rate_info_sheet_powered_by">技术支持</string>
<string name="yield_module_rate_info_sheet_title">利率为浮动利率</string>
<string name="yield_module_receive_sheet_description">充值后,您的资金将自动转入 Aave 账户,开始产生收益。 %s 将从中扣除交易手续费。</string>
<string name="yield_module_start_earning">供应资产</string>
<string name="yield_module_start_earning_sheet_description">您的 %s 将提供给 Aave ,没有锁仓,并且可以完全访问。</string>
<string name="yield_module_start_earning_sheet_fee_policy">请参见充值费用政策</string>
<string name="yield_module_start_earning_sheet_next_deposits">您的下一笔充值将自动发送至 Aave。</string>
<string name="yield_module_start_earning_sheet_next_deposits_v2">您未来所有的收入 %1$s 存款将自动存入 Aave。</string>
<string name="yield_module_status_active">活跃</string>
<string name="yield_module_status_paused">暂停</string>
<string name="yield_module_stop_earning">禁用收益模式</string>
<string name="yield_module_stop_earning_sheet_description">关闭此功能会将您的资产从 Aave 提取,并将其转换回 %s 放入钱包,同时收益累积停止。</string>
<string name="yield_module_stop_earning_sheet_fee_note">退出收益模式时,区块链会收取网络费用。</string>
<string name="yield_module_stop_earning_sheet_title">禁用收益模式</string>
<string name="yield_module_supply">供应</string>
<string name="yield_module_supply_apr">供应 APY</string>
<string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">利息自动累积。</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">利息自动累积</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">收益模式</string>
<string name="yield_module_token_details_earn_notification_processing">启用收益模式</string>
<string name="yield_module_token_details_earn_notification_title">收益模式</string>
<string name="yield_module_transaction_deploy_contract">收益模式合约部署</string>
<string name="yield_module_transaction_enter">已启用收益模式</string>
<string name="yield_module_transaction_enter_subtitle">%1$s 供应给 Aave</string>
<string name="yield_module_transaction_exit">已禁用收益模式</string>
<string name="yield_module_transaction_exit_subtitle">%1$s 已从 Aave 提款</string>
<string name="yield_module_transaction_initialize">收益模式已初始化</string>
<string name="yield_module_transaction_reactivate">收益模式重新激活</string>
<string name="yield_module_transaction_topup">供应给 Aave</string>
<string name="yield_module_transaction_topup_subtitle">%1$s 供应给 Aave</string>
<string name="yield_module_transaction_withdraw">从 Aave提款</string>
<string name="yield_module_transfer_mode_automatic">自动</string>
<string name="yield_module_unable_to_cover_fee_description">添加 %1$s %2$s 以支付交易网络费。</string>
<string name="yield_module_unable_to_cover_fee_title">无法覆盖 %s 费用</string>
<string name="yield_module_unavailable_subtitle">收益模式暂时不可用。请稍后再试。</string>
<string name="yield_module_unavailable_title">收益模式不可用</string>
<string name="yield_supply_chart_loading_error">无法加载图表...</string>
</resources>

View file

@ -1535,6 +1535,7 @@
<string name="swapping_trade_too_large_title">Trade too large</string>
<string name="tangem_pay_beta_notification_subtitle">We would be happy to receive your feedback</string>
<string name="tangem_pay_beta_notification_title">Tangem Pay is now in beta</string>
<string name="tangem_pay_card_details_unable_to_rename_card_title">Unable to rename card</string>
<string name="tangem_pay_card_frozen">Card frozen</string>
<string name="tangem_pay_card_payment">Card payment</string>
<string name="tangem_pay_deposit">Deposit</string>
@ -1601,10 +1602,13 @@
<string name="tangempay_card_details_receive_description">Share your address or show QR-code</string>
<string name="tangempay_card_details_receive_error_description">Technical issues detected. Please try again later or contact support.</string>
<string name="tangempay_card_details_receive_error_title">Receive unavailable now</string>
<string name="tangempay_card_details_rename_card_invalid_description">Only letters and numbers are allowed.</string>
<string name="tangempay_card_details_rename_card_invalid_title">Invalid characters</string>
<string name="tangempay_card_details_reveal_text">Reveal</string>
<string name="tangempay_card_details_show_details">Show details</string>
<string name="tangempay_card_details_swap_description">Swap any asset in your portfolio for card</string>
<string name="tangempay_card_details_title">Card details</string>
<string name="tangempay_card_details_unable_to_rename_card_description">Please try again later.</string>
<string name="tangempay_card_details_unfreeze_card">Unfreeze Card</string>
<string name="tangempay_card_details_view_pin_code_description">Come back to the app if you forget it.</string>
<string name="tangempay_card_details_view_pin_code_title">Your PIN code</string>
@ -1692,6 +1696,10 @@
<string name="token_details_staking_block_title">Staking Service</string>
<string name="token_details_token_type_subtitle">%1$s token in %%image%% %2$s network</string>
<string name="token_details_token_type_subtitle_no_standard">Token in %%image%% %1$s network</string>
<string name="token_details_toolbar_subtitle_network">%s network</string>
<string name="token_details_toolbar_subtitle_standard">%1$s in %2$s network</string>
<string name="token_details_toolbar_title_token_in_account">%1$s in %%image%% %2$s</string>
<string name="token_details_toolbar_title_token_in_wallet">%1$s in %2$s %%image%%</string>
<string name="token_details_unable_hide_alert_message">The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list</string>
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
<string name="token_market_metrics_no_data">N/A</string>

View file

@ -11,6 +11,9 @@ android {
namespace = "com.tangem.features.tokendetails.impl"
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
dependencies {
/** AndroidX */
@ -113,4 +116,9 @@ dependencies {
implementation(deps.decompose.ext.compose)
/** Tests */
testImplementation(deps.test.junit5)
testRuntimeOnly(deps.test.junit5.engine)
testImplementation(deps.test.mockk)
testImplementation(deps.test.truth)
}

View file

@ -97,7 +97,6 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
TokenDetailsScreen(
tokenDetailsUM = tokenDetailsUM,
tokenMarketBlockComponent = tokenMarketBlockComponent,
modifier = modifier,
)
} else {

View file

@ -0,0 +1,73 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.model
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.tokendetails.impl.R
import javax.inject.Inject
@ModelScoped
internal class TokenDetailsDialogFactory @Inject constructor(
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
) {
fun showConfirmHideToken(currency: CryptoCurrency, onConfirm: () -> Unit) {
uiMessageSender.send(
DialogMessage(
title = resourceReference(
id = R.string.token_details_hide_alert_title,
formatArgs = wrappedList(currency.name),
),
message = resourceReference(R.string.token_details_hide_alert_message),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.token_details_hide_alert_hide),
isWarning = true,
onClick = onConfirm,
)
},
secondActionBuilder = { cancelAction() },
),
)
}
fun showLinkedTokens(currency: CryptoCurrency) {
uiMessageSender.send(
DialogMessage(
title = resourceReference(
id = R.string.token_details_unable_hide_alert_title,
formatArgs = wrappedList(currency.symbol),
),
message = resourceReference(
id = R.string.token_details_unable_hide_alert_message,
formatArgs = wrappedList(currency.name, currency.symbol, currency.network.name),
),
),
)
}
fun showDismissIncompleteTransactionConfirm(onConfirm: () -> Unit) {
uiMessageSender.send(
DialogMessage(
message = resourceReference(R.string.warning_kaspa_unfinished_token_transaction_discard_message),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_yes),
onClick = onConfirm,
)
},
secondActionBuilder = { cancelAction() },
),
)
}
fun showError(text: TextReference) {
uiMessageSender.send(DialogMessage(message = text))
}
}

View file

@ -25,6 +25,10 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.OfframpAnalyticsEvent
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -32,9 +36,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.common.ui.tokens.getUnavailabilityReasonText
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
@ -86,6 +88,7 @@ import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
@ -98,13 +101,14 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.Token
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceTypeUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsStateController
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.TokenDetailsExpressStatusFactory
import com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer.InitializeWithCryptoCurrencyTransformer
import com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer.SetTopBarTitleTransformer
import com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer.UpdateTopBarMenuTransformer
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.tokendetails.impl.R
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
@ -114,7 +118,6 @@ import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import com.tangem.utils.extensions.isZero
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -172,6 +175,13 @@ internal class TokenDetailsModel @Inject constructor(
private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
private val dynamicAddressesRepository: DynamicAddressesRepository,
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
private val dialogFactory: TokenDetailsDialogFactory,
private val userWalletsListRepository: UserWalletsListRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletIconUMConverter: WalletIconUMConverter,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val redesignStateController: TokenDetailsStateController,
) : Model(),
TokenDetailsClickIntents,
ExpressTransactionsClickIntents,
@ -213,11 +223,10 @@ internal class TokenDetailsModel @Inject constructor(
userWalletId = userWalletId,
)
private val internalUiState = MutableStateFlow(stateFactory.getInitialState(cryptoCurrency))
val uiState: StateFlow<TokenDetailsState> = internalUiState
val uiState: StateFlow<TokenDetailsState>
field = MutableStateFlow(stateFactory.getInitialState(cryptoCurrency))
private val internalRedesignUiState = MutableStateFlow(createInitialRedesignState())
val redesignUiState: StateFlow<TokenDetailsUM> = internalRedesignUiState
val redesignUiState: StateFlow<TokenDetailsUM> get() = redesignStateController.uiState
// region Clore migration
// TODO: Remove after Clore migration ends ([REDACTED_TASK_KEY])
@ -279,7 +288,10 @@ internal class TokenDetailsModel @Inject constructor(
}
init {
initRedesignState()
updateTopBarMenu()
updateRedesignTopBarMenu()
observeRedesignTopBarTitle()
initButtons()
updateContent()
handleBalanceHiding()
@ -329,7 +341,7 @@ internal class TokenDetailsModel @Inject constructor(
private fun handleBalanceHiding() {
getBalanceHidingSettingsUseCase()
.onEach { settings ->
internalUiState.value = stateFactory.getStateWithUpdatedHidden(
uiState.value = stateFactory.getStateWithUpdatedHidden(
isBalanceHidden = settings.isBalanceHidden,
)
}
@ -345,7 +357,7 @@ internal class TokenDetailsModel @Inject constructor(
.distinctUntilChanged()
.onEach { state ->
sendButtonsEvents(state.states)
internalUiState.value = stateFactory.getManageButtonsState(actions = state.states)
uiState.value = stateFactory.getManageButtonsState(actions = state.states)
}
.flowOn(dispatchers.main)
.launchIn(modelScope)
@ -377,8 +389,8 @@ internal class TokenDetailsModel @Inject constructor(
.distinctUntilChanged()
.onEach { warnings ->
val updatedState = stateFactory.getStateWithNotifications(warnings)
notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications)
internalUiState.value = updatedState
notificationsAnalyticsSender.send(uiState.value, updatedState.notifications)
uiState.value = updatedState
}
.launchIn(modelScope)
.saveIn(warningsJobHolder)
@ -391,7 +403,7 @@ internal class TokenDetailsModel @Inject constructor(
.map { it.status.right() }
.distinctUntilChanged()
.onEach { maybeCurrencyStatus ->
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus)
uiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus)
maybeCurrencyStatus.onRight { status ->
sendOneTimeBalanceLoadedAnalyticsEvent(status)
cryptoCurrencyStatus = status
@ -413,7 +425,7 @@ internal class TokenDetailsModel @Inject constructor(
.distinctUntilChanged()
.onEach { waitForFirstExpressStatusEmmit.value = true }
.onEach { expressTxs ->
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
uiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
expressTxs = expressTxs,
updateBalance = ::updateNetworkToSwapBalance,
)
@ -424,11 +436,11 @@ internal class TokenDetailsModel @Inject constructor(
delay = EXPRESS_STATUS_UPDATE_DELAY,
task = {
runSuspendCatching {
expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs)
expressStatusFactory.getUpdatedExpressStatuses(uiState.value.expressTxs)
}
},
onSuccess = { updatedTxs ->
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
uiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
updatedTxs,
::updateNetworkToSwapBalance,
)
@ -449,14 +461,14 @@ internal class TokenDetailsModel @Inject constructor(
}
yieldSupplyGetRewardsBalanceUseCase(status = status, appCurrency = selectedAppCurrencyFlow.value)
.onEach { formatted ->
internalUiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(formatted)
uiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(formatted)
}
.flowOn(dispatchers.main)
.launchIn(modelScope)
.saveIn(yieldSupplyBalanceJobHolder)
} else {
yieldSupplyBalanceJobHolder.cancel()
internalUiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(
uiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(
YieldSupplyRewardBalance.empty(),
)
}
@ -493,7 +505,7 @@ internal class TokenDetailsModel @Inject constructor(
null
}
internalUiState.update { state ->
uiState.update { state ->
stateFactory.getStakingInfoState(
state = state,
stakingEntryInfo = stakingEntryInfo,
@ -517,7 +529,7 @@ internal class TokenDetailsModel @Inject constructor(
val isSupported = isXPUBSupported()
val isDynamicAddressesAvailable = isSupported && isDynamicAddressesAvailable()
internalUiState.value = stateFactory.getStateWithUpdatedMenu(
uiState.value = stateFactory.getStateWithUpdatedMenu(
userWallet = userWallet,
hasDerivations = hasDerivations,
isSupported = isSupported,
@ -890,7 +902,7 @@ internal class TokenDetailsModel @Inject constructor(
}
override fun onRefreshSwipe(isRefreshing: Boolean) {
internalUiState.value = stateFactory.getRefreshingState()
uiState.value = stateFactory.getRefreshingState()
modelScope.launch(dispatchers.main) {
listOf(
@ -902,29 +914,29 @@ internal class TokenDetailsModel @Inject constructor(
subscribeOnExpressTransactionsUpdates()
},
).awaitAll()
internalUiState.value = stateFactory.getRefreshedState()
uiState.value = stateFactory.getRefreshedState()
}.saveIn(refreshStateJobHolder)
}
override fun onDismissBottomSheet() {
when (val bsContent = internalUiState.value.bottomSheetConfig?.content) {
when (val bsContent = uiState.value.bottomSheetConfig?.content) {
is ExpressStatusBottomSheetConfig -> {
modelScope.launch(dispatchers.main) {
expressStatusFactory.removeTransactionOnBottomSheetClosed(bsContent.value)
}
}
}
internalUiState.value = stateFactory.getStateWithClosedBottomSheet()
uiState.value = stateFactory.getStateWithClosedBottomSheet()
}
override fun onCloseRentInfoNotification() {
internalUiState.value = stateFactory.getStateWithRemovedRentNotification()
uiState.value = stateFactory.getStateWithRemovedRentNotification()
}
override fun onExpressTransactionClick(txId: String) {
val expressTxState = internalUiState.value.expressTxsToDisplay.firstOrNull { it.info.txId == txId }
val expressTxState = uiState.value.expressTxsToDisplay.firstOrNull { it.info.txId == txId }
?: return
internalUiState.value = expressStatusFactory.getStateWithExpressStatusBottomSheet(expressTxState)
uiState.value = expressStatusFactory.getStateWithExpressStatusBottomSheet(expressTxState)
}
override fun onGoToProviderClick(url: String) {
@ -1018,7 +1030,7 @@ internal class TokenDetailsModel @Inject constructor(
}
},
ifRight = {
internalUiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
uiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
},
)
}
@ -1060,7 +1072,7 @@ internal class TokenDetailsModel @Inject constructor(
showErrorDialog(message)
}
},
ifRight = { internalUiState.value = stateFactory.getStateWithRemovedRequiredTrustlineNotification() },
ifRight = { uiState.value = stateFactory.getStateWithRemovedRequiredTrustlineNotification() },
)
}
}
@ -1086,7 +1098,7 @@ internal class TokenDetailsModel @Inject constructor(
TangemLogger.e("Error: $e")
},
ifRight = {
internalUiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
uiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
},
)
}
@ -1120,13 +1132,13 @@ internal class TokenDetailsModel @Inject constructor(
}
}
},
ifRight = { internalUiState.value = stateFactory.getStateWithRemovedHederaAssociateNotification() },
ifRight = { uiState.value = stateFactory.getStateWithRemovedHederaAssociateNotification() },
)
}
}
override fun onBalanceSelect(config: TokenBalanceSegmentedButtonConfig) {
internalUiState.value = stateFactory.getStateWithUpdatedBalanceSegmentedButtonConfig(config)
uiState.value = stateFactory.getStateWithUpdatedBalanceSegmentedButtonConfig(config)
}
override fun onConfirmDisposeExpressStatus() {
@ -1134,7 +1146,7 @@ internal class TokenDetailsModel @Inject constructor(
}
override fun onDisposeExpressStatus() {
val bottomSheetState = internalUiState.value.bottomSheetConfig?.content
val bottomSheetState = uiState.value.bottomSheetConfig?.content
if (bottomSheetState is ExpressStatusBottomSheetConfig) {
modelScope.launch {
expressStatusFactory.removeTransactionOnBottomSheetClosed(
@ -1143,7 +1155,7 @@ internal class TokenDetailsModel @Inject constructor(
)
}
}
internalUiState.value = stateFactory.getStateWithClosedBottomSheet()
uiState.value = stateFactory.getStateWithClosedBottomSheet()
}
override fun onYieldInfoClick() {
@ -1194,52 +1206,16 @@ internal class TokenDetailsModel @Inject constructor(
}
private fun showConfirmHideTokenDialog(currency: CryptoCurrency) {
uiMessageSender.send(
DialogMessage(
title = resourceReference(
id = R.string.token_details_hide_alert_title,
formatArgs = wrappedList(currency.name),
),
message = resourceReference(R.string.token_details_hide_alert_message),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.token_details_hide_alert_hide),
isWarning = true,
onClick = ::onHideConfirmed,
)
},
secondActionBuilder = { cancelAction() },
),
)
dialogFactory.showConfirmHideToken(currency = currency, onConfirm = ::onHideConfirmed)
}
private fun showLinkedTokensDialog(currency: CryptoCurrency) {
uiMessageSender.send(
DialogMessage(
title = resourceReference(
id = R.string.token_details_unable_hide_alert_title,
formatArgs = wrappedList(currency.symbol),
),
message = resourceReference(
id = R.string.token_details_unable_hide_alert_message,
formatArgs = wrappedList(currency.name, currency.symbol, currency.network.name),
),
),
)
dialogFactory.showLinkedTokens(currency = currency)
}
private fun showDismissIncompleteTransactionConfirmDialog() {
uiMessageSender.send(
DialogMessage(
message = resourceReference(R.string.warning_kaspa_unfinished_token_transaction_discard_message),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_yes),
onClick = ::onConfirmDismissIncompleteTransactionClick,
)
},
secondActionBuilder = { cancelAction() },
),
dialogFactory.showDismissIncompleteTransactionConfirm(
onConfirm = ::onConfirmDismissIncompleteTransactionClick,
)
}
@ -1262,7 +1238,7 @@ internal class TokenDetailsModel @Inject constructor(
}
private fun showErrorDialog(text: TextReference) {
uiMessageSender.send(DialogMessage(message = text))
dialogFactory.showError(text = text)
}
private fun checkForActionUpdates() {
@ -1402,29 +1378,79 @@ internal class TokenDetailsModel @Inject constructor(
// endregion Clore migration
private fun createInitialRedesignState(): TokenDetailsUM {
return TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
title = stringReference(cryptoCurrency.name),
subtitle = stringReference(cryptoCurrency.symbol),
menuItems = persistentListOf(),
private fun updateRedesignTopBarMenu() {
modelScope.launch(dispatchers.main) {
val hasDerivations = networkHasDerivationUseCase(
userWallet = userWallet,
network = cryptoCurrency.network,
).getOrElse { false }
val isSupported = isXPUBSupported()
redesignStateController.update(
UpdateTopBarMenuTransformer(
userWallet = userWallet,
hasDerivations = hasDerivations,
isXPubSupported = isSupported,
onGenerateExtendedKey = ::onGenerateExtendedKey,
onHideClick = ::onHideClick,
),
)
}
}
private fun initRedesignState() {
redesignStateController.update(
InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = ::onBackClick,
),
balanceBlockUM = TokenDetailsBalanceBlockUM.Loading(
actionButtons = persistentListOf(),
tokenBalanceTypeUM = TokenBalanceTypeUM.Single,
currencyIconState = CurrencyIconState.Loading,
),
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = cryptoCurrency.symbol),
stakingBlocksState = null,
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
isBalanceHidden = false,
isMarketPriceAvailable = false,
)
}
private fun observeRedesignTopBarTitle() {
combine(
flow = userWalletsListRepository.userWallets.filterNotNull(),
flow2 = isAccountsModeEnabledUseCase.invoke(),
flow3 = singleAccountListSupplier(userWalletId),
flow4 = getAccountCryptoCurrencyStatusUseCase(userWalletId, cryptoCurrency)
.map { status -> status.account }
.distinctUntilChanged(),
) { wallets, accountsModeEnabled, accountList, currentAccount ->
val currentWallet = wallets.firstOrNull { it.walletId == userWalletId } ?: userWallet
TopBarTitleInputs(
hasMultipleWallets = wallets.size > 1,
hasMultipleAccounts = accountsModeEnabled && accountList.accounts.size > 1,
walletName = currentWallet.name,
deviceIconUM = walletIconUMConverter.convert(getWalletIconUseCase(currentWallet)),
account = currentAccount,
)
}
.distinctUntilChanged()
.onEach { inputs ->
redesignStateController.update(
SetTopBarTitleTransformer(
cryptoCurrency = cryptoCurrency,
hasMultipleWallets = inputs.hasMultipleWallets,
hasMultipleAccounts = inputs.hasMultipleAccounts,
walletName = inputs.walletName,
deviceIconUM = inputs.deviceIconUM,
account = inputs.account,
),
)
}
.flowOn(dispatchers.default)
.launchIn(modelScope)
}
private data class TopBarTitleInputs(
val hasMultipleWallets: Boolean,
val hasMultipleAccounts: Boolean,
val walletName: String,
val deviceIconUM: DeviceIconUM,
val account: Account.CryptoPortfolio?,
)
private companion object {
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
const val BASE_DERIVATION_NODE_COUNT = 5

View file

@ -0,0 +1,54 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import javax.inject.Inject
@ModelScoped
internal class TokenDetailsStateController @Inject constructor() {
val uiState: StateFlow<TokenDetailsUM>
field = MutableStateFlow(value = getInitialState())
val value: TokenDetailsUM get() = uiState.value
fun update(function: (TokenDetailsUM) -> TokenDetailsUM) {
uiState.update(function = function)
}
fun update(transformer: Transformer<TokenDetailsUM>) {
uiState.update(function = transformer::transform)
}
private fun getInitialState(): TokenDetailsUM {
return TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = TokenDetailsTopAppBarUM.TitleState.Simple(tokenName = ""),
subtitle = TextReference.EMPTY,
onBackClick = {},
menuItems = persistentListOf(),
),
balanceBlockUM = TokenDetailsBalanceBlockUM.Loading(
actionButtons = persistentListOf(),
tokenBalanceTypeUM = TokenBalanceTypeUM.Single,
currencyIconState = CurrencyIconState.Loading,
),
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = ""),
stakingBlocksState = null,
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
isBalanceHidden = false,
isMarketPriceAvailable = false,
)
}
}

View file

@ -1,12 +1,15 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state
import androidx.compose.runtime.Stable
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
@Stable
@Immutable
internal data class TokenDetailsUM(
val topAppBarUM: TokenDetailsTopAppBarUM,
val balanceBlockUM: TokenDetailsBalanceBlockUM,
@ -17,8 +20,31 @@ internal data class TokenDetailsUM(
val isMarketPriceAvailable: Boolean,
)
@Immutable
internal data class TokenDetailsTopAppBarUM(
val title: TextReference,
val titleState: TitleState,
val subtitle: TextReference,
val menuItems: ImmutableList<TextReference>,
)
val onBackClick: () -> Unit,
val menuItems: ImmutableList<TangemDropdownMenuItem>,
) {
@Immutable
sealed interface TitleState {
val tokenName: String
data class Simple(
override val tokenName: String,
) : TitleState
data class WithWallet(
override val tokenName: String,
val walletName: String,
val deviceIconUM: DeviceIconUM,
) : TitleState
data class WithAccount(
override val tokenName: String,
val accountName: TextReference,
val accountIconUM: AccountIconUM.CryptoPortfolio,
) : TitleState
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.utils.transformer.Transformer
internal class InitializeWithCryptoCurrencyTransformer(
private val cryptoCurrency: CryptoCurrency,
private val onBackClick: () -> Unit,
) : Transformer<TokenDetailsUM> {
override fun transform(prevState: TokenDetailsUM): TokenDetailsUM = prevState.copy(
topAppBarUM = prevState.topAppBarUM.copy(
titleState = TokenDetailsTopAppBarUM.TitleState.Simple(tokenName = cryptoCurrency.name),
subtitle = stringReference(cryptoCurrency.symbol),
onBackClick = onBackClick,
),
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = cryptoCurrency.symbol),
)
}

View file

@ -0,0 +1,78 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.tangem.common.ui.account.AccountIconUM
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.utils.transformer.Transformer
import com.tangem.core.res.R as CoreResR
internal class SetTopBarTitleTransformer(
private val cryptoCurrency: CryptoCurrency,
private val hasMultipleWallets: Boolean,
private val hasMultipleAccounts: Boolean,
private val walletName: String,
private val deviceIconUM: DeviceIconUM,
private val account: Account.CryptoPortfolio?,
) : Transformer<TokenDetailsUM> {
override fun transform(prevState: TokenDetailsUM): TokenDetailsUM = prevState.copy(
topAppBarUM = prevState.topAppBarUM.copy(
titleState = createTitleState(),
subtitle = createSubtitle(),
),
)
private fun createTitleState(): TitleState {
val tokenName = cryptoCurrency.name
return when {
hasMultipleAccounts && account != null -> {
val accountNameUM = account.accountName.toUM()
TitleState.WithAccount(
tokenName = tokenName,
accountName = accountNameUM.value,
accountIconUM = AccountIconUM.CryptoPortfolio(
value = account.icon.value,
color = account.icon.color,
),
)
}
hasMultipleWallets -> TitleState.WithWallet(
tokenName = tokenName,
walletName = walletName,
deviceIconUM = deviceIconUM,
)
else -> TitleState.Simple(tokenName = tokenName)
}
}
private fun createSubtitle(): TextReference {
val networkName = cryptoCurrency.network.name
return when (cryptoCurrency) {
is CryptoCurrency.Token -> {
val standardName = cryptoCurrency.network.standardType
.takeIf { it !is Network.StandardType.Unspecified }
?.name
if (standardName != null) {
resourceReference(
CoreResR.string.token_details_toolbar_subtitle_standard,
wrappedList(standardName, networkName),
)
} else {
resourceReference(CoreResR.string.token_details_toolbar_subtitle_network, wrappedList(networkName))
}
}
is CryptoCurrency.Coin -> {
resourceReference(CoreResR.string.token_details_toolbar_subtitle_network, wrappedList(networkName))
}
}
}
}

View file

@ -0,0 +1,51 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.themedColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class UpdateTopBarMenuTransformer(
private val userWallet: UserWallet,
private val hasDerivations: Boolean,
private val isXPubSupported: Boolean,
private val onGenerateExtendedKey: () -> Unit,
private val onHideClick: () -> Unit,
) : Transformer<TokenDetailsUM> {
override fun transform(prevState: TokenDetailsUM): TokenDetailsUM = prevState.copy(
topAppBarUM = prevState.topAppBarUM.copy(menuItems = createMenuItems()),
)
private fun createMenuItems() = if (userWallet is UserWallet.Cold &&
userWallet.cardTypesResolver.isSingleWalletWithToken()
) {
persistentListOf()
} else {
buildList {
if (isXPubSupported && hasDerivations) {
add(
TangemDropdownMenuItem(
title = resourceReference(R.string.token_details_generate_xpub),
textColor = themedColor { TangemTheme.colors.text.primary1 },
onClick = onGenerateExtendedKey,
),
)
}
add(
TangemDropdownMenuItem(
title = resourceReference(R.string.token_details_hide_token),
textColor = themedColor { TangemTheme.colors.text.warning },
onClick = onHideClick,
),
)
}.toImmutableList()
}
}

View file

@ -1,30 +1,148 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import android.content.res.Configuration
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingTopBar
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.themedColor
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceTypeUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
import dev.chrisbanes.haze.HazeProgressive
import dev.chrisbanes.haze.HazeTint
import kotlinx.collections.immutable.persistentListOf
@Suppress("UnusedParameter")
@Composable
internal fun TokenDetailsScreen(
tokenDetailsUM: TokenDetailsUM,
tokenMarketBlockComponent: TokenMarketBlockComponent?,
modifier: Modifier = Modifier,
) {
internal fun TokenDetailsScreen(tokenDetailsUM: TokenDetailsUM, modifier: Modifier = Modifier) {
val topAppBarUM = tokenDetailsUM.topAppBarUM
// TODO [REDACTED_TASK_KEY] Token Details Make Balance with actions
val balanceBlockHeight = 200.dp
val partialCollapsedHeight = 0.dp
val expandedHeight = balanceBlockHeight + partialCollapsedHeight
val behavior = rememberTangemExitUntilCollapsedScrollBehavior(
expandedHeight = expandedHeight,
partialCollapsedHeight = partialCollapsedHeight,
)
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Text(
text = "Token Details Redesign",
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
Box(
modifier = Modifier
.fillMaxSize()
.hazeSourceTangem(zIndex = -2f),
) {
TangemCollapsingTopBar(
state = behavior.state,
collapsingPart = {
// [REDACTED_TASK_KEY] Token Details Make Balance with actions
Box(
modifier = Modifier
.fillMaxWidth()
.height(balanceBlockHeight),
)
},
body = {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.nestedScroll(behavior.nestedScrollConnection),
) {
// TODO [REDACTED_TASK_KEY] Token Details Make Transaction History
}
},
)
}
val rootBackground by LocalRootBackgroundColor.current
val hazeIntensity by animateFloatAsState(
targetValue = (behavior.state.collapsedFraction * 2f).coerceIn(0f, 1f),
label = "TopBarHazeIntensity",
)
Box(
modifier = Modifier.hazeEffectTangem {
fallbackTint = HazeTint(rootBackground.copy(alpha = hazeIntensity / 2f))
progressive = HazeProgressive.verticalGradient(
startIntensity = hazeIntensity,
endIntensity = 0f,
preferPerformance = true,
)
},
) {
TokenDetailsTopBar(topAppBarUM = topAppBarUM)
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun TokenDetailsScreen_Preview() {
TangemThemePreviewRedesign {
TokenDetailsScreen(
tokenDetailsUM = TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = TitleState.WithAccount(
tokenName = "Tether",
accountName = stringReference("Portfolio"),
accountIconUM = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
),
),
subtitle = stringReference("ERC-20 in Ethereum network"),
onBackClick = {},
menuItems = persistentListOf(
TangemDropdownMenuItem(
title = stringReference("Hide Token"),
textColor = themedColor { TangemTheme.colors.text.warning },
onClick = {},
),
),
),
balanceBlockUM = TokenDetailsBalanceBlockUM.Loading(
actionButtons = persistentListOf(),
tokenBalanceTypeUM = TokenBalanceTypeUM.Single,
currencyIconState = CurrencyIconState.Loading,
),
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = "USDT"),
stakingBlocksState = null,
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
isBalanceHidden = false,
isMarketPriceAvailable = true,
),
)
}
}
}
// endregion Preview

View file

@ -0,0 +1,478 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastForEach
import com.tangem.common.ui.account.AccountIcon
import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.ds.image.TangemDeviceIcon
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.extensions.themedColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import kotlinx.collections.immutable.persistentListOf
import com.tangem.core.ui.R as CoreUiR
@Composable
internal fun TokenDetailsTopBar(topAppBarUM: TokenDetailsTopAppBarUM, modifier: Modifier = Modifier) {
TangemTopBar(
modifier = modifier.statusBarsPadding(),
startContent = {
TangemTopBarActionContent(
actionUM = TangemTopBarActionUM(
iconRes = CoreUiR.drawable.ic_back_24,
onClick = topAppBarUM.onBackClick,
ghostModeProgress = 1f,
),
)
},
endContent = if (topAppBarUM.menuItems.isNotEmpty()) {
{
var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) }
Box {
TangemTopBarActionContent(
actionUM = TangemTopBarActionUM(
iconRes = CoreUiR.drawable.ic_more_default_24,
onClick = { isDropdownMenuShown = true },
ghostModeProgress = 1f,
),
)
TangemDropdownMenu(
expanded = isDropdownMenuShown,
modifier = Modifier.background(TangemTheme.colors.background.primary),
onDismissRequest = { isDropdownMenuShown = false },
content = {
topAppBarUM.menuItems.fastForEach { menuItem ->
TangemDropdownItem(
item = menuItem,
dismissParent = { isDropdownMenuShown = false },
)
}
},
)
}
}
} else {
null
},
content = {
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = TangemTheme.dimens2.x1),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
) {
TokenDetailsTitle(titleState = topAppBarUM.titleState)
Text(
text = topAppBarUM.subtitle.resolveAnnotatedReference(),
color = TangemTheme.colors2.text.neutral.secondary,
style = TangemTheme.typography2.captionMedium12,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
},
)
}
@Composable
private fun TokenDetailsTitle(titleState: TitleState) {
val appearance = TitleAppearance(
style = TangemTheme.typography2.bodySemibold16,
iconSize = TangemTheme.dimens2.x5,
spacing = TangemTheme.dimens2.x1,
)
when (titleState) {
is TitleState.Simple -> {
Text(
text = titleState.tokenName,
color = TangemTheme.colors2.text.neutral.primary,
style = appearance.style,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = MIN_TITLE_FONT_SIZE,
maxFontSize = MAX_TITLE_FONT_SIZE,
),
)
}
is TitleState.WithWallet -> {
AdaptiveTokenWithSecondaryRow(
tokenName = titleState.tokenName,
secondaryName = titleState.walletName,
template = stringResourceSafe(
id = CoreUiR.string.token_details_toolbar_title_token_in_wallet,
titleState.tokenName,
titleState.walletName,
),
appearance = appearance,
icon = {
TangemDeviceIcon(
state = titleState.deviceIconUM,
modifier = Modifier.size(appearance.iconSize),
)
},
)
}
is TitleState.WithAccount -> {
val accountNameStr = titleState.accountName.resolveAnnotatedReference().toString()
AdaptiveTokenWithSecondaryRow(
tokenName = titleState.tokenName,
secondaryName = accountNameStr,
template = stringResourceSafe(
id = CoreUiR.string.token_details_toolbar_title_token_in_account,
titleState.tokenName,
accountNameStr,
),
appearance = appearance,
icon = {
AccountIcon(
name = titleState.accountName,
icon = titleState.accountIconUM,
size = AccountIconSize.ExtraSmall,
)
},
)
}
}
}
/**
* Adaptive title for [TitleState.WithAccount] / [TitleState.WithWallet].
*
* Phrase template carries the [IMAGE_PLACEHOLDER] marker translator decides where
* the icon sits (e.g. "Tether in [⭐] Portfolio" or "Tether in My Wallet [⭐]").
* RTL is handled by BiDi inside the single [Text].
*
* Width-driven cascade:
* 12. Full phrase as single [Text] with inline icon; [TextOverflow.Ellipsis]
* trims the secondary name tail when needed.
* 3. No meaningful tail left fall back to `[tokenName] [icon]` in a [Row];
* icon is a sibling so ellipsis trims only tokenName, never the icon.
* 45. tokenName itself doesn't fit [TextAutoSize] shrinks to
* [MIN_TITLE_FONT_SIZE], then [TextOverflow.Ellipsis] tails.
*
* #1/#2 vs #3 is decided here via [rememberTextMeasurer]; #4/#5 are delegated
* to [TextAutoSize] + [TextOverflow.Ellipsis] in the fallback branch.
*/
@Composable
private fun AdaptiveTokenWithSecondaryRow(
tokenName: String,
secondaryName: String,
template: String,
appearance: TitleAppearance,
icon: @Composable () -> Unit,
) {
val (beforeIcon, afterIcon) = remember(template) { splitTemplate(template) }
val inlineContent = rememberIconInlineContent(appearance.iconSize, icon)
BoxWithConstraints(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
val isFullTextShown = rememberShouldShowFullText(
beforeIcon = beforeIcon,
afterIcon = afterIcon,
secondaryName = secondaryName,
appearance = appearance,
maxWidthPx = constraints.maxWidth,
)
if (isFullTextShown) {
FullPhraseTitle(
beforeIcon = beforeIcon,
afterIcon = afterIcon,
style = appearance.style,
inlineContent = inlineContent,
)
} else {
FallbackTokenWithIconTitle(
tokenName = tokenName,
appearance = appearance,
icon = icon,
)
}
}
}
private fun splitTemplate(template: String): Pair<String, String> {
val parts = template.split(IMAGE_PLACEHOLDER, limit = 2)
return if (parts.size == 2) parts[0] to parts[1] else template to ""
}
@Composable
private fun rememberIconInlineContent(iconSize: Dp, icon: @Composable () -> Unit): Map<String, InlineTextContent> {
val iconSizeSp = with(LocalDensity.current) { iconSize.toSp() }
val currentIcon by rememberUpdatedState(icon)
return remember(iconSizeSp) {
mapOf(
ICON_INLINE_ID to InlineTextContent(
placeholder = Placeholder(
width = iconSizeSp,
height = iconSizeSp,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
),
children = {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
currentIcon()
}
},
),
)
}
}
@Composable
private fun rememberShouldShowFullText(
beforeIcon: String,
afterIcon: String,
secondaryName: String,
appearance: TitleAppearance,
maxWidthPx: Int,
): Boolean {
val measurer = rememberTextMeasurer()
val density = LocalDensity.current
return remember(beforeIcon, afterIcon, secondaryName, maxWidthPx, appearance, density) {
if (maxWidthPx <= 0) return@remember true
val fullTextWidthPx = measurer
.measure(text = beforeIcon + afterIcon, style = appearance.style, softWrap = false)
.size.width
val secondaryWidthPx = measurer
.measure(text = secondaryName, style = appearance.style, softWrap = false)
.size.width
val staticWidthPx = (fullTextWidthPx - secondaryWidthPx).coerceAtLeast(0)
val iconReservePx = with(density) {
(appearance.iconSize + appearance.spacing * 2).toPx()
}.toInt()
val minSecondaryPx = with(density) { MIN_SECONDARY_NAME_WIDTH.toPx() }.toInt()
staticWidthPx + iconReservePx + minSecondaryPx <= maxWidthPx
}
}
@Composable
private fun FullPhraseTitle(
beforeIcon: String,
afterIcon: String,
style: TextStyle,
inlineContent: Map<String, InlineTextContent>,
) {
val fullText = remember(beforeIcon, afterIcon) {
buildAnnotatedString {
append(beforeIcon)
appendInlineContent(ICON_INLINE_ID, IMAGE_PLACEHOLDER)
append(afterIcon)
}
}
Text(
text = fullText,
inlineContent = inlineContent,
color = TangemTheme.colors2.text.neutral.primary,
style = style,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@Composable
private fun FallbackTokenWithIconTitle(tokenName: String, appearance: TitleAppearance, icon: @Composable () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(appearance.spacing, Alignment.CenterHorizontally),
) {
Text(
text = tokenName,
color = TangemTheme.colors2.text.neutral.primary,
style = appearance.style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = MIN_TITLE_FONT_SIZE,
maxFontSize = MAX_TITLE_FONT_SIZE,
),
modifier = Modifier.weight(weight = 1f, fill = false),
)
icon()
}
}
@Immutable
private data class TitleAppearance(
val style: TextStyle,
val iconSize: Dp,
val spacing: Dp,
)
private const val ICON_INLINE_ID = "account_icon"
private const val IMAGE_PLACEHOLDER = "%image%"
private val MIN_TITLE_FONT_SIZE = 12.sp
private val MAX_TITLE_FONT_SIZE = 16.sp
private val MIN_SECONDARY_NAME_WIDTH = 48.dp
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun TokenDetailsTopBar_Preview(
@PreviewParameter(TokenDetailsTopBarPreviewProvider::class) titleState: TitleState,
) {
TangemThemePreviewRedesign {
TokenDetailsTopBar(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = titleState,
subtitle = stringReference("ERC-20 in Ethereum network"),
onBackClick = {},
menuItems = persistentListOf(
TangemDropdownMenuItem(
title = stringReference("Hide Token"),
textColor = themedColor { TangemTheme.colors.text.warning },
onClick = {},
),
),
),
)
}
}
private class TokenDetailsTopBarPreviewProvider : PreviewParameterProvider<TitleState> {
override val values: Sequence<TitleState>
get() = sequenceOf(
// === Base title states ===
// Simple — 1 wallet, 1 account
TitleState.Simple(tokenName = "Tether"),
// WithWallet — N wallets, 1 account
TitleState.WithWallet(
tokenName = "Tether",
walletName = "Tangem wallet",
deviceIconUM = DeviceIconUM.Card(
mainColor = Color.DarkGray,
secondColor = null,
),
),
// WithAccount — 1 wallet, N accounts
TitleState.WithAccount(
tokenName = "Tether",
accountName = stringReference("Portfolio"),
accountIconUM = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
),
),
// === AdaptiveTokenTitleRow cascade cases ===
// Cascade #1 — full text fits as is (short token + short wallet)
TitleState.WithWallet(
tokenName = "BTC",
walletName = "Main",
deviceIconUM = DeviceIconUM.Card(
mainColor = Color.DarkGray,
secondColor = null,
),
),
// Cascade #2 — full text doesn't fit, ellipsized tail still meaningful
TitleState.WithWallet(
tokenName = "Tether",
walletName = "My Long Tangem Hardware Wallet",
deviceIconUM = DeviceIconUM.Card(
mainColor = Color.DarkGray,
secondColor = null,
),
),
// Cascade #3 — secondary part too small to be meaningful, drop to token-only + icon
TitleState.WithWallet(
tokenName = "USDCoinWrapped",
walletName = "Super Extra Long Wallet Name That Definitely Wont Fit",
deviceIconUM = DeviceIconUM.Card(
mainColor = Color.DarkGray,
secondColor = null,
),
),
// Cascade #4 — even tokenName alone doesn't fit at full font size: TextAutoSize shrinks it
TitleState.WithAccount(
tokenName = "VeryLongTokenNameThatOverflowsVeryLongTokenNameThatOverflows",
accountName = stringReference("Portfolio"),
accountIconUM = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
),
),
// Cascade #5 — even at MIN_TITLE_FONT_SIZE doesn't fit: TextOverflow.Ellipsis tails it
TitleState.Simple(
tokenName = "ExtremelyLongTokenNameThatCannotPossiblyFitEvenAtMinFontSize",
),
// Account variant — long account name triggers ellipsized tail (#2)
TitleState.WithAccount(
tokenName = "Tether",
accountName = stringReference("My Personal Long Account Name"),
accountIconUM = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
),
),
// Account variant — drop secondary, icon-only fallback (#3)
TitleState.WithAccount(
tokenName = "USDCoinWrapped",
accountName = stringReference("Super Extra Long Account Name That Wont Fit Anywhere"),
accountIconUM = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
),
),
)
}
// endregion Preview

View file

@ -0,0 +1,128 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.google.common.truth.Truth.assertThat
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.collections.immutable.persistentListOf
import org.junit.jupiter.api.Test
class InitializeWithCryptoCurrencyTransformerTest {
private val cryptoCurrency: CryptoCurrency = mockk(relaxed = true) {
every { name } returns TOKEN_NAME
every { symbol } returns TOKEN_SYMBOL
}
private val onBackClick: () -> Unit = mockk(relaxed = true)
@Test
fun `GIVEN crypto currency WHEN transform THEN top bar title is Simple with token name`() {
// GIVEN
val transformer = InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = onBackClick,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.titleState).isEqualTo(TitleState.Simple(tokenName = TOKEN_NAME))
}
@Test
fun `GIVEN crypto currency WHEN transform THEN subtitle is token symbol`() {
// GIVEN
val transformer = InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = onBackClick,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.subtitle).isEqualTo(stringReference(TOKEN_SYMBOL))
}
@Test
fun `GIVEN onBackClick callback WHEN top bar onBackClick invoked THEN callback is dispatched`() {
// GIVEN
val transformer = InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = onBackClick,
)
// WHEN
val result = transformer.transform(initialState())
result.topAppBarUM.onBackClick()
// THEN
verify(exactly = 1) { onBackClick.invoke() }
}
@Test
fun `GIVEN crypto currency WHEN transform THEN market price loading carries currency symbol`() {
// GIVEN
val transformer = InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = onBackClick,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.marketPriceBlockState)
.isEqualTo(MarketPriceBlockState.Loading(currencySymbol = TOKEN_SYMBOL))
}
@Test
fun `GIVEN any state WHEN transform THEN unrelated fields are preserved`() {
// GIVEN
val state = initialState()
val transformer = InitializeWithCryptoCurrencyTransformer(
cryptoCurrency = cryptoCurrency,
onBackClick = onBackClick,
)
// WHEN
val result = transformer.transform(state)
// THEN — only top bar title/subtitle/onBackClick and marketPriceBlockState are touched
assertThat(result.topAppBarUM.menuItems).isEqualTo(state.topAppBarUM.menuItems)
assertThat(result.balanceBlockUM).isSameInstanceAs(state.balanceBlockUM)
assertThat(result.stakingBlocksState).isEqualTo(state.stakingBlocksState)
assertThat(result.pullToRefreshConfig).isSameInstanceAs(state.pullToRefreshConfig)
assertThat(result.isBalanceHidden).isEqualTo(state.isBalanceHidden)
assertThat(result.isMarketPriceAvailable).isEqualTo(state.isMarketPriceAvailable)
}
private fun initialState(): TokenDetailsUM = TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = TitleState.Simple(tokenName = ""),
subtitle = stringReference(""),
onBackClick = {},
menuItems = persistentListOf(),
),
balanceBlockUM = mockk<TokenDetailsBalanceBlockUM>(relaxed = true),
marketPriceBlockState = mockk<MarketPriceBlockState>(relaxed = true),
stakingBlocksState = null,
pullToRefreshConfig = mockk<PullToRefreshConfig>(relaxed = true),
isBalanceHidden = false,
isMarketPriceAvailable = false,
)
private companion object {
const val TOKEN_NAME = "Tether"
const val TOKEN_SYMBOL = "USDT"
}
}

View file

@ -0,0 +1,204 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import androidx.compose.ui.graphics.Color
import com.google.common.truth.Truth.assertThat
import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import io.mockk.every
import io.mockk.mockk
import kotlinx.collections.immutable.persistentListOf
import org.junit.jupiter.api.Test
class SetTopBarTitleTransformerTest {
private val tokenName = "Tether"
private val walletName = "My Wallet"
private val deviceIconUM: DeviceIconUM = DeviceIconUM.Card(
mainColor = Color.DarkGray,
secondColor = null,
)
private val cryptoCurrency: CryptoCurrency.Coin = mockk(relaxed = true) {
every { name } returns tokenName
}
@Test
fun `GIVEN single wallet single account WHEN transform THEN Simple title`() {
// GIVEN
val transformer = createTransformer(
hasMultipleWallets = false,
hasMultipleAccounts = false,
account = null,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.titleState).isEqualTo(TitleState.Simple(tokenName = tokenName))
}
@Test
fun `GIVEN multiple wallets and single account WHEN transform THEN WithWallet title`() {
// GIVEN
val transformer = createTransformer(
hasMultipleWallets = true,
hasMultipleAccounts = false,
account = null,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
val expected = TitleState.WithWallet(
tokenName = tokenName,
walletName = walletName,
deviceIconUM = deviceIconUM,
)
assertThat(result.topAppBarUM.titleState).isEqualTo(expected)
}
@Test
fun `GIVEN single wallet and multiple accounts WHEN transform THEN WithAccount title`() {
// GIVEN
val transformer = createTransformer(
hasMultipleWallets = false,
hasMultipleAccounts = true,
account = stubAccount(),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
val title = result.topAppBarUM.titleState
assertThat(title).isInstanceOf(TitleState.WithAccount::class.java)
assertThat((title as TitleState.WithAccount).tokenName).isEqualTo(tokenName)
}
@Test
fun `GIVEN multiple wallets AND multiple accounts WHEN transform THEN WithAccount wins`() {
// GIVEN — design priority: account branch wins over wallet branch
val transformer = createTransformer(
hasMultipleWallets = true,
hasMultipleAccounts = true,
account = stubAccount(),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.titleState).isInstanceOf(TitleState.WithAccount::class.java)
}
@Test
fun `GIVEN multiple accounts but null account WHEN transform THEN falls back to wallet branch`() {
// GIVEN — race protection: hasMultipleAccounts=true but account not loaded yet
val transformer = createTransformer(
hasMultipleWallets = true,
hasMultipleAccounts = true,
account = null,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.titleState).isInstanceOf(TitleState.WithWallet::class.java)
}
@Test
fun `GIVEN multiple accounts but null account AND single wallet WHEN transform THEN falls back to Simple`() {
// GIVEN
val transformer = createTransformer(
hasMultipleWallets = false,
hasMultipleAccounts = true,
account = null,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.titleState).isEqualTo(TitleState.Simple(tokenName = tokenName))
}
@Test
fun `GIVEN account with custom icon WHEN transform THEN icon is propagated`() {
// GIVEN
val account = stubAccount(
iconValue = CryptoPortfolioIcon.Icon.Star,
iconColor = CryptoPortfolioIcon.Color.Azure,
)
val transformer = createTransformer(
hasMultipleWallets = false,
hasMultipleAccounts = true,
account = account,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
val title = result.topAppBarUM.titleState as TitleState.WithAccount
val expectedIcon = AccountIconUM.CryptoPortfolio(
value = CryptoPortfolioIcon.Icon.Star,
color = CryptoPortfolioIcon.Color.Azure,
)
assertThat(title.accountIconUM).isEqualTo(expectedIcon)
}
private fun createTransformer(
hasMultipleWallets: Boolean,
hasMultipleAccounts: Boolean,
account: Account.CryptoPortfolio?,
) = SetTopBarTitleTransformer(
cryptoCurrency = cryptoCurrency,
hasMultipleWallets = hasMultipleWallets,
hasMultipleAccounts = hasMultipleAccounts,
walletName = walletName,
deviceIconUM = deviceIconUM,
account = account,
)
private fun stubAccount(
iconValue: CryptoPortfolioIcon.Icon = CryptoPortfolioIcon.Icon.Star,
iconColor: CryptoPortfolioIcon.Color = CryptoPortfolioIcon.Color.Azure,
): Account.CryptoPortfolio {
val icon: CryptoPortfolioIcon = mockk {
every { value } returns iconValue
every { color } returns iconColor
}
return mockk {
every { accountName } returns AccountName.DefaultMain
every { this@mockk.icon } returns icon
}
}
private fun initialState(): TokenDetailsUM = TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = TitleState.Simple(tokenName = ""),
subtitle = stringReference(""),
onBackClick = {},
menuItems = persistentListOf(),
),
balanceBlockUM = mockk<TokenDetailsBalanceBlockUM>(relaxed = true),
marketPriceBlockState = mockk<MarketPriceBlockState>(relaxed = true),
stakingBlocksState = null,
pullToRefreshConfig = mockk<PullToRefreshConfig>(relaxed = true),
isBalanceHidden = false,
isMarketPriceAvailable = false,
)
}

View file

@ -0,0 +1,214 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.google.common.truth.Truth.assertThat
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import io.mockk.verify
import kotlinx.collections.immutable.persistentListOf
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class UpdateTopBarMenuTransformerTest {
private val coldWallet: UserWallet.Cold = mockk(relaxed = true)
private val hotWallet: UserWallet.Hot = mockk(relaxed = true)
private val cardTypesResolver: CardTypesResolver = mockk(relaxed = true)
private val onGenerateExtendedKey: () -> Unit = mockk(relaxed = true)
private val onHideClick: () -> Unit = mockk(relaxed = true)
@BeforeEach
fun setUp() {
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { coldWallet.cardTypesResolver } returns cardTypesResolver
}
@AfterEach
fun tearDown() {
unmockkStatic(UserWallet.Cold::cardTypesResolver)
}
@Test
fun `GIVEN cold wallet AND single wallet with token WHEN transform THEN menu is empty`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns true
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = true,
isXPubSupported = true,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.menuItems).isEmpty()
}
@Test
fun `GIVEN cold wallet AND multi-wallet WHEN transform THEN Hide item is the only one`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = false,
isXPubSupported = false,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.menuItems).hasSize(1)
result.topAppBarUM.menuItems.single().onClick()
verify(exactly = 1) { onHideClick.invoke() }
verify(exactly = 0) { onGenerateExtendedKey.invoke() }
}
@Test
fun `GIVEN hot wallet WHEN transform THEN Hide item is shown regardless of single-wallet flag`() {
// GIVEN — flag is read only for cold wallets, so no stub needed for hotWallet
val transformer = createTransformer(
userWallet = hotWallet,
hasDerivations = false,
isXPubSupported = false,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.menuItems).hasSize(1)
}
@Test
fun `GIVEN xPub supported AND derivations exist WHEN transform THEN both items are shown`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = true,
isXPubSupported = true,
)
// WHEN
val result = transformer.transform(initialState())
// THEN — Generate xPub first, Hide token second
assertThat(result.topAppBarUM.menuItems).hasSize(2)
}
@Test
fun `GIVEN xPub supported but no derivations WHEN transform THEN xPub item is hidden`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = false,
isXPubSupported = true,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.menuItems).hasSize(1)
}
@Test
fun `GIVEN derivations exist but xPub unsupported WHEN transform THEN xPub item is hidden`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = true,
isXPubSupported = false,
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.topAppBarUM.menuItems).hasSize(1)
}
@Test
fun `GIVEN any state WHEN transform THEN unrelated state fields are preserved`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val state = initialState()
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = false,
isXPubSupported = false,
)
// WHEN
val result = transformer.transform(state)
// THEN — only menuItems is touched
assertThat(result.topAppBarUM.titleState).isEqualTo(state.topAppBarUM.titleState)
assertThat(result.topAppBarUM.subtitle).isEqualTo(state.topAppBarUM.subtitle)
assertThat(result.balanceBlockUM).isSameInstanceAs(state.balanceBlockUM)
assertThat(result.marketPriceBlockState).isSameInstanceAs(state.marketPriceBlockState)
}
@Test
fun `GIVEN callbacks WHEN menu items invoked THEN callbacks are dispatched`() {
// GIVEN
every { cardTypesResolver.isSingleWalletWithToken() } returns false
val transformer = createTransformer(
userWallet = coldWallet,
hasDerivations = true,
isXPubSupported = true,
)
// WHEN
val result = transformer.transform(initialState())
result.topAppBarUM.menuItems.forEach { it.onClick() }
// THEN
verify(exactly = 1) { onGenerateExtendedKey.invoke() }
verify(exactly = 1) { onHideClick.invoke() }
}
private fun createTransformer(
userWallet: UserWallet,
hasDerivations: Boolean,
isXPubSupported: Boolean,
) = UpdateTopBarMenuTransformer(
userWallet = userWallet,
hasDerivations = hasDerivations,
isXPubSupported = isXPubSupported,
onGenerateExtendedKey = onGenerateExtendedKey,
onHideClick = onHideClick,
)
private fun initialState(): TokenDetailsUM = TokenDetailsUM(
topAppBarUM = TokenDetailsTopAppBarUM(
titleState = TitleState.Simple(tokenName = "Tether"),
subtitle = stringReference("ERC-20 in Ethereum network"),
onBackClick = {},
menuItems = persistentListOf(),
),
balanceBlockUM = mockk<TokenDetailsBalanceBlockUM>(relaxed = true),
marketPriceBlockState = mockk<MarketPriceBlockState>(relaxed = true),
stakingBlocksState = null,
pullToRefreshConfig = mockk<PullToRefreshConfig>(relaxed = true),
isBalanceHidden = false,
isMarketPriceAvailable = false,
)
}