Internationalizing strings in extensions

See also

In order to internationalize your extension you must mark the strings for internationalization. You can find out how to do this by reading Translating CKAN.

See also

In this tutorial we are assuming that you have read the Writing extensions tutorial.

We will create a simple extension to demonstrate the translation of strings inside extensions. After running:

paster --plugin=ckan create -t ckanext ckanext-itranslation

Change the plugin.py file to:

# encoding: utf-8

from ckan import plugins
from ckan.plugins import toolkit


class ExampleITranslationPlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)

    def update_config(self, config):
        toolkit.add_template_directory(config, 'templates')

Add a template file ckanext-itranslation/templates/home/index.html containing:

{% ckan_extends %}

{% block primary_content %}
{% trans %}This is an untranslated string{% endtrans %}
{% endblock %}

This template provides a sample string that we will internationalize in this tutorial.

Extract strings

Tip

If you have generated a new extension whilst following this tutorial the default template will have generated these files for you and you can simply run the extract_messages command immediately.

Check your setup.py file in your extension for the following lines

setup(
    entry_points='''
        [ckan.plugins]
        itranslation=ckanext.itranslation.plugin:ExampleITranslationPlugin
        [babel.extractors]
        ckan = ckan.lib.extract:extract_ckan
    '''

    message_extractors={
        'ckanext': [
            ('**.py', 'python', None),
            ('**.js', 'javascript', None),
            ('**/templates/**.html', 'ckan', None),
        ],
    }

These lines will already be present in our example, but if you are adding internationalization to an older extension, you may need to add them. If you have your templates in a directory differing from the default location (ckanext/yourplugin/i18n), you may need to change the message_extractors stanza. You can read more about message extractors in the babel documentation.

Add a directory to store your translations:

mkdir ckanext-itranslations/ckanext/itranslations/i18n

Next you will need a babel config file. Add a setup.cfg file containing the following (make sure you replace itranslations with the name of your extension):

[extract_messages]
keywords = translate isPlural
add_comments = TRANSLATORS:
output_file = ckanext/itranslation/i18n/ckanext-itranslation.pot
width = 80

[init_catalog]
domain = ckanext-itranslation
input_file = ckanext/itranslation/i18n/ckanext-itranslation.pot
output_dir = ckanext/itranslation/i18n

[update_catalog]
domain = ckanext-itranslation
input_file = ckanext/itranslation/i18n/ckanext-itranslation.pot
output_dir = ckanext/itranslation/i18n

[compile_catalog]
domain = ckanext-itranslation
directory = ckanext/itranslation/i18n
statistics = true

This file tells babel where the translation files are stored. You can then run the extract_messages command to extract the strings from your extension:

python setup.py extract_messages

This will create a template PO file named ckanext/itranslations/i18n/ckanext-itranslation.pot.

At this point, you can either upload and manage your translations using Transifex or manually edit your translations.

Manually create translations

We will create translation files for the fr locale. Create the translation PO files for the locale that you are translating for by running init_catalog:

python setup.py init_catalog -l fr

This will generate a file called i18n/fr/LC_MESSAGES/ckanext-itranslation.po. This file should contain the untranslated string on our template. You can manually add a translation for it by editing the msgstr section:

msgid "This is an untranslated string"
msgstr "This is a itranslated string"

Translations with Transifex

Once you have created your translations, you can manage them using Transifex. This is out side of the scope of this tutorial, but the Transifex documentation provides tutorials on how to upload translations and how to manage them using the command line client.

Compiling the catalog

Once the translation files (po) have been updated, either manually or via Transifex, compile them by running:

python setup.py compile_catalog

This will generate a mo file containing your translations that can be used by CKAN.

The ITranslation interface

Once you have created the translated strings, you will need to inform CKAN that your extension is translated by implementing the ITranslation interface in your extension. Edit your plugin.py to contain the following.

# encoding: utf-8

from ckan import plugins
from ckan.plugins import toolkit
from ckan.lib.plugins import DefaultTranslation


class ExampleITranslationPlugin(plugins.SingletonPlugin, DefaultTranslation):
    plugins.implements(plugins.ITranslation)
    plugins.implements(plugins.IConfigurer)

    def update_config(self, config):
        toolkit.add_template_directory(config, 'templates')

You’re done! To test your translated extension, make sure you add the extension to your /etc/ckan/default/development.ini, run a paster serve command and browse to http://localhost:5000. You should find that switching to the fr locale in the web interface will change the home page string to this is an itranslated string.

Advanced ITranslation usage

If you are translating a CKAN extension that already exists, or you have structured your extension differently from the default layout. You may have to tell CKAN where to locate your translated files, you can do this by not having your plugin inherit from the DefaultTranslation class and instead implement the ITranslation interface yourself.

i18n_directory() Change the directory of the .mo translation files
i18n_locales() Change the list of locales that this plugin handles
i18n_domain() Change the gettext domain handled by this plugin

JavaScript

Extensions currently cannot provide their own translations for JavaScript strings.

However, they can re-use existing JavaScript translations from CKAN. See Internationalizing strings in JavaScript code for details.