Tableau Localize

View Source

Introduction

Visitors switch locale with a Corex <.select redirect>. Choosing a language navigates to the matching permalink (/<locale>/... for every locale, including the default; / is a root alias for the default locale). Gettext drives copy; locale.js (from install wiring) persists the visitor's choice and syncs the language select.

Localized pages also set lang and dir on <html> from MyApp.Locale so screen readers, font shaping, and bidi text work. For Phoenix apps with localize_web and router scopes, see Localize.

Install first

Wire Gettext, MyApp.Locale, permalink Module.create pages, layout data-locale* attributes, and locale.js before you drop this UI into a layout:

Example site: corex-ui/soonex_i18n. Baseline without locale: corex-ui/soonex.

Already wired?

PieceExpect
Depsgettext, gettext_sigils, localize_web (CLDR display names and RTL); extra_applications: [:localize]
GettextMyApp.Gettext with string locales en / fr / ar
Locale moduleMyApp.Locale with locales/0, swap_path/2, language_select_items/1, lang/1, dir/1, selected_path/2
Layoutlang / dir / data-locale* on <html>; Gettext locale put in RootLayout
JSimport "./locale.js" in site.js; Select hook registered
PagesOne Tableau.Page per locale via Module.create at /<locale>/..., plus root /

Full deps / locale.js / Module.create paste lives in Tableau: optional locale wiring.

Language select

redirect navigates to item.to. The URL is authoritative; corex:set-locale updates localStorage for other tabs.

<.select
  id="corex-language-switch"
  class="select ui-size-sm max-w-6xs"
  dir={MyApp.Locale.dir(@locale)}
  items={MyApp.Locale.language_select_items(MyApp.Locale.current_path(@page))}
  value={MyApp.Locale.language_select_value(MyApp.Locale.current_path(@page), @locale)}
  redirect
  on_value_change_client="corex:set-locale"
  positioning={%Corex.Positioning{same_width: true}}
>
  <:label class="sr-only">{~t"Language"}</:label>
  <:item :let={item}>{item.label}</:item>
  <:trigger>
    <.heroicon name="hero-language" />
  </:trigger>
  <:item_indicator>
    <.heroicon name="hero-check" />
  </:item_indicator>
</.select>

Items use Corex.List.Item so value, label, and destination stay aligned. Keep id="corex-language-switch" so locale.js can sync the control.

Layout placement

Render the select where it belongs in the shell (header, menu, footer). Ensure the page assign (@page) and :locale from RootLayout are available:

<.select
  id="corex-language-switch"
  ...
/>

With Tableau Theming and Tableau Mode, also set data-theme / data-mode on <html>; head scripts stay as documented in Tableau.

After changing strings, extract and merge catalogs:

mix gettext.extract
mix gettext.merge priv/gettext

Fill priv/gettext/<locale>/LC_MESSAGES/default.po.

Troubleshooting

SymptomCheck
Switcher missing or emptyLocale.locales/0 matches supported locales; Select hook registered
Wrong page after switchPermalink exists for that locale (Module.create page); swap_path/2 output matches
Labels look like EN / ARCLDR data downloaded (mix localize.download_locales en fr ar)
dir wrong for RTLLocale.dir/1 and root dir={MyApp.Locale.dir(@locale)}
Select does not synclocale.js imported; id="corex-language-switch"; data-locale-selected-path on <html>