Fixed #33565 -- Improved locale format validation for the makemessages command.
This commit is contained in:
parent
49b470b918
commit
c32858a8ce
1
AUTHORS
1
AUTHORS
|
@ -829,6 +829,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Rodrigo Pinheiro Marques de Araújo <fenrrir@gmail.com>
|
Rodrigo Pinheiro Marques de Araújo <fenrrir@gmail.com>
|
||||||
Rohith P R <https://rohithpr.com>
|
Rohith P R <https://rohithpr.com>
|
||||||
Romain Garrigues <romain.garrigues.cs@gmail.com>
|
Romain Garrigues <romain.garrigues.cs@gmail.com>
|
||||||
|
Ronnie van den Crommenacker
|
||||||
Ronny Haryanto <https://ronny.haryan.to/>
|
Ronny Haryanto <https://ronny.haryan.to/>
|
||||||
Ross Poulton <ross@rossp.org>
|
Ross Poulton <ross@rossp.org>
|
||||||
Roxane Bellot <https://github.com/roxanebellot/>
|
Roxane Bellot <https://github.com/roxanebellot/>
|
||||||
|
|
|
@ -40,6 +40,10 @@ def check_programs(*programs):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_locale(locale):
|
||||||
|
return re.match(r"^[a-z]+$", locale) or re.match(r"^[a-z]+_[A-Z].*$", locale)
|
||||||
|
|
||||||
|
|
||||||
@total_ordering
|
@total_ordering
|
||||||
class TranslatableFile:
|
class TranslatableFile:
|
||||||
def __init__(self, dirpath, file_name, locale_dir):
|
def __init__(self, dirpath, file_name, locale_dir):
|
||||||
|
@ -427,14 +431,41 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
# Build po files for each selected locale
|
# Build po files for each selected locale
|
||||||
for locale in locales:
|
for locale in locales:
|
||||||
if "-" in locale:
|
if not is_valid_locale(locale):
|
||||||
self.stdout.write(
|
# Try to guess what valid locale it could be
|
||||||
"invalid locale %s, did you mean %s?"
|
# Valid examples are: en_GB, shi_Latn_MA and nl_NL-x-informal
|
||||||
% (
|
|
||||||
locale,
|
# Search for characters followed by a non character (i.e. separator)
|
||||||
locale.replace("-", "_"),
|
match = re.match(
|
||||||
),
|
r"^(?P<language>[a-zA-Z]+)"
|
||||||
|
r"(?P<separator>[^a-zA-Z])"
|
||||||
|
r"(?P<territory>.+)$",
|
||||||
|
locale,
|
||||||
)
|
)
|
||||||
|
if match:
|
||||||
|
locale_parts = match.groupdict()
|
||||||
|
language = locale_parts["language"].lower()
|
||||||
|
territory = (
|
||||||
|
locale_parts["territory"][:2].upper()
|
||||||
|
+ locale_parts["territory"][2:]
|
||||||
|
)
|
||||||
|
proposed_locale = f"{language}_{territory}"
|
||||||
|
else:
|
||||||
|
# It could be a language in uppercase
|
||||||
|
proposed_locale = locale.lower()
|
||||||
|
|
||||||
|
# Recheck if the proposed locale is valid
|
||||||
|
if is_valid_locale(proposed_locale):
|
||||||
|
self.stdout.write(
|
||||||
|
"invalid locale %s, did you mean %s?"
|
||||||
|
% (
|
||||||
|
locale,
|
||||||
|
proposed_locale,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.stdout.write("invalid locale %s" % locale)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
if self.verbosity > 0:
|
if self.verbosity > 0:
|
||||||
self.stdout.write("processing locale %s" % locale)
|
self.stdout.write("processing locale %s" % locale)
|
||||||
|
|
|
@ -155,7 +155,8 @@ Logging
|
||||||
Management Commands
|
Management Commands
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* :djadmin:`makemessages` command now supports locales with private sub-tags
|
||||||
|
such as ``nl_NL-x-informal``.
|
||||||
|
|
||||||
Migrations
|
Migrations
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
|
@ -175,7 +175,43 @@ class BasicExtractorTests(ExtractorTests):
|
||||||
self.assertIn("processing locale de", out.getvalue())
|
self.assertIn("processing locale de", out.getvalue())
|
||||||
self.assertIs(Path(self.PO_FILE).exists(), True)
|
self.assertIs(Path(self.PO_FILE).exists(), True)
|
||||||
|
|
||||||
def test_invalid_locale(self):
|
def test_valid_locale_with_country(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["en_GB"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertNotIn("invalid locale en_GB", out.getvalue())
|
||||||
|
self.assertIn("processing locale en_GB", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/en_GB/LC_MESSAGES/django.po").exists(), True)
|
||||||
|
|
||||||
|
def test_valid_locale_tachelhit_latin_morocco(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["shi_Latn_MA"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertNotIn("invalid locale shi_Latn_MA", out.getvalue())
|
||||||
|
self.assertIn("processing locale shi_Latn_MA", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/shi_Latn_MA/LC_MESSAGES/django.po").exists(), True)
|
||||||
|
|
||||||
|
def test_valid_locale_private_subtag(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["nl_NL-x-informal"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertNotIn("invalid locale nl_NL-x-informal", out.getvalue())
|
||||||
|
self.assertIn("processing locale nl_NL-x-informal", out.getvalue())
|
||||||
|
self.assertIs(
|
||||||
|
Path("locale/nl_NL-x-informal/LC_MESSAGES/django.po").exists(), True
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_invalid_locale_uppercase(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command("makemessages", locale=["PL"], stdout=out, verbosity=1)
|
||||||
|
self.assertIn("invalid locale PL, did you mean pl?", out.getvalue())
|
||||||
|
self.assertNotIn("processing locale pl", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/pl/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
|
def test_invalid_locale_hyphen(self):
|
||||||
out = StringIO()
|
out = StringIO()
|
||||||
management.call_command(
|
management.call_command(
|
||||||
"makemessages", locale=["pl-PL"], stdout=out, verbosity=1
|
"makemessages", locale=["pl-PL"], stdout=out, verbosity=1
|
||||||
|
@ -184,6 +220,52 @@ class BasicExtractorTests(ExtractorTests):
|
||||||
self.assertNotIn("processing locale pl-PL", out.getvalue())
|
self.assertNotIn("processing locale pl-PL", out.getvalue())
|
||||||
self.assertIs(Path("locale/pl-PL/LC_MESSAGES/django.po").exists(), False)
|
self.assertIs(Path("locale/pl-PL/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
|
def test_invalid_locale_lower_country(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["pl_pl"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertIn("invalid locale pl_pl, did you mean pl_PL?", out.getvalue())
|
||||||
|
self.assertNotIn("processing locale pl_pl", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/pl_pl/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
|
def test_invalid_locale_private_subtag(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["nl-nl-x-informal"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"invalid locale nl-nl-x-informal, did you mean nl_NL-x-informal?",
|
||||||
|
out.getvalue(),
|
||||||
|
)
|
||||||
|
self.assertNotIn("processing locale nl-nl-x-informal", out.getvalue())
|
||||||
|
self.assertIs(
|
||||||
|
Path("locale/nl-nl-x-informal/LC_MESSAGES/django.po").exists(), False
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_invalid_locale_plus(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
"makemessages", locale=["en+GB"], stdout=out, verbosity=1
|
||||||
|
)
|
||||||
|
self.assertIn("invalid locale en+GB, did you mean en_GB?", out.getvalue())
|
||||||
|
self.assertNotIn("processing locale en+GB", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/en+GB/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
|
def test_invalid_locale_end_with_underscore(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command("makemessages", locale=["en_"], stdout=out, verbosity=1)
|
||||||
|
self.assertIn("invalid locale en_", out.getvalue())
|
||||||
|
self.assertNotIn("processing locale en_", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/en_/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
|
def test_invalid_locale_start_with_underscore(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command("makemessages", locale=["_en"], stdout=out, verbosity=1)
|
||||||
|
self.assertIn("invalid locale _en", out.getvalue())
|
||||||
|
self.assertNotIn("processing locale _en", out.getvalue())
|
||||||
|
self.assertIs(Path("locale/_en/LC_MESSAGES/django.po").exists(), False)
|
||||||
|
|
||||||
def test_comments_extractor(self):
|
def test_comments_extractor(self):
|
||||||
management.call_command("makemessages", locale=[LOCALE], verbosity=0)
|
management.call_command("makemessages", locale=[LOCALE], verbosity=0)
|
||||||
self.assertTrue(os.path.exists(self.PO_FILE))
|
self.assertTrue(os.path.exists(self.PO_FILE))
|
||||||
|
|
Loading…
Reference in New Issue