From: Ben Darnell Date: Wed, 7 Apr 2010 23:54:34 +0000 (-0700) Subject: Add support for reading translation from gettext .mo files instead of CSV. X-Git-Tag: v1.0.0~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=62bc2e40fd24d5389aa7da66cabab6246abd6eec;p=thirdparty%2Ftornado.git Add support for reading translation from gettext .mo files instead of CSV. Based on http://github.com/wmark/anzu/commits/gettext --- diff --git a/tornado/locale.py b/tornado/locale.py index a1dfad5d3..6a8537d75 100644 --- a/tornado/locale.py +++ b/tornado/locale.py @@ -32,6 +32,11 @@ additional arguments to translate(), e.g.: The first string is chosen if len(people) == 1, otherwise the second string is chosen. + +Applications should call one of load_translations (which uses a simple +CSV format) or load_gettext_translations (which uses the .mo format +supported by gettext and related tools). If neither method is called, +the locale.translate method will simply return the original string. """ import csv @@ -44,6 +49,7 @@ import re _default_locale = "en_US" _translations = {} _supported_locales = frozenset([_default_locale]) +_use_gettext = False _log = logging.getLogger('tornado.locale') @@ -128,6 +134,42 @@ def load_translations(directory): _supported_locales = frozenset(_translations.keys() + [_default_locale]) _log.info("Supported locales: %s", sorted(_supported_locales)) +def load_gettext_translations(directory, domain): + """Loads translations from gettext's locale tree + + Locale tree is similar to system's /usr/share/locale, like: + + {directory}/{lang}/LC_MESSAGES/{domain}.mo + + Three steps are required to have you app translated: + + 1. Generate POT translation file + xgettext --language=Python --keyword=_:1,2 -d cyclone file1.py file2.html etc + + 2. Merge against existing POT file: + msgmerge old.po cyclone.po > new.po + + 3. Compile: + msgfmt cyclone.po -o {directory}/pt_BR/LC_MESSAGES/cyclone.mo + """ + import gettext + global _translations + global _supported_locales + global _use_gettext + _translations = {} + for lang in os.listdir(directory): + if os.path.isfile(os.path.join(directory, lang)): continue + try: + os.stat(os.path.join(directory, lang, "LC_MESSAGES", domain+".mo")) + _translations[lang] = gettext.translation(domain, directory, + languages=[lang]) + except Exception, e: + logging.error("Cannot load translation for '%s': %s", lang, str(e)) + continue + _supported_locales = frozenset(_translations.keys() + [_default_locale]) + _use_gettext = True + _log.info("Supported locales: %s", sorted(_supported_locales)) + def get_supported_locales(cls): """Returns a list of all the supported locale codes.""" @@ -162,8 +204,14 @@ class Locale(object): cls._cache = {} if code not in cls._cache: assert code in _supported_locales - translations = _translations.get(code, {}) - cls._cache[code] = Locale(code, translations) + translations = _translations.get(code, None) + if translations is None: + locale = CSVLocale(code, {}) + elif _use_gettext: + locale = GettextLocale(code, translations) + else: + locale = CSVLocale(code, translations) + cls._cache[code] = locale return cls._cache[code] def __init__(self, code, translations): @@ -180,29 +228,14 @@ class Locale(object): _ = self.translate self._months = [ _("January"), _("February"), _("March"), _("April"), - _("May"), _("June"), _("July"), _("August"), + _("May"), _("June"), _("July"), _("August"), _("September"), _("October"), _("November"), _("December")] self._weekdays = [ _("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), _("Friday"), _("Saturday"), _("Sunday")] def translate(self, message, plural_message=None, count=None): - """Returns the translation for the given message for this locale. - - If plural_message is given, you must also provide count. We return - plural_message when count != 1, and we return the singular form - for the given message when count == 1. - """ - if plural_message is not None: - assert count is not None - if count != 1: - message = plural_message - message_dict = self.translations.get("plural", {}) - else: - message_dict = self.translations.get("singular", {}) - else: - message_dict = self.translations.get("unknown", {}) - return message_dict.get(message, message) + raise NotImplementedError() def format_date(self, date, gmt_offset=0, relative=True, shorter=False, full_format=False): @@ -329,6 +362,34 @@ class Locale(object): value = value[:-3] return ",".join(reversed(parts)) +class CSVLocale(Locale): + """Locale implementation using tornado's CSV translation format.""" + def translate(self, message, plural_message=None, count=None): + """Returns the translation for the given message for this locale. + + If plural_message is given, you must also provide count. We return + plural_message when count != 1, and we return the singular form + for the given message when count == 1. + """ + if plural_message is not None: + assert count is not None + if count != 1: + message = plural_message + message_dict = self.translations.get("plural", {}) + else: + message_dict = self.translations.get("singular", {}) + else: + message_dict = self.translations.get("unknown", {}) + return message_dict.get(message, message) + +class GettextLocale(Locale): + """Locale implementation using the gettext module.""" + def translate(self, message, plural_message=None, count=None): + if plural_message is not None: + assert count is not None + return self.translations.ungettext(message, plural_message, count) + else: + return self.translations.ugettext(message) LOCALE_NAMES = { "af_ZA": {"name_en": u"Afrikaans", "name": u"Afrikaans"},