From 54af644429104b2ebb72625bde620a8f31a52511 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Fri, 3 Jul 2026 05:54:28 +0200 Subject: [PATCH] fix(ui): localise the confirm screen, drop the brand on the error state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session-less /confirm page defaulted to English, so it showed the English app title. Carry the recipient's language on the deeplink (?lang) and setLocale on load so the page matches the email. Show a localised brand wordmark (Эрудит / Erudit, matching the email) on the success state only; the invalid/expired state now shows just the message, no header. --- backend/internal/account/email.go | 11 ++++++----- ui/src/lib/i18n/en.ts | 1 + ui/src/lib/i18n/ru.ts | 1 + ui/src/screens/Confirm.svelte | 30 ++++++++++++++++-------------- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/backend/internal/account/email.go b/backend/internal/account/email.go index 4cfdf9a..64f7d53 100644 --- a/backend/internal/account/email.go +++ b/backend/internal/account/email.go @@ -114,7 +114,7 @@ func (s *EmailService) issueCode(ctx context.Context, accountID uuid.UUID, email if err := s.store.replacePendingConfirmation(ctx, accountID, email, codeHash, tokenHash, purpose, s.now().Add(emailCodeTTL)); err != nil { return err } - msg, err := renderConfirmationEmail(purpose, code, s.confirmURL(token), s.baseURL, locale) + msg, err := renderConfirmationEmail(purpose, code, s.confirmURL(token, locale), s.baseURL, locale) if err != nil { return err } @@ -122,13 +122,14 @@ func (s *EmailService) issueCode(ctx context.Context, accountID uuid.UUID, email return s.mailer.Send(ctx, msg) } -// confirmURL builds the absolute one-tap confirm deeplink for token, or "" when no -// public base URL is configured. -func (s *EmailService) confirmURL(token string) string { +// confirmURL builds the absolute one-tap confirm deeplink for token in locale, or "" +// when no public base URL is configured. The locale rides the fragment as ?lang so the +// confirm screen (opened in a browser with no session) renders in the email's language. +func (s *EmailService) confirmURL(token, locale string) string { if s.baseURL == "" { return "" } - return strings.TrimRight(s.baseURL, "/") + emailConfirmPath + token + return strings.TrimRight(s.baseURL, "/") + emailConfirmPath + token + "?lang=" + normalizeLocale(locale) } // accountLocale returns the account's preferred UI language for localising email, diff --git a/ui/src/lib/i18n/en.ts b/ui/src/lib/i18n/en.ts index 6a7cce1..0223ffc 100644 --- a/ui/src/lib/i18n/en.ts +++ b/ui/src/lib/i18n/en.ts @@ -4,6 +4,7 @@ export const en = { 'app.title': 'Scrabble', + 'app.brand': 'Erudit', 'dict.loading': 'Loading…', 'connection.connecting': 'Connecting…', diff --git a/ui/src/lib/i18n/ru.ts b/ui/src/lib/i18n/ru.ts index 063b652..068a61f 100644 --- a/ui/src/lib/i18n/ru.ts +++ b/ui/src/lib/i18n/ru.ts @@ -5,6 +5,7 @@ import type { MessageKey } from './en'; export const ru: Record = { 'app.title': 'Scrabble', + 'app.brand': 'Эрудит', 'dict.loading': 'Загрузка…', 'connection.connecting': 'Подключение…', diff --git a/ui/src/screens/Confirm.svelte b/ui/src/screens/Confirm.svelte index d97b31a..bc64a97 100644 --- a/ui/src/screens/Confirm.svelte +++ b/ui/src/screens/Confirm.svelte @@ -1,22 +1,27 @@