Skip to content

Commit

Permalink
Split the library into another package, keep this as a program only
Browse files Browse the repository at this point in the history
  • Loading branch information
PhuNH committed Sep 2, 2023
1 parent 1a47ea9 commit b3dbfe7
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 1,099 deletions.
28 changes: 5 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@ 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

```bash
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

Expand Down Expand Up @@ -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

Expand All @@ -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`
72 changes: 70 additions & 2 deletions markdown_gettext/cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen <[email protected]>
# 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():
Expand Down Expand Up @@ -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')
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen <[email protected]>
# 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 <EMAIL@ADDRESS>',
'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.
Expand All @@ -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 <EMAIL@ADDRESS>',
'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)
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# SPDX-FileCopyrightText: 2023 Phu Hung Nguyen <[email protected]>
# 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

Expand Down
4 changes: 0 additions & 4 deletions markdown_gettext/extraction/__init__.py

This file was deleted.

41 changes: 0 additions & 41 deletions markdown_gettext/extraction/e_utils.py

This file was deleted.

28 changes: 0 additions & 28 deletions markdown_gettext/extraction/index.py

This file was deleted.

Loading

0 comments on commit b3dbfe7

Please sign in to comment.