Pocoo

open source bulletin board


Howto Internationalize

Pocoo comes with gettext support. You can mark strings as translatable by prefixing it with an underscore '_'. This normaly happens directly when accessing i18n variables, using the request.translate_string method:

def some_method(req, name):
    _ = req.gettext
    return _('Hello %s!') % name

The gettext method also supports plural forms:

def other_method(req, seconds):
    _ = req.gettext
    return _('%d Second', '%d Seconds', seconds) % seconds

The plural form the system chooses depends on the .po file since languages can contain of more than 2 plural forms.

Templates can also contain gettext strings:

{% for user in users %}
    {% trans %}
        {{ user.name }} is {{ user.age }} years old.
    {% endtrans %}
{% endfor %}

{% trans "A small string with %d chars.", myvar %}

Plural support is a bit mor complex:

{% for user in users %}
    {% trans pluralizing user.mails %}
        The user {{ user.name }} has only one mail address: {{ user.mail }}
    {% plural %}
        The user {{ user.name }} has {{ user.mails|count }} mail addresses.
    {% endtrans %}
{% endfor %}

There is no plural support for one line {% trans %} tags.

Pocoo ships a script called build_gettext which looks for translateable strings in python files and template, creates a .po file which translators can translate in several languages using poedit or other translation tools supporting gettext.

In case of only having those two strings from above a .po file would look like that:

# Pocoo Language File
#
msgid ""
msgstr ""
"Project-Id-Version: Pocoo 0.0.1\n"
"POT-Creation-Date: 2006-03-01 06:10:38.547541\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: utf-8\n"
"Generated-By: build_gettext\n"

#: myfile.py:20
msgid "Hello %s!"
msgstr ""

#: otherfile.py:41
msgid "%d Second"
msgid_plural "%d Seconds"
msgstr ""

#: core.pkg/templates/mytemplate.html:?
msgid "%(user.name)s is %(user.age)s old."
msgstr ""

#: core.pkg/templates/mytemplate.html:?
msgid "A small string with %d chars."
msgstr ""

#: core.pkg/templates/pluraltmpl.html:?
msgid "The user %(user.name)s has only one mail address: %(user.mail)s"
msgid_plural "The user %(user.name)s has %(user.mails)s mail addresses."
msgsr ""

Loading Language Files

If you want to provide language files (escpecially when you want to internationalize your own plugin) you have to provide compiled gettext language files in the i18n folder of your package. (It's better when you ship the .po too )

Pocoo looks up all language files in all i18n folders and adds them to the global language file registration. If you want a german language file you call it de.mo pocoo automatically loads it and can use the strings.

Template Notes

The {% trans %} block doesn't allow tags inside of it. You can only use {{ variable }} which gets converted into a stripped python format string. (Leading and trailing whitespaced get removed)

Complex parts have to get splitted into smaller ones:

{% for user in userlist %}
   {% if user.dead %}
       {% trans %}The user {{ user.name }} is marked as dead.{% endtrans %}
   {% else %}
       {% trans %}The user {{ user.name }} is {{ user.age }} years old.{% endtrans %}
   {% endif %}
{% elsefor %}
   {% trans %}There are no users in the database.{% endtrans %}
{% endfor %}

And here the generated .po msgids:

msgid "The user %(user.name)s is marked as dead."
msgid "The user %(user.name)s is %(user.age)s years old."
msgid "There are no users in the database."

Date/Time Formatting

The pocoo l10n module from the code packages ships some template tags for formatting datetime objects. It implements the common strftime format chars as well as some other:

Directive Meaning
%A full weekday name.
%B full month name.
%H Hour (24-hour clock) as a decimal number [00,23].
%I Hour (12-hour clock) as a decimal number [01,12].
%J Day of the year as decimal number [1,366].
%M Minute as a decimal number [00,59].
%N Minute as a decimal number [0,59].
%P Locale's equivalent of either a.m. or p.m.
%S English ordinal suffix for the day of the month, 2 characters; i.e. 'st', 'nd', 'rd' or 'th'
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.
%Y Year with century as a decimal number.
%a abbreviated weekday name.
%b abbreviated month name.
%d Day of the month as a decimal number [01,31].
%j Day of the year as a decimal number [001,366].
%m Month as a decimal number [01,12].
%n Month as a decimal number [1,12].
%p Locale's equivalent of either AM or PM.
%s Second as a decimal number [0,61].
%t Number of days in the given month; i.e. '28' to '31'
%u Weekday as a decimal number [0(Monday),6].
%v Week number of the year (Monday as the first day of the week) as a decimal number [0,53]. All days in a new year preceding the first Monday are considered to be in week 0.
%w Weekday as a decimal number [0(Sunday),6].
%y Year without century as a decimal number [00,99].