Gettext

repository·main·Indexed 19 days ago

https://github.com/elixir-gettext/gettext

An implementation of the standard Gettext internationalization (i18n) and localization (l10n) system for Elixir. It provides translation macros (gettext, ngettext, dgettext), support for .po and .pot files, and Mix tasks for extracting and merging translations. The library allows for custom backend definitions via Gettext.Backend and supports pluralization, message contexts, and interpolation bindings.

Tokens
4K
Snippets
16
Records
20
Agent score
66%

What's inside gettext

  1. Organize Gettext message files

    main

    Messages are stored in Portable Object (.po) files. These files must follow a specific directory structure: priv/gettext/LOCALE/LC_MESSAGES/DOMAIN.po.

    • LOCALE: The language/region code (e.g., pt_BR).
    • DOMAIN: The translation domain. If you use the standard gettext or ngettext macros, the domain is default.

    Example path for pt_BR default messages: priv/gettext/pt_BR/LC_MESSAGES/default.po.

  2. Extract and merge translations with Mix tasks

    main

    Gettext provides Mix tasks to automate the workflow of extracting strings from source code and updating translation files.

    Extract messages

    Scan your source code for gettext calls and sync them to .pot template files in priv/gettext:

    mix gettext.extract

    Merge templates into locales

    Merge the extracted .pot templates into your existing .po locale files:

    Merge into all locales:

    mix gettext.merge priv/gettext

    Merge into one specific locale:

    mix gettext.merge priv/gettext --locale en

    Extract and merge in one step:

    mix gettext.extract --merge
  3. Use Gettext macros for automatic translation extraction

    main

    Gettext provides a family of macros (gettext/2, ngettext/4, etc.) that allow you to translate strings in your code while enabling the mix gettext.extract tool to automatically find and pull these strings into your .pot files.

    To use these macros, you must first set up a Gettext backend in your module using use Gettext, backend: MyApp.Gettext. Once the backend is defined, the standard macros are automatically imported and will use that backend by default.

    defmodule MyApp.Gettext do
      use Gettext, otp_app: :my_app
    end
    
    defmodule MyApp.Controller do
      use MyApp.Gettext
    
      def index(conn, _params) do
        gettext("Hello, world!")
      end
    end
  4. Define a Gettext backend

    main

    To create a custom Gettext backend, you must use Gettext.Backend within a module. You are required to provide the otp_app option. This module generates the necessary callbacks required by the Gettext.Backend behaviour.

    Note on usage pattern: Since version v0.26.0, the workflow is split:

    1. Define the backend using use Gettext.Backend, otp_app: :your_app.
    2. Use the functions in that backend by calling use Gettext, backend: YourApp.Gettext in your application modules.
    defmodule MyApp.Gettext do
      use Gettext.Backend, otp_app: :my_app
    end
  5. Configure a Gettext backend

    main

    To use Gettext, you must first define a backend module by using Gettext.Backend. The otp_app option should point to your application's atom name.

    defmodule MyApp.Gettext do
      use Gettext.Backend, otp_app: :my_app
    end
  6. Use Gettext translation macros

    main

    By using Gettext in your module and passing your backend, you gain access to several translation macros:

    • gettext(string): Translates a simple string.
    • ngettext(singular, plural, n): Handles pluralization based on the integer n.
    • dgettext(domain, string): Translates a string using a specific domain (e.g., for error messages).
    use Gettext, backend: MyApp.Gettext
    
    # Simple message
    gettext("Here is one string to translate")
    
    # Plural message
    number_of_apples = 4
    ngettext("The apple is ripe", "The apples are ripe", number_of_apples)
    
    # Domain-based message
    dgettext("errors", "Here is an error message to translate")
  7. Avoid dynamic messages in Gettext macros

    main

    Gettext macros require msgid, msgid_plural, domain, and msgctxt to be strings (or expand to strings) at compile-time to allow for successful extraction.

    If you need to perform dynamic lookups (where the message key is a variable determined at runtime), do not use the macros. Instead, use the functions in the Gettext module directly:

    # Correct way for dynamic lookup:
    string = "hello world"
    Gettext.gettext(MyApp.Gettext, string)
  8. Use explicit backends with `_with_backend` macros

    main

    If you are in a module that does not have a Gettext backend defined via use Gettext, or if you need to use a specific backend explicitly, you can use the _with_backend variants of the macros. These require you to pass the backend module as the first argument. Note that you must require Gettext.Macros to use them.

    defmodule MyApp.Controller do
      require Gettext.Macros
    
      def index(conn, _params) do
        Gettext.Macros.gettext_with_backend(MyApp.Gettext, "Hello, world!")
      end
    end
  9. Add extracted comments to messages

    main

    You can attach comments to the next Gettext macro call using gettext_comment/1. These comments will appear in your .pot files prefixed with #., providing context for translators.

    gettext_comment("The next message is awesome")
    gettext_comment("Another comment for the next message")
    gettext("The awesome message")
  10. Handle missing plural translations

    main

    Similar to handle_missing_translation/5, this callback is invoked when a plural message is missing for the requested locale. It includes the count n used for pluralization.

    Return Values:

    • {:ok, translated}
    • {:default, translated}
    • {:missing_bindings, translated, missing_atoms}

    Arguments:

    • locale: The requested Gettext.locale().
    • domain: The translation domain string.
    • msgctxt: The message context string.
    • msgid: The singular message ID.
    • msgid_plural: The plural message ID.
    • n: The non-negative integer used for pluralization.
    • bindings: A map of interpolation values.
    @callback handle_missing_plural_translation(
                  Gettext.locale(),
                  domain :: String.t(),
                  msgctxt :: String.t(),
                  msgid :: String.t(),
                  msgid_plural :: String.t(),
                  n :: non_neg_integer(),
                  bindings :: map()
                ) ::
                  {:ok, String.t()} | {:default, String.t()} | {:missing_bindings, String.t(), [atom]}
  11. Handle missing translations

    main

    If a requested translation is not found in the current locale, handle_missing_translation/5 is invoked. This allows you to implement fallback logic (e.g., falling back to a default locale).

    Return Values:

    • {:ok, translated}: Use this if you successfully found or constructed a translation.
    • {:default, translated}: Use this if the result does not strictly match the requested locale (e.g., you are providing a fallback translation).
    • {:missing_bindings, translated, missing_atoms}: Use this if the translation is found but has missing interpolation bindings.

    Arguments:

    • locale: The requested Gettext.locale().
    • domain: The translation domain string.
    • msgctxt: The message context string.
    • msgid: The original message ID string.
    • bindings: A map of interpolation values.
    @callback handle_missing_translation(
                  Gettext.locale(),
                  domain :: String.t(),
                  msgctxt :: String.t(),
                  msgid :: String.t(),
                  bindings :: map()
                ) ::
                  {:ok, String.t()} | {:default, String.t()} | {:missing_bindings, String.t(), [atom]}