GettextOps.Operations.Translate (gettext_ops v0.1.1)

View Source

Translation update operation for bulk updating msgstr values in .po files.

This module provides functionality to update translations in .po files from various input sources (stdin, file, or inline). It accepts translations in a simple format: msgid = msgstr (one per line).

This is the most important command for the LLM workflow, enabling bulk translation updates without manually editing .po files.

Input Format

Each line should contain a msgid and msgstr separated by =:

Sign In = Logga in
Sign Out = Logga ut
Welcome = Välkommen
Error: Invalid input = Fel: Ogiltig inmatning

Empty lines and lines starting with # are ignored.

Entries with a msgctxt

Gettext identifies an entry by the pair {msgctxt, msgid}, so a catalogue may hold several entries with the same msgid under different contexts. The input format carries only a msgid, which is resolved as follows:

  • exactly one entry has that msgid — it is updated;
  • several do — the contextless entry is updated, and context-carrying siblings are left untouched;
  • the msgid exists only under two or more contexts — it cannot be resolved, and is reported in :ambiguous rather than guessed at.

Atomic Updates

Updates are performed atomically by writing to a temporary file and then renaming it to the original file. This prevents corruption if the operation fails midway.

Options

  • :locale - (required) The locale to update (e.g., "sv", "en")
  • :domain - The domain to update (defaults to configured default_domain)
  • :force - Continue even if some msgids are not found or ambiguous (default: false)

Examples

# Parse translation input
{:ok, translations} = GettextOps.Operations.Translate.parse_translations("""
Sign In = Logga in
Sign Out = Logga ut
""")

# Apply translations
{:ok, result} = GettextOps.Operations.Translate.run(translations, locale: "sv")
# => {:ok, %{updated: 2, not_found: []}}

# With force flag (ignore missing msgids)
{:ok, result} = GettextOps.Operations.Translate.run(translations, locale: "sv", force: true)

Summary

Functions

Parse translation input in msgid = msgstr format.

Apply translations to a .po file.

Functions

parse_translations(input)

@spec parse_translations(String.t()) :: {:ok, %{required(String.t()) => String.t()}}

Parse translation input in msgid = msgstr format.

Accepts a string with one translation per line. Empty lines and lines starting with # are ignored. Lines that don't contain = are silently skipped.

Returns {:ok, translations_map} where the map has msgid as keys and msgstr as values.

Examples

iex> input = """
...> Sign In = Logga in
...> Sign Out = Logga ut
...> # This is a comment
...>
...> Welcome = Välkommen
...> """
iex> {:ok, translations} = GettextOps.Operations.Translate.parse_translations(input)
iex> translations
%{"Sign In" => "Logga in", "Sign Out" => "Logga ut", "Welcome" => "Välkommen"}

run(translations, opts)

@spec run(
  map(),
  keyword()
) ::
  {:ok,
   %{
     updated: non_neg_integer(),
     not_found: [String.t()],
     ambiguous: [{String.t(), [String.t()]}]
   }}
  | {:error, term()}

Apply translations to a .po file.

Takes a map of translations (msgid => msgstr) and updates the corresponding entries in the .po file for the given locale.

Parameters

  • translations - Map of msgid => msgstr to apply
  • opts - Keyword list of options (see module documentation)

Returns

  • {:ok, result} - Success with update statistics
  • {:error, reason} - Failure with error reason

The result map contains:

  • :updated - Number of translations successfully applied
  • :not_found - List of msgids that were not found in the .po file
  • :ambiguous - List of {msgid, contexts} for msgids that exist only under two or more different msgctxt values and so could not be resolved

Examples

# Update translations
translations = %{"Sign In" => "Logga in", "Sign Out" => "Logga ut"}
{:ok, result} = GettextOps.Operations.Translate.run(translations, locale: "sv")
# => {:ok, %{updated: 2, not_found: []}}

# With missing msgid (without force)
translations = %{"Nonexistent" => "Translation"}
{:error, reason} = GettextOps.Operations.Translate.run(translations, locale: "sv")
# => {:error, "msgid not found: Nonexistent"}

# With missing msgid (with force)
{:ok, result} = GettextOps.Operations.Translate.run(translations, locale: "sv", force: true)
# => {:ok, %{updated: 0, not_found: ["Nonexistent"]}}