MishkaGervaz.Messages (MishkaGervaz v0.0.2)

Copy Markdown View Source

Provides Gettext macros with configurable backend support.

This module allows using Gettext macros while supporting a configurable backend through the :gettext_backend option or application config.

Configuration

defmodule MyModule do
  use MishkaGervaz.Messages, backend: MishkaCmsCore.Gettext

  def my_function do
    dgettext("mishka_gervaz", "Load More")
  end
end

Option 2: Use application config (for runtime switching)

config :mishka_gervaz, :gettext_backend, MyAppWeb.Gettext

Then:

defmodule MyModule do
  use MishkaGervaz.Messages

  def my_function do
    dgettext("mishka_gervaz", "Load More")
  end
end

Translation Domain

All MishkaGervaz translations use the mishka_gervaz domain, except the messages a form field type refuses a value with, which use the errors domain. Translation files should be placed at:

priv/gettext/LOCALE/LC_MESSAGES/mishka_gervaz.po
priv/gettext/LOCALE/LC_MESSAGES/errors.po

Strings from the DSL

Every string a resource's mishka_gervaz DSL holds is translated when it is drawn, through translate_text/1: labels, placeholders, prompts, {label, value} option labels, confirm messages, empty and error states, notices, headers and footers. A function value is called as it is and is not translated again. Mark a string with dgettext_noop("mishka_gervaz", "...") so mix gettext.extract finds it.

Work in another process

A new process has no Gettext locale of its own. Run work whose result holds words through start_async/4, assign_async/4 or in_caller_locale/1 so it is translated in the locale of the process that started it.

The same three carry the value of each process dictionary key registered with carry_process_keys/1, so work that reads what its caller put in the process dictionary — the tenant a page renders for, say — reads it in the other process too. None is registered by default.

Summary

Functions

Injects Gettext macros into the using module.

Phoenix.LiveView.assign_async/4, with fun run in the caller's Gettext locales and with its values for the keys carry_process_keys/1 registered (in_caller_locale/1).

The process dictionary keys carry_process_keys/1 registered, in the order they were first registered.

Registers process dictionary keys whose values in_caller_locale/1, start_async/4 and assign_async/4 carry into the work they start, beside the Gettext locales.

Gets the configured Gettext backend at runtime.

Wraps fun, of arity 0 or 1, to run in the Gettext locales of the process that calls this, with the values it holds for the keys carry_process_keys/1 registered.

Phoenix.LiveView.start_async/4, with fun run in the caller's Gettext locales and with its values for the keys carry_process_keys/1 registered (in_caller_locale/1).

Translates a string a resource declared in its mishka_gervaz DSL, in the locale of the process that draws it.

Functions

__using__(opts)

(macro)

Injects Gettext macros into the using module.

Options

  • :backend - The Gettext backend module to use. If not provided, falls back to the :gettext_backend config or MishkaGervaz.Gettext.

Usage

# With explicit backend (recommended for extraction)
use MishkaGervaz.Messages, backend: MyApp.Gettext

# With config-based backend
use MishkaGervaz.Messages

assign_async(socket, keys, fun, opts \\ [])

@spec assign_async(
  Phoenix.LiveView.Socket.t(),
  atom() | [atom()],
  (-> {:ok, map()} | {:error, term()}),
  keyword()
) :: Phoenix.LiveView.Socket.t()

Phoenix.LiveView.assign_async/4, with fun run in the caller's Gettext locales and with its values for the keys carry_process_keys/1 registered (in_caller_locale/1).

carried_process_keys()

@spec carried_process_keys() :: [term()]

The process dictionary keys carry_process_keys/1 registered, in the order they were first registered.

carry_process_keys(keys)

@spec carry_process_keys([term()]) :: :ok

Registers process dictionary keys whose values in_caller_locale/1, start_async/4 and assign_async/4 carry into the work they start, beside the Gettext locales.

Call it when the application starts, with the keys its own functions read with Process.get/1. Registering a key again changes nothing. A key the calling process holds no value for is not carried, so the other process reads it as unset too.

Examples

MishkaGervaz.Messages.carry_process_keys([{MyApp.Tenant, :current}])

Process.put({MyApp.Tenant, :current}, "acme")

Task.async(MishkaGervaz.Messages.in_caller_locale(fn -> Process.get({MyApp.Tenant, :current}) end))
|> Task.await()
#=> "acme"

gettext_backend()

Gets the configured Gettext backend at runtime.

Returns the user-configured backend or MishkaGervaz.Gettext as default.

in_caller_locale(fun)

@spec in_caller_locale((-> result) | (arg -> result)) :: (-> result) | (arg -> result)
when result: term(), arg: term()

Wraps fun, of arity 0 or 1, to run in the Gettext locales of the process that calls this, with the values it holds for the keys carry_process_keys/1 registered.

Give the wrapped function to the work another process runs, such as Task.async/1 or Task.async_stream/3: a new process starts with no locale of its own, so words it translates are otherwise in the default locale. The locale set with Gettext.put_locale/1 and each one set with Gettext.put_locale/2 are carried. Both are read when fun is wrapped.

Examples

Gettext.put_locale(MyApp.Gettext, "fa")

Task.async(MishkaGervaz.Messages.in_caller_locale(fn -> Gettext.get_locale(MyApp.Gettext) end))
|> Task.await()
#=> "fa"

start_async(socket, name, fun, opts \\ [])

@spec start_async(Phoenix.LiveView.Socket.t(), term(), (-> term()), keyword()) ::
  Phoenix.LiveView.Socket.t()

Phoenix.LiveView.start_async/4, with fun run in the caller's Gettext locales and with its values for the keys carry_process_keys/1 registered (in_caller_locale/1).

Use it in place of Phoenix.LiveView.start_async/4 wherever the result holds words a person reads.

translate_text(text)

@spec translate_text(term()) :: term()

Translates a string a resource declared in its mishka_gervaz DSL, in the locale of the process that draws it.

A binary is looked up in the mishka_gervaz domain of gettext_backend/0: the translation when the current locale has one, the string itself otherwise. nil and every other value are returned unchanged, so a function or a number a DSL option held passes through. A string holding %{...} is returned as it is.

A resource marks a string only its DSL holds with dgettext_noop("mishka_gervaz", "..."), which returns the English string; MishkaGervaz translates it each time it is drawn.

label dgettext_noop("mishka_gervaz", "Heading")

Examples

iex> MishkaGervaz.Messages.translate_text("No translation for this")
"No translation for this"

iex> MishkaGervaz.Messages.translate_text(nil)
nil