Skip to content

feat(i18n): add internationalization to TUI and Web#6

Merged
sthbryan merged 54 commits into
mainfrom
feat/i18n
May 11, 2026
Merged

feat(i18n): add internationalization to TUI and Web#6
sthbryan merged 54 commits into
mainfrom
feat/i18n

Conversation

@sthbryan
Copy link
Copy Markdown
Owner

Changes

  • Add i18n.T() and i18n.TF() functions for translated strings with format placeholders
  • Add 50+ translation keys for TUI and CLI messages in English and Spanish
  • Update all TUI Go components to use i18n (model_types, view, form, keyhandlers, tunnel_item, help_bar, list views)
  • Update CLI startup and uninstall messages
  • Add i18n to Web components (DeleteModal, EditConnection, NotificationPermission, SettingsToggle, Toasts, Dropdown, ThemeSelector, settings page)

sthbryan added 30 commits May 7, 2026 20:37
- Add i18n to DeleteModal.svelte (delete_connection, confirm_delete, cannot_undo)
- Add connection_deleted and connection_delete_failed to +page.svelte
- Add new translation keys to en.yaml and es.yaml
Copilot AI review requested due to automatic review settings May 10, 2026 03:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a shared internationalization layer across the Go TUI/CLI and the Svelte web UI, including language persistence in settings and an API endpoint to serve translations to the web frontend.

Changes:

  • Introduces a Go internal/i18n package with embedded YAML locales (en/es) and formatting helpers, plus config support for a persisted language.
  • Adds /api/i18n (and related) web endpoints and updates Svelte pages/components to render all user-facing strings via i18n, including a language selector in Settings.
  • Refactors many TUI/CLI strings to use i18n keys and adds a web build step change.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
web-svelte/src/routes/settings/+page.svelte i18n-driven Settings UI + language selector
web-svelte/src/routes/+page.svelte Localized toast messages on delete
web-svelte/src/routes/+layout.svelte Initializes web i18n on mount
web-svelte/src/lib/stores/settings.svelte.ts Adds language to settings state
web-svelte/src/lib/i18n/index.ts New Svelte i18n store + translator
web-svelte/src/lib/components/TunnelCard.svelte Localizes tunnel card UI strings
web-svelte/src/lib/components/Toasts.svelte Localizes toast close aria-label
web-svelte/src/lib/components/ThemeSelector.svelte Localizes “Current theme” label
web-svelte/src/lib/components/ThemeButton.svelte Minor class update (cursor)
web-svelte/src/lib/components/SettingsToggle.svelte Localizes enable/disable aria-label
web-svelte/src/lib/components/NotificationPermission.svelte Localizes notification permission UI
web-svelte/src/lib/components/NewConnection.svelte Localizes new connection form + toasts
web-svelte/src/lib/components/Header.svelte Localizes app name/tagline + settings aria
web-svelte/src/lib/components/EditConnection.svelte Localizes edit connection form + toasts
web-svelte/src/lib/components/Dropdown.svelte Adds i18n defaults for dropdown labels
web-svelte/src/lib/components/DeleteModal.svelte Localizes delete confirmation modal
web-svelte/src/lib/components/ConnectionsPanel.svelte Localizes connections panel empty/loading
web-svelte/src/lib/api/index.ts Exports Settings type
web-svelte/src/lib/api/endpoints/settings.ts Adds language to Settings API type
scripts/build-web-assets.sh Runs bun install before build
internal/web/handlers.go Routes /api/i18n endpoints
internal/web/handlers_settings.go Adds language setting + i18n JSON endpoints
internal/i18n/locales/es.yaml New Spanish translations
internal/i18n/locales/en.yaml New English translations
internal/i18n/i18n.go New core i18n store + helpers
internal/i18n/helpers.go Provider/status/notification helper translators
internal/i18n/embed.go Embeds locale YAMLs + load helpers
internal/config/config.go Persists language in config
internal/app/view.go Localizes TUI loading/status messages
internal/app/update.go Localizes download/status feedback
internal/app/ui/views/settings.go Localizes settings view + language selector
internal/app/ui/views/logs.go Localizes logs view header/hints
internal/app/ui/views/list.go Localizes list view headers/messages
internal/app/ui/views/form.go Localizes form labels/hints/buttons
internal/app/ui/views/empty.go Localizes empty state copy
internal/app/ui/views/downloading.go Localizes downloading/install UI
internal/app/ui/components/tunnel_item.go Localizes tunnel list item status/badges
internal/app/ui/components/help_bar.go Localizes help bar shortcuts
internal/app/ui/components/detail_panel.go Localizes detail panel labels/actions
internal/app/model.go Localizes install failure error message
internal/app/model_types.go Localizes list item description text
internal/app/keyhandlers_settings.go Adds language cycling + persistence
internal/app/keyhandlers_list.go Localizes list actions + errors
internal/app/form.go Localizes validation + success messages
internal/app/app.go Loads translations + initializes language
desktop/main.go Removes embed directive for desktop assets
cmd/ftm/main.go Localizes CLI output strings
Comments suppressed due to low confidence (2)

web-svelte/src/lib/components/Dropdown.svelte:38

  • ariaLabel/label defaults are computed once from t("options") during props destructuring, so they won’t update when the language changes. Prefer deriving a computed label reactively (e.g. const computed = $derived(label ?? t('options'))) and use that in the template, or default to undefined and translate at render time.
    cmd/ftm/main.go:56
  • i18n.T/TF are used before app.New() is called (e.g. --version and --uninstall paths), but translations are only loaded inside app.New() right now. This means these code paths will print raw keys (and formatted strings will be wrong). Initialize i18n (load translations and set language) before any early-return flag handling that uses it.
func main() {
	var (
		webOnly     = flag.Bool("web", false, "Start web dashboard and open browser")
		server      = flag.Bool("server", false, "Start web dashboard only (no browser)")
		port        = flag.Int("port", 0, "Web server port (auto-detect if not specified)")
		showVersion = flag.Bool("version", false, "Show version")
		uninstall   = flag.Bool("uninstall", false, "Uninstall ftm")
	)
	flag.Parse()

	if *showVersion {
		fmt.Println(i18n.TF("version_output", version.Version))
		os.Exit(0)
	}

	if *uninstall {
		doUninstall()
		os.Exit(0)
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/i18n/i18n.go Outdated
Comment thread internal/app/ui/views/downloading.go
Comment thread internal/app/ui/components/tunnel_item.go
Comment thread internal/app/model_types.go
Comment thread web-svelte/src/lib/components/NotificationPermission.svelte
Comment thread web-svelte/src/lib/i18n/index.ts
Comment thread internal/i18n/i18n.go
Comment thread internal/web/handlers_settings.go
Comment thread scripts/build-web-assets.sh Outdated
Comment thread desktop/main.go
@sthbryan sthbryan merged commit d0b3384 into main May 11, 2026
4 checks passed
@sthbryan sthbryan deleted the feat/i18n branch May 11, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants