Skip to content

Instantly share code, notes, and snippets.

@jpmckinney
Last active July 13, 2018 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpmckinney/966d03bfe5390a93049b84fc5a721ebd to your computer and use it in GitHub Desktop.
Save jpmckinney/966d03bfe5390a93049b84fc5a721ebd to your computer and use it in GitHub Desktop.
import gettext
import os
import re
import sys
from collections import OrderedDict
from docutils import frontend
from docutils import nodes
from docutils.parsers import rst
from docutils.utils import new_document
from recommonmark.parser import CommonMarkParser
from recommonmark.transform import AutoStructify
from sphinx import application
class Translator(nodes.NodeVisitor):
def __init__(self, document, domain, localedir, language):
nodes.NodeVisitor.__init__(self, document)
# Whether we are writing output.
self.writing = True
# The writing context.
self.context = [None]
# List item markers.
self.markers = []
# Table column specifications.
self.colspecs = []
# For zebra tables.
self.table_row_index = 0
# The output.
self.text = ''
self.translator = gettext.translation(domain, localedir, languages=[language], fallback=language == 'en')
def append(self, text):
if self.writing:
self.text += text
def translate(self, node):
# See https://github.com/sphinx-doc/sphinx/blob/v1.5.1/sphinx/util/nodes.py#L142-L166
message = node.rawsource.strip()
self.append(self.translator.gettext(message))
def astext(self):
return self.text
def __getattr__(self, name):
# Otherwise, we need to implement a lot of empty methods to avoid exceptions.
return lambda *args: None
# Text
def visit_Text(self, node):
if self.context[-1] == 'block-raw':
self.append(node.astext())
# System
def depart_document(self, node):
self.text = self.text[:-1] # remove extra newline
def visit_system_message(self, node):
self.writing = False
def depart_system_message(self, node):
self.writing = True
# Block or inline
def visit_raw(self, node):
if node.parent.tagname != 'paragraph':
self.context.append('block-raw')
def depart_raw(self, node):
if node.parent.tagname != 'paragraph':
self.context.pop()
self.append('\n\n')
# Block
def visit_block_quote(self, node):
self.context.append('block-quote')
def depart_block_quote(self, node):
self.context.pop()
def visit_paragraph(self, node):
if self.context[-1] == 'block-quote':
self.append('> ')
self.translate(node)
def depart_paragraph(self, node):
if self.context[-1] not in ('th', 'td'):
self.append('\n')
if not self.markers:
self.append('\n')
def visit_literal_block(self, node):
self.append('```{}\n'.format(node.attributes.get('language', '')))
self.append(node.rawsource)
def depart_literal_block(self, node):
self.append('```\n\n')
def visit_section(self, node):
self.append('#' * node.attributes['level'] + ' ')
def visit_title(self, node):
self.translate(node)
def depart_title(self, node):
self.append('\n\n')
# Lists
def visit_bullet_list(self, node):
self.markers.append('*')
def visit_enumerated_list(self, node):
self.markers.append('1.')
def depart_bullet_list(self, node):
self.markers.pop()
if not self.markers:
self.append('\n')
def depart_enumerated_list(self, node):
self.markers.pop()
if not self.markers:
self.append('\n')
def visit_list_item(self, node):
self.append(' ' * (len(self.markers) - 1))
self.append('{} '.format(self.markers[-1]))
# Some parts copied from: docutils.writers._html_base, docutils.writers.html4css1, sphinx.writers.html
# HTML
def html_tag(self, node, tagname=None, suffix='\n', empty=None, **attributes):
if node:
parts = [node.tagname]
atts = {k: v for k, v in node.attributes.items() if v}
else:
parts = [tagname]
atts = {}
atts.update(attributes)
if empty:
infix = ' /'
else:
infix = ''
for name, value in atts.items():
parts.append('{}="{}"'.format(name.lower(), value))
self.append('<{}{}>'.format(' '.join(parts), infix) + suffix)
def close_html_tag(self, node, tagname=None, suffix='\n'):
self.append('</{}>'.format(tagname or node.tagname) + suffix)
def write_colspecs(self):
if self.colspecs:
self.html_tag(None, 'colgroup')
width = sum(node['colwidth'] for node in self.colspecs)
for node in self.colspecs:
colwidth = int(node['colwidth'] * 100.0 / width + 0.5)
self.html_tag(None, 'col', empty=True, width='{}%'.format(colwidth))
self.colspecs = []
self.close_html_tag(None, 'colgroup')
# Tables
def visit_table(self, node):
self.table_row_index = 0
self.html_tag(node, border='1', CLASS='docutils')
def depart_table(self, node):
self.close_html_tag(node, suffix='\n\n')
def visit_colspec(self, node):
self.colspecs.append(node)
def visit_thead(self, node):
self.write_colspecs()
self.html_tag(node, valign='bottom')
def depart_thead(self, node):
self.close_html_tag(node)
def visit_tbody(self, node):
self.write_colspecs()
self.html_tag(node, valign='top')
def depart_tbody(self, node):
self.close_html_tag(node)
def visit_row(self, node):
self.table_row_index += 1
self.html_tag(None, 'tr', CLASS='row-odd' if self.table_row_index % 2 else 'row-even')
def depart_row(self, node):
self.close_html_tag(None, 'tr')
def visit_entry(self, node):
if isinstance(node.parent.parent, nodes.thead):
tagname = 'th'
atts = {'class': 'head'}
else:
tagname = 'td'
atts = {}
self.context.append(tagname)
self.html_tag(None, tagname, suffix='', **atts)
def depart_entry(self, node):
self.close_html_tag(None, self.context.pop())
language = 'es'
localedir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'standard', 'docs', 'locale')
path = sys.argv[1]
with open(path) as f:
text = f.read()
domain = re.match(r'standard/docs/en/(.+)\.md$', path).group(1)
confoverrides = {
'extensions': [
'sphinxcontrib.jsonschema',
'sphinxcontrib.opencontracting',
'sphinxcontrib.opendataservices',
]
}
extension_versions = OrderedDict([
('bids', 'v1.1.3'),
('enquiries', 'v1.1.3'),
('location', 'v1.1.3'),
('lots', 'v1.1.3'),
('milestone_documents', 'v1.1.3'),
('participation_fee', 'v1.1.3'),
('process_title', 'v1.1.3'),
])
# Create minimal settings.
settings = frontend.OptionParser(components=(rst.Parser,)).get_default_values()
app = application.Sphinx('.', None, '.', '.', 'html', confoverrides, None)
app.add_config_value('extension_versions', extension_versions, True)
app.add_config_value('recommonmark_config', {}, True)
settings.env = app.builder.env
settings.env.found_docs.add(domain)
settings.env.temp_data['docname'] = domain
document = new_document(path, settings)
CommonMarkParser().parse(text, document)
AutoStructify(document).apply()
visitor = Translator(document, domain=domain, localedir=localedir, language=language)
document.walkabout(visitor)
with open(path, 'w') as f:
f.write(visitor.astext())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment