[svn] implemented function to collect translations and documented it
authorArmin Ronacher <armin.ronacher@active-4.com>
Mon, 19 Mar 2007 16:52:24 +0000 (17:52 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Mon, 19 Mar 2007 16:52:24 +0000 (17:52 +0100)
--HG--
branch : trunk

docs/src/contextenv.txt
docs/src/i18n.txt [new file with mode: 0644]
docs/src/index.txt
jinja/environment.py
jinja/utils.py

index 28ca2e2814e8f666aa559284dd9461a958ff40cb..bdf2228835461999b72e1cc203fc44fce195e16a 100644 (file)
@@ -33,6 +33,13 @@ in the template evaluation code you may want to override:
     If you don't provide a translator a default one is used to switch
     between singular and plural forms.
 
+    Have a look at the `i18n`_ section for more information.
+
+**def** `get_translations` *(self, name)*:
+
+    Get the translations for the template `name`. Only works if a loader
+    is present. See the `i18n`_ section for more details.
+
 **def** `apply_filters` *(self, value, context, filters)*:
 
     Now this function is a bit tricky and you usually don't have to override
@@ -132,5 +139,6 @@ or to change the way it behaves.
     Return `True` if `name` exists in the context.
 
 
+.. _i18n: i18n.txt
 .. _Quickstart: devintro.txt
 .. _gettext documentation: http://docs.python.org/lib/module-gettext.html
diff --git a/docs/src/i18n.txt b/docs/src/i18n.txt
new file mode 100644 (file)
index 0000000..18bd86f
--- /dev/null
@@ -0,0 +1,72 @@
+====================
+Internationalization
+====================
+
+Jinja includes support for internationalized templates. Because usually the
+application includes i18n/l10n code too there is no script to collect
+translatable strings and no default translation interface. A simple
+implementation wouldn't fit into every application so there are a few things
+you have to do.
+
+
+Writing A Translator
+====================
+
+The most important thing is writing a translator and subclassing the
+Environment so that Jinja knows about the translator. Then you have to
+think of how to resolve the current language. You probably use Jinja in a
+multithreaded environment where each thread (request) might want to have
+a different language. The best way is putting the current language into
+the context, this can work automatically if you create a helper function
+for template rendering. But that's up to you.
+
+However. For many web applications this might be a way:
+
+.. sourcecode:: python
+
+    from jinja import Environment
+    from myapplication import get_translator
+
+    class ApplicationTranslator(object):
+
+        def __init__(self, language):
+            self.language = language
+            self.translator = get_translator(language)
+
+        def gettext(self, string):
+            return self.translator.ugettext(string)
+
+        def ngettext(self, singular, plural, n):
+            return self.translator.ungettext(singuarl, plural, n)
+
+
+    class ApplicationEnvironment(Environment):
+
+        def get_translator(self, context):
+            return ApplicationTranslator(context['LANGUAGE'])
+
+
+    env = ApplicationEnvironment()
+    tmpl = env.get_template('index.html')
+    tmpl.render(LANGUAGE='de_DE')
+
+
+Collecting Translations
+=======================
+
+The next step is to collect the translations. Every Jinja environment
+provides a function called `get_translations` which collects all
+translatable strings from an template.
+
+Example:
+
+.. sourcecode:: pycon
+
+    >>> env.get_translations('index.html')
+    [(1, u'foo', None), (2, u'Foo', None), (3, u'%d Foo', u'%d Foos')]
+
+The first item in the tuple is the linenumer, the second one is the
+singular form and the third is the plural form if given.
+
+Because Jinja is not bound to gettext you can now use these strings to
+create translation files for any translation system.
index 64085e33fdbeda71c75676c3a8fedd436c4c824b..0940fb748a3570464ecd87f6077f8ecea95d4bc5 100644 (file)
@@ -26,6 +26,8 @@ Welcome in the Jinja documentation.
 
   - `Debugging Support <debugging.txt>`_
 
+  - `Internationalization <i18n.txt>`_
+
 - Template Designer Documentation:
 
   - `Syntax Reference <designerdoc.txt>`_
@@ -33,12 +35,3 @@ Welcome in the Jinja documentation.
   - `Differences To Django <fromdjango.txt>`_
 
   - `Recipies <recipies.txt>`_
-
-Undocumented So Far
--------------------
-
-- i18n for Developers
-
-- Writing Custom Loaders
-
-- Changelog / Authors
index ee988b2027fb0937afa5ce6b62d509cdf87b6397..0b2b13ae694ead4748093fc93d0d3a457112f88f 100644 (file)
@@ -13,7 +13,7 @@ from jinja.lexer import Lexer
 from jinja.parser import Parser
 from jinja.loaders import LoaderWrapper
 from jinja.datastructure import Undefined, Context, Markup, FakeTranslator
-from jinja.utils import escape
+from jinja.utils import escape, collect_translations
 from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException
 from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
 
@@ -112,6 +112,14 @@ class Environment(object):
         """
         return FakeTranslator()
 
+    def get_translations(self, name):
+        """
+        Load template `name` and return all translatable strings (note that
+        that it really just returns the strings form this template, not from
+        the parent or any included templates!)
+        """
+        return collect_translations(self.loader.parse(name))
+
     def apply_filters(self, value, context, filters):
         """
         Apply a list of filters on the variable.
index 172df02b03d6d344e4bcc64b55da6df7cc4c4cfc..83746e3a73bb7934b821544741d366b281978e07 100644 (file)
@@ -15,6 +15,7 @@ import re
 import sys
 import string
 from types import MethodType, FunctionType
+from compiler.ast import CallFunc, Name, Const
 from jinja.nodes import Trans
 from jinja.datastructure import Markup
 
@@ -107,20 +108,6 @@ def urlize(text, trim_url_limit=None, nofollow=False):
     return u''.join(words)
 
 
-def find_translations(environment, source):
-    """
-    Find all translatable strings in a template and yield
-    them as (lineno, singular, plural) tuples. If a plural
-    section does not exist it will be None.
-    """
-    queue = [environment.parse(source)]
-    while queue:
-        node = queue.pop()
-        if node.__class__ is Trans:
-            yield node.lineno, node.singular, node.plural
-        queue.extend(node.getChildNodes())
-
-
 def debug_context(env, context):
     """
     Use this function in templates to get a printed context.
@@ -231,6 +218,37 @@ def translate_exception(template, exc_type, exc_value, traceback, context):
                                     lineno, context)
 
 
+def collect_translations(ast):
+    """
+    Collect all translatable strings for the given ast.
+    """
+    todo = [ast]
+    result = []
+    while todo:
+        node = todo.pop()
+        if node.__class__ is Trans:
+            result.append((node.lineno, node.singular, node.plural))
+        elif node.__class__ is CallFunc and \
+             node.node.__class__ is Name and \
+             node.node.name == '_':
+            if len(node.args) in (1, 3):
+                args = []
+                for arg in node.args:
+                    if not arg.__class__ is Const:
+                        break
+                    args.append(arg.value)
+                else:
+                    if len(args) == 1:
+                        singular = args[0]
+                        plural = None
+                    else:
+                        singular, plural, _ = args
+                    result.append((node.lineno, singular, plural))
+        todo.extend(node.getChildNodes())
+    result.sort(lambda a, b: cmp(a[0], b[0]))
+    return result
+
+
 class TracebackLoader(object):
     """
     Fake importer that just returns the source of a template.