Skip to content

Virviil/libfluent

Repository files navigation

libfluent

Module provides Project Fluent bindings and API to build internationalized applications. API is trying to be as simmilar as possible to Elixir's default internatrionalization library - Gettext

At the same time, Fluent.Native module - as native binding - can be used to work with Project Fluent from any structure of choice. One can use it to define his own API, that is opposite to Gettext API as much as he wants.

Using Fluent

To use Fluent, a module that calls use Fluent.Assembly has to be defined:

defmodule MyApp.Fluent do
  use Fluent.Assembly, otp_app: :my_app
end

This automatically defines some funcitons in the MyApp.Fluent module, that can be used to translation:

import MyApp.Fluent

# Simple translation
ftl("hello-world")

# Argument-based translation
ftl("hello-user", userName: "Alice")

# With different types
ftl("shared-photos", userName: "Alice", userGender: "female", photoCount: 3)

Translations

Translations are stored inside FTL files, with .ftl extension. Syntax overview can be found here

FTL files, containgin translations for an application must be stored in a directory (by default it's priv/fluent), that has the following structure:

%FLUENT_TRANSLATIONS_DIRECTORY%
└─ %LOCALE%
   ├─ file_1.ftl
   ├─ file_2.ftl
   └─ file_3.ftl

Here, %LOCALE% is the locale of the translations (for example, en_US), and file_i.ftl are FTL files containing translations. All the files from single translation are loaded as the single scope, so name conflicts inside the files in one folder should be avoided.

A concrete example of such a directory structure could look like this:

priv/gettext
└─ en_US
|  ├─ default.ftl
|  └─ errors.ftl
└─ it
   ├─ default.ftl
   └─ errors.ftl

By default, Fluent expects translations to be stored under the fluent directory inside priv directory of an application. This behaviour can be changed by specifying a :priv option when using Fluent.Assembly:

# Look for translations in my_app/priv/translations instead of
# my_app/priv/gettext
use Fluent.Assembly, otp_app: :my_app, priv: "translations"

Locale

At runtime, all translation functions that do not explicitly take a locale as an argument read the locale from the assembly locale and then fallbacks to libfluent's locale.

Fluent.put_locale/1 can be used to change the locale of all assemblies for the current Elixir process. That's the preferred mechanism for setting the locale at runtime. Fluent.put_locale/2 can be used when you want to set the locale of one specific Fluent assembly without affecting other Fluent assemblies.

Similarly, Fluent.get_locale/0 gets the locale for all assemblies in the current process. Fluent.get_locale/1 gets the locale of a specific assembly for the current process. Check their documentation for more information.

Locales are expressed as strings (like "en" or "fr"); they can be arbitrary strings as long as they match a directory name. As mentioned above, the locale is stored per-process (in the process dictionary): this means that the locale must be set in every new process in order to have the right locale available for that process. Pay attention to this behaviour, since not setting the locale will not result in any errors when Fluent.get_locale/0 or Fluent.get_locale/1 are called; the default locale will be returned instead.

To decide which locale to use, each gettext-related function in a given assembly follows these steps:

  • if there is a assembly-specific locale for the given assembly for this process (see Fluent.put_locale/2), use that, otherwise
  • if there is a global locale for this process (see Fluent.put_locale/1), use that, otherwise
  • if there is a assembly's specific default locale in the configuration for that assembly's :otp_app (see the Default locale section below), use that, otherwise
  • use the default global Fluent locale (see the Default locale section below)

Default locale

The global Fluent default locale can be configured through the :default_locale key of the :libfluent application:

config :libfluent, :default_locale, "fr"

By default the global locale is "en".

If for some reason an assembly requires with a different :default_locale than all other assemblies, you can set the :default_locale inside the assembly configuration, but this approach is generally discouraged as it makes it hard to track which locale each assembly is using:

config :my_app, MyApp.Fluent, default_locale: "fr"

Installation

If available in Hex, the package can be installed by adding fluent to your list of dependencies in mix.exs:

def deps do
  [
    {:libfluent, "~> 0.2.2"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/libfluent.