def _append_modifier(code, modifier):
if modifier == 'euro':
if '.' not in code:
- return code + '.ISO8859-15'
+ # Linux appears to require keeping the "@euro" modifier in place,
+ # even when using the ".ISO8859-15" encoding.
+ return code + '.ISO8859-15@euro'
_, _, encoding = code.partition('.')
- if encoding in ('ISO8859-15', 'UTF-8'):
+ if encoding == 'UTF-8':
return code
if encoding == 'ISO8859-1':
- return _replace_encoding(code, 'ISO8859-15')
+ code = _replace_encoding(code, 'ISO8859-15')
return code + '@' + modifier
def normalize(localename):
# Deal with locale modifiers
code, modifier = code.split('@', 1)
if modifier == 'euro' and '.' not in code:
- # Assume Latin-9 for @euro locales. This is bogus,
- # since some systems may use other encodings for these
- # locales. Also, we ignore other modifiers.
- return code, 'iso-8859-15'
+ # Assume ISO8859-15 for @euro locales. Do note that some systems
+ # may use other encodings for these locales, so this may not always
+ # be correct.
+ return code + '@euro', 'ISO8859-15'
+ else:
+ modifier = ''
if '.' in code:
- return tuple(code.split('.')[:2])
+ code, encoding = code.split('.')[:2]
+ if modifier:
+ code += '@' + modifier
+ return code, encoding
elif code == 'C':
return None, None
elif code == 'UTF-8':
if encoding is None:
return language
else:
- return language + '.' + encoding
+ if '@' in language:
+ language, modifier = language.split('@', 1)
+ else:
+ modifier = ''
+ localename = language + '.' + encoding
+ if modifier:
+ localename += '@' + modifier
+ return localename
except (TypeError, ValueError):
raise TypeError('Locale must be None, a string, or an iterable of '
'two strings -- language code, encoding.') from None
# SS 2025-06-10:
# Remove 'c.utf8' -> 'en_US.UTF-8' because 'en_US.UTF-8' does not exist
# on all platforms.
+#
+# SS 2025-07-30:
+# Remove conflicts with GNU libc.
+#
+# removed 'el_gr@euro'
+# removed 'uz_uz@cyrillic'
locale_alias = {
'a3': 'az_AZ.KOI8-C',
'el': 'el_GR.ISO8859-7',
'el_cy': 'el_CY.ISO8859-7',
'el_gr': 'el_GR.ISO8859-7',
- 'el_gr@euro': 'el_GR.ISO8859-15',
'en': 'en_US.ISO8859-1',
'en_ag': 'en_AG.UTF-8',
'en_au': 'en_AU.ISO8859-1',
'ur_pk': 'ur_PK.CP1256',
'uz': 'uz_UZ.UTF-8',
'uz_uz': 'uz_UZ.UTF-8',
- 'uz_uz@cyrillic': 'uz_UZ.UTF-8',
've': 've_ZA.UTF-8',
've_za': 've_ZA.UTF-8',
'vi': 'vi_VN.TCVN',
from decimal import Decimal
+from test import support
from test.support import cpython_only, verbose, is_android, linked_to_musl, os_helper
from test.support.warnings_helper import check_warnings
from test.support.import_helper import ensure_lazy_imports, import_fresh_module
self.check('cs_CZ.ISO8859-2', 'cs_CZ.ISO8859-2')
def test_euro_modifier(self):
- self.check('de_DE@euro', 'de_DE.ISO8859-15')
- self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15')
+ self.check('de_DE@euro', 'de_DE.ISO8859-15@euro')
+ self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15@euro')
self.check('de_DE.utf8@euro', 'de_DE.UTF-8')
def test_latin_modifier(self):
with self.assertRaises(locale.Error):
locale.setlocale(locale.LC_ALL, loc2)
+ @support.subTests('localename,localetuple', [
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso885915')),
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso88591')),
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')),
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-1')),
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', None)),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso885915')),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso88591')),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-1')),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', None)),
+ ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'iso88597')),
+ ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')),
+ ('el_GR.ISO8859-7@euro', ('el_GR@euro', None)),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso885915')),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso88591')),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-1')),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', None)),
+ ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'utf8')),
+ ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')),
+ ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', None)),
+ ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'utf8')),
+ ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')),
+ ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', None)),
+ ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'utf8')),
+ ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')),
+ ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', None)),
+ ('be_BY.UTF-8@latin', ('be_BY@latin', 'utf8')),
+ ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')),
+ ('be_BY.UTF-8@latin', ('be_BY@latin', None)),
+ ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'utf8')),
+ ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')),
+ ('sr_RS.UTF-8@latin', ('sr_RS@latin', None)),
+ ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'utf8')),
+ ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')),
+ ('ug_CN.UTF-8@latin', ('ug_CN@latin', None)),
+ ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'utf8')),
+ ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')),
+ ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', None)),
+ ])
+ def test_setlocale_with_modifier(self, localename, localetuple):
+ try:
+ locale.setlocale(locale.LC_CTYPE, localename)
+ except locale.Error as exc:
+ self.skipTest(str(exc))
+ loc = locale.setlocale(locale.LC_CTYPE, localetuple)
+ self.assertEqual(loc, localename)
+
+ loctuple = locale.getlocale(locale.LC_CTYPE)
+ loc = locale.setlocale(locale.LC_CTYPE, loctuple)
+ self.assertEqual(loc, localename)
+
+ @support.subTests('localename,localetuple', [
+ ('fr_FR.iso885915@euro', ('fr_FR@euro', 'ISO8859-15')),
+ ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')),
+ ('fr_FR@euro', ('fr_FR@euro', 'ISO8859-15')),
+ ('de_DE.iso885915@euro', ('de_DE@euro', 'ISO8859-15')),
+ ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')),
+ ('de_DE@euro', ('de_DE@euro', 'ISO8859-15')),
+ ('el_GR.iso88597@euro', ('el_GR@euro', 'ISO8859-7')),
+ ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')),
+ ('el_GR@euro', ('el_GR@euro', 'ISO8859-7')),
+ ('ca_ES.iso885915@euro', ('ca_ES@euro', 'ISO8859-15')),
+ ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')),
+ ('ca_ES@euro', ('ca_ES@euro', 'ISO8859-15')),
+ ('ca_ES.utf8@valencia', ('ca_ES@valencia', 'UTF-8')),
+ ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')),
+ ('ca_ES@valencia', ('ca_ES@valencia', 'UTF-8')),
+ ('ks_IN.utf8@devanagari', ('ks_IN@devanagari', 'UTF-8')),
+ ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')),
+ ('ks_IN@devanagari', ('ks_IN@devanagari', 'UTF-8')),
+ ('sd_IN.utf8@devanagari', ('sd_IN@devanagari', 'UTF-8')),
+ ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')),
+ ('sd_IN@devanagari', ('sd_IN@devanagari', 'UTF-8')),
+ ('be_BY.utf8@latin', ('be_BY@latin', 'UTF-8')),
+ ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')),
+ ('be_BY@latin', ('be_BY@latin', 'UTF-8')),
+ ('sr_RS.utf8@latin', ('sr_RS@latin', 'UTF-8')),
+ ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')),
+ ('sr_RS@latin', ('sr_RS@latin', 'UTF-8')),
+ ('ug_CN.utf8@latin', ('ug_CN@latin', 'UTF-8')),
+ ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')),
+ ('ug_CN@latin', ('ug_CN@latin', 'UTF-8')),
+ ('uz_UZ.utf8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')),
+ ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')),
+ ('uz_UZ@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')),
+ ])
+ def test_getlocale_with_modifier(self, localename, localetuple):
+ try:
+ locale.setlocale(locale.LC_CTYPE, localename)
+ except locale.Error as exc:
+ self.skipTest(str(exc))
+ loctuple = locale.getlocale(locale.LC_CTYPE)
+ self.assertEqual(loctuple, localetuple)
+
+ locale.setlocale(locale.LC_CTYPE, loctuple)
+ self.assertEqual(locale.getlocale(locale.LC_CTYPE), localetuple)
+
class TestMiscellaneous(unittest.TestCase):
def test_defaults_UTF8(self):