Last active
July 13, 2018 20:54
-
-
Save jpmckinney/966d03bfe5390a93049b84fc5a721ebd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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