From 9e71d383bf59573a1dd48964a2c7900a57161973 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 11 Feb 2022 16:51:44 +0100 Subject: internationalize using Babel this commit also makes the sdist tarball generated by setuptools REUSE-compliant --- .gitignore | 2 + .reuse/dep5 | 12 ++-- MANIFEST.in | 1 + pyproject.toml | 2 +- setup.cfg | 30 +++++++++- setup.py | 12 +++- src/hydrilla/builder/__main__.py | 51 ++-------------- src/hydrilla/builder/build.py | 31 ++++++++-- .../locales/en_US/LC_MESSAGES/hydrilla_builder.po | 67 ++++++++++++++++++++++ src/hydrilla/util/__init__.py | 2 +- src/hydrilla/util/_util.py | 30 +++++++++- 11 files changed, 178 insertions(+), 62 deletions(-) create mode 100644 src/hydrilla/locales/en_US/LC_MESSAGES/hydrilla_builder.po diff --git a/.gitignore b/.gitignore index a5df35a..1d50e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ dist *.pyc setuptools src/hydrilla/builder/_version.py +src/hydrilla/locales/hydrilla_builder.pot +hydrilla_builder.mo \ No newline at end of file diff --git a/.reuse/dep5 b/.reuse/dep5 index 9cf1093..7cd5158 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -3,8 +3,10 @@ Upstream-Name: Hydrilla builder Upstream-Contact: Wojtek Kosior Source: https://git.koszko.org/hydrilla-builder -# Sample paragraph, commented out: -# -# Files: src/* -# Copyright: $YEAR $NAME <$CONTACT> -# License: ... +Files: PKG-INFO setup.cfg src/hydrilla/builder/_version.py + src/hydrilla.builder.egg-info/* +Copyright: 2022 Wojtek Kosior +License: CC0-1.0 +Comment: Those are files that setuptools generate in sdist. + `setup.cfg` gets stripped of comments during generation of + sdist, so we include it here as well. diff --git a/MANIFEST.in b/MANIFEST.in index 7b65971..133611e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,5 +5,6 @@ # Available under the terms of Creative Commons Zero v1.0 Universal. include src/hydrilla/schemas/*.schema.json* +include src/hydrilla/locales/*/LC_MESSAGES/hydrilla_builder.po include src/test/source-package-example/* global-exclude .git .gitignore .gitmodules diff --git a/pyproject.toml b/pyproject.toml index a41a6ec..cffd3dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ [build-system] build-backend = "setuptools.build_meta" -requires = ["setuptools>=45", "wheel", "setuptools_scm>=5.0"] +requires = ["setuptools>=45", "wheel", "setuptools_scm>=5.0", "babel"] [tool.setuptools_scm] write_to = "src/hydrilla/builder/_version.py" diff --git a/setup.cfg b/setup.cfg index b949fb6..78e384b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,10 +35,12 @@ include_package_data=True install_requires = click jsonschema - importlib; python_version == "2.6" # reuse is optional: # reuse +[options.package_data] +* = *.mo + [options.extras_require] test = pytest setup = setuptools_scm @@ -50,4 +52,28 @@ exclude = [options.entry_points] console_scripts = - hydrilla-builder = hydrilla.builder.__main__:perform_build + hydrilla-builder = hydrilla.builder.build:perform + +[extract_messages] +keywords = _ +width = 80 +input_dirs = src/hydrilla +output_file = src/hydrilla/locales/hydrilla_builder.pot +msgid_bugs_address = koszko@koszko.org +sort_by_file = True +copyright_holder = Wojtek Kosior + +[init_catalog] +input_file = src/hydrilla/locales/hydrilla_builder.pot +output_dir = src/hydrilla/locales/ +domain = hydrilla_builder + +[update_catalog] +input_file = src/hydrilla/locales/hydrilla_builder.pot +output_dir = src/hydrilla/locales/ +domain = hydrilla_builder + +[compile_catalog] +directory = src/hydrilla/locales +use_fuzzy = True +domain = hydrilla_builder diff --git a/setup.py b/setup.py index 6554e08..345febc 100755 --- a/setup.py +++ b/setup.py @@ -7,4 +7,14 @@ import setuptools -setuptools.setup() +from setuptools.command.build_py import build_py + +class CustomBuildCommand(build_py): + ''' + The build command but runs babel before build. + ''' + def run(self, *args, **kwargs): + self.run_command('compile_catalog') + super().run(*args, **kwargs) + +setuptools.setup(cmdclass={'build_py': CustomBuildCommand}) diff --git a/src/hydrilla/builder/__main__.py b/src/hydrilla/builder/__main__.py index e60c50d..87dc9e2 100644 --- a/src/hydrilla/builder/__main__.py +++ b/src/hydrilla/builder/__main__.py @@ -1,50 +1,9 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later +# SPDX-License-Identifier: CC0-1.0 -# Command line interface of Hydrilla package builder. +# Copyright (C) 2022 Wojtek Kosior # -# This file is part of Hydrilla -# -# Copyright (C) 2022 Wojtek Kosior -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# -# I, Wojtek Kosior, thereby promise not to sue for violation of this -# file's license. Although I request that you do not make use this code -# in a proprietary program, I am not going to enforce this in court. - -from pathlib import Path - -import click - -from .build import Build - -dir_type = click.Path(exists=True, file_okay=False, resolve_path=True) +# Available under the terms of Creative Commons Zero v1.0 Universal. -@click.command() -@click.option('-s', '--srcdir', default='./', type=dir_type, show_default=True, - help='Source directory to build from.') -@click.option('-i', '--index-json', default='index.json', type=click.Path(), - help='Path to file to be processed instead of index.json (if not absolute, resolved relative to srcdir).') -@click.option('-d', '--dstdir', type=dir_type, required=True, - help='Destination directory to write built package files to.') -def preform_build(srcdir, index_json, dstdir): - """ - Build Hydrilla package from scrdir and write the resulting files under - dstdir. - """ - build = Build(Path(srcdir), Path(index_json)) - build.write_package_files(Path(dstdir)) +from . import build -preform_build() +build.perform() diff --git a/src/hydrilla/builder/build.py b/src/hydrilla/builder/build.py index cdbfad3..8d25b19 100644 --- a/src/hydrilla/builder/build.py +++ b/src/hydrilla/builder/build.py @@ -32,9 +32,12 @@ from hashlib import sha256 from sys import stderr import jsonschema +import click from .. import util +_ = util.get_gettext('hydrilla_builder') + index_validator = util.validator_for('package_source-1.schema.json') class FileReferenceError(Exception): @@ -99,12 +102,12 @@ def generate_spdx_report(root): try: from reuse._main import main as reuse_main except ModuleNotFoundError: - ReuseError("Could not import 'reuse'. Is the tool installed and visible to this Python instance?") + ReuseError(_('couldnt_import_reuse_is_it_installed')) mocked_output = FileBuffer() if reuse_main(args=['--root', str(root), 'lint'], out=mocked_output) != 0: stderr.write(mocked_output.get_bytes().decode()) - raise ReuseError('Attempt to generate an SPDX report for a REUSE-incompliant package.') + raise ReuseError(_('spdx_report_from_reuse_incompliant')) mocked_output = FileBuffer() if reuse_main(args=['--root', str(root), 'spdx'], out=mocked_output) != 0: @@ -188,10 +191,11 @@ class Build: path = path.resolve() if not path.is_relative_to(self.srcdir): - raise FileReferenceError(f"Attempt to load '{filename}' which lies outside package source directory.") + raise FileReferenceError(_('loading_{}_outside_package_dir') + .format(filename)) if str(path.relative_to(self.srcdir)) == 'index.json': - raise FileReferenceError("Attempt to load 'index.json' which is a reserved filename.") + raise FileReferenceError(_('loading_reserved_index_json')) file_ref = self.files_by_path.get(path) if file_ref is None: @@ -305,7 +309,7 @@ class Build: [self._process_file(f['file']) for f in index_obj['copyright']] if generate_spdx and not spdx_ref.include_in_distribution: - raise FileReferenceError("Told to generate 'report.spdx' but 'report.spdx' is not listed among copyright files. Refusing to proceed.") + raise FileReferenceError(_('report_spdx_not_in_copyright_list')) item_refs = [self._process_item(d) for d in index_obj['definitions']] @@ -373,3 +377,20 @@ class Build: version = '.'.join([str(n) for n in item_def['version']]) with open(item_dir_path / version, 'wt') as output: json.dump(item_def, output) + +dir_type = click.Path(exists=True, file_okay=False, resolve_path=True) + +@click.option('-s', '--srcdir', default='./', type=dir_type, show_default=True, + help=_('source_directory_to_build_from')) +@click.option('-i', '--index-json', default='index.json', type=click.Path(), + help=_('path_instead_of_index_json')) +@click.option('-d', '--dstdir', type=dir_type, required=True, + help=_('built_package_files_destination')) +def perform(srcdir, index_json, dstdir): + """""" + build = Build(Path(srcdir), Path(index_json)) + build.write_package_files(Path(dstdir)) + +perform.__doc__ = _('build_package_from_srcdir_to_dstdir') + +perform = click.command()(perform) diff --git a/src/hydrilla/locales/en_US/LC_MESSAGES/hydrilla_builder.po b/src/hydrilla/locales/en_US/LC_MESSAGES/hydrilla_builder.po new file mode 100644 index 0000000..2d351d6 --- /dev/null +++ b/src/hydrilla/locales/en_US/LC_MESSAGES/hydrilla_builder.po @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# English (United States) translations for hydrilla.builder. +# Copyright (C) 2022 Wojtek Kosior +# Available under the terms of Creative Commons Zero v1.0 Universal. +msgid "" +msgstr "" +"Project-Id-Version: hydrilla.builder 0.1.dev16+g4e46d7f.d20220211\n" +"Report-Msgid-Bugs-To: koszko@koszko.org\n" +"POT-Creation-Date: 2022-02-12 10:49+0100\n" +"PO-Revision-Date: 2022-02-12\n" +"Last-Translator: Wojtek Kosior \n" +"Language: en_US\n" +"Language-Team: en_US \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.8.0\n" + +#: src/hydrilla/builder/build.py:105 +msgid "couldnt_import_reuse_is_it_installed" +msgstr "" +"Could not import 'reuse'. Is the tool installed and visible to this " +"Python instance?" + +#: src/hydrilla/builder/build.py:110 +msgid "spdx_report_from_reuse_incompliant" +msgstr "Attempt to generate an SPDX report for a REUSE-incompliant package." + +#: src/hydrilla/builder/build.py:194 +msgid "loading_{}_outside_package_dir" +msgstr "Attempt to load '{}' which lies outside package source directory." + +#: src/hydrilla/builder/build.py:198 +msgid "loading_reserved_index_json" +msgstr "Attempt to load 'index.json' which is a reserved filename." + +#: src/hydrilla/builder/build.py:312 +msgid "report_spdx_not_in_copyright_list" +msgstr "" +"Told to generate 'report.spdx' but 'report.spdx' is not listed among " +"copyright files. Refusing to proceed." + +#: src/hydrilla/builder/build.py:384 +msgid "source_directory_to_build_from" +msgstr "Source directory to build from." + +#: src/hydrilla/builder/build.py:386 +msgid "path_instead_of_index_json" +msgstr "" +"Path to file to be processed instead of index.json (if not absolute, " +"resolved relative to srcdir)." + +#: src/hydrilla/builder/build.py:388 +msgid "built_package_files_destination" +msgstr "Destination directory to write built package files to." + +#: src/hydrilla/builder/build.py:394 +msgid "build_package_from_srcdir_to_dstdir" +msgstr "" +"Build Hydrilla package from `scrdir` and write the resulting files " +"under `dstdir`." + +#: src/hydrilla/util/_util.py:76 +msgid "bad_comment" +msgstr "bad comment" diff --git a/src/hydrilla/util/__init__.py b/src/hydrilla/util/__init__.py index 2a69e0d..3a13831 100644 --- a/src/hydrilla/util/__init__.py +++ b/src/hydrilla/util/__init__.py @@ -5,4 +5,4 @@ # Available under the terms of Creative Commons Zero v1.0 Universal. from ._util import strip_json_comments, normalize_version, parse_version, \ - version_string, validator_for + version_string, validator_for, get_gettext diff --git a/src/hydrilla/util/_util.py b/src/hydrilla/util/_util.py index 9bebdc1..c7e9f47 100644 --- a/src/hydrilla/util/_util.py +++ b/src/hydrilla/util/_util.py @@ -26,6 +26,8 @@ import re import json +import locale +import gettext from pathlib import Path from typing import Optional @@ -71,7 +73,7 @@ def strip_json_comments(text: str) -> str: # ignore this error, let json module report it stripped = line elif len(match[2]) == 1: - raise json.JSONDecodeError('bad comment', text, + raise json.JSONDecodeError(_('bad_comment'), text, processed + len(match[1])) else: stripped = match[1] @@ -127,3 +129,29 @@ def validator_for(schema_filename: str) -> Draft7Validator: """ return Draft7Validator(resolver.resolve(schema_filename)[1], resolver=resolver) + +def get_gettext(domain: str, lang: Optional[str]=None): + """ + Configure translation and return its gettext() function. + + If `lang` is set, look for translations for `lang`. Otherwise, try to + determine system's default language and use that. + """ + # https://stackoverflow.com/questions/3425294/how-to-detect-the-os-default-language-in-python + # But I am not going to surrender to Microbugs' nonfree, crappy OS to test + # it, to the lines inside try: may fail. + try: + from ctypes.windll import kernel32 as windll + lang = locale.windows_locale[windll.GetUserDefaultUILanguage()] + except: + lang = locale.getdefaultlocale()[0] or 'C' + + translation = gettext.translation( + domain, + localedir=(here.parent / 'locales'), + languages=[lang, 'en_US'] + ) + + return translation.gettext + +_ = get_gettext('hydrilla_builder') -- cgit v1.2.3