13a14,27
> #
> # 2010-06-12 Jan-Hendrik Göllner
> # Made it plural sensitive, added ngettext as default keyword.
> # Any keyworded function that is being supplied > 2 arguments
> # is treated like ngettext.
> # Also added support for constructs like "_('foo' + 10*'bar')"
> # by evaluating the whole expression.
> # Code like _(foo(arg1, arg2) + "bar") does not work by design
> # as that expression must be evaluated at runtime and this script
> # only extracts static strings known before runtime.
> # However it is possible to do things like
> # "ngettext('World', 'Worlds', numWorlds)"
> # as only the first two arguments are evaluated.
> # Advanced version number from 1.5 to 1.6
166d179
< import operator
168c181
< __version__ = '1.5'
---
> __version__ = '1.6'
170c183
< default_keywords = ['_']
---
> default_keywords = ['_', 'ngettext']
193a207
> "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
358c372
< self.__data = []
---
> self.__args = []
369a384
>
377c392,404
< self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
---
> try:
> s = safe_eval(tstring)
> except Exception as e:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: could not evaluate argument "%(arg)s"'
> ) % {
> 'arg': tstring,
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> print >> sys.stderr, str(e)
> else:
> self.__addentry([s], lineno, isdocstring=1)
397c424,436
< self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
---
> try:
> s = safe_eval(tstring)
> except Exception as e:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: could not evaluate argument "%(arg)s"'
> ) % {
> 'arg': tstring,
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> print >> sys.stderr, str(e)
> else:
> self.__addentry(s, lineno, isdocstring=1)
406c445
< self.__data = []
---
> self.__args = ['']
408c447,448
< self.__state = self.__openseen
---
> self.__depth = 0
> self.__state = self.__scanstring1
412c452,453
< def __openseen(self, ttype, tstring, lineno):
---
> def __scanstring1(self, ttype, tstring, lineno):
> # handle first argument, which is supposed to be a string.
414,419c455,484
< # We've seen the last of the translatable strings. Record the
< # line number of the first line of the strings and update the list
< # of messages seen. Reset state for the next batch. If there
< # were no strings inside _(), then just ignore this entry.
< if self.__data:
< self.__addentry(EMPTYSTRING.join(self.__data))
---
> # End of list of arguments for the current function call.
> # If the argument list is empty (as in keyword()), ignore this call.
> # otherwise evaluate the fragments we collected as the first
> # argument and record its line number and update the list of
> # messages seen. Reset state for the next batch.
> if self.__args[-1]:
> try:
> s = safe_eval(self.__args[-1])
> except Exception as e:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: could not evaluate argument "%(arg)s"'
> ) % {
> 'arg': self.__args[-1],
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> print >> sys.stderr, str(e)
> self.__state = self.__waiting
> return
> if type(s) == str or type(s) == unicode:
> self.__args[-1] = s
> self.__addentry(self.__args)
> else:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: argument is no str or unicode object "%(arg)s"'
> ) % {
> 'arg': s,
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
421,425c486,524
< elif ttype == tokenize.STRING:
< self.__data.append(safe_eval(tstring))
< elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT,
< token.NEWLINE, tokenize.NL]:
< # warn if we see anything else than STRING or whitespace
---
> elif ttype == tokenize.OP and tstring == ',':
> # Start of the next argument.
> try:
> s = safe_eval(self.__args[-1])
> except Exception as e:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: could not evaluate argument "%(arg)s"'
> ) % {
> 'arg': self.__args[-1],
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> print >> sys.stderr, str(e)
> self.__state = self.__waiting
> return
> if type(s) == str or type(s) == unicode:
> self.__args[-1] = s
> self.__args.append('') # next argument.
> self.__state = self.__scanstring2
> else:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: argument 1 is no str or unicode object "%(arg)s"'
> ) % {
> 'arg': s,
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> self.__state = self.__waiting
> else:
> # add string to current argument for later evaluation.
> # no state change in this case.
> self.__args[-1] += tstring
>
> def __scanstring2(self, ttype, tstring, lineno):
> # handle second argument, which is supposed to be a string.
> if ttype == tokenize.OP and tstring == ')':
> # End of list of arguments for the current function call.
> # This is an error if we expect either one or three arguments but
> # never two.
427c526
< '*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"'
---
> '*** %(file)s:%(lineno)s: unexpected number of arguments (2)"'
429d527
< 'token': tstring,
434,435c532,570
<
< def __addentry(self, msg, lineno=None, isdocstring=0):
---
> elif ttype == tokenize.OP and tstring == ',':
> # Start of the next argument. We do not need to parse it, we only
> # made sure it is there and now we assume this is a plural call.
> try:
> s = safe_eval(self.__args[-1])
> except Exception as e:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: could not evaluate argument "%(arg)s"'
> ) % {
> 'arg': self.__args[-1],
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> print >> sys.stderr, str(e)
> self.__state = self.__waiting
> return
> s = safe_eval(self.__args[-1])
> if type(s) == str or type(s) == unicode:
> self.__args[-1] = s
> self.__addentry(self.__args)
> self.__state = self.__waiting
> else:
> print >> sys.stderr, _(
> '*** %(file)s:%(lineno)s: argument 2 is no str or unicode object "%(arg)s"'
> ) % {
> 'arg': s,
> 'file': self.__curfile,
> 'lineno': self.__lineno
> }
> self.__state = self.__waiting
> else:
> # add string to current argument for later evaluation.
> # no state change in this case.
> self.__args[-1] += tstring
>
> def __addentry(self, args, lineno=None, isdocstring=0):
> isplural = 0
> if len(args) > 1:
> isplural = 1
438c573,581
< if not msg in self.__options.toexclude:
---
> exclude = 0
> if args[0] in self.__options.toexclude:
> exclude = 1
> if isplural:
> if args[1] not in self.__options.toexclude:
> # in case of plural, both strings must be in the toexclude list
> # to exclude this entry.
> exclude = 0
> if not exclude:
440c583,587
< self.__messages.setdefault(msg, {})[entry] = isdocstring
---
> # entries look like this:
> # {('arg1','arg2') : {(filename,lineno) : },
> # ('arg1',) : {(filename,lineno) : }}
> # a key with len > 1 indicates plurals
> self.__messages.setdefault(tuple(args[0:2]), {})[entry] = isdocstring
465d611
< isdocstring = 0
469,470c615
< if reduce(operator.__add__, v.values()):
< isdocstring = 1
---
> isdocstring = sum(v.values())
500,502c645,651
< print >> fp, 'msgid', normalize(k)
< print >> fp, 'msgstr ""\n'
<
---
> print >> fp, 'msgid', normalize(k[0])
> if len(k) > 1:
> print >> fp, 'msgid_plural', normalize(k[1])
> print >> fp, 'msgstr[0] ""'
> print >> fp, 'msgstr[1] ""\n'
> else:
> print >> fp, 'msgstr ""\n'