Localize.Message.MF1 (Localize MF1 v0.1.0)

Copy Markdown View Source

Formats ICU MessageFormat 1 messages with Localize.

ICU MessageFormat 1 (MF1) is the message syntax of ICU4J and ICU4C, of translation management systems such as Crowdin, and of ex_cldr_messages: arguments ({name}), formatted arguments ({count, number}) and the plural, select and selectordinal selectors. Localize itself formats MessageFormat 2 (Localize.Message); this library formats MF1 messages with Localize's locales, plural rules and formatters, so that applications can keep existing MF1 translations.

The API follows Cldr.Message from ex_cldr_messages:

Arguments and bindings

Bindings are a keyword list or map, keyed by atom or string, for named arguments such as {name}, or a list of values for positional arguments such as {0}. A number binding may be a {number, options} tuple whose options are given to Localize.Number.to_string/2.

Formatted arguments

ArgumentFormatted with
{n}Localize.Chars.to_string/2
{n, number}, {n, number, integer} (and currency, percent, permille, short, long)Localize.Number.to_string/2
{n, number, ordinal}the digits_ordinal rule ("1st")
{n, spellout} (and verbose, year, ordinal)Localize.Number.Rbnf.to_string/3
{d, date}, {t, time}, {dt, datetime} (and short to full, or a pattern)Localize.Date, Localize.Time and Localize.DateTime
{u, unit} (and short, long, narrow)Localize.Unit.to_string/2
{l, list} (and or, unit and the other list styles)Localize.List.to_string/2
{m, money} (and short, long)Money.to_string/2, when ex_money is a dependency

A :name style, as in {n, number, :spellout_ordinal}, is a format configured with the :formats option or the :formats key of the :localize_mf1 application environment, or else an RBNF rule or a number format of that name. Number and date skeletons (::…) are not supported.

Summary

Types

Bindings for a message's arguments.

Functions

Returns the names (and positions) of the arguments a message uses.

Returns a message in canonical form.

Returns a message in canonical form, raising on error.

Formats a MessageFormat 1 message.

Formats a MessageFormat 1 message, raising on error.

Formats a MessageFormat 1 message to an iolist, with the arguments that were and were not bound.

Returns the Jaro distance between the canonical forms of two messages.

Returns the Jaro distance between two messages, raising on error.

Returns whether a message is written in MessageFormat 2 rather than MessageFormat 1.

Types

bindings()

@type bindings() :: Keyword.t() | map() | list()

Bindings for a message's arguments.

Functions

bindings(message)

@spec bindings(String.t() | Localize.Message.MF1.Parser.t()) ::
  [String.t() | non_neg_integer()] | {:error, Exception.t()}

Returns the names (and positions) of the arguments a message uses.

Arguments

Returns

  • A list of argument names (strings) and positions (integers), in the order Cldr.Message.bindings/1 gave them, or

  • {:error, exception} when the message cannot be parsed.

Examples

iex> Localize.Message.MF1.bindings("This {variable} is in the message")
["variable"]

canonical_message(message, options \\ [])

@spec canonical_message(String.t(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Returns a message in canonical form.

Messages that differ only in layout white space and quoting have the same canonical form, so it can be used as a Gettext message id. The form is that of Cldr.Message.canonical_message/2, so existing msgids carry over.

Arguments

Options

  • :trim is true (the default) to trim white space from the message first, or false.

  • :pretty is true to put each selector clause on a line of its own, indented, or false (the default).

Returns

  • {:ok, canonical_message}, or

  • {:error, exception} when the message cannot be parsed or is not a string.

Examples

iex> Localize.Message.MF1.canonical_message("{greeting } to you!")
{:ok, "{greeting} to you!"}

iex> Localize.Message.MF1.canonical_message("{n, plural, other {# items} one {# item}}")
{:ok, "{n, plural, one {# item} other {# items}}"}

canonical_message!(message, options \\ [])

@spec canonical_message!(String.t(), Keyword.t()) :: String.t() | no_return()

Returns a message in canonical form, raising on error.

Arguments

  • message is a message string.

  • options is a keyword list of options.

Options

Returns

  • The canonical message. An exception is raised when the message cannot be parsed.

Examples

iex> Localize.Message.MF1.canonical_message!("{greeting } to you!")
"{greeting} to you!"

format(message, bindings \\ [], options \\ [])

@spec format(String.t(), bindings(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}

Formats a MessageFormat 1 message.

Arguments

  • message is a MessageFormat 1 message string. A MessageFormat 2 message (see mf2?/1) is formatted by Localize.Message.format/3.

  • bindings is a keyword list or map of named arguments, or a list of positional arguments. The default is [].

  • options is a keyword list of options.

Options

  • :locale is a locale identifier or a Localize.LanguageTag.t/0. The default is Localize.get_locale/0.

  • :trim is true to trim white space from the message before it is formatted. The default is false.

  • :allow_positional_args is false to return an error for a message with positional arguments. The default is true.

  • :formats is a map or keyword list of named formats, each a keyword list of formatter options, used by :name styles.

  • Any other option is given to every formatter, as in ex_cldr_messages; for example currency: :EUR for {price, number, currency}.

Returns

  • {:ok, formatted_message}, or

  • {:error, %Localize.BindError{}} when an argument has no binding, or

  • {:error, exception} when the message cannot be parsed or a value cannot be formatted.

Examples

iex> Localize.Message.MF1.format("{greeting} to you!", greeting: "Good morning")
{:ok, "Good morning to you!"}

iex> Localize.Message.MF1.format("{count, plural, one {# item} other {# items}}", count: 3)
{:ok, "3 items"}

format!(message, bindings \\ [], options \\ [])

@spec format!(String.t(), bindings(), Keyword.t()) :: String.t() | no_return()

Formats a MessageFormat 1 message, raising on error.

Arguments

  • message is a message string, as for format/3.

  • bindings is a keyword list, map or list of bindings.

  • options is a keyword list of options.

Options

Returns

  • The formatted message. An exception is raised when the message cannot be formatted.

Examples

iex> Localize.Message.MF1.format!("{greeting} to you!", greeting: "Good morning")
"Good morning to you!"

iex> Localize.Message.MF1.format!("{{Hello, world!}}")
"Hello, world!"

format_list(parsed, bindings \\ [], options \\ [])

@spec format_list(Localize.Message.MF1.Parser.t(), bindings(), Keyword.t()) ::
  {:ok, list(), list(), list()}
  | {:error, list(), list(), list()}
  | {:error, Exception.t()}

Formats a message parsed by Localize.Message.MF1.Parser.parse/2 to an iolist.

Arguments

  • parsed is a parsed message.

  • bindings is a keyword list, map or list of bindings.

  • options is a keyword list of options.

Options

  • See format/3. :trim and :allow_positional_args apply only when parsing, so they have no effect here.

Returns

Examples

iex> {:ok, parsed} = Localize.Message.MF1.Parser.parse("{count, number} jelly beans")
iex> Localize.Message.MF1.format_list(parsed, [count: 1234], locale: :de)
{:ok, ["1.234", " jelly beans"], ["count"], []}

format_to_iolist(message, bindings \\ [], options \\ [])

@spec format_to_iolist(String.t(), bindings(), Keyword.t()) ::
  {:ok, list(), list(), list()}
  | {:error, list(), list(), list()}
  | {:error, Exception.t()}

Formats a MessageFormat 1 message to an iolist, with the arguments that were and were not bound.

Arguments

  • message is a MessageFormat 1 message string.

  • bindings is a keyword list, map or list of bindings.

  • options is a keyword list of options.

Options

Returns

  • {:ok, iolist, bound, []} where bound lists the arguments used, or

  • {:error, iolist, bound, unbound} when arguments in unbound have no binding; each is written into iolist as its source text, such as {name}, or

  • {:error, exception} when the message cannot be parsed or a value cannot be formatted.

Examples

iex> Localize.Message.MF1.format_to_iolist("{greeting} to you!", greeting: "Good morning")
{:ok, ["Good morning", " to you!"], ["greeting"], []}

jaro_distance(message1, message2, options \\ [])

@spec jaro_distance(String.t(), String.t(), Keyword.t()) ::
  {:ok, float()} | {:error, Exception.t()}

Returns the Jaro distance between the canonical forms of two messages.

It finds a message that changed only a little, such as a translation key edited for readability.

Arguments

  • message1 and message2 are MessageFormat 1 message strings.

  • options is a keyword list of options.

Options

  • :trim is true to trim white space from the messages first, or false (the default).

Returns

  • {:ok, distance} where distance is a float from 0.0 (no similarity) to 1.0 (the same canonical message), or

  • {:error, exception} when either message cannot be parsed.

Examples

iex> Localize.Message.MF1.jaro_distance("{greetings} to you!", "{greeting} to you!")
{:ok, 0.9824561403508771}

jaro_distance!(message1, message2, options \\ [])

@spec jaro_distance!(String.t(), String.t(), Keyword.t()) :: float() | no_return()

Returns the Jaro distance between two messages, raising on error.

Arguments

  • message1 and message2 are MessageFormat 1 message strings.

  • options is a keyword list of options.

Options

Returns

  • A float from 0.0 to 1.0. An exception is raised when either message cannot be parsed.

Examples

iex> Localize.Message.MF1.jaro_distance!("{greetings} to you!", "{greeting} to you!")
0.9824561403508771

mf2?(message)

@spec mf2?(term()) :: boolean()

Returns whether a message is written in MessageFormat 2 rather than MessageFormat 1.

An MF2 message starts with a declaration or matcher (.input, .local, .match) or a quoted pattern ({{); any other message is read as MF1.

Arguments

  • message is a message string.

Returns

  • true when message is an MF2 message, or

  • false otherwise, including when message is not a string.

Examples

iex> Localize.Message.MF1.mf2?("Hello {name}")
false

iex> Localize.Message.MF1.mf2?("{{Hello {$name}}}")
true