Modern applications must be able to handle different languages. Internationalization (i18n) in pycerberus refers to validating locale-dependent input data (e.g. different decimal separator characters) as well as validation errors in different languages. The former aspect is not yet covered by default but you should be able to write custom validators easily.
All messages from validators included in pycerberus can be translated in different languages using the standard gettext library. The language of validation error messages will be chosen depending on the locale which is given in the state dictionary,
i18n support in pycerberus is a bit broader than just translating existing error messages. i18n becomes interesting when you write your own validators (based on the ones that come with pycerberus) and your translations need to play along with the built-in ones:
All i18n support in pycerberus aims to provide custom validators with a nice, simple-to-use API while maintaining the flexibility that serious applications need.
If you want to get translated error messages from a validator, you set the correct ‘’context’‘. formencode looks for a key named ‘locale’ in the context dictionary:
validator = IntegerValidator()
validator.process('foo', context={'locale': 'en'}) # u'Please enter a number.'
validator.process('foo', context={'locale': 'de'}) # u'Bitte geben Sie eine Zahl ein.'
Usually you don’t have to know much about how pycerberus uses gettext internally. Just for completeness: The default domain is ‘pycerberus’. By default translations (.mo files) are loaded from pycerberus.locales, with a fall back to the system-wide locale dir ‘’/usr/share/locale’‘.
To translate messages from a custom validator, you need to declare them in the messages() method and mark the message strings as translatable:
from pycerberus.api import Validator
from pycerberus.i18n import _
class MyValidator(Validator):
def messages(self):
return {
'foo': _('A message.'),
'bar': _('Another message.'),
}
# your validation logic ...
Afterwards you just have to start the usual gettext process. I always use Babel because it provides the very convenient pybabel tool which simplifies the workflow a lot:
Assume your custom validator is a subclass of a built-in validator but you don’t like the built-in translation. Of course you can replace pycerberus’ mo files directly. However there is also another way where you don’t have to change pycerberus itself:
class CustomValidatorThatOverridesTranslations(Validator):
def messages(self):
return {'empty': _('My custom message if the value is empty'),
'custom': _('A custom message')}
# ...
This validator will use a different message for the ‘empty’ error and you can define custom translations for this key in your own .po files.
The gettext library is configurable, e.g. in which directory your .mo files are located and which domain (.mo filename) should be used. In pycerberus this is configurable by validator:
class ValidatorWithCustomGettextOptions(Validator):
def messages(self):
return {'custom': _('A custom message')}
def translation_parameters(self, context):
return {'domain': 'myapp', 'localedir': '/home/foo/locale'}
# ...
These translation parameters are passed directly to the ‘’gettext’’ call so you can read about the available options in the gettext documentation. Your parameter will be applied for all messages which were declared in your validator class (but not in others). So you can modify the parameters for your own validator but keep all the existing parameters (and translations) for built-in validators.
Sometimes you don’t want to use gettext. For instance you could store translations in a relational database so that your users can update the messages themselves without fiddling with gettext tools:
class ValidatorWithNonGettextTranslation(FrameworkValidator):
def messages(self):
return {'custom': _('A custom message')}
def translate_message(self, key, native_message, translation_parameters, context):
# fetch the translation for 'native_message' from somewhere
translated_message = get_translation_from_db(native_message)
return translated_message
You can use this mechanism to plug in arbitrary translation systems into gettext. Your translation mechanism is (again) only applied to keys which were defined by your specific validator class. If you want to use your translation system also for keys which were defined by built-in validators, you need to re-define these keys in your class as shown in the previous section.