Replace deprecated locale.getdefaultlocale()

gettext.translation() can do most of the work for us, calling
gettext.find() and with fallback=True returning a NullTranslations if no
catalog is found.

The do_unicode parameter of install_builtin() should have been removed
in:
384e1e196 ("Remove Python 2 gettext builtin installation", 2020-04-15)
This commit is contained in:
Chris Mayo 2023-01-13 19:23:56 +00:00
parent ec9386d002
commit efc368e6ea
4 changed files with 12 additions and 130 deletions

View file

@ -93,7 +93,7 @@ def get_link_pat(arg, strict=False):
}
def init_i18n(loc=None):
def init_i18n():
"""Initialize i18n with the configured locale dir. The environment
variable LOCPATH can also specify a locale dir.
@ -104,7 +104,7 @@ def init_i18n(loc=None):
else:
# Need Python 3.9 for importlib.resources.files
locdir = os.path.join(__path__[0], 'data', 'locale')
i18n.init(COMMAND_NAME, locdir, loc=loc)
i18n.init(COMMAND_NAME, locdir)
# install translated log level names
import logging

View file

@ -113,7 +113,6 @@ def print_locale_info(out=stderr):
"""Print locale info."""
for key in ("LANGUAGE", "LC_ALL", "LC_CTYPE", "LANG"):
print_env_info(key, out=out)
print(_("Default locale:"), i18n.get_locale(), file=out)
# Environment variables influencing the interpreter execution

View file

@ -18,144 +18,26 @@ Application internationalization support.
"""
# i18n support
import os
import locale
import gettext
import sys
import codecs
# more supported languages are added in init()
supported_languages = {'en'}
default_language = default_encoding = None
default_directory = None
default_domain = None
default_encoding = None
def install_builtin(translator, do_unicode):
"""Install _() and _n() gettext methods into default namespace."""
def init(domain, directory):
"""Initialize this gettext i18n module and install the gettext translator class."""
global default_encoding
default_encoding = locale.getpreferredencoding(False)
translator = gettext.translation(domain, localedir=directory, fallback=True)
import builtins
builtins.__dict__['_'] = translator.gettext
# also install ngettext
builtins.__dict__['_n'] = translator.ngettext
class Translator(gettext.GNUTranslations):
"""A translation class always installing its gettext methods into the
default namespace."""
def install(self, do_unicode):
"""Install gettext methods into the default namespace."""
install_builtin(self, do_unicode)
class NullTranslator(gettext.NullTranslations):
"""A dummy translation class always installing its gettext methods into
the default namespace."""
def install(self, do_unicode):
"""Install gettext methods into the default namespace."""
install_builtin(self, do_unicode)
def init(domain, directory, loc=None):
"""Initialize this gettext i18n module. Searches for supported languages
and installs the gettext translator class."""
global default_language, default_encoding, default_domain, default_directory
default_directory = directory
default_domain = domain
if os.path.isdir(directory):
# get supported languages
for lang in os.listdir(directory):
path = os.path.join(directory, lang, 'LC_MESSAGES')
mo_file = os.path.join(path, f"{domain}.mo")
if os.path.exists(mo_file):
supported_languages.add(lang)
if loc is None:
loc, encoding = get_locale()
else:
encoding = get_locale()[1]
if loc in supported_languages:
default_language = loc
else:
default_language = "en"
# Even if the default language is not supported, the encoding should
# be installed. Otherwise the Python installation is borked.
default_encoding = encoding
install_language(default_language)
def install_language(language):
"""Install translation service routines into default namespace."""
translator = get_translator(
default_domain, default_directory, languages=[get_lang(language)], fallback=True
)
do_unicode = True
translator.install(do_unicode)
def get_translator(
domain,
directory,
languages=None,
translatorklass=Translator,
fallback=False,
fallbackklass=NullTranslator,
):
"""Search the appropriate GNUTranslations class."""
translator = gettext.translation(
domain,
localedir=directory,
languages=languages,
class_=translatorklass,
fallback=fallback,
)
if not isinstance(translator, gettext.GNUTranslations) and fallbackklass:
translator = fallbackklass()
return translator
def get_lang(lang):
"""Return lang if it is supported, or the default language."""
if lang in supported_languages:
return lang
return default_language
def get_locale():
"""Search the default platform locale and norm it.
@returns (locale, encoding)
@rtype (string, string)"""
try:
loc, encoding = locale.getdefaultlocale()
except ValueError:
# locale configuration is broken - ignore that
loc, encoding = None, None
if loc is None:
loc = "C"
else:
loc = norm_locale(loc)
if encoding is None:
encoding = "ascii"
return (loc, encoding)
def norm_locale(loc):
"""Normalize a locale."""
loc = locale.normalize(loc)
# split up the locale into its base components
pos = loc.find('@')
if pos >= 0:
loc = loc[:pos]
pos = loc.find('.')
if pos >= 0:
loc = loc[:pos]
pos = loc.find('_')
if pos >= 0:
loc = loc[:pos]
return loc
def get_encoded_writer(out=sys.stdout, encoding=None, errors='replace'):
"""Get wrapped output writer with given encoding and error handling."""
if encoding is None:

View file

@ -34,7 +34,8 @@ class TestBase(unittest.TestCase):
"""Ensure the current locale setting is the default.
Otherwise, warnings will get translated and will break tests."""
super().setUp()
init_i18n(loc="C")
os.environ["LANG"] = "C"
init_i18n()
def run(cmd, verbosity=0, **kwargs):