An Ansible callback plugin to support a simple translations system (i18n)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
southerntofu 9c13666a65 Explain the task name issue in more detail 1 year ago
i18n Simple example 1 year ago
.gitignore Initial commit 1 year ago
LICENSE Initial commit 1 year ago Explain the task name issue in more detail 1 year ago wrapper for easy demo 1 year ago Translate more stuff, do not fail when language isn't set 1 year ago
playbook.yml Simple example 1 year ago


This callback plugin for Ansible proposes translations for plays/tasks. It is forked from the excellent dense plugin by Dag Wieers (@dagwieers).


On Debian stable, once ansible is setup, you simply need to copy the file to ansible's plugins/callback directory:

$ git clone
$ sudo cp ansible-i18n/ /usr/lib/python3/dist-packages/ansible/plugins/callback/

Now that the plugin is setup (yes, that was it), you may want to configure Ansible to use it. To use the plugin for a single run, you can use the ANSIBLE_STDOUT_CALLBACK environment variable:

$ ANSIBLE_STDOUT_CALLBACK=i18n ansible-playbook -i hosts playbook.yml

If you want to use the plugin for the whole project, you can set it in the project's root in an ansible.cfg file:

stdout_callback = i18n

Additionally, if you want to use the plugin for all projects, you can set it in ~/.ansible.cfg (user setting) or /etc/ansible/ansible.cfg (system setting).

NOTE: If you're using the ansible command and not ansible-playbook, you need to either set bin_ansible_callbacks field to true in the config, or use the ANSIBLE_LOAD_CALLBACKS_PLUGINS=1 environment variale.


When initializing the plugins, it will look for the i18n folder in your current directory. If it exists, it will load the files in there as separate languages: i18n/fr.yml for french, i18n/en.yml for english, etc. If the folder does not exist or is empty, no translation is used at all.

Once the languages are loaded, the plugin will lookup your current locale through the LANG environment variable. The first two characters are extracted, and if they match a language in the translations, this language is used. If the locale doesn't match a known language :

  • if there is only one language found in the i18n folder, it is used
  • otherwise, no translation is used (what, would you like me to randomly pick a language?)

For each operation type, status and name, the plugin will look for a key in the current language. If this translated key does not exist, the key name itself is used. THis means if your language is only partially translated, you will not get the full experience but it should not blow up in your face.

If you want to translate a task called "Setup web server" to another language, you could simply place a Setup web server: Installer le serveur web line in the i18n/fr.yml file. However, you may want to select a slightly less human-friendly name for the task so that further human beings down the line will not be tempted to edit it. Indeed, once you start translating, changing the task names requires to edit all translations!

Although it is not in practice a problem with very small project, this is why it is strongly advised to keep translations for all languages in the i18n folder. However, as running in debug mode cannot translate task names, you should pick task names that are still somewhat readable, like "setup-web-server".

Setup web server: Installer le serveur web


To get started, you need only a few lines. Here's what i used for a sample i18n/fr.yml:

ok: ok
changed: changéE
ignored: ignoré
failed: échoué
unreachable: injoignable
task: Tâche
handler: Gestionnaire
Gathering Facts: Rassembler les faits

Then, as you add task/play names don't forget to translate them.


An example playbook.yml is provided in this repository for demo purposes. You can run it with the script, which passes all arguments (such as -v and -vvv) to ansible-playbook:

# Following your locale
$ ./
# In french
$ LANG=fr ./
# Display more info about stuff that changed
$ ./ -v


What's your use-case?

Some people may argue it's pointless to translate a playbook: you wrote it so you can understand it, right? However, not everyone writes their own playbooks! I'm currently working on Ansible-based declarative configuration for a french-speaking tilde server and i would like english speakers (and others) to be able to fork our server, and run our playbook in their own language!

I don't think someone should have to learn ansible (or awk for that matter) to selfhost a server, and i also don't think they should have to learn a foreign language to understand what's happening on their machine. That's my usecase.

How do i use a default language as fallback?

Short answer: Define the LANG environment variable properly. If you need to do it for one command only, then just type LANG=fr before your ansible command.

If you really want to have a default language, you can simply write your task/play names in this language, and if they don't have translations, they won't be translated. However, this is strongly discouraged because if you do that, simply changing your task name will break existing translations!

Can it break something?

It should not. If a translation is not found, the standard (untranslated) name is used instead. If something breaks by using this plugin, it's a bug so please report it.

How do i debug things?

The usual ansible flags -v and -vvv apply. -v will provide results on tasks/handlers that changed. -vvv will run the debug output, which means you'll have a lot of information but unfortunately no translations.

Nice term tricks, how did you do?

I didn't. All the super sweet stuff found in this repo was built by @dagwieers so huge thanks to this person for saving me from information overdose.

Is this plugin feature-complete?

This plugin works for me. I hope it helps other people as well. Patches are welcome, but i don't want to maintain a complicated plugin so if that's your project don't hesitate to fork.

There's still a few TODO to print format error messages properly. This is the case for:

  • line 179: YAMLError when loading translation files
  • line 193 and 199: we should print a warning when the languaged defined in the locale was not found

What license is it?

This is GPLv3+ software, because :

  • copyleft is pretty cool (fuck private property, share everything)
  • the original code is GPLv3+ so please don't argue