diff --git a/Bitkit/AppScene.swift b/Bitkit/AppScene.swift index 83df9fe3..334d4875 100644 --- a/Bitkit/AppScene.swift +++ b/Bitkit/AppScene.swift @@ -187,7 +187,7 @@ struct AppScene: View { private func handleCurrencyStaleData(_: Bool) { if currency.hasStaleData { - app.toast(type: .error, title: "Rates currently unavailable", description: "An error has occurred. Please try again later.") + app.toast(type: .error, title: t("other__rates_unavailable_title"), description: t("other__rates_unavailable_description")) } } diff --git a/Bitkit/Components/Activity/ActivityBanner.swift b/Bitkit/Components/Activity/ActivityBanner.swift index e97eec15..b190cbdf 100644 --- a/Bitkit/Components/Activity/ActivityBanner.swift +++ b/Bitkit/Components/Activity/ActivityBanner.swift @@ -26,7 +26,7 @@ struct ActivityBanner: View { .frame(width: 20, height: 20) .foregroundColor(accentColor) - Text(tTodo("TRANSFER IN PROGRESS")) + Text(t("wallet__activity_transfer_in_progress")) .font(Fonts.black(size: 20)) .foregroundColor(.textPrimary) .kerning(0) diff --git a/Bitkit/Components/Home/Suggestions.swift b/Bitkit/Components/Home/Suggestions.swift index 5e138ee3..c6c5ab9e 100644 --- a/Bitkit/Components/Home/Suggestions.swift +++ b/Bitkit/Components/Home/Suggestions.swift @@ -82,7 +82,7 @@ let cards: [SuggestionCardData] = [ SuggestionCardData( id: "notifications", title: t("cards__notifications__title"), - description: tTodo("When Bitkit is closed"), + description: t("cards__notifications__description"), imageName: "bell-figure", color: .purple24, action: .notifications diff --git a/Bitkit/MainNavView.swift b/Bitkit/MainNavView.swift index cf522206..ed2dece0 100644 --- a/Bitkit/MainNavView.swift +++ b/Bitkit/MainNavView.swift @@ -189,8 +189,8 @@ struct MainNavView: View { Logger.error("Failed to sync push notifications with backend: \(error)") app.toast( type: .error, - title: tTodo("Notification Registration Failed"), - description: tTodo("Bitkit was unable to register for push notifications.") + title: t("other__notification_registration_failed_title"), + description: t("other__notification_registration_failed_description") ) } } @@ -210,8 +210,8 @@ struct MainNavView: View { Logger.error("Failed to sync push notifications: \(error)") app.toast( type: .error, - title: tTodo("Notification Registration Failed"), - description: tTodo("Bitkit was unable to register for push notifications.") + title: t("other__notification_registration_failed_title"), + description: t("other__notification_registration_failed_description") ) } } @@ -271,14 +271,14 @@ struct MainNavView: View { case .contacts: if app.hasSeenContactsIntro { // ContactsView() - Text("Coming Soon") + Text(t("common__coming_soon")) } else { ContactsIntroView() } case .profile: if app.hasSeenProfileIntro { // ProfileView() - Text("Coming Soon") + Text(t("common__coming_soon")) } else { ProfileIntroView() } @@ -306,7 +306,7 @@ struct MainNavView: View { case let .activityDetail(activity): ActivityItemView(item: activity) case let .activityExplorer(activity): ActivityExplorerView(item: activity) case .buyBitcoin: BuyBitcoinView() - case .contacts: Text("Coming Soon") + case .contacts: Text(t("common__coming_soon")) case .contactsIntro: ContactsIntroView() case .savingsWallet: SavingsWalletView() case .spendingWallet: SpendingWalletView() @@ -327,7 +327,7 @@ struct MainNavView: View { case .savingsConfirm: SavingsConfirmView() case .savingsAdvanced: SavingsAdvancedView() case .savingsProgress: SavingsProgressView() - case .profile: Text("Coming Soon") + case .profile: Text(t("common__coming_soon")) case .profileIntro: ProfileIntroView() case .scanner: ScannerScreen() diff --git a/Bitkit/Managers/ScannerManager.swift b/Bitkit/Managers/ScannerManager.swift index d794d0ea..2df87785 100644 --- a/Bitkit/Managers/ScannerManager.swift +++ b/Bitkit/Managers/ScannerManager.swift @@ -150,8 +150,8 @@ class ScannerManager: ObservableObject { else { app.toast( type: .error, - title: "Error", - description: tTodo("Sorry. Bitkit wasn't able to load this image.") + title: t("other__scan__error__title_process"), + description: t("other__scan__error__image_load") ) return } @@ -159,8 +159,8 @@ class ScannerManager: ObservableObject { guard let cgImage = image.cgImage else { app.toast( type: .error, - title: "Error", - description: tTodo("Sorry. Bitkit wasn't able to process this image.") + title: t("other__scan__error__title_process"), + description: t("other__scan__error__image_process") ) return } @@ -171,8 +171,8 @@ class ScannerManager: ObservableObject { DispatchQueue.main.async { app.toast( type: .error, - title: tTodo("Detection Error"), - description: tTodo("Failed to process the image for QR codes.") + title: t("other__scan__error__title"), + description: t("other__scan__error__detection") ) } return @@ -183,8 +183,8 @@ class ScannerManager: ObservableObject { DispatchQueue.main.async { app.toast( type: .error, - title: tTodo("No QR Code Found"), - description: tTodo("Sorry. Bitkit wasn't able to detect a QR code in this image.") + title: t("other__scan__error__title_no_qr"), + description: t("other__scan__error__no_qr") ) } return @@ -198,8 +198,8 @@ class ScannerManager: ObservableObject { DispatchQueue.main.async { app.toast( type: .error, - title: tTodo("No QR Code Found"), - description: tTodo("Sorry. Bitkit wasn't able to detect a QR code in this image.") + title: t("other__scan__error__title_no_qr"), + description: t("other__scan__error__no_qr") ) } return @@ -230,8 +230,8 @@ class ScannerManager: ObservableObject { Logger.error(error, context: "Failed to process image") app.toast( type: .error, - title: tTodo("Error"), - description: tTodo("Sorry. An error occurred when trying to process this image.") + title: t("other__scan__error__title_process"), + description: t("other__scan__error__image_process") ) } } diff --git a/Bitkit/Resources/Localization/en.lproj/Localizable.strings b/Bitkit/Resources/Localization/en.lproj/Localizable.strings index 84df6832..1312c4fe 100644 --- a/Bitkit/Resources/Localization/en.lproj/Localizable.strings +++ b/Bitkit/Resources/Localization/en.lproj/Localizable.strings @@ -37,6 +37,7 @@ "common__continue" = "Continue"; "common__cancel" = "Cancel"; "common__close" = "Close"; +"common__coming_soon" = "Coming Soon"; "common__are_you_sure" = "Are You Sure?"; "common__yes_proceed" = "Yes, Proceed"; "common__try_again" = "Try Again"; @@ -52,17 +53,24 @@ "common__done" = "Done"; "common__delete" = "Delete"; "common__delete_yes" = "Yes, Delete"; +"common__error" = "Error"; +"common__off" = "Off"; "common__ok" = "OK"; "common__ok_random" = "Awesome!\nNice!\nCool!\nGreat!\nFantastic!\nSweet!\nExcellent!\nTerrific!"; +"common__on" = "On"; +"common__qr_code" = "QR Code"; "common__reset" = "Reset"; "common__retry" = "Retry"; "common__later" = "Later"; +"common__show_all" = "Show All"; +"common__show_details" = "Show Details"; "common__skip" = "Skip"; "common__copied" = "Copied To Clipboard"; "common__yes" = "Yes"; "common__no" = "No"; "common__back" = "Back"; "common__learn_more" = "Learn More"; +"common__success" = "Success"; "common__understood" = "Understood"; "common__connect" = "Connect"; "common__min" = "Min"; @@ -136,6 +144,7 @@ "lightning__spending_confirm__amount" = "To spending"; "lightning__spending_confirm__total" = "Total"; "lightning__spending_confirm__default" = "Use Defaults"; +"lightning__spending_confirm__background_setup" = "Set up in background"; "lightning__spending_advanced__title" = "Receiving\ncapacity"; "lightning__spending_advanced__fee" = "Liquidity fee"; "lightning__liquidity__title" = "Liquidity\n& routing"; @@ -276,10 +285,10 @@ "lightning__wait_text_top" = "Please wait for Bitkit to connect to the payment network (±10 seconds)."; "lightning__wait_text_bottom" = "Connecting & Syncing..."; "onboarding__tos_header" = "Bitkit\nterms of use"; -"onboarding__tos_checkbox" = "Terms of use"; -"onboarding__tos_checkbox_value" = "I declare that I have read and accept the terms of use."; +"onboarding__tos_checkbox" = "Terms of Use"; +"onboarding__tos_checkbox_value" = "By continuing you declare that you have read and accept the terms of use."; "onboarding__pp_checkbox" = "Privacy Policy"; -"onboarding__pp_checkbox_value" = "I declare that I have read and accept the privacy policy."; +"onboarding__pp_checkbox_value" = "By continuing you declare that you have read and accept the privacy policy."; "onboarding__welcome_title" = "You can ₿ \nthe change"; "onboarding__welcome_text" = "Use Bitkit to pay anyone, anywhere, any time, and spend your bitcoin on the things you value in life."; "onboarding__get_started" = "Get Started"; @@ -369,13 +378,24 @@ "other__scan_err_decoding" = "Decoding Error"; "other__scan_err_interpret_title" = "Unable To Interpret Provided Data"; "other__scan_err_not_payable_msg" = "This QR code does not appear to contain payment data."; +"other__notification_registration_failed_title" = "Notification Registration Failed"; +"other__notification_registration_failed_description" = "Bitkit was unable to register for push notifications."; "other__rate_error_title" = "Bitcoin Price Update Failed"; "other__rate_error_msg_date" = "Bitkit could not update the current Bitcoin exchange rate. Using price from {date}"; "other__rate_error_msg_nodate" = "Bitkit could not update the current Bitcoin exchange rate. Please try again later."; +"other__rates_unavailable_title" = "Rates currently unavailable"; +"other__rates_unavailable_description" = "An error has occurred. Please try again later."; +"other__lightning_not_running_title" = "Lightning not running"; +"other__lightning_not_running_description" = "Please try again later."; +"other__unsupported_invoice_title" = "Unsupported"; +"other__unsupported_invoice_description" = "This type of invoice is not supported yet"; "other__lnurl_pay_error" = "Unable To Pay (LNURL)"; "other__lnurl_ln_error_msg" = "Could not start local Lightning node. Please try again or restart Bitkit."; "other__lnurl_pay_error_no_capacity" = "Not enough outbound/sending capacity to complete lnurl-pay request."; "other__lnurl_channel_error" = "Unable To Open Channel (LNURL)"; +"other__lnurl_channel_loading" = "Loading channel information..."; +"other__lnurl_channel_load_failed" = "Failed to load channel information"; +"other__lnurl_channel_node_id_missing" = "Node ID is missing"; "other__lnurl_channel_error_raw" = "An error occured when you tried paying: {raw}"; "other__lnurl_channel_header" = "Lightning Connection"; "other__lnurl_channel_title" = "New\nlightning\nconnection"; @@ -393,6 +413,8 @@ "other__lnurl_auth_success_title" = "Signed In"; "other__lnurl_auth_success_msg_domain" = "You successfully signed in to {domain}."; "other__lnurl_auth_success_msg_no_domain" = "You successfully signed in."; +"other__lnurl_withdr_failure_title" = "Withdrawal Failed"; +"other__lnurl_withdr_failure_text" = "Your withdrawal was unsuccessful. Please scan the QR code again or contact support."; "other__lnurl_withdr_error" = "Withdraw Failed (LNURL)"; "other__lnurl_withdr_error_generic" = "Sorry, an error occurred."; "other__lnurl_withdr_error_no_capacity" = "Not enough receiving capacity to complete withdraw request."; @@ -415,6 +437,14 @@ "other__transfer_notification__body" = "Open Bitkit to complete your transfer"; "other__scan__error__generic" = "Bitkit is unable to read the provided data."; "other__scan__error__expired" = "This Lightning invoice has expired."; +"other__scan__error__detection" = "Failed to process the image for QR codes."; +"other__scan__error__image_load" = "Sorry. Bitkit wasn't able to load this image."; +"other__scan__error__image_process" = "Sorry. Bitkit wasn't able to process this image."; +"other__scan__error__no_qr" = "Sorry. Bitkit wasn't able to detect a QR code in this image."; +"other__scan__error__title" = "Detection Error"; +"other__scan__error__title_no_qr" = "No QR Code Found"; +"other__scan__error__title_process" = "Error"; +"other__scan__manual_prompt" = "Enter QR Code String"; "other__gift__claiming__title" = "Claiming Gift"; "other__gift__claiming__text" = "Claiming your Bitkit gift code..."; "other__gift__error__title" = "Gift Code Error"; @@ -423,6 +453,8 @@ "other__gift__used__title" = "Used Code"; "other__gift__used__text" = "This Bitkit gift code has already been used, and the funds have been paid out."; "other__gift__used__button" = "OK"; +"other__gift__used_up__title" = "Out of Gifts"; +"other__gift__used_up__text" = "Sorry, you're too late! All gifts for this code have already been claimed."; "other__shop__intro__title" = "Shop"; "other__shop__intro__description" = "Get your life on the Bitcoin standard. Spend your Bitcoin on digital gift cards, eSIMs, phone refills, and more."; "other__shop__intro__button" = "Get Started"; @@ -430,6 +462,28 @@ "other__shop__discover__tabs__shop" = "Shop"; "other__shop__discover__tabs__map" = "Map"; "other__shop__discover__label" = "Gift card categories"; +"other__shop__category_apparel" = "Apparel"; +"other__shop__category_automobiles" = "Automobiles"; +"other__shop__category_cruises" = "Cruises"; +"other__shop__category_ecommerce" = "Ecommerce"; +"other__shop__category_electronics" = "Electronics"; +"other__shop__category_entertainment" = "Entertainment"; +"other__shop__category_experiences" = "Experiences"; +"other__shop__category_flights" = "Flights"; +"other__shop__category_food" = "Food"; +"other__shop__category_food_delivery" = "Food Delivery"; +"other__shop__category_games" = "Games"; +"other__shop__category_gifts" = "Gifts"; +"other__shop__category_groceries" = "Groceries"; +"other__shop__category_health_beauty" = "Health & Beauty"; +"other__shop__category_home" = "Home"; +"other__shop__category_multi_brand" = "Multi-Brand"; +"other__shop__category_pets" = "Pets"; +"other__shop__category_restaurants" = "Restaurants"; +"other__shop__category_retail" = "Retail"; +"other__shop__category_streaming" = "Streaming"; +"other__shop__category_travel" = "Travel"; +"other__shop__category_voip" = "VoIP"; "other__shop__discover__gift-cards__title" = "Gift Cards"; "other__shop__discover__gift-cards__description" = "Shop with Bitcoin"; "other__shop__discover__esims__title" = "ESims"; @@ -520,6 +574,10 @@ "security__display_seed" = "Show Seed Phrase"; "security__contact_support" = "Contact Support"; "security__wipe_app" = "Wipe App"; +"security__wipe_failed_title" = "Wipe Failed"; +"security__wipe_failed_description" = "Bitkit was unable to reset your wallet data. Please try again."; +"security__recovery_mnemonic_error_title" = "Mnemonic Error"; +"security__recovery_mnemonic_error_text" = "Unable to load mnemonic phrase"; "security__close_app" = "Close Bitkit"; "security__export_title" = "Export To Phone"; "security__export_text" = "You can export a copy of your wallet data as a .ZIP file. This file is encrypted with the password you set below."; @@ -597,6 +655,10 @@ "settings__general__language" = "Language"; "settings__general__language_title" = "Language"; "settings__general__language_other" = "Interface language"; +"settings__general__language_changed_title" = "Language Changed"; +"settings__general__language_changed_message" = "The app needs to be restarted for the language change to take full effect."; +"settings__logs__delete_confirm" = "Are you sure you want to delete all log files? This action cannot be undone."; +"settings__logs__zip_failed" = "Failed to create log zip file"; "settings__widgets__nav_title" = "Widgets"; "settings__widgets__showWidgets" = "Widgets"; "settings__widgets__showWidgetTitles" = "Show Widget Titles"; @@ -767,6 +829,11 @@ "settings__addr__sats_found" = "₿ {totalBalance} found"; "settings__addr__gen_20" = "Generate 20 More"; "settings__addr__check_balances" = "Check Balances"; +"settings__addr__error_loading_title" = "Error Loading Addresses"; +"settings__addr__error_loading_more_title" = "Error Loading More Addresses"; +"settings__addr__balances_updated_title" = "Balances Updated"; +"settings__addr__balances_updated_description" = "Address balances have been refreshed"; +"settings__addr__error_checking_balances_title" = "Error Checking Balances"; "settings__addr__copied" = "Copied to clipboard"; "settings__es__error_host_port" = "Please specify a host and port to connect to."; "settings__es__error_host" = "Please specify a host to connect to."; @@ -797,6 +864,8 @@ "settings__rgs__button_connect" = "Connect"; "settings__rgs__update_success_title" = "Rapid-Gossip-Sync Server Updated"; "settings__rgs__update_success_description" = "You may need to restart the app once or twice for this change to take effect."; +"settings__rgs__error_peer" = "RGS Error"; +"settings__rgs__server_error_description" = "Bitkit could not establish a connection to the RGS server."; "settings__wr__error_wr" = "Web Relay Error"; "settings__wr__error_url" = "Please specify a URL to connect to."; "settings__wr__error_https" = "Not a valid HTTPS url."; @@ -923,6 +992,11 @@ "wallet__send_regular" = "Regular Payment"; "wallet__send_error_slash_ln" = "Unfortunately contact could not be paid instantly. You can try a regular payment (more expensive, slower)."; "wallet__send_error_tx_failed" = "Transaction Failed"; +"wallet__send_error" = "Send Error"; +"wallet__send_insufficient_funds_title" = "Insufficient Funds"; +"wallet__send_insufficient_funds_description" = "You do not have enough funds in the selected wallet."; +"wallet__send_no_utxos_title" = "No UTXOs"; +"wallet__send_no_utxos_description" = "You do not have any UTXOs to spend."; "wallet__send_error_create_tx" = "Unable to broadcast the transaction. Please try again."; "wallet__tag_remove_error_title" = "Removing Tag Failed"; "wallet__tag_remove_error_description" = "Bitkit was unable to find the transaction data."; @@ -948,9 +1022,11 @@ "wallet__send_coin_selection_output_to_small_description" = "Please remove UTXOs or increase your send amount to proceed."; "wallet__send_fee_error_min" = "Unable to decrease the fee any further."; "wallet__send_fee_custom" = "Set Custom Fee"; +"wallet__send_fee_use_suggested" = "Use Suggested Fee"; "wallet__send_fee_total" = "₿ {feeSats} for this transaction"; "wallet__send_fee_total_fiat" = "₿ {feeSats} for this transaction ({fiatSymbol}{fiatFormatted})"; "wallet__note" = "Note"; +"wallet__lnurl_pay_confirm__fee_instant" = "Instant (±$0.02)"; "wallet__lnurl_pay_confirm__comment" = "Comment"; "wallet__lnurl_pay_confirm__comment_placeholder" = "Optional comment to receiver"; "wallet__tags" = "Tags"; @@ -958,6 +1034,8 @@ "wallet__tags_add_button" = "Add"; "wallet__tags_add_error_header" = "Adding Tag Failed"; "wallet__tags_add_error_description" = "Bitkit was unable to find the transaction data."; +"wallet__tags_add_error_no_payment_id" = "Payment ID not available"; +"wallet__tags_delete_error_header" = "Failed to delete tag"; "wallet__tags_new" = "New tag"; "wallet__tags_new_enter" = "Enter a new tag"; "wallet__tags_previously" = "Previously used tags"; @@ -990,6 +1068,8 @@ "wallet__receive_show_qr" = "Show QR Code"; "wallet__receive_text_lnfunds" = "Want to receive Lightning funds?"; "wallet__receive_spending" = "Receive on Spending Balance"; +"wallet__receive_spending_title" = "Receive on spending balance"; +"wallet__receive_spending_description" = "Enjoy instant and cheap\ntransactions with friends, family,\nand merchants."; "wallet__receive_connect_initial" = "To set up your spending balance, a {networkFee} network fee and {serviceFee} service provider fee will be deducted."; "wallet__receive_connect_additional" = "To receive more instant Bitcoin, Bitkit has to increase your liquidity. A {networkFee} network fee and {serviceFee} service provider fee will be deducted from the amount you specified."; "wallet__receive_liquidity__nav_title" = "Spending Balance Setup"; @@ -998,10 +1078,14 @@ "wallet__receive_liquidity__text_additional" = "Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nBitkit needs to increase the receiving capacity of your spending balance to process this payment."; "wallet__receive_liquidity__label" = "Spending Balance Liquidity"; "wallet__receive_liquidity__label_additional" = "Additional Spending Balance Liquidity"; +"wallet__receive_background_setup" = "Enable background setup"; +"wallet__receive_background_setup_text" = "Enable background setup to safely exit Bitkit while your balance is being configured."; "wallet__receive_cjit_error" = "Transaction Failed"; "wallet__receive_cjit_error_msg" = "Failed to send funds to your spending account."; "wallet__receive_will" = "You will receive"; "wallet__receive_ldk_init" = "Spending Balance Initializing..."; +"wallet__receive_lightning_not_ready_title" = "Lightning not ready"; +"wallet__receive_lightning_not_ready_description" = "Lightning node must be running to create an invoice"; "wallet__minimum" = "MINIMUM"; "wallet__activity" = "Activity"; "wallet__activity_show_all" = "Show All Activity"; @@ -1012,11 +1096,13 @@ "wallet__activity_pending" = "Pending"; "wallet__activity_failed" = "Failed"; "wallet__activity_transfer" = "Transfer"; +"wallet__activity_transfer_in_progress" = "TRANSFER IN PROGRESS"; "wallet__activity_transfer_savings_pending" = "From Spending (±{duration})"; "wallet__activity_transfer_savings_done" = "From Spending"; "wallet__activity_transfer_spending_pending" = "From Savings (±{duration})"; "wallet__activity_transfer_spending_done" = "From Savings"; "wallet__activity_transfer_to_spending" = "To Spending"; +"wallet__fund_receive_title" = "Fund Receive"; "wallet__activity_transfer_to_savings" = "To Savings"; "wallet__activity_transfer_pending" = "Transfer (±{duration})"; "wallet__activity_confirms_in" = "Confirms in {feeRateDescription}"; @@ -1071,6 +1157,8 @@ "wallet__activity_group_year" = "This year"; "wallet__details_savings_title" = "Savings"; "wallet__details_spending_title" = "Spending"; +"wallet__transfer_to_savings" = "Transfer To Savings"; +"wallet__transfer_to_spending" = "Transfer To Spending"; "wallet__savings__title" = "Savings"; "wallet__savings__onboarding" = "Send\nbitcoin\nto your\nsavings balance"; "wallet__spending__title" = "Spending"; diff --git a/Bitkit/ViewModels/AppViewModel.swift b/Bitkit/ViewModels/AppViewModel.swift index 29428c61..f78eb62f 100644 --- a/Bitkit/ViewModels/AppViewModel.swift +++ b/Bitkit/ViewModels/AppViewModel.swift @@ -150,7 +150,7 @@ extension AppViewModel { } func toast(_ error: Error) { - toast(type: .error, title: "Error", description: error.localizedDescription) + toast(type: .error, title: t("common__error"), description: error.localizedDescription) } func hideToast() { @@ -172,7 +172,7 @@ extension AppViewModel { case let .onChain(invoice): if let lnInvoice = invoice.params?["lightning"] { guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } // Lightning invoice param found, prefer lightning payment if possible @@ -188,7 +188,7 @@ extension AppViewModel { handleScannedOnchainInvoice(invoice) case let .lightning(invoice): guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } @@ -196,7 +196,7 @@ extension AppViewModel { if lightningService.canSend(amountSats: invoice.amountSatoshis) { handleScannedLightningInvoice(invoice, bolt11: uri) } else { - toast(type: .error, title: "Insufficient Funds", description: "You do not have enough funds to send this payment.") + toast(type: .error, title: t("wallet__send_insufficient_funds_title"), description: t("wallet__send_insufficient_funds_description")) } case let .lnurlPay(data: lnurlPayData): Logger.debug("LNURL: \(lnurlPayData)") @@ -212,7 +212,7 @@ extension AppViewModel { handleLnurlAuth(lnurlAuthData, lnurl: uri) case let .nodeId(url, network): guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } @@ -223,7 +223,7 @@ extension AppViewModel { sheetViewModel.showSheet(.gift, data: GiftConfig(code: code, amount: Int(amount))) default: Logger.warn("Unhandled invoice type: \(data)") - toast(type: .error, title: "Unsupported", description: "This type of invoice is not supported yet") + toast(type: .error, title: t("other__unsupported_invoice_title"), description: t("other__unsupported_invoice_description")) } } @@ -248,7 +248,7 @@ extension AppViewModel { private func handleLnurlPayInvoice(_ data: LnurlPayData) { // Check if lightning service is running guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } @@ -274,7 +274,7 @@ extension AppViewModel { private func handleLnurlWithdraw(_ data: LnurlWithdrawData) { // Check if lightning service is running guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } @@ -314,7 +314,7 @@ extension AppViewModel { private func handleLnurlChannel(_ data: LnurlChannelData) { // Check if lightning service is running guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } @@ -325,7 +325,7 @@ extension AppViewModel { private func handleLnurlAuth(_ data: LnurlAuthData, lnurl: String) { // Check if lightning service is running guard lightningService.status?.isRunning == true else { - toast(type: .error, title: "Lightning not running", description: "Please try again later.") + toast(type: .error, title: t("other__lightning_not_running_title"), description: t("other__lightning_not_running_description")) return } diff --git a/Bitkit/Views/Backup/BackupMnemonic.swift b/Bitkit/Views/Backup/BackupMnemonic.swift index fb7bbcdf..25377408 100644 --- a/Bitkit/Views/Backup/BackupMnemonic.swift +++ b/Bitkit/Views/Backup/BackupMnemonic.swift @@ -15,8 +15,8 @@ struct BackupMnemonicView: View { private var note: String { showMnemonic - ? "Bitkit cannot access your funds and cannot help recover them if you lose your recovery phrase. Keep it safe!" - : "Make sure no one can see your screen. Never share your recovery phrase with anyone, as it may result in loss of funds." + ? t("security__mnemonic_cannot_recover") + : t("security__mnemonic_never_share") } private var mnemonicAccessibilityLabel: String { @@ -77,11 +77,11 @@ struct BackupMnemonicView: View { if Env.isDebug || Env.isTestFlight { let mnemonicString = mnemonic.joined(separator: " ") UIPasteboard.general.string = mnemonicString - app.toast(type: .success, title: t("common__copied"), description: "Mnemonic copied to clipboard") + app.toast(type: .success, title: t("common__copied")) } } - BodySText(tTodo(note), textColor: .brandAccent, accentFont: Fonts.bold) + BodySText(note, textColor: .brandAccent, accentFont: Fonts.bold) Spacer() diff --git a/Bitkit/Views/Backup/BackupPassphrase.swift b/Bitkit/Views/Backup/BackupPassphrase.swift index ecebb682..e1157762 100644 --- a/Bitkit/Views/Backup/BackupPassphrase.swift +++ b/Bitkit/Views/Backup/BackupPassphrase.swift @@ -33,7 +33,7 @@ struct BackupPassphrase: View { .frame(maxWidth: .infinity) BodySText( - tTodo("Never share your passphrase with anyone, as it may result in loss of funds. Keep it secret!"), + t("security__pass_never_share"), accentColor: .brandAccent, accentFont: Fonts.bold ) diff --git a/Bitkit/Views/Gift/GiftUsedUp.swift b/Bitkit/Views/Gift/GiftUsedUp.swift index 1a72fb34..eb874366 100644 --- a/Bitkit/Views/Gift/GiftUsedUp.swift +++ b/Bitkit/Views/Gift/GiftUsedUp.swift @@ -6,10 +6,10 @@ struct GiftUsedUp: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - SheetHeader(title: t("Out of Gifts")) + SheetHeader(title: t("other__gift__used_up__title")) VStack(spacing: 0) { - BodyMText(tTodo("Sorry, you’re too late! All gifts for this code have already been claimed.")) + BodyMText(t("other__gift__used_up__text")) .frame(maxWidth: .infinity, alignment: .leading) Spacer() diff --git a/Bitkit/Views/Onboarding/OnboardingSlider.swift b/Bitkit/Views/Onboarding/OnboardingSlider.swift index 6af0bfe0..a13b7323 100644 --- a/Bitkit/Views/Onboarding/OnboardingSlider.swift +++ b/Bitkit/Views/Onboarding/OnboardingSlider.swift @@ -67,7 +67,7 @@ struct OnboardingSlider: View { OnboardingTab( imageName: "keyring", title: t("onboarding__slide0_header"), - text: tTodo("Bitkit hands you the keys to control your money, profile, and contacts. Take charge and become borderless."), + text: t("onboarding__slide0_text"), accentColor: .blueAccent ) .tag(0) @@ -76,7 +76,7 @@ struct OnboardingSlider: View { OnboardingTab( imageName: "lightning", title: t("onboarding__slide1_header"), - text: tTodo("Enjoy instant and cheap payments with friends, family, and merchants on the Lightning Network."), + text: t("onboarding__slide1_text"), disclaimerText: GeoService.shared.isGeoBlocked ? t("onboarding__slide1_note") : nil, accentColor: .purpleAccent ) @@ -86,9 +86,7 @@ struct OnboardingSlider: View { OnboardingTab( imageName: "shield-figure", title: t("onboarding__slide3_header"), - text: tTodo( - "Your money, your privacy. Swipe to hide your balance and spend more privately, with no data tracking and zero KYC." - ), + text: t("onboarding__slide3_text"), accentColor: .greenAccent ) .tag(2) diff --git a/Bitkit/Views/Onboarding/TermsView.swift b/Bitkit/Views/Onboarding/TermsView.swift index 06b923f3..def3f5f8 100644 --- a/Bitkit/Views/Onboarding/TermsView.swift +++ b/Bitkit/Views/Onboarding/TermsView.swift @@ -25,8 +25,8 @@ private struct TermsFooter: View { VStack(spacing: 25) { VStack(alignment: .leading, spacing: 12) { FooterItem( - title: tTodo("Terms of Use"), - subtitle: tTodo("By continuing you declare that you have read and accept the terms of use."), + title: t("onboarding__tos_checkbox"), + subtitle: t("onboarding__tos_checkbox_value"), subtitleUrl: nil ) @@ -34,7 +34,7 @@ private struct TermsFooter: View { FooterItem( title: t("onboarding__pp_checkbox"), - subtitle: tTodo("By continuing you declare that you have read and accept the privacy policy."), + subtitle: t("onboarding__pp_checkbox_value"), subtitleUrl: URL(string: Env.privacyPolicyUrl) ) diff --git a/Bitkit/Views/Recovery/RecoveryMnemonicScreen.swift b/Bitkit/Views/Recovery/RecoveryMnemonicScreen.swift index c9ba8a27..16c3bbf1 100644 --- a/Bitkit/Views/Recovery/RecoveryMnemonicScreen.swift +++ b/Bitkit/Views/Recovery/RecoveryMnemonicScreen.swift @@ -97,8 +97,8 @@ struct RecoveryMnemonicScreen: View { } else { app.toast( type: .error, - title: "Mnemonic Error", - description: "Unable to load mnemonic phrase" + title: t("security__recovery_mnemonic_error_title"), + description: t("security__recovery_mnemonic_error_text") ) } @@ -106,8 +106,8 @@ struct RecoveryMnemonicScreen: View { } catch { app.toast( type: .error, - title: "Mnemonic Error", - description: "Unable to load mnemonic phrase" + title: t("security__recovery_mnemonic_error_title"), + description: t("security__recovery_mnemonic_error_text") ) } diff --git a/Bitkit/Views/Recovery/RecoveryScreen.swift b/Bitkit/Views/Recovery/RecoveryScreen.swift index dffc613e..392e9b8b 100644 --- a/Bitkit/Views/Recovery/RecoveryScreen.swift +++ b/Bitkit/Views/Recovery/RecoveryScreen.swift @@ -105,7 +105,7 @@ struct RecoveryScreen: View { private func onExportLogs() { Task { guard let zipURL = LogService.shared.zipLogs() else { - app.toast(type: .error, title: "Error", description: "Failed to create log zip file") + app.toast(type: .error, title: t("common__error"), description: t("settings__logs__zip_failed")) return } @@ -215,8 +215,8 @@ struct RecoveryScreen: View { } catch { app.toast( type: .error, - title: "Wipe Failed", - description: "Bitkit was unable to reset your wallet data. Please try again." + title: t("security__wipe_failed_title"), + description: t("security__wipe_failed_description") ) } } diff --git a/Bitkit/Views/Scanner/ManualScanPrompt.swift b/Bitkit/Views/Scanner/ManualScanPrompt.swift index d7d06885..3c36629c 100644 --- a/Bitkit/Views/Scanner/ManualScanPrompt.swift +++ b/Bitkit/Views/Scanner/ManualScanPrompt.swift @@ -17,7 +17,7 @@ struct ScannerManualEntryPrompt: View { .accessibilityIdentifier("DialogCancel") } - BodyMSBText("Enter QR Code String") + BodyMSBText(t("other__scan__manual_prompt")) SwiftUI.TextField("", text: $text) .textInputAutocapitalization(.never) diff --git a/Bitkit/Views/Scanner/ScannerScreen.swift b/Bitkit/Views/Scanner/ScannerScreen.swift index 1a660905..4d54f50c 100644 --- a/Bitkit/Views/Scanner/ScannerScreen.swift +++ b/Bitkit/Views/Scanner/ScannerScreen.swift @@ -45,7 +45,7 @@ struct ScannerScreen: View { if Env.isE2E { CustomButton( - title: "Enter QR Code String", + title: t("other__scan__manual_prompt"), variant: .secondary, shouldExpand: true ) { diff --git a/Bitkit/Views/Scanner/ScannerSheet.swift b/Bitkit/Views/Scanner/ScannerSheet.swift index 511287b8..e98a7873 100644 --- a/Bitkit/Views/Scanner/ScannerSheet.swift +++ b/Bitkit/Views/Scanner/ScannerSheet.swift @@ -45,7 +45,7 @@ struct ScannerSheet: View { if Env.isE2E { CustomButton( - title: "Enter QRCode String", + title: t("other__scan__manual_prompt"), variant: .secondary, shouldExpand: true ) { diff --git a/Bitkit/Views/Settings/Advanced/AddressViewer.swift b/Bitkit/Views/Settings/Advanced/AddressViewer.swift index b2c4d4aa..7f4e95bc 100644 --- a/Bitkit/Views/Settings/Advanced/AddressViewer.swift +++ b/Bitkit/Views/Settings/Advanced/AddressViewer.swift @@ -223,7 +223,7 @@ struct AddressViewer: View { // Load More Button if !addresses.isEmpty { CustomButton( - title: "Generate 20 More", + title: t("settings__addr__gen_20"), variant: .secondary, size: .small, isLoading: isLoading, @@ -282,7 +282,7 @@ struct AddressViewer: View { } catch { await MainActor.run { - app.toast(type: .error, title: "Error Loading Addresses", description: error.localizedDescription) + app.toast(type: .error, title: t("settings__addr__error_loading_title"), description: error.localizedDescription) isLoading = false } } @@ -313,7 +313,7 @@ struct AddressViewer: View { } catch { await MainActor.run { - app.toast(type: .error, title: "Error Loading More Addresses", description: error.localizedDescription) + app.toast(type: .error, title: t("settings__addr__error_loading_more_title"), description: error.localizedDescription) isLoading = false } } @@ -331,11 +331,11 @@ struct AddressViewer: View { await MainActor.run { addressBalances = balances isLoadingBalances = false - app.toast(type: .success, title: "Balances Updated", description: "Address balances have been refreshed") + app.toast(type: .success, title: t("settings__addr__balances_updated_title"), description: t("settings__addr__balances_updated_description")) } } catch { await MainActor.run { - app.toast(type: .error, title: "Error Checking Balances", description: error.localizedDescription) + app.toast(type: .error, title: t("settings__addr__error_checking_balances_title"), description: error.localizedDescription) isLoadingBalances = false } } diff --git a/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift b/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift index d10a8e53..c033dd87 100644 --- a/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift +++ b/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift @@ -316,7 +316,7 @@ struct LightningConnectionsView: View { private func onExportLogs() { Task { guard let zipURL = LogService.shared.zipLogs() else { - app.toast(type: .error, title: "Error", description: "Failed to create log zip file") + app.toast(type: .error, title: t("common__error"), description: t("settings__logs__zip_failed")) return } diff --git a/Bitkit/Views/Settings/Advanced/RgsSettingsScreen.swift b/Bitkit/Views/Settings/Advanced/RgsSettingsScreen.swift index 4e5df643..e2734957 100644 --- a/Bitkit/Views/Settings/Advanced/RgsSettingsScreen.swift +++ b/Bitkit/Views/Settings/Advanced/RgsSettingsScreen.swift @@ -97,8 +97,8 @@ struct RgsSettingsScreen: View { } else { app.toast( type: .warning, - title: tTodo("settings__rgs__error_peer"), - description: errorMessage ?? tTodo("settings__rgs__server_error_description"), + title: t("settings__rgs__error_peer"), + description: errorMessage ?? t("settings__rgs__server_error_description"), accessibilityIdentifier: "RgsErrorToast" ) } diff --git a/Bitkit/Views/Settings/Backup/ResetAndRestore.swift b/Bitkit/Views/Settings/Backup/ResetAndRestore.swift index 97bcd23a..286906df 100644 --- a/Bitkit/Views/Settings/Backup/ResetAndRestore.swift +++ b/Bitkit/Views/Settings/Backup/ResetAndRestore.swift @@ -69,8 +69,8 @@ struct ResetAndRestore: View { } catch { app.toast( type: .error, - title: "Wipe Failed", - description: "Bitkit was unable to reset your wallet data. Please try again." + title: t("security__wipe_failed_title"), + description: t("security__wipe_failed_description") ) } } diff --git a/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift b/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift index d6c0a495..959d6f88 100644 --- a/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift +++ b/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift @@ -42,10 +42,10 @@ struct LanguageSettingsScreen: View { .navigationBarHidden(true) .padding(.horizontal, 16) .bottomSafeAreaPadding() - .alert("Language Changed", isPresented: $showAlert) { - Button("OK", role: .cancel) {} + .alert(t("settings__general__language_changed_title"), isPresented: $showAlert) { + Button(t("common__ok"), role: .cancel) {} } message: { - Text("The app needs to be restarted for the language change to take full effect.") + Text(t("settings__general__language_changed_message")) } } } diff --git a/Bitkit/Views/Settings/GeneralSettingsView.swift b/Bitkit/Views/Settings/GeneralSettingsView.swift index 7aedc6d1..200be288 100644 --- a/Bitkit/Views/Settings/GeneralSettingsView.swift +++ b/Bitkit/Views/Settings/GeneralSettingsView.swift @@ -58,7 +58,7 @@ struct GeneralSettingsView: View { NavigationLink(value: Route.widgetsSettings) { SettingsListLabel( title: t("settings__widgets__nav_title"), - rightText: settings.showWidgets ? tTodo("On") : tTodo("Off") + rightText: settings.showWidgets ? t("common__on") : t("common__off") ) } .accessibilityIdentifier("WidgetsSettings") @@ -66,7 +66,7 @@ struct GeneralSettingsView: View { NavigationLink(value: app.hasSeenQuickpayIntro ? Route.quickpay : Route.quickpayIntro) { SettingsListLabel( title: t("settings__quickpay__nav_title"), - rightText: settings.enableQuickpay ? tTodo("On") : tTodo("Off") + rightText: settings.enableQuickpay ? t("common__on") : t("common__off") ) } .accessibilityIdentifier("QuickpaySettings") @@ -74,7 +74,7 @@ struct GeneralSettingsView: View { NavigationLink(value: app.hasSeenNotificationsIntro ? Route.notifications : Route.notificationsIntro) { SettingsListLabel( title: t("settings__notifications__nav_title"), - rightText: settings.enableNotifications ? tTodo("On") : tTodo("Off") + rightText: settings.enableNotifications ? t("common__on") : t("common__off") ) } .accessibilityIdentifier("NotificationsSettings") diff --git a/Bitkit/Views/Settings/LogView.swift b/Bitkit/Views/Settings/LogView.swift index e1e99be4..974f3be6 100644 --- a/Bitkit/Views/Settings/LogView.swift +++ b/Bitkit/Views/Settings/LogView.swift @@ -32,12 +32,12 @@ struct LogView: View { .disabled(logFiles.isEmpty) ) .alert("Delete All Logs", isPresented: $showingDeleteConfirmation) { - Button("Cancel", role: .cancel) {} - Button("Delete", role: .destructive) { + Button(t("common__cancel"), role: .cancel) {} + Button(t("common__delete"), role: .destructive) { deleteAllLogs() } } message: { - Text("Are you sure you want to delete all log files? This action cannot be undone.") + Text(t("settings__logs__delete_confirm")) } .task { loadLogFiles() diff --git a/Bitkit/Views/Shop/ShopDiscover.swift b/Bitkit/Views/Shop/ShopDiscover.swift index 3c36fe25..385fc106 100644 --- a/Bitkit/Views/Shop/ShopDiscover.swift +++ b/Bitkit/Views/Shop/ShopDiscover.swift @@ -38,28 +38,28 @@ struct ShopDiscover: View { // Categories data private let categories: [ShopCategory] = [ - ShopCategory(title: "Apparel", route: "buy/apparel", iconName: "pedestrian"), - ShopCategory(title: "Automobiles", route: "buy/automobiles", iconName: "car"), - ShopCategory(title: "Cruises", route: "buy/cruises", iconName: "train"), - ShopCategory(title: "Ecommerce", route: "buy/ecommerce", iconName: "shopping-cart"), - ShopCategory(title: "Electronics", route: "buy/electronics", iconName: "printer"), - ShopCategory(title: "Entertainment", route: "buy/entertainment", iconName: "headphones"), - ShopCategory(title: "Experiences", route: "buy/experiences", iconName: "globe"), - ShopCategory(title: "Flights", route: "buy/flights", iconName: "airplane"), - ShopCategory(title: "Food", route: "buy/food", iconName: "storefront"), - ShopCategory(title: "Food Delivery", route: "buy/food-delivery", iconName: "bicycle"), - ShopCategory(title: "Games", route: "buy/games", iconName: "game-controller"), - ShopCategory(title: "Gifts", route: "buy/gifts", iconName: "gift"), - ShopCategory(title: "Groceries", route: "buy/groceries", iconName: "shopping-bag"), - ShopCategory(title: "Health & Beauty", route: "buy/health-beauty", iconName: "heartbeat"), - ShopCategory(title: "Home", route: "buy/home", iconName: "house"), - ShopCategory(title: "Multi-Brand", route: "buy/multi-brand", iconName: "stack"), - ShopCategory(title: "Pets", route: "buy/pets", iconName: "horse"), - ShopCategory(title: "Restaurants", route: "buy/restaurants", iconName: "fork-knife"), - ShopCategory(title: "Retail", route: "buy/retail", iconName: "storefront"), - ShopCategory(title: "Streaming", route: "buy/streaming", iconName: "video-camera"), - ShopCategory(title: "Travel", route: "buy/travel", iconName: "airplane"), - ShopCategory(title: "VoIP", route: "buy/voip", iconName: "phone-call"), + ShopCategory(title: t("other__shop__category_apparel"), route: "buy/apparel", iconName: "pedestrian"), + ShopCategory(title: t("other__shop__category_automobiles"), route: "buy/automobiles", iconName: "car"), + ShopCategory(title: t("other__shop__category_cruises"), route: "buy/cruises", iconName: "train"), + ShopCategory(title: t("other__shop__category_ecommerce"), route: "buy/ecommerce", iconName: "shopping-cart"), + ShopCategory(title: t("other__shop__category_electronics"), route: "buy/electronics", iconName: "printer"), + ShopCategory(title: t("other__shop__category_entertainment"), route: "buy/entertainment", iconName: "headphones"), + ShopCategory(title: t("other__shop__category_experiences"), route: "buy/experiences", iconName: "globe"), + ShopCategory(title: t("other__shop__category_flights"), route: "buy/flights", iconName: "airplane"), + ShopCategory(title: t("other__shop__category_food"), route: "buy/food", iconName: "storefront"), + ShopCategory(title: t("other__shop__category_food_delivery"), route: "buy/food-delivery", iconName: "bicycle"), + ShopCategory(title: t("other__shop__category_games"), route: "buy/games", iconName: "game-controller"), + ShopCategory(title: t("other__shop__category_gifts"), route: "buy/gifts", iconName: "gift"), + ShopCategory(title: t("other__shop__category_groceries"), route: "buy/groceries", iconName: "shopping-bag"), + ShopCategory(title: t("other__shop__category_health_beauty"), route: "buy/health-beauty", iconName: "heartbeat"), + ShopCategory(title: t("other__shop__category_home"), route: "buy/home", iconName: "house"), + ShopCategory(title: t("other__shop__category_multi_brand"), route: "buy/multi-brand", iconName: "stack"), + ShopCategory(title: t("other__shop__category_pets"), route: "buy/pets", iconName: "horse"), + ShopCategory(title: t("other__shop__category_restaurants"), route: "buy/restaurants", iconName: "fork-knife"), + ShopCategory(title: t("other__shop__category_retail"), route: "buy/retail", iconName: "storefront"), + ShopCategory(title: t("other__shop__category_streaming"), route: "buy/streaming", iconName: "video-camera"), + ShopCategory(title: t("other__shop__category_travel"), route: "buy/travel", iconName: "airplane"), + ShopCategory(title: t("other__shop__category_voip"), route: "buy/voip", iconName: "phone-call"), ] // Featured cards data diff --git a/Bitkit/Views/Transfer/FundReceiveView.swift b/Bitkit/Views/Transfer/FundReceiveView.swift index bd3b0382..9581f83f 100644 --- a/Bitkit/Views/Transfer/FundReceiveView.swift +++ b/Bitkit/Views/Transfer/FundReceiveView.swift @@ -5,7 +5,7 @@ struct FundReceiveView: View { @EnvironmentObject var app: AppViewModel var body: some View { - Text("FundReceiveView") + Text(t("wallet__fund_receive_title")) .navigationBarTitleDisplayMode(.inline) } } diff --git a/Bitkit/Views/Transfer/LnurlChannel.swift b/Bitkit/Views/Transfer/LnurlChannel.swift index 81135cf9..d1a3bd37 100644 --- a/Bitkit/Views/Transfer/LnurlChannel.swift +++ b/Bitkit/Views/Transfer/LnurlChannel.swift @@ -46,7 +46,7 @@ struct LnurlChannel: View { VStack(spacing: 16) { ProgressView() .scaleEffect(1.2) - BodyMText("Loading channel information...", textColor: .textSecondary) + BodyMText(t("other__lnurl_channel_loading"), textColor: .textSecondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) } else if channelInfo != nil { @@ -95,7 +95,7 @@ struct LnurlChannel: View { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) .foregroundColor(.orange) - BodyMText("Failed to load channel information", textColor: .textSecondary) + BodyMText(t("other__lnurl_channel_load_failed"), textColor: .textSecondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) } @@ -136,8 +136,8 @@ struct LnurlChannel: View { guard let nodeId = wallet.nodeId else { app.toast( type: .error, - title: "Error", - description: "Node ID is missing" + title: t("common__error"), + description: t("other__lnurl_channel_node_id_missing") ) return } diff --git a/Bitkit/Views/Transfer/SpendingConfirm.swift b/Bitkit/Views/Transfer/SpendingConfirm.swift index 99fa783a..93a7a7e5 100644 --- a/Bitkit/Views/Transfer/SpendingConfirm.swift +++ b/Bitkit/Views/Transfer/SpendingConfirm.swift @@ -72,7 +72,7 @@ struct SpendingConfirm: View { } HStack(alignment: .center, spacing: 0) { - BodyMText(tTodo("Set up in background"), textColor: .textPrimary) + BodyMText(t("lightning__spending_confirm__background_setup"), textColor: .textPrimary) Spacer() diff --git a/Bitkit/Views/Wallets/Activity/ActivityLatest.swift b/Bitkit/Views/Wallets/Activity/ActivityLatest.swift index aad4ead9..74eb8123 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityLatest.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityLatest.swift @@ -50,7 +50,7 @@ struct ActivityLatest: View { } ) } else { - CustomButton(title: tTodo("Show All"), variant: .tertiary) { + CustomButton(title: t("common__show_all"), variant: .tertiary) { navigation.navigate(.activityList) } .accessibilityIdentifier("ActivityShowAll") diff --git a/Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawFailure.swift b/Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawFailure.swift index c880d521..0d053037 100644 --- a/Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawFailure.swift +++ b/Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawFailure.swift @@ -6,17 +6,15 @@ struct LnurlWithdrawFailure: View { @EnvironmentObject var sheets: SheetViewModel let amount: UInt64 - // TODO: add localized strings - var body: some View { VStack(spacing: 0) { - SheetHeader(title: "Withdrawal Failed", showBackButton: true) + SheetHeader(title: t("other__lnurl_withdr_failure_title"), showBackButton: true) MoneyStack(sats: Int(amount), showSymbol: true) .padding(.top, 16) .padding(.bottom, 42) - BodyMText("Your withdrawal was unsuccessful. Please scan the QR code again or contact support.") + BodyMText(t("other__lnurl_withdr_failure_text")) .frame(maxWidth: .infinity, alignment: .leading) Spacer() @@ -30,11 +28,11 @@ struct LnurlWithdrawFailure: View { Spacer() HStack(alignment: .center, spacing: 16) { - CustomButton(title: "Support", variant: .secondary) { + CustomButton(title: t("lightning__support"), variant: .secondary) { onSupport() } - CustomButton(title: "Scan QR") { + CustomButton(title: t("wallet__recipient_scan")) { onScan() } } diff --git a/Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift b/Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift index f8350d99..590dd027 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift @@ -91,7 +91,11 @@ struct ReceiveCjitAmount: View { } } else { // Show error if node is not running or timed out - app.toast(type: .warning, title: "Lightning not ready", description: "Lightning node must be running to create an invoice") + app.toast( + type: .warning, + title: t("wallet__receive_lightning_not_ready_title"), + description: t("wallet__receive_lightning_not_ready_description") + ) } } } diff --git a/Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift b/Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift index db06ad85..ca669d9b 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift @@ -58,7 +58,7 @@ struct ReceiveCjitConfirmation: View { Spacer() HStack(alignment: .center, spacing: 0) { - BodyMText(tTodo("Enable background setup"), textColor: .textPrimary) + BodyMText(t("wallet__receive_background_setup"), textColor: .textPrimary) Spacer() diff --git a/Bitkit/Views/Wallets/Receive/ReceiveCjitGeoBlocked.swift b/Bitkit/Views/Wallets/Receive/ReceiveCjitGeoBlocked.swift index cb1bf6b5..754b441c 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveCjitGeoBlocked.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveCjitGeoBlocked.swift @@ -21,7 +21,7 @@ struct ReceiveCjitGeoBlocked: View { Spacer() - CustomButton(title: tTodo("Advanced Setup")) { + CustomButton(title: t("onboarding__advanced_setup")) { sheets.hideSheet() navigation.navigate(.fundingAdvanced) } diff --git a/Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift b/Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift index 752deb13..4496338d 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift @@ -41,11 +41,11 @@ struct ReceiveCjitLearnMore: View { Spacer() - BodyMText(tTodo("Enable background setup to safely exit Bitkit while your balance is being configured.")) + BodyMText(t("wallet__receive_background_setup_text")) .padding(.bottom, 16) HStack(alignment: .center, spacing: 0) { - BodyMText(tTodo("Enable background setup"), textColor: .textPrimary) + BodyMText(t("wallet__receive_background_setup"), textColor: .textPrimary) Spacer() diff --git a/Bitkit/Views/Wallets/Receive/ReceiveEdit.swift b/Bitkit/Views/Wallets/Receive/ReceiveEdit.swift index eca166e0..d325ee7c 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveEdit.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveEdit.swift @@ -149,8 +149,8 @@ struct ReceiveEdit: View { // Show error if node is not running or timed out app.toast( type: .warning, - title: "Lightning not ready", - description: "Lightning node must be running to create an invoice" + title: t("wallet__receive_lightning_not_ready_title"), + description: t("wallet__receive_lightning_not_ready_description") ) } } @@ -167,7 +167,7 @@ struct ReceiveEdit: View { tagManager.removeTagFromSelection(tag) } } catch { - app.toast(type: .error, title: "Failed to delete tag", description: error.localizedDescription) + app.toast(type: .error, title: t("wallet__tags_delete_error_header"), description: error.localizedDescription) } } diff --git a/Bitkit/Views/Wallets/Receive/ReceiveQr.swift b/Bitkit/Views/Wallets/Receive/ReceiveQr.swift index c1e415e2..cf7bd318 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveQr.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveQr.swift @@ -116,7 +116,7 @@ struct ReceiveQr: View { } } } else { - CustomButton(title: showDetails ? tTodo("QR Code") : tTodo("Show Details")) { + CustomButton(title: showDetails ? t("common__qr_code") : t("common__show_details")) { showDetails.toggle() } .accessibilityIdentifier(showDetails ? "QRCode" : "ShowDetails") @@ -210,10 +210,10 @@ struct ReceiveQr: View { var cjitOnboarding: some View { VStack(alignment: .leading, spacing: 0) { - DisplayText(tTodo("Receive on spending balance"), accentColor: .purpleAccent) + DisplayText(t("wallet__receive_spending_title"), accentColor: .purpleAccent) .padding(.bottom, 12) - BodyMText(tTodo("Enjoy instant and cheap\ntransactions with friends, family,\nand merchants.")) + BodyMText(t("wallet__receive_spending_description")) Spacer() diff --git a/Bitkit/Views/Wallets/Receive/ReceiveTag.swift b/Bitkit/Views/Wallets/Receive/ReceiveTag.swift index febc620d..a6b6f3b2 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveTag.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveTag.swift @@ -41,7 +41,7 @@ struct ReceiveTag: View { do { guard let paymentId = await wallet.paymentId(), !paymentId.isEmpty else { - app.toast(type: .error, title: "Failed to add tag", description: "Payment ID not available") + app.toast(type: .error, title: t("wallet__tags_add_error_header"), description: t("wallet__tags_add_error_no_payment_id")) return } @@ -57,7 +57,7 @@ struct ReceiveTag: View { navigationPath.removeLast() } } catch { - app.toast(type: .error, title: "Failed to add tag", description: error.localizedDescription) + app.toast(type: .error, title: t("wallet__tags_add_error_header"), description: error.localizedDescription) } } } diff --git a/Bitkit/Views/Wallets/SavingsWalletView.swift b/Bitkit/Views/Wallets/SavingsWalletView.swift index a4fc5f03..e299b5d3 100644 --- a/Bitkit/Views/Wallets/SavingsWalletView.swift +++ b/Bitkit/Views/Wallets/SavingsWalletView.swift @@ -35,7 +35,7 @@ struct SavingsWalletView: View { ScrollView(showsIndicators: false) { ActivityList(viewType: .onchain) - CustomButton(title: tTodo("Show All"), variant: .tertiary) { + CustomButton(title: t("common__show_all"), variant: .tertiary) { navigation.navigate(.activityList) } /// Leave some space for TabBar @@ -75,7 +75,7 @@ struct SavingsWalletView: View { var transferButton: some View { CustomButton( - title: tTodo("Transfer To Spending"), + title: t("wallet__transfer_to_spending"), variant: .secondary, icon: Image("arrow-up-down") .resizable() diff --git a/Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift b/Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift index cd39e4d1..b9571bd6 100644 --- a/Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift +++ b/Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift @@ -74,7 +74,7 @@ struct LnurlPayConfirm: View { .padding(.trailing, 6) // TODO: get actual fee - BodySSBText("Instant (±$0.02)") + BodySSBText(t("wallet__lnurl_pay_confirm__fee_instant")) } } .padding(.vertical) diff --git a/Bitkit/Views/Wallets/Send/SendAmountView.swift b/Bitkit/Views/Wallets/Send/SendAmountView.swift index 8943849a..54db3ff2 100644 --- a/Bitkit/Views/Wallets/Send/SendAmountView.swift +++ b/Bitkit/Views/Wallets/Send/SendAmountView.swift @@ -178,8 +178,8 @@ struct SendAmountView: View { if UInt64(wallet.maxSendLightningSats) < amountSats { app.toast( type: .error, - title: "Insufficient Funds", - description: "You do not have enough funds in the selected wallet." + title: t("wallet__send_insufficient_funds_title"), + description: t("wallet__send_insufficient_funds_description") ) return } @@ -195,8 +195,8 @@ struct SendAmountView: View { if wallet.availableUtxos.isEmpty { app.toast( type: .error, - title: "No UTXOs", - description: "You do not have any UTXOs to spend." + title: t("wallet__send_no_utxos_title"), + description: t("wallet__send_no_utxos_description") ) return } @@ -204,8 +204,8 @@ struct SendAmountView: View { if wallet.availableUtxos.reduce(0, { $0 + $1.valueSats }) < amountSats { app.toast( type: .error, - title: "Insufficient Funds", - description: "You do not have enough funds in the selected wallet." + title: t("wallet__send_insufficient_funds_title"), + description: t("wallet__send_insufficient_funds_description") ) return } @@ -218,8 +218,8 @@ struct SendAmountView: View { if totalSelectedSats < amountSats { app.toast( type: .error, - title: "Insufficient Funds", - description: "You do not have enough funds in the selected wallet." + title: t("wallet__send_insufficient_funds_title"), + description: t("wallet__send_insufficient_funds_description") ) return } @@ -228,7 +228,7 @@ struct SendAmountView: View { } } catch { Logger.error(error, context: "Failed to set fee rate or send amount") - app.toast(type: .error, title: "Send Error", description: error.localizedDescription) + app.toast(type: .error, title: t("wallet__send_error"), description: error.localizedDescription) } } diff --git a/Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift b/Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift index 58d845ee..eeba1363 100644 --- a/Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift +++ b/Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift @@ -49,7 +49,7 @@ struct SendEnterManuallyView: View { Spacer(minLength: 16) - CustomButton(title: "Continue", isDisabled: !app.isManualEntryInputValid) { + CustomButton(title: t("common__continue"), isDisabled: !app.isManualEntryInputValid) { await handleContinue() } .buttonBottomPadding(isFocused: isTextEditorFocused) diff --git a/Bitkit/Views/Wallets/Send/SendUtxoSelectionView.swift b/Bitkit/Views/Wallets/Send/SendUtxoSelectionView.swift index ab3f7ef8..24a32973 100644 --- a/Bitkit/Views/Wallets/Send/SendUtxoSelectionView.swift +++ b/Bitkit/Views/Wallets/Send/SendUtxoSelectionView.swift @@ -73,7 +73,7 @@ struct SendUtxoSelectionView: View { navigationPath.append(.confirm) } catch { Logger.error(error, context: "Failed to set fee rate") - app.toast(type: .error, title: "Send Error", description: error.localizedDescription) + app.toast(type: .error, title: t("wallet__send_error"), description: error.localizedDescription) } } .padding(.bottom, 16) diff --git a/Bitkit/Views/Wallets/Sheets/AddTagSheet.swift b/Bitkit/Views/Wallets/Sheets/AddTagSheet.swift index d8df73a7..e87b0736 100644 --- a/Bitkit/Views/Wallets/Sheets/AddTagSheet.swift +++ b/Bitkit/Views/Wallets/Sheets/AddTagSheet.swift @@ -63,7 +63,7 @@ struct AddTagSheet: View { try await activityListViewModel.appendTags(toActivity: config.activityId, tags: [tag]) sheets.hideSheet() } catch { - app.toast(type: .error, title: "Failed to add tag", description: error.localizedDescription) + app.toast(type: .error, title: t("wallet__tags_add_error_header"), description: error.localizedDescription) } } } diff --git a/Bitkit/Views/Wallets/Sheets/BoostSheet.swift b/Bitkit/Views/Wallets/Sheets/BoostSheet.swift index 9ee5af76..92295629 100644 --- a/Bitkit/Views/Wallets/Sheets/BoostSheet.swift +++ b/Bitkit/Views/Wallets/Sheets/BoostSheet.swift @@ -146,7 +146,7 @@ struct BoostSheet: View { .accessibilityIdentifier("Plus") } - CustomButton(title: "Use Suggested Fee", size: .small) { + CustomButton(title: t("wallet__send_fee_use_suggested"), size: .small) { isEditingFee = false editedFeeRate = nil } diff --git a/Bitkit/Views/Wallets/SpendingWalletView.swift b/Bitkit/Views/Wallets/SpendingWalletView.swift index 339eb9e6..c3c6984b 100644 --- a/Bitkit/Views/Wallets/SpendingWalletView.swift +++ b/Bitkit/Views/Wallets/SpendingWalletView.swift @@ -35,7 +35,7 @@ struct SpendingWalletView: View { ScrollView(showsIndicators: false) { ActivityList(viewType: .lightning) - CustomButton(title: tTodo("Show All"), variant: .tertiary) { + CustomButton(title: t("common__show_all"), variant: .tertiary) { navigation.navigate(.activityList) } /// Leave some space for TabBar @@ -75,7 +75,7 @@ struct SpendingWalletView: View { var transferButton: some View { CustomButton( - title: tTodo("Transfer To Savings"), + title: t("wallet__transfer_to_savings"), variant: .secondary, icon: Image("arrow-up-down") .resizable() diff --git a/Bitkit/Views/Widgets/WidgetsIntroView.swift b/Bitkit/Views/Widgets/WidgetsIntroView.swift index eb71d827..4e03c2d1 100644 --- a/Bitkit/Views/Widgets/WidgetsIntroView.swift +++ b/Bitkit/Views/Widgets/WidgetsIntroView.swift @@ -6,7 +6,7 @@ struct WidgetsIntroView: View { var body: some View { OnboardingView( - navTitle: tTodo("Widgets"), + navTitle: t("widgets__widgets"), title: t("widgets__onboarding__title"), description: t("widgets__onboarding__description"), imageName: "puzzle",