Packages
A tool for pseudolocalizing Gettext translations in Elixir applications. Helps identify hardcoded strings, character encoding issues, and UI problems by converting ASCII characters to similar-looking Unicode characters with text expansion simulation.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/gettext.pseudolocalize.ex
defmodule Mix.Tasks.Gettext.Pseudolocalize do
use Mix.Task
@shortdoc "Creates a pseudolocalized version of the xx locale"
@moduledoc """
Creates a pseudolocalized version of the xx locale for testing internationalization.
Pseudolocalization is a software testing method used to verify internationalization aspects
of software. It replaces ordinary characters with accented, uncommon, or otherwise modified
versions while maintaining the readability of the text.
## Usage
First, extract your gettext strings:
$ mix gettext.extract
Then run the pseudolocalization:
$ mix gettext.pseudolocalize
This will process all .po files in the xx locale directory (priv/gettext/xx/LC_MESSAGES/).
"""
@impl Mix.Task
def run(args) do
# First extract and merge
target_dir =
case args do
[custom_dir] ->
custom_dir
_ ->
with_xx_pluralizer(fn ->
Mix.Task.run("gettext.merge", ["priv/gettext", "--locale=xx"])
end)
Path.join(["priv", "gettext", "xx", "LC_MESSAGES"])
end
# Process all .po files in the target directory
target_dir
|> Path.join("*.po")
|> Path.wildcard()
|> Enum.each(&process_file(&1))
Mix.shell().info([:green, "✓ Pseudolocalization complete"])
end
# gettext ~> 1.0 raises `Gettext.Plural.UnknownLocaleError` when creating a new
# PO file for "xx" because the default `Gettext.Plural` has no entry for it.
# Temporarily swap in `GettextPseudolocalize.Plural`, which knows about "xx"
# and delegates everything else to the default, then restore the previous
# configuration so we don't affect the host application.
defp with_xx_pluralizer(fun) do
previous = Application.get_env(:gettext, :plural_forms)
Application.put_env(:gettext, :plural_forms, GettextPseudolocalize.Plural)
try do
fun.()
after
case previous do
nil -> Application.delete_env(:gettext, :plural_forms)
mod -> Application.put_env(:gettext, :plural_forms, mod)
end
end
end
defp process_file(file_path) do
with {:ok, po} <- Expo.PO.parse_file(file_path),
updated_po = update_messages(po) do
content = Expo.PO.compose(updated_po)
File.write!(file_path, content)
Mix.shell().info("Processed #{Path.basename(file_path)}")
end
end
defp update_messages(%Expo.Messages{} = po) do
updated_messages = Enum.map(po.messages, &update_message/1)
%{po | messages: updated_messages}
end
defp update_message(%Expo.Message.Singular{msgstr: [""]} = message) do
%{message | msgstr: [pseudolocalize(message.msgid)]}
end
defp update_message(%Expo.Message.Plural{msgstr: msgstr} = message) when is_map(msgstr) do
updated_msgstr =
Map.new(msgstr, fn
{index, [""]} when index == 0 -> {index, [pseudolocalize(message.msgid)]}
{index, [""]} -> {index, [pseudolocalize(message.msgid_plural)]}
other -> other
end)
%{message | msgstr: updated_msgstr}
end
defp update_message(message), do: message
defp pseudolocalize([string]) when is_binary(string) do
GettextPseudolocalize.Process.convert(string)
end
end