diff --git a/.cursor/rules/general-rules.mdc b/.cursor/rules/general-rules.mdc index efe32cd30..f9f053759 100644 --- a/.cursor/rules/general-rules.mdc +++ b/.cursor/rules/general-rules.mdc @@ -11,7 +11,6 @@ alwaysApply: true - Read [project-design.mdc](mdc:.cursor/rules/project-design.mdc) to understand the codebase - Read [project-conventions.mdc](mdc:.cursor/rules/project-conventions.mdc) to understand _how_ to write code for the codebase - Read [ui-ux-design-guidelines.mdc](mdc:.cursor/rules/ui-ux-design-guidelines.mdc) to understand how to implement frontend code specifically -- Ignore i18n methods and files. Hardcode strings in English for now to optimize speed of development. - ActiveRecord migrations must inherit from `ActiveRecord::Migration[7.2]`. Do **not** use version 8.0 yet. ## Prohibited actions diff --git a/CLAUDE.md b/CLAUDE.md index 8f0e2de7f..575ba82c5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,8 +56,7 @@ Only proceed with pull request creation if ALL checks pass. - Use `Current.family` for the current family. Do NOT use `current_family`. ### Development Guidelines -- Prior to generating any code, carefully read the project conventions and guidelines -- Ignore i18n methods and files. Hardcode strings in English for now to optimize speed of development +- Carefully read project conventions and guidelines before generating any code. - Do not run `rails server` in your responses - Do not run `touch tmp/restart.txt` - Do not run `rails credentials` @@ -112,6 +111,15 @@ Sidekiq handles asynchronous tasks: - Always use functional tokens (e.g., `text-primary` not `text-white`) - Prefer semantic HTML elements over JS components - Use `icon` helper for icons, never `lucide_icon` directly +- **i18n**: All user-facing strings must use localization (i18n). Update locale files for each new or changed element. + +### Internationalization (i18n) Guidelines +- **Key Organization**: Use hierarchical keys by feature: `accounts.index.title`, `transactions.form.amount_label` +- **Translation Helper**: Always use `t()` helper for user-facing strings +- **Interpolation**: Use for dynamic content: `t("users.greeting", name: user.name)` +- **Pluralization**: Use Rails pluralization: `t("transactions.count", count: @transactions.count)` +- **Locale Files**: Update `config/locales/en.yml` for new strings +- **Missing Translations**: Configure to raise errors in development for missing keys ### Multi-Currency Support - All monetary values stored in base currency (user's primary currency) @@ -220,11 +228,36 @@ Sidekiq handles asynchronous tasks: ```erb
- - + +
``` +**Example locale file structure (config/locales/en.yml):** +```yaml +en: + components: + transaction_details: + show_details: "Show Details" + hide_details: "Hide Details" + amount_label: "Amount" + date_label: "Date" + category_label: "Category" +``` + +**i18n Best Practices:** +- Organize keys by feature/component: `components.transaction_details.show_details` +- Use descriptive key names that indicate purpose: `show_details` not `button` +- Group related translations together in the same namespace +- Use interpolation for dynamic content: `t("users.welcome", name: user.name)` +- Always update locale files when adding new user-facing strings + **Controller Best Practices:** - Keep controllers lightweight and simple (< 7 targets) - Use private methods and expose clear public API diff --git a/app/views/pages/dashboard.html.erb b/app/views/pages/dashboard.html.erb index 3951d6106..f67237d48 100644 --- a/app/views/pages/dashboard.html.erb +++ b/app/views/pages/dashboard.html.erb @@ -1,13 +1,17 @@ <% content_for :page_header do %>
-

Welcome back, <%= Current.user.first_name %>

-

Here's what's happening with your finances

+

+ <%= t("pages.dashboard.welcome", name: Current.user.first_name) %> +

+

+ <%= t("pages.dashboard.subtitle") %> +

<%= render DS::Link.new( icon: "plus", - text: "New", + text: t("pages.dashboard.new"), href: new_account_path, frame: :modal, class: "hidden lg:inline-flex" diff --git a/app/views/pages/dashboard/_balance_sheet.html.erb b/app/views/pages/dashboard/_balance_sheet.html.erb index 305733242..5273e5031 100644 --- a/app/views/pages/dashboard/_balance_sheet.html.erb +++ b/app/views/pages/dashboard/_balance_sheet.html.erb @@ -117,8 +117,12 @@ icon: classification_group.icon, ) %> -

No <%= classification_group.name %> yet

-

<%= "Add your #{classification_group.name} accounts to see a full breakdown" %>

+

+ <%= t("pages.dashboard.balance_sheet.no_items", name: classification_group.name) %> +

+

+ <%= t("pages.dashboard.balance_sheet.add_accounts", name: classification_group.name) %> +

<% end %> diff --git a/app/views/pages/dashboard/_cashflow_sankey.html.erb b/app/views/pages/dashboard/_cashflow_sankey.html.erb index ec03f8418..1f9b30053 100644 --- a/app/views/pages/dashboard/_cashflow_sankey.html.erb +++ b/app/views/pages/dashboard/_cashflow_sankey.html.erb @@ -2,7 +2,7 @@

- Cashflow + <%= t("pages.dashboard.cashflow_sankey.title") %>

<%= form_with url: root_path, method: :get, data: { controller: "auto-submit-form", turbo_frame: "cashflow_sankey_section" } do |form| %> @@ -30,10 +30,10 @@ icon: "activity" # cashflow placeholder icon ) %> -

No cash flow data for this time period

-

Add transactions to display cash flow data or expand the time period

+

<%= t("pages.dashboard.cashflow_sankey.no_data_title") %>

+

<%= t("pages.dashboard.cashflow_sankey.no_data_description") %>

<%= render DS::Link.new( - text: "Add transaction", + text: t("pages.dashboard.cashflow_sankey.add_transaction"), icon: "plus", href: new_transaction_path, frame: :modal diff --git a/app/views/pages/dashboard/_no_accounts_graph_placeholder.html.erb b/app/views/pages/dashboard/_no_accounts_graph_placeholder.html.erb index 5b0909c1f..b8cca831b 100644 --- a/app/views/pages/dashboard/_no_accounts_graph_placeholder.html.erb +++ b/app/views/pages/dashboard/_no_accounts_graph_placeholder.html.erb @@ -5,10 +5,10 @@ icon: "layers", ) %> -

No accounts yet

-

Add accounts to display net worth data

+

<%= t("pages.dashboard.no_accounts.title") %>

+

<%= t("pages.dashboard.no_accounts.description") %>

<%= render DS::Link.new( - text: "Add account", + text: t("pages.dashboard.no_accounts.add_account"), icon: "plus", href: new_account_path, frame: :modal diff --git a/app/views/pages/dashboard/_outflows_donut.html.erb b/app/views/pages/dashboard/_outflows_donut.html.erb index e89f4d4fe..cefd472f7 100644 --- a/app/views/pages/dashboard/_outflows_donut.html.erb +++ b/app/views/pages/dashboard/_outflows_donut.html.erb @@ -2,7 +2,7 @@

- Outflows + <%= t("pages.dashboard.outflows_donut.title") %>

<%= form_with url: root_path, method: :get, data: { controller: "auto-submit-form", turbo_frame: "outflows_donut_section" } do |form| %> @@ -33,7 +33,7 @@
- Total Outflows + <%= t("pages.dashboard.outflows_donut.total_outflows") %>
diff --git a/config/locales/views/pages/ca.yml b/config/locales/views/pages/ca.yml index 30081e7b0..568c59920 100644 --- a/config/locales/views/pages/ca.yml +++ b/config/locales/views/pages/ca.yml @@ -2,13 +2,30 @@ ca: pages: changelog: - title: Novetats + title: "Novetats" dashboard: + welcome: "Benvingut/da de nou, %{name}" + subtitle: "Això és el que està passant amb les teves finances" + new: "Nou" net_worth_chart: - data_not_available: No hi ha dades disponibles per al període seleccionat - title: Patrimoni net + data_not_available: "Dades no disponibles per al període seleccionat" + title: "Patrimoni net" no_account_empty_state: - new_account: Nou compte - no_account_subtitle: Com que no s'ha afegit cap compte, no hi ha dades per mostrar. Afegeix els teus primers comptes per començar a veure dades al tauler. - no_account_title: Encara no hi ha cap compte - + new_account: "Nou compte" + no_account_subtitle: "Com que no s'ha afegit cap compte, no hi ha dades per mostrar. Afegeix els teus primers comptes per començar a veure dades al tauler." + no_account_title: "Encara no hi ha comptes" + balance_sheet: + no_items: "Encara no hi ha %{name}" + add_accounts: "Afegeix els teus comptes de %{name} per veure'n el detall complet" + cashflow_sankey: + title: "Flux de caixa" + no_data_title: "No hi ha dades de flux de caixa per a aquest període de temps" + no_data_description: "Afegeix transaccions per mostrar dades de flux de caixa o amplia el període de temps" + add_transaction: "Afegeix una transacció" + no_accounts: + title: "Encara no hi ha comptes" + description: "Afegeix comptes per mostrar dades de patrimoni net" + add_account: "Afegeix un compte" + outflows_donut: + title: "Sortides" + total_outflows: "Total sortides" diff --git a/config/locales/views/pages/en.yml b/config/locales/views/pages/en.yml index 1ee15ca35..f33a3e00a 100644 --- a/config/locales/views/pages/en.yml +++ b/config/locales/views/pages/en.yml @@ -4,11 +4,28 @@ en: changelog: title: What's new dashboard: + welcome: "Welcome back, %{name}" + subtitle: "Here's what's happening with your finances" + new: "New" net_worth_chart: data_not_available: Data not available for the selected period title: Net Worth no_account_empty_state: new_account: New account - no_account_subtitle: Since no accounts have been added, there's no data to - display. Add your first accounts to start viewing dashboard data. + no_account_subtitle: Since no accounts have been added, there's no data to display. Add your first accounts to start viewing dashboard data. no_account_title: No accounts yet + balance_sheet: + no_items: "No %{name} yet" + add_accounts: "Add your %{name} accounts to see a full breakdown" + cashflow_sankey: + title: "Cashflow" + no_data_title: "No cash flow data for this time period" + no_data_description: "Add transactions to display cash flow data or expand the time period" + add_transaction: "Add transaction" + no_accounts: + title: "No accounts yet" + description: "Add accounts to display net worth data" + add_account: "Add account" + outflows_donut: + title: "Outflows" + total_outflows: "Total Outflows"