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
Return `True` if `name` exists in the context.
+.. _i18n: i18n.txt
.. _Quickstart: devintro.txt
.. _gettext documentation: http://docs.python.org/lib/module-gettext.html
--- /dev/null
+====================
+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.
- `Debugging Support <debugging.txt>`_
+ - `Internationalization <i18n.txt>`_
+
- Template Designer Documentation:
- `Syntax Reference <designerdoc.txt>`_
- `Differences To Django <fromdjango.txt>`_
- `Recipies <recipies.txt>`_
-
-Undocumented So Far
--------------------
-
-- i18n for Developers
-
-- Writing Custom Loaders
-
-- Changelog / Authors
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
"""
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.
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
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.
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.