Hey folks, I start drafting a recipe teaching how to use gettext + live session to deal with i18n. I would be glad if you had some feedback on that.
Phoenix Recipe Easy I18n with Live Session
Problem
You need to internationalize your website. You decide to use gettext
, and your system uses LiveView. Everything you find seems too complicated or doesn't quickly meet your needs without much configuration.
How can we use LiveSession to take care of our application's i18n?
Solution
With LiveView 0.18, we can have functional components and easily trigger an event from our functional component and intercept them in a general way using LiveSession.
This was the most straightforward approach I could think of to share and modify the language of the page we are accessing.
With a global assign, we can access the current locale and modify the language of the page using the with_locale from gettext.
Attempt
First, we must define a second language for our i18n tool (gettext). To do this, go to your project directory through the terminal and run the following command:
mix gettext.merge priv/gettext --locale pt_BR
The chosen language was pt_BR
, but it can be any available language you want to use.
With our new language set, we will now create our live session that will set our default language in the assigns for all live views and will have the method to handle language switching in the system.
Inside our _web
folder, we will create a module called locale.ex
, and it will have the following structure:
defmodule PhoenixI18nWeb.Locale do
import Phoenix.LiveView
use Phoenix.Component
def on_mount(:default, _params, _session, socket) do
{:cont,
socket
|> assign(:locale, "en")
|> attach_hook(:set_locale, :handle_event, &handle_event/3)}
end
defp handle_event("toggle_locale", %{"locale" => "en"}, socket) do
locale = "pt_BR"
perform_assigns(socket, locale)
end
defp handle_event("toggle_locale", %{"locale" => "pt_BR"}, socket) do
locale = "en"
perform_assigns(socket, locale)
end
defp handle_event(_, _, socket) do
{:cont, socket}
end
defp perform_assigns(socket, locale) do
Gettext.put_locale(IagocavalcanteWeb.Gettext, locale)
{:halt, socket |> assign(locale: locale)}
end
end
In the on_mount function, we set the default locale to English and have a hook responsible for handling the language change event. We call it toggle_locale.
With this done, we are ready to create our component that will call the hook added to the mount. It will have the following structure:
defmodule PhoenixI18nWeb.ToggleLocale do
use Phoenix.Component
def toggle_locale(assigns) do
~H"""
<button
phx-click="toggle_locale"
phx-value-locale={@locale}
type="button"
aria-label="Toggle locale"
class="group rounded-full bg-white/90 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
>
<img
src={"/images/flags/#{@locale}.png"}
class="h-6 w-6 fill-zinc-700 stroke-zinc-500 transition dark:fill-teal-400/10 dark:stroke-teal-500"
/>
</button>
"""
end
end
With this, we now need to update our live view and add wherever we want to use Gettext with the chosen language the following code:
<%= Gettext.with_locale(@locale, fn -> %>
<%= gettext("Peace of mind from prototype to production.") %>
<% end) %>
Finally, let's add our live session to the routes that we need to take care of for i18N:
live_session :locale, on_mount: [PhoenixI18nWeb.RestoreLocale] do
live "/", HomeLive, :home
end
Conclusion
If you're looking for a straightforward way to implement i18n with live view, you can check out the complete example in this repository and see a live demo by following this link.