|
@@ -1,32 +1,104 @@
|
|
|
-import gettext
|
|
|
+# MODULE: grass
|
|
|
+#
|
|
|
+# AUTHOR(S): Vaclav Petras <wenzeslaus gmail com>
|
|
|
+#
|
|
|
+# PURPOSE: Top level file of the grass package and its initialization
|
|
|
+#
|
|
|
+# COPYRIGHT: (C) 2021 Vaclav Petras, and by the GRASS Development Team
|
|
|
+#
|
|
|
+# This program is free software under the GNU General Public
|
|
|
+# License (>=v2). Read the file COPYING that comes with GRASS
|
|
|
+# for details.
|
|
|
+
|
|
|
+"""Top-level GRASS GIS Python package
|
|
|
+
|
|
|
+Importing the package (or any subpackage) initializes translation functions
|
|
|
+so that the function ``_`` appears in the global namespace (as an additional build-in).
|
|
|
+"""
|
|
|
+
|
|
|
+import builtins as _builtins
|
|
|
import os
|
|
|
|
|
|
-import six
|
|
|
|
|
|
-# Setup i18N
|
|
|
+# Setup translations
|
|
|
#
|
|
|
-# Calling `gettext.install()` injects `_()` in the builtins namespace and
|
|
|
-# thus it becomes available globally (i.e. in the same process). Any python
|
|
|
-# code that needs to use _() should make sure that it firsts imports this
|
|
|
-# library.
|
|
|
+# The translations in the GRASS GIS Python package grass, GRASS Python modules
|
|
|
+# (scripts), and the wxPython GUI (wxGUI) are handled as application translations,
|
|
|
+# rather than Python module or package translations. This means that that translation
|
|
|
+# function called `_` (underscore) is added to buildins namespace. When the grass
|
|
|
+# package or any subpackage (or an object from there) is imported, the `_` function
|
|
|
+# becomes available globally (i.e. for all Python code in the same process). This is
|
|
|
+# the good part.
|
|
|
+
|
|
|
+# Unfortunately, creating `_` in the global namespace has unitended consequences such
|
|
|
+# as issues in interactive shells, with doctests, and it is conflicting with a common
|
|
|
+# practice of using `_` for unused variables. This is the same behavior as with the
|
|
|
+# common function `gettext.install()` which also adds `_` into the global namespace.
|
|
|
+# The solution to this is to use the module translations approach instead of
|
|
|
+# application translation approach. Without changing the overall approach, the current
|
|
|
+# code can be modified to allow for using imports of translation functions instead of
|
|
|
+# relying on the buildins when desired as a transitional state before removing the
|
|
|
+# modification of buildins.
|
|
|
#
|
|
|
-# Note: we need to do this at the beginning of this module in order to
|
|
|
-# ensure that the injection happens before anything else gets imported.
|
|
|
+# The current code mitigates two other issues associated with `gettext.install()`
|
|
|
+# approach: First, it delays initialization of translations to the time when they are
|
|
|
+# needed instead of doing it during import time (and possibly failing when
|
|
|
+# environmental variables pointing to the source file are not set properly). Second,
|
|
|
+# it adds multiple domains as fallback unlike `gettext.install()` which simply uses
|
|
|
+# the last used domain.
|
|
|
#
|
|
|
-# For more info please check the following links:
|
|
|
-# - https://docs.python.org/2/library/gettext.html#gettext.install
|
|
|
-# - https://pymotw.com/2//gettext/index.html#application-vs-module-localization
|
|
|
+# For more info, please check the following links:
|
|
|
+# - https://docs.python.org/3/library/gettext.html#gettext.translation
|
|
|
+# - https://github.com/python/cpython/blob/main/Lib/gettext.py (esp. install function)
|
|
|
+# - https://pymotw.com/3//gettext/index.html#application-vs-module-localization
|
|
|
# - https://www.wefearchange.org/2012/06/the-right-way-to-internationalize-your.html
|
|
|
-#
|
|
|
-_LOCALE_DIR = os.path.join(os.getenv("GISBASE"), "locale")
|
|
|
-if six.PY2:
|
|
|
- gettext.install("grasslibs", _LOCALE_DIR, unicode=True)
|
|
|
- gettext.install("grassmods", _LOCALE_DIR, unicode=True)
|
|
|
- gettext.install("grasswxpy", _LOCALE_DIR, unicode=True)
|
|
|
-else:
|
|
|
- gettext.install("grasslibs", _LOCALE_DIR)
|
|
|
- gettext.install("grassmods", _LOCALE_DIR)
|
|
|
- gettext.install("grasswxpy", _LOCALE_DIR)
|
|
|
+
|
|
|
+
|
|
|
+def _translate(text):
|
|
|
+ """Get translated version of text
|
|
|
+
|
|
|
+ The first call to this function initializes translations, i.e., simply importing
|
|
|
+ the package does not require the translations to be availabe. However, a first
|
|
|
+ call to translate a message will do the initialization first before translating
|
|
|
+ the message.
|
|
|
+ """
|
|
|
+ if _translate.translation is None:
|
|
|
+ # Initialize translations if needed. This should happen (only) during the
|
|
|
+ # the first call of the function.
|
|
|
+ try:
|
|
|
+ import gettext # pylint: disable=import-outside-toplevel
|
|
|
+
|
|
|
+ gisbase = os.environ["GISBASE"]
|
|
|
+ locale_dir = os.path.join(gisbase, "locale")
|
|
|
+ # With fallback set to True, not finding the translations files for
|
|
|
+ # a language or domain results in a use of null translation, so this
|
|
|
+ # does not raise an exception even if the locale settings is broken
|
|
|
+ # or the translation files were not installed.
|
|
|
+ fallback = True
|
|
|
+ translation = gettext.translation(
|
|
|
+ "grasslibs", locale_dir, fallback=fallback
|
|
|
+ )
|
|
|
+ # Add other domains as fallback.
|
|
|
+ translation.add_fallback(
|
|
|
+ gettext.translation("grassmods", locale_dir, fallback=fallback)
|
|
|
+ )
|
|
|
+ translation.add_fallback(
|
|
|
+ gettext.translation("grasswxpy", locale_dir, fallback=fallback)
|
|
|
+ )
|
|
|
+ # Store the resulting translation object.
|
|
|
+ _translate.translation = translation
|
|
|
+ except (KeyError, ImportError):
|
|
|
+ # If the environmental variable is not set or there is no gettext,
|
|
|
+ # use null translation as an ultimate fallback.
|
|
|
+ _translate.translation = gettext.NullTranslations()
|
|
|
+ return _translate.translation.gettext(text)
|
|
|
+
|
|
|
+
|
|
|
+# Initialize the translation attribute of the translate function to indicate
|
|
|
+# that the translations are not initialized.
|
|
|
+_translate.translation = None
|
|
|
+
|
|
|
+_builtins.__dict__["_"] = _translate
|
|
|
|
|
|
|
|
|
__all__ = ["script", "temporal"]
|