feat(i18n): extract hardcoded English strings to locale files (#1806)

* Extract hardcoded strings to i18n

Replace numerous hardcoded English strings with I18n lookups (t / I18n.t) across controllers, views, helpers, and components, and convert model validation error messages to symbol keys. Added multiple locale files under config/locales for models and views. This centralizes user-facing notices/alerts, UI text, import/validation messages, and prepares the app for localization and easier translation maintenance.

* Update en.yml

* Update preview-cleanup.yml

* Revert "Update preview-cleanup.yml"

This reverts commit 1ba6d3c34c.

* test: align i18n assertions with translated messages

* Standardize balance error key and tweak locales

Replace SophtronAccount's :requires_balance error key with :no_balance and update related locale strings for sophtron, plaid, and simplefin accounts to use the new key and clearer copy. Also switch the QIF upload redirect notice to use a relative translation key (t('.qif_uploaded')), remove an unused SSO providers help line, and fix a trailing-newline/whitespace issue in the subscriptions locale. These changes standardize validation keys and improve translation consistency and messaging.

---------

Co-authored-by: KiloClaw <kiloclaw@openclaw.ai>
This commit is contained in:
Brendon Scheiber
2026-05-17 09:52:49 +02:00
committed by GitHub
co-authored by KiloClaw
parent d74b1b2a11
commit 0c126b1674
201 changed files with 1845 additions and 835 deletions
+32 -32
View File
@@ -6,7 +6,7 @@
<%= icon "alert-circle", class: "w-5 h-5 text-destructive mr-2 shrink-0" %>
<div>
<p class="text-sm font-medium text-destructive">
<%= pluralize(sso_provider.errors.count, "error") %> prohibited this provider from being saved:
<%= t("admin.sso_providers.form.errors_title", count: sso_provider.errors.count) %>
</p>
<ul class="mt-2 text-sm text-destructive list-disc list-inside">
<% sso_provider.errors.full_messages.each do |message| %>
@@ -20,38 +20,38 @@
<%= styled_form_with model: [:admin, sso_provider], class: "space-y-6", data: { controller: "admin-sso-form" } do |form| %>
<div class="space-y-4">
<h3 class="font-medium text-primary">Basic Information</h3>
<h3 class="font-medium text-primary"><%= t("admin.sso_providers.form.basic_information") %></h3>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<%= form.select :strategy,
options_for_select([
["OpenID Connect", "openid_connect"],
["SAML 2.0", "saml"],
["Google OAuth2", "google_oauth2"],
["GitHub", "github"]
[t("admin.sso_providers.form.strategy_openid_connect"), "openid_connect"],
[t("admin.sso_providers.form.strategy_saml"), "saml"],
[t("admin.sso_providers.form.strategy_google_oauth2"), "google_oauth2"],
[t("admin.sso_providers.form.strategy_github"), "github"]
], sso_provider.strategy),
{ label: "Strategy" },
{ label: t("admin.sso_providers.form.strategy_label") },
{ data: { action: "change->admin-sso-form#toggleFields" } } %>
<%= form.text_field :name,
label: "Name",
placeholder: "e.g., keycloak, authentik",
label: t("admin.sso_providers.form.name_label"),
placeholder: t("admin.sso_providers.form.name_placeholder"),
required: true,
data: { action: "input->admin-sso-form#updateCallbackUrl" } %>
</div>
<p class="text-xs text-secondary -mt-2">Unique identifier (lowercase, numbers, underscores only)</p>
<p class="text-xs text-secondary -mt-2"><%= t("admin.sso_providers.form.name_help") %></p>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<%= form.text_field :label,
label: "Button Label",
placeholder: "e.g., Sign in with Keycloak",
label: t("admin.sso_providers.form.label_label"),
placeholder: t("admin.sso_providers.form.label_placeholder"),
required: true %>
<div>
<%= form.text_field :icon,
label: "Icon (optional)",
placeholder: "e.g., key, shield" %>
<p class="text-xs text-secondary mt-1">Lucide icon name for the login button</p>
label: t("admin.sso_providers.form.icon_label"),
placeholder: t("admin.sso_providers.form.icon_placeholder") %>
<p class="text-xs text-secondary mt-1"><%= t("admin.sso_providers.form.icon_help") %></p>
</div>
</div>
@@ -65,42 +65,42 @@
</div>
<div class="border-t border-primary pt-4 space-y-4">
<h3 class="font-medium text-primary">OAuth/OIDC Configuration</h3>
<h3 class="font-medium text-primary"><%= t("admin.sso_providers.form.oauth_configuration") %></h3>
<div data-oidc-field class="<%= "hidden" unless sso_provider.strategy == "openid_connect" %>">
<%= form.text_field :issuer,
label: "Issuer URL",
placeholder: "https://your-idp.example.com/realms/your-realm",
label: t("admin.sso_providers.form.issuer_label"),
placeholder: t("admin.sso_providers.form.issuer_placeholder"),
data: { action: "blur->admin-sso-form#validateIssuer" } %>
<p class="text-xs text-secondary mt-1">OIDC issuer URL (validates .well-known/openid-configuration)</p>
<p class="text-xs text-secondary mt-1"><%= t("admin.sso_providers.form.issuer_help") %></p>
</div>
<%= form.text_field :client_id,
label: "Client ID",
placeholder: "your-client-id",
label: t("admin.sso_providers.form.client_id_label"),
placeholder: t("admin.sso_providers.form.client_id_placeholder"),
required: true %>
<%= form.password_field :client_secret,
label: "Client Secret",
placeholder: sso_provider.persisted? ? "••••••••" : "your-client-secret",
label: t("admin.sso_providers.form.client_secret_label"),
placeholder: sso_provider.persisted? ? t("admin.sso_providers.form.client_secret_placeholder_existing") : t("admin.sso_providers.form.client_secret_placeholder_new"),
required: !sso_provider.persisted? %>
<% if sso_provider.persisted? %>
<p class="text-xs text-secondary -mt-2">Leave blank to keep existing secret</p>
<p class="text-xs text-secondary -mt-2"><%= t("admin.sso_providers.form.client_secret_help_existing") %></p>
<% end %>
<div data-oidc-field class="<%= "hidden" unless sso_provider.strategy == "openid_connect" %>">
<label class="block text-sm font-medium text-primary mb-1">Callback URL</label>
<label class="block text-sm font-medium text-primary mb-1"><%= t("admin.sso_providers.form.redirect_uri_label") %></label>
<div class="flex items-center gap-2">
<code class="flex-1 bg-surface px-3 py-2 rounded text-sm text-secondary overflow-x-auto"
data-admin-sso-form-target="callbackUrl"><%= "#{request.base_url}/auth/#{sso_provider.name.presence || 'PROVIDER_NAME'}/callback" %></code>
<button type="button"
data-action="click->admin-sso-form#copyCallback"
class="p-2 text-secondary hover:text-primary shrink-0"
title="Copy to clipboard">
title="<%= t("admin.sso_providers.form.copy_button") %>">
<%= icon "copy", class: "w-4 h-4" %>
</button>
</div>
<p class="text-xs text-secondary mt-1">Configure this URL in your identity provider</p>
<p class="text-xs text-secondary mt-1"><%= t("admin.sso_providers.form.redirect_uri_help") %></p>
</div>
</div>
@@ -172,18 +172,18 @@
</details>
<div>
<label class="block text-sm font-medium text-primary mb-1">SP Callback URL (ACS URL)</label>
<label class="block text-sm font-medium text-primary mb-1"><%= t("admin.sso_providers.form.saml_sp_callback_url_label") %></label>
<div class="flex items-center gap-2">
<code class="flex-1 bg-surface px-3 py-2 rounded text-sm text-secondary overflow-x-auto"
data-admin-sso-form-target="samlCallbackUrl"><%= "#{request.base_url}/auth/#{sso_provider.name.presence || 'PROVIDER_NAME'}/callback" %></code>
<button type="button"
data-action="click->admin-sso-form#copySamlCallback"
class="p-2 text-secondary hover:text-primary shrink-0"
title="Copy to clipboard">
title="<%= t("admin.sso_providers.form.copy_button") %>">
<%= icon "copy", class: "w-4 h-4" %>
</button>
</div>
<p class="text-xs text-secondary mt-1">Configure this URL as the Assertion Consumer Service URL in your IdP</p>
<p class="text-xs text-secondary mt-1"><%= t("admin.sso_providers.form.saml_sp_callback_url_help") %></p>
</div>
</div>
@@ -282,8 +282,8 @@
</div>
<div class="flex gap-3">
<%= 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" %>
</div>
</div>