mirror of
https://github.com/we-promise/sure
synced 2026-04-25 17:15:07 +02:00
Provider optimisation (#375)
* Implement a provider configured account type * Fix for SimpleFIN * FIX tests and linter
This commit is contained in:
@@ -56,7 +56,10 @@ class AccountsController < ApplicationController
|
||||
end
|
||||
|
||||
def new
|
||||
@show_lunchflow_link = family.can_connect_lunchflow?
|
||||
# Get all registered providers with any credentials configured
|
||||
@provider_configs = Provider::Factory.registered_adapters.flat_map do |adapter_class|
|
||||
adapter_class.connection_configs(family: family)
|
||||
end
|
||||
end
|
||||
|
||||
def sync_all
|
||||
@@ -158,45 +161,21 @@ class AccountsController < ApplicationController
|
||||
return
|
||||
end
|
||||
|
||||
@available_providers = []
|
||||
account_type_name = @account.accountable_type
|
||||
|
||||
# Check SimpleFIN
|
||||
if family.can_connect_simplefin?
|
||||
@available_providers << {
|
||||
name: "SimpleFIN",
|
||||
key: "simplefin",
|
||||
description: "Connect to your bank via SimpleFIN",
|
||||
path: select_existing_account_simplefin_items_path(account_id: @account.id)
|
||||
}
|
||||
end
|
||||
# Get all available provider configs dynamically for this account type
|
||||
provider_configs = Provider::Factory.connection_configs_for_account_type(
|
||||
account_type: account_type_name,
|
||||
family: family
|
||||
)
|
||||
|
||||
# Check Plaid US
|
||||
if family.can_connect_plaid_us?
|
||||
@available_providers << {
|
||||
name: "Plaid",
|
||||
key: "plaid_us",
|
||||
description: "Connect to your US bank via Plaid",
|
||||
path: select_existing_account_plaid_items_path(account_id: @account.id, region: "us")
|
||||
}
|
||||
end
|
||||
|
||||
# Check Plaid EU
|
||||
if family.can_connect_plaid_eu?
|
||||
@available_providers << {
|
||||
name: "Plaid (EU)",
|
||||
key: "plaid_eu",
|
||||
description: "Connect to your EU bank via Plaid",
|
||||
path: select_existing_account_plaid_items_path(account_id: @account.id, region: "eu")
|
||||
}
|
||||
end
|
||||
|
||||
# Check Lunch Flow
|
||||
if family.can_connect_lunchflow?
|
||||
@available_providers << {
|
||||
name: "Lunch Flow",
|
||||
key: "lunchflow",
|
||||
description: "Connect to your bank via Lunch Flow",
|
||||
path: select_existing_account_lunchflow_items_path(account_id: @account.id)
|
||||
# Build available providers list with paths resolved for this specific account
|
||||
@available_providers = provider_configs.map do |config|
|
||||
{
|
||||
name: config[:name],
|
||||
key: config[:key],
|
||||
description: config[:description],
|
||||
path: config[:existing_account_path].call(@account.id)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -66,9 +66,13 @@ module AccountableResource
|
||||
|
||||
private
|
||||
def set_link_options
|
||||
@show_us_link = Current.family.can_connect_plaid_us?
|
||||
@show_eu_link = Current.family.can_connect_plaid_eu?
|
||||
@show_lunchflow_link = Current.family.can_connect_lunchflow?
|
||||
account_type_name = accountable_type.name
|
||||
|
||||
# Get all available provider configs dynamically for this account type
|
||||
@provider_configs = Provider::Factory.connection_configs_for_account_type(
|
||||
account_type: account_type_name,
|
||||
family: Current.family
|
||||
)
|
||||
end
|
||||
|
||||
def accountable_type
|
||||
|
||||
@@ -31,6 +31,27 @@ class Provider::Base
|
||||
raise NotImplementedError, "#{self.class} must implement #provider_name"
|
||||
end
|
||||
|
||||
# Defines which account types this provider supports
|
||||
# Override in subclasses to specify supported account types
|
||||
# @return [Array<String>] Array of account type class names (e.g., ["Depository", "CreditCard"])
|
||||
def self.supported_account_types
|
||||
[]
|
||||
end
|
||||
|
||||
# Returns provider connection configurations
|
||||
# Override in subclasses to provide connection metadata for UI
|
||||
# @param family [Family] The family to check connection availability for
|
||||
# @return [Array<Hash>] Array of connection configurations with keys:
|
||||
# - key: Unique identifier (e.g., "lunchflow", "plaid_us")
|
||||
# - name: Display name (e.g., "Lunch Flow", "Plaid")
|
||||
# - description: User-facing description
|
||||
# - can_connect: Boolean, whether family can connect to this provider
|
||||
# - new_account_path: Proc that generates path for new account flow
|
||||
# - existing_account_path: Proc that generates path for linking existing account
|
||||
def self.connection_configs(family:)
|
||||
[]
|
||||
end
|
||||
|
||||
# Returns the provider type (class name)
|
||||
# @return [String] The provider account class name
|
||||
def provider_type
|
||||
|
||||
@@ -63,6 +63,39 @@ class Provider::Factory
|
||||
find_adapter_class(provider_type).present?
|
||||
end
|
||||
|
||||
# Get all registered adapter classes
|
||||
# @return [Array<Class>] List of registered adapter classes
|
||||
def registered_adapters
|
||||
ensure_adapters_loaded
|
||||
registry.values.uniq
|
||||
end
|
||||
|
||||
# Get adapters that support a specific account type
|
||||
# @param account_type [String] The account type class name (e.g., "Depository", "CreditCard")
|
||||
# @return [Array<Class>] List of adapter classes that support this account type
|
||||
def adapters_for_account_type(account_type)
|
||||
registered_adapters.select do |adapter_class|
|
||||
adapter_class.supported_account_types.include?(account_type)
|
||||
end
|
||||
end
|
||||
|
||||
# Check if any provider supports a given account type
|
||||
# @param account_type [String] The account type class name
|
||||
# @return [Boolean]
|
||||
def supports_account_type?(account_type)
|
||||
adapters_for_account_type(account_type).any?
|
||||
end
|
||||
|
||||
# Get all available provider connection configs for a given account type
|
||||
# @param account_type [String] The account type class name (e.g., "Depository")
|
||||
# @param family [Family] The family to check connection availability for
|
||||
# @return [Array<Hash>] Array of connection configurations from all providers
|
||||
def connection_configs_for_account_type(account_type:, family:)
|
||||
adapters_for_account_type(account_type).flat_map do |adapter_class|
|
||||
adapter_class.connection_configs(family: family)
|
||||
end
|
||||
end
|
||||
|
||||
# Clear all registered adapters (useful for testing)
|
||||
def clear_registry!
|
||||
@registry = {}
|
||||
|
||||
@@ -5,6 +5,34 @@ class Provider::LunchflowAdapter < Provider::Base
|
||||
# Register this adapter with the factory
|
||||
Provider::Factory.register("LunchflowAccount", self)
|
||||
|
||||
# Define which account types this provider supports
|
||||
def self.supported_account_types
|
||||
%w[Depository CreditCard Loan]
|
||||
end
|
||||
|
||||
# Returns connection configurations for this provider
|
||||
def self.connection_configs(family:)
|
||||
return [] unless family.can_connect_lunchflow?
|
||||
|
||||
[ {
|
||||
key: "lunchflow",
|
||||
name: "Lunch Flow",
|
||||
description: "Connect to your bank via Lunch Flow",
|
||||
can_connect: true,
|
||||
new_account_path: ->(accountable_type, return_to) {
|
||||
Rails.application.routes.url_helpers.select_accounts_lunchflow_items_path(
|
||||
accountable_type: accountable_type,
|
||||
return_to: return_to
|
||||
)
|
||||
},
|
||||
existing_account_path: ->(account_id) {
|
||||
Rails.application.routes.url_helpers.select_existing_account_lunchflow_items_path(
|
||||
account_id: account_id
|
||||
)
|
||||
}
|
||||
} ]
|
||||
end
|
||||
|
||||
def provider_name
|
||||
"lunchflow"
|
||||
end
|
||||
|
||||
@@ -17,6 +17,63 @@ class Provider::PlaidAdapter < Provider::Base
|
||||
# Register this adapter with the factory for ALL PlaidAccount instances
|
||||
Provider::Factory.register("PlaidAccount", self)
|
||||
|
||||
# Define which account types this provider supports (US region)
|
||||
def self.supported_account_types
|
||||
%w[Depository CreditCard Loan Investment]
|
||||
end
|
||||
|
||||
# Returns connection configurations for this provider
|
||||
# Plaid can return multiple configs (US and EU) depending on family setup
|
||||
def self.connection_configs(family:)
|
||||
configs = []
|
||||
|
||||
# US configuration
|
||||
if family.can_connect_plaid_us?
|
||||
configs << {
|
||||
key: "plaid_us",
|
||||
name: "Plaid",
|
||||
description: "Connect to your US bank via Plaid",
|
||||
can_connect: true,
|
||||
new_account_path: ->(accountable_type, return_to) {
|
||||
Rails.application.routes.url_helpers.new_plaid_item_path(
|
||||
region: "us",
|
||||
accountable_type: accountable_type
|
||||
)
|
||||
},
|
||||
existing_account_path: ->(account_id) {
|
||||
Rails.application.routes.url_helpers.select_existing_account_plaid_items_path(
|
||||
account_id: account_id,
|
||||
region: "us"
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# EU configuration
|
||||
if family.can_connect_plaid_eu?
|
||||
configs << {
|
||||
key: "plaid_eu",
|
||||
name: "Plaid (EU)",
|
||||
description: "Connect to your EU bank via Plaid",
|
||||
can_connect: true,
|
||||
new_account_path: ->(accountable_type, return_to) {
|
||||
Rails.application.routes.url_helpers.new_plaid_item_path(
|
||||
region: "eu",
|
||||
accountable_type: accountable_type
|
||||
)
|
||||
},
|
||||
existing_account_path: ->(account_id) {
|
||||
Rails.application.routes.url_helpers.select_existing_account_plaid_items_path(
|
||||
account_id: account_id,
|
||||
region: "eu"
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
configs
|
||||
end
|
||||
|
||||
# Mutex for thread-safe configuration loading
|
||||
# Initialized at class load time to avoid race conditions on mutex creation
|
||||
@config_mutex = Mutex.new
|
||||
|
||||
@@ -5,6 +5,33 @@ class Provider::SimplefinAdapter < Provider::Base
|
||||
# Register this adapter with the factory
|
||||
Provider::Factory.register("SimplefinAccount", self)
|
||||
|
||||
# Define which account types this provider supports
|
||||
def self.supported_account_types
|
||||
%w[Depository CreditCard Loan Investment]
|
||||
end
|
||||
|
||||
# Returns connection configurations for this provider
|
||||
def self.connection_configs(family:)
|
||||
return [] unless family.can_connect_simplefin?
|
||||
|
||||
[ {
|
||||
key: "simplefin",
|
||||
name: "SimpleFIN",
|
||||
description: "Connect to your bank via SimpleFIN",
|
||||
can_connect: true,
|
||||
new_account_path: ->(accountable_type, return_to) {
|
||||
Rails.application.routes.url_helpers.new_simplefin_item_path(
|
||||
accountable_type: accountable_type
|
||||
)
|
||||
},
|
||||
existing_account_path: ->(account_id) {
|
||||
Rails.application.routes.url_helpers.select_existing_account_simplefin_items_path(
|
||||
account_id: account_id
|
||||
)
|
||||
}
|
||||
} ]
|
||||
end
|
||||
|
||||
def provider_name
|
||||
"simplefin"
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= render layout: "accounts/new/container", locals: { title: t(".title") } do %>
|
||||
<div class="text-sm"
|
||||
<% if @show_lunchflow_link %>
|
||||
<% if @provider_configs.any? { |c| c[:key] == "lunchflow" } %>
|
||||
data-controller="lunchflow-preload"
|
||||
<% end %>>
|
||||
<% unless params[:classification] == "liability" %>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<%# locals: (path:, accountable_type:, show_us_link: true, show_eu_link: true, show_lunchflow_link: false) %>
|
||||
<%# locals: (path:, accountable_type:, provider_configs:) %>
|
||||
|
||||
<%= render layout: "accounts/new/container", locals: { title: t(".title"), back_path: new_account_path } do %>
|
||||
<div class="text-sm"
|
||||
<% if show_lunchflow_link %>
|
||||
<% if provider_configs.any? { |c| c[:key] == "lunchflow" } %>
|
||||
data-controller="lunchflow-preload"
|
||||
data-lunchflow-preload-accountable-type-value="<%= h(accountable_type) %>"
|
||||
<% if params[:return_to] %>
|
||||
data-lunchflow-preload-return-to-value="<%= h(params[:return_to]) %>"
|
||||
<% end %>
|
||||
<% end %>>
|
||||
<%# Manual entry option %>
|
||||
<%= link_to path, class: "flex items-center gap-4 w-full text-center text-primary focus:outline-hidden focus:bg-surface border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-surface rounded-lg p-2" do %>
|
||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||
<%= icon("keyboard") %>
|
||||
@@ -16,48 +17,31 @@
|
||||
<%= t("accounts.new.method_selector.manual_entry") %>
|
||||
<% end %>
|
||||
|
||||
<% if show_us_link %>
|
||||
<%# Default US-only Link %>
|
||||
<%= link_to new_plaid_item_path(region: "us", accountable_type: accountable_type),
|
||||
class: "text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-surface border border-transparent focus:border focus:border-primary px-2 hover:bg-surface rounded-lg p-2",
|
||||
data: { turbo_frame: "modal" } do %>
|
||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||
<%= icon("link-2") %>
|
||||
</span>
|
||||
<%= t("accounts.new.method_selector.connected_entry") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%# Dynamic provider links %>
|
||||
<% provider_configs.each do |config| %>
|
||||
<% link_path = config[:new_account_path].call(accountable_type, params[:return_to]) %>
|
||||
<% is_lunchflow = config[:key] == "lunchflow" %>
|
||||
|
||||
<%# EU Link %>
|
||||
<% if show_eu_link %>
|
||||
<%= link_to new_plaid_item_path(region: "eu", accountable_type: accountable_type),
|
||||
<%= link_to link_path,
|
||||
class: "text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-surface border border-transparent focus:border focus:border-primary px-2 hover:bg-surface rounded-lg p-2",
|
||||
data: { turbo_frame: "modal" } do %>
|
||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||
<%= icon("link-2") %>
|
||||
</span>
|
||||
<%= t("accounts.new.method_selector.connected_entry_eu") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%# Lunchflow Link %>
|
||||
<% if show_lunchflow_link %>
|
||||
<%= link_to select_accounts_lunchflow_items_path(accountable_type: accountable_type, return_to: params[:return_to]),
|
||||
class: "text-primary flex items-center gap-4 w-full text-center focus:outline-hidden focus:bg-surface border border-transparent focus:border focus:border-primary px-2 hover:bg-surface rounded-lg p-2",
|
||||
data: {
|
||||
data: is_lunchflow ? {
|
||||
turbo_frame: "modal",
|
||||
turbo_action: "advance",
|
||||
lunchflow_preload_target: "link"
|
||||
} do %>
|
||||
} : { turbo_frame: "modal" } do %>
|
||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||
<%= icon("link-2") %>
|
||||
</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<%= t("accounts.new.method_selector.lunchflow_entry") %>
|
||||
<span data-lunchflow-preload-target="spinner" class="hidden">
|
||||
<%= icon("loader-2", class: "animate-spin") %>
|
||||
<% if is_lunchflow %>
|
||||
<span class="flex items-center gap-2">
|
||||
<%= t("accounts.new.method_selector.link_with_provider", provider: config[:name]) %>
|
||||
<span data-lunchflow-preload-target="spinner" class="hidden">
|
||||
<%= icon("loader-2", class: "animate-spin") %>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= t("accounts.new.method_selector.link_with_provider", provider: config[:name]) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<% if params[:step] == "method_select" %>
|
||||
<%= render "accounts/new/method_selector",
|
||||
path: new_credit_card_path(return_to: params[:return_to]),
|
||||
show_us_link: @show_us_link,
|
||||
show_eu_link: @show_eu_link,
|
||||
show_lunchflow_link: @show_lunchflow_link,
|
||||
provider_configs: @provider_configs,
|
||||
accountable_type: "CreditCard" %>
|
||||
<% else %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<% if params[:step] == "method_select" %>
|
||||
<%= render "accounts/new/method_selector",
|
||||
path: new_crypto_path(return_to: params[:return_to]),
|
||||
show_us_link: @show_us_link,
|
||||
show_eu_link: @show_eu_link,
|
||||
provider_configs: @provider_configs,
|
||||
accountable_type: "Crypto" %>
|
||||
<% else %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<% if params[:step] == "method_select" %>
|
||||
<%= render "accounts/new/method_selector",
|
||||
path: new_depository_path(return_to: params[:return_to]),
|
||||
show_us_link: @show_us_link,
|
||||
show_eu_link: @show_eu_link,
|
||||
show_lunchflow_link: @show_lunchflow_link,
|
||||
provider_configs: @provider_configs,
|
||||
accountable_type: "Depository" %>
|
||||
<% else %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<% if params[:step] == "method_select" %>
|
||||
<%= render "accounts/new/method_selector",
|
||||
path: new_investment_path(return_to: params[:return_to]),
|
||||
show_us_link: @show_us_link,
|
||||
show_eu_link: @show_eu_link,
|
||||
provider_configs: @provider_configs,
|
||||
accountable_type: "Investment" %>
|
||||
<% else %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<% if params[:step] == "method_select" %>
|
||||
<%= render "accounts/new/method_selector",
|
||||
path: new_loan_path(return_to: params[:return_to]),
|
||||
show_us_link: @show_us_link,
|
||||
show_eu_link: @show_eu_link,
|
||||
show_lunchflow_link: @show_lunchflow_link,
|
||||
provider_configs: @provider_configs,
|
||||
accountable_type: "Loan" %>
|
||||
<% else %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<header class="flex items-center gap-2 mb-4">
|
||||
<h1 class="text-xl text-primary font-medium">Connect SimpleFin</h1>
|
||||
</header>
|
||||
<%= turbo_frame_tag "modal" do %>
|
||||
<%= render DS::Dialog.new do |dialog| %>
|
||||
<% dialog.with_header(title: t(".title")) %>
|
||||
|
||||
<% if @error_message.present? %>
|
||||
<div class="mb-4 p-3 rounded-md bg-destructive/10 text-destructive text-sm">
|
||||
<%= @error_message %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% dialog.with_body do %>
|
||||
<% if @error_message.present? %>
|
||||
<div class="mb-4 p-3 rounded-md bg-destructive/10 text-destructive text-sm">
|
||||
<%= @error_message %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="bg-container p-4 rounded-lg shadow-border-xs">
|
||||
<%= form_with model: @simplefin_item, url: simplefin_items_path, method: :post, data: { turbo: true } do |f| %>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<%= f.label :setup_token, "Setup token", class: "text-sm text-secondary block mb-1" %>
|
||||
<%= f.text_field :setup_token, class: "input", placeholder: "paste your SimpleFin setup token" %>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= f.submit "Connect", class: "btn btn--primary" %>
|
||||
<%= link_to "Cancel", accounts_path, class: "btn" %>
|
||||
</div>
|
||||
</div>
|
||||
<%= form_with model: @simplefin_item, url: simplefin_items_path, method: :post, data: { turbo: true, turbo_frame: "_top" } do |f| %>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<%= f.label :setup_token, t(".setup_token"), class: "text-sm text-secondary block mb-1" %>
|
||||
<%= f.text_field :setup_token, class: "input", placeholder: t(".setup_token_placeholder") %>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 justify-end pt-2">
|
||||
<%= link_to t(".cancel"), accounts_path, class: "btn", data: { turbo_frame: "_top", action: "DS--dialog#close" } %>
|
||||
<%= f.submit t(".connect"), class: "btn btn--primary" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -28,6 +28,7 @@ ca:
|
||||
method_selector:
|
||||
connected_entry: Enllaça el compte
|
||||
connected_entry_eu: Enllaça el compte de la UE
|
||||
link_with_provider: "Enllaça amb %{provider}"
|
||||
manual_entry: Introdueix el saldo del compte
|
||||
title: Com vols afegir-lo?
|
||||
title: Què vols afegir?
|
||||
|
||||
@@ -28,6 +28,7 @@ de:
|
||||
method_selector:
|
||||
connected_entry: Konto verknüpfen
|
||||
connected_entry_eu: EU-Konto verknüpfen
|
||||
link_with_provider: "Mit %{provider} verknüpfen"
|
||||
manual_entry: Kontostand manuell eingeben
|
||||
title: Wie möchtest du es hinzufügen
|
||||
title: Was möchtest du hinzufügen
|
||||
|
||||
@@ -34,6 +34,7 @@ en:
|
||||
method_selector:
|
||||
connected_entry: Link account
|
||||
connected_entry_eu: Link EU account
|
||||
link_with_provider: "Link with %{provider}"
|
||||
lunchflow_entry: Link Lunch Flow account
|
||||
manual_entry: Enter account balance
|
||||
title: How would you like to add it?
|
||||
|
||||
@@ -29,6 +29,7 @@ es:
|
||||
method_selector:
|
||||
connected_entry: Vincular cuenta
|
||||
connected_entry_eu: Vincular cuenta de la UE
|
||||
link_with_provider: "Vincular con %{provider}"
|
||||
lunchflow_entry: Vincular cuenta de Lunch Flow
|
||||
manual_entry: Introducir saldo de la cuenta
|
||||
title: ¿Cómo te gustaría añadirla?
|
||||
|
||||
@@ -28,6 +28,7 @@ nb:
|
||||
method_selector:
|
||||
connected_entry: Koble til konto
|
||||
connected_entry_eu: Koble til EU-konto
|
||||
link_with_provider: "Koble til %{provider}"
|
||||
manual_entry: Angi kontobalanse
|
||||
title: Hvordan vil du legge den til?
|
||||
title: Hva vil du legge til?
|
||||
|
||||
@@ -29,6 +29,7 @@ ro:
|
||||
method_selector:
|
||||
connected_entry: Conectează cont
|
||||
connected_entry_eu: Conectează cont UE
|
||||
link_with_provider: "Conectează cu %{provider}"
|
||||
lunchflow_entry: Conectează cont Lunch Flow
|
||||
manual_entry: Introdu soldul contului
|
||||
title: Cum dorești să adaugi?
|
||||
|
||||
@@ -28,6 +28,7 @@ tr:
|
||||
method_selector:
|
||||
connected_entry: Hesabı bağla
|
||||
connected_entry_eu: AB hesabı bağla
|
||||
link_with_provider: "%{provider} ile bağla"
|
||||
manual_entry: Hesap bakiyesi gir
|
||||
title: Nasıl eklemek istersiniz?
|
||||
title: Ne eklemek istersiniz?
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
ca:
|
||||
simplefin_items:
|
||||
new:
|
||||
title: Connecta SimpleFin
|
||||
setup_token: Token de configuració
|
||||
setup_token_placeholder: enganxa el teu token de configuració SimpleFin
|
||||
connect: Connecta
|
||||
cancel: Cancel·la
|
||||
create:
|
||||
success: Connexió SimpleFin afegida correctament! Els teus comptes apareixeran en breu mentre es sincronitzen en segon pla.
|
||||
errors:
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
de:
|
||||
simplefin_items:
|
||||
new:
|
||||
title: SimpleFin verbinden
|
||||
setup_token: Setup-Token
|
||||
setup_token_placeholder: Füge dein SimpleFin-Setup-Token ein
|
||||
connect: Verbinden
|
||||
cancel: Abbrechen
|
||||
create:
|
||||
success: SimpleFin-Verbindung erfolgreich hinzugefügt! Deine Konten werden in Kürze angezeigt, sobald sie im Hintergrund synchronisiert wurden.
|
||||
errors:
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
en:
|
||||
simplefin_items:
|
||||
new:
|
||||
title: Connect SimpleFin
|
||||
setup_token: Setup token
|
||||
setup_token_placeholder: paste your SimpleFin setup token
|
||||
connect: Connect
|
||||
cancel: Cancel
|
||||
create:
|
||||
success: SimpleFin connection added successfully! Your accounts will appear shortly as they sync in the background.
|
||||
errors:
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
es:
|
||||
simplefin_items:
|
||||
new:
|
||||
title: Conectar SimpleFin
|
||||
setup_token: Token de configuración
|
||||
setup_token_placeholder: pega tu token de configuración de SimpleFin
|
||||
connect: Conectar
|
||||
cancel: Cancelar
|
||||
create:
|
||||
success: ¡Conexión SimpleFin añadida con éxito! Tus cuentas aparecerán en breve mientras se sincronizan en segundo plano.
|
||||
errors:
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
ro:
|
||||
simplefin_items:
|
||||
new:
|
||||
title: Conectează SimpleFin
|
||||
setup_token: Token de configurare
|
||||
setup_token_placeholder: lipește token-ul tău de configurare SimpleFin
|
||||
connect: Conectează
|
||||
cancel: Anulează
|
||||
create:
|
||||
success: Conexiunea SimpleFin a fost adăugată cu succes! Conturile tale vor apărea în scurt timp pe măsură ce se sincronizează în fundal.
|
||||
errors:
|
||||
|
||||
Reference in New Issue
Block a user