Skip to content

Commit 133c8db

Browse files
authored
Merge branch 'master' into master
2 parents efe2502 + 3ce1e61 commit 133c8db

68 files changed

Lines changed: 1923 additions & 900 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.8.2
3+
rev: v0.9.1
44
hooks:
55
- id: ruff
66
args:

AUTHORS

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Babel is written and maintained by the Babel team and various contributors:
1212
- Philip Jenvey
1313
- benselme
1414
- Isaac Jurado
15+
- Tomas R.
1516
- Tobias Bieniek
1617
- Erick Wilder
1718
- Jonah Lawrence
@@ -20,15 +21,13 @@ Babel is written and maintained by the Babel team and various contributors:
2021
- Kevin Deldycke
2122
- Ville Skyttä
2223
- Jon Dufresne
24+
- Hugo van Kemenade
2325
- Jun Omae
24-
- Hugo
2526
- Heungsub Lee
26-
- Tomas R
2727
- Jakob Schnitzer
2828
- Sachin Paliwal
2929
- Alex Willmer
3030
- Daniel Neuhäuser
31-
- Hugo van Kemenade
3231
- Miro Hrončok
3332
- Cédric Krier
3433
- Luke Plant
@@ -50,6 +49,13 @@ Babel is written and maintained by the Babel team and various contributors:
5049
- Arturas Moskvinas
5150
- Leonardo Pistone
5251
- Hyunjun Kim
52+
- wandrew004
53+
- James McKinney
54+
- Tomáš Hrnčiar
55+
- Gabe Sherman
56+
- mattdiaz007
57+
- Dylan Kiss
58+
- Daniel Roschka
5359
- buhtz
5460
- Bohdan Malomuzh
5561
- Leonid

CHANGES.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,58 @@
11
Babel Changelog
22
===============
33

4+
Version 2.17.0
5+
--------------
6+
7+
Happy 2025! This release is being made from FOSDEM 2025, in Brussels, Belgium.
8+
9+
Thank you to all contributors, new and old,
10+
and here's to another great year of internationalization and localization!
11+
12+
Features
13+
~~~~~~~~
14+
15+
* CLDR: Babel now uses CLDR 46, by @tomasr8 in :gh:`1145`
16+
* Dates: Allow specifying an explicit format in parse_date/parse_time by @tomasr8 in :gh:`1131`
17+
* Dates: More alternate characters are now supported by `format_skeleton`. By @tomasr8 in :gh:`1122`
18+
* Dates: Support short and narrow formats for format_timedelta when using `add_direction`, by @akx in :gh:`1163`
19+
* Messages: .po files now enclose white spaces in filenames like GNU gettext does. By @Dunedan in :gh:`1105`, and @tomasr8 in :gh:`1120`
20+
* Messages: Initial support for `Message.python_brace_format`, by @tomasr8 in :gh:`1169`
21+
* Numbers: LC_MONETARY is now preferred when formatting currencies, by @akx in :gh:`1173`
22+
23+
Bugfixes
24+
~~~~~~~~
25+
26+
* Dates: Make seconds optional in `parse_time` time formats by @tomasr8 in :gh:`1141`
27+
* Dates: Replace `str.index` with `str.find` by @tomasr8 in :gh:`1130`
28+
* Dates: Strip extra leading slashes in `/etc/localtime` by @akx in :gh:`1165`
29+
* Dates: Week numbering and formatting of dates with week numbers was repaired by @jun66j5 in :gh:`1179`
30+
* General: Improve handling for `locale=None` by @akx in :gh:`1164`
31+
* General: Remove redundant assignment in `Catalog.__setitem__` by @tomasr8 in :gh:`1167`
32+
* Messages: Fix extracted lineno with nested calls, by @dylankiss in :gh:`1126`
33+
* Messages: Fix of list index out of range when translations is empty, by @gabe-sherman in :gh:`1135`
34+
* Messages: Fix the way obsolete messages are stored by @tomasr8 in :gh:`1132`
35+
* Messages: Simplify `read_mo` logic regarding `catalog.charset` by @tomasr8 in :gh:`1148`
36+
* Messages: Use the first matching method & options, rather than first matching method & last options, by @jpmckinney in :gh:`1121`
37+
38+
Deprecation and compatibility
39+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40+
41+
* Dates: Fix deprecation warnings for `datetime.utcnow()` by @tomasr8 in :gh:`1119`
42+
* Docs: Adjust docs/conf.py to add compatibility with sphinx 8 by @hrnciar in :gh:`1155`
43+
* General: Import `Literal` from the typing module by @tomasr8 in :gh:`1175`
44+
* General: Replace `OrderedDict` with just `dict` by @tomasr8 in :gh:`1149`
45+
* Messages: Mark `wraptext` deprecated; use `TextWrapper` directly in `write_po` by @akx in :gh:`1140`
46+
47+
Infrastructure
48+
~~~~~~~~~~~~~~
49+
50+
* Add tzdata as dev dependency and sync with tox.ini by @wandrew004 in :gh:`1159`
51+
* Duplicate test code was deleted by @mattdiaz007 in :gh:`1138`
52+
* Increase test coverage of the `python_format` checker by @tomasr8 in :gh:`1176`
53+
* Small cleanups by @akx in :gh:`1160`, :gh:`1166`, :gh:`1170` and :gh:`1172`
54+
* Update CI to use python 3.13 and Ubuntu 24.04 by @tomasr8 in :gh:`1153`
55+
456
Version 2.16.0
557
--------------
658

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2013-2024 by the Babel Team, see AUTHORS for more information.
1+
Copyright (c) 2013-2025 by the Babel Team, see AUTHORS for more information.
22

33
Redistribution and use in source and binary forms, with or without
44
modification, are permitted provided that the following conditions

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Details can be found in the HTML files in the ``docs`` folder.
99

1010
For more information please visit the Babel web site:
1111

12-
http://babel.pocoo.org/
12+
https://babel.pocoo.org/
1313

1414
Join the chat at https://gitter.im/python-babel/babel
1515

babel/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
access to various locale display names, localized number and date
1313
formatting, etc.
1414
15-
:copyright: (c) 2013-2024 by the Babel Team.
15+
:copyright: (c) 2013-2025 by the Babel Team.
1616
:license: BSD, see LICENSE for more details.
1717
"""
1818

@@ -25,11 +25,12 @@
2525
parse_locale,
2626
)
2727

28-
__version__ = '2.16.0'
28+
__version__ = '2.17.0'
2929

3030
__all__ = [
3131
'Locale',
3232
'UnknownLocaleError',
33+
'__version__',
3334
'default_locale',
3435
'get_locale_identifier',
3536
'negotiate_locale',

babel/core.py

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
Core locale representation and locale data access.
66
7-
:copyright: (c) 2013-2024 by the Babel Team.
7+
:copyright: (c) 2013-2025 by the Babel Team.
88
:license: BSD, see LICENSE for more details.
99
"""
1010

@@ -13,16 +13,23 @@
1313
import os
1414
import pickle
1515
from collections.abc import Iterable, Mapping
16-
from typing import TYPE_CHECKING, Any
16+
from typing import TYPE_CHECKING, Any, Literal
1717

1818
from babel import localedata
1919
from babel.plural import PluralRule
2020

21-
__all__ = ['UnknownLocaleError', 'Locale', 'default_locale', 'negotiate_locale',
22-
'parse_locale']
21+
__all__ = [
22+
'Locale',
23+
'UnknownLocaleError',
24+
'default_locale',
25+
'get_global',
26+
'get_locale_identifier',
27+
'negotiate_locale',
28+
'parse_locale',
29+
]
2330

2431
if TYPE_CHECKING:
25-
from typing_extensions import Literal, TypeAlias
32+
from typing_extensions import TypeAlias
2633

2734
_GLOBAL_KEY: TypeAlias = Literal[
2835
"all_currencies",
@@ -259,6 +266,7 @@ def negotiate(
259266
:param preferred: the list of locale identifiers preferred by the user
260267
:param available: the list of locale identifiers available
261268
:param aliases: a dictionary of aliases for locale identifiers
269+
:param sep: separator for parsing; e.g. Windows tends to use '-' instead of '_'.
262270
"""
263271
identifier = negotiate_locale(preferred, available, sep=sep,
264272
aliases=aliases)
@@ -269,7 +277,7 @@ def negotiate(
269277
@classmethod
270278
def parse(
271279
cls,
272-
identifier: str | Locale | None,
280+
identifier: Locale | str | None,
273281
sep: str = '_',
274282
resolve_likely_subtags: bool = True,
275283
) -> Locale:
@@ -286,8 +294,8 @@ def parse(
286294
Locale('de', territory='DE')
287295
288296
If the `identifier` parameter is neither of these, such as `None`
289-
e.g. because a default locale identifier could not be determined,
290-
a `TypeError` is raised:
297+
or an empty string, e.g. because a default locale identifier
298+
could not be determined, a `TypeError` is raised:
291299
292300
>>> Locale.parse(None)
293301
Traceback (most recent call last):
@@ -325,10 +333,23 @@ def parse(
325333
:raise `UnknownLocaleError`: if no locale data is available for the
326334
requested locale
327335
:raise `TypeError`: if the identifier is not a string or a `Locale`
336+
:raise `ValueError`: if the identifier is not a valid string
328337
"""
329338
if isinstance(identifier, Locale):
330339
return identifier
331-
elif not isinstance(identifier, str):
340+
341+
if not identifier:
342+
msg = (
343+
f"Empty locale identifier value: {identifier!r}\n\n"
344+
f"If you didn't explicitly pass an empty value to a Babel function, "
345+
f"this could be caused by there being no suitable locale environment "
346+
f"variables for the API you tried to use."
347+
)
348+
if isinstance(identifier, str):
349+
raise ValueError(msg) # `parse_locale` would raise a ValueError, so let's do that here
350+
raise TypeError(msg)
351+
352+
if not isinstance(identifier, str):
332353
raise TypeError(f"Unexpected value for identifier: {identifier!r}")
333354

334355
parts = parse_locale(identifier, sep=sep)
@@ -562,7 +583,7 @@ def languages(self) -> localedata.LocaleDataDict:
562583
>>> Locale('de', 'DE').languages['ja']
563584
u'Japanisch'
564585
565-
See `ISO 639 <http://www.loc.gov/standards/iso639-2/>`_ for
586+
See `ISO 639 <https://www.loc.gov/standards/iso639-2/>`_ for
566587
more information.
567588
"""
568589
return self._data['languages']
@@ -574,7 +595,7 @@ def scripts(self) -> localedata.LocaleDataDict:
574595
>>> Locale('en', 'US').scripts['Hira']
575596
u'Hiragana'
576597
577-
See `ISO 15924 <http://www.evertype.com/standards/iso15924/>`_
598+
See `ISO 15924 <https://www.unicode.org/iso15924/>`_
578599
for more information.
579600
"""
580601
return self._data['scripts']
@@ -586,7 +607,7 @@ def territories(self) -> localedata.LocaleDataDict:
586607
>>> Locale('es', 'CO').territories['DE']
587608
u'Alemania'
588609
589-
See `ISO 3166 <http://www.iso.org/iso/en/prods-services/iso3166ma/>`_
610+
See `ISO 3166 <https://en.wikipedia.org/wiki/ISO_3166>`_
590611
for more information.
591612
"""
592613
return self._data['territories']
@@ -1068,7 +1089,10 @@ def unit_display_names(self) -> localedata.LocaleDataDict:
10681089
return self._data['unit_display_names']
10691090

10701091

1071-
def default_locale(category: str | None = None, aliases: Mapping[str, str] = LOCALE_ALIASES) -> str | None:
1092+
def default_locale(
1093+
category: str | tuple[str, ...] | list[str] | None = None,
1094+
aliases: Mapping[str, str] = LOCALE_ALIASES,
1095+
) -> str | None:
10721096
"""Returns the system default locale for a given category, based on
10731097
environment variables.
10741098
@@ -1092,11 +1116,22 @@ def default_locale(category: str | None = None, aliases: Mapping[str, str] = LOC
10921116
- ``LC_CTYPE``
10931117
- ``LANG``
10941118
1095-
:param category: one of the ``LC_XXX`` environment variable names
1119+
:param category: one or more of the ``LC_XXX`` environment variable names
10961120
:param aliases: a dictionary of aliases for locale identifiers
10971121
"""
1098-
varnames = (category, 'LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG')
1099-
for name in filter(None, varnames):
1122+
1123+
varnames = ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG')
1124+
if category:
1125+
if isinstance(category, str):
1126+
varnames = (category, *varnames)
1127+
elif isinstance(category, (list, tuple)):
1128+
varnames = (*category, *varnames)
1129+
else:
1130+
raise TypeError(f"Invalid type for category: {category!r}")
1131+
1132+
for name in varnames:
1133+
if not name:
1134+
continue
11001135
locale = os.getenv(name)
11011136
if locale:
11021137
if name == 'LANGUAGE' and ':' in locale:
@@ -1235,6 +1270,8 @@ def parse_locale(
12351270
:raise `ValueError`: if the string does not appear to be a valid locale
12361271
identifier
12371272
"""
1273+
if not identifier:
1274+
raise ValueError("empty locale identifier")
12381275
identifier, _, modifier = identifier.partition('@')
12391276
if '.' in identifier:
12401277
# this is probably the charset/encoding, which we don't care about

0 commit comments

Comments
 (0)