diff --git a/app/components/UI/account/activity_date.html.erb b/app/components/UI/account/activity_date.html.erb index f563801ab..6fd158d53 100644 --- a/app/components/UI/account/activity_date.html.erb +++ b/app/components/UI/account/activity_date.html.erb @@ -21,7 +21,7 @@
<%= end_balance_money.format %> - <%= render DS::Tooltip.new(text: "The end of day balance, after all transactions and adjustments", placement: "left", size: "sm") %> + <%= render DS::Tooltip.new(text: t(".balance_tooltip"), placement: "left", size: "sm") %>
<%= helpers.icon "chevron-down", class: "group-open:rotate-180" %>
@@ -32,7 +32,7 @@ <% if balance %> <%= render UI::Account::BalanceReconciliation.new(balance: balance, account: account) %> <% else %> -

No balance data available for this date

+

<%= t(".no_balance_data") %>

<% end %> diff --git a/app/components/UI/account/chart.html.erb b/app/components/UI/account/chart.html.erb index efcdca7d6..54933dcfe 100644 --- a/app/components/UI/account/chart.html.erb +++ b/app/components/UI/account/chart.html.erb @@ -50,7 +50,7 @@ data-time-series-chart-data-value="<%= series.to_json %>"> <% else %>
-

No data available

+

<%= t(".no_data_available") %>

<% end %> diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 6d5e6b9fc..a38e5166c 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -58,7 +58,7 @@ class CategoriesController < ApplicationController def destroy_all Current.family.categories.destroy_all - redirect_back_or_to categories_path, notice: "All categories deleted" + redirect_back_or_to categories_path, notice: t(".success") end def bootstrap diff --git a/app/controllers/chats_controller.rb b/app/controllers/chats_controller.rb index eb3e2c5c7..aefb7daf4 100644 --- a/app/controllers/chats_controller.rb +++ b/app/controllers/chats_controller.rb @@ -29,7 +29,7 @@ class ChatsController < ApplicationController @chat.update!(chat_params) respond_to do |format| - format.html { redirect_back_or_to chat_path(@chat), notice: "Chat updated" } + format.html { redirect_back_or_to chat_path(@chat), notice: t(".success") } format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@chat, :title), partial: "chats/chat_title", locals: { chat: @chat }) } end end @@ -38,7 +38,7 @@ class ChatsController < ApplicationController @chat.destroy clear_last_viewed_chat - redirect_to chats_path, notice: "Chat was successfully deleted" + redirect_to chats_path, notice: t(".notice") end def retry diff --git a/app/controllers/concerns/self_hostable.rb b/app/controllers/concerns/self_hostable.rb index 3631571ae..83498269b 100644 --- a/app/controllers/concerns/self_hostable.rb +++ b/app/controllers/concerns/self_hostable.rb @@ -23,7 +23,7 @@ module SelfHostable if controller_name == "pages" && action_name == "redis_configuration_error" # If Redis is now working, redirect to home if redis_connected? - redirect_to root_path, notice: "Redis is now configured properly! You can now setup your Sure application." + redirect_to root_path, notice: t("concerns.self_hostable.redis_configured") end return diff --git a/app/controllers/holdings_controller.rb b/app/controllers/holdings_controller.rb index 14c7f55b8..45a89f938 100644 --- a/app/controllers/holdings_controller.rb +++ b/app/controllers/holdings_controller.rb @@ -42,7 +42,7 @@ class HoldingsController < ApplicationController @holding.destroy_holding_and_entries! flash[:notice] = t(".success") else - flash[:alert] = "You cannot delete this holding" + flash[:alert] = t(".cannot_delete") end respond_to do |format| diff --git a/app/controllers/import/cleans_controller.rb b/app/controllers/import/cleans_controller.rb index 7d91f2134..4b0b46c16 100644 --- a/app/controllers/import/cleans_controller.rb +++ b/app/controllers/import/cleans_controller.rb @@ -6,7 +6,7 @@ class Import::CleansController < ApplicationController def show unless @import.configured? redirect_path = @import.is_a?(PdfImport) ? import_path(@import) : import_configuration_path(@import) - return redirect_to redirect_path, alert: "Please configure your import before proceeding." + return redirect_to redirect_path, alert: t(".not_configured") end rows = @import.rows_ordered diff --git a/app/controllers/import/confirms_controller.rb b/app/controllers/import/confirms_controller.rb index 1a687d4bd..029a58469 100644 --- a/app/controllers/import/confirms_controller.rb +++ b/app/controllers/import/confirms_controller.rb @@ -8,7 +8,7 @@ class Import::ConfirmsController < ApplicationController return redirect_to import_path(@import) end - redirect_to import_clean_path(@import), alert: "You have invalid data, please edit until all errors are resolved" unless @import.cleaned? + redirect_to import_clean_path(@import), alert: t(".invalid_data") unless @import.cleaned? end private diff --git a/app/controllers/import/qif_category_selections_controller.rb b/app/controllers/import/qif_category_selections_controller.rb index 2ed7b195a..cf7538b9b 100644 --- a/app/controllers/import/qif_category_selections_controller.rb +++ b/app/controllers/import/qif_category_selections_controller.rb @@ -54,7 +54,7 @@ class Import::QifCategorySelectionsController < ApplicationController @import.sync_mappings unless format_changed end - redirect_to import_clean_path(@import), notice: "Categories and tags saved." + redirect_to import_clean_path(@import), notice: t(".success") end private diff --git a/app/controllers/import/uploads_controller.rb b/app/controllers/import/uploads_controller.rb index 0212bc850..bd08c3e2a 100644 --- a/app/controllers/import/uploads_controller.rb +++ b/app/controllers/import/uploads_controller.rb @@ -85,7 +85,7 @@ class Import::UploadsController < ApplicationController @import.sync_mappings end - redirect_to import_qif_category_selection_path(@import), notice: "QIF file uploaded successfully." + redirect_to import_qif_category_selection_path(@import), notice: t(".qif_uploaded") end def csv_str diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 4a3cc0492..108cbd393 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -22,9 +22,9 @@ class ImportsController < ApplicationController def publish @import.publish_later - redirect_to import_path(@import), notice: "Your import has started in the background." + redirect_to import_path(@import), notice: t(".started") rescue Import::MaxRowCountExceededError - redirect_back_or_to import_path(@import), alert: "Your import exceeds the maximum row count of #{@import.max_row_count}." + redirect_back_or_to import_path(@import), alert: t(".max_rows_exceeded", max: @import.max_row_count) end def index @@ -112,22 +112,22 @@ class ImportsController < ApplicationController def revert @import.revert_later - redirect_to imports_path, notice: "Import is reverting in the background." + redirect_to imports_path, notice: t(".started") end def apply_template if @import.suggested_template @import.apply_template!(@import.suggested_template) - redirect_to import_configuration_path(@import), notice: "Template applied." + redirect_to import_configuration_path(@import), notice: t(".template_applied") else - redirect_to import_configuration_path(@import), alert: "No template found, please manually configure your import." + redirect_to import_configuration_path(@import), alert: t(".no_template_found") end end def destroy @import.destroy - redirect_to imports_path, notice: "Your import has been deleted." + redirect_to imports_path, notice: t(".deleted") end private diff --git a/app/controllers/invite_codes_controller.rb b/app/controllers/invite_codes_controller.rb index f9bcf6760..436e9a7d1 100644 --- a/app/controllers/invite_codes_controller.rb +++ b/app/controllers/invite_codes_controller.rb @@ -8,13 +8,13 @@ class InviteCodesController < ApplicationController def create InviteCode.generate! - redirect_back_or_to invite_codes_path, notice: "Code generated" + redirect_back_or_to invite_codes_path, notice: t(".success") end def destroy code = InviteCode.find(params[:id]) code.destroy - redirect_back_or_to invite_codes_path, notice: "Code deleted" + redirect_back_or_to invite_codes_path, notice: t(".success") end private diff --git a/app/controllers/oidc_accounts_controller.rb b/app/controllers/oidc_accounts_controller.rb index 25548995d..a0e31e7a4 100644 --- a/app/controllers/oidc_accounts_controller.rb +++ b/app/controllers/oidc_accounts_controller.rb @@ -7,7 +7,7 @@ class OidcAccountsController < ApplicationController @pending_auth = session[:pending_oidc_auth] if @pending_auth.nil? - redirect_to new_session_path, alert: "No pending OIDC authentication found" + redirect_to new_session_path, alert: t(".no_pending_oidc") return end @@ -26,7 +26,7 @@ class OidcAccountsController < ApplicationController @pending_auth = session[:pending_oidc_auth] if @pending_auth.nil? - redirect_to new_session_path, alert: "No pending OIDC authentication found" + redirect_to new_session_path, alert: t(".no_pending_oidc") return end @@ -75,7 +75,7 @@ class OidcAccountsController < ApplicationController @pending_auth = session[:pending_oidc_auth] if @pending_auth.nil? - redirect_to new_session_path, alert: "No pending OIDC authentication found" + redirect_to new_session_path, alert: t(".no_pending_oidc") return end @@ -91,7 +91,7 @@ class OidcAccountsController < ApplicationController @pending_auth = session[:pending_oidc_auth] if @pending_auth.nil? - redirect_to new_session_path, alert: "No pending OIDC authentication found" + redirect_to new_session_path, alert: t(".no_pending_oidc") return end @@ -104,7 +104,7 @@ class OidcAccountsController < ApplicationController # domain is not allowed, block JIT account creation—unless there's a # pending invitation for this user. unless invitation.present? || (!AuthConfig.jit_link_only? && AuthConfig.allowed_oidc_domain?(email)) - redirect_to new_session_path, alert: "SSO account creation is disabled. Please contact an administrator." + redirect_to new_session_path, alert: t(".account_creation_disabled") return end @@ -164,7 +164,7 @@ class OidcAccountsController < ApplicationController elsif accept_pending_invitation_for(@user) t("invitations.accept_choice.joined_household") else - "Welcome! Your account has been created." + t(".account_created") end redirect_to root_path, notice: notice else diff --git a/app/controllers/pending_duplicate_merges_controller.rb b/app/controllers/pending_duplicate_merges_controller.rb index 9b690daa5..ec2914097 100644 --- a/app/controllers/pending_duplicate_merges_controller.rb +++ b/app/controllers/pending_duplicate_merges_controller.rb @@ -21,7 +21,7 @@ class PendingDuplicateMergesController < ApplicationController # Manually merge the pending transaction with the selected posted transaction unless merge_params[:posted_entry_id].present? - redirect_back_or_to transactions_path, alert: "Please select a posted transaction to merge with" + redirect_back_or_to transactions_path, alert: t(".no_posted_selected") return end @@ -29,7 +29,7 @@ class PendingDuplicateMergesController < ApplicationController posted_entry = find_eligible_posted_entry(merge_params[:posted_entry_id]) unless posted_entry - redirect_back_or_to transactions_path, alert: "Invalid transaction selected for merge" + redirect_back_or_to transactions_path, alert: t(".invalid_transaction") return end @@ -48,9 +48,9 @@ class PendingDuplicateMergesController < ApplicationController # Immediately merge if @transaction.merge_with_duplicate! - redirect_back_or_to transactions_path, notice: "Pending transaction merged with posted transaction" + redirect_back_or_to transactions_path, notice: t(".merge_success") else - redirect_back_or_to transactions_path, alert: "Could not merge transactions" + redirect_back_or_to transactions_path, alert: t(".merge_failed") end rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotDestroyed, ActiveRecord::Deadlocked, ActiveRecord::LockWaitTimeout => e @@ -64,7 +64,7 @@ class PendingDuplicateMergesController < ApplicationController @transaction = entry.entryable unless @transaction.is_a?(Transaction) && @transaction.pending? - redirect_to transactions_path, alert: "This feature is only available for pending transactions" + redirect_to transactions_path, alert: t("pending_duplicate_merges.set_transaction.pending_only") end end diff --git a/app/controllers/plaid_items_controller.rb b/app/controllers/plaid_items_controller.rb index bfe8c5bd5..08de7e0b4 100644 --- a/app/controllers/plaid_items_controller.rb +++ b/app/controllers/plaid_items_controller.rb @@ -62,7 +62,7 @@ class PlaidItemsController < ApplicationController .select { |pa| pa.account_provider.nil? && pa.account.nil? } # Not linked via new or legacy system if @available_plaid_accounts.empty? - redirect_to account_path(@account), alert: "No available Plaid accounts to link. Please connect a new Plaid account first." + redirect_to account_path(@account), alert: t(".no_available_accounts") end end @@ -72,13 +72,13 @@ class PlaidItemsController < ApplicationController # Verify the Plaid account belongs to this family's Plaid items unless Current.family.plaid_items.include?(plaid_account.plaid_item) - redirect_to account_path(@account), alert: "Invalid Plaid account selected" + redirect_to account_path(@account), alert: t(".invalid_account") return end # Verify the Plaid account is not already linked if plaid_account.account_provider.present? || plaid_account.account.present? - redirect_to account_path(@account), alert: "This Plaid account is already linked" + redirect_to account_path(@account), alert: t(".already_linked") return end @@ -88,7 +88,7 @@ class PlaidItemsController < ApplicationController provider: plaid_account ) - redirect_to accounts_path, notice: "Account successfully linked to Plaid" + redirect_to accounts_path, notice: t(".success") end private diff --git a/app/controllers/rules_controller.rb b/app/controllers/rules_controller.rb index 5e7496cb8..ae9e1ee1f 100644 --- a/app/controllers/rules_controller.rb +++ b/app/controllers/rules_controller.rb @@ -86,8 +86,8 @@ class RulesController < ApplicationController def update if @rule.update(rule_params) respond_to do |format| - format.html { redirect_back_or_to rules_path, notice: "Rule updated" } - format.turbo_stream { stream_redirect_back_or_to rules_path, notice: "Rule updated" } + format.html { redirect_back_or_to rules_path, notice: t(".success") } + format.turbo_stream { stream_redirect_back_or_to rules_path, notice: t(".success") } end else render :edit, status: :unprocessable_entity @@ -96,12 +96,12 @@ class RulesController < ApplicationController def destroy @rule.destroy - redirect_to rules_path, notice: "Rule deleted" + redirect_to rules_path, notice: t(".success") end def destroy_all Current.family.rules.destroy_all - redirect_to rules_path, notice: "All rules deleted" + redirect_to rules_path, notice: t(".success") end def confirm_all diff --git a/app/controllers/settings/api_keys_controller.rb b/app/controllers/settings/api_keys_controller.rb index c509df41a..d132e9a5f 100644 --- a/app/controllers/settings/api_keys_controller.rb +++ b/app/controllers/settings/api_keys_controller.rb @@ -31,7 +31,7 @@ class Settings::ApiKeysController < ApplicationController existing_keys.each { |key| key.update_column(:revoked_at, Time.current) } if @api_key.save - flash[:notice] = "Your API key has been created successfully" + flash[:notice] = t(".success") redirect_to settings_api_key_path else # Restore existing keys if new key creation failed @@ -42,13 +42,13 @@ class Settings::ApiKeysController < ApplicationController def destroy if @api_key.nil? - flash[:alert] = "API key not found" + flash[:alert] = t(".not_found") elsif @api_key.demo_monitoring_key? - flash[:alert] = "This API key cannot be revoked" + flash[:alert] = t(".cannot_revoke") elsif @api_key.revoke! - flash[:notice] = "API key has been revoked successfully" + flash[:notice] = t(".revoked_successfully") else - flash[:alert] = "Failed to revoke API key" + flash[:alert] = t(".revoke_failed") end redirect_to settings_api_key_path end diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 6e8dbbf0e..7015e55f4 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -29,9 +29,9 @@ class Settings::ProfilesController < ApplicationController if @user.destroy # Also destroy the invitation associated with this user for this family Current.family.invitations.find_by(email: @user.email)&.destroy - flash[:notice] = "Member removed successfully." + flash[:notice] = t(".member_removed") else - flash[:alert] = "Failed to remove member." + flash[:alert] = t(".member_removal_failed") end redirect_to settings_profile_path diff --git a/app/controllers/settings/providers_controller.rb b/app/controllers/settings/providers_controller.rb index bdd9b485e..014be3d74 100644 --- a/app/controllers/settings/providers_controller.rb +++ b/app/controllers/settings/providers_controller.rb @@ -66,9 +66,9 @@ class Settings::ProvidersController < ApplicationController # Reload provider configurations if needed reload_provider_configs(updated_fields) - redirect_to settings_providers_path, notice: "Provider settings updated successfully" + redirect_to settings_providers_path, notice: t(".updated_successfully") else - redirect_to settings_providers_path, notice: "No changes were made" + redirect_to settings_providers_path, notice: t(".no_changes") end rescue => error Rails.logger.error("Failed to update provider settings: #{error.class} - #{error.message}") diff --git a/app/controllers/snaptrade_items_controller.rb b/app/controllers/snaptrade_items_controller.rb index 6fab6b0ec..6062c4dfb 100644 --- a/app/controllers/snaptrade_items_controller.rb +++ b/app/controllers/snaptrade_items_controller.rb @@ -380,7 +380,7 @@ class SnaptradeItemsController < ApplicationController end def link_accounts - redirect_to settings_providers_path, alert: "Use the account setup flow instead" + redirect_to settings_providers_path, alert: t(".use_setup_flow") end def select_existing_account diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index da02534bd..d6d5ba7d7 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -8,7 +8,7 @@ class SubscriptionsController < ApplicationController # Upgrade page for unsubscribed users def upgrade if Current.family.subscription&.active? - redirect_to root_path, notice: "You are already contributing. Thank you!" + redirect_to root_path, notice: t(".already_contributing") else @plan = params[:plan] || "annual" render layout: "onboardings" @@ -33,9 +33,9 @@ class SubscriptionsController < ApplicationController def create if Current.family.can_start_trial? Current.family.start_trial_subscription! - redirect_to root_path, notice: "Welcome to Sure!" + redirect_to root_path, notice: t(".welcome") else - redirect_to root_path, alert: "You have already started or completed a trial. Please upgrade to continue." + redirect_to root_path, alert: t(".trial_already_used") end end @@ -54,9 +54,9 @@ class SubscriptionsController < ApplicationController if checkout_result.success? Current.family.start_subscription!(checkout_result.subscription_id) - redirect_to root_path, notice: "Welcome to Sure! Your contribution is appreciated." + redirect_to root_path, notice: t(".welcome_with_contribution") else - redirect_to root_path, alert: "Something went wrong processing your contribution. Please try again." + redirect_to root_path, alert: t(".contribution_failed") end end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 1c2862907..219971529 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -36,7 +36,7 @@ class TagsController < ApplicationController def destroy_all Current.family.tags.destroy_all - redirect_back_or_to tags_path, notice: "All tags deleted" + redirect_back_or_to tags_path, notice: t(".all_deleted") end private diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 8ba56d8e1..a1932c555 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -107,7 +107,7 @@ class TransactionsController < ApplicationController @entry.mark_user_modified! @entry.transaction.lock_attr!(:tag_ids) if @entry.transaction.tags.any? - flash[:notice] = "Transaction created" + flash[:notice] = t(".created") respond_to do |format| format.html { redirect_back_or_to account_path(@entry.account) } @@ -141,7 +141,7 @@ class TransactionsController < ApplicationController @entry.reload respond_to do |format| - format.html { redirect_back_or_to account_path(@entry.account), notice: "Transaction updated" } + format.html { redirect_back_or_to account_path(@entry.account), notice: t(".updated") } format.turbo_stream do in_split_group = helpers.in_split_group?(@entry, params[:grouped]) render turbo_stream: [ diff --git a/app/controllers/transfer_matches_controller.rb b/app/controllers/transfer_matches_controller.rb index 341688d5d..4e67653ba 100644 --- a/app/controllers/transfer_matches_controller.rb +++ b/app/controllers/transfer_matches_controller.rb @@ -32,7 +32,7 @@ class TransferMatchesController < ApplicationController @transfer.sync_account_later - redirect_back_or_to transactions_path, notice: "Transfer created" + redirect_back_or_to transactions_path, notice: t(".success") end private diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1ee9ee95b..d265734c6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -6,7 +6,7 @@ class UsersController < ApplicationController if @user.resend_confirmation_email redirect_to settings_profile_path, notice: t(".success") else - redirect_to settings_profile_path, alert: t("no_pending_change") + redirect_to settings_profile_path, alert: t(".no_pending_change") end end diff --git a/app/controllers/valuations_controller.rb b/app/controllers/valuations_controller.rb index 5a2966167..9dbfdd597 100644 --- a/app/controllers/valuations_controller.rb +++ b/app/controllers/valuations_controller.rb @@ -57,8 +57,8 @@ class ValuationsController < ApplicationController if result.success? respond_to do |format| - format.html { redirect_back_or_to account_path(account), notice: "Account updated" } - format.turbo_stream { stream_redirect_back_or_to(account_path(account), notice: "Account updated") } + format.html { redirect_back_or_to account_path(account), notice: t(".account_updated") } + format.turbo_stream { stream_redirect_back_or_to(account_path(account), notice: t(".account_updated")) } end else @error_message = result.error_message @@ -84,7 +84,7 @@ class ValuationsController < ApplicationController @entry.reload respond_to do |format| - format.html { redirect_back_or_to account_path(@entry.account), notice: "Entry updated" } + format.html { redirect_back_or_to account_path(@entry.account), notice: t(".entry_updated") } format.turbo_stream do render turbo_stream: [ turbo_stream.replace( diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f00d876ea..d15266e03 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -74,7 +74,7 @@ module ApplicationHelper def family_moniker - Current.family&.moniker_label || "Family" + Current.family&.moniker_label || I18n.t("shared.family_moniker.singular") end def family_moniker_downcase @@ -82,7 +82,7 @@ module ApplicationHelper end def family_moniker_plural - Current.family&.moniker_label_plural || "Families" + Current.family&.moniker_label_plural || I18n.t("shared.family_moniker.plural") end def family_moniker_plural_downcase diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb index 2d586dee7..458dae432 100644 --- a/app/helpers/categories_helper.rb +++ b/app/helpers/categories_helper.rb @@ -1,21 +1,21 @@ module CategoriesHelper def transfer_category Category.new \ - name: "Transfer", + name: I18n.t("categories.virtual.transfer"), color: Category::TRANSFER_COLOR, lucide_icon: "arrow-right-left" end def payment_category Category.new \ - name: "Payment", + name: I18n.t("categories.virtual.payment"), color: Category::PAYMENT_COLOR, lucide_icon: "arrow-right" end def trade_category Category.new \ - name: "Trade", + name: I18n.t("categories.virtual.trade"), color: Category::TRADE_COLOR end diff --git a/app/helpers/custom_confirm.rb b/app/helpers/custom_confirm.rb index cdf245853..f2d59a009 100644 --- a/app/helpers/custom_confirm.rb +++ b/app/helpers/custom_confirm.rb @@ -38,14 +38,14 @@ class CustomConfirm end def default_title - "Are you sure?" + I18n.t("shared.custom_confirm.default_title") end def default_body - "This is not reversible." + I18n.t("shared.custom_confirm.default_body") end def default_btn_text - "Confirm" + I18n.t("shared.custom_confirm.default_btn_text") end end diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb index 6cf680452..3f4628720 100644 --- a/app/helpers/imports_helper.rb +++ b/app/helpers/imports_helper.rb @@ -1,31 +1,31 @@ module ImportsHelper def mapping_label(mapping_class) { - "Import::AccountTypeMapping" => "Account Type", - "Import::AccountMapping" => "Account", - "Import::CategoryMapping" => "Category", - "Import::TagMapping" => "Tag" + "Import::AccountTypeMapping" => I18n.t("imports.mapping_labels.account_type"), + "Import::AccountMapping" => I18n.t("imports.mapping_labels.account"), + "Import::CategoryMapping" => I18n.t("imports.mapping_labels.category"), + "Import::TagMapping" => I18n.t("imports.mapping_labels.tag") }.fetch(mapping_class.name) end def import_col_label(key) { - date: "Date", - amount: "Amount", - name: "Name", - currency: "Currency", - category: "Category", - tags: "Tags", - account: "Account", - notes: "Notes", - qty: "Quantity", - ticker: "Ticker", - exchange: "Exchange", - price: "Price", - entity_type: "Type", - category_parent: "Parent category", - category_color: "Color", - category_icon: "Lucide icon" + date: I18n.t("imports.column_labels.date"), + amount: I18n.t("imports.column_labels.amount"), + name: I18n.t("imports.column_labels.name"), + currency: I18n.t("imports.column_labels.currency"), + category: I18n.t("imports.column_labels.category"), + tags: I18n.t("imports.column_labels.tags"), + account: I18n.t("imports.column_labels.account"), + notes: I18n.t("imports.column_labels.notes"), + qty: I18n.t("imports.column_labels.qty"), + ticker: I18n.t("imports.column_labels.ticker"), + exchange: I18n.t("imports.column_labels.exchange"), + price: I18n.t("imports.column_labels.price"), + entity_type: I18n.t("imports.column_labels.entity_type"), + category_parent: I18n.t("imports.column_labels.category_parent"), + category_color: I18n.t("imports.column_labels.category_color"), + category_icon: I18n.t("imports.column_labels.category_icon") }[key] end diff --git a/app/models/api_key.rb b/app/models/api_key.rb index ae24ccec3..4c12b4668 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -112,7 +112,7 @@ class ApiKey < ApplicationRecord def prevent_demo_monitoring_key_destroy! return unless demo_monitoring_key? - errors.add(:base, "Cannot destroy demo monitoring API key") + errors.add(:base, :cannot_destroy_demo_key) throw(:abort) end end diff --git a/app/models/category_import.rb b/app/models/category_import.rb index 15e3a6dd8..1bf4564bb 100644 --- a/app/models/category_import.rb +++ b/app/models/category_import.rb @@ -17,7 +17,7 @@ class CategoryImport < Import parent = ensure_placeholder_category(row.category_parent) if parent && parent == category - errors.add(:base, "Category '#{category.name}' cannot be its own parent") + errors.add(:base, :own_parent, name: category.name) raise ActiveRecord::RecordInvalid.new(self) end @@ -82,7 +82,7 @@ class CategoryImport < Import missing_headers = required_column_keys.map(&:to_s).reject { |key| header_for(key).present? } return if missing_headers.empty? - errors.add(:base, "Missing required columns: #{missing_headers.join(', ')}") + errors.add(:base, :missing_columns, columns: missing_headers.join(", ")) raise ActiveRecord::RecordInvalid.new(self) end diff --git a/app/models/import.rb b/app/models/import.rb index 2f1256ff4..ac3c5d5df 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -418,7 +418,7 @@ class Import < ApplicationRecord end if duplicate_headers.any? - errors.add(:base, "CSV headers normalize to duplicate columns: #{duplicate_headers.map { |headers| headers.join(', ') }.join('; ')}") + errors.add(:base, :duplicate_headers, columns: duplicate_headers.map { |headers| headers.join(", ") }.join("; ")) raise ActiveRecord::RecordInvalid, self end diff --git a/app/models/indexa_capital_item.rb b/app/models/indexa_capital_item.rb index ccea401b4..53bd562a8 100644 --- a/app/models/indexa_capital_item.rb +++ b/app/models/indexa_capital_item.rb @@ -176,6 +176,6 @@ class IndexaCapitalItem < ApplicationRecord def credentials_present_on_create return if credentials_configured? - errors.add(:base, "Either INDEXA_API_TOKEN env var or username/document/password credentials are required") + errors.add(:base, :credentials_required) end end diff --git a/app/models/plaid_account.rb b/app/models/plaid_account.rb index ae2ecfeae..fa5922ae1 100644 --- a/app/models/plaid_account.rb +++ b/app/models/plaid_account.rb @@ -70,6 +70,6 @@ class PlaidAccount < ApplicationRecord # Plaid guarantees at least one of these. This validation is a sanity check for that guarantee. def has_balance return if current_balance.present? || available_balance.present? - errors.add(:base, "Plaid account must have either current or available balance") + errors.add(:base, :no_balance) end end diff --git a/app/models/recurring_transaction.rb b/app/models/recurring_transaction.rb index ffc2c75be..090d31549 100644 --- a/app/models/recurring_transaction.rb +++ b/app/models/recurring_transaction.rb @@ -24,7 +24,7 @@ class RecurringTransaction < ApplicationRecord def merchant_or_name_present if merchant_id.blank? && name.blank? - errors.add(:base, "Either merchant or name must be present") + errors.add(:base, :merchant_or_name_required) end end diff --git a/app/models/rule.rb b/app/models/rule.rb index b53f80f83..d5b89eef0 100644 --- a/app/models/rule.rb +++ b/app/models/rule.rb @@ -140,14 +140,14 @@ class Rule < ApplicationRecord return if new_record? && !actions.empty? if actions.reject(&:marked_for_destruction?).empty? - errors.add(:base, "must have at least one action") + errors.add(:base, :min_actions) end end def no_duplicate_actions action_types = actions.reject(&:marked_for_destruction?).map(&:action_type) - errors.add(:base, "Rule cannot have duplicate actions #{action_types.inspect}") if action_types.uniq.count != action_types.count + errors.add(:base, :duplicate_actions, types: action_types.inspect) if action_types.uniq.count != action_types.count end # Validation: To keep rules simple and easy to understand, we don't allow nested compound conditions. @@ -157,7 +157,7 @@ class Rule < ApplicationRecord conditions.each do |condition| if condition.compound? if condition.sub_conditions.any? { |sub_condition| sub_condition.compound? } - errors.add(:base, "Compound conditions cannot be nested") + errors.add(:base, :nested_conditions) end end end diff --git a/app/models/rule_import.rb b/app/models/rule_import.rb index 2fac4cb21..31cf4025a 100644 --- a/app/models/rule_import.rb +++ b/app/models/rule_import.rb @@ -115,7 +115,7 @@ class RuleImport < Import # Validate resource type unless resource_type == "transaction" - errors.add(:base, "Unsupported resource type: #{resource_type}") + errors.add(:base, :unsupported_resource_type, resource_type: resource_type) raise ActiveRecord::RecordInvalid.new(self) end @@ -124,13 +124,13 @@ class RuleImport < Import conditions_data = parse_json_safely(row.conditions, "conditions") actions_data = parse_json_safely(row.actions, "actions") rescue JSON::ParserError => e - errors.add(:base, "Invalid JSON in conditions or actions: #{e.message}") + errors.add(:base, :invalid_json, message: e.message) raise ActiveRecord::RecordInvalid.new(self) end # Validate we have at least one action if actions_data.empty? - errors.add(:base, "Rule must have at least one action") + errors.add(:base, :min_actions) raise ActiveRecord::RecordInvalid.new(self) end diff --git a/app/models/simplefin_account.rb b/app/models/simplefin_account.rb index bf9571edb..049834667 100644 --- a/app/models/simplefin_account.rb +++ b/app/models/simplefin_account.rb @@ -137,6 +137,6 @@ class SimplefinAccount < ApplicationRecord end def has_balance return if current_balance.present? || available_balance.present? - errors.add(:base, "SimpleFin account must have either current or available balance") + errors.add(:base, :no_balance) end end diff --git a/app/models/sophtron_account.rb b/app/models/sophtron_account.rb index e22769883..8179b7b84 100644 --- a/app/models/sophtron_account.rb +++ b/app/models/sophtron_account.rb @@ -158,7 +158,7 @@ class SophtronAccount < ApplicationRecord end def has_balance return if balance.present? || available_balance.present? - errors.add(:base, "Sophtron account must have either current or available balance") + errors.add(:base, :no_balance) end def first_present(hash, *keys) diff --git a/app/models/sso_provider.rb b/app/models/sso_provider.rb index f9f2ef7c4..b5c9fe25b 100644 --- a/app/models/sso_provider.rb +++ b/app/models/sso_provider.rb @@ -90,7 +90,7 @@ class SsoProvider < ApplicationRecord idp_sso_url = settings&.dig("idp_sso_url") if idp_metadata_url.blank? && idp_sso_url.blank? - errors.add(:settings, "Either IdP Metadata URL or IdP SSO URL is required for SAML providers") + errors.add(:settings, :saml_url_required) end # If using manual config, require certificate @@ -99,17 +99,17 @@ class SsoProvider < ApplicationRecord idp_fingerprint = settings&.dig("idp_cert_fingerprint") if idp_cert.blank? && idp_fingerprint.blank? - errors.add(:settings, "Either IdP Certificate or Certificate Fingerprint is required when not using metadata URL") + errors.add(:settings, :saml_cert_required) end end # Validate URL formats if provided if idp_metadata_url.present? && !valid_url?(idp_metadata_url) - errors.add(:settings, "IdP Metadata URL must be a valid URL") + errors.add(:settings, :metadata_url_invalid) end if idp_sso_url.present? && !valid_url?(idp_sso_url) - errors.add(:settings, "IdP SSO URL must be a valid URL") + errors.add(:settings, :sso_url_invalid) end end diff --git a/app/models/transfer.rb b/app/models/transfer.rb index d2dcbf667..878e899be 100644 --- a/app/models/transfer.rb +++ b/app/models/transfer.rb @@ -107,12 +107,12 @@ class Transfer < ApplicationRecord private def transfer_has_different_accounts return unless inflow_transaction&.entry && outflow_transaction&.entry - errors.add(:base, "Must be from different accounts") if to_account == from_account + errors.add(:base, :different_accounts) if to_account == from_account end def transfer_has_same_family return unless inflow_transaction&.entry && outflow_transaction&.entry - errors.add(:base, "Must be from same family") unless to_account&.family == from_account&.family + errors.add(:base, :same_family) unless to_account&.family == from_account&.family end def transfer_has_opposite_amounts @@ -126,10 +126,10 @@ class Transfer < ApplicationRecord if inflow_entry.currency == outflow_entry.currency # For same currency, amounts must be exactly opposite - errors.add(:base, "Must have opposite amounts") if inflow_amount + outflow_amount != 0 + errors.add(:base, :opposite_amounts) if inflow_amount + outflow_amount != 0 else # For different currencies, just check the signs are opposite - errors.add(:base, "Must have opposite amounts") unless inflow_amount.negative? && outflow_amount.positive? + errors.add(:base, :opposite_amounts) unless inflow_amount.negative? && outflow_amount.positive? end end @@ -138,6 +138,6 @@ class Transfer < ApplicationRecord date_diff = (inflow_transaction.entry.date - outflow_transaction.entry.date).abs max_days = status == "confirmed" ? 30 : 4 - errors.add(:base, "Must be within #{max_days} days") if date_diff > max_days + errors.add(:base, :within_days, count: max_days) if date_diff > max_days end end diff --git a/app/views/accounts/_account.html.erb b/app/views/accounts/_account.html.erb index 59dca6c4e..bcc57a68d 100644 --- a/app/views/accounts/_account.html.erb +++ b/app/views/accounts/_account.html.erb @@ -54,7 +54,7 @@ <% if account.draft? %> <%= render DS::Link.new( - text: "Complete setup", + text: t(".complete_setup"), href: edit_account_path(account, return_to: return_to), variant: :outline, frame: :modal diff --git a/app/views/accounts/new/_container.html.erb b/app/views/accounts/new/_container.html.erb index 387a59f14..6c1c16112 100644 --- a/app/views/accounts/new/_container.html.erb +++ b/app/views/accounts/new/_container.html.erb @@ -29,13 +29,13 @@ diff --git a/app/views/accounts/show/_activity.html.erb b/app/views/accounts/show/_activity.html.erb index a36407a1f..5660695c9 100644 --- a/app/views/accounts/show/_activity.html.erb +++ b/app/views/accounts/show/_activity.html.erb @@ -7,11 +7,11 @@ <% unless @account.linked? %> <% if @account.permission_for(Current.user).in?([ :owner, :full_control ]) %> <%= render DS::Menu.new(variant: "button") do |menu| %> - <% menu.with_button(text: "New", variant: "secondary", icon: "plus") %> + <% menu.with_button(text: t(".new"), variant: "secondary", icon: "plus") %> <% menu.with_item( variant: "link", - text: "New balance", + text: t(".new_balance"), icon: "circle-dollar-sign", href: new_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }) %> @@ -48,7 +48,7 @@ <%= icon("search") %> <%= hidden_field_tag :account_id, @account.id %> <%= form.search_field :search, - placeholder: "Search entries by name", + placeholder: t(".search_placeholder"), value: @q[:search], class: "form-field__input placeholder:text-sm placeholder:text-secondary", "data-auto-submit-form-target": "auto" %> diff --git a/app/views/accounts/show/_header.html.erb b/app/views/accounts/show/_header.html.erb index cafd97811..ddbad5a2b 100644 --- a/app/views/accounts/show/_header.html.erb +++ b/app/views/accounts/show/_header.html.erb @@ -17,7 +17,7 @@ <% end %> <% if account.draft? %> <%= render DS::Link.new( - text: "Complete setup", + text: t(".complete_setup"), href: edit_account_path(account), variant: :outline, size: :sm, diff --git a/app/views/accounts/show/_menu.html.erb b/app/views/accounts/show/_menu.html.erb index cb577369e..de1b77626 100644 --- a/app/views/accounts/show/_menu.html.erb +++ b/app/views/accounts/show/_menu.html.erb @@ -3,9 +3,9 @@ <% permission = account.permission_for(Current.user) %> <%= render DS::Menu.new(testid: "account-menu") do |menu| %> <% if permission.in?([ :owner, :full_control ]) %> - <% menu.with_item(variant: "link", text: "Edit", href: edit_account_path(account), icon: "pencil-line", data: { turbo_frame: :modal }) %> + <% menu.with_item(variant: "link", text: t(".edit"), href: edit_account_path(account), icon: "pencil-line", data: { turbo_frame: :modal }) %> <% end %> - <% menu.with_item(variant: "link", text: "Sharing", href: account_sharing_path(account), icon: "users", data: { turbo_frame: :modal }) %> + <% menu.with_item(variant: "link", text: t(".sharing"), href: account_sharing_path(account), icon: "users", data: { turbo_frame: :modal }) %> <% menu.with_item(variant: "link", text: t(".statements"), href: account_path(account, tab: "statements"), icon: "archive") %> <% if permission.in?([ :owner, :full_control ]) %> @@ -31,7 +31,7 @@ <% if account.owned_by?(Current.user) && !account.linked? %> <% menu.with_item( variant: "button", - text: "Delete account", + text: t(".delete_account"), href: account_path(account), method: :delete, icon: "trash-2", diff --git a/app/views/admin/sso_providers/_form.html.erb b/app/views/admin/sso_providers/_form.html.erb index 5a3527c3e..9845bab1a 100644 --- a/app/views/admin/sso_providers/_form.html.erb +++ b/app/views/admin/sso_providers/_form.html.erb @@ -6,7 +6,7 @@ <%= icon "alert-circle", class: "w-5 h-5 text-destructive mr-2 shrink-0" %>

- <%= pluralize(sso_provider.errors.count, "error") %> prohibited this provider from being saved: + <%= t("admin.sso_providers.form.errors_title", count: sso_provider.errors.count) %>

@@ -282,8 +282,8 @@
- <%= link_to "Cancel", admin_sso_providers_path, class: "px-4 py-2 text-sm font-medium text-secondary hover:text-primary" %> - <%= form.submit sso_provider.persisted? ? "Update Provider" : "Create Provider", + <%= link_to t("admin.sso_providers.form.cancel"), admin_sso_providers_path, class: "px-4 py-2 text-sm font-medium text-secondary hover:text-primary" %> + <%= form.submit sso_provider.persisted? ? t("admin.sso_providers.form.update_provider") : t("admin.sso_providers.form.create_provider"), class: "px-4 py-2 button-bg-primary text-inverse rounded-lg text-sm font-medium hover:button-bg-primary-hover" %>
diff --git a/app/views/admin/sso_providers/index.html.erb b/app/views/admin/sso_providers/index.html.erb index c9ccfc6a1..c799d374d 100644 --- a/app/views/admin/sso_providers/index.html.erb +++ b/app/views/admin/sso_providers/index.html.erb @@ -1,14 +1,14 @@ -<%= content_for :page_title, "SSO Providers" %> +<%= content_for :page_title, t(".page_title") %>

- Manage single sign-on authentication providers for your instance. + <%= t(".description") %> <% unless FeatureFlags.db_sso_providers? %> - Changes require a server restart to take effect. + <%= t(".restart_required") %> <% end %>

- <%= settings_section title: "Configured Providers" do %> + <%= settings_section title: t(".configured_providers") do %> <% if @sso_providers.any? %>
<% @sso_providers.each do |provider| %> @@ -27,20 +27,20 @@
<% if provider.enabled? %> - Enabled + <%= t(".enabled") %> <% else %> - Disabled + <%= t(".disabled") %> <% end %> - <%= link_to edit_admin_sso_provider_path(provider), class: "p-1 text-secondary hover:text-primary", title: "Edit" do %> + <%= link_to edit_admin_sso_provider_path(provider), class: "p-1 text-secondary hover:text-primary", title: t(".edit") do %> <%= icon "pencil", class: "w-4 h-4" %> <% end %> - <%= button_to toggle_admin_sso_provider_path(provider), method: :patch, class: "p-1 text-secondary hover:text-primary", title: provider.enabled? ? "Disable" : "Enable", form: { data: { turbo_confirm: "Are you sure you want to #{provider.enabled? ? 'disable' : 'enable'} this provider?" } } do %> + <%= button_to toggle_admin_sso_provider_path(provider), method: :patch, class: "p-1 text-secondary hover:text-primary", title: provider.enabled? ? t(".disable") : t(".enable"), form: { data: { turbo_confirm: provider.enabled? ? t("admin.sso_providers.toggle.confirm_disable") : t("admin.sso_providers.toggle.confirm_enable") } } do %> <%= icon provider.enabled? ? "toggle-right" : "toggle-left", class: "w-4 h-4" %> <% end %> - <%= button_to admin_sso_provider_path(provider), method: :delete, class: "p-1 text-destructive hover:text-destructive", title: "Delete", form: { data: { turbo_confirm: "Are you sure you want to delete this provider? This action cannot be undone." } } do %> + <%= button_to admin_sso_provider_path(provider), method: :delete, class: "p-1 text-destructive hover:text-destructive", title: t(".delete"), form: { data: { turbo_confirm: t("admin.sso_providers.destroy.confirm") } } do %> <%= icon "trash-2", class: "w-4 h-4" %> <% end %>
@@ -50,14 +50,14 @@ <% else %>
<%= icon "key", class: "w-12 h-12 mx-auto text-secondary mb-3" %> -

No SSO providers configured yet.

+

<%= t(".no_providers_message") %>

<% end %>
<%= link_to new_admin_sso_provider_path, class: "inline-flex items-center gap-2 text-sm font-medium text-primary hover:text-secondary" do %> <%= icon "plus", class: "w-4 h-4" %> - Add Provider + <%= t(".add_provider") %> <% end %>
<% end %> @@ -100,26 +100,25 @@ <% end %> <% end %> - <%= settings_section title: "Configuration Mode", collapsible: true, open: false do %> + <%= settings_section title: t(".configuration_mode"), collapsible: true, open: false do %>
-

Database-backed providers

-

Load providers from database instead of YAML config

+

<%= t(".db_backed_providers") %>

+

<%= t(".db_backed_providers_description") %>

<% if FeatureFlags.db_sso_providers? %> - Enabled + <%= t(".enabled") %> <% else %> - Disabled + <%= t(".disabled") %> <% end %>

- Set AUTH_PROVIDERS_SOURCE=db to enable database-backed providers. - This allows changes without server restarts. + <%= t(".db_backed_providers_help_html") %>

<% end %> diff --git a/app/views/assistant_messages/_assistant_message.html.erb b/app/views/assistant_messages/_assistant_message.html.erb index 9768ee0d0..6b00a3c2d 100644 --- a/app/views/assistant_messages/_assistant_message.html.erb +++ b/app/views/assistant_messages/_assistant_message.html.erb @@ -9,7 +9,7 @@ <% elsif assistant_message.reasoning? %>
-

Assistant reasoning

+

<%= t(".assistant_reasoning") %>

<%= icon("chevron-down", class: "group-open:transform group-open:rotate-180") %>
diff --git a/app/views/assistant_messages/_tool_calls.html.erb b/app/views/assistant_messages/_tool_calls.html.erb index 59c149225..d2adace6a 100644 --- a/app/views/assistant_messages/_tool_calls.html.erb +++ b/app/views/assistant_messages/_tool_calls.html.erb @@ -3,15 +3,15 @@
<%= icon("chevron-right", class: "group-open:transform group-open:rotate-90") %> -

Tool Calls

+

<%= t(".tool_calls") %>

<% message.tool_calls.each do |tool_call| %>
-

Function:

+

<%= t(".function") %>

<%= tool_call.function_name %>

-

Arguments:

+

<%= t(".arguments") %>

<%= tool_call.function_arguments %>
<% end %> diff --git a/app/views/budget_categories/_confirm_button.html.erb b/app/views/budget_categories/_confirm_button.html.erb index a9d983317..730ebf1e3 100644 --- a/app/views/budget_categories/_confirm_button.html.erb +++ b/app/views/budget_categories/_confirm_button.html.erb @@ -1,6 +1,6 @@
<%= render DS::Button.new( - text: "Confirm", + text: t(".confirm"), variant: "primary", full_width: true, href: budget_path(budget), diff --git a/app/views/budget_categories/_no_categories.html.erb b/app/views/budget_categories/_no_categories.html.erb index 44d8915b1..ed949d2d9 100644 --- a/app/views/budget_categories/_no_categories.html.erb +++ b/app/views/budget_categories/_no_categories.html.erb @@ -1,18 +1,18 @@
-

Oops!

+

<%= t(".oops") %>

- You have not created or assigned any expense categories to your transactions yet. + <%= t(".no_categories_message") %>

<%= render DS::Button.new( - text: "Use defaults (recommended)", + text: t(".use_defaults"), href: bootstrap_categories_path, ) %> <%= render DS::Link.new( - text: "New category", + text: t(".new_category"), variant: "outline", icon: "plus", href: new_category_path, diff --git a/app/views/budget_categories/index.html.erb b/app/views/budget_categories/index.html.erb index d5ded43d7..049d5ede8 100644 --- a/app/views/budget_categories/index.html.erb +++ b/app/views/budget_categories/index.html.erb @@ -8,9 +8,9 @@
-

Edit your category budgets

+

<%= t(".title") %>

- Adjust category budgets to set spending limits. Unallocated funds will be automatically assigned as uncategorized. + <%= t(".description") %>

diff --git a/app/views/budget_categories/show.html.erb b/app/views/budget_categories/show.html.erb index 154a7958f..6d8d64ed9 100644 --- a/app/views/budget_categories/show.html.erb +++ b/app/views/budget_categories/show.html.erb @@ -1,7 +1,7 @@ <%= render DS::Dialog.new(variant: :drawer) do |dialog| %> <% dialog.with_header do %>
-

Category

+

<%= t(".category") %>

<%= @budget_category.name %>

@@ -26,12 +26,12 @@ <% end %> <% dialog.with_body do %> - <% dialog.with_section(title: "Overview", open: true) do %> + <% dialog.with_section(title: t(".overview"), open: true) do %>
- <%= @budget_category.budget.start_date.strftime("%b %Y") %> spending + <%= t(".spending", date: @budget_category.budget.start_date.strftime("%b %Y")) %>
<%= format_money @budget_category.actual_spending_money %> @@ -40,30 +40,30 @@ <% if @budget_category.budget.initialized? %>
-
Status
+
<%= t(".status") %>
<% if @budget_category.available_to_spend.negative? %>
<%= icon "alert-circle", size: "sm", color: "destructive" %> <%= format_money @budget_category.available_to_spend_money.abs %> - overspent + <%= t(".overspent") %>
<% elsif @budget_category.available_to_spend.zero? %>
<%= icon "x-circle", size: "sm", color: "warning" %> <%= format_money @budget_category.available_to_spend_money %> - left + <%= t(".left") %>
<% else %>
<%= icon "check-circle", size: "sm", color: "success" %> <%= format_money @budget_category.available_to_spend_money %> - left + <%= t(".left") %>
<% end %>
-
Budgeted
+
<%= t(".budgeted") %>
<%= format_money @budget_category.budgeted_spending_money %>
@@ -71,14 +71,14 @@ <% end %>
-
Monthly average spending
+
<%= t(".monthly_average_spending") %>
<%= @budget_category.avg_monthly_expense_money.format %>
-
Monthly median spending
+
<%= t(".monthly_median_spending") %>
<%= @budget_category.median_monthly_expense_money.format %>
@@ -87,7 +87,7 @@
<% end %> - <% dialog.with_section(title: "Recent Transactions", open: true) do %> + <% dialog.with_section(title: t(".recent_transactions"), open: true) do %>
<% if @recent_transactions.any? %> @@ -120,7 +120,7 @@ <%= render DS::Link.new( - text: "View all category transactions", + text: t(".view_all_transactions"), variant: "outline", full_width: true, href: transactions_path(q: { @@ -132,7 +132,7 @@ ) %> <% else %>

- No transactions found for this budget period. + <%= t(".no_transactions") %>

<% end %>
diff --git a/app/views/budgets/_actuals_summary.html.erb b/app/views/budgets/_actuals_summary.html.erb index fe9509d9a..524fb0006 100644 --- a/app/views/budgets/_actuals_summary.html.erb +++ b/app/views/budgets/_actuals_summary.html.erb @@ -2,7 +2,7 @@
-

Income

+

<%= t(".income") %>

<%= budget.actual_income_money.format %> @@ -30,7 +30,7 @@
-

Expenses

+

<%= t(".expenses") %>

<%= budget.actual_spending_money.format %> diff --git a/app/views/budgets/_budget_donut.html.erb b/app/views/budgets/_budget_donut.html.erb index 8e0520b37..15c88046f 100644 --- a/app/views/budgets/_budget_donut.html.erb +++ b/app/views/budgets/_budget_donut.html.erb @@ -5,7 +5,7 @@
<% if budget.initialized? %>
- Spent + <%= t(".spent") %>
"> @@ -13,7 +13,7 @@
<%= render DS::Link.new( - text: "of #{budget.budgeted_spending_money.format}", + text: t(".of_budget", amount: budget.budgeted_spending_money.format), variant: "secondary", icon: "pencil", icon_position: "right", @@ -26,7 +26,7 @@
<%= render DS::Link.new( - text: "New budget", + text: t(".new_budget"), size: "sm", icon: "plus", href: edit_budget_path(budget) @@ -47,7 +47,7 @@

<%= render DS::Link.new( - text: "of #{bc.budgeted_spending_money.format(precision: 0)}", + text: t(".of_budget", amount: bc.budgeted_spending_money.format(precision: 0)), variant: "secondary", icon: "pencil", icon_position: "right", @@ -59,7 +59,7 @@ <% end %>
diff --git a/app/views/enable_banking_items/select_existing_account.html.erb b/app/views/enable_banking_items/select_existing_account.html.erb index 9303aa252..9d77b7071 100644 --- a/app/views/enable_banking_items/select_existing_account.html.erb +++ b/app/views/enable_banking_items/select_existing_account.html.erb @@ -1,15 +1,15 @@ <%# Modal: Link an existing manual account to a Enable Banking account %> <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Link Enable Banking account") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %> <% if @available_enable_banking_accounts.blank? %>
-

All Enable Banking accounts appear to be linked already.

+

<%= t(".all_linked") %>

    -
  • If you just connected or synced, try again after the sync completes.
  • -
  • To link a different account, first unlink it from the account’s actions menu.
  • +
  • <%= t(".try_after_sync") %>
  • +
  • <%= t(".unlink_to_move") %>
<% else %> @@ -22,7 +22,7 @@
<%= eba.name.presence || eba.account_id %> - <%= eba.currency %> • Balance: <%= number_to_currency((eba.current_balance || 0), unit: eba.currency) %> + <%= eba.currency %> • <%= t(".balance") %>: <%= number_to_currency((eba.current_balance || 0), unit: eba.currency) %>
@@ -30,8 +30,8 @@
- <%= render DS::Button.new(text: "Link", variant: :primary, icon: "link-2", type: :submit) %> - <%= render DS::Link.new(text: "Cancel", variant: :secondary, href: accounts_path, data: { turbo_frame: "_top" }) %> + <%= render DS::Button.new(text: t(".link"), variant: :primary, icon: "link-2", type: :submit) %> + <%= render DS::Link.new(text: t(".cancel"), variant: :secondary, href: accounts_path, data: { turbo_frame: "_top" }) %>
<% end %> <% end %> diff --git a/app/views/enable_banking_items/setup_accounts.html.erb b/app/views/enable_banking_items/setup_accounts.html.erb index 5da8e3016..97060d2c2 100644 --- a/app/views/enable_banking_items/setup_accounts.html.erb +++ b/app/views/enable_banking_items/setup_accounts.html.erb @@ -1,8 +1,8 @@ <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Set Up Your Enable Banking Accounts") do %> + <% dialog.with_header(title: t(".title")) do %>
<%= icon "building-2", class: "text-primary" %> - Choose the correct account types for your imported accounts + <%= t(".header_subtitle") %>
<% end %> @@ -13,7 +13,7 @@ data: { controller: "loading-button", action: "submit->loading-button#showLoading", - loading_button_loading_text_value: "Creating Accounts...", + loading_button_loading_text_value: t(".creating_accounts"), turbo_frame: "_top" }, class: "space-y-6" do |form| %> @@ -24,7 +24,7 @@ <%= icon "info", size: "sm", class: "text-primary mt-0.5 flex-shrink-0" %>

- Choose the correct account type for each Enable Banking account: + <%= t(".choose_account_type") %>

    <% @account_type_options.reject { |_, type| type == "skip" }.each do |label, _| %> @@ -53,15 +53,15 @@ <%= icon "calendar", size: "sm", class: "text-primary mt-0.5 flex-shrink-0" %>

    - Historical Data Range: + <%= t(".historical_data_range") %>

    <%= form.date_field :sync_start_date, - label: "Start syncing transactions from:", + label: t(".sync_start_date_label"), value: @enable_banking_item.sync_start_date || 3.months.ago.to_date, min: 2.years.ago.to_date, max: Date.current, class: "w-full max-w-xs rounded-md border border-primary px-3 py-2 text-sm bg-container-inset text-primary", - help_text: "Select how far back you want to sync transaction history. Maximum 2 years of history available." %> + help_text: t(".sync_start_date_help") %>
@@ -81,7 +81,7 @@

<%= enable_banking_account.account_type_display %>

<% end %> <% if enable_banking_account.current_balance.present? %> -

Balance: <%= number_to_currency(enable_banking_account.current_balance, unit: enable_banking_account.currency) %>

+

<%= t(".balance") %>: <%= number_to_currency(enable_banking_account.current_balance, unit: enable_banking_account.currency) %>

<% end %>
@@ -92,7 +92,7 @@ data-account-type-selector-account-id-value="<%= enable_banking_account.id %>" data-account-type-selector-suggested-subtype-value="<%= enable_banking_account.suggested_subtype %>">
- <%= label_tag "account_types[#{enable_banking_account.id}]", "Account Type:", + <%= label_tag "account_types[#{enable_banking_account.id}]", t(".account_type_label"), class: "block text-sm font-medium text-primary mb-2" %> <%= select_tag "account_types[#{enable_banking_account.id}]", options_for_select(@account_type_options, enable_banking_account.suggested_account_type || "skip"), @@ -115,7 +115,7 @@
<%= render DS::Button.new( - text: "Create Accounts", + text: t(".create_accounts"), variant: "primary", icon: "plus", type: "submit", @@ -123,7 +123,7 @@ data: { loading_button_target: "button" } ) %> <%= render DS::Link.new( - text: "Cancel", + text: t(".cancel"), variant: "secondary", href: accounts_path ) %> diff --git a/app/views/family_exports/new.html.erb b/app/views/family_exports/new.html.erb index d6942158b..9433eee2f 100644 --- a/app/views/family_exports/new.html.erb +++ b/app/views/family_exports/new.html.erb @@ -1,40 +1,40 @@ <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Export your data", subtitle: "Download all your financial data") %> + <% dialog.with_header(title: t(".dialog_title"), subtitle: t(".dialog_subtitle")) %> <% dialog.with_body do %>
-

What's included:

+

<%= t(".whats_included") %>

  • <%= icon "check", class: "shrink-0 mt-0.5 text-positive" %> - All accounts and balances + <%= t(".accounts_and_balances") %>
  • <%= icon "check", class: "shrink-0 mt-0.5 text-positive" %> - Transaction history + <%= t(".transaction_history") %>
  • <%= icon "check", class: "shrink-0 mt-0.5 text-positive" %> - Investment trades + <%= t(".investment_trades") %>
  • <%= icon "check", class: "shrink-0 mt-0.5 text-positive" %> - Categories, tags and rules + <%= t(".categories_tags_rules") %>

- Note: This export includes all of your data, but only some of the data can be imported back via the CSV import feature. We support account, transaction (with category and tags), and trade imports. Other account data cannot be imported and is for your records only. + <%= t(".note_label") %>: <%= t(".note_description") %>

<%= form_with url: family_exports_path, method: :post, class: "space-y-4" do |form| %>
- <%= link_to "Cancel", "#", class: "flex-1 text-center px-4 py-2 text-primary bg-surface border border-primary rounded-lg hover:bg-surface-hover", data: { action: "click->modal#close" } %> - <%= form.submit "Export data", class: "flex-1 bg-inverse text-inverse rounded-lg px-4 py-2 cursor-pointer hover:bg-inverse-hover" %> + <%= link_to t(".cancel"), "#", class: "flex-1 text-center px-4 py-2 text-primary bg-surface border border-primary rounded-lg hover:bg-surface-hover", data: { action: "click->modal#close" } %> + <%= form.submit t(".export_data"), class: "flex-1 bg-inverse text-inverse rounded-lg px-4 py-2 cursor-pointer hover:bg-inverse-hover" %>
<% end %>
diff --git a/app/views/family_merchants/_family_merchant.html.erb b/app/views/family_merchants/_family_merchant.html.erb index 8e3306df7..c0df9dfec 100644 --- a/app/views/family_merchants/_family_merchant.html.erb +++ b/app/views/family_merchants/_family_merchant.html.erb @@ -18,10 +18,10 @@ <%= render DS::Menu.new do |menu| %> - <% menu.with_item(variant: "link", text: "Edit", href: edit_family_merchant_path(family_merchant), icon: "pencil", data: { turbo_frame: "modal" }) %> + <% menu.with_item(variant: "link", text: t(".edit"), href: edit_family_merchant_path(family_merchant), icon: "pencil", data: { turbo_frame: "modal" }) %> <% menu.with_item( variant: "button", - text: "Delete", + text: t(".delete"), href: family_merchant_path(family_merchant), icon: "trash-2", method: :delete, diff --git a/app/views/holdings/_cost_basis_cell.html.erb b/app/views/holdings/_cost_basis_cell.html.erb index 8d595baee..0b82ca299 100644 --- a/app/views/holdings/_cost_basis_cell.html.erb +++ b/app/views/holdings/_cost_basis_cell.html.erb @@ -30,7 +30,7 @@ <% else %>
<%= icon "pencil", size: "xs" %> - Set + <%= t(".set") %>
<% end %> <% end %> diff --git a/app/views/ibkr_items/select_existing_account.html.erb b/app/views/ibkr_items/select_existing_account.html.erb index 3598da843..d1d27663b 100644 --- a/app/views/ibkr_items/select_existing_account.html.erb +++ b/app/views/ibkr_items/select_existing_account.html.erb @@ -1,14 +1,14 @@ <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Link Interactive Brokers account") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %> <% if @available_ibkr_accounts.blank? %>
-

No unlinked Interactive Brokers accounts are available yet.

+

<%= t(".no_accounts_available") %>

    -
  • Run a sync from Settings > Providers after updating your Flex query.
  • -
  • Wait for the account discovery sync to finish.
  • +
  • <%= t(".run_sync_hint") %>
  • +
  • <%= t(".wait_for_sync") %>
<% else %> @@ -21,7 +21,7 @@
<%= ibkr_account.name.presence || ibkr_account.ibkr_account_id %> - <%= ibkr_account.currency %> • Balance: <%= number_to_currency((ibkr_account.current_balance || 0), unit: Money::Currency.new(ibkr_account.currency || "USD").symbol) %> + <%= ibkr_account.currency %> • <%= t(".balance") %>: <%= number_to_currency((ibkr_account.current_balance || 0), unit: Money::Currency.new(ibkr_account.currency || "USD").symbol) %>
@@ -29,8 +29,8 @@
- <%= render DS::Button.new(text: "Link", variant: :primary, icon: "link-2", type: :submit) %> - <%= render DS::Link.new(text: "Cancel", variant: :secondary, href: accounts_path, data: { turbo_frame: "_top" }) %> + <%= render DS::Button.new(text: t(".link"), variant: :primary, icon: "link-2", type: :submit) %> + <%= render DS::Link.new(text: t(".cancel"), variant: :secondary, href: accounts_path, data: { turbo_frame: "_top" }) %>
<% end %> <% end %> diff --git a/app/views/impersonation_sessions/_super_admin_bar.html.erb b/app/views/impersonation_sessions/_super_admin_bar.html.erb index a09a8250f..718471b67 100644 --- a/app/views/impersonation_sessions/_super_admin_bar.html.erb +++ b/app/views/impersonation_sessions/_super_admin_bar.html.erb @@ -1,20 +1,20 @@
<%= icon "alert-triangle", size: "lg", color: "current", class: "mr-2" %> - Super Admin + <%= t(".super_admin") %>
- <%= link_to "Jobs", sidekiq_web_url, class: "text-white underline hover:text-gray-100" %> + <%= link_to t(".jobs"), sidekiq_web_url, class: "text-white underline hover:text-gray-100" %>
<% if Current.session.active_impersonator_session.present? %>
- Impersonating: <%= Current.impersonated_user.email %> + <%= t(".impersonating") %>: <%= Current.impersonated_user.email %>
- <%= button_to "Leave", leave_impersonation_sessions_path, method: :delete, class: "items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> - <%= button_to "Terminate", complete_impersonation_session_path(Current.session.active_impersonator_session), method: :put, class: "items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> + <%= button_to t(".leave"), leave_impersonation_sessions_path, method: :delete, class: "items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> + <%= button_to t(".terminate"), complete_impersonation_session_path(Current.session.active_impersonator_session), method: :put, class: "items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %>
<% else %> <% if Current.true_user.impersonator_support_sessions.in_progress.any? %> @@ -23,16 +23,16 @@ Current.true_user.impersonator_support_sessions.in_progress.map { |session| ["#{session.impersonated.email} (#{session.status})", session.id] }, - { prompt: "Join a session" }, + { prompt: t(".join_a_session") }, { class: "rounded-md text-sm border-0 focus:ring-0 ring-0 text-black font-mono" } %> - <%= f.submit "Join", + <%= f.submit t(".join"), class: "inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> <% end %> <% end %> <%= form_with model: ImpersonationSession.new, class: "flex items-center space-x-2" do |f| %> - <%= f.text_field :impersonated_id, class: "rounded-md text-sm border-0 focus:ring-0 ring-0 text-black font-mono w-96", placeholder: "UUID", autocomplete: "off" %> - <%= f.submit "Request Impersonation", class: "inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> + <%= f.text_field :impersonated_id, class: "rounded-md text-sm border-0 focus:ring-0 ring-0 text-black font-mono w-96", placeholder: t(".uuid_placeholder"), autocomplete: "off" %> + <%= f.submit t(".request_impersonation"), class: "inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500" %> <% end %> <% end %>
diff --git a/app/views/import/cleans/show.html.erb b/app/views/import/cleans/show.html.erb index 14941acf8..834b9741a 100644 --- a/app/views/import/cleans/show.html.erb +++ b/app/views/import/cleans/show.html.erb @@ -14,11 +14,11 @@
<%= icon "check-circle", size: "sm", color: "success" %> -

Your data has been cleaned

+

<%= t(".data_cleaned") %>

<%= render DS::Link.new( - text: "Next step", + text: t(".next_step"), variant: "primary", href: @import.is_a?(PdfImport) ? import_path(@import) : import_confirm_path(@import), frame: :_top, @@ -35,8 +35,8 @@
- <%= link_to "All rows", import_clean_path(@import, per_page: params[:per_page], view: "all"), class: "p-2 rounded-lg flex-1 md:flex-auto text-center #{params[:view] != 'errors' ? 'bg-container' : ''}" %> - <%= link_to "Error rows", import_clean_path(@import, per_page: params[:per_page], view: "errors"), class: "p-2 rounded-lg flex-1 md:flex-auto text-center #{params[:view] == 'errors' ? 'bg-container' : ''}" %> + <%= link_to t(".all_rows"), import_clean_path(@import, per_page: params[:per_page], view: "all"), class: "p-2 rounded-lg flex-1 md:flex-auto text-center #{params[:view] != 'errors' ? 'bg-container' : ''}" %> + <%= link_to t(".error_rows"), import_clean_path(@import, per_page: params[:per_page], view: "errors"), class: "p-2 rounded-lg flex-1 md:flex-auto text-center #{params[:view] == 'errors' ? 'bg-container' : ''}" %>
diff --git a/app/views/import/configurations/_account_import.html.erb b/app/views/import/configurations/_account_import.html.erb index a8c4ec010..2b50e4cb7 100644 --- a/app/views/import/configurations/_account_import.html.erb +++ b/app/views/import/configurations/_account_import.html.erb @@ -1,18 +1,18 @@ <%# locals: (import:) %> <%= styled_form_with model: @import, url: import_configuration_path(@import), scope: :import, method: :patch, class: "space-y-4" do |form| %> - <%= form.select :entity_type_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Entity Type" } %> - <%= form.select :name_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Name" }, required: true %> - <%= form.select :amount_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Balance" }, required: true %> - <%= form.select :currency_col_label, import.csv_headers, { include_blank: "Default", label: "Currency" } %> + <%= form.select :entity_type_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".entity_type") } %> + <%= form.select :name_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".name") }, required: true %> + <%= form.select :amount_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".balance") }, required: true %> + <%= form.select :currency_col_label, import.csv_headers, { include_blank: t(".default"), label: t(".currency") } %>
- <%= form.select :date_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Balance Date" } %> + <%= form.select :date_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".balance_date") } %> <%= form.select :date_format, Family::DATE_FORMATS, - { label: "Date Format", prompt: "Select format" }, + { label: t(".date_format"), prompt: t(".select_format") }, required: @import.date_col_label.present? %>
- <%= form.submit "Apply configuration", disabled: import.complete? %> + <%= form.submit t(".apply_configuration"), disabled: import.complete? %> <% end %> diff --git a/app/views/import/configurations/_trade_import.html.erb b/app/views/import/configurations/_trade_import.html.erb index b9ac4685c..4ac559487 100644 --- a/app/views/import/configurations/_trade_import.html.erb +++ b/app/views/import/configurations/_trade_import.html.erb @@ -3,38 +3,38 @@ <%= styled_form_with model: @import, url: import_configuration_path(@import), scope: :import, method: :patch, class: "space-y-4" do |form| %>
- <%= form.select :date_col_label, import.csv_headers, { include_blank: "Select column", label: "Date" }, required: true %> + <%= form.select :date_col_label, import.csv_headers, { include_blank: t(".select_column"), label: t(".date_label") }, required: true %> <%= form.select :date_format, Family::DATE_FORMATS, { label: t(".date_format_label")}, label: true, required: true %>
- <%= form.select :qty_col_label, import.csv_headers, { include_blank: "Select column", label: "Quantity" }, required: true %> - <%= form.select :signage_convention, [["Buys are positive qty", "inflows_positive"], ["Buys are negative qty", "inflows_negative"]], label: true, required: true %> + <%= form.select :qty_col_label, import.csv_headers, { include_blank: t(".select_column"), label: t(".quantity_label") }, required: true %> + <%= form.select :signage_convention, [[t(".buys_are_positive"), "inflows_positive"], [t(".buys_are_negative"), "inflows_negative"]], label: true, required: true %>
- <%= form.select :currency_col_label, import.csv_headers, { include_blank: "Default", label: "Currency" } %> - <%= form.select :number_format, Import::NUMBER_FORMATS.keys, { label: "Format", prompt: "Select format" }, required: true %> + <%= form.select :currency_col_label, import.csv_headers, { include_blank: t(".default"), label: t(".currency_label") } %> + <%= form.select :number_format, Import::NUMBER_FORMATS.keys, { label: t(".format_label"), prompt: t(".select_format") }, required: true %>
- <%= form.select :ticker_col_label, import.csv_headers, { include_blank: "Select column", label: "Ticker" }, required: true %> - <%= form.select :exchange_operating_mic_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Stock exchange code" } %> - <%= form.select :price_col_label, import.csv_headers, { include_blank: "Select column", label: "Price" }, required: true %> + <%= form.select :ticker_col_label, import.csv_headers, { include_blank: t(".select_column"), label: t(".ticker_label") }, required: true %> + <%= form.select :exchange_operating_mic_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".stock_exchange_code_label") } %> + <%= form.select :price_col_label, import.csv_headers, { include_blank: t(".select_column"), label: t(".price_label") }, required: true %> <% unless import.account.present? %> - <%= form.select :account_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Account" } %> + <%= form.select :account_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".account_label") } %> <% end %> - <%= form.select :name_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Name" } %> + <%= form.select :name_col_label, import.csv_headers, { include_blank: t(".leave_empty"), label: t(".name_label") } %> <% unless Security.providers.any? %>

- Note: The security prices provider is not configured. Your trade imports will work, but Sure will not backfill price history. Please go to your settings to configure this. + <%= t(".note_label") %>: <%= t(".no_security_provider_warning") %>

<% end %>
- <%= form.submit "Apply configuration", disabled: import.complete? %> + <%= form.submit t(".apply_configuration"), disabled: import.complete? %> <% end %> diff --git a/app/views/import/configurations/_transaction_import.html.erb b/app/views/import/configurations/_transaction_import.html.erb index e0466d550..e6b927101 100644 --- a/app/views/import/configurations/_transaction_import.html.erb +++ b/app/views/import/configurations/_transaction_import.html.erb @@ -19,11 +19,11 @@
<%= form.select :date_col_label, import.csv_headers, - { label: "Date", prompt: "Select column" }, + { label: t(".date_label"), prompt: t(".select_column") }, required: true %> <%= form.select :date_format, Family::DATE_FORMATS, - { label: t(".date_format_label"), prompt: "Select format" }, + { label: t(".date_format_label"), prompt: t(".select_format") }, required: true %>
@@ -31,21 +31,21 @@
<%= form.select :amount_col_label, import.csv_headers, - { label: "Amount", container_class: "w-2/5", prompt: "Select column" }, + { label: t(".amount_label"), container_class: "w-2/5", prompt: t(".select_column") }, required: true %> <%= form.select :currency_col_label, import.csv_headers, - { include_blank: "Default", label: "Currency", container_class: "w-1/5" } %> + { include_blank: t(".default"), label: t(".currency_label"), container_class: "w-1/5" } %> <%= form.select :number_format, Import::NUMBER_FORMATS.keys, - { label: "Format", prompt: "Select format", container_class: "w-2/5" }, + { label: t(".format_label"), prompt: t(".select_format"), container_class: "w-2/5" }, required: true %>
<%# Amount Type Strategy %> <%= form.select :amount_type_strategy, Import::AMOUNT_TYPE_STRATEGIES.map { |strategy| [strategy.humanize, strategy] }, - { label: "Amount type strategy", prompt: "Select strategy" }, + { label: t(".amount_type_strategy_label"), prompt: t(".select_strategy") }, required: true, data: { action: "import#handleAmountTypeStrategyChange", @@ -58,8 +58,8 @@
<%= form.select :signage_convention, - [["Incomes are positive", "inflows_positive"], ["Incomes are negative", "inflows_negative"]], - { label: "Amount type", prompt: "Select convention" }, + [[t(".incomes_are_positive"), "inflows_positive"], [t(".incomes_are_negative"), "inflows_negative"]], + { label: t(".amount_type_label"), prompt: t(".select_convention") }, required: @import.amount_type_strategy == "signed_amount" %>
<% end %> @@ -70,32 +70,32 @@
- Set + <%= t(".set") %> <%= form.select :entity_type_col_label, import.csv_headers, - { prompt: "Select column", container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, + { prompt: t(".select_column"), container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, required: @import.amount_type_strategy == "custom_column", data: { action: "import#handleAmountTypeChange" } %> - as amount type column + <%= t(".as_amount_type_column") %>
" data-import-target="amountTypeValue"> - Set + <%= t(".set") %> <%= form.select :amount_type_identifier_value, @import.selectable_amount_type_values, - { prompt: "Select value", container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, + { prompt: t(".select_value"), container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, required: @import.amount_type_strategy == "custom_column", data: { action: "import#handleAmountTypeIdentifierChange" } %> - as identifier value + <%= t(".as_identifier_value") %>
" data-import-target="amountTypeInflowValue"> - Treat "<%= @import.amount_type_identifier_value %>" as + <%= t(".treat_as_html", value: @import.amount_type_identifier_value) %> <%= form.select :amount_type_inflow_value, - [["Income (inflow)", "inflows_positive"], ["Expense (outflow)", "inflows_negative"]], - { prompt: "Select type", container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, + [[t(".income_inflow"), "inflows_positive"], [t(".expense_outflow"), "inflows_negative"]], + { prompt: t(".select_type"), container_class: "w-48 px-3 py-1.5 border border-secondary rounded-md" }, required: @import.amount_type_strategy == "custom_column" %>
@@ -105,21 +105,21 @@ <% unless import.account.present? %> <%= form.select :account_col_label, import.csv_headers, - { include_blank: "Leave empty", label: "Account" } %> + { include_blank: t(".leave_empty"), label: t(".account_label") } %> <% end %> <%= form.select :name_col_label, import.csv_headers, - { include_blank: "Leave empty", label: "Name" } %> + { include_blank: t(".leave_empty"), label: t(".name_label") } %> <%= form.select :category_col_label, import.csv_headers, - { include_blank: "Leave empty", label: "Category" } %> + { include_blank: t(".leave_empty"), label: t(".category_label") } %> <%= form.select :tags_col_label, import.csv_headers, - { include_blank: "Leave empty", label: "Tags" } %> + { include_blank: t(".leave_empty"), label: t(".tags_label") } %> <%= form.select :notes_col_label, import.csv_headers, - { include_blank: "Leave empty", label: "Notes" } %> + { include_blank: t(".leave_empty"), label: t(".notes_label") } %> - <%= form.submit "Apply configuration", disabled: import.complete? %> + <%= form.submit t(".apply_configuration"), disabled: import.complete? %> <% end %> diff --git a/app/views/import/confirms/_mappings.html.erb b/app/views/import/confirms/_mappings.html.erb index 2bb31ee8f..2cfb6ab46 100644 --- a/app/views/import/confirms/_mappings.html.erb +++ b/app/views/import/confirms/_mappings.html.erb @@ -12,7 +12,7 @@ <%= tag.p t(".no_accounts"), class: "text-sm" %> <%= render DS::Link.new( - text: "Create account", + text: t(".create_account"), variant: "primary", href: new_account_path(return_to: import_confirm_path(import)), frame: :modal @@ -60,7 +60,7 @@
<%= render DS::Link.new( - text: "Next", + text: t(".next"), variant: "primary", href: is_last_step ? import_path(import) : url_for(step: step_idx + 2), icon: "arrow-right", diff --git a/app/views/import/uploads/show.html.erb b/app/views/import/uploads/show.html.erb index cc9a94136..56a5e320b 100644 --- a/app/views/import/uploads/show.html.erb +++ b/app/views/import/uploads/show.html.erb @@ -93,7 +93,7 @@ <%# ── Standard CSV upload ── %>
- <%= render "imports/drag_drop_overlay", title: "Drop CSV to upload", subtitle: "Your file will be uploaded automatically" %> + <%= render "imports/drag_drop_overlay", title: t(".drop_csv_title"), subtitle: t(".drop_csv_subtitle") %>
@@ -103,8 +103,8 @@ <%= render DS::Tabs.new(active_tab: params[:tab] || "csv-upload", url_param_key: "tab", testid: "import-tabs") do |tabs| %> <% tabs.with_nav do |nav| %> - <% nav.with_btn(id: "csv-upload", label: "Upload CSV") %> - <% nav.with_btn(id: "csv-paste", label: "Copy & Paste") %> + <% nav.with_btn(id: "csv-upload", label: t(".upload_csv_tab")) %> + <% nav.with_btn(id: "csv-paste", label: t(".copy_paste_tab")) %> <% end %> <% tabs.with_panel(tab_id: "csv-upload") do %> @@ -112,7 +112,7 @@ <%= form.select :col_sep, Import::SEPARATORS, label: true %> <% if @import.type == "TransactionImport" || @import.type == "TradeImport" %> - <%= form.select :account_id, @import.family.accounts.visible.alphabetically.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %> + <%= form.select :account_id, @import.family.accounts.visible.alphabetically.pluck(:name, :id), { label: t(".account_optional_label"), include_blank: t(".multi_account_import"), selected: @import.account_id } %> <% end %>
- <%= form.submit "Upload CSV", disabled: @import.complete? %> + <%= form.submit t(".upload_csv_button"), disabled: @import.complete? %> <% end %> <% end %> @@ -144,16 +144,16 @@ <%= form.select :col_sep, Import::SEPARATORS, label: true %> <% if @import.type == "TransactionImport" || @import.type == "TradeImport" %> - <%= form.select :account_id, @import.family.accounts.visible.alphabetically.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %> + <%= form.select :account_id, @import.family.accounts.visible.alphabetically.pluck(:name, :id), { label: t(".account_optional_label"), include_blank: t(".multi_account_import"), selected: @import.account_id } %> <% end %> <%= form.text_area :raw_file_str, rows: 10, required: true, - placeholder: "Paste your CSV file contents here", + placeholder: t(".paste_csv_placeholder"), "data-auto-submit-form-target": "auto" %> - <%= form.submit "Upload CSV", disabled: @import.complete? %> + <%= form.submit t(".upload_csv_button"), disabled: @import.complete? %> <% end %> <% end %> <% end %> @@ -161,7 +161,7 @@
- <%= link_to "Download a sample CSV", "/imports/#{@import.id}/upload/sample_csv", class: "text-primary underline", data: { turbo: false } %> to see the required CSV format + <%= link_to t(".download_sample_csv"), "/imports/#{@import.id}/upload/sample_csv", class: "text-primary underline", data: { turbo: false } %> <%= t(".to_see_format") %>
diff --git a/app/views/imports/_failure.html.erb b/app/views/imports/_failure.html.erb index ee9e8b9fc..3cdc4008f 100644 --- a/app/views/imports/_failure.html.erb +++ b/app/views/imports/_failure.html.erb @@ -7,10 +7,10 @@
-

Import failed

-

Please check that your file format, for any errors and that all required fields are filled, then come back and try again.

+

<%= t(".title") %>

+

<%= t(".description") %>

- <%= render DS::Button.new(text: "Try again", href: publish_import_path(import), full_width: true) %> + <%= render DS::Button.new(text: t(".try_again"), href: publish_import_path(import), full_width: true) %>
diff --git a/app/views/imports/_importing.html.erb b/app/views/imports/_importing.html.erb index 546701705..7fb8ed100 100644 --- a/app/views/imports/_importing.html.erb +++ b/app/views/imports/_importing.html.erb @@ -7,13 +7,13 @@
-

Import in progress

-

Your import is in progress. Check the imports menu for status updates or click 'Check Status' to refresh the page for updates. Feel free to continue using the app.

+

<%= t(".title") %>

+

<%= t(".description") %>

- <%= render DS::Link.new(text: "Check status", href: import_path(import), variant: "primary", full_width: true) %> - <%= render DS::Link.new(text: "Back to dashboard", href: root_path, variant: "secondary", full_width: true) %> + <%= render DS::Link.new(text: t(".check_status"), href: import_path(import), variant: "primary", full_width: true) %> + <%= render DS::Link.new(text: t(".back_to_dashboard"), href: root_path, variant: "secondary", full_width: true) %>
diff --git a/app/views/imports/_revert_failure.html.erb b/app/views/imports/_revert_failure.html.erb index 9d64e7a76..6c4872d06 100644 --- a/app/views/imports/_revert_failure.html.erb +++ b/app/views/imports/_revert_failure.html.erb @@ -7,12 +7,12 @@
-

Reverting import failed

-

Please try again

+

<%= t(".title") %>

+

<%= t(".description") %>

<%= render DS::Button.new( - text: "Try again", + text: t(".try_again"), full_width: true, href: revert_import_path(import) ) %> diff --git a/app/views/imports/_success.html.erb b/app/views/imports/_success.html.erb index c0849e94b..c3bc2d1eb 100644 --- a/app/views/imports/_success.html.erb +++ b/app/views/imports/_success.html.erb @@ -7,12 +7,12 @@
-

Import successful

-

Your imported data has been successfully added to the app and is now ready for use.

+

<%= t(".title") %>

+

<%= t(".description") %>

<%= render DS::Link.new( - text: "Back to dashboard", + text: t(".back_to_dashboard"), variant: "primary", full_width: true, href: root_path diff --git a/app/views/layouts/shared/_confirm_dialog.html.erb b/app/views/layouts/shared/_confirm_dialog.html.erb index 03e923818..a25c36f72 100644 --- a/app/views/layouts/shared/_confirm_dialog.html.erb +++ b/app/views/layouts/shared/_confirm_dialog.html.erb @@ -5,17 +5,17 @@
-

Are you sure?

+

<%= t(".are_you_sure") %>

<%= icon("x", as_button: true, type: "submit", value: "cancel") %>
-

This action cannot be undone.

+

<%= t(".cannot_be_undone") %>

<% ["primary", "outline-destructive", "destructive"].each do |variant| %> <%= render DS::Button.new( - text: "Confirm", + text: t(".confirm"), variant: variant, autofocus: true, full_width: true, diff --git a/app/views/loans/tabs/_overview.html.erb b/app/views/loans/tabs/_overview.html.erb index 41167e6fb..2c6d3810a 100644 --- a/app/views/loans/tabs/_overview.html.erb +++ b/app/views/loans/tabs/_overview.html.erb @@ -46,7 +46,7 @@
<%= render DS::Link.new( - text: "Edit loan details", + text: t(".edit_loan_details"), variant: "ghost", href: edit_loan_path(account), frame: :modal diff --git a/app/views/lunchflow_items/_api_error.html.erb b/app/views/lunchflow_items/_api_error.html.erb index b1d6cee13..50050e35a 100644 --- a/app/views/lunchflow_items/_api_error.html.erb +++ b/app/views/lunchflow_items/_api_error.html.erb @@ -1,22 +1,22 @@ <%# locals: (error_message:, return_path:) %> <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Lunch Flow Connection Error") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
<%= render DS::Alert.new( - title: "Unable to connect to Lunch Flow", + title: t(".unable_to_connect"), message: error_message, variant: :error ) %>
-

Common Issues:

+

<%= t(".common_issues") %>

    -
  • Invalid API Key: Check your API key in Provider Settings
  • -
  • Expired Credentials: Generate a new API key from Lunch Flow
  • -
  • Network Issue: Check your internet connection
  • -
  • Service Down: Lunch Flow API may be temporarily unavailable
  • +
  • <%= t(".invalid_api_key_label") %>: <%= t(".invalid_api_key_desc") %>
  • +
  • <%= t(".expired_credentials_label") %>: <%= t(".expired_credentials_desc") %>
  • +
  • <%= t(".network_issue_label") %>: <%= t(".network_issue_desc") %>
  • +
  • <%= t(".service_down_label") %>: <%= t(".service_down_desc") %>
@@ -24,7 +24,7 @@ <%= link_to settings_providers_path, class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors", data: { turbo: false } do %> - Check Provider Settings + <%= t(".check_provider_settings") %> <% end %>
diff --git a/app/views/lunchflow_items/_setup_required.html.erb b/app/views/lunchflow_items/_setup_required.html.erb index afd03df41..1773e81ef 100644 --- a/app/views/lunchflow_items/_setup_required.html.erb +++ b/app/views/lunchflow_items/_setup_required.html.erb @@ -1,21 +1,21 @@ <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Lunch Flow Setup Required") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
<%= render DS::Alert.new( - title: "API Key Not Configured", - message: "Before you can link Lunch Flow accounts, you need to configure your Lunch Flow API key.", + title: t(".api_key_not_configured"), + message: t(".api_key_description"), variant: :warning ) %>
-

Setup Steps:

+

<%= t(".setup_steps_title") %>

    -
  1. Go to Settings → Providers
  2. -
  3. Find the Lunch Flow section
  4. -
  5. Enter your Lunch Flow API key
  6. -
  7. Return here to link your accounts
  8. +
  9. <%= t(".setup_step_1_html") %>
  10. +
  11. <%= t(".setup_step_2_html") %>
  12. +
  13. <%= t(".setup_step_3") %>
  14. +
  15. <%= t(".setup_step_4") %>
@@ -23,7 +23,7 @@ <%= link_to settings_providers_path, class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors", data: { turbo: false } do %> - Go to Provider Settings + <%= t(".go_to_provider_settings") %> <% end %>
diff --git a/app/views/mercury_items/_api_error.html.erb b/app/views/mercury_items/_api_error.html.erb index 4e4124635..0f5696b14 100644 --- a/app/views/mercury_items/_api_error.html.erb +++ b/app/views/mercury_items/_api_error.html.erb @@ -1,25 +1,25 @@ <%# locals: (error_message:, return_path:) %> <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Mercury Connection Error") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
<%= icon("alert-circle", class: "text-destructive w-5 h-5 shrink-0 mt-0.5") %>
-

Unable to connect to Mercury

+

<%= t(".unable_to_connect") %>

<%= error_message %>

-

Common Issues:

+

<%= t(".common_issues") %>

    -
  • Invalid API Token: Check your API token in Provider Settings
  • -
  • Expired Credentials: Generate a new API token from Mercury
  • -
  • Insufficient Permissions: Ensure your token has read-only access
  • -
  • Network Issue: Check your internet connection
  • -
  • Service Down: Mercury API may be temporarily unavailable
  • +
  • <%= t(".invalid_api_token_label") %>: <%= t(".invalid_api_token_desc") %>
  • +
  • <%= t(".expired_credentials_label") %>: <%= t(".expired_credentials_desc") %>
  • +
  • <%= t(".insufficient_permissions_label") %>: <%= t(".insufficient_permissions_desc") %>
  • +
  • <%= t(".network_issue_label") %>: <%= t(".network_issue_desc") %>
  • +
  • <%= t(".service_down_label") %>: <%= t(".service_down_desc") %>
@@ -27,7 +27,7 @@ <%= link_to settings_providers_path, class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors", data: { turbo: false } do %> - Check Provider Settings + <%= t(".check_provider_settings") %> <% end %>
diff --git a/app/views/mercury_items/_setup_required.html.erb b/app/views/mercury_items/_setup_required.html.erb index 069847729..5711d968d 100644 --- a/app/views/mercury_items/_setup_required.html.erb +++ b/app/views/mercury_items/_setup_required.html.erb @@ -1,23 +1,23 @@ <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Mercury Setup Required") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
<%= icon("alert-circle", class: "text-warning w-5 h-5 shrink-0 mt-0.5") %>
-

API Token Not Configured

-

Before you can link Mercury accounts, you need to configure your Mercury API token.

+

<%= t(".api_token_not_configured") %>

+

<%= t(".api_token_description") %>

-

Setup Steps:

+

<%= t(".setup_steps_title") %>

    -
  1. Go to Settings > Providers
  2. -
  3. Find the Mercury section
  4. -
  5. Enter your Mercury API token
  6. -
  7. Return here to link your accounts
  8. +
  9. <%= t(".setup_step_1_html") %>
  10. +
  11. <%= t(".setup_step_2_html") %>
  12. +
  13. <%= t(".setup_step_3") %>
  14. +
  15. <%= t(".setup_step_4") %>
@@ -25,7 +25,7 @@ <%= link_to settings_providers_path, class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors", data: { turbo: false } do %> - Go to Provider Settings + <%= t(".go_to_provider_settings") %> <% end %>
diff --git a/app/views/messages/_chat_form.html.erb b/app/views/messages/_chat_form.html.erb index 85f686f57..1fbc57c82 100644 --- a/app/views/messages/_chat_form.html.erb +++ b/app/views/messages/_chat_form.html.erb @@ -10,7 +10,7 @@ <%# In the future, this will be a dropdown with different AI models %> <%= f.hidden_field :ai_model, value: default_ai_model %> - <%= f.text_area :content, placeholder: "Ask anything ...", value: message_hint, + <%= f.text_area :content, placeholder: t(".placeholder"), value: message_hint, class: "w-full border-0 focus:ring-0 text-sm resize-none px-1 bg-transparent", data: { chat_target: "input", action: "input->chat#autoResize keydown->chat#handleInputKeyDown" }, rows: 1 %> @@ -27,5 +27,5 @@
<% end %> -

AI responses are informational only. Not financial advice!

+

<%= t(".disclaimer") %>

diff --git a/app/views/pages/dashboard/_balance_sheet.html.erb b/app/views/pages/dashboard/_balance_sheet.html.erb index fafc75e2f..b2ff7d632 100644 --- a/app/views/pages/dashboard/_balance_sheet.html.erb +++ b/app/views/pages/dashboard/_balance_sheet.html.erb @@ -37,13 +37,13 @@
-
Name
+
<%= t(".name") %>
-

Weight

+

<%= t(".weight") %>

-

Value

+

<%= t(".value") %>

diff --git a/app/views/pages/feedback.html.erb b/app/views/pages/feedback.html.erb index 71739e2c8..29031ce01 100644 --- a/app/views/pages/feedback.html.erb +++ b/app/views/pages/feedback.html.erb @@ -1,22 +1,22 @@ -<%= content_for :page_title, "Feedback" %> +<%= content_for :page_title, t(".title") %>
-

Leave feedback

-

Let us know if you have any specific feedback. Feel free to include links to videos or screenshots.

+

<%= t(".heading") %>

+

<%= t(".description") %>

<%= link_to "https://github.com/we-promise/sure/discussions/categories/feature-requests", target: "_blank", rel: "noopener noreferrer", class: "w-full md:w-1/3 flex flex-col items-center p-4 border border-alpha-black-25 rounded-xl hover:bg-container-hover" do %> <%= image_tag "github-icon.svg", class: "w-8 h-8 mb-2" %> - Write a feature request + <%= t(".feature_request") %> <% end %> <%= link_to "https://github.com/we-promise/sure/issues/new?assignees=&labels=bug&template=bug_report.md&title=", target: "_blank", rel: "noopener noreferrer", class: "w-full md:w-1/3 flex flex-col items-center p-4 border border-alpha-black-25 rounded-xl hover:bg-container-hover" do %> <%= image_tag "github-icon.svg", class: "w-8 h-8 mb-2" %> - File a bug report + <%= t(".bug_report") %> <% end %> <%= link_to "https://discord.gg/36ZGBsxYEK", target: "_blank", rel: "noopener noreferrer", class: "w-full md:w-1/3 flex flex-col items-center p-4 border border-alpha-black-25 rounded-xl hover:bg-container-hover" do %> <%= image_tag "discord-icon.svg", class: "w-8 h-8 mb-2" %> - Discuss <%= product_name %> with others + <%= t(".discuss", product: product_name) %> <% end %>
diff --git a/app/views/pages/intro.html.erb b/app/views/pages/intro.html.erb index 6ebceb315..7378ccdeb 100644 --- a/app/views/pages/intro.html.erb +++ b/app/views/pages/intro.html.erb @@ -1,6 +1,6 @@ <% content_for :page_header do %>
-

Welcome!

+

<%= t(".welcome") %>


<% end %> @@ -10,12 +10,12 @@
<%= image_tag "logomark-color.svg", class: "w-16 h-16" %>
-

Intro experience coming soon

+

<%= t(".coming_soon") %>

- We're building a richer onboarding journey to learn about your goals, milestones, and day-to-day needs. For now, head over to the chat sidebar to start a conversation with Sure and let us know where you are in your financial journey. + <%= t(".description") %>

- <%= link_to "Start chatting", chats_path, class: "inline-flex items-center gap-2 px-4 py-2 rounded-lg button-bg-primary text-inverse font-medium" %> + <%= link_to t(".start_chatting"), chats_path, class: "inline-flex items-center gap-2 px-4 py-2 rounded-lg button-bg-primary text-inverse font-medium" %>
diff --git a/app/views/pages/redis_configuration_error.html.erb b/app/views/pages/redis_configuration_error.html.erb index a95e00862..8a1a6411f 100644 --- a/app/views/pages/redis_configuration_error.html.erb +++ b/app/views/pages/redis_configuration_error.html.erb @@ -1,4 +1,4 @@ -<% content_for :title, "Redis Configuration Required - Sure" %> +<% content_for :title, t(".page_title") %>
@@ -8,8 +8,8 @@
<%= icon "alert-triangle", class: "w-8 h-8 text-red-600" %>
-

Redis Configuration Required

-

Your self-hosted Sure installation needs Redis to be properly configured.

+

<%= t(".heading") %>

+

<%= t(".subheading") %>

@@ -18,8 +18,8 @@
<%= icon "info", class: "w-5 h-5 text-amber-600 mt-0.5 mr-3 flex-shrink-0" %>
-

Why is Redis required?

-

Sure uses Redis to power Sidekiq background jobs for tasks like syncing account data, processing imports, and other background operations that keep your financial data up to date.

+

<%= t(".why_required_title") %>

+

<%= t(".why_required_body") %>

@@ -27,7 +27,7 @@
<%= render DS::Link.new( - text: "View Setup Guide", + text: t(".view_setup_guide"), href: "https://github.com/we-promise/sure/blob/main/docs/hosting/docker.md", variant: "primary", size: "lg", @@ -36,16 +36,16 @@ target: "_blank", rel: "noopener noreferrer" ) %> -

Follow our complete Docker setup guide to configure Redis

+

<%= t(".setup_guide_hint") %>

-

Once you've configured Redis, refresh this page to continue.

+

<%= t(".refresh_hint") %>

<%= render DS::Button.new( - text: "Refresh Page", + text: t(".refresh_page"), variant: "secondary", icon: "refresh-cw", type: "button", diff --git a/app/views/properties/_overview_fields.html.erb b/app/views/properties/_overview_fields.html.erb index 0fe59023d..d641c7bdf 100644 --- a/app/views/properties/_overview_fields.html.erb +++ b/app/views/properties/_overview_fields.html.erb @@ -2,8 +2,8 @@
<%= form.text_field :name, - label: "Name", - placeholder: "Vacation home", + label: t(".name_label"), + placeholder: t(".name_placeholder"), required: true %> <%= form.hidden_field :accountable_type, value: "Property" %> @@ -11,24 +11,24 @@ <%= form.fields_for :accountable do |property_form| %> <%= property_form.select :subtype, Property::SUBTYPES.map { |k, v| [v[:long], k] }, - { prompt: "Select type", label: "Property type" }, required: true %> + { prompt: t(".subtype_prompt"), label: t(".property_type_label") }, required: true %>
<%= property_form.number_field :year_built, - label: "Year Built (optional)", - placeholder: "1990", + label: t(".year_built_label"), + placeholder: t(".year_built_placeholder"), min: 1500, max: Time.current.year %>
<%= property_form.number_field :area_value, - label: "Area (optional)", - placeholder: "1200", + label: t(".area_label"), + placeholder: t(".area_placeholder"), min: 0 %> <%= property_form.select :area_unit, - [["Square Feet", "sqft"], ["Square Meters", "sqm"]], - { label: "Area Unit" } %> + [[t(".square_feet"), "sqft"], [t(".square_meters"), "sqm"]], + { label: t(".area_unit_label") } %>
<% end %> diff --git a/app/views/properties/address.html.erb b/app/views/properties/address.html.erb index 834f7c95c..489ab62b1 100644 --- a/app/views/properties/address.html.erb +++ b/app/views/properties/address.html.erb @@ -1,5 +1,5 @@ <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Enter property manually") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
@@ -13,25 +13,25 @@ <%= form.fields_for :address do |address_form| %> <%= address_form.text_field :line1, - label: "Address Line 1", - placeholder: "123 Main Street" %> + label: t(".address_line1_label"), + placeholder: t(".address_line1_placeholder") %>
<%= address_form.text_field :locality, - label: "City", - placeholder: "San Francisco" %> + label: t(".city_label"), + placeholder: t(".city_placeholder") %> <%= address_form.text_field :region, - label: "State/Region", - placeholder: "CA" %> + label: t(".state_region_label"), + placeholder: t(".state_region_placeholder") %>
<%= address_form.text_field :postal_code, - label: "Postal Code", - placeholder: "12345" %> + label: t(".postal_code_label"), + placeholder: t(".postal_code_placeholder") %> <%= address_form.text_field :country, - label: "Country", - placeholder: "USA" %> + label: t(".country_label"), + placeholder: t(".country_placeholder") %>
<% end %>
@@ -39,7 +39,7 @@
<%= render DS::Button.new( - text: "Save", + text: t(".save"), variant: "primary", ) %>
diff --git a/app/views/properties/balances.html.erb b/app/views/properties/balances.html.erb index a25f77936..3cd96041d 100644 --- a/app/views/properties/balances.html.erb +++ b/app/views/properties/balances.html.erb @@ -1,5 +1,5 @@ <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Enter property manually") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
<%= render "properties/form_tabs", account: @account, active_tab: "value" %> @@ -11,15 +11,15 @@ <%= render "properties/form_alert", notice: @success_message, error: @error_message %> <%= form.money_field :balance, - label: "Estimated market value", - label_tooltip: "The estimated market value of your property. This number can often be found on sites like Zillow or Redfin, and is never an exact number.", + label: t(".market_value_label"), + label_tooltip: t(".market_value_tooltip"), placeholder: "0" %>
<%= render DS::Button.new( - text: @account.active? ? "Save" : "Next", + text: @account.active? ? t(".save") : t(".next"), variant: "primary", ) %>
diff --git a/app/views/properties/new.html.erb b/app/views/properties/new.html.erb index 03feb04a0..32ab6d960 100644 --- a/app/views/properties/new.html.erb +++ b/app/views/properties/new.html.erb @@ -1,5 +1,5 @@ <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Enter property manually") %> + <% dialog.with_header(title: t(".title")) %> <% dialog.with_body do %>
@@ -16,7 +16,7 @@
<%= render DS::Button.new( - text: "Next", + text: t(".next"), variant: "primary", ) %>
diff --git a/app/views/properties/tabs/_overview.html.erb b/app/views/properties/tabs/_overview.html.erb index 3a33e092f..df3417443 100644 --- a/app/views/properties/tabs/_overview.html.erb +++ b/app/views/properties/tabs/_overview.html.erb @@ -30,7 +30,7 @@
<%= render DS::Link.new( - text: "Edit account details", + text: t(".edit_account_details"), href: edit_property_path(account), variant: "ghost", frame: :modal diff --git a/app/views/reports/_investment_flows.html.erb b/app/views/reports/_investment_flows.html.erb index 83db092e2..ee773b994 100644 --- a/app/views/reports/_investment_flows.html.erb +++ b/app/views/reports/_investment_flows.html.erb @@ -1,7 +1,7 @@ <%# locals: (investment_flows:) %>

- Track money flowing into and out of your investment accounts through contributions and withdrawals. + <%= t(".description") %>

@@ -9,36 +9,36 @@
<%= icon("trending-up", size: "sm", class: "text-green-600") %> -

Contributions

+

<%= t(".contributions") %>

<%= format_money(investment_flows.contributions) %>
-

Money added to investments

+

<%= t(".contributions_description") %>

<%= icon("trending-down", size: "sm", class: "text-orange-600") %> -

Withdrawals

+

<%= t(".withdrawals") %>

<%= format_money(investment_flows.withdrawals) %>
-

Money withdrawn from investments

+

<%= t(".withdrawals_description") %>

<%= icon("arrow-right-left", size: "sm", class: "text-primary") %> -

Net Flow

+

<%= t(".net_flow") %>

<%= format_money(investment_flows.net_flow) %>
-

Total net change

+

<%= t(".net_flow_description") %>

diff --git a/app/views/rule/conditions/_condition_group.html.erb b/app/views/rule/conditions/_condition_group.html.erb index 54f056bae..09161462f 100644 --- a/app/views/rule/conditions/_condition_group.html.erb +++ b/app/views/rule/conditions/_condition_group.html.erb @@ -11,11 +11,11 @@
<%# Show prefix on condition groups, except the first one %>
- and + <%= t(".and_prefix") %>
-

match

- <%= form.select :operator, [["all", "and"], ["any", "or"]], { container_class: "w-fit" }, data: { rules_target: "operatorField" } %> -

of the following conditions

+

<%= t(".match") %>

+ <%= form.select :operator, [[t(".all"), "and"], [t(".any"), "or"]], { container_class: "w-fit" }, data: { rules_target: "operatorField" } %> +

<%= t(".of_the_following_conditions") %>

<%= icon( @@ -40,7 +40,7 @@ <%= render DS::Button.new( - text: "Add condition", + text: t(".add_condition"), leading_icon: "plus", variant: "ghost", type: "button", diff --git a/app/views/rules/_form.html.erb b/app/views/rules/_form.html.erb index 2f4f1fe18..7c942a35f 100644 --- a/app/views/rules/_form.html.erb +++ b/app/views/rules/_form.html.erb @@ -12,10 +12,10 @@
<%= icon "tag", size: "sm" %> -

Rule name (optional)

+

<%= t(".rule_name_label") %>

- <%= f.text_field :name, placeholder: "Enter a name for this rule", class: "form-field__input" %> + <%= f.text_field :name, placeholder: t(".rule_name_placeholder"), class: "form-field__input" %>
@@ -50,15 +50,15 @@
- <%= render DS::Button.new(text: "Add condition", icon: "plus", variant: "ghost", type: "button", data: { action: "rules#addCondition" }) %> - <%= render DS::Button.new(text: "Add condition group", icon: "copy-plus", variant: "ghost", type: "button", data: { action: "rules#addConditionGroup" }) %> + <%= render DS::Button.new(text: t(".add_condition"), icon: "plus", variant: "ghost", type: "button", data: { action: "rules#addCondition" }) %> + <%= render DS::Button.new(text: t(".add_condition_group"), icon: "copy-plus", variant: "ghost", type: "button", data: { action: "rules#addConditionGroup" }) %>
-

THEN

+

<%= t(".then") %>

<%# Action template, used by Stimulus controller to add new actions dynamically %> @@ -75,7 +75,7 @@ <% end %> - <%= render DS::Button.new(text: "Add action", icon: "plus", variant: "ghost", type: "button", data: { action: "rules#addAction" }) %> + <%= render DS::Button.new(text: t(".add_action"), icon: "plus", variant: "ghost", type: "button", data: { action: "rules#addAction" }) %>
@@ -87,13 +87,13 @@
<%= f.radio_button :effective_date_enabled, false, checked: rule.effective_date.nil?, data: { action: "rules#clearEffectiveDate" } %> - <%= f.label :effective_date_enabled_false, "All past and future #{rule.resource_type}s", class: "text-sm text-primary" %> + <%= f.label :effective_date_enabled_false, t(".all_past_and_future", resource: rule.resource_type.pluralize), class: "text-sm text-primary" %>
<%= f.radio_button :effective_date_enabled, true, checked: rule.effective_date.present? %> - <%= f.label :effective_date_enabled_true, "Starting from", class: "text-sm text-primary" %> + <%= f.label :effective_date_enabled_true, t(".starting_from"), class: "text-sm text-primary" %>
<%= f.date_field :effective_date, container_class: "w-fit", data: { rules_target: "effectiveDateInput" } %> diff --git a/app/views/rules/_rule.html.erb b/app/views/rules/_rule.html.erb index 2e882e4ba..3106e8b4f 100644 --- a/app/views/rules/_rule.html.erb +++ b/app/views/rules/_rule.html.erb @@ -21,14 +21,14 @@ <% end %> <% if additional_condition_count.positive? %> - and <%= additional_condition_count %> more <%= additional_condition_count == 1 ? "condition" : "conditions" %> + <%= t(".and_more_conditions", count: additional_condition_count) %> <% end %>

<% end %>
- THEN + <%= t(".then") %>

@@ -36,14 +36,14 @@ <%= t("rules.no_action") %> <% else %> <% if rule.actions.first.value && rule.actions.first.options %> - <%= rule.actions.first.executor.label %> to <%= rule.actions.first.value_display %> + <%= t(".action_label_to", label: rule.actions.first.executor.label, value: rule.actions.first.value_display) %> <% else %> <%= rule.actions.first.executor.label %> <% end %> <% end %> <% if rule.actions.count > 1 %> - and <%= rule.actions.count - 1 %> more <%= rule.actions.count - 1 == 1 ? "action" : "actions" %> + <%= t(".and_more_actions", count: rule.actions.count - 1) %> <% end %>

@@ -54,9 +54,9 @@

<% if rule.effective_date.nil? %> - All past and future <%= rule.resource_type.pluralize %> + <%= t(".all_past_and_future", resource: rule.resource_type.pluralize) %> <% else %> - <%= rule.resource_type.pluralize %> on or after <%= rule.effective_date.strftime("%b %-d, %Y") %> + <%= t(".on_or_after", resource: rule.resource_type.pluralize, date: rule.effective_date.strftime("%b %-d, %Y")) %> <% end %>

@@ -67,11 +67,11 @@ <%= f.toggle :active, { data: { auto_submit_form_target: "auto" } } %> <% end %> <%= render DS::Menu.new do |menu| %> - <% menu.with_item(variant: "link", text: "Edit", href: edit_rule_path(rule), icon: "pencil", data: { turbo_frame: "modal" }) %> - <% menu.with_item(variant: "link", text: "Re-apply rule", href: confirm_rule_path(rule), icon: "refresh-cw", data: { turbo_frame: "modal" }) %> + <% menu.with_item(variant: "link", text: t(".edit"), href: edit_rule_path(rule), icon: "pencil", data: { turbo_frame: "modal" }) %> + <% menu.with_item(variant: "link", text: t(".re_apply_rule"), href: confirm_rule_path(rule), icon: "refresh-cw", data: { turbo_frame: "modal" }) %> <% menu.with_item( variant: "button", - text: "Delete", + text: t(".delete"), href: rule_path(rule), icon: "trash-2", method: :delete, diff --git a/app/views/rules/confirm.html.erb b/app/views/rules/confirm.html.erb index cf505c8f9..13947e59f 100644 --- a/app/views/rules/confirm.html.erb +++ b/app/views/rules/confirm.html.erb @@ -1,18 +1,16 @@ <%= render DS::Dialog.new(reload_on_close: params[:reload_on_close].present?) do |dialog| %> <% title = if @rule.name.present? - "Confirm changes to \"#{@rule.name}\"" + t(".title_with_name", name: @rule.name) else - "Confirm changes" + t(".title") end %> <% dialog.with_header(title: title) %> <% dialog.with_body do %>

- You are about to apply this rule to - <%= @rule.affected_resource_count %> <%= @rule.resource_type.pluralize %> - that meet the specified rule criteria. Please confirm if you wish to proceed with this change. + <%= t(".apply_notice_html", count: @rule.affected_resource_count, resource: @rule.resource_type.pluralize) %>

<% if @rule.actions.any? { |a| a.action_type == "auto_categorize" } %> @@ -21,25 +19,24 @@
<%= icon "info", class: "w-4 h-4 text-blue-600 mt-0.5 flex-shrink-0" %>
-

AI Cost Estimation

+

<%= t(".ai_cost_title") %>

<% if @estimated_cost.present? %>

- This will use AI to categorize <%= affected_count %> transaction<%= "s" if affected_count != 1 %>. - Estimated cost: ~$<%= sprintf("%.4f", @estimated_cost) %> + <%= t(".ai_cost_with_estimate_html", count: affected_count, cost: sprintf("%.4f", @estimated_cost)) %>

<% else %>

- This will use AI to categorize <%= affected_count %> transaction<%= "s" if affected_count != 1 %>. + <%= t(".ai_cost_no_estimate_html", count: affected_count) %> <% if @selected_model.present? %> - Cost estimation unavailable for model "<%= @selected_model %>". + <%= t(".cost_unavailable_model", model: @selected_model) %> <% else %> - Cost estimation unavailable (no LLM provider configured). + <%= t(".cost_unavailable_no_provider") %> <% end %> - You may incur costs, please check with the model provider for the most up-to-date prices. + <%= t(".cost_warning") %>

<% end %>

- <%= link_to "View usage history", settings_llm_usage_path, class: "underline hover:text-blue-800" %> + <%= link_to t(".view_usage_history"), settings_llm_usage_path, class: "underline hover:text-blue-800" %>

@@ -47,7 +44,7 @@ <% end %> <%= render DS::Button.new( - text: "Confirm changes", + text: t(".confirm_changes"), href: apply_rule_path(@rule), method: :post, full_width: true, diff --git a/app/views/rules/index.html.erb b/app/views/rules/index.html.erb index 00d0dfb94..05c5361dd 100644 --- a/app/views/rules/index.html.erb +++ b/app/views/rules/index.html.erb @@ -1,4 +1,4 @@ -<%= content_for :page_title, "Rules" %> +<%= content_for :page_title, t(".page_title") %> <%= content_for :page_actions do %> <% if @rules.any? %> <%= render DS::Menu.new do |menu| %> @@ -15,7 +15,7 @@ )) %> <% menu.with_item( variant: "button", - text: "Delete all rules", + text: t(".delete_all_rules"), href: destroy_all_rules_path, icon: "trash-2", method: :delete, @@ -30,7 +30,7 @@ ) %> <% end %> <%= render DS::Link.new( - text: "New rule", + text: t(".new_rule"), variant: "primary", href: new_rule_path(resource_type: "transaction"), icon: "plus", @@ -42,7 +42,7 @@
<%= icon("circle-alert", size: "sm") %>

- AI-enabled rule actions will cost money. Be sure to filter as narrowly as possible to avoid unnecessary costs. + <%= t(".ai_cost_warning") %>

<% end %> @@ -51,15 +51,15 @@
-

Rules

+

<%= t(".rules_heading") %>

·

<%= @rules.count %>

- Sort by: + <%= t(".sort_by") %> <%= form_with url: rules_path, method: :get, local: true, class: "flex items-center", data: { controller: "auto-submit-form" } do |form| %> <%= form.select :sort_by, - options_for_select([["Name", "name"], ["Updated At", "updated_at"]], @sort_by), + options_for_select([[t(".sort_name"), "name"], [t(".sort_updated_at"), "updated_at"]], @sort_by), {}, class: "min-w-[120px] bg-transparent rounded border-none cursor-pointer text-primary uppercase text-xs w-auto", data: { auto_submit_form_target: "auto", autosubmit_trigger_event: "change" } %> @@ -70,7 +70,7 @@ variant: "icon", icon: "arrow-up-down", size: :sm, - title: "Toggle sort direction" + title: t(".toggle_sort_direction") ) %>
@@ -83,11 +83,11 @@ <% else %>
-

No rules yet

-

Set up rules to perform actions to your transactions and other data on every account sync.

+

<%= t(".no_rules_title") %>

+

<%= t(".no_rules_description") %>

<%= render DS::Link.new( - text: "New rule", + text: t(".new_rule"), variant: "primary", href: new_rule_path(resource_type: "transaction"), icon: "plus", diff --git a/app/views/sessions/mobile_sso_start.html.erb b/app/views/sessions/mobile_sso_start.html.erb index 74fa9ebfd..123aca3a7 100644 --- a/app/views/sessions/mobile_sso_start.html.erb +++ b/app/views/sessions/mobile_sso_start.html.erb @@ -4,5 +4,5 @@ - + diff --git a/app/views/settings/_settings_nav.html.erb b/app/views/settings/_settings_nav.html.erb index 19bd4a241..f72cbc196 100644 --- a/app/views/settings/_settings_nav.html.erb +++ b/app/views/settings/_settings_nav.html.erb @@ -28,13 +28,13 @@ nav_sections = [ header: t(".advanced_section_title"), items: [ { label: t(".ai_prompts_label"), path: settings_ai_prompts_path, icon: "bot" }, - { label: "LLM Usage", path: settings_llm_usage_path, icon: "activity" }, + { label: t(".llm_usage_label"), path: settings_llm_usage_path, icon: "activity" }, { label: t(".api_keys_label"), path: settings_api_key_path, icon: "key" }, { label: t(".self_hosting_label"), path: settings_hosting_path, icon: "database", if: self_hosted? }, { label: t(".imports_label"), path: imports_path, icon: "download" }, { label: t(".exports_label"), path: family_exports_path, icon: "upload" }, - { label: "SSO Providers", path: admin_sso_providers_path, icon: "key-round", if: Current.user&.super_admin? }, - { label: "Users", path: admin_users_path, icon: "users", if: Current.user&.super_admin? } + { label: t(".sso_providers_label"), path: admin_sso_providers_path, icon: "key-round", if: Current.user&.super_admin? }, + { label: t(".users_label"), path: admin_users_path, icon: "users", if: Current.user&.super_admin? } ] } : nil ), diff --git a/app/views/settings/api_keys/created.html.erb b/app/views/settings/api_keys/created.html.erb index d37189000..d0c4e7bca 100644 --- a/app/views/settings/api_keys/created.html.erb +++ b/app/views/settings/api_keys/created.html.erb @@ -1,6 +1,6 @@ -<%= content_for :page_title, "API Key Created" %> +<%= content_for :page_title, t(".page_title") %> -<%= settings_section title: "API Key Created Successfully", subtitle: "Your new API key has been generated successfully." do %> +<%= settings_section title: t(".success_title"), subtitle: t(".success_description") do %>
@@ -11,21 +11,21 @@ variant: :success ) %>
-

API Key Created Successfully!

-

Your new API key "<%= @api_key.name %>" has been created and is ready to use.

+

<%= t(".success_title") %>

+

<%= t(".key_ready", name: @api_key.name) %>

-

Your API Key

-

Copy and store this key securely. You'll need it to authenticate your API requests.

+

<%= t(".your_api_key") %>

+

<%= t(".copy_store_securely") %>

<%= @api_key.plain_key %> <%= render DS::Button.new( - text: "Copy API Key", + text: t(".copy_key"), variant: "ghost", icon: "copy", data: { action: "clipboard#copy" } @@ -35,42 +35,41 @@
-

Key Details

+

<%= t(".key_details_title") %>

- Name: + <%= t(".key_name_label") %> <%= @api_key.name %>
- Permissions: + <%= t(".permissions_label") %> <%= @api_key.scopes.map { |scope| case scope - when "read_accounts" then "View Accounts" - when "read_transactions" then "View Transactions" - when "read_balances" then "View Balances" - when "write_transactions" then "Create Transactions" + when "read_accounts" then t("settings.api_keys_controller.scope_descriptions.read_accounts") + when "read_transactions" then t("settings.api_keys_controller.scope_descriptions.read_transactions") + when "read_balances" then t("settings.api_keys_controller.scope_descriptions.read_balances") + when "write_transactions" then t("settings.api_keys_controller.scope_descriptions.write_transactions") else scope.humanize end }.join(", ") %>
- Created: + <%= t(".created_label") %> <%= @api_key.created_at.strftime("%B %d, %Y at %I:%M %p") %>
- <%= render DS::Alert.new(title: "Important Security Note", variant: :warning) do %> + <%= render DS::Alert.new(title: t(".security_note_title"), variant: :warning) do %>

- This is the only time your API key will be displayed. Make sure to copy it now and store it securely. - If you lose this key, you'll need to generate a new one. + <%= t(".security_note_body") %>

<% end %>
-

How to use your API key

+

<%= t(".usage_instructions_title") %>

<%= t("settings.api_keys.show.current_api_key.usage_instructions", product_name: product_name) %>

curl -H "X-Api-Key: <%= @api_key.plain_key %>" <%= request.base_url %>/api/v1/accounts @@ -79,7 +78,7 @@
<%= render DS::Link.new( - text: "Continue to API Key Settings", + text: t(".continue"), href: settings_api_key_path, variant: "primary" ) %> diff --git a/app/views/settings/api_keys/created.turbo_stream.erb b/app/views/settings/api_keys/created.turbo_stream.erb index dec5baeb9..f187a6d31 100644 --- a/app/views/settings/api_keys/created.turbo_stream.erb +++ b/app/views/settings/api_keys/created.turbo_stream.erb @@ -2,10 +2,10 @@

- API Key Created + <%= t("settings.api_keys.created.page_title") %>

- <%= settings_section title: "API Key Created Successfully", subtitle: "Your new API key has been generated successfully." do %> + <%= settings_section title: t("settings.api_keys.created.success_title"), subtitle: t("settings.api_keys.created.success_description") do %>
@@ -16,21 +16,21 @@ variant: :success ) %>
-

API Key Created Successfully!

-

Your new API key "<%= @api_key.name %>" has been created and is ready to use.

+

<%= t("settings.api_keys.created.success_title") %>

+

<%= t("settings.api_keys.created.key_ready", name: @api_key.name) %>

-

Your API Key

-

Copy and store this key securely. You'll need it to authenticate your API requests.

+

<%= t("settings.api_keys.created.your_api_key") %>

+

<%= t("settings.api_keys.created.copy_store_securely") %>

<%= @api_key.plain_key %> <%= render DS::Button.new( - text: "Copy API Key", + text: t("settings.api_keys.created.copy_key"), variant: "ghost", icon: "copy", data: { action: "clipboard#copy" } @@ -40,43 +40,42 @@
-

Key Details

+

<%= t("settings.api_keys.created.key_details_title") %>

- Name: + <%= t("settings.api_keys.created.key_name_label") %> <%= @api_key.name %>
- Permissions: + <%= t("settings.api_keys.created.permissions_label") %> <%= @api_key.scopes.map { |scope| case scope - when "read_accounts" then "View Accounts" - when "read_transactions" then "View Transactions" - when "read_balances" then "View Balances" - when "write_transactions" then "Create Transactions" + when "read_accounts" then t("settings.api_keys_controller.scope_descriptions.read_accounts") + when "read_transactions" then t("settings.api_keys_controller.scope_descriptions.read_transactions") + when "read_balances" then t("settings.api_keys_controller.scope_descriptions.read_balances") + when "write_transactions" then t("settings.api_keys_controller.scope_descriptions.write_transactions") else scope.humanize end }.join(", ") %>
- Created: + <%= t("settings.api_keys.created.created_label") %> <%= @api_key.created_at.strftime("%B %d, %Y at %I:%M %p") %>
- <%= render DS::Alert.new(title: "Important Security Note", variant: :warning) do %> + <%= render DS::Alert.new(title: t("settings.api_keys.created.security_note_title"), variant: :warning) do %>

- This is the only time your API key will be displayed. Make sure to copy it now and store it securely. - If you lose this key, you'll need to generate a new one. + <%= t("settings.api_keys.created.security_note_body") %>

<% end %>
-

How to use your API key

-

Include your API key in the X-Api-Key header when making requests:

+

<%= t("settings.api_keys.created.usage_instructions_title") %>

+

<%= t("settings.api_keys.show.current_api_key.usage_instructions", product_name: product_name) %>

curl -H "X-Api-Key: <%= @api_key.plain_key %>" <%= request.base_url %>/api/v1/accounts
@@ -84,7 +83,7 @@
<%= render DS::Link.new( - text: "Continue to API Key Settings", + text: t("settings.api_keys.created.continue"), href: settings_api_key_path, variant: "primary" ) %> diff --git a/app/views/settings/api_keys/new.html.erb b/app/views/settings/api_keys/new.html.erb index 34def86b0..d37a24f70 100644 --- a/app/views/settings/api_keys/new.html.erb +++ b/app/views/settings/api_keys/new.html.erb @@ -1,20 +1,20 @@ -<%= content_for :page_title, "Create New API Key" %> +<%= content_for :page_title, t(".create_new_api_key") %> -<%= settings_section title: nil, subtitle: "Generate a new API key to access your Sure data programmatically." do %> +<%= settings_section title: nil, subtitle: t(".subtitle") do %> <%= styled_form_with model: @api_key, url: settings_api_key_path, class: "space-y-4" do |form| %> <%= form.text_field :name, - placeholder: "e.g., My Budget App, Portfolio Tracker", - label: "API Key Name", - help_text: "Choose a descriptive name to help you identify this key later." %> + placeholder: t(".name_placeholder"), + label: t(".name_label"), + help_text: t(".name_help_text") %>
- <%= form.label :scopes, "Permissions", class: "block text-sm font-medium text-primary mb-2" %> -

Select the permissions this API key should have:

+ <%= form.label :scopes, t(".permissions_label"), class: "block text-sm font-medium text-primary mb-2" %> +

<%= t(".permissions_help") %>

<% [ - ["read", "Read Only", "View your accounts, transactions, and balances"], - ["read_write", "Read/Write", "View your data and create new transactions"] + ["read", t(".scope_read_only"), t(".scope_read_only_description")], + ["read_write", t(".scope_read_write"), t(".scope_read_write_description")] ].each do |value, label, description| %>
- <%= render DS::Alert.new(title: "Security Warning", variant: :warning) do %> + <%= render DS::Alert.new(title: t(".security_warning_title"), variant: :warning) do %>

- Your API key will be displayed only once after creation. Make sure to copy and store it securely. - Anyone with access to this key can access your data according to the permissions you select. + <%= t(".security_warning_body") %>

<% end %>
<%= render DS::Link.new( - text: "Cancel", + text: t(".cancel"), href: settings_api_key_path, variant: "ghost" ) %> <%= render DS::Button.new( - text: "Save API Key", + text: t(".save_api_key"), variant: "primary", type: "submit" ) %> diff --git a/app/views/settings/api_keys/show.html.erb b/app/views/settings/api_keys/show.html.erb index b398aaca1..c32e87349 100644 --- a/app/views/settings/api_keys/show.html.erb +++ b/app/views/settings/api_keys/show.html.erb @@ -1,5 +1,5 @@ <% if @newly_created && @plain_key %> - <%= content_for :page_title, "API Key Created Successfully" %> + <%= content_for :page_title, t(".newly_created.page_title") %>
@@ -12,21 +12,21 @@ variant: :success ) %>
-

API Key Created Successfully!

-

Your new API key "<%= @current_api_key.name %>" has been created and is ready to use.

+

<%= t(".newly_created.heading") %>

+

<%= t(".newly_created.key_ready", name: @current_api_key.name) %>

-

Your API Key

-

Copy and store this key securely. You'll need it to authenticate your API requests.

+

<%= t(".newly_created.your_api_key") %>

+

<%= t(".newly_created.copy_store_securely") %>

<%= @current_api_key.plain_key %> <%= render DS::Button.new( - text: "Copy API Key", + text: t(".newly_created.copy_api_key"), variant: "ghost", icon: "copy", data: { action: "clipboard#copy" } @@ -36,7 +36,7 @@
-

How to use your API key

+

<%= t(".newly_created.how_to_use") %>

<%= t(".current_api_key.usage_instructions", product_name: product_name) %>

curl -H "X-Api-Key: <%= @current_api_key.plain_key %>" <%= request.base_url %>/api/v1/accounts @@ -45,7 +45,7 @@
<%= render DS::Link.new( - text: "Continue to API Key Settings", + text: t(".newly_created.continue"), href: settings_api_key_path, variant: "primary" ) %> @@ -53,10 +53,10 @@
<% elsif @current_api_key %> - <%= content_for :page_title, "Your API Key" %> + <%= content_for :page_title, t(".current_api_key.title") %> <%= content_for :page_actions do %> <%= render DS::Link.new( - text: "Create New Key", + text: t(".current_api_key.regenerate_key"), href: new_settings_api_key_path(regenerate: true), variant: "secondary" ) %> @@ -75,30 +75,30 @@

<%= @current_api_key.name %>

- Created <%= time_ago_in_words(@current_api_key.created_at) %> ago + <%= t(".current_api_key.created_ago", time: time_ago_in_words(@current_api_key.created_at)) %> <% if @current_api_key.last_used_at %> - • Last used <%= time_ago_in_words(@current_api_key.last_used_at) %> ago + • <%= t(".current_api_key.last_used_ago", time: time_ago_in_words(@current_api_key.last_used_at)) %> <% else %> - • Never used + • <%= t(".current_api_key.never_used") %> <% end %>

-

Active

+

<%= t(".current_api_key.active") %>

-

Permissions

+

<%= t(".current_api_key.permissions") %>

<% @current_api_key.scopes.each do |scope| %> <%= icon("shield-check", class: "w-3 h-3") %> <%= case scope - when "read" then "Read Only" - when "read_write" then "Read/Write" + when "read" then t(".current_api_key.scope_read_only") + when "read_write" then t(".current_api_key.scope_read_write") else scope.humanize end %> @@ -107,14 +107,14 @@
-

Your API Key

-

Copy and store this key securely. You'll need it to authenticate your API requests.

+

<%= t(".current_api_key.title") %>

+

<%= t(".current_api_key.copy_store_securely") %>

<%= @current_api_key.plain_key %> <%= render DS::Button.new( - text: "Copy API Key", + text: t(".current_api_key.copy_api_key"), variant: "ghost", icon: "copy", data: { action: "clipboard#copy" } @@ -124,7 +124,7 @@
-

How to use your API key

+

<%= t(".current_api_key.usage_instructions_title") %>

<%= t(".current_api_key.usage_instructions", product_name: product_name) %>

curl -H "X-Api-Key: <%= @current_api_key.plain_key %>" <%= request.base_url %>/api/v1/accounts @@ -133,12 +133,12 @@
<%= render DS::Button.new( - text: "Revoke Key", + text: t(".current_api_key.revoke_key"), href: settings_api_key_path, method: :delete, variant: "destructive", data: { - turbo_confirm: "Are you sure you want to revoke this API key?" + turbo_confirm: t(".current_api_key.revoke_confirmation") } ) %>
diff --git a/app/views/settings/hostings/_brand_fetch_settings.html.erb b/app/views/settings/hostings/_brand_fetch_settings.html.erb index ea83248e4..03c52701f 100644 --- a/app/views/settings/hostings/_brand_fetch_settings.html.erb +++ b/app/views/settings/hostings/_brand_fetch_settings.html.erb @@ -2,21 +2,21 @@

<%= t(".title") %>

<% if ENV["BRAND_FETCH_CLIENT_ID"].present? %> -

You have successfully configured your Brand Fetch Client ID through the BRAND_FETCH_CLIENT_ID environment variable.

+

<%= t(".env_configured_message") %>

<% else %>
<%= t(".description") %>
- (show details) + <%= t(".show_details") %>
  1. - Visit brandfetch.com and create a free Brand Fetch Developer account. + <%= t(".setup_step_1_html") %>
  2. - Go to the Logo API page. + <%= t(".setup_step_2_html") %>
  3. - Tap the eye icon under the "Your Client ID" section to reveal your Client ID and paste it below. + <%= t(".setup_step_3") %>
diff --git a/app/views/settings/llm_usages/show.html.erb b/app/views/settings/llm_usages/show.html.erb index 7abcd4f5e..dfb11ead1 100644 --- a/app/views/settings/llm_usages/show.html.erb +++ b/app/views/settings/llm_usages/show.html.erb @@ -1,22 +1,22 @@ -<%= content_for :page_title, "LLM Usage & Costs" %> +<%= content_for :page_title, t(".page_title") %>
-

Track your AI usage and estimated costs

+

<%= t(".subtitle") %>

<%= form_with url: settings_llm_usage_path, method: :get, class: "flex gap-4 items-end flex-wrap" do |f| %>
- <%= f.label :start_date, "Start Date", class: "block text-sm font-medium text-primary mb-1" %> + <%= f.label :start_date, t(".start_date"), class: "block text-sm font-medium text-primary mb-1" %> <%= f.date_field :start_date, value: @start_date, class: "rounded-lg border border-primary px-3 py-2 text-sm bg-container-inset text-primary w-full" %>
- <%= f.label :end_date, "End Date", class: "block text-sm font-medium text-primary mb-1" %> + <%= f.label :end_date, t(".end_date"), class: "block text-sm font-medium text-primary mb-1" %> <%= f.date_field :end_date, value: @end_date, class: "rounded-lg border border-primary px-3 py-2 text-sm bg-container-inset text-primary w-full" %>
- <%= render DS::Button.new(variant: :primary, size: :md, type: "submit", text: "Filter", class: "md:w-auto w-full justify-center") %> + <%= render DS::Button.new(variant: :primary, size: :md, type: "submit", text: t(".filter"), class: "md:w-auto w-full justify-center") %> <% end %>
@@ -25,7 +25,7 @@
<%= icon "activity", class: "w-5 h-5 text-secondary" %> -

Total Requests

+

<%= t(".total_requests") %>

<%= number_with_delimiter(@statistics[:total_requests]) %>

@@ -33,19 +33,19 @@
<%= icon "hash", class: "w-5 h-5 text-secondary" %> -

Total Tokens

+

<%= t(".total_tokens") %>

<%= number_with_delimiter(@statistics[:total_tokens]) %>

- <%= number_with_delimiter(@statistics[:total_prompt_tokens]) %> prompt / - <%= number_with_delimiter(@statistics[:total_completion_tokens]) %> completion + <%= number_with_delimiter(@statistics[:total_prompt_tokens]) %> <%= t(".prompt") %> / + <%= number_with_delimiter(@statistics[:total_completion_tokens]) %> <%= t(".completion") %>

<%= icon "dollar-sign", class: "w-5 h-5 text-secondary" %> -

Total Cost

+

<%= t(".total_cost") %>

$<%= sprintf("%.2f", @statistics[:total_cost]) %>

@@ -53,15 +53,14 @@
<%= icon "trending-up", class: "w-5 h-5 text-secondary" %> -

Avg Cost/Request

+

<%= t(".avg_cost_per_request") %>

$<%= sprintf("%.4f", @statistics[:avg_cost]) %>

<% if @statistics[:requests_with_cost] < @statistics[:total_requests] %>

- Based on <%= number_with_delimiter(@statistics[:requests_with_cost]) %> of - <%= number_with_delimiter(@statistics[:total_requests]) %> requests with cost data + <%= t(".based_on_requests", with_cost: number_with_delimiter(@statistics[:requests_with_cost]), total: number_with_delimiter(@statistics[:total_requests])) %>

<% end %>
@@ -70,7 +69,7 @@ <% if @statistics[:by_operation].any? %>
-

Cost by Operation

+

<%= t(".cost_by_operation") %>

<% @statistics[:by_operation].each do |operation, cost| %> @@ -87,7 +86,7 @@ <% if @statistics[:by_model].any? %>
-

Cost by Model

+

<%= t(".cost_by_model") %>

<% @statistics[:by_model].each do |model, cost| %> @@ -103,17 +102,17 @@
-

Recent Usage

+

<%= t(".recent_usage") %>

<% if @llm_usages.any? %> - - - - - + + + + + @@ -133,7 +132,7 @@
<%= icon "alert-circle", class: "w-4 h-4 text-red-600 theme-dark:text-red-400" %> - Failed + <%= t(".failed") %>
DateOperationModelTokensCost<%= t(".col_date") %><%= t(".col_operation") %><%= t(".col_model") %><%= t(".col_tokens") %><%= t(".col_cost") %>
<% else %>
-

No usage data found for the selected period

+

<%= t(".no_usage_data") %>

<% end %>
@@ -171,11 +170,9 @@
<%= icon "info", class: "w-5 h-5 text-blue-600 mt-0.5" %>
-

About Cost Estimates

+

<%= t(".cost_estimates_title") %>

- Costs are estimated based on OpenAI's pricing as of 2025. Actual costs may vary. - Pricing is per 1 million tokens and varies by model. - Custom or self-hosted models will show "N/A" and are not included in cost totals. + <%= t(".cost_estimates_description") %>

diff --git a/app/views/settings/payments/show.html.erb b/app/views/settings/payments/show.html.erb index ac0184cea..0768440cd 100644 --- a/app/views/settings/payments/show.html.erb +++ b/app/views/settings/payments/show.html.erb @@ -13,7 +13,7 @@
<% if @family.has_active_subscription? %>

- Currently on the <%= @family.subscription.name %>.
+ <%= t(".currently_on_plan") %> <%= @family.subscription.name %>.
<% if @family.next_payment_date %> <% if @family.subscription_pending_cancellation? %> @@ -25,21 +25,21 @@

<% elsif @family.trialing? %>

- Currently using the open demo of <%= product_name %>
+ <%= t(".trialing", product_name: product_name) %>
- (Data will be deleted in <%= @family.days_left_in_trial %> days) + (<%= t(".trial_days_left", count: @family.days_left_in_trial) %>)

<% else %> -

You are currently not contributing

-

Contributions to <%= product_name %> will show here.

+

<%= t(".not_contributing_prefix") %> <%= t(".not_contributing_emphasis") %>

+

<%= t(".contributions_note", product_name: product_name) %>

<% end %>
<% if @family.has_active_subscription? %> <%= render DS::Link.new( - text: "Manage", + text: t(".manage"), icon: "external-link", variant: "primary", icon_position: "right", @@ -48,7 +48,7 @@ ) %> <% else %> <%= render DS::Link.new( - text: "Choose level", + text: t(".choose_level"), variant: "primary", icon: "plus", icon_position: "right", @@ -59,7 +59,7 @@
<%= image_tag "stripe-logo.svg", class: "w-5 h-5 shrink-0" %> -

Payment via Stripe

+

<%= t(".payment_via_stripe") %>

<% end %> diff --git a/app/views/settings/providers/_enable_banking_panel.html.erb b/app/views/settings/providers/_enable_banking_panel.html.erb index 45344ea55..b73009f96 100644 --- a/app/views/settings/providers/_enable_banking_panel.html.erb +++ b/app/views/settings/providers/_enable_banking_panel.html.erb @@ -65,32 +65,32 @@ ["Sweden (SE)", "SE"], ["United Kingdom (GB)", "GB"] ], enable_banking_item.country_code), - { label: true, include_blank: "Select country..." }, - { label: "Country", class: "form-field__input" } %> + { label: true, include_blank: t("settings.providers.enable_banking_panel.select_country") }, + { label: t("settings.providers.enable_banking_panel.country_label"), class: "form-field__input" } %> <% if has_authenticated_connections && !is_new_record %> <%= render DS::Alert.new( variant: :warning, - title: "Configuration locked", - message: "Disconnect all linked banks before changing these credentials." + title: t("settings.providers.enable_banking_panel.config_locked_title"), + message: t("settings.providers.enable_banking_panel.config_locked_message") ) %> <% end %> <%= form.text_field :application_id, - label: "Application ID", - placeholder: is_new_record ? "Enter application ID" : "Enter new ID to update", + label: t("settings.providers.enable_banking_panel.application_id_label"), + placeholder: is_new_record ? t("settings.providers.enable_banking_panel.application_id_placeholder_new") : t("settings.providers.enable_banking_panel.application_id_placeholder_update"), value: enable_banking_item.application_id, disabled: has_authenticated_connections && !is_new_record %> <%= form.text_area :client_certificate, - label: "Client Certificate (with Private Key)", + label: t("settings.providers.enable_banking_panel.client_certificate_label"), placeholder: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", rows: 6, class: "form-field__input font-mono text-xs", disabled: has_authenticated_connections && !is_new_record %>
- <%= form.submit is_new_record ? "Save and connect" : "Update connection", + <%= form.submit is_new_record ? t("settings.providers.enable_banking_panel.save_and_connect") : t("settings.providers.enable_banking_panel.update_connection"), class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
<% end %> @@ -122,22 +122,22 @@ <% elsif item.session_valid? %>
-

<%= item.aspsp_name || "Connected Bank" %>

+

<%= item.aspsp_name || t("settings.providers.enable_banking_panel.connected_bank") %>

- Session expires: <%= item.session_expires_at&.strftime("%b %d, %Y") || "Unknown" %> + <%= t("settings.providers.enable_banking_panel.session_expires", date: item.session_expires_at&.strftime("%b %d, %Y") || t("settings.providers.enable_banking_panel.unknown")) %>

<% elsif item.session_expired? %>
-

<%= item.aspsp_name || "Connection" %>

-

Session expired - reconnect

+

<%= item.aspsp_name || t("settings.providers.enable_banking_panel.connection") %>

+

<%= t("settings.providers.enable_banking_panel.session_expired_reconnect") %>

<% else %>
-

Configured

-

Ready to link accounts

+

<%= t("settings.providers.enable_banking_panel.configured") %>

+

<%= t("settings.providers.enable_banking_panel.ready_to_link") %>

<% end %>
@@ -148,28 +148,28 @@ method: :post, class: "inline-flex items-center justify-center rounded-lg px-3 py-1.5 text-xs font-medium text-primary bg-container border border-primary hover:bg-surface-inset transition-colors", data: { turbo: false } do %> - Sync + <%= t("settings.providers.enable_banking_panel.sync") %> <% end %> <% elsif item.session_expired? %> <%= button_to reauthorize_enable_banking_item_path(item), method: :post, class: "inline-flex items-center justify-center rounded-lg px-3 py-1.5 text-xs font-medium text-white bg-warning hover:opacity-90 transition-colors", data: { turbo: false } do %> - Reconnect + <%= t("settings.providers.enable_banking_panel.reconnect") %> <% end %> <% else %> <%= link_to select_bank_enable_banking_item_path(item), class: "inline-flex items-center justify-center rounded-lg px-3 py-1.5 text-xs font-medium text-inverse button-bg-primary hover:button-bg-primary-hover transition-colors", data: { turbo_frame: "modal" } do %> - Connect bank + <%= t("settings.providers.enable_banking_panel.connect_bank") %> <% end %> <% end %> <%= button_to enable_banking_item_path(item), method: :delete, class: "inline-flex items-center justify-center rounded-lg px-3 py-1.5 text-xs font-medium text-destructive hover:bg-destructive/10 transition-colors", - data: { turbo_confirm: "Are you sure you want to remove this connection?" } do %> - Remove + data: { turbo_confirm: t("settings.providers.enable_banking_panel.remove_confirm") } do %> + <%= t("settings.providers.enable_banking_panel.remove") %> <% end %>
@@ -183,7 +183,7 @@ class: "inline-flex items-center gap-2 justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover transition-colors", data: { turbo_frame: "modal" } do %> <%= icon "plus", size: "sm" %> - Add Connection + <%= t("settings.providers.enable_banking_panel.add_connection") %> <% end %>
<% end %> diff --git a/app/views/settings/providers/_lunchflow_panel.html.erb b/app/views/settings/providers/_lunchflow_panel.html.erb index e4bccf2ba..9b128f8dd 100644 --- a/app/views/settings/providers/_lunchflow_panel.html.erb +++ b/app/views/settings/providers/_lunchflow_panel.html.erb @@ -27,17 +27,17 @@ data: { turbo: true }, class: "space-y-3" do |form| %> <%= form.text_field :api_key, - label: "API Key", - placeholder: is_new_record ? "Paste API key here" : "Enter new API key to update", + label: t("settings.providers.lunchflow_panel.api_key_label"), + placeholder: is_new_record ? t("settings.providers.lunchflow_panel.api_key_placeholder_new") : t("settings.providers.lunchflow_panel.api_key_placeholder_update"), type: :password %> <%= form.text_field :base_url, - label: "Base URL (Optional)", - placeholder: "https://lunchflow.app/api/v1 (default)", + label: t("settings.providers.lunchflow_panel.base_url_label"), + placeholder: t("settings.providers.lunchflow_panel.base_url_placeholder"), value: lunchflow_item.base_url %>
- <%= form.submit is_new_record ? "Save and connect" : "Update connection", + <%= form.submit is_new_record ? t("settings.providers.lunchflow_panel.save_and_connect") : t("settings.providers.lunchflow_panel.update_connection"), class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
<% end %> diff --git a/app/views/settings/providers/_simplefin_panel.html.erb b/app/views/settings/providers/_simplefin_panel.html.erb index a20b6ac4b..b169c5cd9 100644 --- a/app/views/settings/providers/_simplefin_panel.html.erb +++ b/app/views/settings/providers/_simplefin_panel.html.erb @@ -20,12 +20,12 @@ data: { turbo: true }, class: "space-y-3" do |form| %> <%= form.text_field :setup_token, - label: "Setup Token", - placeholder: "Paste SimpleFIN setup token", + label: t("settings.providers.simplefin_panel.setup_token_label"), + placeholder: t("settings.providers.simplefin_panel.setup_token_placeholder"), type: :password %>
- <%= form.submit "Save and connect", + <%= form.submit t("settings.providers.simplefin_panel.save_and_connect"), class: "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium text-inverse button-bg-primary hover:button-bg-primary-hover focus:outline-none focus:ring-2 focus:ring-gray-900 theme-dark:focus:ring-white focus:ring-offset-2 transition-colors" %>
<% end %> diff --git a/app/views/simplefin_items/edit.html.erb b/app/views/simplefin_items/edit.html.erb index f0d3e0811..51f26f39d 100644 --- a/app/views/simplefin_items/edit.html.erb +++ b/app/views/simplefin_items/edit.html.erb @@ -1,11 +1,11 @@ -<% content_for :title, "Update SimpleFin Connection" %> +<% content_for :title, t(".title") %> <%= turbo_frame_tag "modal" do %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Update SimpleFin Connection") do %> + <% dialog.with_header(title: t(".title")) do %>
<%= icon "building-2", class: "text-primary" %> - Get a new setup token to reconnect your SimpleFin account + <%= t(".header_subtitle") %>
<% end %> @@ -16,12 +16,12 @@ <%= icon "info", size: "sm", class: "text-primary mt-0.5 flex-shrink-0" %>

- Your SimpleFIN connection needs to be updated: + <%= t(".connection_needs_update") %>

    -
  1. Visit SimpleFIN Bridge to create a new setup token
  2. -
  3. Copy the token and paste it below
  4. -
  5. Click "Update" to restore access
  6. +
  7. <%= t(".step_1_html") %>
  8. +
  9. <%= t(".step_2") %>
  10. +
  11. <%= t(".step_3") %>
@@ -46,14 +46,14 @@
<%= render DS::Button.new( - text: "Update", + text: t(".update"), variant: "primary", icon: "refresh-cw", type: "submit", class: "flex-1" ) %> <%= render DS::Link.new( - text: "Cancel", + text: t(".cancel"), variant: "secondary", href: accounts_path ) %> diff --git a/app/views/simplefin_items/setup_accounts.html.erb b/app/views/simplefin_items/setup_accounts.html.erb index 81070c2e1..cf8441da3 100644 --- a/app/views/simplefin_items/setup_accounts.html.erb +++ b/app/views/simplefin_items/setup_accounts.html.erb @@ -1,10 +1,10 @@ -<% content_for :title, "Set Up SimpleFin Accounts" %> +<% content_for :title, t(".title") %> <%= render DS::Dialog.new do |dialog| %> - <% dialog.with_header(title: "Set Up Your SimpleFin Accounts") do %> + <% dialog.with_header(title: t(".title")) do %>
<%= icon "building-2", class: "text-primary" %> - Choose the correct account types for your imported accounts + <%= t(".header_subtitle") %>
<% end %> @@ -15,7 +15,7 @@ data: { controller: "loading-button", action: "submit->loading-button#showLoading", - loading_button_loading_text_value: "Creating Accounts...", + loading_button_loading_text_value: t(".creating_accounts"), turbo_frame: "_top" }, class: "space-y-6" do |form| %> @@ -26,14 +26,14 @@ <%= icon "info", size: "sm", class: "text-primary mt-0.5 flex-shrink-0" %>

- Choose the correct account type for each SimpleFin account: + <%= t(".choose_account_type") %>

    -
  • Checking or Savings - Regular bank accounts
  • -
  • Credit Card - Credit card accounts
  • -
  • Investment - Brokerage, 401(k), IRA accounts
  • -
  • Loan or Mortgage - Debt accounts
  • -
  • Other Asset - Everything else
  • +
  • <%= t(".account_type_checking_savings") %> - <%= t(".account_type_checking_savings_desc") %>
  • +
  • <%= t(".account_type_credit_card") %> - <%= t(".account_type_credit_card_desc") %>
  • +
  • <%= t(".account_type_investment") %> - <%= t(".account_type_investment_desc") %>
  • +
  • <%= t(".account_type_loan") %> - <%= t(".account_type_loan_desc") %>
  • +
  • <%= t(".account_type_other_asset") %> - <%= t(".account_type_other_asset_desc") %>
@@ -45,12 +45,10 @@ <%= icon "clock", size: "sm", class: "text-primary mt-0.5 flex-shrink-0" %>

- Transaction History: + <%= t(".transaction_history_title") %>

- SimpleFin typically provides 60-90 days of transaction history, depending on your bank. - After initial setup, new transactions will sync automatically going forward. - Historical data availability varies by institution and account type. + <%= t(".transaction_history_description_html") %>

@@ -100,7 +98,7 @@
- <%= label_tag "account_types[#{simplefin_account.id}]", "Account Type:", + <%= label_tag "account_types[#{simplefin_account.id}]", t(".account_type_label"), class: "block text-sm font-medium text-primary mb-2" %> <%= select_tag "account_types[#{simplefin_account.id}]", options_for_select(@account_type_options, selected_type), @@ -146,7 +144,7 @@
<%= render DS::Button.new( - text: "Create Accounts", + text: t(".create_accounts"), variant: "primary", icon: "plus", type: "submit", @@ -154,7 +152,7 @@ data: { loading_button_target: "button" } ) %> <%= render DS::Link.new( - text: "Cancel", + text: t(".cancel"), variant: "secondary", href: accounts_path ) %> diff --git a/app/views/subscriptions/upgrade.html.erb b/app/views/subscriptions/upgrade.html.erb index 0c600cf99..fd7d49936 100644 --- a/app/views/subscriptions/upgrade.html.erb +++ b/app/views/subscriptions/upgrade.html.erb @@ -1,17 +1,17 @@