Internationalization and Localization#
Sure uses Rails' standard I18n framework with structured YAML translation files and per-request locale switching. The app distinguishes between supported locales (fully translated, selectable by users) and available locales (broader set of language files used for graceful degradation via fallbacks).
Supported Locales#
LanguagesHelper::SUPPORTED_LOCALES defines the 16 fully translated locales exposed in the UI:
| Locale | Language |
|---|---|
en | English |
fr | French |
de | German |
es | Spanish |
tr | Turkish |
nb | Norwegian Bokmål |
ca | Catalan |
ro | Romanian |
ru | Russian |
pl | Polish |
pt-BR | Brazilian Portuguese |
zh-CN | Chinese (Simplified) |
zh-TW | Chinese (Traditional) |
nl | Dutch |
hu | Hungarian |
vi | Vietnamese |
language_options filters I18n.available_locales down to this list for UI dropdowns. An EXCLUDED_LOCALES list keeps test locales (en-BORK, en-au-ocker) and regional duplicates (fr-FR, de-DE, en-US, etc.) out of user-facing selection.
Beyond these 16, config/locales/defaults/ holds files for 117+ language codes and regional variants, giving the fallback system a broad base .
Translation File Organization#
All translation files live under config/locales/ and are organized by domain :
| Directory | Purpose |
|---|---|
defaults/ | Global Rails defaults (number formats, date/time, ActiveRecord errors) — 117+ locale files |
views/ | Feature-scoped view strings — 82+ subdirectories (accounts, transactions, budgets, investments, settings, etc.) |
models/ | ActiveRecord attribute names and validation messages |
mailers/ | Email copy (invitation_mailer/, pdf_import_mailer/) |
breadcrumbs/ | Navigation breadcrumb labels — 14 locales |
Each YAML file is keyed by locale at the root (e.g., en:, de:). Example files for account statements: .
Fallbacks are enabled — config.i18n.fallbacks = true in config/application.rb ensures missing translations fall back to English during incremental translation rollout, rather than raising errors.
Locale Switching#
The Localize concern (included in ApplicationController) wraps every request in I18n.with_locale using this priority chain:
localeURL param — explicit overrideCurrent.user.locale— user's stored preferenceAccept-Languageheader — browser preference, parsed with q-value sortingCurrent.family.locale— family-level fallbackI18n.default_locale— final fallback
Auto-save on first visit: When the locale is resolved from the Accept-Language header and the user has no stored preference, it is automatically persisted to users.locale via update_column .
The Accept-Language parser handles q-values and normalizes locale strings (underscores → hyphens), then tries exact match followed by primary-language match against SUPPORTED_LOCALES .
Adding or Updating Translations#
- New feature strings: Add a YAML file under
config/locales/views/<feature>/(ormodels/,mailers/) for each supported locale. - New locale: Add the locale code to
SUPPORTED_LOCALESinapp/helpers/languages_helper.rband create corresponding translation files. - Insight generation runs within
I18n.with_locale(family.locale)so AI-generated text is localized per family .