merge with DasIch/sphinx-i18n

This commit is contained in:
Georg Brandl
2010-08-21 19:34:05 +02:00
5 changed files with 52 additions and 30 deletions

View File

@@ -6,9 +6,8 @@ Internationalization
.. versionadded:: 1.1 .. versionadded:: 1.1
Complementary to translations provided for Sphinx-generated messages such as Complementary to translations provided for Sphinx-generated messages such as
navigation bars Sphinx provides mechanisms facilitating *document* translations navigation bars, Sphinx provides mechanisms facilitating *document* translations
in itself. It relies on the existing configuration values :confval:`language` in itself. It relies on the existing configuration values :confval:`language`
and :confval:`locale_dirs`. and :confval:`locale_dirs`.
.. XXX write more! .. XXX write more!

View File

@@ -5,17 +5,19 @@
The MessageCatalogBuilder class. The MessageCatalogBuilder class.
:copyright: Copyright 2010 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import collections
from datetime import datetime
from os import path from os import path
from codecs import open
from datetime import datetime
from collections import defaultdict
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.builders.versioning import VersioningBuilderMixin
from sphinx.util.nodes import extract_messages 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.util.console import darkgreen
POHEADER = ur""" POHEADER = ur"""
@@ -39,14 +41,17 @@ msgstr ""
"""[1:] """[1:]
class MessageCatalogBuilder(Builder):
class I18nBuilder(Builder, VersioningBuilderMixin):
""" """
Builds gettext-style message catalogs (.pot files). General i18n builder.
""" """
name = 'gettext' name = 'i18n'
def init(self): def init(self):
self.catalogs = collections.defaultdict(list) Builder.init(self)
VersioningBuilderMixin.init(self)
self.catalogs = defaultdict(dict)
def get_target_uri(self, docname, typ=None): def get_target_uri(self, docname, typ=None):
return '' return ''
@@ -58,18 +63,26 @@ class MessageCatalogBuilder(Builder):
return return
def write_doc(self, docname, doctree): def write_doc(self, docname, doctree):
"""
Store a document's translatable strings in the message catalog of its
section. For this purpose a document's *top-level directory* -- or
otherwise its *name* -- is considered its section.
"""
catalog = self.catalogs[docname.split(SEP, 1)[0]] catalog = self.catalogs[docname.split(SEP, 1)[0]]
for _, msg in extract_messages(doctree):
# XXX msgctxt for duplicate messages self.handle_versioning(docname, doctree, nodes.TextElement)
if msg not in catalog:
catalog.append(msg) for node, msg in extract_messages(doctree):
catalog.setdefault(node.uid, msg)
def finish(self): def finish(self):
Builder.finish(self)
VersioningBuilderMixin.finish(self)
class MessageCatalogBuilder(I18nBuilder):
"""
Builds gettext-style message catalogs (.pot files).
"""
name = 'gettext'
def finish(self):
I18nBuilder.finish(self)
data = dict( data = dict(
version = self.config.version, version = self.config.version,
copyright = self.config.copyright, copyright = self.config.copyright,
@@ -81,13 +94,15 @@ class MessageCatalogBuilder(Builder):
self.catalogs.iteritems(), "writing message catalogs... ", self.catalogs.iteritems(), "writing message catalogs... ",
lambda (section, _):darkgreen(section), len(self.catalogs)): lambda (section, _):darkgreen(section), len(self.catalogs)):
pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') pofn = path.join(self.outdir, section + '.pot')
pofile = open(pofn, 'w', encoding='utf-8')
try: try:
pofile.write(POHEADER % data) pofile.write(POHEADER % data)
for message in messages: for uid, message in messages.iteritems():
# message contains *one* line of text ready for translation # message contains *one* line of text ready for translation
message = message.replace(u'\\', ur'\\').replace(u'"', ur'\"') message = message.replace(u'\\', ur'\\'). \
pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message replace(u'"', ur'\"')
pofile.write(pomsg.encode('utf-8')) pomsg = u'#%s\nmsgid "%s"\nmsgstr ""\n\n' % (uid, message)
pofile.write(pomsg)
finally: finally:
pofile.close() pofile.close()

View File

@@ -211,7 +211,7 @@ class Locale(Transform):
# XXX ctx not used # XXX ctx not used
#ctx = node.parent #ctx = node.parent
patch = new_document(source, settings) patch = new_document(source, settings)
msgstr = catalog.ugettext(msg) msgstr = catalog.gettext(msg)
# XXX add marker to untranslated parts # XXX add marker to untranslated parts
if not msgstr or msgstr == msg: # as-of-yet untranslated if not msgstr or msgstr == msg: # as-of-yet untranslated
continue continue
@@ -793,6 +793,7 @@ class BuildEnvironment:
if node['level'] < filterlevel: if node['level'] < filterlevel:
node.parent.remove(node) node.parent.remove(node)
def process_dependencies(self, docname, doctree): def process_dependencies(self, docname, doctree):
""" """
Process docutils-generated dependency info. Process docutils-generated dependency info.

View File

@@ -218,4 +218,6 @@ def init(locale_dirs, language, catalog='sphinx'):
translator = gettext.NullTranslations() translator = gettext.NullTranslations()
has_translation = False has_translation = False
translators[catalog] = translator translators[catalog] = translator
if hasattr(translator, 'ugettext'):
translator.gettext = translator.ugettext
return translator, has_translation return translator, has_translation

View File

@@ -46,7 +46,8 @@ def test_gettext(app):
if p.returncode != 0: if p.returncode != 0:
print stdout print stdout
print stderr print stderr
assert False, 'msginit exited with return code %s' % p.returncode assert False, 'msginit exited with return code %s' % \
p.returncode
assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' assert (app.outdir / 'en_US.po').isfile(), 'msginit failed'
try: try:
p = Popen(['msgfmt', 'en_US.po', '-o', p = Popen(['msgfmt', 'en_US.po', '-o',
@@ -59,12 +60,14 @@ def test_gettext(app):
if p.returncode != 0: if p.returncode != 0:
print stdout print stdout
print stderr print stderr
assert False, 'msgfmt exited with return code %s' % p.returncode assert False, 'msgfmt exited with return code %s' % \
assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), 'msgfmt failed' p.returncode
assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), \
'msgfmt failed'
finally: finally:
os.chdir(cwd) os.chdir(cwd)
_ = gettext.translation('test_root', app.outdir, languages=['en']).ugettext _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
assert _("Testing various markup") == u"Testing various markup" assert _("Testing various markup") == u"Testing various markup"
@with_app(buildername='gettext') @with_app(buildername='gettext')
@@ -95,7 +98,9 @@ def setup_patch():
print stdout print stdout
print stderr print stderr
assert False, 'msgfmt exited with return code %s' % p.returncode assert False, 'msgfmt exited with return code %s' % p.returncode
assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), 'msgfmt failed' assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), \
'msgfmt failed'
def teardown_patch(): def teardown_patch():
(test_root / 'xx').rmtree() (test_root / 'xx').rmtree()
test_patch.setup = setup_patch test_patch.setup = setup_patch