Filters

Let’s go ahead and add to our application message template.

# coding: utf-8

from h10n import Translator


locales = {
    'en-US': {
        'message': {
            'removed': {
                'filter': """
                    $object = self.locale['object'][$object].format()
                """,
                'msg': u'{object} has been successfully removed'
            }
        },
        'object': {
            'article': u'Article',
            'comment': u'Comment',
        },
    },
    'ru-RU': {
        'object': {
            'article': u'Статья',
            'comment': u'Комментарий',
        },
    },
}
t = Translator(locales=locales, default='en-US')

assert(t.translate('message:removed', 'Object has been removed', object='article') ==
       u'Article has been successfully removed')
assert(t.translate('message:removed', 'Object has been removed', object='comment') ==
       u'Comment has been successfully removed')

t.lang = 'ru'
assert(t.translate('message:removed', 'Object has been removed', object='article') ==
       u'Article has been successfully removed')
assert(t.translate('message:removed', 'Object has been removed', object='comment') ==
       u'Comment has been successfully removed')

There are two interesting things. Let’s start from last one. We don’t added catalog message into Russian locale. So, translator used English (default) locale as fallback one to translate wrong Russian messages. It also logged debug information, but it failed with message “No handlers could be found for logger “h10n.translator” (this message will be fired to stderr if you run this program), because we didn’t configure logger. We will do it later, on learning h10n Debugging.

Most interesting thing is message:removed translation message, which is defined using full form of definition. The best way to show difference between full and simple forms is to look on how message object is constructed. If you look into sources of h10n.core.Catalog, which constructs h10n.core.Message objects from definitions, you will see, that it’s little bit complex than this:

if isinstance(message, basestring):
    message = {'msg': message}
message = Message(**message)

A translation string is provided via msg keyword argument. Other keyword argument is filter one. Filter is just Python code with little pinch of syntax sugar. Filter makes all dirty work on translation. How it work? Method translate of translator object accepts keyword arguments. When it find specified message, it call message’s h10n.core.Message.format() method passing these arguments. Method format process provided arguments using filter and calls format method of translation string msg, using filtered arguments (last format method is standard method of str and unicode objects, see PEP 3101 for details).

So, string:

$object = self.locale['object'][$object].format()

Becomes a function, which accept message object as first argument and dict of keyword arguments as second one:

def filter(self, kw):
    kw['object'] = self.locale['object'][kw['object']].format()

Here we used locale attribute of h10n.core.Message object to get access to parent locale. Method __getitem__ of h10n.core.Locale returns catalog object. Method __getitem__ of h10n.core.Catalog returns message. Method format of message object returns formatted translation string. So, this call in message filter:

self.locale['object']['article'].format()

...performs same operations as this one:

t.translate('object:article', u'Article')

Let’s do the same thing in Russian. Take notice, Russian have other rules than English. We can’t simply substitute object name into translation string of message:removed. Verb “удалять” (to remove) should be inflected according to noun’s gender, which represent object name. There are three noun’s gender in Russian: masculine, feminine and neuter.

# coding: utf-8

from h10n import Translator


locales = {
    'en-US': {
        'message': {
            'removed': {
                'filter': """
                    $object = self.locale['object'][$object].format()
                """,
                'msg': u'{object} has been successfully removed'
            }
        },
        'object': {
            'article': u'Article',
            'comment': u'Comment',
        },
    },
    'ru-RU': {
        'message': {
            'removed': {
                'filter': """
                    $object = self.locale['object'][$object]
                    $gender = $object.gender
                    $object = $object.format()
                """,
                'key': '{gender}',
                'msg': {
                    'm': u'{object} успешно удален',
                    'f': u'{object} успешно удалена',
                    'n': u'{object} успешно удалено',
                }
            }
        },
        'object': {
            'article': {
                'msg': u'Статья',
                'gender': 'f',
            },
            'comment': {
                'msg': u'Комментарий',
                'gender': 'm',
            },
        },
    },
}
t = Translator(locales=locales, default='en-US')

assert(t.translate('message:removed', 'Object has been removed', object='article') ==
       u'Article has been successfully removed')
assert(t.translate('message:removed', 'Object has been removed', object='comment') ==
       u'Comment has been successfully removed')

t.lang = 'ru'
assert(t.translate('message:removed', 'Object has been removed', object='article') ==
       u'Статья успешно удалена')
assert(t.translate('message:removed', 'Object has been removed', object='comment') ==
       u'Комментарий успешно удален')

Here we added gender attribute to the messages from object catalog, and use this attribute on translation message:removed to select appropriate translation string. The best explanation, how it works, is a source code of Message.format method:

def format(self, **kw):
    params = self.defaults.copy()
    params.update(kw)
    if self.filter:
        self.filter(self, params)
    msg = self.msg
    if self.key is not None:
        key = self.key.format(**params)
        msg = msg[key]
    return msg.format(**params)

Project Versions

Previous topic

Basic Translation

Next topic

Helpers

This Page