8000 gh-64243: Implement `locale.getlocale` fall back in `gettext.find` by StanFromIreland · Pull Request #131477 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-64243: Implement locale.getlocale fall back in gettext.find #131477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
Review
  • Loading branch information
StanFromIreland committed Jun 19, 2025
commit c40039fa3dbc4a120da243d1e79e8fcbe8d27aa7
4 changes: 2 additions & 2 deletions Doc/library/gettext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ install themselves in the built-in namespace as the function :func:`!_`.

If *localedir* is not given, then the default system locale directory is used.
[#]_ If *languages* is not given, then the environment variable :envvar:`LANGUAGE`
is searched, it falls back to :func:`locale.setlocale`, which in turn falls
back to the environment variables :envvar:`LC_ALL`, :envvar:`LC_MESSAGES`, and
is searched, it falls back to the current locale or to the environment
variables :envvar:`LC_ALL`, :envvar:`LC_MESSAGES`, and
:envvar:`LANG` where the first one returning a non-empty value is used for the
*languages* variable. The environment variables should contain a colon separated
list of languages, which will be split on the colon to produce the expected list
Expand Down
4 changes: 2 additions & 2 deletions Lib/gettext.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,8 @@ def find(domain, localedir=None, languages=None, all=False):
languages = []
if val := os.environ.get('LANGUAGE'):
languages = val.split(':')
elif (loc := locale.setlocale(locale.LC_MESSAGES)) != (None, None):
languages = [".".join(filter(None, loc))]
elif loc := locale.setlocale(locale.LC_MESSAGES):
languages = loc.split(':')
else:
for envar in ('LC_ALL', 'LC_MESSAGES', 'LANG'):
val = os.environ.get(envar)
Expand Down
59 changes: 46 additions & 13 deletions Lib/test/test_gettext.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import sys

import locale
import os
import base64
import gettext
Expand All @@ -6,8 +9,7 @@
from functools import partial

from test import support
from test.support import os_helper

from test.support import os_helper, run_with_locale

# TODO:
# - Add new tests, for example for "dgettext"
Expand Down Expand Up @@ -736,32 +738,63 @@ def create_mo_file(self, lang):
f.write(GNU_MO_DATA)
return mo_file

def _for_all_vars(self, mo_file, locale):
def _for_all_vars(self, mo_file, locale, expected=True):
for var in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
self.env.set(var, locale)
result = gettext.find("mofile",
localedir=os.path.join(self.tempdir, "locale"))
self.assertEqual(mo_file, result)
if expected:
self.assertEqual(mo_file, result)
else:
self.assertIsNone(result)
self.env.unset(var)

@unittest.mock.patch("locale.setlocale", return_value=(None, None))
@unittest.mock.patch("locale.setlocale", return_value='')
def test_find_with_env_vars(self, patch_getlocale):
# test that find correctly finds the environment variables
# when languages are not supplied
mo_file = self.create_mo_file("ca_ES")
self._for_all_vars(mo_file, "ca_ES")
self._for_all_vars(mo_file, "ca_ES.UTF-8")
self._for_all_vars(mo_file, "ca_ES.UTF-8.mo")
self._for_all_vars(mo_file, "es_ES:ca_ES:fr_FR")
self._for_all_vars(mo_file, "ca_ES@euro")
self._for_all_vars(mo_file, "ca_ES.UTF-8@euro")
self._for_all_vars(mo_file, "ca_ES@valencia")
self._for_all_vars(mo_file, "C", expected=False)
self._for_all_vars(mo_file, "C.UTF-8", expected=False)

@unittest.mock.patch('gettext._expand_lang')
def test_encoding_not_ignored(self, patch_expand_lang):
self.env.set('LANGUAGE', 'ga_IE.UTF-8')
gettext.find("mofile")
patch_expand_lang.assert_any_call('ga_IE.UTF-8')
self.env.unset('LANGUAGE')

def test_find_LANGUAGE_priority(self):
orig = locale.setlocale(locale.LC_MESSAGES)
self.addCleanup(lambda: locale.setlocale(locale.LC_MESSAGES, orig))
self.env.set('LANGUAGE', 'ga_IE')
self.env.set('LC_ALL', 'pt_BR')
locale.setlocale(locale.LC_MESSAGES, 'pt_BR')
mo_file = self.create_mo_file("ga_IE")
self._for_all_vars(mo_file, "ga_IE")
self._for_all_vars(mo_file, "ga_IE.UTF-8")
self._for_all_vars(mo_file, "es_ES:ga_IE:fr_FR")
self._for_all_vars(mo_file, "ga_IE@euro")

result = gettext.find("mofile", localedir=os.path.join(self.tempdir, "locale"))
self.assertEqual(result, mo_file)
locale.setlocale(locale.LC_MESSAGES, orig)

def test_process_vars_override(self):
mo_file = self.create_mo_file("ga_IE")
with unittest.mock.patch("locale.setlocale", return_value=('ga_IE', 'UTF-8')):
orig = locale.setlocale(locale.LC_MESSAGES)
self.addCleanup(lambda: locale.setlocale(locale.LC_MESSAGES, orig))
mo_file = self.create_mo_file("ca_ES")
for loc in ("ca_ES", "ca_ES.UTF-8", "ca_ES@euro", "ca_ES@valencia"):
locale.setlocale(locale.LC_MESSAGES, loc)
result = gettext.find("mofile", localedir=os.path.join(self.tempdir, "locale"))
self.assertEqual(mo_file, result)
with unittest.mock.patch("locale.setlocale", return_value=('ga_IE', None)):
for loc in ("C", "C.UTF-8"):
locale.setlocale(locale.LC_MESSAGES, loc)
result = gettext.find("mofile", localedir=os.path.join(self.tempdir, "locale"))
self.assertEqual(mo_file, result)
self.assertIsNone(result)

def test_find_with_languages(self):
# test that passed languages are used
Expand Down
0