]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add support for reading translation from gettext .mo files instead of CSV.
authorBen Darnell <bdarnell@beaker.local>
Wed, 7 Apr 2010 23:54:34 +0000 (16:54 -0700)
committerBen Darnell <bdarnell@beaker.local>
Wed, 7 Apr 2010 23:54:34 +0000 (16:54 -0700)
Based on http://github.com/wmark/anzu/commits/gettext

tornado/locale.py

index a1dfad5d36f61402070073dd0fc230c36f4d2fdf..6a8537d7500981941a68cab6624c78ad8fc23842 100644 (file)
@@ -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"},