Browse Source

[mod] add i18n infrastructure for SearXNG message files (searxng.msg)

With this patch ``searxng.msg`` files can be added to SearXNG.  In
``searxng.msg`` files messages can be defined which are not captured by babel's
gettext, like the generic names of the categories or messages that are stored in
constants.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Markus Heiser 3 years ago
parent
commit
b9cf3c82a1
5 changed files with 65 additions and 1 deletions
  1. 3 0
      babel.cfg
  2. 1 0
      docs/dev/translation.rst
  3. 8 0
      docs/src/searx.babel_extract.rst
  4. 2 1
      manage
  5. 51 0
      searx/babel_extract.py

+ 3 - 0
babel.cfg

@@ -1,4 +1,7 @@
+[extractors]
+searxng_msg = searx.babel_extract.extract
 [ignore: **/node_modules/**]
 [ignore: **/node_modules/**]
 [python: **.py]
 [python: **.py]
 [jinja2: **/templates/**.html]
 [jinja2: **/templates/**.html]
 extensions=jinja2.ext.autoescape,jinja2.ext.with_
 extensions=jinja2.ext.autoescape,jinja2.ext.with_
+[searxng_msg: **/searxng.msg]

+ 1 - 0
docs/dev/translation.rst

@@ -16,6 +16,7 @@ Translation
 
 
 .. sidebar:: |translated|
 .. sidebar:: |translated|
 
 
+   - :ref:`searx.babel_extract`
    - Weblate_
    - Weblate_
    - SearXNG `translations branch`_
    - SearXNG `translations branch`_
    - SearXNG `Weblate repository`_
    - SearXNG `Weblate repository`_

+ 8 - 0
docs/src/searx.babel_extract.rst

@@ -0,0 +1,8 @@
+.. _searx.babel_extract:
+
+===============================
+Custom message extractor (i18n)
+===============================
+
+.. automodule:: searx.babel_extract
+  :members:

+ 2 - 1
manage

@@ -30,7 +30,7 @@ GECKODRIVER_VERSION="v0.30.0"
 export NODE_MINIMUM_VERSION="16.13.0"
 export NODE_MINIMUM_VERSION="16.13.0"
 # SPHINXOPTS=
 # SPHINXOPTS=
 BLACK_OPTIONS=("--target-version" "py37" "--line-length" "120" "--skip-string-normalization")
 BLACK_OPTIONS=("--target-version" "py37" "--line-length" "120" "--skip-string-normalization")
-BLACK_TARGETS=("--exclude" "searx/static,searx/languages.py" "searx" "searxng_extra" "tests")
+BLACK_TARGETS=("--exclude" "searx/static,searx/languages.py" "--include" "searxng.msg" "searx" "searxng_extra" "tests")
 
 
 pylint.FILES() {
 pylint.FILES() {
 
 
@@ -41,6 +41,7 @@ pylint.FILES() {
     # These py files are linted by test.pylint()
     # These py files are linted by test.pylint()
 
 
     grep -l -r --include \*.py '^#[[:blank:]]*lint:[[:blank:]]*pylint' searx searxng_extra tests
     grep -l -r --include \*.py '^#[[:blank:]]*lint:[[:blank:]]*pylint' searx searxng_extra tests
+    find . -name searxng.msg
 }
 }
 
 
 YAMLLINT_FILES=()
 YAMLLINT_FILES=()

+ 51 - 0
searx/babel_extract.py

@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# lint: pylint
+"""This module implements the :origin:`searxng_msg <babel.cfg>` extractor to
+extract messages from:
+
+- None
+
+The ``searxng.msg`` files are selected by Babel_, see Babel's configuration in
+:origin:`babel.cfg`::
+
+    searxng_msg = searx.babel_extract.extract
+    ...
+    [searxng_msg: **/searxng.msg]
+
+A ``searxng.msg`` file is a python file that is *executed* by the
+:py:obj:`extract` function.  Additional ``searxng.msg`` files can be added by:
+
+1. Adding a ``searxng.msg`` file in one of the SearXNG python packages and
+2. implement a method in :py:obj:`extract` that yields messages from this file.
+
+.. _Babel: https://babel.pocoo.org/en/latest/index.html
+
+"""
+
+from os import path
+
+SEARXNG_MSG_FILE = "searxng.msg"
+_MSG_FILES = []
+
+
+def extract(
+    # pylint: disable=unused-argument
+    fileobj,
+    keywords,
+    comment_tags,
+    options,
+):
+    """Extract messages from ``searxng.msg`` files by a custom extractor_.
+
+    .. _extractor:
+       https://babel.pocoo.org/en/latest/messages.html#writing-extraction-methods
+    """
+    if fileobj.name not in _MSG_FILES:
+        raise RuntimeError("don't know how to extract messages from %s" % fileobj.name)
+
+    namespace = {}
+    exec(fileobj.read(), {}, namespace)  # pylint: disable=exec-used
+
+    for name in namespace['__all__']:
+        for k, v in namespace[name].items():
+            yield 0, '_', v, ["%s['%s']" % (name, k)]