CILicense: MITHex version badgeHexdocs badge

Gettext Sigils

An Elixir library that provides a ~t sigil for using Gettext translations with less boilerplate and improved readability:

# before
gettext("Hello, %{name}", name: user.name)

# after
~t"Hello, #{user.name}"

Installation

Using igniter

The package provides an igniter install task for easy installation and setup:

mix igniter.install gettext_sigils

The installer also rewrites existing use Gettext calls in the project to use GettextSigils.

Manually

The package can be installed by adding gettext_sigils to your list of dependencies in mix.exs:

def deps do
  [
    {:gettext_sigils, "~> 0.5.0"}
  ]
end

Basic Usage

To use the ~t sigil in your module, just use GettextSigils instead of the default Gettext module (for example in MyAppWeb.html_helpers/0 when using Phoenix):

# replace this
use Gettext, backend: MyApp.Gettext

# with this
use GettextSigils, backend: MyApp.Gettext

You can then use the ~t sigil instead of the gettext macro:

~t"Hello, World!"
# same as
gettext("Hello, World!")

Note: The default Gettext macros (gettext, pgettext, dgettext, ...) remain available in modules that use GettextSigils, so you can mix and match as needed.

Features

Interpolation

Interpolated expressions become Gettext bindings with automatically derived keys:

~t"The #{fruit.name} is #{color}"
# => gettext("The %{fruit_name} is %{color}", fruit_name: fruit.name, color: color)

See the Interpolation guide for the full key derivation rules, handling of ambiguous keys, and explicit key = expr syntax.

Domain & Context

Set a default Gettext domain and context per module under the :sigils key. Every ~t sigil in the module then uses these by default:

use GettextSigils,
  backend: MyApp.Gettext,
  sigils: [
    domain: "frontend",
    context: "dashboard"
  ]

~t"Welcome"   # => dpgettext(MyApp.Gettext, "frontend", "dashboard", "Welcome")

Modifiers

Single-letter suffixes on the ~t sigil tweak how a translation is produced. Define them under the :modifiers key: each entry maps a letter to a keyword list of :domain and/or :context overrides that apply when the sigil carries that letter.

defmodule MyAppWeb.DashboardLive do
  use GettextSigils,
    backend: MyApp.Gettext,
    sigils: [
      domain: "frontend",
      context: inspect(__MODULE__),
      modifiers: [
        e: [domain: "errors"],
        g: [domain: :default, context: nil]
      ]
    ]

  ~t"Welcome"     # domain: "frontend", context: "MyAppWeb.DashboardLive"
  ~t"Not found"e  # domain: "errors",   context: "MyAppWeb.DashboardLive"
  ~t"Yes"g        # backend default domain, no context
  ~t"Not found"eg # backend default domain, no context (g runs after e)
end

Modifier entries can also point at a module for message rewriting, postprocessing, or custom pluralization. See the Modifiers guide for the full picture.

Pluralization

The built-in N modifier turns a ~t sigil into a plural Gettext call, selecting the singular or plural form at runtime based on the count binding:

~t"#{count} error(s)"N

Pluralization requires a count binding key. Use an explicit binding key to bind it to any value:

~t"#{count = length(errors)} error(s)"N

See the Pluralization guide for the full rules and how to write a custom pluralization modifier.

Usage Rules

GettextSigils ships with usage rules and skills for LLM coding agents (Claude Code, Cursor, Codex, etc.) via the usage_rules library. See the LLM guide for setup instructions.

Sponsoring

Shoutout to my employer 🦓 zebbra for allowing me to make this public. Need Elixir expertise made in 🇨🇭 Switzerland? Feel free to reach out.