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'