Source

mercurial-translation-ja / pickup.py

Full commit
#!/usr/bin/env python

####################

lineno = 0

####################

def undefined_state(value):
    raise BaseException('unexpected invocation:%d: %s' % (lineno, value))

def wait_msgid(value):
    if 0 != len(value):
        global state
        infostock[0] = value
        state = skip_msgid

def wait_msgstr(value):
    if 0 != len(value):
        global state
        infostock[1] = value
        state = skip_msgstr

def skip_msgid(value):
    infostock[0] += value

def skip_msgstr(value):
    if not infostock[1].endswith('\\n'):
        infostock[5].append(lineno - 1)
    infostock[1] += value

infostock = None
state = None

def clear_status():
    global infostock, state
    # 0      1       2       3              4      5
    # msgid, msgstr, lineno, filesname map, fuzzy, wrapped lineno
    infostock = ['', '', 0, {}, False, []]
    state = undefined_state

####################

def comment_strategy(line):
    if line.startswith(':'):
        line = line[1:]
        for entry in line.split():
            entry = entry.strip()
            if 0 == len(entry): continue
            filename, lineno = entry.split(':')
            filenamemap = infostock[3]
            filenamemap[filename]  = filename
    else:
        for entry in line.split(','):
            if entry.strip() == 'fuzzy':
                infostock[4] = True

def msgid_strategy(line):
    id = line[1:-1] # eliminate quotations
    if 0 != len(id):
        global state
        infostock[0] = id
        state = skip_msgid
    else:
        state = wait_msgid

def msgstr_strategy(line):
    infostock[2] = lineno
    str = line[1:-1] # eliminate quotations
    if 0 != len(str):
        global state
        infostock[1] = str
        state = skip_msgstr
    else:
        state = wait_msgstr

def value_strategy(line):
    str = line[:-1] # eliminate tail quotation
    state(str)

top_strategies = [
    ('#', comment_strategy),
    ('msgid ', msgid_strategy),
    ('msgstr ', msgstr_strategy),
    ('"', value_strategy),
]

####################

def check_untranslated(msgs):
    if 0 == len(infostock[1]):
        msgs.append('not yet translated')

def check_fuzzy(msgs):
    if infostock[4]:
        msgs.append('fuzzy msgid')

def check_wrapped(msgs):
    if infostock[5]:
        detail = '\n'.join(['%s:%d:wrapped here' % (filename, n)
                            for n in infostock[5]])
        msgs.append('unexpected wrap in msgstr\n%s' % detail)

import unicodedata
eaw = getattr(unicodedata, 'east_asian_width', None)
if eaw is None:
    sys.stderr.write("WARN: unicodedata doesn't have east_asian_width\n")
    def colwidth(s):
        return len(s)
else:
    def colwidth(s):
        us = s.decode('string-escape').decode('utf-8')
        return sum([eaw(c) in "WFA" and 2 or 1 for c in us])

titlemarks = ['"', '=', '-', '.', '#']

def hastitlemark(msgids):
    return ((1 < len(msgids)) and
            (1 < len(msgids[1])) and
            (msgids[1] == msgids[1][0] * len(msgids[1])) and
            (msgids[1][0] in titlemarks))

def check_rst_error(msgs):
    if (infostock[1] and
        infostock[0].endswith('::') and not infostock[1].endswith('::')):
        msgs.append('translation forgot "::" at the end of message')

    msgids = infostock[0].split('\\n')
    if hastitlemark(msgids):
        msgstrs = infostock[1].split('\\n')
        if len(msgstrs) < 2:
            msgs.append("translation has less lines")
        elif msgids[1][0] != msgstrs[1][0]:
            msgs.append("rst title mark is not as same as one in msgid")
        elif colwidth(msgstrs[0]) != colwidth(msgstrs[1]):
            msgs.append("length of title mark line isn't equal to title")

####################

import sys
import optparse

optionlist = [
    optparse.make_option('-u', '--untranslated',
                         help='pickup only untranslated messages',
                         action='store_const', const=1, dest='untranslated'
                         ),
    optparse.make_option('-f', '--fuzzy',
                         help='pickup only fuzzy messages',
                         action='store_const', const=1, dest='fuzzy'
                         ),
    optparse.make_option('-w', '--wrapped',
                         help='pickup only unexpectedly wrapped messages',
                         action='store_const', const=1, dest='wrapped'
                         ),
    optparse.make_option('-r', '--rst-error',
                         help='pickup only rst syntax error',
                         action='store_const', const=1, dest='rst_error'
                         ),
]

optparser = optparse.OptionParser(usage='USAGE: %prog [options]',
                                  option_list=optionlist)

(options, args) = optparser.parse_args()

if 1 < len(args):
    expecteds = args[1:]

    def checkargs(filenames):
        if not expecteds: return True
        for filename in filenames:
            if filename in expecteds: return True
        return False

    interestedin = checkargs
else:
    interestedin = lambda(x): True

if (0 < len(args)) and ('-' != args[0]):
    filename = args[0]
    infile = open(filename)
else:
    filename = '<stdin>'
    infile = sys.stdin

if ((not options.untranslated) and
    (not options.fuzzy) and
    (not options.wrapped) and
    (not options.rst_error)):
    # show all detected errors by default
    options.untranslated = 1
    options.fuzzy = 1
    options.wrapped = 1
    options.rst_error = 1

checklist = [
    (lambda : options.untranslated,
     check_untranslated),
    (lambda : options.fuzzy,
     check_fuzzy),
    (lambda : options.wrapped,
     check_wrapped),
    (lambda : options.rst_error,
     check_rst_error),
]

####################

def parse_line(line):
    for prefix, strategy in top_strategies:
        if line.startswith(prefix):
            line = line[len(prefix):] # trim off
            strategy(line)
            break
    else:
        if 0 != len(line):
            raise BaseException('unexpected line: %s' % line)

        if 0 == len(infostock[0]):
            return

        # empty line flush current status

        if interestedin(infostock[3].keys()):
            msgs = []
            for enabled, checkfunc in checklist:
                if enabled():
                    checkfunc(msgs)
            for msg in msgs:
                print '%s:%d:%s' % (filename, infostock[2], msg)

        # clear information
        clear_status()

clear_status()

for line in infile:
    line = line.strip()
    lineno += 1

    parse_line(line)

# flush stocked information
parse_line('')