Catching translation issues early
Published 5 April 2022
Building applications in the European market often means that you sooner or later will have to translate your application to more than one language. This causes changes in workflows, processes around building/changing functionality, how you think about your UI and a lot of more things.
With this comes a certain set of challenges such as ensuring that you are consistently translating your UI, entities and make sure that you don’t forget it in a stressful moment.
At BoardClic we’re making use of the brilliant gettext
package for Elixir
(which is built into Phoenix). Except being a great tool in general it makes it possible
for us to build automated tools that helps us ensure that we’re not forgetting to
translate a string, causing subpar experiences for our users:
mix gettext.extract --check-up-to-date
Implemented in gettext 0.19.0, this little nifty mix task will make certain that all strings passed to any of the gettext-functions are properly extracted into the pot/po-files.
We implemented this as a part of our CI workflow upon release and it has worked
wonders for us. The only thing to remember is that some strings (such as custom
error messages in ecto) will have to be marked for extraction. It’s easily done
by wrapping them in a dgettext_noop/2
.
Test verifying that translations doesn’t have an empty msgstr
While it’s great to be able to ensure that all translations has been extracted successfully it’s also great if we could ensure that they are in fact also translated.
Our way of making sure of that is to have a test that looks at your translations and simply checks if any of them are missing:
defmodule TranslationIntegrityTest do
use ExUnit.Case, async: true
@locales Application.compile_env!(:myapp, [MyAppWeb.Gettext, :locales])
@default_locale Application.compile_env!(:myapp, [MyAppWeb.Gettext, :default_locale])
@translation_files ~w[default errors]
for locale <- @locales -- [@default_locale] do
describe "`#{locale}` translations" do
for file_name <- @translation_files do
@translation Gettext.PO.parse_file!("priv/gettext/#{locale}/LC_MESSAGES/#{file_name}.po")
test "translates #{file_name} text" do
untranslated_translations = Enum.filter(@translation.translations, &(&1.msgstr == [""]))
unless Enum.empty?(untranslated_translations) do
missing_translations =
build_missing_translations_error_string(
untranslated_translations,
@translation.file
)
flunk(missing_translations)
end
end
end
end
end
def build_missing_translations_error_string(untranslated_translations, file_name) do
translation_str =
Enum.map_join(untranslated_translations, "\n", fn translation ->
"""
"#{translation.msgid}"
#{file_name}:#{translation.po_source_line}
"""
end)
"""
The following strings were not translated:
#{translation_str}
"""
end
end
In case there is a missing translation we’d end up with this helpful message:
1) test `sv_SE` translations translates errors text (TranslationIntegrityTest)
test/translation_integrity_test.exs:13
The following strings were not translated:
"is invalid"
priv/gettext/sv_SE/LC_MESSAGES/errors.po:20
code: flunk(missing_translations)
stacktrace:
test/translation_integrity_test.exs:23: (test)
Automating translations
Since not everyone in our team are native Swedes we don’t want to enable them to
build features on their own without the need to bring someone else into the mix
whenever there is a string changed/added somewhere. To aid this we’ve built our
own little auto-translator on top of gettext
which adds a mix task that
can enable a faster workflow by simply filling in the blanks.
While this isn’t a true solution and quality of the translations needs to be checked we’re still handling parts of this in pull requests. However it’s always nicer to have something that can be improved than being forced to add the translations yourself as a reviewer.
Since there is still improvements to be made on our auto-translator we’re going to smoothen the edges a bit before we share it.
Closing words
We hope that our workflow around translations can help you and your team even if it’s just inspiration. We’re very happy with the extra layer of quality assurance we get from the described tooling.