Mastering Phoenix i18n with Plural Support
2025-02-12 4 min Elixir
Handle complex translations and pluralization elegantly in Phoenix applications.
Initial Setup
# lib/my_app/gettext.ex
defmodule MyApp.Gettext do
use Gettext, otp_app: :my_app
end
# config/config.exs
config :my_app, MyApp.Gettext,
locales: ~w(en es_MX),
default_locale: "en"
Before
def notification_text(count) do
if count == 1 do
"1 pending notification"
else
"#{count} pending notifications"
end
end
def email_notification(name, count) do
if count == 1 do
"Dear #{name}, you have 1 new message"
else
"Dear #{name}, you have #{count} new messages"
end
end
After
# priv/gettext/es_MX/LC_MESSAGES/alerts.po
msgid "%{count} pending notification"
msgid_plural "%{count} pending notifications"
msgstr[0] "%{count} notificación pendiente"
msgstr[1] "%{count} notificaciones pendientes"
# priv/gettext/es_MX/LC_MESSAGES/emails.po
msgid "Dear %{name}, you have %{count} new message"
msgid_plural "Dear %{name}, you have %{count} new messages"
msgstr[0] "Estimado/a %{name}, tiene %{count} mensaje nuevo"
msgstr[1] "Estimado/a %{name}, tiene %{count} mensajes nuevos"
# In your code
def notification_text(count) do
import MyApp.Gettext
dngettext(
"alerts",
"%{count} pending notification",
"%{count} pending notifications",
count,
%{count: count}
)
end
def email_notification(name, count) do
import MyApp.Gettext
dngettext(
"emails",
"Dear %{name}, you have %{count} new message",
"Dear %{name}, you have %{count} new messages",
count,
%{name: name, count: count}
)
end
Key points
- Always import MyApp.Gettext in modules using translations
- Use proper locale codes (es_MX instead of es) for region-specific translations
- Organize translations by context in separate .po files (alerts.po, emails.po)
Pro tips
- Extract new translations: mix gettext.extract
- Create new locale (es_MX) from existing: mix gettext.merge priv/gettext —locale es_MX
- Use Gettext.put_locale(MyApp.Gettext, “es_MX”) to switch locales dynamically
Additional info
Store locale in session:
conn.put_session(:locale, "es_MX")
Add locale plug:
plug :set_locale
def set_locale(conn, _) do
locale = get_session(conn, :locale) || "en"
Gettext.put_locale(MyApp.Gettext, locale)
conn
end
- Use gettext for simple translations
- Use dgettext for domain-specific translations without pluralization
- Use ngettext/dngettext for pluralization cases
Save this tip for later! 📩