From 005235a54ac9e745b285ce1dc08c27ffff02b96e Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 2 Feb 2026 13:00:27 -0600 Subject: [PATCH] more gettext --- GETTEXT_MIGRATION_NOTES.md | 123 ++++++++++++++++++++++++++++++++++ lib/towerops_web/user_auth.ex | 32 ++++----- 2 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 GETTEXT_MIGRATION_NOTES.md diff --git a/GETTEXT_MIGRATION_NOTES.md b/GETTEXT_MIGRATION_NOTES.md new file mode 100644 index 00000000..b3819b6f --- /dev/null +++ b/GETTEXT_MIGRATION_NOTES.md @@ -0,0 +1,123 @@ +# Gettext Migration Progress Notes + +## Current Status: Phase 2 Nearly Complete + +### ✅ Completed Phases + +1. **Phase 1: Infrastructure** - DONE (previous session) + - Created gettext helpers with domain-specific functions + - Set up pot files for each domain + - Created `mix populate_english` task + +2. **Phase 2: Equipment Features** - DONE ✅ + - DeviceLive.Index: device discovery, reordering, error messages + - DeviceLive.Show: backup messages, permission errors + - DeviceLive.Form: device CRUD operations + - AlertLive.Index: alert acknowledgment messages + - SiteLive.Show: site discovery messages + - SiteLive.Form: site CRUD and SNMP/agent propagation + - All equipment tests passing (7/7) + +3. **Phase 2: Admin Features** - DONE ✅ + - Admin.UserLive.Index: user deletion messages + - Admin.OrgLive.Index: organization deletion messages + - UserAuth: impersonation start/stop messages (3 strings) + - All admin tests passing (87 tests) + +4. **Phase 2: Core Components** - DONE ✅ + - ConsentPrompt component migrated: + - Modal title, policy review message + - Policy acceptance labels + - Consent disclaimer text + - Accept button text + +5. **Phase 2: Auth Flash Messages (user_auth.ex)** - DONE ✅ + - Migrated ALL remaining auth messages in user_auth.ex: + - Plug functions: `require_sudo_mode`, `require_authenticated_user`, `require_superuser` + - `load_current_organization` (3 messages) + - LiveView on_mount callbacks (all auth-related messages) + - Added `use Gettext, backend: ToweropsWeb.Gettext` to user_auth.ex + - Added `import ToweropsWeb.GettextHelpers` to user_auth.ex + +### 🔄 Remaining Work + +#### Still Need to Migrate + +1. **UserSettingsLive session management** (lib/towerops_web/live/user_settings_live/session_manager.ex): + - Line 16: "Mobile device removed successfully." + - Line 20: "Failed to remove mobile device." + - Line 39: "Failed to update alert preferences." + +2. **Policy consent message** (lib/towerops_web/user_auth.ex): + - Line 659: "Thank you for accepting the updated policies." + +3. **Other scattered flash messages** - Need to grep for remaining untranslated strings + +#### Final Steps (Phase 3) +4. Extract new strings: `mix gettext.extract` +5. Populate English: `mix populate_english` +6. Run full test suite: `mix test` +7. Run precommit: `mix precommit` +8. Commit final changes +9. Update design document status + +### Git Commits So Far + +1. `89a076f` - Equipment features + sudo mode test fixes +2. `a995e62` - Admin features migration +3. `2ef154e` - Core components (consent prompt) +4. *PENDING* - Auth flash messages (user_auth.ex - just completed, not committed yet) + +### Files Modified (Not Yet Committed) + +- `lib/towerops_web/user_auth.ex` - All auth flash messages migrated to t_auth() + +### Next Steps to Resume + +1. Migrate remaining 4 flash messages: + - session_manager.ex (3 messages) + - user_auth.ex line 659 (1 message) + +2. Run `mix gettext.extract && mix populate_english` + +3. Test everything: + ```bash + mix test + mix precommit + ``` + +4. Commit the auth messages: + ```bash + mix format && git add -A && git commit -m "feat: migrate authentication flash messages to gettext" + ``` + +5. Migrate final remaining messages and create final commit + +6. Update todo list to mark phase complete + +### Helper Functions Reference + +- `t_equipment(msg)` - Equipment domain (devices, sites, alerts, agents) +- `t_admin(msg)` - Admin domain (user management, org management, impersonation) +- `t_auth(msg)` - Auth domain (login, TOTP, sudo mode, permissions) +- `gettext(msg)` - Default domain (generic UI strings) + +All helpers support interpolation: `t_equipment("Device %{name} created", name: device.name)` + +### User Question Answered + +**Agent WebSocket Connection Issue:** +User tried connecting agent to `http://localhost:4000` but got WebSocket errors. + +**Solution provided:** +- Agent auto-converts `http://localhost:4000` to `ws://localhost:4000/socket/agent/websocket` +- Suggested troubleshooting: + 1. Verify Phoenix running with `mix phx.server` + 2. Test WebSocket with `wscat -c ws://localhost:4000/socket/agent/websocket` + 3. Create valid agent token in UI at `/agents` + 4. Run agent with debug: `RUST_LOG=debug cargo run -- --api-url http://localhost:4000 --token ` +- Common issues: expired token, Phoenix not running, wrong port + +### Design Document Location + +`docs/plans/2026-02-02-gettext-internationalization-design.md` diff --git a/lib/towerops_web/user_auth.ex b/lib/towerops_web/user_auth.ex index 868f3477..8374ecca 100644 --- a/lib/towerops_web/user_auth.ex +++ b/lib/towerops_web/user_auth.ex @@ -289,14 +289,14 @@ defmodule ToweropsWeb.UserAuth do {false, true} -> conn - |> put_flash(:error, "Please verify your identity to continue.") + |> put_flash(:error, t_auth("Please verify your identity to continue.")) |> maybe_store_return_to() |> redirect(to: ~p"/users/sudo-verify") |> halt() {false, false} -> conn - |> put_flash(:error, "Two-factor authentication is required for this action.") + |> put_flash(:error, t_auth("Two-factor authentication is required for this action.")) |> redirect(to: ~p"/account/totp-enrollment") |> halt() end @@ -345,7 +345,7 @@ defmodule ToweropsWeb.UserAuth do conn else conn - |> put_flash(:error, "You must log in to access this page.") + |> put_flash(:error, t_auth("You must log in to access this page.")) |> maybe_store_return_to() |> redirect(to: ~p"/users/log-in") |> halt() @@ -362,7 +362,7 @@ defmodule ToweropsWeb.UserAuth do conn else conn - |> put_flash(:error, "You must be a superuser to access this page.") + |> put_flash(:error, t_auth("You must be a superuser to access this page.")) |> redirect(to: ~p"/orgs") |> halt() end @@ -431,20 +431,20 @@ defmodule ToweropsWeb.UserAuth do |> put_session(:current_organization_id, organization.id) else conn - |> put_flash(:error, "You don't have access to this organization.") + |> put_flash(:error, t_auth("You don't have access to this organization.")) |> redirect(to: ~p"/orgs") |> halt() end else conn - |> put_flash(:error, "Organization not found.") + |> put_flash(:error, t_auth("Organization not found.")) |> redirect(to: ~p"/orgs") |> halt() end rescue Ecto.NoResultsError -> conn - |> put_flash(:error, "Organization not found.") + |> put_flash(:error, t_auth("Organization not found.")) |> redirect(to: ~p"/orgs") |> halt() end @@ -479,7 +479,7 @@ defmodule ToweropsWeb.UserAuth do else socket = socket - |> LiveView.put_flash(:error, "You must log in to access this page.") + |> LiveView.put_flash(:error, t_auth("You must log in to access this page.")) |> LiveView.redirect(to: ~p"/users/log-in") {:halt, socket} @@ -501,7 +501,7 @@ defmodule ToweropsWeb.UserAuth do else socket = socket - |> LiveView.put_flash(:error, "You don't have access to this organization.") + |> LiveView.put_flash(:error, t_auth("You don't have access to this organization.")) |> LiveView.redirect(to: ~p"/orgs") {:halt, socket} @@ -509,7 +509,7 @@ defmodule ToweropsWeb.UserAuth do else socket = socket - |> LiveView.put_flash(:error, "Organization not found.") + |> LiveView.put_flash(:error, t_auth("Organization not found.")) |> LiveView.redirect(to: ~p"/orgs") {:halt, socket} @@ -518,7 +518,7 @@ defmodule ToweropsWeb.UserAuth do Ecto.NoResultsError -> socket = socket - |> LiveView.put_flash(:error, "Organization not found.") + |> LiveView.put_flash(:error, t_auth("Organization not found.")) |> LiveView.redirect(to: ~p"/orgs") {:halt, socket} @@ -547,7 +547,7 @@ defmodule ToweropsWeb.UserAuth do else socket = socket - |> LiveView.put_flash(:error, "You must be a superuser to access this page.") + |> LiveView.put_flash(:error, t_auth("You must be a superuser to access this page.")) |> LiveView.redirect(to: ~p"/orgs") {:halt, socket} @@ -568,7 +568,7 @@ defmodule ToweropsWeb.UserAuth do {false, true} -> socket = socket - |> LiveView.put_flash(:error, "Please verify your identity to continue.") + |> LiveView.put_flash(:error, t_auth("Please verify your identity to continue.")) |> LiveView.redirect(to: ~p"/users/sudo-verify") {:halt, socket} @@ -576,7 +576,7 @@ defmodule ToweropsWeb.UserAuth do {false, false} -> socket = socket - |> LiveView.put_flash(:error, "Two-factor authentication is required for this action.") + |> LiveView.put_flash(:error, t_auth("Two-factor authentication is required for this action.")) |> LiveView.redirect(to: ~p"/account/totp-enrollment") {:halt, socket} @@ -597,7 +597,7 @@ defmodule ToweropsWeb.UserAuth do if user && !Accounts.totp_enabled?(user) do socket = socket - |> LiveView.put_flash(:error, "You must set up two-factor authentication to continue.") + |> LiveView.put_flash(:error, t_auth("You must set up two-factor authentication to continue.")) |> LiveView.redirect(to: ~p"/account/totp-enrollment") {:halt, socket} @@ -615,7 +615,7 @@ defmodule ToweropsWeb.UserAuth do else socket = socket - |> LiveView.put_flash(:error, "You must be an organization owner to access this page.") + |> LiveView.put_flash(:error, t_auth("You must be an organization owner to access this page.")) |> LiveView.redirect(to: ~p"/orgs") {:halt, socket}