Commits

Katsunori FUJIWARA committed 14e5590 Merge

IMPORT: sync-line => downstream-line

Comments (0)

Files changed (8)

 
 *~
 *.mo
+*.pyc
 cdb274766b42ff40ecf87923a8d434fe6df5d6a8 official_2.6.3
 b05558a6362de502b18f51064637b7051ce4dd0b official_2.7
 8b90af2ae7d93302ba7176790fd3ab54a6bfef9c official_2.8
+427319af6a30eeebb1981328e0a3386e4a12ae6f official_2.8.1

check-translation.py

+#!/usr/bin/env python
+#
+# check-translation.py - check Mercurial specific translation problems
+
+import polib
+import re
+import os
+import unicodedata
+
+checkers = []
+
+def checker(level, msgidpat):
+    def decorator(func):
+        if msgidpat:
+            match = re.compile(msgidpat).search
+        else:
+            match = lambda msgid: True
+        checkers.append((func, level))
+        func.match = match
+        return func
+    return decorator
+
+def match(checker, pe):
+    """Examine whether POEntry "pe" is target of specified checker or not
+    """
+    if not checker.match(pe.msgid):
+        return
+    # examine suppression by translator comment
+    nochecker = 'no-%s-check' % checker.__name__
+    for tc in pe.tcomment.split():
+        if nochecker == tc:
+            return
+    return True
+
+####################
+
+def fatalchecker(msgidpat=None):
+    return checker('fatal', msgidpat)
+
+@fatalchecker(r'\$\$')
+def promptchoice(pe):
+    """Check translation of the string given to "ui.promptchoice()"
+
+    >>> pe = polib.POEntry(
+    ...     msgid ='prompt$$missing &sep$$missing &amp$$followed by &none',
+    ...     msgstr='prompt  missing &sep$$missing  amp$$followed by none&')
+    >>> match(promptchoice, pe)
+    True
+    >>> for e in promptchoice(pe): print e
+    number of choices differs between msgid and msgstr
+    msgstr has invalid choice missing '&'
+    msgstr has invalid '&' followed by none
+    """
+    idchoices = [c.rstrip(' ') for c in pe.msgid.split('$$')[1:]]
+    strchoices = [c.rstrip(' ') for c in pe.msgstr.split('$$')[1:]]
+
+    if len(idchoices) != len(strchoices):
+        yield "number of choices differs between msgid and msgstr"
+
+    indices = [(c, c.find('&')) for c in strchoices]
+    if [c for c, i in indices if i == -1]:
+        yield "msgstr has invalid choice missing '&'"
+    if [c for c, i in indices if len(c) == i + 1]:
+        yield "msgstr has invalid '&' followed by none"
+
+####################
+
+def warningchecker(msgidpat=None):
+    return checker('warning', msgidpat)
+
+@warningchecker()
+def taildoublecolons(pe):
+    """Check equality of tail '::'-ness between msgid and msgstr
+
+    >>> pe = polib.POEntry(
+    ...     msgid ='ends with ::',
+    ...     msgstr='ends with ::')
+    >>> for e in taildoublecolons(pe): print e
+    >>> pe = polib.POEntry(
+    ...     msgid ='ends with ::',
+    ...     msgstr='ends without double-colons')
+    >>> for e in taildoublecolons(pe): print e
+    tail '::'-ness differs between msgid and msgstr
+    >>> pe = polib.POEntry(
+    ...     msgid ='ends without double-colons',
+    ...     msgstr='ends with ::')
+    >>> for e in taildoublecolons(pe): print e
+    tail '::'-ness differs between msgid and msgstr
+    """
+    if pe.msgid.endswith('::') != pe.msgstr.endswith('::'):
+        yield "tail '::'-ness differs between msgid and msgstr"
+
+@warningchecker()
+def indentation(pe):
+    """Check equality of initial indentation between msgid and msgstr
+
+    This may report unexpected warning, because this doesn't aware
+    the syntax of rst document and the context of msgstr.
+
+    >>> pe = polib.POEntry(
+    ...     msgid ='    indented text',
+    ...     msgstr='  narrowed indentation')
+    >>> for e in indentation(pe): print e
+    initial indentation width differs betweeen msgid and msgstr
+    """
+    idindent = len(pe.msgid) - len(pe.msgid.lstrip())
+    strindent = len(pe.msgstr) - len(pe.msgstr.lstrip())
+    if idindent != strindent:
+        yield "initial indentation width differs betweeen msgid and msgstr"
+
+sectionmarksre = re.compile(r'^(?:"{2,}|={2,}|-{2,}|\.{2,}|#{2,})$')
+
+# How to treat ambiguous-width characters. Set to 'wide' to treat as wide.
+wide = (os.environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide"
+        and "WFA" or "WF")
+
+def ucolwidth(d):
+    eaw = getattr(unicodedata, 'east_asian_width', None)
+    if eaw is not None:
+        return sum([eaw(c) in wide and 2 or 1 for c in d])
+    return len(d)
+
+@warningchecker(r'(?m)^(?:"{2,}|={2,}|-{2,}|\.{2,}|#{2,})$')
+def sectionmarks(pe):
+    """Check section marks and title text
+
+    >>> pe = polib.POEntry(
+    ...     msgid =(u'========================\\n'
+    ...              'overline-underline style\\n'
+    ...              '========================'),
+    ...     msgstr=(u'==========\\n'
+    ...              'multiple title\\n'
+    ...              'text lines\\n'
+    ...              '\\n'
+    ...              '----------------\\n'
+    ...              '----------------\\n'
+    ...              '\\n'
+    ...              '=============\\n'
+    ...              'mark mismatch\\n'
+    ...              '.............\\n'
+    ...              '################\\n'
+    ...              'length mismatch\\n'
+    ...              '#############\\n'
+    ...              '\\n'
+    ...              '============\\n'
+    ...              'no underline'))
+    >>> match(sectionmarks, pe)
+    True
+    >>> for e in sectionmarks(pe): print e
+    multiple title text lines for section mark '='
+    no title text for section mark '-'
+    section mark differs between overline ('=') and underline ('.')
+    length of section mark '#' differs between overline and underline
+    no underline corresponed to overline for section mark '='
+    >>> pe = polib.POEntry(
+    ...     msgid =(u'underline style\\n'
+    ...              '==============='),
+    ...     msgstr=(u'length mismatch\\n'
+    ...              '============\\n'
+    ...              'long normal text\\n'
+    ...              'title text\\n'
+    ...              '----------'))
+    >>> match(sectionmarks, pe)
+    True
+    >>> for e in sectionmarks(pe): print e
+    column width differs between title text and section mark '='
+    """
+    titletext = overline = None
+    for l in pe.msgstr.split('\n'):
+        if overline: # overline-underline style
+            if not sectionmarksre.match(l):
+                if titletext:
+                    yield ("multiple title text lines for section mark %r"
+                           % (overline[0].encode('ascii')))
+                    titletext = overline = None # clear current context
+                else:
+                    titletext = l
+                continue
+
+            if not titletext:
+                yield ("no title text for section mark %r"
+                       % (overline[0].encode('ascii')))
+            if overline[0] != l[0]:
+                yield ("section mark differs"
+                       " between overline (%r) and underline (%r)"
+                       % (overline[0].encode('ascii'), l[0].encode('ascii')))
+            if len(overline) != len(l):
+                yield ("length of section mark %r differs"
+                       " between overline and underline"
+                       % (overline[0].encode('ascii')))
+        else:
+            if not l.strip():
+                continue # skip empty line
+            if not sectionmarksre.match(l):
+                titletext = l
+                continue
+            if not titletext: # overline for overline-underline style
+                overline = l
+                continue
+
+            # this length check is needed, because:
+            # - section title in underline style is processed also by
+            #   Mercurial builtin minirst, and
+            # - minirst recognizes section titlte correctly, only when
+            #   column width is same between title text and section marks
+            if ucolwidth(titletext) != len(l):
+                yield ("column width differs"
+                       " between title text and section mark %r"
+                       % (l[0].encode('ascii')))
+
+        titletext = overline = None # clear current context
+
+    if overline:
+        yield ("no underline corresponed to overline for section mark %r"
+               % (overline[0].encode('ascii')))
+
+####################
+
+def check(pofile, fatal=True, warning=False):
+    targetlevel = { 'fatal': fatal, 'warning': warning }
+    targetcheckers = [(checker, level)
+                      for checker, level in checkers
+                      if targetlevel[level]]
+    if not targetcheckers:
+        return []
+
+    detected = []
+    for pe in pofile.translated_entries():
+        errors = []
+        for checker, level in targetcheckers:
+            if match(checker, pe):
+                errors.extend((level, checker.__name__, error)
+                              for error in checker(pe))
+        if errors:
+            detected.append((pe, errors))
+    return detected
+
+########################################
+
+if __name__ == "__main__":
+    import sys
+    import optparse
+
+    optparser = optparse.OptionParser("""%prog [options] pofile ...
+
+This checks Mercurial specific translation problems in specified
+'*.po' files.
+
+Each detected problems are shown in the format below::
+
+    filename:linenum:type(checker): problem detail .....
+
+"type" is "fatal" or "warning". "checker" is the name of the function
+detecting corresponded error.
+
+Checking by checker "foo" on the specific msgstr can be suppressed by
+the "translator comment" like below. Multiple "no-xxxx-check" should
+be separated by whitespaces::
+
+    # no-foo-check
+    msgid = "....."
+    msgstr = "....."
+""")
+    optparser.add_option("", "--warning",
+                         help="show also warning level problems",
+                         action="store_true")
+    optparser.add_option("", "--doctest",
+                         help="run doctest of this tool, instead of check",
+                         action="store_true")
+    (options, args) = optparser.parse_args()
+
+    if options.doctest:
+        import doctest
+        failures, tests = doctest.testmod()
+        sys.exit(failures and 1 or 0)
+
+    # replace polib._POFileParser to show linenum of problematic msgstr
+    class ExtPOFileParser(polib._POFileParser):
+        def process(self, symbol, linenum):
+            super(ExtPOFileParser, self).process(symbol, linenum)
+            if symbol == 'MS': # msgstr
+                self.current_entry.linenum = linenum
+    polib._POFileParser = ExtPOFileParser
+
+    detected = []
+    warning = options.warning
+    for f in args:
+        detected.extend((f, pe, errors)
+                        for pe, errors in check(polib.pofile(f),
+                                                warning=warning))
+    if detected:
+        for f, pe, errors in detected:
+            for level, checker, error in errors:
+                sys.stderr.write('%s:%d:%s(%s): %s\n'
+                                 % (f, pe.linenum, level, checker, error))
+        sys.exit(1)
 # XXXX exists           XXXX が存在します
 # do not XXXX           XXXX できません ※ 「XXXX してはいけない」の意
 # XXXX failed           XXXX が(or に)失敗
+# failed to XXXX        XXXX が(or に)失敗
 # error XXXX-ing        XXXX が(or に)失敗
 # error while XXXX      XXXX が(or に)失敗
 # cannot XXXX           XXXX が(or に)失敗
 # search                探索
 # server                サーバ
 # shelved change        退避内容(for 処理視点) or 退避情報(for 管理視点)
+# skipping xxxx         xxxx を無視
 # source url (of subrepo) (サブリポジトリの)参照先 URL
 # stop                  中断(再開可能な場合)/中止(再開不可能な場合)
 # subrepo               サブリポジトリ
 # tracked xxxx          構成管理対象の xxxx
 # tracked, un           未登録
 # type, xxxxx           xxxx 種別
-# unknown xxxx          未知の xxxx
+# unknown xxxx          未知の xxxx (or 構成管理対象外)
 # user                  ユーザ
 # working copy(of xxx)  作業領域(中の xxx)
 # working directory     作業領域
 "Project-Id-Version: Mercurial\n"
 "Report-Msgid-Bugs-To: <mercurial-devel@selenic.com>\n"
 "POT-Creation-Date: 2013-11-28 16:18+0900\n"
-"PO-Revision-Date: 2013-10-31 03:00+0900\n"
+"PO-Revision-Date: 2013-11-28 20:03+0900\n"
 "Last-Translator: Japanese translation team <mercurial-ja@googlegroups.com>\n"
 "Language-Team: Japanese\n"
 "Language: ja\n"
 
 #, python-format
 msgid "connecting to %s:%s as %s, password %s\n"
-msgstr "%s:%s に %s として接続しています (パスワード:%s)\n"
+msgstr "%s:%s に %s として接続 (パスワード:%s)\n"
 
 #, python-format
 msgid "query: %s %s\n"
 
 #, python-format
 msgid "looking up user %s\n"
-msgstr "ユーザ %s を検索しています\n"
+msgstr "ユーザ %s を検索\n"
 
 #, python-format
 msgid "cannot find bugzilla user id for %s"
-msgstr "%s の buzilla ユーザ ID 見つけることができません"
+msgstr "%s の buzilla ユーザ ID 見つかりません"
 
 #, python-format
 msgid "cannot find bugzilla user id for %s or %s"
-msgstr "%s か %s の buzilla ユーザ ID 見つけることができません"
+msgstr "%s か %s の buzilla ユーザ ID 見つかりません"
 
 msgid "Bugzilla/MySQL cannot update bug state\n"
 msgstr "Bugzilla/MySQL 連携ではバグ状態を更新できません\n"
 
 #, python-format
 msgid "skipping malformed alias: %s\n"
-msgstr "不正な形式の別名無視します: %s\n"
+msgstr "不正な形式の別名無視: %s\n"
 
 msgid "count rate for the specified revision or range"
 msgstr "処理対象とする特定リビジョン/範囲の指定"
 
 #, python-format
 msgid "cannot find required \"%s\" tool"
-msgstr "要求されたツール '%s' 見つけることができません"
+msgstr "要求されたツール '%s' 見つかりません"
 
 #, python-format
 msgid "splicemap entry %s is not a valid revision identifier"
 
 #, python-format
 msgid "could not open map file %r: %s"
-msgstr "変換ファイル %r を開くことができません: %s"
+msgstr "変換ファイル %r が開けません: %s"
 
 #, python-format
 msgid "%s: invalid source repository type"
 
 #, python-format
 msgid "splice map revision %s is not being converted, ignoring\n"
-msgstr "継ぎ合わせ指定中の %s が変換対象ではないため無視します\n"
+msgstr "継ぎ合わせ指定中の %s が変換対象ではないため無視\n"
 
 #, python-format
 msgid "unknown splice map parent: %s"
 
 #, python-format
 msgid "overriding mapping for author %s, was %s, will be %s\n"
-msgstr "作成者 %s の %s への変換を、 %s への変換で上書きします\n"
+msgstr "作成者 %s の %s への変換を、 %s への変換で上書き\n"
 
 #, python-format
 msgid "spliced in %s as parents of %s\n"
 
 #, python-format
 msgid "connecting to %s\n"
-msgstr "%s 接続中\n"
+msgstr "%s 接続中\n"
 
 msgid "CVS pserver authentication failed"
 msgstr "CVS pserver の認証に失敗"
 msgstr "CVS ログキャッシュ %s 読み込み中\n"
 
 msgid "ignoring old cache\n"
-msgstr "古いログキャッシュを無視します\n"
+msgstr "古いログキャッシュを無視\n"
 
 #, python-format
 msgid "cache has %d log entries\n"
 msgstr "%s は GNU Arch 形式ではないと思われます"
 
 msgid "cannot find a GNU Arch tool"
-msgstr "GNU Arch ツール見つけることができません"
+msgstr "GNU Arch ツール見つかりません"
 
 #, python-format
 msgid "analyzing tree version %s...\n"
 
 #, python-format
 msgid "ignoring: %s\n"
-msgstr "例外を無視します: %s\n"
+msgstr "例外を無視: %s\n"
 
 #, python-format
 msgid "%s does not look like a monotone repository"
 
 #, python-format
 msgid "no revision found in module %s"
-msgstr "モジュール %s リビジョンが見つかりません"
+msgstr "モジュール %s にはリビジョンがりません"
 
 #, python-format
 msgid "expected %s to be at %r, but not found"
 
 #, python-format
 msgid "ignoring empty branch %s\n"
-msgstr "空ブランチ %s を無視します\n"
+msgstr "空ブランチ %s を無視\n"
 
 #, python-format
 msgid "found branch %s at %d\n"
 "  native = LF"
 
 msgid ".. note::"
-msgstr ""
-
-#, fuzzy
+msgstr ".. note::"
+
 msgid ""
 "   The rules will first apply when files are touched in the working\n"
 "   copy, e.g. by updating to null and back to tip to touch all files."
 msgstr ""
-".. note::\n"
 "   変換設定の適用契機は、 作業領域中のファイルに対する最初の更新です。\n"
 "   例えば、 :hg:`update null` 後の :hg:`update tip` により、\n"
 "   全てのファイルが更新されます。"
 
 #, python-format
 msgid "ignoring unknown EOL style '%s' from %s\n"
-msgstr "未知の改行形式 '%s' (%s 由来) を無視します\n"
+msgstr "未知の改行形式 '%s' (%s 由来) を無視\n"
 
 #, python-format
 msgid "warning: ignoring .hgeol file due to parse error at %s: %s\n"
-msgstr "警告: 解析エラーにより .hgeol ファイルを無視します (%s 行目: %s)\n"
+msgstr "警告: 解析エラーにより .hgeol ファイルを無視 (%s 行目: %s)\n"
 
 #, python-format
 msgid "  %s in %s should not have %s line endings"
 msgstr "--rev と --change は同時には指定できません"
 
 msgid "cleaning up temp directory\n"
-msgstr "一時ディレクトリ破棄しています\n"
+msgstr "一時ディレクトリ破棄\n"
 
 msgid "use external program to diff repository (or selected files)"
 msgstr "外部コマンドを使用したリポジトリ(ないし指定ファイル)の差分表示"
 
 #, python-format
 msgid "updating to %d:%s\n"
-msgstr "%d に更新しています:%s\n"
+msgstr "リビジョン %d:%s で更新中\n"
 
 #, python-format
 msgid "merging with %d:%s\n"
-msgstr "%d とマージしています:%s\n"
+msgstr "リビジョン %d:%s とマージ中\n"
 
 #, python-format
 msgid "new changeset %d:%s merges remote changes with local\n"
 
 #, python-format
 msgid "%s:%d node does not exist\n"
-msgstr "%s:%d ノードは存在しません\n"
+msgstr "ノード %s:%d は存在しません\n"
 
 msgid "hg sigcheck REV"
 msgstr "hg sigcheck REV"
 
 #, python-format
 msgid "no valid signature for %s\n"
-msgstr "%s 正しい署名はありません\n"
+msgstr "%s に対する正しい署名はありません\n"
 
 msgid "make the signature local"
 msgstr "自リポジトリローカルな署名"
 msgstr "%d:%s への署名中\n"
 
 msgid "error while signing"
-msgstr "署名処理失敗"
+msgstr "署名処理失敗"
 
 msgid ""
 "working copy of .hgsigs is changed (please commit .hgsigs manually or use --"
 
 #, python-format
 msgid "hgcia: sending update to %s\n"
-msgstr "hgcia: %s に更新を送信しています\n"
+msgstr "hgcia: %s に更新を送信\n"
 
 msgid "email.from must be defined when sending by email"
 msgstr "電子メール送信のためには email.from の定義が必要です"
 
 #. i18n: bisect changeset status
 msgid "ignored"
-msgstr "無視しました"
+msgstr "無視"
 
 msgid "hg debug-rev-parse REV"
 msgstr "hg debug-rev-parse REV"
 msgstr "ヘッダ"
 
 msgid "topo-order"
-msgstr "履歴トポロジーの順序"
+msgstr "履歴構造の順序"
 
 msgid "parents"
 msgstr "親"
 "    履歴改変は中止されます。 紛らわしい例としては、 反映候補リビジョンが、\n"
 "    複数のブランチ上に存在するケースなどがあります。"
 
-#, fuzzy
 msgid ""
 "    Use \"min(outgoing() and ::.)\" or similar revset specification\n"
 "    instead of --outgoing to specify edit target revision exactly in\n"
 "    紛らわしさのために --outgoing が使用できない場合、 \"min(outgoing()\n"
 "    and ::.)\" または同等の revset 表記を使って、 改変対象リビジョンを、\n"
 "    厳密に指定してください。 リビジョン指定の詳細は :hg:`help revsets`\n"
-"    を参照してください。\n"
-"    "
+"    を参照してください。"
 
 msgid ""
 "    Returns 0 on success, 1 if user intervention is required (not only\n"
 "    conflicts).\n"
 "    "
 msgstr ""
+"    成功時のコマンド終了値は 0、 ユーザによる作業 (\"edit\" 指定による、\n"
+"    意図的なもの以外に、 予期せぬ衝突発生時も含みます) が必要な場合は\n"
+"    1 です。\n"
+"    "
 
 msgid "source has mq patches applied"
 msgstr "元リポジトリでは MQ パッチが適用中です"
 
 #, python-format
 msgid "%s event: created %s\n"
-msgstr "%s: %s を作成\n"
+msgstr "イベント %s: %s を作成\n"
 
 #, python-format
 msgid "%s event: deleted %s\n"
-msgstr "%s: %s を削除\n"
+msgstr "イベント %s: %s を削除\n"
 
 #, python-format
 msgid "%s event: modified %s\n"
-msgstr "%s: %s を変更\n"
+msgstr "イベント %s: %s を変更\n"
 
 #, python-format
 msgid "filesystem containing %s was unmounted\n"
 "    # CVS 的なキーワード展開よりも SVN 的なものを選択\n"
 "    svn = True"
 
-#, fuzzy
 msgid ""
 "   The more specific you are in your filename patterns the less you\n"
 "   lose speed in huge repositories."
 msgstr ""
-".. note::\n"
-"   ファイル名パターンが更に特殊になる場合、\n"
-"   リポジトリサイズ次第では性能劣化が生じ得ます。"
+"   ファイル名パターンが更に特殊になる場合、 リポジトリサイズ次第では、\n"
+"   実行性能が低下する可能性があります。"
 
 msgid ""
 "For [keywordmaps] template mapping and expansion demonstration and\n"
 msgid "uncommitted changes"
 msgstr "作業領域の変更が未コミットです"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "remote turned local normal file %s into a largefile\n"
 "use (l)argefile or keep (n)ormal file?$$ &Largefile $$ &Normal file"
 msgstr ""
-"ファイル %s が大容量ファイル化されています。\n"
+"通常ファイル %s が、マージ対象リビジョンでは大容量ファイル化されています。\n"
 "大容量:(l)argefile と通常:(n)ormal file のどちらの形式を採用しますか?$$ "
 "&Largefile $$ &Normal file"
 
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "remote turned local largefile %s into a normal file\n"
 "keep (l)argefile or use (n)ormal file?$$ &Largefile $$ &Normal file"
 msgstr ""
-"ファイル %s が通常ファイル化されています。\n"
+"大容量ファイル %s が、マージ対象リビジョンでは通常ファイル化されています。\n"
 "大容量:(l)argefile と通常:(n)ormal file のどちらの形式を採用しますか?$$ "
 "&Largefile $$ &Normal file"
 
 "&Local $$ &Other"
 
 msgid "no files to copy"
-msgstr "コピーするファイルがありません"
+msgstr "複製対象ファイルがありません"
 
 msgid "destination largefile already exists"
 msgstr "大容量ファイルの複製先は既に存在します"
 msgstr "未知のアーカイブ種別 '%s'"
 
 msgid "cannot give prefix when archiving to files"
-msgstr "アーカイブにファイルを追加するときは接頭辞を指定できません"
+msgstr "files でのアーカイブには接頭辞を指定できません"
 
 #, python-format
 msgid "largefile %s not found in repo store or system cache"
 
 #, python-format
 msgid "remotestore: could not open file %s: %s"
-msgstr "remotestore: ファイル %s を開くことができません: %s"
+msgstr "remotestore: ファイル %s が開けません: %s"
 
 #, python-format
 msgid "changeset %s: %s: contents differ\n"
 
 #, python-format
 msgid "required features are not supported in the destination: %s"
-msgstr "連携先リポジトリでは必要な機能が未サポートです: %s"
+msgstr "必要な機能が連携先リポジトリでは未サポートです: %s"
 
 #, python-format
 msgid "file \"%s\" is a largefile standin"
 
 #, python-format
 msgid "cannot push '%s' - %s\n"
-msgstr "パッチ '%s' 適用できませんでした - %s\n"
+msgstr "パッチ '%s' 適用に失敗 - %s\n"
 
 msgid "all patches are currently applied\n"
 msgstr "全てのパッチが適用中です\n"
 "    引数指定が無い場合、 現在のガード設定を表示します。\n"
 "    引数が指定された場合、 指定パッチに対してガードを設定します。"
 
-#, fuzzy
 msgid "    .. note::"
-msgstr "    .. container:: verbose"
-
-#, fuzzy
+msgstr "    .. note::"
+
 msgid "       Specifying negative guards now requires '--'."
-msgstr ""
-"    .. note::\n"
-"       「負」のガード設定には、 ガード指定の前に '--' 指定が必要です。"
+msgstr "       負のガード設定には、 ガード指定の前に '--' 引数が必要です。"
 
 msgid "    To set guards on another patch::"
 msgstr "    現行パッチ以外にガードを設定するには::"
 "  :``bundle``: ``hg unbundle`` 経由での反映"
 
 msgid "  Default: serve."
-msgstr " デフォルト値は serve です。"
+msgstr "  デフォルト値は serve です。"
 
 msgid ""
 "notify.strip\n"
 msgstr "説明文を独立したメールで送信"
 
 msgid "hg email [OPTION]... [DEST]..."
-msgstr "hg email [OPTION]... [DEST]...\""
+msgstr "hg email [OPTION]... [DEST]..."
 
 msgid "send changesets by email"
 msgstr "電子メールによる変更内容のパッチ送付"
 "default\n"
 "      hg email -b -r 3000 DEST  # bundle of all ancestors of 3000 not in DEST"
 msgstr ""
-"                                # ※ 以下、すべて bundle 形式\n"
+"      # ※ 以下、すべて bundle 形式\n"
 "      hg email -b               # default 側に無いリビジョン\n"
 "      hg email -b DEST          # DEST 側に無いリビジョン\n"
 "      hg email -b -r 3000       # 3000 以前で default 側に無いリビジョン\n"
 msgstr "作業領域中の未登録ファイルを削除するコマンド"
 
 msgid "abort if an error occurs"
-msgstr "エラー発生した場合に処理を中止"
+msgstr "エラー発生時には処理を中止"
 
 msgid "purge ignored files too"
-msgstr "無視したファイルも削除する"
+msgstr "無視対象ファイルも削除"
 
 msgid "print filenames instead of deleting them"
 msgstr "ファイル削除の変わりにファイル名表示を実施"
 msgstr "ファイル名をNUL文字(0x00)で終端(xargs -p/--print との併用向け)"
 
 msgid "hg purge [OPTION]... [DIR]..."
-msgstr "hg purge [OPTION]... [DIR]...\""
+msgstr "hg purge [OPTION]... [DIR]..."
 
 msgid "removes files not tracked by Mercurial"
 msgstr "Mercurial の管理対象外ファイルの削除"
 
 #, python-format
 msgid "%s cannot be removed"
-msgstr "%s 削除できませんでした"
+msgstr "%s 削除に失敗"
 
 #, python-format
 msgid "warning: %s\n"
 "    衝突解消後に --continue/-c で再開するか、 --abort/-a で中止\n"
 "    (移動関連情報の破棄) してください。"
 
-#, fuzzy
 msgid ""
 "    Returns 0 on success, 1 if nothing to rebase or there are\n"
 "    unresolved conflicts.\n"
 "    "
 msgstr ""
-"    成功時のコマンド終了値は 0、 未解消ファイルがある場合は 1 です。\n"
+"    成功時のコマンド終了値は 0、 移動が必要なリビジョンが無い場合や、\n"
+"    未解消の衝突が発生した場合は 1 です。\n"
 "    "
 
 msgid "message can only be specified with collapse"
 "    他のリポジトリと履歴情報を共有する、 リポジトリ/作業領域を、\n"
 "    新規に作成します。"
 
-#, fuzzy
 msgid ""
 "       using rollback or extensions that destroy/modify history (mq,\n"
 "       rebase, etc.) can cause considerable confusion with shared\n"
 "       the broken clone to reset it to a changeset that still exists.\n"
 "    "
 msgstr ""
-"    .. note::\n"
 "       rollback の実施や、 履歴を改変するエクステンション (例: mq や\n"
 "       rebase 等) の利用は、 リポジトリ間での共有に混乱をもたらします。\n"
 "       典型的な例は、 共有関係にあるリポジトリの両方が、 \n"
 msgstr "非互換なバージョンの shelve エクステンションによる復旧処理中です"
 
 msgid "cannot shelve while merging"
-msgstr "マージ実施中は変更退避できません"
+msgstr "マージ実施中は変更退避できません"
 
 #, python-format
 msgid "a shelved change named '%s' already exists"
 msgstr "[win32mbcs] 文字コード '%s' によるファイル名変換に失敗\n"
 
 msgid "[win32mbcs] cannot activate on this platform.\n"
-msgstr "[win32mbcs] このプラットフォームでは実行できません。\n"
+msgstr "[win32mbcs] エクステンションを有効化できないプラットフォームで。\n"
 
 msgid "perform automatic newline conversion"
 msgstr "改行形式の自動変換"
 "  zc-test = http://example.com:8000/test\n"
 
 msgid "archive prefix contains illegal components"
-msgstr "アーカイブの接頭辞が不正なコンポーネントを含みます"
+msgstr "アーカイブの接頭辞が不正な要素を含みます"
 
 msgid "archiving"
 msgstr "アーカイブ中"
 
 #, python-format
 msgid "can't read commit message '%s': %s"
-msgstr "コミットログ '%s' 読み込むことができません: %s"
+msgstr "コミットログ '%s' 読み込ません: %s"
 
 msgid "limit must be a positive integer"
 msgstr "制限には正数を指定してください"
 
 #, python-format
 msgid "%s: deleted in working copy\n"
-msgstr "%s: 作業コピーから削除しました\n"
+msgstr "%s: 作業領域から削除しました\n"
 
 #, python-format
 msgid "%s: cannot copy - %s\n"
 msgstr "%s を %s にコピー中\n"
 
 msgid "no source or destination specified"
-msgstr "作業元もしくは作業先を指定していません"
+msgstr "複製元/複製先の指定がありません"
 
 msgid "no destination specified"
-msgstr "作業先を指定していません"
+msgstr "複製先指定がありません"
 
 msgid "with multiple sources, destination must be an existing directory"
-msgstr "複数の作業元の場合、 作業先は存在するディレクトリを指定してください"
+msgstr "複製元が複数の場合、存在するディレクトリを複製先に指定してください"
 
 #, python-format
 msgid "destination %s is not a directory"
-msgstr "作業先 %s はディレクトリではありません"
+msgstr "複製先 %s はディレクトリではありません"
 
 msgid "(consider using --after)\n"
 msgstr "(--after を使ってみては?)\n"
 
 #, python-format
 msgid "found revision %s from %s\n"
-msgstr "リビジョン %s を %s で見つけました\n"
+msgstr "リビジョン %s (%s) が指定日時に合致します\n"
 
 msgid "revision matching date not found"
-msgstr "リビジョンに一致する日付がありません"
+msgstr "指定日時に合致するリビジョンがありません"
 
 #, python-format
 msgid "cannot follow file not in parent revision: \"%s\""
 "    それ以外の場合は、 マージが実施されますが、\n"
 "    マージ結果はコミットされません。"
 
-#, fuzzy
 msgid ""
 "      backout cannot be used to fix either an unwanted or\n"
 "      incorrect merge."
 msgstr ""
-"    .. note::\n"
 "      :hg:`backout` による打消し機能は、マージ実施リビジョンに対しては、\n"
 "      適用できません。"
 
 msgstr "          hg log --graph -r \"bisect(range)\""
 
 msgid "      See :hg:`help revsets` for more about the `bisect()` keyword."
-msgstr ""
-"    `bisect()` キーワードの詳細は :hg:`help revsets` を参照してください。"
+msgstr "      `bisect()` の詳細は :hg:`help revsets` を参照してください。"
 
 msgid "The first good revision is:\n"
 msgstr "最初の good なリビジョンは:\n"
 msgstr "不正な引数の組み合わせです"
 
 msgid "current bisect revision is unknown - start a new bisect to fix"
-msgstr "現在の検証対象リビジョンが不在です - 探索を最初からやり直してください"
+msgstr "未知のリビジョンが検証対象です - 探索を最初からやり直してください"
 
 msgid "current bisect revision is a merge"
 msgstr "現在の検証対象リビジョンはマージ実施リビジョンです"
 msgstr "--rev と --rename は併用できません"
 
 msgid "bookmark name required"
-msgstr "ブックマーク名を要求しました"
+msgstr "ブックマーク名指定が必要です"
 
 #, python-format
 msgid "bookmark '%s' does not exist"
 msgstr "ブックマーク '%s' は存在しません"
 
 msgid "new bookmark name required"
-msgstr "新しいブックマーク名を要求しました"
+msgstr "新しいブックマーク名が必要です"
 
 msgid "only one new bookmark name allowed"
 msgstr "新規ブックマーク名の指定は1つだけです"
 msgid "set or show the current branch name"
 msgstr "ブランチ名の設定、 ないし現ブランチ名の表示"
 
-#, fuzzy
 msgid ""
 "       Branch names are permanent and global. Use :hg:`bookmark` to create "
 "a\n"
 "       light-weight bookmark instead. See :hg:`help glossary` for more\n"
 "       information about named branches and bookmarks."
 msgstr ""
-"    .. note::\n"
-"       ブランチ名は永続的で且つ共有されます。\n"
+"       ブランチ名の情報は永続的で、 他リポジトリにも伝搬されます。\n"
 "       軽量な名前付けが必要なら、\n"
 "       :hg:`bookmark` でブックマークを作成してください。\n"
 "       名前付きブランチと、 ブックマークの詳細に関しては、\n"
 msgid "    Differences between files are shown using the unified diff format."
 msgstr "    差分は unified diff 形式で表示されます。"
 
-#, fuzzy
 msgid ""
 "       diff may generate unexpected results for merges, as it will\n"
 "       default to comparing against the working directory's first\n"
 "       parent changeset if no revisions are specified."
 msgstr ""
-"    .. note::\n"
 "       マージ実施リビジョンに対する本コマンドの差分出力が、\n"
 "       期待と異なる場合があるのは、 対象リビジョンが無指定の場合に\n"
 "       比較対象となるのが、 作業領域の第1親に固定されているためです。"
 "    ヘッダ情報として表示される情報は:作成者/日付/\n"
 "    (default 以外の場合は)ブランチ名前/ハッシュ値/親リビジョン/コミットログ"
 
-#, fuzzy
 msgid ""
 "       export may generate unexpected diff output for merge\n"
 "       changesets, as it will compare the merge changeset against its\n"
 "       first parent only."
 msgstr ""
-"    .. note::\n"
-"       マージ実施リビジョンに対する本コマンドの差分出力が、\n"
-"       期待と異なる場合があるのは、 比較対象が対象リビジョンの第1親に\n"
-"       固定されているためです。"
+"       本コマンドの差分出力が、 マージ実施リビジョンに対しては、\n"
+"       期待と異なる場合があります。 差分出力の際の比較対象が、\n"
+"       指定リビジョンの第1親に固定されているためです。"
 
 msgid ""
 "    Output may be to a file, in which case the name of the file is\n"
 "    手動で衝突を解決してください。 全ての衝突が解消されたならば、\n"
 "    -c/--continue 指定により、 未完了の移植を再開してください。"
 
-#, fuzzy
 msgid "      The -c/--continue option does not reapply earlier options."
-msgstr ""
-"    .. note::\n"
-"      -c/--continue でも、 以前のオプション指定までは再現されません。"
+msgstr "      -c/--continue でも、 以前のオプション指定までは再現されません。"
 
 msgid ""
 "      - copy a single change to the stable branch and edit its description::"
 
 #, python-format
 msgid "skipping ungraftable merge revision %s\n"
-msgstr "移植できないマージリビジョン %s を飛ばしています\n"
+msgstr "移植できないマージリビジョン %s を無視\n"
 
 #, python-format
 msgid "skipping ancestor revision %s\n"
-msgstr "祖先リビジョン %s を飛ばしています\n"
+msgstr "祖先リビジョン %s を無視\n"
 
 #, python-format
 msgid "skipping revision %s (already grafted to %s)\n"
-msgstr "リビジョン %s を飛ばしています (%s に移植済み)\n"
+msgstr "リビジョン %s を無視 (%s に移植済み)\n"
 
 #, python-format
 msgid "skipping already grafted revision %s (%s also has origin %d)\n"
-msgstr "移植済みリビジョン %s を飛ばしています (%s も同じリビジョン %d 由来)\n"
+msgstr "移植済みリビジョン %s を無視 (%s も同じリビジョン %d 由来)\n"
 
 #, python-format
 msgid "skipping already grafted revision %s (was grafted from %d)\n"
-msgstr "移植済みリビジョン %s を飛ばしています (移植元: %d)\n"
+msgstr "移植済みリビジョン %s を無視 (移植元: %d)\n"
 
 #, python-format
 msgid "grafting revision %s\n"
 
 #, python-format
 msgid " (started at %s)"
-msgstr "(%s から開始)"
+msgstr " (%s から開始)"
 
 msgid "show only help for extensions"
 msgstr "エクステンションのヘルプのみを表示"
 "    -v/--verbose が指定された場合、 変更対象ファイル一覧と、\n"
 "    コミットログの全文も表示されます。"
 
-#, fuzzy
 msgid ""
 "       log -p/--patch may generate unexpected diff output for merge\n"
 "       changesets, as it will only compare the merge changeset against\n"
 "       its first parent. Also, only files different from BOTH parents\n"
 "       will appear in files:."
 msgstr ""
-"    .. note::\n"
-"       マージ実施リビジョンに対する -p/--patch 指定による差分出力が、\n"
-"       期待と異なる場合があるのは、 比較対象が対象リビジョンの第1親に\n"
-"       固定されているためです。 ファイル一覧が予期せぬ内容となるのは、\n"
-"       親同士で内容が異なるファイルのみが列挙されるためです。"
-
-#, fuzzy
+"       -p/--patch 指定による差分出力が、 マージ実施リビジョンに対しては、\n"
+"       期待と異なる場合があります。 差分出力の際の比較対象が、\n"
+"       指定リビジョンの第1親に固定されているためです。 ファイル一覧が、\n"
+"       期待と異なる場合もあります。 更新対象ファイル一覧に列挙されるのは、\n"
+"       マージ対象リビジョンの間で、 内容が異なるファイルのみが、\n"
+"       更新対象として列挙されるためです。"
+
 msgid ""
 "       for performance reasons, log FILE may omit duplicate changes\n"
 "       made on branches and will not show deletions. To see all\n"
 "       changes including duplicates and deletions, use the --removed\n"
 "       switch."
 msgstr ""
-"    .. note::\n"
 "       ファイル名指定付きでの実行では、 複数ブランチ上での同一変更や、\n"
 "       登録除外実施リビジョンは、 性能的な点から表示しません。\n"
 "       重複変更や登録除外を含め、 全てのリビジョンを表示する場合、\n"
 msgid "only one phase can be specified"
 msgstr "フェーズ指定は1つだけです"
 
-#, fuzzy, python-format
+#, python-format
 msgid "cannot move %i changesets to a higher phase, use --force\n"
 msgstr ""
-"公開方向へのフェーズ変更が %i 個のリビジョンで失敗しました。\n"
-"適宜 --force 指定を行ってください。\n"
+"フェーズ変更による公開制限が %i 個のリビジョンで失敗しました。\n"
+"適宜 --force を指定してください。\n"
 
 #, python-format
 msgid "phase changed for %i changesets\n"
 "    連携先に存在しない名前付きブランチを新規作成する場合は --new-branch\n"
 "    を使用します。 このオプションは、 新規ブランチの作成のみを許可します。"
 
-#, fuzzy
 msgid ""
 "      Extra care should be taken with the -f/--force option,\n"
 "      which will push all new heads on all branches, an action which will\n"
 "      almost always cause confusion for collaborators."
 msgstr ""
-"    .. note::\n"
 "      -f/--force を指定した場合、 対象ブランチ上の全ヘッドリビジョンが、\n"
 "      連携先に反映されます。 この挙動は多くの場合、 共同作業者の間で、\n"
 "      無用な混乱の原因となりますので、 指定の際には十分な注意が必要です。"
 msgid "restore files to their checkout state"
 msgstr "親リビジョンの状態でファイルを復旧"
 
-#, fuzzy
 msgid ""
 "       To check out earlier revisions, you should use :hg:`update REV`.\n"
 "       To cancel an uncommitted merge (and lose your changes),\n"
 "       use :hg:`update --clean .`."
 msgstr ""
-"    .. note::\n"
-"       作業領域を、 別リビジョン時点の内容で更新する場合は\n"
-"       :hg:`update 対象リビジョン` を実行してください。\n"
-"       マージ実施を途中で取り消す場合は\n"
-"       :hg:`update --clean .` を実行してください\n"
-"       (マージにおける修正内容は破棄されます)。"
+"       指定リビジョンでの作業領域の更新は :hg:`update 対象リビジョン`\n"
+"       で行います。 マージ途中での取り消しは :hg:`update --clean .`\n"
+"       で行います (マージにおける修正内容は破棄されます)。"
 
 msgid ""
 "    With no revision specified, revert the specified files or directories\n"
 "    -q/--quiet 指定がある場合、 -u/--unknown ないし -i/--ignored\n"
 "    が明示的に指定されない限り、 未登録ファイルは表示されません。"
 
-#, fuzzy
 msgid ""
 "       status may appear to disagree with diff if permissions have\n"
 "       changed or a merge has occurred. The standard diff format does\n"
 "       not report permission changes and diff only reports changes\n"
 "       relative to one merge parent."
 msgstr ""
-"    .. note::\n"
-"       権限設定の変更やマージが行われた場合、 差分表示から期待される\n"
-"       結果とは異なる状態が表示される可能性があります。\n"
-"       標準的な差分形式は権限変更の情報を含みませんし、\n"
-"       マージ実施リビジョンに対しては、 一方の親リビジョンとの差分\n"
-"       しか表示しないためです。"
+"       権限設定の変更やマージが行われた場合、 状態表示の結果が、\n"
+"       差分表示から期待される結果とは、 異なる可能性があります。\n"
+"       標準的な差分形式は、 権限変更の情報を含みませんし、\n"
+"       マージ実施リビジョンでの差分表示は、 差分出力の際の比較対象が、\n"
+"       指定リビジョンの第1親に固定されているためです。"
 
 msgid ""
 "    If one revision is given, it is used as the base revision.\n"
 msgstr "hg: %s\n"
 
 msgid "abort: remote error:\n"
-msgstr "中: 連携エラー:\n"
+msgstr "中: 連携エラー:\n"
 
 #, python-format
 msgid "abort: %s!\n"
-msgstr "中: %s!\n"
+msgstr "中: %s!\n"
 
 #, python-format
 msgid "abort: %s"
-msgstr "中: %s"
+msgstr "中: %s"
 
 msgid " empty string\n"
 msgstr " 空文字列\n"
 
 #, python-format
 msgid "abort: error: %s\n"
-msgstr "中: エラー: %s\n"
+msgstr "中: エラー: %s\n"
 
 msgid "broken pipe\n"
 msgstr "パイプ破壊(EPIPE)\n"
 
 #, python-format
 msgid "abort: %s: '%s'\n"
-msgstr "中: %s: '%s'\n"
+msgstr "中: %s: '%s'\n"
 
 msgid "interrupted!\n"
 msgstr "中断されました!\n"
 "パイプ破壊(EPIPE)\n"
 
 msgid "abort: out of memory\n"
-msgstr "中: メモリ不足\n"
+msgstr "中: メモリ不足\n"
 
 msgid "the extension author."
 msgstr "エクステンションの作者"
 
 #, python-format
 msgid "warning: internal:merge cannot merge symlinks for %s\n"
-msgstr "警告: internal:merge はシンボリックリンク %s マージできません\n"
+msgstr "警告: internal:merge はシンボリックリンク %s マージできません\n"
 
 msgid ""
 "``internal:dump``\n"
 
 #, python-format
 msgid "unknown encoding '%s'"
-msgstr "不明なエンコーディング '%s' が指定されました"
+msgstr "未知のエンコーディング '%s' が指定されました"
 
 msgid ""
 "``eol(style)``\n"
 
 #, python-format
 msgid "    %s"
-msgstr "   %s"
+msgstr "    %s"
 
 #, python-format
 msgid "alias for: hg %s"
 "    collapses each collection of repositories found within a subdirectory\n"
 "    into a single entry for that subdirectory. Default is False."
 msgstr ""
+"``collapse``\n"
 "    ``descend`` が有効な場合、 サブディレクトリ配下のリポジトリ群も、\n"
 "    単一の一覧ページに表示されます。 同時に ``collapse`` も有効な場合、\n"
 "    サブディレクトリ配下のリポジトリ群は、 対応パスへの誘導を行う、\n"
 msgid "Lastly, there is Mercurial's internal format:"
 msgstr "最後に、 Mercurial 固有の内部形式を示します:"
 
-#, fuzzy
 msgid "- ``1165411109 0`` (Wed Dec 6 13:18:29 2006 UTC)"
-msgstr "- ``1165432709 0`` (2006年12月6日 13:18:29 UTC)"
+msgstr "- ``1165411109 0`` (2006年12月6日 13:18:29 UTC)"
 
 msgid ""
 "This is the internal representation format for dates. The first number\n"
 "    HGPLAIN is enabled. Currently the only value supported is \"i18n\",\n"
 "    which preserves internationalization in plain mode."
 msgstr ""
-"    HGPLAIN による設定無効化の際でも、\n"
-"    維持する機能をカンマ区切りで列挙します。\n"
-"    現在利用可能な機能名は \"i18n\" のみで、\n"
+"HGPLAINEXCEPT\n"
+"    HGPLAIN による設定無効化の際でも、 継続して利用したい機能名を、\n"
+"    カンマ区切りで列挙します。 現在利用可能な機能名は \"i18n\" のみで、\n"
 "    国際化関連機能が維持されます。"
 
 msgid ""
 msgid ""
 "``x - y``\n"
 "  Files in x but not in y."
-msgstr "  ファイル群 x のうち、 y に属さないもの。"
+msgstr ""
+"``x - y``\n"
+"  ファイル群 x のうち、 y に属さないもの。"
 
 msgid "The following predicates are supported:"
 msgstr "使用可能な述語を以下に列挙します:"
 "  更にスラッシュ('/')を付与してください::"
 
 msgid "    ssh://example.com//tmp/repository"
-msgstr "     例: ssh://example.com//tmp/repository"
+msgstr "    例: ssh://example.com//tmp/repository"
 
 msgid ""
 "- Mercurial doesn't use its own compression via SSH; the right thing\n"
 msgstr "指定の複製元は、 リビジョン指定付き複製が未サポートです"
 
 msgid "clone from remote to remote not supported"
-msgstr "リモートからリモートの複製は未サポートです"
+msgstr "リモートからリモートの複製は未サポートです"
 
 msgid "updating to bookmark @\n"
 msgstr "ブックマーク @ への更新中\n"
 
 #, python-format
 msgid "repository %s already exists"
-msgstr "%sというリポジトリは既に存在しています"
+msgstr "リポジトリ %s は既に存在しています"
 
 #, python-format
 msgid ".hg/sharedpath points to nonexistent directory %s"
 msgstr "中断トランザクションを検出 - 'hg recover' を実施してください"
 
 msgid "rolling back interrupted transaction\n"
-msgstr "中断されたトランザクションをロールバックしています\n"
+msgstr "中断されたトランザクションをロールバック\n"
 
 msgid "no interrupted transaction available\n"
 msgstr "中断されたトランザクションはありません\n"
 msgstr "連携先の changegroupsubset 機能未対応により、 部分取り込みできません。"
 
 msgid "destination does not support push"
-msgstr "指定の連携先には履歴反映できません"
+msgstr "指定の連携先には履歴反映できません"
 
 #, python-format
 msgid "cannot lock source repo, skipping local %s phase update\n"
 msgstr "その処理はサーバで禁止されています"
 
 msgid "locking the remote repository failed"
-msgstr "連携先リポジトリロックできませんでした"
+msgstr "連携先リポジトリロックに失敗"
 
 msgid "the server sent an unknown error code"
 msgstr "サーバが未知のエラーコードを返却しました"
 
 #, python-format
 msgid "failed to remove %s from manifest"
-msgstr "マニフェストから %s 削除できませんでした"
+msgstr "マニフェストから %s 削除に失敗"
 
 #, python-format
 msgid "invalid pattern (%s): %s"
 msgstr "ファイル名の文字大小の問題で %s と %s が衝突します"
 
 msgid "resolving manifests\n"
-msgstr "管理ファイル一覧解決しています\n"
+msgstr "管理ファイル一覧解決\n"
 
 #, python-format
 msgid ""
 "local changed %s which remote deleted\n"
 "use (c)hanged version or (d)elete?$$ &Changed $$ &Delete"
 msgstr ""
-"変更したファイル %s は別リビジョンで登録除外されています。\n"
+"変更したファイル %s が、マージ対象リビジョンで登録除外されています。\n"
 "変更:(c)hanged と登録除外:(d)elete のどちらを採用しますか?$$ &Changed $$ "
 "&Delete"
 
 "remote changed %s which local deleted\n"
 "use (c)hanged version or leave (d)eleted?$$ &Changed $$ &Deleted"
 msgstr ""
-"リモートで変更された %s は、ローカルで削除されています。\n"
-"変更:(c)hanged と削除:(d)elete のどちらを採用しますか?$$ &Changed $$ "
+"登録除外済みのファイル %s が、マージ対象リビジョンで変更されています。\n"
+"変更:(c)hanged と登録除外:(d)elete のどちらを採用しますか?$$ &Changed $$ "
 "&Deleted"
 
 #, python-format
 
 #, python-format
 msgid "getting %s\n"
-msgstr "%s 取得しています\n"
+msgstr "%s 取得\n"
 
 msgid "updating"
 msgstr "更新中"
 
 #, python-format
 msgid "patching file %s\n"
-msgstr "ファイル %s パッチ適用しています\n"
+msgstr "ファイル %s パッチ適用\n"
 
 #, python-format
 msgid "bad hunk #%d %s (%d %d %d %d)"
 
 #, python-format
 msgid "cannot create %s: destination already exists\n"
-msgstr "%s を作成できません: 作業先にすでに存在します\n"
+msgstr "%s を作成できません: 対象ファイルが既に存在します\n"
 
 #, python-format
 msgid "file %s already exists\n"
 
 #, python-format
 msgid "cannot create %s: destination already exists"
-msgstr "%s を作成できません: 作業先にすでに存在します"
+msgstr "%s を作成できません: 対象ファイルが既に存在します"
 
 #, python-format
 msgid "unsupported parser state: %s"
 msgid "cannot %s; remote repository does not support the %r capability"
 msgstr "%s ができません。 連携先は機能 %r が未サポートです"
 
-#, fuzzy
 msgid "cannot lookup negative revision"
-msgstr "連携先でのリビジョンの検索"
+msgstr "負値のリビジョン指定は無効です"
 
 msgid "cannot change null revision phase"
 msgstr "null リビジョンのフェーズは変更できません"
 
 #, python-format
 msgid "killed by signal %d"
-msgstr "%d のシグナルで強制終了ました"
+msgstr "シグナル %d で強制終了されました"
 
 #, python-format
 msgid "saved backup bundle to %s\n"
 
 #, python-format
 msgid "warning: removing potentially hostile .hg/hgrc in '%s'"
-msgstr ""
+msgstr "警告: '%s' 中の .hg/hgrc は潜在的な問題となりえるため破棄します"
 
 #, python-format
 msgid "unknown subrepo type %s"
 
 #, python-format
 msgid "unknown method '%s'"
-msgstr "不明な処理 '%s' が指定されました"
+msgstr "未知の処理 '%s' が指定されました"
 
 msgid "expected a symbol"
 msgstr "シンボル指定が必要です"
 
 #, python-format
 msgid "unknown function '%s'"
-msgstr "不明な関数 '%s' が指定されました"
+msgstr "未知の関数 '%s' が指定されました"
 
 msgid "expected template specifier"
 msgstr "テンプレート指定が必要です"
 
 #. i18n: "get" is a keyword
 msgid "get() expects a dict as first argument"
-msgstr "get() の第1引数は辞書でなければなりません"
+msgstr "get() の第1引数は辞書を指定してください"
 
 #. i18n: "if" is a keyword
 msgid "if expects two or three arguments"
 msgstr "%s.%s の値 ('%s') はバイト数を表す値ではありません"
 
 msgid "enter a commit username:"
-msgstr "コミットするユーザ名を入力してください:"
+msgstr "コミット実施ユーザ名を入力してください:"
 
 #, python-format
 msgid "no username found, using '%s' instead\n"
 
 #, python-format
 msgid "username %s contains a newline\n"
-msgstr "ユーザ名 %s 改行を含んでいます\n"
+msgstr "ユーザ名 %s 改行を含んでいます\n"
 
 msgid "response expected"
-msgstr "レスポンスがありません"
+msgstr "何らかの入力が必要です"
 
 msgid "unrecognized response\n"
-msgstr "認識できないレスポンス\n"
+msgstr "入力が不正です\n"
 
 msgid "password: "
 msgstr "パスワード: "
 msgstr "union 形式のリポジトリは新規作成できません"
 
 msgid "http authorization required"
-msgstr "HTTP 認証に失敗"
+msgstr "HTTP 認証が要求されています"
 
 msgid "http authorization required\n"
-msgstr "HTTP 認証要求しました\n"
+msgstr "HTTP 認証要求されています\n"
 
 #, python-format
 msgid "realm: %s\n"
-msgstr "認証領域: %s\n"
+msgstr "認証領域(realm): %s\n"
 
 #, python-format
 msgid "user: %s\n"
 
 #, python-format
 msgid "command '%s' failed: %s"
-msgstr "コマンド '%s' 失敗: %s"
-
-#, fuzzy
+msgstr "コマンド '%s' が失敗: %s"
+
 msgid "filename ends with '\\', which is invalid on Windows"
-msgstr "ファイル名の末尾が、 Windows 上で不正な '%s' です"
-
-#, fuzzy
+msgstr "ファイル名の末尾が、 Windows 上で不正な文字 '\\' です"
+
 msgid "directory name ends with '\\', which is invalid on Windows"
-msgstr "ファイル名の末尾が、 Windows 上で不正な '%s' です"
+msgstr "ディレクトリ名の末尾が、 Windows 上で不正な文字 '\\' です"
 
 #, python-format
 msgid "filename contains '%s', which is reserved on Windows"
 
 #, python-format
 msgid "%s must be nonnegative (see 'hg help dates')"
-msgstr "%s は正の値でなければなりません ('hg help dates' 参照)"
+msgstr "%s は正の値を指定してください ('hg help dates' 参照)"
 
 #, python-format
 msgid "%.0f GB"
 
 #, python-format
 msgid "%s in manifests not found"
-msgstr "管理対象一覧中 %s は存在しません"
+msgstr "管理対象一覧中 %s は存在しません"
 
 #, python-format
 msgid "warning: orphan revlog '%s'"
 msgstr "履歴反映に失敗:"
 
 msgid "number of cpus must be an integer"
-msgstr "CPU 数は数値でなければなりません"
-
-#~ msgid ""
-#~ "    Returns 0 on success, 1 if nothing to rebase.\n"
-#~ "    "
-#~ msgstr ""
-#~ "    成功時のコマンド終了値は 0、 移植が実施されない場合は 1 です。\n"
-#~ "    "
-
-#~ msgid "exact revision search"
-#~ msgstr "リビジョン識別子による検索"
-
-#~ msgid "literal keyword search"
-#~ msgstr "キーワードによる検索"
-
-#~ msgid "revset expression search"
-#~ msgstr "revset 記述による検索"
+msgstr "CPU 数には数値を指定してください"
 #!/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 "WF" 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 0 == len(infostock[1]) or infostock[4]:
-        return # not yet translated or fuzzy
-
-    if infostock[0].endswith('::') != infostock[1].endswith('::'):
-        msgs.append('different "::"-end-ness between msgid and msgstr')
-
-    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("different rst title mark between msgid and msgstr")
-        elif colwidth(msgstrs[0]) != colwidth(msgstrs[1]):
-            msgs.append("different length between title marks and title text")
-
-    if '$$' in infostock[0]:
-        idparts = infostock[0].split('$$')
-        idmsg = idparts[0].rstrip(' ').decode('string-escape')
-        idresps = [(s.find('&'), len(s)) for s in
-                   [p.strip(' ') for p in idparts[1:]]]
-
-        strparts = infostock[1].split('$$')
-        strmsg = strparts[0].rstrip(' ').decode('string-escape')
-        strresps = [(s.find('&'), len(s)) for s in
-                    [p.strip(' ') for p in strparts[1:]]]
-
-        if [1 for (i, l) in idresps if i == -1]:
-            msgs.append("msgid has invalid choice missing '&'")
-        if [1 for (i, l) in idresps if l <= (i + 1)]:
-            msgs.append("msgid has invalid '&' followed by none")
-
-        if [1 for (i, l) in strresps if i == -1]:
-            msgs.append("msgstr has invalid choice missing '&'")
-        if [1 for (i, l) in strresps if l <= (i + 1)]:
-            msgs.append("msgstr has invalid '&' followed by none")
-
-        if (idmsg[-1] == '\n') != (strmsg[-1] == '\n'):
-            msgs.append("different EOL of prompt text between msgid and msgstr")
-        if len(idparts) != len(strparts):
-            msgs.append("different number of choices between msgid and msgstr")
-
-####################
-
 import sys
 import optparse
+import polib
 
-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("""%prog [options] pofile [target ...]
 
-optparser = optparse.OptionParser(usage='USAGE: %prog [options]',
-                                  option_list=optionlist)
+This picks untranslated/fuzzy messages up from specified '*.po' file.
 
+This only picks up messages occured in 'target' source files, if specified.
+'target' has to match exactly against filename listed in 'occurrence'
+comment (started with '#:') of '*.po' file.
+
+Both fuzzy and untranslated messages are picked up, if neither '--fuzzy'
+nor '--untranslated' is specified.
+""")
+optparser.add_option("", "--fuzzy",
+                     help="pick fuzzy messages up ",
+                     action="store_true")
+optparser.add_option("", "--untranslated",
+                     help="pick untranslated messages up",
+                     action="store_true")
 (options, args) = optparser.parse_args()
 
+if not args:
+    sys.stderr.write("%s:error: at least one *.po file is required\n"
+                     % (sys.argv[0]))
+    sys.exit(1)
+
+# replace polib._POFileParser to show linenum of problematic msgstr
+class ExtPOFileParser(polib._POFileParser):
+    def process(self, symbol, linenum):
+        super(ExtPOFileParser, self).process(symbol, linenum)
+        if symbol == 'MS': # msgstr
+            self.current_entry.linenum = linenum
+polib._POFileParser = ExtPOFileParser
+
+pofilename = args[0]
+pofile = polib.pofile(pofilename)
 if 1 < len(args):
-    expecteds = args[1:]
+    targets = set(args[1:])
+    def match(pe):
+        for f, l in pe.occurrences:
+            if f in targets:
+                return True
+else:
+    match = lambda pe: True
 
-    def checkargs(filenames):
-        if not expecteds: return True
-        for filename in filenames:
-            if filename in expecteds: return True
-        return False
+if not options.untranslated and not options.fuzzy:
+    options.untranslated = options.fuzzy = True
 
-    interestedin = checkargs
-else:
-    interestedin = lambda(x): True
+poentries = []
+if options.untranslated:
+    for pe in pofile.untranslated_entries():
+        if match(pe):
+            poentries.append(pe)
+if options.fuzzy:
+    for pe in pofile.fuzzy_entries():
+        if match(pe):
+            poentries.append(pe)
 
-if (0 < len(args)) and ('-' != args[0]):
-    filename = args[0]
-    infile = open(filename)
-else:
-    filename = '<stdin>'
-    infile = sys.stdin
+for pe in sorted(poentries, lambda x, y: x.linenum - y.linenum):
+    sys.stderr.write("%s:%d: %s\n"
+                     % (pofilename, pe.linenum,
+                        'fuzzy' in pe.flags
+                        and 'marked as fuzzy' or 'not yet translated'))
 
-if ((not options.untranslated) and
-    (not options.fuzzy) and
-    (not options.wrapped) and
-    (not options.rst_error)):
-    # show all detected errors other than "wrapped" 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('')
+if poentries:
+    sys.exit(1)
+copyright (c) 2006-2010 David JEAN LOUIS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+# -*- coding: utf-8 -*-
+# no-check-code
+#
+# License: MIT (see LICENSE file provided)
+# vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
+
+"""
+**polib** allows you to manipulate, create, modify gettext files (pot, po and
+mo files).  You can load existing files, iterate through it's entries, add,
+modify entries, comments or metadata, etc. or create new po files from scratch.
+
+**polib** provides a simple and pythonic API via the :func:`~polib.pofile` and
+:func:`~polib.mofile` convenience functions.
+"""
+
+__author__    = 'David Jean Louis <izimobil@gmail.com>'
+__version__   = '0.6.4'
+__all__       = ['pofile', 'POFile', 'POEntry', 'mofile', 'MOFile', 'MOEntry',
+                 'detect_encoding', 'escape', 'unescape', 'detect_encoding',]
+
+import array
+import codecs
+import os
+import re
+import struct
+import sys
+import textwrap
+import types
+
+
+# the default encoding to use when encoding cannot be detected
+default_encoding = 'utf-8'
+
+# _pofile_or_mofile {{{
+
+def _pofile_or_mofile(f, type, **kwargs):
+    """
+    Internal function used by :func:`polib.pofile` and :func:`polib.mofile` to
+    honor the DRY concept.
+    """
+    # get the file encoding
+    enc = kwargs.get('encoding')
+    if enc is None:
+        enc = detect_encoding(f, type == 'mofile')
+
+    # parse the file
+    kls = type == 'pofile' and _POFileParser or _MOFileParser
+    parser = kls(
+        f,
+        encoding=enc,
+        check_for_duplicates=kwargs.get('check_for_duplicates', False)
+    )
+    instance = parser.parse()
+    instance.wrapwidth = kwargs.get('wrapwidth', 78)
+    return instance
+
+# }}}
+# function pofile() {{{
+
+def pofile(pofile, **kwargs):
+    """
+    Convenience function that parses the po or pot file ``pofile`` and returns
+    a :class:`~polib.POFile` instance.
+
+    Arguments:
+
+    ``pofile``
+        string, full or relative path to the po/pot file or its content (data).
+
+    ``wrapwidth``
+        integer, the wrap width, only useful when the ``-w`` option was passed
+        to xgettext (optional, default: ``78``).
+
+    ``encoding``
+        string, the encoding to use (e.g. "utf-8") (default: ``None``, the
+        encoding will be auto-detected).
+
+    ``check_for_duplicates``
+        whether to check for duplicate entries when adding entries to the
+        file (optional, default: ``False``).
+    """
+    return _pofile_or_mofile(pofile, 'pofile', **kwargs)
+
+# }}}
+# function mofile() {{{
+
+def mofile(mofile, **kwargs):
+    """
+    Convenience function that parses the mo file ``mofile`` and returns a
+    :class:`~polib.MOFile` instance.
+
+    Arguments:
+
+    ``mofile``
+        string, full or relative path to the mo file or its content (data).
+
+    ``wrapwidth``
+        integer, the wrap width, only useful when the ``-w`` option was passed
+        to xgettext to generate the po file that was used to format the mo file
+        (optional, default: ``78``).
+
+    ``encoding``
+        string, the encoding to use (e.g. "utf-8") (default: ``None``, the
+        encoding will be auto-detected).
+
+    ``check_for_duplicates``
+        whether to check for duplicate entries when adding entries to the
+        file (optional, default: ``False``).
+    """
+    return _pofile_or_mofile(mofile, 'mofile', **kwargs)
+
+# }}}
+# function detect_encoding() {{{
+
+def detect_encoding(file, binary_mode=False):
+    """
+    Try to detect the encoding used by the ``file``. The ``file`` argument can
+    be a PO or MO file path or a string containing the contents of the file.
+    If the encoding cannot be detected, the function will return the value of
+    ``default_encoding``.
+
+    Arguments:
+
+    ``file``
+        string, full or relative path to the po/mo file or its content.
+
+    ``binary_mode``
+        boolean, set this to True if ``file`` is a mo file.
+    """
+    rx = re.compile(r'"?Content-Type:.+? charset=([\w_\-:\.]+)')
+
+    def charset_exists(charset):
+        """Check whether ``charset`` is valid or not."""
+        try:
+            codecs.lookup(charset)
+        except LookupError:
+            return False
+        return True
+
+    if not os.path.exists(file):
+        match = rx.search(file)
+        if match:
+            enc = match.group(1).strip()
+            if charset_exists(enc):
+                return enc
+    else:
+        if binary_mode:
+            mode = 'rb'
+        else:
+            mode = 'r'
+        f = open(file, mode)
+        for l in f.readlines():
+            match = rx.search(l)
+            if match:
+                f.close()
+                enc = match.group(1).strip()
+                if charset_exists(enc):
+                    return enc
+        f.close()
+    return default_encoding
+
+# }}}
+# function escape() {{{
+
+def escape(st):
+    """
+    Escapes the characters ``\\\\``, ``\\t``, ``\\n``, ``\\r`` and ``"`` in
+    the given string ``st`` and returns it.
+    """
+    return st.replace('\\', r'\\')\
+             .replace('\t', r'\t')\
+             .replace('\r', r'\r')\
+             .replace('\n', r'\n')\
+             .replace('\"', r'\"')
+
+# }}}
+# function unescape() {{{
+
+def unescape(st):
+    """
+    Unescapes the characters ``\\\\``, ``\\t``, ``\\n``, ``\\r`` and ``"`` in
+    the given string ``st`` and returns it.
+    """
+    def unescape_repl(m):
+        m = m.group(1)
+        if m == 'n':
+            return '\n'
+        if m == 't':
+            return '\t'
+        if m == 'r':
+            return '\r'
+        if m == '\\':
+            return '\\'
+        return m # handles escaped double quote
+    return re.sub(r'\\(\\|n|t|r|")', unescape_repl, st)
+
+# }}}
+# class _BaseFile {{{
+
+class _BaseFile(list):
+    """
+    Common base class for the :class:`~polib.POFile` and :class:`~polib.MOFile`
+    classes. This class should **not** be instanciated directly.
+    """
+
+    def __init__(self, *args, **kwargs):
+        """
+        Constructor, accepts the following keyword arguments:
+
+        ``pofile``
+            string, the path to the po or mo file, or its content as a string.
+
+        ``wrapwidth``
+            integer, the wrap width, only useful when the ``-w`` option was
+            passed to xgettext (optional, default: ``78``).
+
+        ``encoding``
+            string, the encoding to use, defaults to ``default_encoding``
+            global variable (optional).
+
+        ``check_for_duplicates``
+            whether to check for duplicate entries when adding entries to the
+            file, (optional, default: ``False``).
+        """
+        list.__init__(self)
+        # the opened file handle
+        pofile = kwargs.get('pofile', None)
+        if pofile and os.path.exists(pofile):
+            self.fpath = pofile
+        else:
+            self.fpath = kwargs.get('fpath')
+        # the width at which lines should be wrapped
+        self.wrapwidth = kwargs.get('wrapwidth', 78)
+        # the file encoding
+        self.encoding = kwargs.get('encoding', default_encoding)
+        # whether to check for duplicate entries or not
+        self.check_for_duplicates = kwargs.get('check_for_duplicates', False)
+        # header
+        self.header = ''
+        # both po and mo files have metadata
+        self.metadata = {}
+        self.metadata_is_fuzzy = 0
+
+    def __unicode__(self):
+        """
+        Returns the unicode representation of the file.
+        """
+        ret = []
+        entries = [self.metadata_as_entry()] + \
+                  [e for e in self if not e.obsolete]
+        for entry in entries:
+            ret.append(entry.__unicode__(self.wrapwidth))
+        for entry in self.obsolete_entries():
+            ret.append(entry.__unicode__(self.wrapwidth))
+        ret = '\n'.join(ret)
+
+        if type(ret) != types.UnicodeType:
+            return unicode(ret, self.encoding)
+        return ret
+
+    def __str__(self):
+        """
+        Returns the string representation of the file.
+        """
+        return unicode(self).encode(self.encoding)
+
+    def __contains__(self, entry):
+        """
+        Overriden ``list`` method to implement the membership test (in and
+        not in).
+        The method considers that an entry is in the file if it finds an entry
+        that has the same msgid (the test is **case sensitive**).
+
+        Argument:
+
+        ``entry``
+            an instance of :class:`~polib._BaseEntry`.
+        """
+        return self.find(entry.msgid, by='msgid') is not None
+
+    def __eq__(self, other):
+        return unicode(self) == unicode(other)
+
+    def append(self, entry):
+        """
+        Overriden method to check for duplicates entries, if a user tries to
+        add an entry that is already in the file, the method will raise a
+        ``ValueError`` exception.
+
+        Argument:
+
+        ``entry``
+            an instance of :class:`~polib._BaseEntry`.
+        """
+        if self.check_for_duplicates and entry in self:
+            raise ValueError('Entry "%s" already exists' % entry.msgid)
+        super(_BaseFile, self).append(entry)
+
+    def insert(self, index, entry):
+        """
+        Overriden method to check for duplicates entries, if a user tries to
+        add an entry that is already in the file, the method will raise a
+        ``ValueError`` exception.
+
+        Arguments:
+
+        ``index``
+            index at which the entry should be inserted.
+
+        ``entry``
+            an instance of :class:`~polib._BaseEntry`.
+        """
+        if self.check_for_duplicates and entry in self:
+            raise ValueError('Entry "%s" already exists' % entry.msgid)
+        super(_BaseFile, self).insert(index, entry)
+
+    def metadata_as_entry(self):
+        """
+        Returns the file metadata as a :class:`~polib.POFile` instance.
+        """
+        e = POEntry(msgid='')
+        mdata = self.ordered_metadata()
+        if mdata:
+            strs = []
+            for name, value in mdata:
+                # Strip whitespace off each line in a multi-line entry
+                strs.append('%s: %s' % (name, value))
+            e.msgstr = '\n'.join(strs) + '\n'
+        if self.metadata_is_fuzzy:
+            e.flags.append('fuzzy')
+        return e
+
+    def save(self, fpath=None, repr_method='__str__'):
+        """
+        Saves the po file to ``fpath``.
+        If it is an existing file and no ``fpath`` is provided, then the
+        existing file is rewritten with the modified data.
+
+        Keyword arguments:
+
+        ``fpath``
+            string, full or relative path to the file.
+
+        ``repr_method``
+            string, the method to use for output.
+        """
+        if self.fpath is None and fpath is None:
+            raise IOError('You must provide a file path to save() method')
+        contents = getattr(self, repr_method)()
+        if fpath is None:
+            fpath = self.fpath
+        if repr_method == 'to_binary':
+            fhandle = open(fpath, 'wb')
+        else: