| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,7 +42,7 @@ The :mod:`locale` module defines the following exception and functions: | |||
| 42 | 42 | If *locale* is a pair, it is converted to a locale name using | |
| 43 | 43 | the locale aliasing engine. | |
| 44 | 44 | The language code has the same format as a :ref:`locale name <locale_name>`, | |
| 45 | - but without encoding and ``@``-modifier. | ||
| 45 | + but without encoding. | ||
| 46 | 46 | The language code and encoding can be ``None``. | |
| 47 | 47 | ||
| 48 | 48 | If *locale* is omitted or ``None``, the current setting for *category* is | |
@@ -58,6 +58,9 @@ The :mod:`locale` module defines the following exception and functions: | |||
| 58 | 58 | specified in the :envvar:`LANG` environment variable). If the locale is not | |
| 59 | 59 | changed thereafter, using multithreading should not cause problems. | |
| 60 | 60 | ||
| 61 | + .. versionchanged:: next | ||
| 62 | + Support language codes with ``@``-modifiers. | ||
| 63 | + | ||
| 61 | 64 | ||
| 62 | 65 | .. function:: localeconv() | |
| 63 | 66 | ||
@@ -366,11 +369,15 @@ The :mod:`locale` module defines the following exception and functions: | |||
| 366 | 369 | values except :const:`LC_ALL`. It defaults to :const:`LC_CTYPE`. | |
| 367 | 370 | ||
| 368 | 371 | The language code has the same format as a :ref:`locale name <locale_name>`, | |
| 369 | - but without encoding and ``@``-modifier. | ||
| 372 | + but without encoding. | ||
| 370 | 373 | The language code and encoding may be ``None`` if their values cannot be | |
| 371 | 374 | determined. | |
| 372 | 375 | The "C" locale is represented as ``(None, None)``. | |
| 373 | 376 | ||
| 377 | + .. versionchanged:: next | ||
| 378 | + ``@``-modifier are no longer silently removed, but included in | ||
| 379 | + the language code. | ||
| 380 | + | ||
| 374 | 381 | ||
| 375 | 382 | .. function:: getpreferredencoding(do_setlocale=True) | |
| 376 | 383 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -274,6 +274,15 @@ http.cookies | |||
| 274 | 274 | (Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.) | |
| 275 | 275 | ||
| 276 | 276 | ||
| 277 | + locale | ||
| 278 | + ------ | ||
| 279 | + | ||
| 280 | + * :func:`~locale.setlocale` now supports language codes with ``@``-modifiers. | ||
| 281 | + ``@``-modifier are no longer silently removed in :func:`~locale.getlocale`, | ||
| 282 | + but included in the language code. | ||
| 283 | + (Contributed by Serhiy Storchaka in :gh:`137729`.) | ||
| 284 | + | ||
| 285 | + | ||
| 277 | 286 | math | |
| 278 | 287 | ---- | |
| 279 | 288 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -375,12 +375,14 @@ def _replace_encoding(code, encoding): | |||
| 375 | 375 | def _append_modifier(code, modifier): | |
| 376 | 376 | if modifier == 'euro': | |
| 377 | 377 | if '.' not in code: | |
| 378 | - return code + '.ISO8859-15' | ||
| 378 | + # Linux appears to require keeping the "@euro" modifier in place, | ||
| 379 | + # even when using the ".ISO8859-15" encoding. | ||
| 380 | + return code + '.ISO8859-15@euro' | ||
| 379 | 381 | _, _, encoding = code.partition('.') | |
| 380 | - if encoding in ('ISO8859-15', 'UTF-8'): | ||
| 382 | + if encoding == 'UTF-8': | ||
| 381 | 383 | return code | |
| 382 | 384 | if encoding == 'ISO8859-1': | |
| 383 | - return _replace_encoding(code, 'ISO8859-15') | ||
| 385 | + code = _replace_encoding(code, 'ISO8859-15') | ||
| 384 | 386 | return code + '@' + modifier | |
| 385 | 387 | ||
| 386 | 388 | def normalize(localename): | |
@@ -485,13 +487,18 @@ def _parse_localename(localename): | |||
| 485 | 487 | # Deal with locale modifiers | |
| 486 | 488 | code, modifier = code.split('@', 1) | |
| 487 | 489 | if modifier == 'euro' and '.' not in code: | |
| 488 | - # Assume Latin-9 for @euro locales. This is bogus, | ||
| 489 | - # since some systems may use other encodings for these | ||
| 490 | - # locales. Also, we ignore other modifiers. | ||
| 491 | - return code, 'iso-8859-15' | ||
| 490 | + # Assume ISO8859-15 for @euro locales. Do note that some systems | ||
| 491 | + # may use other encodings for these locales, so this may not always | ||
| 492 | + # be correct. | ||
| 493 | + return code + '@euro', 'ISO8859-15' | ||
| 494 | + else: | ||
| 495 | + modifier = '' | ||
| 492 | 496 | ||
| 493 | 497 | if '.' in code: | |
| 494 | - return tuple(code.split('.')[:2]) | ||
| 498 | + code, encoding = code.split('.')[:2] | ||
| 499 | + if modifier: | ||
| 500 | + code += '@' + modifier | ||
| 501 | + return code, encoding | ||
| 495 | 502 | elif code == 'C': | |
| 496 | 503 | return None, None | |
| 497 | 504 | elif code == 'UTF-8': | |
@@ -516,7 +523,14 @@ def _build_localename(localetuple): | |||
| 516 | 523 | if encoding is None: | |
| 517 | 524 | return language | |
| 518 | 525 | else: | |
| 519 | - return language + '.' + encoding | ||
| 526 | + if '@' in language: | ||
| 527 | + language, modifier = language.split('@', 1) | ||
| 528 | + else: | ||
| 529 | + modifier = '' | ||
| 530 | + localename = language + '.' + encoding | ||
| 531 | + if modifier: | ||
| 532 | + localename += '@' + modifier | ||
| 533 | + return localename | ||
| 520 | 534 | except (TypeError, ValueError): | |
| 521 | 535 | raise TypeError('Locale must be None, a string, or an iterable of ' | |
| 522 | 536 | 'two strings -- language code, encoding.') from None | |
@@ -888,6 +902,12 @@ def getpreferredencoding(do_setlocale=True): | |||
| 888 | 902 | # SS 2025-06-10: | |
| 889 | 903 | # Remove 'c.utf8' -> 'en_US.UTF-8' because 'en_US.UTF-8' does not exist | |
| 890 | 904 | # on all platforms. | |
| 905 | + # | ||
| 906 | + # SS 2025-07-30: | ||
| 907 | + # Remove conflicts with GNU libc. | ||
| 908 | + # | ||
| 909 | + # removed 'el_gr@euro' | ||
| 910 | + # removed 'uz_uz@cyrillic' | ||
| 891 | 911 | ||
| 892 | 912 | locale_alias = { | |
| 893 | 913 | 'a3': 'az_AZ.KOI8-C', | |
@@ -1021,7 +1041,6 @@ def getpreferredencoding(do_setlocale=True): | |||
| 1021 | 1041 | 'el': 'el_GR.ISO8859-7', | |
| 1022 | 1042 | 'el_cy': 'el_CY.ISO8859-7', | |
| 1023 | 1043 | 'el_gr': 'el_GR.ISO8859-7', | |
| 1024 | - 'el_gr@euro': 'el_GR.ISO8859-15', | ||
| 1025 | 1044 | 'en': 'en_US.ISO8859-1', | |
| 1026 | 1045 | 'en_ag': 'en_AG.UTF-8', | |
| 1027 | 1046 | 'en_au': 'en_AU.ISO8859-1', | |
@@ -1456,7 +1475,6 @@ def getpreferredencoding(do_setlocale=True): | |||
| 1456 | 1475 | 'ur_pk': 'ur_PK.CP1256', | |
| 1457 | 1476 | 'uz': 'uz_UZ.UTF-8', | |
| 1458 | 1477 | 'uz_uz': 'uz_UZ.UTF-8', | |
| 1459 | - 'uz_uz@cyrillic': 'uz_UZ.UTF-8', | ||
| 1460 | 1478 | 've': 've_ZA.UTF-8', | |
| 1461 | 1479 | 've_za': 've_ZA.UTF-8', | |
| 1462 | 1480 | 'vi': 'vi_VN.TCVN', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,5 @@ | |||
| 1 | 1 | from decimal import Decimal | |
| 2 | + from test import support | ||
| 2 | 3 | from test.support import cpython_only, verbose, is_android, linked_to_musl, os_helper | |
| 3 | 4 | from test.support.warnings_helper import check_warnings | |
| 4 | 5 | from test.support.import_helper import ensure_lazy_imports, import_fresh_module | |
@@ -425,8 +426,8 @@ def test_hyphenated_encoding(self): | |||
| 425 | 426 | self.check('cs_CZ.ISO8859-2', 'cs_CZ.ISO8859-2') | |
| 426 | 427 | ||
| 427 | 428 | def test_euro_modifier(self): | |
| 428 | - self.check('de_DE@euro', 'de_DE.ISO8859-15') | ||
| 429 | - self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15') | ||
| 429 | + self.check('de_DE@euro', 'de_DE.ISO8859-15@euro') | ||
| 430 | + self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15@euro') | ||
| 430 | 431 | self.check('de_DE.utf8@euro', 'de_DE.UTF-8') | |
| 431 | 432 | ||
| 432 | 433 | def test_latin_modifier(self): | |
@@ -534,6 +535,105 @@ def test_setlocale_long_encoding(self): | |||
| 534 | 535 | with self.assertRaises(locale.Error): | |
| 535 | 536 | locale.setlocale(locale.LC_ALL, loc2) | |
| 536 | 537 | ||
| 538 | + @support.subTests('localename,localetuple', [ | ||
| 539 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso885915')), | ||
| 540 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso88591')), | ||
| 541 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')), | ||
| 542 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-1')), | ||
| 543 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', None)), | ||
| 544 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso885915')), | ||
| 545 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso88591')), | ||
| 546 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')), | ||
| 547 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-1')), | ||
| 548 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', None)), | ||
| 549 | + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'iso88597')), | ||
| 550 | + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')), | ||
| 551 | + ('el_GR.ISO8859-7@euro', ('el_GR@euro', None)), | ||
| 552 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso885915')), | ||
| 553 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso88591')), | ||
| 554 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')), | ||
| 555 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-1')), | ||
| 556 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', None)), | ||
| 557 | + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'utf8')), | ||
| 558 | + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')), | ||
| 559 | + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', None)), | ||
| 560 | + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'utf8')), | ||
| 561 | + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')), | ||
| 562 | + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', None)), | ||
| 563 | + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'utf8')), | ||
| 564 | + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')), | ||
| 565 | + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', None)), | ||
| 566 | + ('be_BY.UTF-8@latin', ('be_BY@latin', 'utf8')), | ||
| 567 | + ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')), | ||
| 568 | + ('be_BY.UTF-8@latin', ('be_BY@latin', None)), | ||
| 569 | + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'utf8')), | ||
| 570 | + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')), | ||
| 571 | + ('sr_RS.UTF-8@latin', ('sr_RS@latin', None)), | ||
| 572 | + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'utf8')), | ||
| 573 | + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')), | ||
| 574 | + ('ug_CN.UTF-8@latin', ('ug_CN@latin', None)), | ||
| 575 | + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'utf8')), | ||
| 576 | + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), | ||
| 577 | + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', None)), | ||
| 578 | + ]) | ||
| 579 | + def test_setlocale_with_modifier(self, localename, localetuple): | ||
| 580 | + try: | ||
| 581 | + locale.setlocale(locale.LC_CTYPE, localename) | ||
| 582 | + except locale.Error as exc: | ||
| 583 | + self.skipTest(str(exc)) | ||
| 584 | + loc = locale.setlocale(locale.LC_CTYPE, localetuple) | ||
| 585 | + self.assertEqual(loc, localename) | ||
| 586 | + | ||
| 587 | + loctuple = locale.getlocale(locale.LC_CTYPE) | ||
| 588 | + loc = locale.setlocale(locale.LC_CTYPE, loctuple) | ||
| 589 | + self.assertEqual(loc, localename) | ||
| 590 | + | ||
| 591 | + @support.subTests('localename,localetuple', [ | ||
| 592 | + ('fr_FR.iso885915@euro', ('fr_FR@euro', 'ISO8859-15')), | ||
| 593 | + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')), | ||
| 594 | + ('fr_FR@euro', ('fr_FR@euro', 'ISO8859-15')), | ||
| 595 | + ('de_DE.iso885915@euro', ('de_DE@euro', 'ISO8859-15')), | ||
| 596 | + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')), | ||
| 597 | + ('de_DE@euro', ('de_DE@euro', 'ISO8859-15')), | ||
| 598 | + ('el_GR.iso88597@euro', ('el_GR@euro', 'ISO8859-7')), | ||
| 599 | + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')), | ||
| 600 | + ('el_GR@euro', ('el_GR@euro', 'ISO8859-7')), | ||
| 601 | + ('ca_ES.iso885915@euro', ('ca_ES@euro', 'ISO8859-15')), | ||
| 602 | + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')), | ||
| 603 | + ('ca_ES@euro', ('ca_ES@euro', 'ISO8859-15')), | ||
| 604 | + ('ca_ES.utf8@valencia', ('ca_ES@valencia', 'UTF-8')), | ||
| 605 | + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')), | ||
| 606 | + ('ca_ES@valencia', ('ca_ES@valencia', 'UTF-8')), | ||
| 607 | + ('ks_IN.utf8@devanagari', ('ks_IN@devanagari', 'UTF-8')), | ||
| 608 | + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')), | ||
| 609 | + ('ks_IN@devanagari', ('ks_IN@devanagari', 'UTF-8')), | ||
| 610 | + ('sd_IN.utf8@devanagari', ('sd_IN@devanagari', 'UTF-8')), | ||
| 611 | + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')), | ||
| 612 | + ('sd_IN@devanagari', ('sd_IN@devanagari', 'UTF-8')), | ||
| 613 | + ('be_BY.utf8@latin', ('be_BY@latin', 'UTF-8')), | ||
| 614 | + ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')), | ||
| 615 | + ('be_BY@latin', ('be_BY@latin', 'UTF-8')), | ||
| 616 | + ('sr_RS.utf8@latin', ('sr_RS@latin', 'UTF-8')), | ||
| 617 | + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')), | ||
| 618 | + ('sr_RS@latin', ('sr_RS@latin', 'UTF-8')), | ||
| 619 | + ('ug_CN.utf8@latin', ('ug_CN@latin', 'UTF-8')), | ||
| 620 | + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')), | ||
| 621 | + ('ug_CN@latin', ('ug_CN@latin', 'UTF-8')), | ||
| 622 | + ('uz_UZ.utf8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), | ||
| 623 | + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), | ||
| 624 | + ('uz_UZ@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), | ||
| 625 | + ]) | ||
| 626 | + def test_getlocale_with_modifier(self, localename, localetuple): | ||
| 627 | + try: | ||
| 628 | + locale.setlocale(locale.LC_CTYPE, localename) | ||
| 629 | + except locale.Error as exc: | ||
| 630 | + self.skipTest(str(exc)) | ||
| 631 | + loctuple = locale.getlocale(locale.LC_CTYPE) | ||
| 632 | + self.assertEqual(loctuple, localetuple) | ||
| 633 | + | ||
| 634 | + locale.setlocale(locale.LC_CTYPE, loctuple) | ||
| 635 | + self.assertEqual(locale.getlocale(locale.LC_CTYPE), localetuple) | ||
| 636 | + | ||
| 537 | 637 | ||
| 538 | 638 | class TestMiscellaneous(unittest.TestCase): | |
| 539 | 639 | def test_defaults_UTF8(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + :func:`locale.setlocale` now supports language codes with ``@``-modifiers. | ||
| 2 | + ``@``-modifier are no longer silently removed in :func:`locale.getlocale`, | ||
| 3 | + but included in the language code. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,13 +44,24 @@ def parse(filename): | |||
| 44 | 44 | # Ignore one letter locale mappings (except for 'c') | |
| 45 | 45 | if len(locale) == 1 and locale != 'c': | |
| 46 | 46 | continue | |
| 47 | + if '@' in locale and '@' not in alias: | ||
| 48 | + # Do not simply remove the "@euro" modifier. | ||
| 49 | + # Glibc generates separate locales with the "@euro" modifier, and | ||
| 50 | + # not always generates a locale without it with the same encoding. | ||
| 51 | + # It can also affect collation. | ||
| 52 | + if locale.endswith('@euro') and not locale.endswith('.utf-8@euro'): | ||
| 53 | + alias += '@euro' | ||
| 47 | 54 | # Normalize encoding, if given | |
| 48 | 55 | if '.' in locale: | |
| 49 | 56 | lang, encoding = locale.split('.')[:2] | |
| 50 | 57 | encoding = encoding.replace('-', '') | |
| 51 | 58 | encoding = encoding.replace('_', '') | |
| 52 | 59 | locale = lang + '.' + encoding | |
| 53 | 60 | data[locale] = alias | |
| 61 | + # Conflict with glibc. | ||
| 62 | + data.pop('el_gr@euro', None) | ||
| 63 | + data.pop('uz_uz@cyrillic', None) | ||
| 64 | + data.pop('uz_uz.utf8@cyrillic', None) | ||
| 54 | 65 | return data | |
| 55 | 66 | ||
| 56 | 67 | def parse_glibc_supported(filename): | |
@@ -81,7 +92,7 @@ def parse_glibc_supported(filename): | |||
| 81 | 92 | # Add an encoding to alias | |
| 82 | 93 | alias, _, modifier = alias.partition('@') | |
| 83 | 94 | alias = _locale._replace_encoding(alias, alias_encoding) | |
| 84 | - if modifier and not (modifier == 'euro' and alias_encoding == 'ISO-8859-15'): | ||
| 95 | + if modifier: | ||
| 85 | 96 | alias += '@' + modifier | |
| 86 | 97 | data[locale] = alias | |
| 87 | 98 | return data | |
| Back | FazBrowse Home | New Git URL |
0 commit comments