Back to Tips

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! 📩