diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index c35513e7..2ba56bfa 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 0.3.64
+current_version = 0.3.65
[bumpversion:file:mdpo/__init__.py]
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6ae33b56..37445131 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,12 +9,16 @@ repos:
rev: v4.0.1
hooks:
- id: trailing-whitespace
+ name: trailing-whitespace
- id: end-of-file-fixer
+ name: end-of-file-fixer
- id: double-quote-string-fixer
+ name: double-quote-string-fixer
- repo: https://github.com/asottile/add-trailing-comma
rev: v2.1.0
hooks:
- id: add-trailing-comma
+ name: add-trailing-comma
args:
- --py36-plus
- repo: https://github.com/asottile/setup-cfg-fmt
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
new file mode 100644
index 00000000..4cf06a66
--- /dev/null
+++ b/.pre-commit-hooks.yaml
@@ -0,0 +1,5 @@
+- id: md2po2md
+ name: md2po2md
+ entry: md2po2md
+ description: Translates Markdown files using PO files for a set of predefined language codes creating multiple directories, one for each language
+ language: python
diff --git a/.yamllint b/.yamllint
index d7d828a4..63b008ad 100644
--- a/.yamllint
+++ b/.yamllint
@@ -29,13 +29,15 @@ rules:
key-ordering:
ignore: |
.pre-commit-config.yaml
+ .pre-commit-hooks.yaml
.github
.readthedocs.yml
test/test_po2md/.markdownlint-cli2.yaml
line-length:
allow-non-breakable-words: true
- ignore:
+ ignore: |
.pre-commit-config.yaml
+ .pre-commit-hooks.yaml
max: 96
new-lines:
type: unix
diff --git a/docs/api.rst b/docs/api.rst
index 48526468..9af04f84 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -16,6 +16,12 @@ po2md
.. automodule:: mdpo.po2md
:members: pofile_to_markdown
+md2po2md
+========
+
+.. automodule:: mdpo.md2po2md
+ :members: markdown_to_pofile_to_markdown
+
mdpo2html
=========
diff --git a/docs/cli.rst b/docs/cli.rst
index 022b174f..3c9428ea 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -60,6 +60,21 @@ The output produced by :ref:`po2md-cli` is compatible with the following
+.. _md2po2md-cli:
+
+md2po2md
+========
+
+.. sphinx_argparse_cli::
+ :module: mdpo.md2po2md.__main__
+ :func: build_parser
+ :prog: md2po2md
+ :title:
+
+.. raw:: html
+
+
+
.. _mdpo2html-cli:
mdpo2html
diff --git a/docs/devref/index.rst b/docs/devref/index.rst
index 76ff6086..1c219cce 100644
--- a/docs/devref/index.rst
+++ b/docs/devref/index.rst
@@ -17,3 +17,4 @@ mdpo/
mdpo/md2po/mdpo.md2po.index
mdpo/mdpo2html/mdpo.mdpo2html.index
mdpo/po2md/mdpo.po2md.index
+ mdpo/md2po2md/mdpo.md2po2md.index
diff --git a/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__init__.rst b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__init__.rst
new file mode 100644
index 00000000..7ebb5a4f
--- /dev/null
+++ b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__init__.rst
@@ -0,0 +1,6 @@
+***********
+__init__.py
+***********
+
+.. autofunction:: mdpo.md2po2md.markdown_to_pofile_to_markdown
+ :noindex:
diff --git a/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__main__.rst b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__main__.rst
new file mode 100644
index 00000000..550dd319
--- /dev/null
+++ b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.__main__.rst
@@ -0,0 +1,6 @@
+***********
+__main__.py
+***********
+
+.. automodule:: mdpo.md2po2md.__main__
+ :members:
diff --git a/docs/devref/mdpo/md2po2md/mdpo.md2po2md.index.rst b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.index.rst
new file mode 100644
index 00000000..a891f75d
--- /dev/null
+++ b/docs/devref/mdpo/md2po2md/mdpo.md2po2md.index.rst
@@ -0,0 +1,9 @@
+*********
+md2po2md/
+*********
+
+.. toctree::
+ :maxdepth: 2
+
+ mdpo.md2po2md.__init__
+ mdpo.md2po2md.__main__
diff --git a/docs/index.rst b/docs/index.rst
index 3c98b015..602f487d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -14,7 +14,6 @@ Markdown files translation using pofiles. Fully complies with
tutorial
commands
related-utilities
- useful-recipes
.. toctree::
:maxdepth: 2
@@ -22,6 +21,7 @@ Markdown files translation using pofiles. Fully complies with
cli
api
+ pc-hooks
changelog
.. toctree::
diff --git a/docs/pc-hooks.rst b/docs/pc-hooks.rst
new file mode 100644
index 00000000..8981a1be
--- /dev/null
+++ b/docs/pc-hooks.rst
@@ -0,0 +1,22 @@
+****************
+pre-commit hooks
+****************
+
+md2po2md
+========
+
+.. code-block:: yaml
+
+ - repo: https://github.com/mondeja/mdpo
+ rev: v0.3.65
+ hooks:
+ - id: md2po2md
+ args:
+ - prueba.md
+ - -l
+ - es
+ - -o
+ - locale/{lang}
+
+.. seealso::
+ * :ref:`md2po2md CLI`
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index e02fca9f..8910e87d 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -61,6 +61,39 @@ This will be the output after previous two commands:
+Simple README file translation
+==============================
+
+Just use :ref:`md2po2md CLI`:
+
+.. code-block:: bash
+
+ md2po2md README.md -l es -l fr -o "locale/{lang}"
+
+Define the languages to translate into using the ``-l`` option.
+
+You also can use the next snippet to include links for the translations:
+
+.. code-block:: html
+
+
+
+ > Read this document in other languages:
+ >
+ > - [Español][readme-es]
+ > - [Français][readme-fr]
+
+
+ [readme-es]: https://github.com/user/repo/blob/master/locale/es/README.md
+ [readme-fr]: https://github.com/user/repo/blob/master/locale/fr/README.md
+
+.. seealso::
+ * :ref:`md2po2md CLI`
+
+.. raw:: html
+
+
+
HTML-from-Markdown to HTML
==========================
diff --git a/docs/useful-recipes.rst b/docs/useful-recipes.rst
deleted file mode 100644
index 79666054..00000000
--- a/docs/useful-recipes.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-**************
-Useful recipes
-**************
-
-Simple README file translation
-==============================
-
-I think that this is the simplest way that I've found to translate the READMEs
-of my projects. Just create a folder ``locale/`` with language folders inside
-and execute the next line (posix shell):
-
-.. code-block:: bash
-
- langs=( es ); for l in "${langs[@]}"; do md2po -qs README.md -po locale/$l/README.po && po2md -q README.md -s locale/$l/README.md -p locale/$l/README.po; done
-
-Define the languages to translate into using the ``langs`` variable separating
-them by spaces.
-
-You also can use the next snippet to include links for the translations:
-
-.. code-block:: html
-
-
-
- > Read this document in other languages:
- >
- > - [Español][readme-es]
-
-
- [readme-es]: https://github.com/user/repo/blob/master/locale/es/README.md
diff --git a/mdpo/__init__.py b/mdpo/__init__.py
index 61a9f5ba..d5243071 100644
--- a/mdpo/__init__.py
+++ b/mdpo/__init__.py
@@ -5,7 +5,7 @@
from mdpo.po2md import pofile_to_markdown
-__version__ = '0.3.64'
+__version__ = '0.3.65'
__title__ = 'mdpo'
__description__ = ('Markdown file translation utilities using PO files')
__all__ = (
diff --git a/mdpo/cli.py b/mdpo/cli.py
index cb4c8f0c..6b7a7bfe 100644
--- a/mdpo/cli.py
+++ b/mdpo/cli.py
@@ -4,6 +4,7 @@
import sys
from mdpo import __version__
+from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS
from mdpo.text import parse_escaped_pairs
@@ -87,12 +88,13 @@ def parse_metadata_cli_arguments(metadata):
)
-def add_common_cli_first_arguments(parser):
- """Adds common mdpo arguments to an argument parser at the beginning.
+def add_common_cli_first_arguments(parser, quiet=True):
+ """Add common mdpo arguments to an argument parser at the beginning.
Args:
parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
extend.
+ quiet (bool): Include the argument ``-q/--quiet``.
"""
parser.add_argument(
'-h', '--help', action='help',
@@ -104,14 +106,15 @@ def add_common_cli_first_arguments(parser):
version=f'%(prog)s {__version__}',
help='Show program version number and exit.',
)
- parser.add_argument(
- '-q', '--quiet', action='store_true',
- help='Do not print output to STDOUT.',
- )
+ if quiet:
+ parser.add_argument(
+ '-q', '--quiet', action='store_true',
+ help='Do not print output to STDOUT.',
+ )
def add_common_cli_latest_arguments(parser):
- """Adds common mdpo arguments to an argument parser at the end.
+ """Add common mdpo arguments to an argument parser at the end.
Args:
parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
@@ -128,3 +131,53 @@ def add_common_cli_latest_arguments(parser):
' you can pass either \'--command-alias "mdpo-on:mdpo-enable"\''
' or \'--command-alias "mdpo-on:enable"\' arguments.',
)
+
+
+def add_extensions_argument(parser):
+ """Add the ``-x/--extension`` argument to an argument parser.
+
+ Args:
+ parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
+ extend.
+ """
+ parser.add_argument(
+ '-x', '--extension', '--ext', dest='extensions', action='append',
+ default=None,
+ help='md4c extension used to parse markdown content formatted as'
+ ' pymd4c extension keyword arguments. This argument can be passed'
+ ' multiple times. If is not passed, next extensions are used:'
+ f' {", ".join(DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS)}.'
+ ' You can see all available at'
+ ' https://github.com/dominickpastore/pymd4c#parser-option-flags',
+ metavar='EXTENSION',
+ )
+
+
+def add_debug_option(parser):
+ """Add the ``-D/--debug`` option to an argument parser.
+
+ Args:
+ parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
+ extend.
+ """
+ parser.add_argument(
+ '-D', '--debug', dest='debug', action='store_true',
+ help='Print useful messages in the parsing process showing the'
+ ' contents of all Markdown elements.',
+ )
+
+
+def add_nolocation_option(parser):
+ """Add the ``--no-location/--nolocation`` option to an argument parser.
+
+ Args:
+ parser (:py:class:`argparse.ArgumentParser`): Arguments parser to
+ extend.
+ """
+ parser.add_argument(
+ '--no-location', '--nolocation', dest='location', action='store_false',
+ help="Do not write '#: filename:line' lines. Note that using this"
+ ' option makes it harder for technically skilled translators to'
+ " understand each message's context. Same as 'xgettext "
+ "--no-location'.",
+ )
diff --git a/mdpo/md2po/__main__.py b/mdpo/md2po/__main__.py
index 3015dcc3..55d9d3e0 100755
--- a/mdpo/md2po/__main__.py
+++ b/mdpo/md2po/__main__.py
@@ -9,6 +9,9 @@
from mdpo.cli import (
add_common_cli_first_arguments,
add_common_cli_latest_arguments,
+ add_debug_option,
+ add_extensions_argument,
+ add_nolocation_option,
parse_command_aliases_cli_arguments,
parse_metadata_cli_arguments,
)
@@ -91,24 +94,8 @@ def build_parser():
' passed as \'-po/--po-filepath\' parameter will be removed.'
' Only has effect used in combination with \'--merge-pofiles\'.',
)
- parser.add_argument(
- '--no-location', '--nolocation', dest='location', action='store_false',
- help="Do not write '#: filename:line' lines. Note that using this"
- ' option makes it harder for technically skilled translators to'
- " understand each message's context. Same as 'xgettext "
- "--no-location'.",
- )
- parser.add_argument(
- '-x', '--extension', '--ext', dest='extensions', action='append',
- default=None,
- help='md4c extension used to parse markdown content formatted as'
- ' pymd4c extension keyword arguments. This argument can be passed'
- ' multiple times. If is not passed, next extensions are used:'
- f' {", ".join(DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS)}.'
- ' You can see all available at'
- ' https://github.com/dominickpastore/pymd4c#parser-option-flags',
- metavar='',
- )
+ add_nolocation_option(parser)
+ add_extensions_argument(parser)
parser.add_argument(
'--po-encoding', dest='po_encoding', default=None,
help='Resulting PO file encoding.', metavar='',
@@ -147,12 +134,8 @@ def build_parser():
' \'-d "Content-Type: text/plain; charset=utf-8"'
' -d "Language: es"\'.',
)
- parser.add_argument(
- '-D', '--debug', dest='debug', action='store_true',
- help='Print useful messages in the parsing process showing the'
- ' contents of all Markdown elements.',
- )
add_common_cli_latest_arguments(parser)
+ add_debug_option(parser)
return parser
@@ -213,10 +196,9 @@ def run(args=[]):
ignore_msgids=opts.ignore_msgids,
command_aliases=opts.command_aliases,
metadata=opts.metadata,
+ wrapwidth=opts.wrapwidth,
debug=opts.debug,
)
- if isinstance(opts.wrapwidth, int):
- kwargs['wrapwidth'] = opts.wrapwidth
pofile = markdown_to_pofile(opts.glob_or_content, **kwargs)
diff --git a/mdpo/md2po2md/__init__.py b/mdpo/md2po2md/__init__.py
new file mode 100644
index 00000000..97ac14d4
--- /dev/null
+++ b/mdpo/md2po2md/__init__.py
@@ -0,0 +1,137 @@
+"""Markdown to PO file to Markdown translator."""
+
+import glob
+import os
+import re
+
+from mdpo.md2po import markdown_to_pofile
+from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS
+from mdpo.po2md import pofile_to_markdown
+
+
+def markdown_to_pofile_to_markdown(
+ langs,
+ input_paths_glob,
+ output_paths_schema,
+ extensions=DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS,
+ command_aliases={},
+ location=True,
+ debug=False,
+ md2po_kwargs={},
+ po2md_kwargs={},
+):
+ """Translate a set of Markdown files using PO files.
+
+ Args:
+ langs (list): List of languages used to build the output directories.
+ input_paths_glob (str): Glob covering Markdown files to translate.
+ output_paths_schema (str): Path schema for outputs, built using
+ placeholders. There is a mandatory placeholder for languages:
+ ``{lang}``; and one optional for output basename: ``{basename}``.
+ For example, for the schema ``locale/{lang}``, the languages
+ ``['es', 'fr']`` and a ``README.md`` as input, the next files will
+ be written:
+
+ * ``locale/es/README.po``
+ * ``locale/es/README.md``
+ * ``locale/fr/README.po``
+ * ``locale/fr/README.md``
+
+ Note that you can omit ``{basename}``, specifying a directory for
+ each language with ``locale/{lang}`` for this example.
+ Unexistent directories and files will be created, so you don't
+ have to prepare the output directories before the execution.
+ extensions (list): md4c extensions used to parse markdown content,
+ formatted as a list of 'pymd4c' keyword arguments. You can see all
+ available at `pymd4c repository `_.
+ command_aliases (dict): Mapping of aliases to use custom mdpo command
+ names in comments. The ``mdpo-`` prefix in command names resolution
+ is optional. For example, if you want to use ````
+ instead of ````, you can pass the dictionaries
+ ``{"mdpo-on": "mdpo-enable"}`` or ``{"mdpo-on": "enable"}`` to this
+ parameter.
+ location (bool): Store references of top-level blocks in which are
+ found the messages in PO file `#: reference` comments.
+ debug (bool): Add events displaying all parsed elements in the
+ extraction process.
+ md2po_kwargs (dict): Additional optional arguments passed to
+ ``markdown_to_pofile`` function.
+ po2md_kwargs (dict): Additional optional arguments passed to
+ ``pofile_to_markdown`` function.
+ """
+ if '{lang}' not in output_paths_schema:
+ raise ValueError(
+ "You must pass the replacer '{lang}' inside the argument"
+ " 'output_paths_schema'.",
+ )
+
+ try:
+ input_paths_glob_ = glob.glob(input_paths_glob)
+ except re.error:
+ # some strings like '[s-m]' will produce
+ # 're.error: bad character range ... at position'
+ raise ValueError(
+ "The argument 'input_paths_glob' must be a valid glob or file"
+ ' path.',
+ )
+ else:
+ if not input_paths_glob_:
+ raise FileNotFoundError(
+ f'The glob \'{input_paths_glob}\' does not match any file.',
+ )
+
+ for filepath in input_paths_glob_:
+ for lang in langs:
+ md_ext = os.path.splitext(filepath)[-1]
+
+ file_basename = os.path.splitext(os.path.basename(filepath))[0]
+
+ format_kwargs = {'lang': lang}
+ if '{basename}' in output_paths_schema:
+ format_kwargs['basename'] = file_basename
+ po_filepath = output_paths_schema.format(**format_kwargs)
+
+ po_basename = os.path.basename(po_filepath)
+ po_dirpath = (
+ os.path.dirname(po_filepath)
+ if (po_basename.count('.') or file_basename == po_basename)
+ else po_filepath
+ )
+
+ os.makedirs(os.path.abspath(po_dirpath), exist_ok=True)
+ if os.path.isdir(po_filepath):
+ po_filepath = (
+ po_filepath.rstrip(os.sep) + os.sep +
+ os.path.basename(filepath) + '.po'
+ )
+ if not po_filepath.endswith('.po'):
+ po_filepath += '.po'
+
+ format_kwargs['ext'] = md_ext.lstrip('.')
+ md_filepath = output_paths_schema.format(**format_kwargs)
+ if os.path.isdir(md_filepath):
+ md_filepath = (
+ md_filepath.rstrip(os.sep) + os.sep +
+ os.path.basename(filepath)
+ )
+
+ markdown_to_pofile(
+ filepath,
+ save=True,
+ po_filepath=po_filepath,
+ extensions=extensions,
+ command_aliases=command_aliases,
+ debug=debug,
+ location=location,
+ **md2po_kwargs,
+ )
+
+ pofile_to_markdown(
+ filepath,
+ [po_filepath],
+ save=md_filepath,
+ command_aliases=command_aliases,
+ debug=debug,
+ **po2md_kwargs,
+ )
diff --git a/mdpo/md2po2md/__main__.py b/mdpo/md2po2md/__main__.py
new file mode 100644
index 00000000..8424be06
--- /dev/null
+++ b/mdpo/md2po2md/__main__.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+"""md2po2md command line interface."""
+
+import argparse
+import os
+import sys
+
+from mdpo.cli import (
+ add_common_cli_first_arguments,
+ add_common_cli_latest_arguments,
+ add_debug_option,
+ add_extensions_argument,
+ add_nolocation_option,
+ parse_command_aliases_cli_arguments,
+)
+from mdpo.md2po2md import markdown_to_pofile_to_markdown
+from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS
+
+
+DESCRIPTION = (
+ 'Translates Markdown files using PO files for a set of predefined language'
+ ' codes creating multiple directories, one for each language.'
+)
+
+
+def build_parser():
+ parser = argparse.ArgumentParser(description=DESCRIPTION, add_help=False)
+ add_common_cli_first_arguments(parser, quiet=False)
+ parser.add_argument(
+ 'input_paths_glob', metavar='GLOB', nargs='*',
+ help='Glob to markdown input files to translate.'
+ ' If not provided, will be read from STDIN.',
+ )
+ parser.add_argument(
+ '-l', '--lang', dest='langs', default=[], action='append',
+ help='Language codes used to create the output directories.'
+ ' Can be passed multiple times.',
+ metavar='LANG',
+ )
+ parser.add_argument(
+ '-o', '--output', dest='output_paths_schema',
+ required=True, type=str,
+ help='Path schema for outputs, built using placeholders. There is a'
+ ' mandatory placeholder for languages: \'{lang}\'; and one'
+ ' optional for output basename: \'{basename}\'. For example,'
+ ' for the schema \'locale/{lang}\', the languages \'es\' and'
+ ' \'fr\' and a \'README.md\' as input, the next files will be'
+ ' written: \'locale/es/README.po\', \'locale/es/README.md\','
+ ' \'locale/fr/README.po\' and \'locale/fr/README.md\'.'
+ ' Note that you can omit \'{basename}\', specifying a'
+ ' directory for each language with \'locale/{lang}\' for this'
+ ' example. Unexistent directories and files will be created, '
+ ' so you don\'t have to prepare the output directories before'
+ ' the execution.',
+ metavar='PATH_SCHEMA',
+ )
+ add_nolocation_option(parser)
+ add_extensions_argument(parser)
+ add_common_cli_latest_arguments(parser)
+ add_debug_option(parser)
+ return parser
+
+
+def parse_options(args=[]):
+ parser = build_parser()
+ if '-h' in args or '--help' in args:
+ parser.print_help()
+ sys.exit(1)
+ opts, unknown = parser.parse_known_args(args)
+
+ input_paths_glob = ''
+ if not sys.stdin.isatty():
+ input_paths_glob += sys.stdin.read().strip('\n')
+ if isinstance(opts.input_paths_glob, list) and opts.input_paths_glob:
+ input_paths_glob += opts.input_paths_glob[0]
+ opts.input_paths_glob = input_paths_glob
+
+ if opts.extensions is None:
+ opts.extensions = DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS
+
+ opts.command_aliases = parse_command_aliases_cli_arguments(
+ opts.command_aliases,
+ )
+
+ return opts
+
+
+def run(args=[]):
+ prev_mdpo_running = os.environ.get('_MDPO_RUNNING')
+ os.environ['_MDPO_RUNNING'] = 'true'
+
+ try:
+ opts = parse_options(args)
+
+ kwargs = dict(
+ extensions=opts.extensions,
+ command_aliases=opts.command_aliases,
+ debug=opts.debug,
+ location=opts.location,
+ )
+
+ markdown_to_pofile_to_markdown(
+ opts.langs,
+ opts.input_paths_glob,
+ opts.output_paths_schema,
+ **kwargs,
+ )
+ finally:
+ if prev_mdpo_running is None:
+ del os.environ['_MDPO_RUNNING']
+ else:
+ os.environ['_MDPO_RUNNING'] = prev_mdpo_running
+ return 0
+
+
+def main():
+ sys.exit(run(args=sys.argv[1:])) # pragma: no cover
+
+
+if __name__ == '__main__':
+ main()
diff --git a/mdpo/po2md/__main__.py b/mdpo/po2md/__main__.py
index 9f79cb77..796a898b 100644
--- a/mdpo/po2md/__main__.py
+++ b/mdpo/po2md/__main__.py
@@ -10,6 +10,7 @@
from mdpo.cli import (
add_common_cli_first_arguments,
add_common_cli_latest_arguments,
+ add_debug_option,
parse_command_aliases_cli_arguments,
)
from mdpo.po2md import pofile_to_markdown
@@ -72,12 +73,8 @@ def build_parser():
' charset=\\n\'.',
metavar='',
)
- parser.add_argument(
- '-D', '--debug', dest='debug', action='store_true',
- help='Print useful messages in the parsing process showing the'
- ' contents of all Markdown elements.',
- )
add_common_cli_latest_arguments(parser)
+ add_debug_option(parser)
return parser
diff --git a/setup.cfg b/setup.cfg
index a982b37b..eaf9b840 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = mdpo
-version = 0.3.64
+version = 0.3.65
description = Markdown files translation using PO files.
long_description = file: README.md
long_description_content_type = text/markdown
@@ -34,6 +34,7 @@ project_urls =
packages =
mdpo
mdpo.md2po
+ mdpo.md2po2md
mdpo.mdpo2html
mdpo.po2md
install_requires =
@@ -46,6 +47,7 @@ include_package_data = True
console_scripts =
md2po = mdpo.md2po.__main__:main
po2md = mdpo.po2md.__main__:main
+ md2po2md = mdpo.md2po2md.__main__:main
mdpo2html = mdpo.mdpo2html.__main__:main
[options.extras_require]
@@ -95,6 +97,7 @@ per-file-ignores =
mdpo/md2po/__main__.py: D103
mdpo/po2md/__init__.py: D101, D102, D103, D107
mdpo/po2md/__main__.py: D103
+ mdpo/md2po2md/__main__.py: D103
mdpo/mdpo2html/__init__.py: D101, D102, D103, D107, D205, D415
mdpo/mdpo2html/__main__.py: D103
docstring-convention = google
diff --git a/test/test_md2po2md/test_md2po2md_cli.py b/test/test_md2po2md/test_md2po2md_cli.py
new file mode 100644
index 00000000..e19f6eff
--- /dev/null
+++ b/test/test_md2po2md/test_md2po2md_cli.py
@@ -0,0 +1,179 @@
+"""Tests for ``md2po2md`` CLI"""
+
+import os
+import shutil
+import tempfile
+from uuid import uuid4
+
+import pytest
+
+from mdpo.md2po2md.__main__ import run
+
+
+@pytest.mark.parametrize('output_arg', ('-o', '--output'))
+@pytest.mark.parametrize('langs_arg', ('-l', '--lang'))
+@pytest.mark.parametrize(
+ (
+ 'langs',
+ 'input_paths_glob',
+ 'output',
+ 'input_files_content',
+ 'expected_files_content',
+ ),
+ (
+ pytest.param(
+ ['es'],
+ 'README.md',
+ 'locale/{lang}',
+ {'README.md': 'Foo\n\nBar\n'},
+ {
+ 'locale/es/README.md.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'locale/es/README.md': 'Foo\n\nBar\n',
+ },
+ id='es-locale/{lang}',
+ ),
+ pytest.param(
+ ['es', 'fr'],
+ 'README.md',
+ 'locale/{lang}',
+ {'README.md': 'Foo\n\nBar\n'},
+ {
+ 'locale/es/README.md.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'locale/es/README.md': 'Foo\n\nBar\n',
+ 'locale/fr/README.md.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'locale/fr/README.md': 'Foo\n\nBar\n',
+ },
+ id='README-es,fr-locale/{lang}',
+ ),
+ pytest.param(
+ ['es'],
+ 'README.md',
+ 'locale/',
+ {},
+ ValueError,
+ id='es-locale/-ValueError',
+ ),
+ pytest.param(
+ ['es'],
+ 'README.md',
+ 'locale/{lang}/{basename}',
+ {'README.md': 'Foo\n\nBar\n'},
+ {
+ 'locale/es/README.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'locale/es/README': 'Foo\n\nBar\n',
+ },
+ id='es-locale/{lang}/{basename}',
+ ),
+ pytest.param(
+ ['es'],
+ 'README.md',
+ '{lang}/{basename}',
+ {'README.md': 'Foo\n\nBar\n'},
+ {
+ 'es/README.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'es/README': 'Foo\n\nBar\n',
+ },
+ id='es-{lang}/{basename}',
+ ),
+ pytest.param(
+ ['es'],
+ 'README.md',
+ '{lang}',
+ {'README.md': 'Foo\n\nBar\n'},
+ {
+ 'es/README.md.po': (
+ '#\nmsgid ""\nmsgstr ""\n\nmsgid "Foo"\nmsgstr ""\n\n'
+ 'msgid "Bar"\nmsgstr ""\n'
+ ),
+ 'es/README.md': 'Foo\n\nBar\n',
+ },
+ id='es-{lang}',
+ ),
+ pytest.param(
+ ['es'],
+ '[s-m]',
+ '{lang}/{basename}',
+ {},
+ ValueError,
+ id='invalid-glob-ValueError',
+ ),
+ pytest.param(
+ ['es'],
+ 'foobar*',
+ '{lang}/{basename}',
+ {},
+ FileNotFoundError,
+ id='no-files-matching-input-glob',
+ ),
+ ),
+)
+def test_md2po2md_arguments(
+ output_arg,
+ langs_arg,
+ langs,
+ input_paths_glob,
+ output,
+ input_files_content,
+ expected_files_content,
+):
+ # create base directory and files
+ basedir = os.path.join(tempfile.gettempdir(), uuid4().hex[:8])
+ if os.path.isdir(basedir):
+ shutil.rmtree(basedir)
+ os.mkdir(basedir)
+
+ for relpath, content in input_files_content.items():
+ filepath = os.path.join(basedir, relpath)
+ with open(filepath, 'w') as f:
+ f.write(content)
+
+ def cleanup():
+ if os.path.isdir(basedir):
+ shutil.rmtree(basedir)
+
+ # run md2po2md
+ cmd = [
+ os.path.join(basedir, input_paths_glob),
+ output_arg, os.path.join(basedir, output),
+ ]
+ for lang in langs:
+ cmd.extend([langs_arg, lang])
+ cmd.append('--no-location')
+
+ if hasattr(expected_files_content, '__traceback__'):
+ with pytest.raises(expected_files_content):
+ run(cmd)
+ return cleanup()
+ run(cmd)
+
+ # Check number of files
+ expected_number_of_files = (
+ len(expected_files_content.keys()) + len(input_files_content.keys())
+ )
+ n_files = 0
+ for root, dirs, files in os.walk(basedir, topdown=False):
+ n_files += len(files)
+ assert n_files == expected_number_of_files
+
+ # Check expected content
+ for relpath, content in expected_files_content.items():
+ filepath = os.path.join(basedir, relpath)
+ with open(filepath) as f:
+ assert f.read() == content
+
+ cleanup()