From e9e178829b272298f0b0752e2281d4f7af24c729 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 19 Mar 2007 17:52:24 +0100 Subject: [PATCH] [svn] implemented function to collect translations and documented it --HG-- branch : trunk --- docs/src/contextenv.txt | 8 +++++ docs/src/i18n.txt | 72 +++++++++++++++++++++++++++++++++++++++++ docs/src/index.txt | 11 ++----- jinja/environment.py | 10 +++++- jinja/utils.py | 46 ++++++++++++++++++-------- 5 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 docs/src/i18n.txt diff --git a/docs/src/contextenv.txt b/docs/src/contextenv.txt index 28ca2e2..bdf2228 100644 --- a/docs/src/contextenv.txt +++ b/docs/src/contextenv.txt @@ -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 index 0000000..18bd86f --- /dev/null +++ b/docs/src/i18n.txt @@ -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. diff --git a/docs/src/index.txt b/docs/src/index.txt index 64085e3..0940fb7 100644 --- a/docs/src/index.txt +++ b/docs/src/index.txt @@ -26,6 +26,8 @@ Welcome in the Jinja documentation. - `Debugging Support `_ + - `Internationalization `_ + - Template Designer Documentation: - `Syntax Reference `_ @@ -33,12 +35,3 @@ Welcome in the Jinja documentation. - `Differences To Django `_ - `Recipies `_ - -Undocumented So Far -------------------- - -- i18n for Developers - -- Writing Custom Loaders - -- Changelog / Authors diff --git a/jinja/environment.py b/jinja/environment.py index ee988b2..0b2b13a 100644 --- a/jinja/environment.py +++ b/jinja/environment.py @@ -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. diff --git a/jinja/utils.py b/jinja/utils.py index 172df02..83746e3 100644 --- a/jinja/utils.py +++ b/jinja/utils.py @@ -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. -- 2.26.2