def setup(app): # The root of the repository. basedir = Path(__file__).resolve().parents[1] # `LOCALE_DIR` from `config.mk`. localedir = basedir / 'docs' / 'locale' language = app.config.overrides.get('language', 'en') headers = ['Title', 'Description', 'Extension'] # The gettext domain for schema translations. Should match the domain in the `pybabel compile` command. schema_domain = f'{gettext_domain_prefix}schema' # The gettext domain for codelist translations. Should match the domain in the `pybabel compile` command. codelists_domain = f'{gettext_domain_prefix}codelists' patched_dir = basedir / 'schema' / 'patched' profile_dir = basedir / 'schema' / 'profile' patched_build_dir = basedir / 'docs' / '_static' / 'patched' profile_build_dir = basedir / 'build' / language translate([ # The glob patterns in `babel_ocds_schema.cfg` should match these filenames. (glob(str(patched_dir / '*-schema.json')), patched_build_dir, schema_domain), (glob(str(profile_dir / '*-schema.json')), profile_build_dir, schema_domain), # The glob patterns in `babel_ocds_codelist.cfg` should match these. (glob(str(patched_dir / 'codelists' / '*.csv')), patched_build_dir / 'codelists', codelists_domain), (glob(str(profile_dir / 'codelists' / '*.csv')), profile_build_dir / 'codelists', codelists_domain), ], localedir, language, headers, version=standard_version) # Copy the untranslated extension.json file. with (profile_dir / 'extension.json').open() as f: extension_json = f.read() with (profile_build_dir / 'extension.json').open('w') as f: f.write(extension_json)
def setup(app): # The root of the repository. basedir = Path(__file__).resolve().parents[1] # `LOCALE_DIR` from `config.mk`. localedir = basedir / 'docs' / 'locale' language = app.config.overrides.get('language', 'en') headers = ['Title', 'Description', 'Extension'] # The gettext domain for schema translations. Should match the domain in the `pybabel compile` command. schema_domain = f'{gettext_domain_prefix}schema' # The gettext domain for codelist translations. Should match the domain in the `pybabel compile` command. codelists_domain = f'{gettext_domain_prefix}codelists' standard_dir = basedir / 'schema' standard_build_dir = basedir / 'build' / language branch = os.getenv('GITHUB_REF', 'latest').rsplit('/', 1)[-1] translate( [ # The glob patterns in `babel_ocds_schema.cfg` should match these filenames. (glob(str(standard_dir / '*-schema.json')), standard_build_dir, schema_domain), # The glob patterns in `babel_ocds_codelist.cfg` should match these. (glob(str(standard_dir / 'codelists' / '*.csv')), standard_build_dir / 'codelists', codelists_domain), ], localedir, language, headers, version=branch)
def setup(app): app.add_config_value('extension_versions', extension_versions, True) app.add_config_value('recommonmark_config', {'enable_eval_rst': True}, True) app.add_transform(AutoStructify) app.add_transform(AutoStructifyLowPriority) # The root of the repository. basedir = Path(os.path.realpath(__file__)).parents[3] # The `LOCALE_DIR` from `config.mk`. localedir = basedir / 'standard' / 'docs' / 'locale' language = app.config.overrides.get('language', 'en') # The gettext domain for schema translations. Should match the domain in the `pybabel compile` command. schema_domain = '{}schema'.format(gettext_domain_prefix) # The gettext domain for codelist translations. Should match the domain in the `pybabel compile` command. codelists_domain = '{}codelists'.format(gettext_domain_prefix) standard_dir = basedir / 'standard' / 'schema' standard_build_dir = basedir / 'build' / language translate( [ # The glob patterns in `babel_ocds_schema.cfg` should match these filenames. (glob(str(standard_dir / '*-schema.json')), standard_build_dir, schema_domain), # The glob patterns in `babel_ocds_codelist.cfg` should match these. (glob(str(standard_dir / 'codelists' / '*.csv')), standard_build_dir / 'codelists', codelists_domain), ], localedir, language, version=os.environ.get('TRAVIS_BRANCH', 'latest'))
def test_translate_codelists(monkeypatch, caplog): class Translation(object): def __init__(self, *args, **kwargs): pass def gettext(self, *args, **kwargs): return { 'Code': 'Código', 'Title': 'Título', 'Description': 'Descripción', 'Open': 'Abierta', 'Selective': 'Selectiva', 'All interested suppliers may submit a tender.': 'Todos los proveedores interesados pueden enviar una propuesta.', # noqa: E501 'Only qualified suppliers are invited to submit a tender.': 'Sólo los proveedores calificados son invitados a enviar una propuesta.', # noqa: E501 }[args[0]] monkeypatch.setattr(gettext, 'translation', Translation) caplog.set_level(logging.INFO) with TemporaryDirectory() as sourcedir: with open(os.path.join(sourcedir, 'method.csv'), 'w') as f: f.write(codelist) with TemporaryDirectory() as builddir: translate([ (glob(os.path.join(sourcedir, '*.csv')), builddir, 'codelists'), ], '', 'es', headers) with open(os.path.join(builddir, 'method.csv')) as f: rows = [dict(row) for row in csv.DictReader(f)] assert rows == [{ 'Código': 'open', 'Descripción': 'Todos los proveedores interesados pueden enviar una propuesta.', 'Título': 'Abierta' }, { 'Código': 'selective', 'Descripción': 'Sólo los proveedores calificados son invitados a enviar una propuesta.', 'Título': 'Selectiva' }] assert len(caplog.records) == 1 assert caplog.records[0].levelname == 'INFO' assert caplog.records[ 0].message == f'Translating to es using "codelists" domain, into {builddir}'
def setup(app): app.add_config_value('extension_versions', extension_versions, True) app.add_config_value('recommonmark_config', { 'auto_toc_tree_section': 'Contents', 'enable_eval_rst': True }, True) app.add_transform(AutoStructify) # The root of the repository. basedir = Path(os.path.realpath(__file__)).parents[1] # The `LOCALE_DIR` from `config.mk`. localedir = basedir / 'locale' language = app.config.overrides.get('language', 'en') headers = ['Title', 'Description', 'Extension'] # The gettext domain for schema translations. Should match the domain in the `pybabel compile` command. schema_domain = '{}schema'.format(gettext_domain_prefix) # The gettext domain for codelist translations. Should match the domain in the `pybabel compile` command. codelists_domain = '{}codelists'.format(gettext_domain_prefix) patched_dir = basedir / 'schema' / 'patched' profile_dir = basedir / 'schema' / 'profile' patched_build_dir = basedir / 'docs' / '_static' / 'patched' profile_build_dir = basedir / 'build' / language translate( [ # The glob patterns in `babel_ocds_schema.cfg` should match these filenames. (glob(str(patched_dir / '*-schema.json')), patched_build_dir, schema_domain), (glob(str(profile_dir / '*-schema.json')), profile_build_dir, schema_domain), # The glob patterns in `babel_ocds_codelist.cfg` should match these. (glob(str(patched_dir / 'codelists' / '*.csv')), patched_build_dir / 'codelists', codelists_domain), (glob(str(profile_dir / 'codelists' / '*.csv')), profile_build_dir / 'codelists', codelists_domain), ], localedir, language, headers, version=standard_version) # Copy the untranslated extension.json file. with (profile_dir / 'extension.json').open() as f: extension_json = f.read() with (profile_build_dir / 'extension.json').open('w') as f: f.write(extension_json)
def test_translate_extension_metadata(monkeypatch, caplog): for metadata in (extension_metadata, extension_metadata_language_map): class Translation(object): def __init__(self, *args, **kwargs): pass def gettext(self, *args, **kwargs): return { 'Location': 'Ubicación', 'Communicates the location of proposed or executed contract delivery.': 'Comunica la ubicación de la entrega del contrato propuesto o ejecutado.', # noqa: E501 }[args[0]] monkeypatch.setattr(gettext, 'translation', Translation) caplog.set_level(logging.INFO) with TemporaryDirectory() as sourcedir: with open(os.path.join(sourcedir, 'extension.json'), 'w') as f: f.write(metadata) with TemporaryDirectory() as builddir: translate([ ([os.path.join(sourcedir, 'extension.json') ], builddir, 'schema'), ], '', 'es', headers) with open(os.path.join(builddir, 'extension.json')) as f: data = json.load(f) assert data == { "name": { "es": "Ubicación" }, "description": { "es": "Comunica la ubicación de la entrega del contrato propuesto o ejecutado." }, "compatibility": ["1.1"] } assert len(caplog.records) == 1 assert caplog.records[0].levelname == 'INFO' assert caplog.records[ 0].message == f'Translating to es using "schema" domain, into {builddir}' caplog.clear()
def translate_schema_and_codelists(language='en'): # The root of the repository. basedir = Path(os.path.realpath(__file__)).parents[1] build_dir = basedir / 'docs' / '_build_schema' localedir = basedir / 'docs' / 'locale' # The gettext domain for schema translations. Should match the domain in the `pybabel compile` command. schema_domain = 'schema' # The gettext domain for codelist translations. Should match the domain in the `pybabel compile` command. codelist_domain = 'codelist' schema_source_dir = basedir / 'schema' codelist_source_dir = basedir / 'schema' / 'codelists' schema_target_dir = build_dir codelist_target_dir = build_dir / 'codelists' def _json_dumps(data): return json.dumps( data, ensure_ascii=False, indent=2, separators=(',', ': ')) + '\n' # This is a slight monkey patch on translate to make sure translated schemas follow # same indentation as core schemas translate._json_dumps = _json_dumps translate.translate( [ # The glob patterns in `babel_bods_schema.cfg` should match these filenames. (glob(str(schema_source_dir / '*.json')), str(schema_target_dir), schema_domain), # The glob patterns in `babel_bods_codelist.cfg` should match these. (glob(str(codelist_source_dir / '*.csv')), str(codelist_target_dir), codelist_domain), ], str(localedir), language, version=os.environ.get('TRAVIS_BRANCH', 'latest'), headers='title,description,technical note') print("Translated schema and codelists to {}".format(language))
def load_extensions_info(self): """ Gets the core extensions from the extension registry. If the language is not 'en', produces the translated versions for each core extension. """ extensions_dir = Path('extensions') if extensions_dir.exists(): shutil.rmtree(str(extensions_dir)) extensions_dir.mkdir() # download extension files according to version registry = ExtensionRegistry(self.extension_versions_url, self.extensions_url) for version in registry.filter(core=True, version=self.version): if version.id not in self.exclusions: zip_file = version.zipfile() zip_file.extractall(path=str(extensions_dir)) # rename path to extension id path = extensions_dir / zip_file.infolist()[0].filename path.rename(extensions_dir / version.id) if self.lang is 'en': output_dir = extensions_dir else: # translate core extensions translation_sources_dir = Path('ocds-extensions-translations-master') if translation_sources_dir.exists(): shutil.rmtree(str(translation_sources_dir)) res = requests.get('https://github.com/open-contracting/ocds-extensions-translations/archive/master.zip') res.raise_for_status() content = BytesIO(res.content) with ZipFile(content) as zipfile: zipfile.extractall() output_dir = Path('translations') / self.lang if output_dir.exists(): shutil.rmtree(str(output_dir)) output_dir.mkdir(parents=True) locale = str(translation_sources_dir / 'locale') headers = ['Title', 'Description', 'Extension'] for dir in [x for x in extensions_dir.iterdir() if x.is_dir()]: translate([ (glob(str(dir / 'extension.json')), output_dir / dir.parts[-1], dir.parts[-1] + '/' + self.version + '/schema'), (glob(str(dir / 'release-schema.json')), output_dir / dir.parts[-1], dir.parts[-1] + '/' + self.version + '/schema') ], locale, self.lang, headers) self.extension_urls = [path.resolve(strict=True).as_uri() for path in output_dir.iterdir() if path.is_dir()] # get names and descriptions for each extension for dir in [x for x in output_dir.iterdir() if x.is_dir()]: path = dir.joinpath('extension.json') info = {} with path.open() as f: info = json.load(f) self.descriptions[info['name'][self.lang]] = info['description'][self.lang] # mapping-schema looks for the name of the name extension in English if self.lang is not 'en': info['name']['en'] = info['name'][self.lang] with path.open(mode='w') as f: json.dump(info, f) return self.extension_urls
def test_translate_markdown(monkeypatch, caplog): class Translation(object): def __init__(self, *args, **kwargs): pass def gettext(self, *args, **kwargs): return { 'Skip Heading': 'Entête à sauter', 'Heading 1': 'Titre 1', 'Heading 2': 'Titre 2', 'Heading **3**': 'Titre **3**', 'Paragraph text and ```literal text```': 'Texte de paragraphe et ```texte littéral```', '`Literal text`': '`Texte littéral`', 'Blockquote text': 'Texte de citation', '![Caption](http://example.com/example.png)': '![Légende](http://example.com/example-fr.png)', 'This is a [pending](examples/test.md) xref.': 'Ceci est un xref [en suspens](examples/test.md).', 'This is a **[bold link](http://example.com/test.md)**.': 'Ceci est un **[lien en gras](http://example.com/test.md)**.', # noqa: E501 'This is <em>inline HTML</em>.': 'Ceci est <em>HTML en ligne</em>.', 'Bulleted list item 1': 'Élément de liste à puces 1', 'Bulleted list item 2': 'Élément de liste à puces 2', 'Enumerated list item 1': 'Élément de liste énumérée 1', 'Enumerated list item 2': 'Élément de liste énumérée 2', '[Link list item 1](http://example.com/en/1.html)': '[Élément de liste de liens 1](http://example.com/fr/1.html)', # noqa: E501 '[Link list item 2](http://example.com/en/2.html)': '[Élément de liste de liens 2](http://example.com/fr/2.html)', # noqa: E501 '': '', }[args[0]] monkeypatch.setattr(gettext, 'translation', Translation) caplog.set_level(logging.INFO) with TemporaryDirectory() as sourcedir: with open(os.path.join(sourcedir, 'README.md'), 'w') as f: f.write(extension_readme) with TemporaryDirectory() as builddir: translate([ ([os.path.join(sourcedir, 'README.md')], builddir, 'docs'), ], '', 'fr', headers) with open(os.path.join(builddir, 'README.md')) as f: text = f.read() assert text == """##### Entête à sauter # Titre 1 ## Titre 2 ### Titre **3** Texte de paragraphe et `texte littéral` `Texte littéral` > Texte de citation ``` Raw paragraph text ``` ``` Literal block ``` ```json { "json": "block" } ``` <h3>Subheading</h3> ![Légende](http://example.com/example-fr.png) Ceci est un xref [en suspens](examples/test.md). Ceci est un **[lien en gras](http://example.com/test.md)**. Ceci est <em>HTML en ligne</em>. - Élément de liste à puces 1 - Élément de liste à puces 2 1. Élément de liste énumérée 1 1. Élément de liste énumérée 2 - [Élément de liste de liens 1](http://example.com/fr/1.html) - [Élément de liste de liens 2](http://example.com/fr/2.html) """ assert len(caplog.records) == 1 assert caplog.records[0].levelname == 'INFO' assert caplog.records[ 0].message == f'Translating to fr using "docs" domain, into {builddir}'
def test_translate_schema(monkeypatch, caplog): class Translation(object): def __init__(self, *args, **kwargs): pass def gettext(self, *args, **kwargs): return { 'Schema for an Open Contracting Record package {{version}} [{{lang}}]': 'Esquema para un paquete de Registros de Contrataciones Abiertas {{version}} [{{lang}}]', # noqa: E501 'The record package contains a list of records along with some publishing…': 'El paquete de registros contiene una lista de registros junto con algunos…', # noqa: E501 'Releases': 'Entregas', 'An array of linking identifiers or releases': 'Una matriz de enlaces a identificadores o entregas', 'Linked releases': 'Entregas vinculadas', 'A list of objects that identify the releases associated with this Open…': 'Una lista de objetos que identifican las entregas asociadas con este Open…', # noqa: E501 'Embedded releases': 'Entregas embebidas', 'A list of releases, with all the data. The releases MUST be sorted into date…': 'Una lista de entregas, con todos los datos. Las entregas DEBEN ordenarse…', # noqa: E501 }[args[0]] monkeypatch.setattr(gettext, 'translation', Translation) caplog.set_level(logging.INFO) with TemporaryDirectory() as sourcedir: with open(os.path.join(sourcedir, 'record-package-schema.json'), 'w') as f: f.write(schema) with open(os.path.join(sourcedir, 'untranslated.json'), 'w') as f: f.write(schema) with TemporaryDirectory() as builddir: translate([ ([os.path.join(sourcedir, 'record-package-schema.json') ], builddir, 'schema'), ], '', 'es', headers, version='1.1') with open(os.path.join(builddir, 'record-package-schema.json')) as f: data = json.load(f) assert not os.path.exists( os.path.join(builddir, 'untranslated.json')) assert data == { "title": "Esquema para un paquete de Registros de Contrataciones Abiertas 1.1 [es]", "description": "El paquete de registros contiene una lista de registros junto con algunos…", "definitions": { "record": { "properties": { "releases": { "title": "Entregas", "description": "Una matriz de enlaces a identificadores o entregas", "oneOf": [{ "title": "Entregas vinculadas", "description": "Una lista de objetos que identifican las entregas asociadas con este Open…" }, { "title": "Entregas embebidas", "description": "Una lista de entregas, con todos los datos. Las entregas DEBEN ordenarse…" }] } } } } } assert len(caplog.records) == 1 assert caplog.records[0].levelname == 'INFO' assert caplog.records[ 0].message == f'Translating to es using "schema" domain, into {builddir}'