From b3dbfe735ce3283bb0ddf9af8f3002ae7ed619fb Mon Sep 17 00:00:00 2001 From: PhuNH <1079742+PhuNH@users.noreply.github.com> Date: Sat, 2 Sep 2023 20:20:59 +0200 Subject: [PATCH] Split the library into another package, keep this as a program only --- README.md | 28 +- markdown_gettext/cli.py | 72 +++- .../e_domain.py => domain_extraction.py} | 79 ++-- .../g_domain.py => domain_generation.py} | 21 +- markdown_gettext/extraction/__init__.py | 4 - markdown_gettext/extraction/e_utils.py | 41 -- markdown_gettext/extraction/index.py | 28 -- markdown_gettext/extraction/renderer_i18n.py | 120 ------ markdown_gettext/generation/__init__.py | 4 - markdown_gettext/generation/g_utils.py | 36 -- markdown_gettext/generation/index.py | 54 --- markdown_gettext/generation/renderer_l10n.py | 382 ------------------ markdown_gettext/utils.py | 7 - poetry.lock | 18 +- pyproject.toml | 6 +- tests/__init__.py | 0 tests/resources/__init__.py | 0 tests/resources/blockquotes.md | 91 ----- tests/resources/definition_list.md | 39 -- tests/resources/lists.md | 80 ---- tests/resources/renderer.md | 58 --- tests/test_renderer_i18n.py | 33 -- tests/test_renderer_l10n.py | 50 --- 23 files changed, 152 insertions(+), 1099 deletions(-) rename markdown_gettext/{extraction/e_domain.py => domain_extraction.py} (55%) rename markdown_gettext/{generation/g_domain.py => domain_generation.py} (83%) delete mode 100644 markdown_gettext/extraction/__init__.py delete mode 100644 markdown_gettext/extraction/e_utils.py delete mode 100644 markdown_gettext/extraction/index.py delete mode 100644 markdown_gettext/extraction/renderer_i18n.py delete mode 100644 markdown_gettext/generation/__init__.py delete mode 100644 markdown_gettext/generation/g_utils.py delete mode 100644 markdown_gettext/generation/index.py delete mode 100644 markdown_gettext/generation/renderer_l10n.py delete mode 100644 markdown_gettext/utils.py delete mode 100644 tests/__init__.py delete mode 100644 tests/resources/__init__.py delete mode 100644 tests/resources/blockquotes.md delete mode 100644 tests/resources/definition_list.md delete mode 100644 tests/resources/lists.md delete mode 100644 tests/resources/renderer.md delete mode 100644 tests/test_renderer_i18n.py delete mode 100644 tests/test_renderer_l10n.py diff --git a/README.md b/README.md index 61c5d7d..1b58bc0 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,10 @@ SPDX-License-Identifier: CC-BY-SA-4.0 # markdown-gettext -Markdown i18n with gettext. +A command line program to do i18n and l10n for individual Markdown files. CommonMark compliant. All core Markdown elements are supported, as well as -front matter, table, and definition list. - -The package can be used as a command line program to do i18n and l10n for -individual Markdown files, and as a library to make larger programs. +YAML front matter, table, and definition list. ## Install @@ -19,8 +16,7 @@ individual Markdown files, and as a library to make larger programs. pip install markdown-gettext ``` -## Usage as a command line program -_("Usage as a library" in Development section)_ +## Usage You can use either `md-gettext` or `markdown-gettext` command @@ -60,11 +56,9 @@ Some notes about how different elements are handled: - Inlines: newlines and consecutive spaces are not kept; - Content of each HTML block isn't parsed into finer tokens but processed as a whole; -- Fenced code blocks: only `//` comments are processed; - -## Development +- Fenced code blocks: only `//` single comments are processed; -### Environment +## Development environment - With Conda @@ -73,15 +67,3 @@ conda env create -f environment.yml conda activate mg poetry install ``` - -### Usage as a library - -#### Extraction -- Subclass `DomainExtractionProtocol`, implement -`DomainExtractionProtocol.render_front_matter` -- Subclass `RendererMarkdownI18N` - -#### Generation -- Subclass `DomainGenerationProtocol`, implement -`DomainGenerationProtocol.render_front_matter` -- Subclass `RendererMarkdownL10N` \ No newline at end of file diff --git a/markdown_gettext/cli.py b/markdown_gettext/cli.py index e3ce686..e87c4ac 100644 --- a/markdown_gettext/cli.py +++ b/markdown_gettext/cli.py @@ -1,10 +1,21 @@ # SPDX-FileCopyrightText: 2023 Phu Hung Nguyen # SPDX-License-Identifier: LGPL-2.1-or-later +import os +import shutil +import subprocess from argparse import ArgumentParser, RawTextHelpFormatter +from typing import Tuple -from .extraction import extract -from .generation import generate +from markdown_it import MarkdownIt +from mdit_py_i18n.renderer_i18n import RendererMarkdownI18N +from mdit_py_i18n.renderer_l10n import RendererMarkdownL10N +from mdit_py_i18n.utils import L10NResult +from mdit_py_plugins.deflist import deflist_plugin +from mdit_py_plugins.front_matter import front_matter_plugin + +from .domain_extraction import DomainExtraction +from .domain_generation import DomainGeneration, gettext_func def main(): @@ -32,3 +43,60 @@ def main(): args = parser.parse_args() args.func(args) + + +def extract(args): + md_path = args.md + mdi = MarkdownIt(renderer_cls=RendererMarkdownI18N).use(front_matter_plugin)\ + .enable('table').use(deflist_plugin) + domain_e = DomainExtraction() + with open(md_path) as f_md: + env = { + 'path': md_path, + 'domain_extraction': domain_e + } + mdi.render(f_md.read(), env) + package = args.package or md_path + target = args.pot + os.makedirs(os.path.dirname(os.path.abspath(target)), exist_ok=True) + domain_e.make_pot(package, args.report_addr, args.team_addr, target) + + +def compile_po(lang: str, po_path: str): + target_dir = f'locale/{lang}/LC_MESSAGES' + os.makedirs(target_dir, exist_ok=True) + po_basename = os.path.basename(po_path) + mo_path = f'{target_dir}/{po_basename[:-2]}mo' + command = f'msgfmt {po_path} -o {mo_path}' + subprocess.run(command, shell=True, check=True) + + +def generate(args): + lang = args.lang + po_path = args.po + + compile_po(lang, po_path) + os.environ['LANGUAGE'] = lang + domain_name = os.path.splitext(os.path.basename(po_path))[0] + l10n_func = gettext_func(domain_name) + + mdi = MarkdownIt(renderer_cls=RendererMarkdownL10N).use(front_matter_plugin)\ + .enable('table').use(deflist_plugin) + domain_g = DomainGeneration(l10n_func) + + def render_content_file(src_path: str) -> Tuple[L10NResult, L10NResult]: + with open(src_path) as f_content: + env = { + 'domain_generation': domain_g + } + return mdi.render(f_content.read(), env) + + def write_content_file(fm: str, content: str, target_path: str): + os.makedirs(os.path.dirname(os.path.abspath(target_path)), exist_ok=True) + with open(target_path, 'w+') as f_target: + f_target.write(fm) + f_target.write(content) + + fm_result, content_result = render_content_file(args.in_md) + write_content_file(fm_result.localized, content_result.localized, args.out_md) + shutil.rmtree('locale') diff --git a/markdown_gettext/extraction/e_domain.py b/markdown_gettext/domain_extraction.py similarity index 55% rename from markdown_gettext/extraction/e_domain.py rename to markdown_gettext/domain_extraction.py index 54a969d..98e0edd 100644 --- a/markdown_gettext/extraction/e_domain.py +++ b/markdown_gettext/domain_extraction.py @@ -1,47 +1,45 @@ # SPDX-FileCopyrightText: 2023 Phu Hung Nguyen # SPDX-License-Identifier: LGPL-2.1-or-later +from dataclasses import dataclass, field from datetime import datetime -from typing import Any, Protocol, List +from typing import List, Tuple import polib import yaml +from mdit_py_i18n import utils -from .e_utils import I18NEntry, Occurrence -from .. import utils +@dataclass +class I18NEntry: + msgid: str + occurrences: List[Tuple[str, int]] = field(default_factory=list) + comment: str = '' + msgctxt: str = '' -class DomainExtractionProtocol(Protocol): - entries: List[I18NEntry] + def to_poentry(self) -> polib.POEntry: + return polib.POEntry(msgid=self.msgid, + msgstr='', + occurrences=self.occurrences, + comment=self.comment, + msgctxt=self.msgctxt if self.msgctxt != '' else None) - def add_entry(self, src_path: str, msgid: str, line_num: int, comment: str = ''): - occ = Occurrence(src_path, line_num) - self.entries.append(I18NEntry(msgid, occ, comment)) - def make_pot(self, package: str, report_address: str, team_address: str, dest_path: str): - pot = polib.POFile(check_for_duplicates=True) - pot.metadata = { - 'Project-Id-Version': f'{package} 1.0', - 'Report-Msgid-Bugs-To': report_address, - 'POT-Creation-Date': datetime.now().astimezone().strftime('%Y-%m-%d %H:%M%z'), - 'PO-Revision-Date': 'YEAR-MO-DA HO:MI+ZONE', - 'Last-Translator': 'FULL NAME ', - 'Language-Team': f'LANGUAGE <{team_address}>', - 'MIME-Version': '1.0', - 'Content-Type': 'text/plain; charset=utf-8', - 'Content-Transfer-Encoding': '8bit', - } - for e in self.entries: - e.add_to_pot(pot) - pot.save(dest_path) - - def render_front_matter(self, path: str, content: str, markup: str) -> Any: - ... - - -class DomainExtraction(DomainExtractionProtocol): +class DomainExtraction: + """ + Implements `DomainExtractionProtocol` + """ def __init__(self): - self.entries = [] + self.entries: List[I18NEntry] = [] + + def add_entry(self, path: str, msgid: str, line_num: int, comment: str = '', msgctxt: str = ''): + if not msgid: + return + for e in self.entries: + if msgid == e.msgid and msgctxt == e.msgctxt: + e.occurrences.append((path, line_num)) + return + self.entries.append(I18NEntry(msgid, [(path, line_num)], comment, msgctxt)) def i12ize_front_matter(self, o, path: str): """Internationalize an object in front matter. @@ -55,6 +53,23 @@ def i12ize_front_matter(self, o, path: str): for _, value in (enumerate(o) if isinstance(o, list) else o.items()): self.i12ize_front_matter(value, path) - def render_front_matter(self, path: str, content: str, markup: str): + def render_front_matter(self, path: str, content: str, _markup: str): fm = yaml.safe_load(content) self.i12ize_front_matter(fm, path) + + def make_pot(self, package: str, report_address: str, team_address: str, dest_path: str): + pot = polib.POFile() + pot.metadata = { + 'Project-Id-Version': f'{package} 1.0', + 'Report-Msgid-Bugs-To': report_address, + 'POT-Creation-Date': datetime.now().astimezone().strftime('%Y-%m-%d %H:%M%z'), + 'PO-Revision-Date': 'YEAR-MO-DA HO:MI+ZONE', + 'Last-Translator': 'FULL NAME ', + 'Language-Team': f'LANGUAGE <{team_address}>', + 'MIME-Version': '1.0', + 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Transfer-Encoding': '8bit', + } + for e in self.entries: + pot.append(e.to_poentry()) + pot.save(dest_path) diff --git a/markdown_gettext/generation/g_domain.py b/markdown_gettext/domain_generation.py similarity index 83% rename from markdown_gettext/generation/g_domain.py rename to markdown_gettext/domain_generation.py index d6e2695..1fda525 100644 --- a/markdown_gettext/generation/g_domain.py +++ b/markdown_gettext/domain_generation.py @@ -1,22 +1,23 @@ # SPDX-FileCopyrightText: 2023 Phu Hung Nguyen # SPDX-License-Identifier: LGPL-2.1-or-later -from typing import Any, Protocol +import gettext import yaml +from mdit_py_i18n import utils +from mdit_py_i18n.utils import L10NFunc, L10NResult -from .g_utils import L10NFunc, L10NResult -from .. import utils +def gettext_func(domain_name): + gettext.bindtextdomain(domain_name, 'locale') + gettext.textdomain(domain_name) + return gettext.gettext -class DomainGenerationProtocol(Protocol): - l10n_func: L10NFunc - def render_front_matter(self, content: str, markup: str) -> Any: - ... - - -class DomainGeneration(DomainGenerationProtocol): +class DomainGeneration: + """ + Implements `DomainGenerationProtocol` + """ def __init__(self, l10n_func: L10NFunc): self.l10n_func = l10n_func diff --git a/markdown_gettext/extraction/__init__.py b/markdown_gettext/extraction/__init__.py deleted file mode 100644 index d015dfd..0000000 --- a/markdown_gettext/extraction/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -from .index import extract diff --git a/markdown_gettext/extraction/e_utils.py b/markdown_gettext/extraction/e_utils.py deleted file mode 100644 index 43a47d1..0000000 --- a/markdown_gettext/extraction/e_utils.py +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -from dataclasses import dataclass - -import polib - - -@dataclass -class Occurrence: - src_path: str - line_num: int - - # this is used with tuple() - def __iter__(self): - for key in self.__dict__: - # polib requires an int for line number, but number 0 passed to it would be lost, so we use string - yield str(self.__getattribute__(key)) - - -@dataclass -class I18NEntry: - msgid: str - occurrence: Occurrence - comment: str = '' - - def to_poentry(self) -> polib.POEntry: - return polib.POEntry(msgid=self.msgid, - msgstr='', - occurrences=[tuple(self.occurrence)], - comment=self.comment) - - def add_to_pot(self, pot: polib.POFile): - if self.msgid: - if old_entry := pot.find(self.msgid): - old_entry.occurrences.append(tuple(self.occurrence)) - else: - try: - pot.append(self.to_poentry()) - except ValueError: - pass diff --git a/markdown_gettext/extraction/index.py b/markdown_gettext/extraction/index.py deleted file mode 100644 index 55a7fba..0000000 --- a/markdown_gettext/extraction/index.py +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import os - -from markdown_it import MarkdownIt -from mdit_py_plugins.deflist import deflist_plugin -from mdit_py_plugins.front_matter import front_matter_plugin - -from .e_domain import DomainExtraction -from .renderer_i18n import RendererMarkdownI18N - - -def extract(args): - md_path = args.md - mdi = MarkdownIt(renderer_cls=RendererMarkdownI18N).use(front_matter_plugin)\ - .enable('table').use(deflist_plugin) - domain_e = DomainExtraction() - with open(md_path) as f_md: - env = { - 'path': md_path, - 'domain_extraction': domain_e - } - mdi.render(f_md.read(), env) - package = args.package or md_path - target = args.pot - os.makedirs(os.path.dirname(target), exist_ok=True) - domain_e.make_pot(package, args.report_addr, args.team_addr, target) diff --git a/markdown_gettext/extraction/renderer_i18n.py b/markdown_gettext/extraction/renderer_i18n.py deleted file mode 100644 index b712730..0000000 --- a/markdown_gettext/extraction/renderer_i18n.py +++ /dev/null @@ -1,120 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import inspect -from typing import Sequence, MutableMapping - -import pygments.token -from markdown_it.renderer import RendererProtocol -from markdown_it.token import Token -from markdown_it.utils import OptionsDict -from pygments import lexers, util - -from .. import utils -from .e_domain import DomainExtractionProtocol - - -class _MdCtx: - def __init__(self, env: MutableMapping): - self.path: str = env['path'] - self.domain_e: DomainExtractionProtocol = env['domain_extraction'] - - def add_entry(self, msgid: str, line_number: int, comment: str = ''): - self.domain_e.add_entry(self.path, msgid, line_number, comment) - - -def _link_ref(env: MutableMapping, md_ctx: _MdCtx): - refs = env.get('references', {}).items() - if len(refs) == 0: - return - for ref, details in refs: - if title := details.get('title', ''): - # TODO: line number? - md_ctx.add_entry(title, 0) - - -class RendererMarkdownI18N(RendererProtocol): - def __init__(self, _): - self.rules = { - k: v - for k, v in inspect.getmembers(self, predicate=inspect.ismethod) - if not (k.startswith("render") or k.startswith("_")) - } - - def render(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping): - """ - :param tokens: list of block tokens to render - :param options: properties of parser instance - :param env: containing: - - 'path': path of the source file - - 'domain_extraction': a `DomainExtractionProtocol` object - :return: None - """ - md_ctx = _MdCtx(env) - - for i, token in enumerate(tokens): - if token.type in self.rules: - r = self.rules[token.type](tokens, i, md_ctx) - if r == -1: - break - - _link_ref(env, md_ctx) - - @classmethod - def front_matter(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx): - token = tokens[idx] - md_ctx.domain_e.render_front_matter(md_ctx.path, token.content, token.markup) - - @classmethod - def inline(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx): - token = tokens[idx] - content = utils.SPACES_PATTERN.sub(' ', token.content.replace('\n', ' ')) - if content and not utils.SPACES_PATTERN.fullmatch(content): - md_ctx.add_entry(content, token.map[0] + 1) - - @classmethod - def fence(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx): - token = tokens[idx] - try: - lexer = lexers.get_lexer_by_name(token.info) - except util.ClassNotFound: - lexer = lexers.guess_lexer(token.content) - code_toks = lexer.get_tokens(token.content) - - # temporary content of the comment being parsed - # also indicates whether we are parsing a comment or not - comment = '' - # number of the line where the comment starts - comment_line_num = 0 - # number of the last line with a comment token - last_comment_line_num = 0 - # the token starts with one line of the fence, then the content. +1: 0-base -> 1-base - line_num = token.map[0] + 1 + 1 - - # concatenate comment tokens until either a non-comment token or a blank line or end of token stream - for tok_type, tok_val in code_toks: - if tok_type == pygments.token.Token.Comment.Single: - # when another comment is already being parsed and there's a blank line - if comment and line_num - last_comment_line_num > 1: - md_ctx.add_entry(comment, comment_line_num) - comment = '' - comment_line_num = 0 - comment_match = utils.SINGLE_COMMENT_PATTERN.match(tok_val) - if comment != '': - comment += ' ' - comment += comment_match.group(2).strip() - if comment_line_num == 0: - comment_line_num = line_num - last_comment_line_num = line_num - elif tok_val.strip() and comment: - md_ctx.add_entry(comment, comment_line_num) - comment = '' - comment_line_num = 0 - line_num += tok_val.count('\n') - if comment: - md_ctx.add_entry(comment, comment_line_num) - - @classmethod - def html_block(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx): - token = tokens[idx] - md_ctx.add_entry(token.content, token.map[0] + 1) diff --git a/markdown_gettext/generation/__init__.py b/markdown_gettext/generation/__init__.py deleted file mode 100644 index 61e674f..0000000 --- a/markdown_gettext/generation/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -from .index import generate diff --git a/markdown_gettext/generation/g_utils.py b/markdown_gettext/generation/g_utils.py deleted file mode 100644 index a2401b5..0000000 --- a/markdown_gettext/generation/g_utils.py +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import gettext -from typing import Callable - - -def gettext_func(domain_name): - gettext.bindtextdomain(domain_name, 'locale') - gettext.textdomain(domain_name) - return gettext.gettext - - -class L10NResult: - """Localized content, total number of messages, number of translations. - If there are no messages, rate will be -1 - """ - def __init__(self, localized, total_count: int, l10n_count: int): - self.localized = localized - self.total_count = total_count - self.l10n_count = l10n_count - - def __str__(self): - return f'({self.l10n_count}/{self.total_count})' - - @property - def rate(self): - return self.l10n_count / self.total_count if self.total_count > 0 else -1 - - def sum_rate_with(self, other: 'L10NResult'): - l10n_count = self.l10n_count + other.l10n_count - total_count = self.total_count + other.total_count - return l10n_count / total_count if total_count > 0 else -1 - - -L10NFunc = Callable[[str], str] diff --git a/markdown_gettext/generation/index.py b/markdown_gettext/generation/index.py deleted file mode 100644 index d9059f1..0000000 --- a/markdown_gettext/generation/index.py +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import os -import subprocess -from typing import Tuple - -from markdown_it import MarkdownIt -from mdit_py_plugins.deflist import deflist_plugin -from mdit_py_plugins.front_matter import front_matter_plugin - -from .g_domain import DomainGeneration -from .g_utils import L10NResult, gettext_func -from .renderer_l10n import RendererMarkdownL10N - - -def compile_po(lang: str, po_path: str): - target_dir = f'locale/{lang}/LC_MESSAGES' - os.makedirs(target_dir, exist_ok=True) - po_basename = os.path.basename(po_path) - mo_path = f'{target_dir}/{po_basename[:-2]}mo' - command = f'msgfmt {po_path} -o {mo_path}' - subprocess.run(command, shell=True, check=True) - - -def generate(args): - lang = args.lang - po_path = args.po - - compile_po(lang, po_path) - os.environ['LANGUAGE'] = lang - - domain_name = os.path.splitext(os.path.basename(po_path))[0] - l10n_func = gettext_func(domain_name) - - mdi = MarkdownIt(renderer_cls=RendererMarkdownL10N).use(front_matter_plugin)\ - .enable('table').use(deflist_plugin) - domain_g = DomainGeneration(l10n_func) - - def render_content_file(src_path: str) -> Tuple[L10NResult, L10NResult]: - with open(src_path) as f_content: - env = { - 'domain_generation': domain_g - } - return mdi.render(f_content.read(), env) - - def write_content_file(fm: str, content: str, target_path: str): - os.makedirs(os.path.dirname(target_path), exist_ok=True) - with open(target_path, 'w+') as f_target: - f_target.write(fm) - f_target.write(content) - - fm_result, content_result = render_content_file(args.in_md) - write_content_file(fm_result.localized, content_result.localized, args.out_md) diff --git a/markdown_gettext/generation/renderer_l10n.py b/markdown_gettext/generation/renderer_l10n.py deleted file mode 100644 index a7dda55..0000000 --- a/markdown_gettext/generation/renderer_l10n.py +++ /dev/null @@ -1,382 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -"""Render localized markdown""" - -import inspect -import textwrap -from collections.abc import MutableMapping -from dataclasses import dataclass -from typing import Sequence, Tuple - -import pygments.token -from markdown_it.renderer import RendererProtocol -from markdown_it.token import Token -from markdown_it.utils import OptionsDict -from pygments import lexers, util - -from .. import utils -from .g_utils import L10NResult -from .g_domain import DomainGenerationProtocol - -SETEXT_HEADING_MARKUPS = {'-', '='} -ORDERED_LIST_MARKUPS = {'.', ')'} - - -class _MdCtx: - def __init__(self, env: MutableMapping): - self.line_indent = '' - self.indent_1st_line = '' # list item, definition detail, atx heading - self.indent_1st_line_len = 0 - self.indents = [] - self.setext_heading = '' - self.table_sep = '' - self.in_table = False - self.domain_g: DomainGenerationProtocol = env['domain_generation'] - - def get_line_indent(self): - if not self.indent_1st_line: - return self.line_indent - if self.indent_1st_line_len > 0: - line_indent = self.line_indent[:-self.indent_1st_line_len] + self.indent_1st_line - else: - line_indent = self.line_indent + self.indent_1st_line - self.indent_1st_line = '' - self.indent_1st_line_len = 0 - return line_indent - - -@dataclass -class _FenceCtx: - localized: str = '' # the l10n result - # temporary content of the comment being parsed - # also indicates whether we are parsing a comment or not - comment: str = '' - indent: str = '' # things on the same line before the comment - next_indent: str = '' # a second field when 'indent' is busy - - -# TODO: parameterize: fence code comment i18n, wrap width, comment identifiers -# TODO: multiline comment? -def _fence_comment(fence_ctx: _FenceCtx, md_ctx: _MdCtx, content_result: L10NResult): - localized_comment = md_ctx.domain_g.l10n_func(fence_ctx.comment) - if localized_comment is not fence_ctx.comment: - content_result.l10n_count += 1 - content_result.total_count += 1 - subsequent_indent = ' ' * len(fence_ctx.indent) if fence_ctx.indent.strip() else fence_ctx.indent - comment_lines = textwrap.wrap(localized_comment, - 100, - initial_indent=f'{fence_ctx.indent}// ', - subsequent_indent=f'{subsequent_indent}// ') - for line in comment_lines: - fence_ctx.localized += f'{line}\n' - fence_ctx.comment = '' - - -def _fence(token: Token, md_ctx: _MdCtx, content_result: L10NResult): - try: - lexer = lexers.get_lexer_by_name(token.info) - except util.ClassNotFound: - lexer = lexers.guess_lexer(token.content) - code_toks = lexer.get_tokens(token.content) - - # values to use in _fence_comment function - fence_ctx = _FenceCtx() - # number of the last line with a comment token - last_comment_line_num = 0 - # the token starts with one line of the fence, then the content. +1: 0-base -> 1-base - line_num = token.map[0] + 1 + 1 - - # concatenate comment tokens until either a non-comment token or a blank line or end of token stream - for tok_type, tok_val in code_toks: - if tok_type == pygments.token.Token.Comment.Single: - # when another comment is already being parsed and there's a blank line - if fence_ctx.comment and line_num - last_comment_line_num > 1: - _fence_comment(fence_ctx, md_ctx, content_result) - last_nl = fence_ctx.next_indent.rfind('\n') - fence_ctx.localized += fence_ctx.next_indent[:last_nl + 1] - fence_ctx.indent = fence_ctx.next_indent[last_nl + 1:] - comment_match = utils.SINGLE_COMMENT_PATTERN.match(tok_val) - if fence_ctx.comment != '': - fence_ctx.comment += ' ' - fence_ctx.comment += comment_match.group(2).strip() - last_comment_line_num = line_num - else: - if fence_ctx.comment: - if tok_val.strip(): - _fence_comment(fence_ctx, md_ctx, content_result) - fence_ctx.indent = fence_ctx.next_indent + tok_val - fence_ctx.next_indent = '' - else: - fence_ctx.next_indent = tok_val - else: - last_nl = tok_val.rfind('\n') - if last_nl != -1: - fence_ctx.localized += fence_ctx.indent + tok_val[:last_nl + 1] - fence_ctx.indent = tok_val[last_nl + 1:] - else: - fence_ctx.indent += tok_val - line_num += tok_val.count('\n') - if fence_ctx.comment: - _fence_comment(fence_ctx, md_ctx, content_result) - return fence_ctx.localized - - -def _front_matter(token: Token, env: MutableMapping) -> L10NResult: - domain_g: DomainGenerationProtocol = env['domain_generation'] - fm_result = domain_g.render_front_matter(token.content, token.markup) - return fm_result - - -def _link_ref(env: MutableMapping, md_ctx: _MdCtx, content_result: L10NResult): - refs = env.get('references', {}).items() - if len(refs) == 0: - return - content_result.localized += '\n' - for ref, details in refs: - href = details['href'] - content_result.localized += f'[{ref}]: {href}' - if title := details.get('title', ''): - localized_title = md_ctx.domain_g.l10n_func(title) - if localized_title is not title: - content_result.l10n_count += 1 - content_result.total_count += 1 - content_result.localized += f' "{localized_title}"' - content_result.localized += '\n' - - -class RendererMarkdownL10N(RendererProtocol): - __output__ = "md" - - def __init__(self, _): - self.rules = { - k: v - for k, v in inspect.getmembers(self, predicate=inspect.ismethod) - if not (k.startswith("render") or k.startswith("_")) - } - - def render( - self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping - ) -> Tuple[L10NResult, L10NResult]: - """ - :param tokens: list of block tokens to render - :param options: properties of parser instance - :param env: containing 'domain_generation' a `DomainGenerationProtocol` object - :return: an `L10NResult` - """ - if tokens[0].type == 'front_matter': - fm_result = _front_matter(tokens[0], env) - tokens = tokens[1:] - else: - fm_result = L10NResult('', 0, 0) - - md_ctx = _MdCtx(env) - content_result = L10NResult('', 0, 0) - - for i, token in enumerate(tokens): - if token.type in self.rules: - r = self.rules[token.type](tokens, i, md_ctx, content_result) - if r == -1: - break - - _link_ref(env, md_ctx, content_result) - - return fm_result, content_result - - @classmethod - def inline(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - token = tokens[idx] - content = utils.SPACES_PATTERN.sub(' ', token.content.replace('\n', ' ')) - if content and not utils.SPACES_PATTERN.fullmatch(content): - localized_content = md_ctx.domain_g.l10n_func(content) - else: - localized_content = content - if localized_content is not content: - content_result.l10n_count += 1 - content_result.total_count += 1 - if md_ctx.in_table: - content_result.localized += localized_content - else: - content_result.localized += f'{md_ctx.get_line_indent()}{localized_content}' - - # blockquote - # TODO: blockquote inside list - @classmethod - def blockquote_open(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, _content_result: L10NResult): - token = tokens[idx] - md_ctx.line_indent = f'{md_ctx.get_line_indent()}{token.markup} ' - - @classmethod - def blockquote_close(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - md_ctx.line_indent = md_ctx.line_indent[:-2] - content_result.localized += f'{md_ctx.line_indent}\n' - - # heading - @classmethod - def heading_open(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, _content_result: L10NResult): - token = tokens[idx] - if token.markup not in SETEXT_HEADING_MARKUPS: - md_ctx.indent_1st_line += f'{token.markup} ' - # md_ctx.indent_1st_line_len += 0 - # md_ctx.line_indent += ' ' * 0 - md_ctx.indents.append(0) - else: - md_ctx.setext_heading = token.markup - - @classmethod - def heading_close(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - if md_ctx.setext_heading: - content_result.localized += f'\n{md_ctx.get_line_indent()}{md_ctx.setext_heading}' - md_ctx.setext_heading = '' - else: - md_ctx.indents.pop() - # the added len is 0, so line_indent remains the same - content_result.localized += '\n' - - # thematic break - @classmethod - def hr(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - token = tokens[idx] - # always use '_' here to differentiate this from setext headings and bullet list items - content_result.localized += f'{md_ctx.get_line_indent()}{len(token.markup) * "_"}\n' - - # list - # TODO: loose lists? - @classmethod - def list_item_open(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, _content_result: L10NResult): - token = tokens[idx] - markup = f'{token.info}{token.markup}' if token.markup in ORDERED_LIST_MARKUPS else f'{token.markup}' - md_ctx.indent_1st_line += f'{markup} ' - added_len = len(markup) + 1 - md_ctx.indent_1st_line_len += added_len - md_ctx.line_indent += ' ' * added_len - md_ctx.indents.append(added_len) - - @classmethod - def list_item_close(cls, - _tokens: Sequence[Token], - _idx: int, - md_ctx: _MdCtx, - _content_result: L10NResult): - latest_len = md_ctx.indents.pop() - md_ctx.line_indent = md_ctx.line_indent[:-latest_len] - - @classmethod - def bullet_list_close(cls, - tokens: Sequence[Token], - idx: int, - md_ctx: _MdCtx, - content_result: L10NResult): - # add a blank line when next token is not a closing one - if idx < len(tokens) - 1 and tokens[idx + 1].nesting != -1: - content_result.localized += f'{md_ctx.line_indent}\n' - - @classmethod - def ordered_list_close(cls, - tokens: Sequence[Token], - idx: int, - md_ctx: _MdCtx, - content_result: L10NResult): - # add a blank line when next token is not a closing one - if idx < len(tokens) - 1 and tokens[idx + 1].nesting != -1: - content_result.localized += f'{md_ctx.line_indent}\n' - - # paragraph - @classmethod - def paragraph_close(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += '\n' - if idx < len(tokens) - 1: - next_token = tokens[idx + 1] - # add a blank line when next token is a setext heading_open, an indented code block, a paragraph open, - # or a definition list open - if (next_token.type == 'heading_open' and next_token.markup in SETEXT_HEADING_MARKUPS) \ - or next_token.type in {'code_block', 'paragraph_open', 'dl_open'}: - content_result.localized += f'{md_ctx.line_indent}\n' - - # indented code block - @classmethod - def code_block(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - token = tokens[idx] - localized_code_block = token.content.replace('\n', f'\n{md_ctx.line_indent} ') - content_result.localized += f'{md_ctx.get_line_indent()} {localized_code_block}\n' - - # fenced code block - @classmethod - def fence(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - token = tokens[idx] - localized_fence = _fence(token, md_ctx, content_result) - localized_fence = localized_fence.replace('\n', f'\n{md_ctx.line_indent}') - # a newline is at the end of token.content already, so we only need to append token.markup there - content_result.localized += f'''{md_ctx.get_line_indent()}{token.markup}{token.info} -{md_ctx.line_indent}{localized_fence}{token.markup} -''' - - # html block - @classmethod - def html_block(cls, tokens: Sequence[Token], idx: int, md_ctx: _MdCtx, content_result: L10NResult): - token = tokens[idx] - localized_html = md_ctx.domain_g.l10n_func(token.content) - if localized_html is not token.content: - content_result.l10n_count += 1 - content_result.total_count += 1 - localized_html = localized_html.replace('\n', f'\n{md_ctx.line_indent}') - content_result.localized += f'{md_ctx.get_line_indent()}{localized_html}\n' - - # table - @classmethod - def table_open(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, _content_result: L10NResult): - md_ctx.in_table = True - - @classmethod - def tr_open(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += f'{md_ctx.get_line_indent()}|' - - @classmethod - def th_open(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += ' ' - md_ctx.table_sep += '| --- ' - - @classmethod - def th_close(cls, _tokens: Sequence[Token], _idx: int, _md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += ' |' - - @classmethod - def thead_close(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - md_ctx.table_sep += '|\n' - content_result.localized += f'{md_ctx.line_indent}{md_ctx.table_sep}' - md_ctx.table_sep = '' - - @classmethod - def td_open(cls, _tokens: Sequence[Token], _idx: int, _md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += ' ' - - @classmethod - def td_close(cls, _tokens: Sequence[Token], _idx: int, _md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += ' |' - - @classmethod - def tr_close(cls, _tokens: Sequence[Token], _idx: int, _md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += '\n' - - @classmethod - def table_close(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += f'{md_ctx.line_indent}\n' - md_ctx.in_table = False - - # definition list - @classmethod - def dd_open(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, _content_result: L10NResult): - md_ctx.indent_1st_line += f': ' - md_ctx.indent_1st_line_len += 2 - md_ctx.line_indent += ' ' * 2 - md_ctx.indents.append(2) - - @classmethod - def dd_close(cls, _tokens: Sequence[Token], _idx: int, md_ctx: _MdCtx, content_result: L10NResult): - latest_len = md_ctx.indents.pop() - md_ctx.line_indent = md_ctx.line_indent[:-latest_len] - content_result.localized += f'{md_ctx.line_indent}\n' - - @classmethod - def dt_close(cls, _tokens: Sequence[Token], _idx: int, _md_ctx: _MdCtx, content_result: L10NResult): - content_result.localized += '\n' diff --git a/markdown_gettext/utils.py b/markdown_gettext/utils.py deleted file mode 100644 index 75a7990..0000000 --- a/markdown_gettext/utils.py +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import re - -SPACES_PATTERN = re.compile(r'\s+') -SINGLE_COMMENT_PATTERN = re.compile('(// *)(.*)') diff --git a/poetry.lock b/poetry.lock index 6fdaea9..240a72c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,6 +20,18 @@ profiling = ["gprof2dot"] rtd = ["mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx-book-theme", "jupyter-sphinx"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "mdit-py-i18n" +version = "0.1.0" +description = "Markdown i18n and l10n using markdown-it-py" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +markdown-it-py = {version = ">=3.0.0,<4.0.0", extras = ["plugins"]} +Pygments = ">=2.16.1,<3.0.0" + [[package]] name = "mdit-py-plugins" version = "0.4.0" @@ -74,13 +86,17 @@ python-versions = ">=3.6" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "431d5514060ece2a39beacd4afec26e8ecf2efacf3398349bf7e9af0b907f931" +content-hash = "0c4ae740e60171d838ebac09497cabbf8bdfb366b7bcc566ba4eb05d56bcfec9" [metadata.files] markdown-it-py = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] +mdit-py-i18n = [ + {file = "mdit-py-i18n-0.1.0.tar.gz", hash = "sha256:b0b993b57eadcef78cfe8b1698e329bea215a17e3871ce88163d26a7f0eeac69"}, + {file = "mdit_py_i18n-0.1.0-py3-none-any.whl", hash = "sha256:725c2bec6aa03ccafc063e2bdf36b74a6e2ab5534d5e53905367e87a6a7bd110"}, +] mdit-py-plugins = [ {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, diff --git a/pyproject.toml b/pyproject.toml index a4f961a..a3df3f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ [tool.poetry] name = "markdown-gettext" -version = "0.1.0" +version = "0.1.1" description = "Markdown i18n with gettext" authors = ["Phu Hung Nguyen "] license = "LGPL-2.1-or-later" @@ -12,7 +12,6 @@ repository = "https://github.com/phunh/markdown-gettext" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Internationalization", "Programming Language :: Python", "Programming Language :: Python :: 3" @@ -21,10 +20,9 @@ packages = [{include = "markdown_gettext"}] [tool.poetry.dependencies] python = "^3.8" -markdown-it-py = {extras = ["plugins"], version = "^3.0.0"} polib = "^1.2.0" -Pygments = "^2.16.1" PyYAML = "^6.0.1" +mdit-py-i18n = "^0.1.0" [tool.poetry.scripts] md-gettext = 'markdown_gettext.cli:main' diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/resources/__init__.py b/tests/resources/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/resources/blockquotes.md b/tests/resources/blockquotes.md deleted file mode 100644 index 1067ed9..0000000 --- a/tests/resources/blockquotes.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -authors: -- SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -SPDX-License-Identifier: CC0-1.0 ---- -> simple blockquote - -> consecutive second blockquote - -> blockquote first line -> and second line with a marker - -> blockquote first line -and second line without a marker - -> blockquote first paragraph -> -> and second paragraph - -> blockquote -> nested coming -> -> > second level -> still second level but another line -> -> back to first level -> -> > > third level -> -> 1. ordered list item 1 -> 2. ordered list item 2 -> in a new line -> -> - bullet list item 1 -> - bullet list item 2 -> - bullet list item 3 -> -> 1. ordered list item 1 containing bullet list -> - bullet list item 1 inside ordered list item 1 -> - bullet list item 2 inside ordered list item 1 -> 2. ordered list item 2 after item 1 containing bullet list -> -> 1) # atx heading in list in blockquote -> -> # atx heading level 1 -> -> #### atx heading level 4 -> -> setext heading level 1 -> = -> -> setext heading level 2 -> - -> -> not a paragraph -> prepended to setext heading level 1 -> = -> -> paragraph separated from -> -> setext heading level 2 -> - -> -> paragraph before a thematic break -> -> --- -> paragraph after a thematic break -> -> int main(): -> return 0 -> -> ```python -> def main(): -> return None -> ``` -> ->
->

html block

->
-> -> | head | head2 | -> |------|-------| -> | body | body2 | -> -> term number 1 -> : details number 1 -> -> term number 2 -> : details number 2 -> -> last paragraph \ No newline at end of file diff --git a/tests/resources/definition_list.md b/tests/resources/definition_list.md deleted file mode 100644 index 2736dbe..0000000 --- a/tests/resources/definition_list.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -authors: -- SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -SPDX-License-Identifier: CC0-1.0 ---- -term number 1 -: details number 1 - -term number 2 -: details number 2 -onto the next line, without indentation - -term number 3 -: details number 3 - onto the next line, with indentation - -term with indented code block -: details with indented code block - - before the code block - - def main: - pass - after the code block - -term with html block -: details with html block -
-

html block

-
-
- -complex term -: complex details - > 1. list inside blockquote - - 1. --- - 2. # heading inside blockquote inside list item 2 - paragraph after heading \ No newline at end of file diff --git a/tests/resources/lists.md b/tests/resources/lists.md deleted file mode 100644 index 26a59bc..0000000 --- a/tests/resources/lists.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -authors: -- SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -SPDX-License-Identifier: CC0-1.0 ---- -* bullet 1 -* bullet 2 -bullet 2 second line -* bullet 3 - -1. ordered 1 -2. ordered 2 -3. ordered 3 -4. ordered 4 - -* bullet 1 - * nested bullet 1 - * nested nested bullet 1 - * nested nested bullet 2 - * nested bullet 2 -* bullet 2 - -1) ordered 1 - 1) nested ordered 1 - 2) nested ordered 2 - 1) nested nested ordered 1 - 3) nested ordered 3 -2) ordered 2 - 1) nested 2.1 - 2) nested 2.2 - -1. mixed list - - bullet inside 1 - - bullet 2 inside 1 - 1. ordered inside bullet 2 inside 1 -2. second item - -1) ordered 1 with bullet list - + bullet 1 inside 1 - + bullet 2 inside 1 - -1) ordered 2 after a blank line - -1. 1. list right at beginning of an item -2. second item - -1) list with blockquote - > inside blockquote - > 1. list inside blockquote - > 2. another list item - -1. list with code - - int main(): - return 0 -2. fence in item - - ```python - def main(): - return None - ``` - -3. # can an atx heading be here? - --- - now a table - - | head | head2 | - |------|-------| - | body | body2 | -4. two paragraphs - - in one list item -5. term number 2 - : details number 2 - onto the next line, without indentation -6. can a setext heading be here? - - - [foo]: /url "title" - - this is a reference [foo]. and after the reference. \ No newline at end of file diff --git a/tests/resources/renderer.md b/tests/resources/renderer.md deleted file mode 100644 index 2e88f16..0000000 --- a/tests/resources/renderer.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -authors: -- SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -SPDX-License-Identifier: CC0-1.0 ---- -# atx heading - -setext heading -== - ---- - -paragraph here - -and more - - int main() { - int a = 1; - return(a) - } - -```c -#include "header.h" -// comment 1 -#include "header2.h" -// comment 2 -int main() { - -// Comment spreaded -// on 2 lines, not indented - - // Comment spreaded - // on 2 lines, indented - - - - // A super long long comment - // on multiple lines - // trying to make it into 3 lines - // now the fourth one here - return 0; - -// Last comment -} -``` - -
-

html block

-
-
- -[foo]: /url "title" - -this is a reference [foo]. and after the reference. - -| head | head2 | -|------|-------| -| body | body2 | \ No newline at end of file diff --git a/tests/test_renderer_i18n.py b/tests/test_renderer_i18n.py deleted file mode 100644 index 250083f..0000000 --- a/tests/test_renderer_i18n.py +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import importlib.resources as pkg_resources -import unittest - -from markdown_it import MarkdownIt -from mdit_py_plugins.deflist import deflist_plugin -from mdit_py_plugins.front_matter import front_matter_plugin - -from markdown_gettext.extraction.e_domain import DomainExtraction -from markdown_gettext.extraction.renderer_i18n import RendererMarkdownI18N - - -class RendererMarkdownI18NTestCase(unittest.TestCase): - mdi = MarkdownIt(renderer_cls=RendererMarkdownI18N).use(front_matter_plugin)\ - .enable('table').use(deflist_plugin) - - def test_renderer(self): - path = 'renderer.md' - domain_e = DomainExtraction() - with pkg_resources.open_text('tests.resources', path) as f_obj: - env = { - 'path': path, - 'domain_extraction': domain_e - } - tokens = self.mdi.parse(f_obj.read(), env) - self.mdi.renderer.render(tokens, self.mdi.options, env) - self.assertEqual(19, len(domain_e.entries)) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_renderer_l10n.py b/tests/test_renderer_l10n.py deleted file mode 100644 index f13e1d6..0000000 --- a/tests/test_renderer_l10n.py +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen -# SPDX-License-Identifier: LGPL-2.1-or-later - -import importlib.resources as pkg_resources -import unittest - -from markdown_it import MarkdownIt -from mdit_py_plugins.deflist import deflist_plugin -from mdit_py_plugins.front_matter import front_matter_plugin - -from markdown_gettext.generation.g_domain import DomainGeneration -from markdown_gettext.generation.renderer_l10n import RendererMarkdownL10N - - -class RendererMarkdownL10NTestCase(unittest.TestCase): - mdi = MarkdownIt(renderer_cls=RendererMarkdownL10N).use(front_matter_plugin)\ - .enable('table').use(deflist_plugin) - - def _prep_test(self, f_obj): - env = { - 'domain_generation': DomainGeneration(lambda s: s) - } - tokens = self.mdi.parse(f_obj.read(), env) - fm_result, content_result = self.mdi.renderer.render(tokens, self.mdi.options, env) - localized_tokens = self.mdi.parse(fm_result.localized + content_result.localized) - return tokens, content_result, localized_tokens - - def test_blockquotes(self): - with pkg_resources.open_text('tests.resources', 'blockquotes.md') as f_obj: - tokens, content_result, localized_tokens = self._prep_test(f_obj) - self.assertEqual([token.type for token in tokens], [token.type for token in localized_tokens]) - - def test_lists(self): - with pkg_resources.open_text('tests.resources', 'lists.md') as f_obj: - tokens, content_result, localized_tokens = self._prep_test(f_obj) - self.assertEqual([token.type for token in tokens], [token.type for token in localized_tokens]) - - def test_definition_list(self): - with pkg_resources.open_text('tests.resources', 'definition_list.md') as f_obj: - tokens, content_result, localized_tokens = self._prep_test(f_obj) - self.assertEqual([token.type for token in tokens], [token.type for token in localized_tokens]) - - def test_others(self): - with pkg_resources.open_text('tests.resources', 'renderer.md') as f_obj: - tokens, content_result, localized_tokens = self._prep_test(f_obj) - self.assertEqual([token.type for token in tokens], [token.type for token in localized_tokens]) - - -if __name__ == '__main__': - unittest.main()