Skip to content

Instantly share code, notes, and snippets.

@angelikatyborska
Last active January 16, 2022 16:24
Show Gist options
  • Save angelikatyborska/fadbde5c3d4f2db25a58a4519d3b94ac to your computer and use it in GitHub Desktop.
Save angelikatyborska/fadbde5c3d4f2db25a58a4519d3b94ac to your computer and use it in GitHub Desktop.
A unit test for Gettext translations that checks if the original and the translation use the same HTML tags. Uses Floki to parse HTML.
defmodule MyAppWeb.GettextTest do
use ExUnit.Case
import MyAppWeb.Gettext
# A unit test for Gettext translations that checks if the original and the translation
# use the same HTML tags.
#
# Uses Floki to parse HTML.
describe "translations" do
test "all translation use the same HTML tags as the original" do
files =
"priv/gettext/**/LC_MESSAGES/"
|> Path.wildcard()
|> Enum.map(fn dir -> Enum.map(File.ls!(dir), &{dir, &1}) end)
|> List.flatten()
translations =
Enum.reduce(files, [], fn {dir, file}, acc ->
path = "#{dir}/#{file}"
{:ok, po} =
path
|> File.read!()
|> Gettext.PO.parse_string()
Enum.reduce(po.translations, acc, fn translation, acc2 ->
strings =
case translation do
%{msgstr: strings} when is_list(strings) -> strings
%{msgstr: map} when is_map(map) -> List.flatten(Map.values(map))
end
[
%{
id: translation.msgid,
path: "#{path}:#{translation.po_source_line}",
strings: strings
}
| acc2
]
end)
end)
translations_with_errors =
Enum.reduce(translations, [], fn %{id: id, path: path, strings: strings}, acc ->
expected_tags = get_html_tags_from_string(Enum.join(id, " "))
Enum.reduce(strings, [], fn string, acc2 ->
actual_tags = get_html_tags_from_string(string)
unexpected_tags = actual_tags -- expected_tags
missing_tags = expected_tags -- actual_tags
if string == "" or (unexpected_tags == [] and missing_tags == []) do
acc2
else
[%{path: path, missing_tags: missing_tags, unexpected_tags: unexpected_tags} | acc2]
end
end) ++
acc
end)
assert Enum.empty?(translations_with_errors),
Enum.map(translations_with_errors, fn error ->
"""
#{error.path}:
\tmissing: #{Enum.join(error.missing_tags, ", ")}
\t extra: #{Enum.join(error.unexpected_tags, ", ")}
"""
end)
|> Enum.join("\n")
end
end
defp get_html_tags_from_string(string) do
html = Floki.parse_fragment!(string)
get_html_tags_from_html(html, [])
end
# sort so that order in which tags appear in the string doesn't matter for comparisons
defp get_html_tags_from_html([], acc), do: Enum.sort(acc)
defp get_html_tags_from_html([string | rest], acc) when is_bitstring(string) do
get_html_tags_from_html(rest, acc)
end
defp get_html_tags_from_html([{tag, _attrs, children} | rest], acc) do
children_tags = get_html_tags_from_html(children, [])
get_html_tags_from_html(rest, children_tags ++ [tag | acc])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment