app/django/templatetags/i18n.py
changeset 54 03e267d67478
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/django/templatetags/i18n.py	Fri Jul 18 18:22:23 2008 +0000
@@ -0,0 +1,259 @@
+import re
+
+from django.template import Node, Variable, VariableNode
+from django.template import TemplateSyntaxError, TokenParser, Library
+from django.template import TOKEN_TEXT, TOKEN_VAR
+from django.utils import translation
+from django.utils.encoding import force_unicode
+
+register = Library()
+
+class GetAvailableLanguagesNode(Node):
+    def __init__(self, variable):
+        self.variable = variable
+
+    def render(self, context):
+        from django.conf import settings
+        context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
+        return ''
+
+class GetCurrentLanguageNode(Node):
+    def __init__(self, variable):
+        self.variable = variable
+
+    def render(self, context):
+        context[self.variable] = translation.get_language()
+        return ''
+
+class GetCurrentLanguageBidiNode(Node):
+    def __init__(self, variable):
+        self.variable = variable
+
+    def render(self, context):
+        context[self.variable] = translation.get_language_bidi()
+        return ''
+
+class TranslateNode(Node):
+    def __init__(self, value, noop):
+        self.value = Variable(value)
+        self.noop = noop
+
+    def render(self, context):
+        value = self.value.resolve(context)
+        if self.noop:
+            return value
+        else:
+            return translation.ugettext(value)
+
+class BlockTranslateNode(Node):
+    def __init__(self, extra_context, singular, plural=None, countervar=None,
+            counter=None):
+        self.extra_context = extra_context
+        self.singular = singular
+        self.plural = plural
+        self.countervar = countervar
+        self.counter = counter
+
+    def render_token_list(self, tokens):
+        result = []
+        vars = []
+        for token in tokens:
+            if token.token_type == TOKEN_TEXT:
+                result.append(token.contents)
+            elif token.token_type == TOKEN_VAR:
+                result.append(u'%%(%s)s' % token.contents)
+                vars.append(token.contents)
+        return ''.join(result), vars
+
+    def render(self, context):
+        tmp_context = {}
+        for var, val in self.extra_context.items():
+            tmp_context[var] = val.render(context)
+        # Update() works like a push(), so corresponding context.pop() is at
+        # the end of function
+        context.update(tmp_context)
+        singular, vars = self.render_token_list(self.singular)
+        if self.plural and self.countervar and self.counter:
+            count = self.counter.resolve(context)
+            context[self.countervar] = count
+            plural, vars = self.render_token_list(self.plural)
+            result = translation.ungettext(singular, plural, count)
+        else:
+            result = translation.ugettext(singular)
+        # Escape all isolated '%' before substituting in the context.
+        result = re.sub(u'%(?!\()', u'%%', result)
+        data = dict([(v, force_unicode(context[v])) for v in vars])
+        context.pop()
+        return result % data
+
+def do_get_available_languages(parser, token):
+    """
+    This will store a list of available languages
+    in the context.
+
+    Usage::
+
+        {% get_available_languages as languages %}
+        {% for language in languages %}
+        ...
+        {% endfor %}
+
+    This will just pull the LANGUAGES setting from
+    your setting file (or the default settings) and
+    put it into the named variable.
+    """
+    args = token.contents.split()
+    if len(args) != 3 or args[1] != 'as':
+        raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args
+    return GetAvailableLanguagesNode(args[2])
+
+def do_get_current_language(parser, token):
+    """
+    This will store the current language in the context.
+
+    Usage::
+
+        {% get_current_language as language %}
+
+    This will fetch the currently active language and
+    put it's value into the ``language`` context
+    variable.
+    """
+    args = token.contents.split()
+    if len(args) != 3 or args[1] != 'as':
+        raise TemplateSyntaxError, "'get_current_language' requires 'as variable' (got %r)" % args
+    return GetCurrentLanguageNode(args[2])
+
+def do_get_current_language_bidi(parser, token):
+    """
+    This will store the current language layout in the context.
+
+    Usage::
+
+        {% get_current_language_bidi as bidi %}
+
+    This will fetch the currently active language's layout and
+    put it's value into the ``bidi`` context variable.
+    True indicates right-to-left layout, otherwise left-to-right
+    """
+    args = token.contents.split()
+    if len(args) != 3 or args[1] != 'as':
+        raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args
+    return GetCurrentLanguageBidiNode(args[2])
+
+def do_translate(parser, token):
+    """
+    This will mark a string for translation and will
+    translate the string for the current language.
+
+    Usage::
+
+        {% trans "this is a test" %}
+
+    This will mark the string for translation so it will
+    be pulled out by mark-messages.py into the .po files
+    and will run the string through the translation engine.
+
+    There is a second form::
+
+        {% trans "this is a test" noop %}
+
+    This will only mark for translation, but will return
+    the string unchanged. Use it when you need to store
+    values into forms that should be translated later on.
+
+    You can use variables instead of constant strings
+    to translate stuff you marked somewhere else::
+
+        {% trans variable %}
+
+    This will just try to translate the contents of
+    the variable ``variable``. Make sure that the string
+    in there is something that is in the .po file.
+    """
+    class TranslateParser(TokenParser):
+        def top(self):
+            value = self.value()
+            if self.more():
+                if self.tag() == 'noop':
+                    noop = True
+                else:
+                    raise TemplateSyntaxError, "only option for 'trans' is 'noop'"
+            else:
+                noop = False
+            return (value, noop)
+    value, noop = TranslateParser(token.contents).top()
+    return TranslateNode(value, noop)
+
+def do_block_translate(parser, token):
+    """
+    This will translate a block of text with parameters.
+
+    Usage::
+
+        {% blocktrans with foo|filter as bar and baz|filter as boo %}
+        This is {{ bar }} and {{ boo }}.
+        {% endblocktrans %}
+
+    Additionally, this supports pluralization::
+
+        {% blocktrans count var|length as count %}
+        There is {{ count }} object.
+        {% plural %}
+        There are {{ count }} objects.
+        {% endblocktrans %}
+
+    This is much like ngettext, only in template syntax.
+    """
+    class BlockTranslateParser(TokenParser):
+        def top(self):
+            countervar = None
+            counter = None
+            extra_context = {}
+            while self.more():
+                tag = self.tag()
+                if tag == 'with' or tag == 'and':
+                    value = self.value()
+                    if self.tag() != 'as':
+                        raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
+                    extra_context[self.tag()] = VariableNode(
+                            parser.compile_filter(value))
+                elif tag == 'count':
+                    counter = parser.compile_filter(self.value())
+                    if self.tag() != 'as':
+                        raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
+                    countervar = self.tag()
+                else:
+                    raise TemplateSyntaxError, "unknown subtag %s for 'blocktrans' found" % tag
+            return (countervar, counter, extra_context)
+
+    countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
+
+    singular = []
+    plural = []
+    while parser.tokens:
+        token = parser.next_token()
+        if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
+            singular.append(token)
+        else:
+            break
+    if countervar and counter:
+        if token.contents.strip() != 'plural':
+            raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags inside it"
+        while parser.tokens:
+            token = parser.next_token()
+            if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
+                plural.append(token)
+            else:
+                break
+    if token.contents.strip() != 'endblocktrans':
+        raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents
+
+    return BlockTranslateNode(extra_context, singular, plural, countervar,
+            counter)
+
+register.tag('get_available_languages', do_get_available_languages)
+register.tag('get_current_language', do_get_current_language)
+register.tag('get_current_language_bidi', do_get_current_language_bidi)
+register.tag('trans', do_translate)
+register.tag('blocktrans', do_block_translate)