app/django/templatetags/i18n.py
changeset 54 03e267d67478
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 import re
       
     2 
       
     3 from django.template import Node, Variable, VariableNode
       
     4 from django.template import TemplateSyntaxError, TokenParser, Library
       
     5 from django.template import TOKEN_TEXT, TOKEN_VAR
       
     6 from django.utils import translation
       
     7 from django.utils.encoding import force_unicode
       
     8 
       
     9 register = Library()
       
    10 
       
    11 class GetAvailableLanguagesNode(Node):
       
    12     def __init__(self, variable):
       
    13         self.variable = variable
       
    14 
       
    15     def render(self, context):
       
    16         from django.conf import settings
       
    17         context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
       
    18         return ''
       
    19 
       
    20 class GetCurrentLanguageNode(Node):
       
    21     def __init__(self, variable):
       
    22         self.variable = variable
       
    23 
       
    24     def render(self, context):
       
    25         context[self.variable] = translation.get_language()
       
    26         return ''
       
    27 
       
    28 class GetCurrentLanguageBidiNode(Node):
       
    29     def __init__(self, variable):
       
    30         self.variable = variable
       
    31 
       
    32     def render(self, context):
       
    33         context[self.variable] = translation.get_language_bidi()
       
    34         return ''
       
    35 
       
    36 class TranslateNode(Node):
       
    37     def __init__(self, value, noop):
       
    38         self.value = Variable(value)
       
    39         self.noop = noop
       
    40 
       
    41     def render(self, context):
       
    42         value = self.value.resolve(context)
       
    43         if self.noop:
       
    44             return value
       
    45         else:
       
    46             return translation.ugettext(value)
       
    47 
       
    48 class BlockTranslateNode(Node):
       
    49     def __init__(self, extra_context, singular, plural=None, countervar=None,
       
    50             counter=None):
       
    51         self.extra_context = extra_context
       
    52         self.singular = singular
       
    53         self.plural = plural
       
    54         self.countervar = countervar
       
    55         self.counter = counter
       
    56 
       
    57     def render_token_list(self, tokens):
       
    58         result = []
       
    59         vars = []
       
    60         for token in tokens:
       
    61             if token.token_type == TOKEN_TEXT:
       
    62                 result.append(token.contents)
       
    63             elif token.token_type == TOKEN_VAR:
       
    64                 result.append(u'%%(%s)s' % token.contents)
       
    65                 vars.append(token.contents)
       
    66         return ''.join(result), vars
       
    67 
       
    68     def render(self, context):
       
    69         tmp_context = {}
       
    70         for var, val in self.extra_context.items():
       
    71             tmp_context[var] = val.render(context)
       
    72         # Update() works like a push(), so corresponding context.pop() is at
       
    73         # the end of function
       
    74         context.update(tmp_context)
       
    75         singular, vars = self.render_token_list(self.singular)
       
    76         if self.plural and self.countervar and self.counter:
       
    77             count = self.counter.resolve(context)
       
    78             context[self.countervar] = count
       
    79             plural, vars = self.render_token_list(self.plural)
       
    80             result = translation.ungettext(singular, plural, count)
       
    81         else:
       
    82             result = translation.ugettext(singular)
       
    83         # Escape all isolated '%' before substituting in the context.
       
    84         result = re.sub(u'%(?!\()', u'%%', result)
       
    85         data = dict([(v, force_unicode(context[v])) for v in vars])
       
    86         context.pop()
       
    87         return result % data
       
    88 
       
    89 def do_get_available_languages(parser, token):
       
    90     """
       
    91     This will store a list of available languages
       
    92     in the context.
       
    93 
       
    94     Usage::
       
    95 
       
    96         {% get_available_languages as languages %}
       
    97         {% for language in languages %}
       
    98         ...
       
    99         {% endfor %}
       
   100 
       
   101     This will just pull the LANGUAGES setting from
       
   102     your setting file (or the default settings) and
       
   103     put it into the named variable.
       
   104     """
       
   105     args = token.contents.split()
       
   106     if len(args) != 3 or args[1] != 'as':
       
   107         raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args
       
   108     return GetAvailableLanguagesNode(args[2])
       
   109 
       
   110 def do_get_current_language(parser, token):
       
   111     """
       
   112     This will store the current language in the context.
       
   113 
       
   114     Usage::
       
   115 
       
   116         {% get_current_language as language %}
       
   117 
       
   118     This will fetch the currently active language and
       
   119     put it's value into the ``language`` context
       
   120     variable.
       
   121     """
       
   122     args = token.contents.split()
       
   123     if len(args) != 3 or args[1] != 'as':
       
   124         raise TemplateSyntaxError, "'get_current_language' requires 'as variable' (got %r)" % args
       
   125     return GetCurrentLanguageNode(args[2])
       
   126 
       
   127 def do_get_current_language_bidi(parser, token):
       
   128     """
       
   129     This will store the current language layout in the context.
       
   130 
       
   131     Usage::
       
   132 
       
   133         {% get_current_language_bidi as bidi %}
       
   134 
       
   135     This will fetch the currently active language's layout and
       
   136     put it's value into the ``bidi`` context variable.
       
   137     True indicates right-to-left layout, otherwise left-to-right
       
   138     """
       
   139     args = token.contents.split()
       
   140     if len(args) != 3 or args[1] != 'as':
       
   141         raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args
       
   142     return GetCurrentLanguageBidiNode(args[2])
       
   143 
       
   144 def do_translate(parser, token):
       
   145     """
       
   146     This will mark a string for translation and will
       
   147     translate the string for the current language.
       
   148 
       
   149     Usage::
       
   150 
       
   151         {% trans "this is a test" %}
       
   152 
       
   153     This will mark the string for translation so it will
       
   154     be pulled out by mark-messages.py into the .po files
       
   155     and will run the string through the translation engine.
       
   156 
       
   157     There is a second form::
       
   158 
       
   159         {% trans "this is a test" noop %}
       
   160 
       
   161     This will only mark for translation, but will return
       
   162     the string unchanged. Use it when you need to store
       
   163     values into forms that should be translated later on.
       
   164 
       
   165     You can use variables instead of constant strings
       
   166     to translate stuff you marked somewhere else::
       
   167 
       
   168         {% trans variable %}
       
   169 
       
   170     This will just try to translate the contents of
       
   171     the variable ``variable``. Make sure that the string
       
   172     in there is something that is in the .po file.
       
   173     """
       
   174     class TranslateParser(TokenParser):
       
   175         def top(self):
       
   176             value = self.value()
       
   177             if self.more():
       
   178                 if self.tag() == 'noop':
       
   179                     noop = True
       
   180                 else:
       
   181                     raise TemplateSyntaxError, "only option for 'trans' is 'noop'"
       
   182             else:
       
   183                 noop = False
       
   184             return (value, noop)
       
   185     value, noop = TranslateParser(token.contents).top()
       
   186     return TranslateNode(value, noop)
       
   187 
       
   188 def do_block_translate(parser, token):
       
   189     """
       
   190     This will translate a block of text with parameters.
       
   191 
       
   192     Usage::
       
   193 
       
   194         {% blocktrans with foo|filter as bar and baz|filter as boo %}
       
   195         This is {{ bar }} and {{ boo }}.
       
   196         {% endblocktrans %}
       
   197 
       
   198     Additionally, this supports pluralization::
       
   199 
       
   200         {% blocktrans count var|length as count %}
       
   201         There is {{ count }} object.
       
   202         {% plural %}
       
   203         There are {{ count }} objects.
       
   204         {% endblocktrans %}
       
   205 
       
   206     This is much like ngettext, only in template syntax.
       
   207     """
       
   208     class BlockTranslateParser(TokenParser):
       
   209         def top(self):
       
   210             countervar = None
       
   211             counter = None
       
   212             extra_context = {}
       
   213             while self.more():
       
   214                 tag = self.tag()
       
   215                 if tag == 'with' or tag == 'and':
       
   216                     value = self.value()
       
   217                     if self.tag() != 'as':
       
   218                         raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
       
   219                     extra_context[self.tag()] = VariableNode(
       
   220                             parser.compile_filter(value))
       
   221                 elif tag == 'count':
       
   222                     counter = parser.compile_filter(self.value())
       
   223                     if self.tag() != 'as':
       
   224                         raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
       
   225                     countervar = self.tag()
       
   226                 else:
       
   227                     raise TemplateSyntaxError, "unknown subtag %s for 'blocktrans' found" % tag
       
   228             return (countervar, counter, extra_context)
       
   229 
       
   230     countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
       
   231 
       
   232     singular = []
       
   233     plural = []
       
   234     while parser.tokens:
       
   235         token = parser.next_token()
       
   236         if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
       
   237             singular.append(token)
       
   238         else:
       
   239             break
       
   240     if countervar and counter:
       
   241         if token.contents.strip() != 'plural':
       
   242             raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags inside it"
       
   243         while parser.tokens:
       
   244             token = parser.next_token()
       
   245             if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
       
   246                 plural.append(token)
       
   247             else:
       
   248                 break
       
   249     if token.contents.strip() != 'endblocktrans':
       
   250         raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents
       
   251 
       
   252     return BlockTranslateNode(extra_context, singular, plural, countervar,
       
   253             counter)
       
   254 
       
   255 register.tag('get_available_languages', do_get_available_languages)
       
   256 register.tag('get_current_language', do_get_current_language)
       
   257 register.tag('get_current_language_bidi', do_get_current_language_bidi)
       
   258 register.tag('trans', do_translate)
       
   259 register.tag('blocktrans', do_block_translate)