diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index d4f5d837e..88c128f56 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -13,13 +13,18 @@ from collections import defaultdict from datetime import datetime from os import path from codecs import open +import os +import pickle from docutils import nodes +from docutils.utils import Reporter from sphinx.builders import Builder from sphinx.util.nodes import extract_messages -from sphinx.util.osutil import SEP +from sphinx.util.osutil import SEP, copyfile from sphinx.util.console import darkgreen +from sphinx.environment import WarningStream +from sphinx.versioning import add_uids, merge_doctrees POHEADER = ur""" # SOME DESCRIPTIVE TITLE. @@ -47,7 +52,42 @@ class I18NBuilder(Builder): def init(self): Builder.init(self) - self.catalogs = defaultdict(list) + self.catalogs = defaultdict(dict) + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree'): + copyfile(fp, fp + '.old') + + def get_old_doctree(self, docname): + fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') + try: + f = open(fp, 'rb') + try: + doctree = pickle.load(f) + finally: + f.close() + except IOError: + return None + doctree.settings.env = self.env + doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, + stream=WarningStream(self.env._warnfunc)) + + def resave_doctree(self, docname, doctree): + reporter = doctree.reporter + doctree.reporter = None + doctree.settings.warning_stream = None + doctree.settings.env = None + doctree.settings.record_dependencies = None + + fp = self.env.doc2path(docname, self.doctreedir, '.doctree') + f = open(fp, 'wb') + try: + pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) + finally: + f.close() + + doctree.reporter = reporter def get_target_uri(self, docname, typ=None): return '' @@ -60,9 +100,24 @@ class I18NBuilder(Builder): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split(SEP, 1)[0]] - for _, msg in extract_messages(doctree): - if msg not in catalog: - catalog.append(msg) + old_doctree = self.get_old_doctree(docname) + + if old_doctree: + list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + else: + list(add_uids(doctree, nodes.TextElement)) + self.resave_doctree(docname, doctree) + + for node, msg in extract_messages(doctree): + catalog.setdefault(node.uid, msg) + + def finish(self): + Builder.finish(self) + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree.old'): + os.remove(fp) class MessageCatalogBuilder(I18NBuilder): """ @@ -71,6 +126,7 @@ class MessageCatalogBuilder(I18NBuilder): name = 'gettext' def finish(self): + I18NBuilder.finish(self) data = dict( version = self.config.version, copyright = self.config.copyright, @@ -86,7 +142,7 @@ class MessageCatalogBuilder(I18NBuilder): pofile = open(pofp, 'w', encoding='utf-8') try: pofile.write(POHEADER % data) - for message in messages: + for message in messages.itervalues(): # message contains *one* line of text ready for translation message = message.replace(u'\\', ur'\\'). \ replace(u'"', ur'\"')