Patch by Claude for #16084.
This commit is contained in:
parent
b9c8bbf372
commit
2babab0bb3
|
@ -19,25 +19,28 @@ STATUS_OK = 0
|
||||||
|
|
||||||
@total_ordering
|
@total_ordering
|
||||||
class TranslatableFile(object):
|
class TranslatableFile(object):
|
||||||
def __init__(self, dirpath, file_name):
|
def __init__(self, dirpath, file_name, locale_dir):
|
||||||
self.file = file_name
|
self.file = file_name
|
||||||
self.dirpath = dirpath
|
self.dirpath = dirpath
|
||||||
|
self.locale_dir = locale_dir
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TranslatableFile: %s>" % os.sep.join([self.dirpath, self.file])
|
return "<TranslatableFile: %s>" % os.sep.join([self.dirpath, self.file])
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.dirpath == other.dirpath and self.file == other.file
|
return self.path == other.path
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if self.dirpath == other.dirpath:
|
return self.path < other.path
|
||||||
return self.file < other.file
|
|
||||||
return self.dirpath < other.dirpath
|
|
||||||
|
|
||||||
def process(self, command, potfile, domain, keep_pot=False):
|
@property
|
||||||
|
def path(self):
|
||||||
|
return os.path.join(self.dirpath, self.file)
|
||||||
|
|
||||||
|
def process(self, command, domain):
|
||||||
"""
|
"""
|
||||||
Extract translatable literals from self.file for :param domain:
|
Extract translatable literals from self.file for :param domain:,
|
||||||
creating or updating the :param potfile: POT file.
|
creating or updating the POT file.
|
||||||
|
|
||||||
Uses the xgettext GNU gettext utility.
|
Uses the xgettext GNU gettext utility.
|
||||||
"""
|
"""
|
||||||
|
@ -91,8 +94,6 @@ class TranslatableFile(object):
|
||||||
if status != STATUS_OK:
|
if status != STATUS_OK:
|
||||||
if is_templatized:
|
if is_templatized:
|
||||||
os.unlink(work_file)
|
os.unlink(work_file)
|
||||||
if not keep_pot and os.path.exists(potfile):
|
|
||||||
os.unlink(potfile)
|
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
"errors happened while running xgettext on %s\n%s" %
|
"errors happened while running xgettext on %s\n%s" %
|
||||||
(self.file, errors))
|
(self.file, errors))
|
||||||
|
@ -100,11 +101,14 @@ class TranslatableFile(object):
|
||||||
# Print warnings
|
# Print warnings
|
||||||
command.stdout.write(errors)
|
command.stdout.write(errors)
|
||||||
if msgs:
|
if msgs:
|
||||||
|
# Write/append messages to pot file
|
||||||
|
potfile = os.path.join(self.locale_dir, '%s.pot' % str(domain))
|
||||||
if is_templatized:
|
if is_templatized:
|
||||||
old = '#: ' + work_file[2:]
|
old = '#: ' + work_file[2:]
|
||||||
new = '#: ' + orig_file[2:]
|
new = '#: ' + orig_file[2:]
|
||||||
msgs = msgs.replace(old, new)
|
msgs = msgs.replace(old, new)
|
||||||
write_pot_file(potfile, msgs)
|
write_pot_file(potfile, msgs)
|
||||||
|
|
||||||
if is_templatized:
|
if is_templatized:
|
||||||
os.unlink(work_file)
|
os.unlink(work_file)
|
||||||
|
|
||||||
|
@ -232,21 +236,21 @@ class Command(NoArgsCommand):
|
||||||
settings.configure(USE_I18N = True)
|
settings.configure(USE_I18N = True)
|
||||||
|
|
||||||
self.invoked_for_django = False
|
self.invoked_for_django = False
|
||||||
|
self.locale_paths = []
|
||||||
|
self.default_locale_path = None
|
||||||
if os.path.isdir(os.path.join('conf', 'locale')):
|
if os.path.isdir(os.path.join('conf', 'locale')):
|
||||||
localedir = os.path.abspath(os.path.join('conf', 'locale'))
|
self.locale_paths = [os.path.abspath(os.path.join('conf', 'locale'))]
|
||||||
|
self.default_locale_path = self.locale_paths[0]
|
||||||
self.invoked_for_django = True
|
self.invoked_for_django = True
|
||||||
# Ignoring all contrib apps
|
# Ignoring all contrib apps
|
||||||
self.ignore_patterns += ['contrib/*']
|
self.ignore_patterns += ['contrib/*']
|
||||||
elif os.path.isdir('locale'):
|
|
||||||
localedir = os.path.abspath('locale')
|
|
||||||
else:
|
else:
|
||||||
raise CommandError("This script should be run from the Django Git "
|
self.locale_paths.extend(list(settings.LOCALE_PATHS))
|
||||||
"tree or your project or app tree. If you did indeed run it "
|
# Allow to run makemessages inside an app dir
|
||||||
"from the Git checkout or your project or application, "
|
if os.path.isdir('locale'):
|
||||||
"maybe you are just missing the conf/locale (in the django "
|
self.locale_paths.append(os.path.abspath('locale'))
|
||||||
"tree) or locale (for project and application) directory? It "
|
if self.locale_paths:
|
||||||
"is not created automatically, you have to create it by hand "
|
self.default_locale_path = self.locale_paths[0]
|
||||||
"if you want to enable i18n for your project or application.")
|
|
||||||
|
|
||||||
# We require gettext version 0.15 or newer.
|
# We require gettext version 0.15 or newer.
|
||||||
output, errors, status = _popen('xgettext --version')
|
output, errors, status = _popen('xgettext --version')
|
||||||
|
@ -261,24 +265,25 @@ class Command(NoArgsCommand):
|
||||||
"gettext 0.15 or newer. You are using version %s, please "
|
"gettext 0.15 or newer. You are using version %s, please "
|
||||||
"upgrade your gettext toolset." % match.group())
|
"upgrade your gettext toolset." % match.group())
|
||||||
|
|
||||||
potfile = self.build_pot_file(localedir)
|
|
||||||
|
|
||||||
# Build po files for each selected locale
|
|
||||||
locales = []
|
|
||||||
if locale is not None:
|
|
||||||
locales += locale.split(',') if not isinstance(locale, list) else locale
|
|
||||||
elif process_all:
|
|
||||||
locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))
|
|
||||||
locales = [os.path.basename(l) for l in locale_dirs]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
potfiles = self.build_potfiles()
|
||||||
|
|
||||||
|
# Build po files for each selected locale
|
||||||
|
locales = []
|
||||||
|
if locale is not None:
|
||||||
|
locales = locale.split(',') if not isinstance(locale, list) else locale
|
||||||
|
elif process_all:
|
||||||
|
locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path))
|
||||||
|
locales = [os.path.basename(l) for l in locale_dirs]
|
||||||
|
|
||||||
for locale in locales:
|
for locale in locales:
|
||||||
if self.verbosity > 0:
|
if self.verbosity > 0:
|
||||||
self.stdout.write("processing locale %s\n" % locale)
|
self.stdout.write("processing locale %s\n" % locale)
|
||||||
self.write_po_file(potfile, locale)
|
for potfile in potfiles:
|
||||||
|
self.write_po_file(potfile, locale)
|
||||||
finally:
|
finally:
|
||||||
if not self.keep_pot and os.path.exists(potfile):
|
if not self.keep_pot:
|
||||||
os.unlink(potfile)
|
self.remove_potfiles()
|
||||||
|
|
||||||
def build_pot_file(self, localedir):
|
def build_pot_file(self, localedir):
|
||||||
file_list = self.find_files(".")
|
file_list = self.find_files(".")
|
||||||
|
@ -292,9 +297,41 @@ class Command(NoArgsCommand):
|
||||||
f.process(self, potfile, self.domain, self.keep_pot)
|
f.process(self, potfile, self.domain, self.keep_pot)
|
||||||
return potfile
|
return potfile
|
||||||
|
|
||||||
|
def build_potfiles(self):
|
||||||
|
"""Build pot files and apply msguniq to them"""
|
||||||
|
file_list = self.find_files(".")
|
||||||
|
self.remove_potfiles()
|
||||||
|
for f in file_list:
|
||||||
|
f.process(self, self.domain)
|
||||||
|
|
||||||
|
potfiles = []
|
||||||
|
for path in self.locale_paths:
|
||||||
|
potfile = os.path.join(path, '%s.pot' % str(self.domain))
|
||||||
|
if not os.path.exists(potfile):
|
||||||
|
continue
|
||||||
|
msgs, errors, status = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
|
||||||
|
(self.wrap, self.location, potfile))
|
||||||
|
if errors:
|
||||||
|
if status != STATUS_OK:
|
||||||
|
raise CommandError(
|
||||||
|
"errors happened while running msguniq\n%s" % errors)
|
||||||
|
elif self.verbosity > 0:
|
||||||
|
self.stdout.write(errors)
|
||||||
|
with open(potfile, 'w') as fp:
|
||||||
|
fp.write(msgs)
|
||||||
|
potfiles.append(potfile)
|
||||||
|
return potfiles
|
||||||
|
|
||||||
|
def remove_potfiles(self):
|
||||||
|
for path in self.locale_paths:
|
||||||
|
pot_path = os.path.join(path, '%s.pot' % str(self.domain))
|
||||||
|
if os.path.exists(pot_path):
|
||||||
|
os.unlink(pot_path)
|
||||||
|
|
||||||
def find_files(self, root):
|
def find_files(self, root):
|
||||||
"""
|
"""
|
||||||
Helper method to get all files in the given root.
|
Helper function to get all files in the given root. Also check that there
|
||||||
|
is a matching locale dir for each file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def is_ignored(path, ignore_patterns):
|
def is_ignored(path, ignore_patterns):
|
||||||
|
@ -315,12 +352,26 @@ class Command(NoArgsCommand):
|
||||||
dirnames.remove(dirname)
|
dirnames.remove(dirname)
|
||||||
if self.verbosity > 1:
|
if self.verbosity > 1:
|
||||||
self.stdout.write('ignoring directory %s\n' % dirname)
|
self.stdout.write('ignoring directory %s\n' % dirname)
|
||||||
|
elif dirname == 'locale':
|
||||||
|
dirnames.remove(dirname)
|
||||||
|
self.locale_paths.insert(0, os.path.join(os.path.abspath(dirpath), dirname))
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if is_ignored(os.path.normpath(os.path.join(dirpath, filename)), self.ignore_patterns):
|
file_path = os.path.normpath(os.path.join(dirpath, filename))
|
||||||
|
if is_ignored(file_path, self.ignore_patterns):
|
||||||
if self.verbosity > 1:
|
if self.verbosity > 1:
|
||||||
self.stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
|
self.stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
|
||||||
else:
|
else:
|
||||||
all_files.append(TranslatableFile(dirpath, filename))
|
locale_dir = None
|
||||||
|
for path in self.locale_paths:
|
||||||
|
if os.path.abspath(dirpath).startswith(os.path.dirname(path)):
|
||||||
|
locale_dir = path
|
||||||
|
break
|
||||||
|
if not locale_dir:
|
||||||
|
locale_dir = self.default_locale_path
|
||||||
|
if not locale_dir:
|
||||||
|
raise CommandError(
|
||||||
|
"Unable to find a locale path to store translations for file %s" % file_path)
|
||||||
|
all_files.append(TranslatableFile(dirpath, filename, locale_dir))
|
||||||
return sorted(all_files)
|
return sorted(all_files)
|
||||||
|
|
||||||
def write_po_file(self, potfile, locale):
|
def write_po_file(self, potfile, locale):
|
||||||
|
@ -328,16 +379,8 @@ class Command(NoArgsCommand):
|
||||||
Creates or updates the PO file for self.domain and :param locale:.
|
Creates or updates the PO file for self.domain and :param locale:.
|
||||||
Uses contents of the existing :param potfile:.
|
Uses contents of the existing :param potfile:.
|
||||||
|
|
||||||
Uses mguniq, msgmerge, and msgattrib GNU gettext utilities.
|
Uses msgmerge, and msgattrib GNU gettext utilities.
|
||||||
"""
|
"""
|
||||||
msgs, errors, status = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
|
|
||||||
(self.wrap, self.location, potfile))
|
|
||||||
if errors:
|
|
||||||
if status != STATUS_OK:
|
|
||||||
raise CommandError(
|
|
||||||
"errors happened while running msguniq\n%s" % errors)
|
|
||||||
elif self.verbosity > 0:
|
|
||||||
self.stdout.write(errors)
|
|
||||||
|
|
||||||
basedir = os.path.join(os.path.dirname(potfile), locale, 'LC_MESSAGES')
|
basedir = os.path.join(os.path.dirname(potfile), locale, 'LC_MESSAGES')
|
||||||
if not os.path.isdir(basedir):
|
if not os.path.isdir(basedir):
|
||||||
|
@ -345,8 +388,6 @@ class Command(NoArgsCommand):
|
||||||
pofile = os.path.join(basedir, '%s.po' % str(self.domain))
|
pofile = os.path.join(basedir, '%s.po' % str(self.domain))
|
||||||
|
|
||||||
if os.path.exists(pofile):
|
if os.path.exists(pofile):
|
||||||
with open(potfile, 'w') as fp:
|
|
||||||
fp.write(msgs)
|
|
||||||
msgs, errors, status = _popen('msgmerge %s %s -q "%s" "%s"' %
|
msgs, errors, status = _popen('msgmerge %s %s -q "%s" "%s"' %
|
||||||
(self.wrap, self.location, pofile, potfile))
|
(self.wrap, self.location, pofile, potfile))
|
||||||
if errors:
|
if errors:
|
||||||
|
@ -355,8 +396,10 @@ class Command(NoArgsCommand):
|
||||||
"errors happened while running msgmerge\n%s" % errors)
|
"errors happened while running msgmerge\n%s" % errors)
|
||||||
elif self.verbosity > 0:
|
elif self.verbosity > 0:
|
||||||
self.stdout.write(errors)
|
self.stdout.write(errors)
|
||||||
elif not self.invoked_for_django:
|
else:
|
||||||
msgs = self.copy_plural_forms(msgs, locale)
|
msgs = open(potfile, 'r').read()
|
||||||
|
if not self.invoked_for_django:
|
||||||
|
msgs = self.copy_plural_forms(msgs, locale)
|
||||||
msgs = msgs.replace(
|
msgs = msgs.replace(
|
||||||
"#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "")
|
"#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "")
|
||||||
with open(pofile, 'w') as fp:
|
with open(pofile, 'w') as fp:
|
||||||
|
|
|
@ -193,7 +193,8 @@ Ignore files or directories matching this glob-style pattern. Use multiple
|
||||||
times to ignore more (makemessages command).
|
times to ignore more (makemessages command).
|
||||||
.TP
|
.TP
|
||||||
.I \-\-no\-default\-ignore
|
.I \-\-no\-default\-ignore
|
||||||
Don't ignore the common private glob-style patterns 'CVS', '.*' and '*~' (makemessages command).
|
Don't ignore the common private glob-style patterns 'CVS', '.*', '*~' and '*.pyc'
|
||||||
|
(makemessages command).
|
||||||
.TP
|
.TP
|
||||||
.I \-\-no\-wrap
|
.I \-\-no\-wrap
|
||||||
Don't break long message lines into several lines (makemessages command).
|
Don't break long message lines into several lines (makemessages command).
|
||||||
|
|
|
@ -472,7 +472,7 @@ Example usage::
|
||||||
Use the ``--ignore`` or ``-i`` option to ignore files or directories matching
|
Use the ``--ignore`` or ``-i`` option to ignore files or directories matching
|
||||||
the given :mod:`glob`-style pattern. Use multiple times to ignore more.
|
the given :mod:`glob`-style pattern. Use multiple times to ignore more.
|
||||||
|
|
||||||
These patterns are used by default: ``'CVS'``, ``'.*'``, ``'*~'``
|
These patterns are used by default: ``'CVS'``, ``'.*'``, ``'*~'``, ``'*.pyc'``
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
|
@ -499,7 +499,7 @@ for technically skilled translators to understand each message's context.
|
||||||
.. versionadded:: 1.6
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
Use the ``--keep-pot`` option to prevent django from deleting the temporary
|
Use the ``--keep-pot`` option to prevent django from deleting the temporary
|
||||||
.pot file it generates before creating the .po file. This is useful for
|
.pot files it generates before creating the .po file. This is useful for
|
||||||
debugging errors which may prevent the final language files from being created.
|
debugging errors which may prevent the final language files from being created.
|
||||||
|
|
||||||
runfcgi [options]
|
runfcgi [options]
|
||||||
|
|
|
@ -1543,24 +1543,9 @@ All message file repositories are structured the same way. They are:
|
||||||
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
||||||
|
|
||||||
To create message files, you use the :djadmin:`django-admin.py makemessages <makemessages>`
|
To create message files, you use the :djadmin:`django-admin.py makemessages <makemessages>`
|
||||||
tool. You only need to be in the same directory where the ``locale/`` directory
|
tool. And you use :djadmin:`django-admin.py compilemessages <compilemessages>`
|
||||||
is located. And you use :djadmin:`django-admin.py compilemessages <compilemessages>`
|
|
||||||
to produce the binary ``.mo`` files that are used by ``gettext``.
|
to produce the binary ``.mo`` files that are used by ``gettext``.
|
||||||
|
|
||||||
You can also run :djadmin:`django-admin.py compilemessages
|
You can also run :djadmin:`django-admin.py compilemessages
|
||||||
--settings=path.to.settings <compilemessages>` to make the compiler process all
|
--settings=path.to.settings <compilemessages>` to make the compiler process all
|
||||||
the directories in your :setting:`LOCALE_PATHS` setting.
|
the directories in your :setting:`LOCALE_PATHS` setting.
|
||||||
|
|
||||||
Finally, you should give some thought to the structure of your translation
|
|
||||||
files. If your applications need to be delivered to other users and will be used
|
|
||||||
in other projects, you might want to use app-specific translations. But using
|
|
||||||
app-specific translations and project-specific translations could produce weird
|
|
||||||
problems with :djadmin:`makemessages`: it will traverse all directories below
|
|
||||||
the current path and so might put message IDs into a unified, common message
|
|
||||||
file for the current project that are already in application message files.
|
|
||||||
|
|
||||||
The easiest way out is to store applications that are not part of the project
|
|
||||||
(and so carry their own translations) outside the project tree. That way,
|
|
||||||
:djadmin:`django-admin.py makemessages <makemessages>`, when ran on a project
|
|
||||||
level will only extract strings that are connected to your explicit project and
|
|
||||||
not strings that are distributed independently.
|
|
||||||
|
|
|
@ -5,10 +5,13 @@ import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.test.utils import override_settings
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
|
from django.utils import six
|
||||||
from django.utils.six import StringIO
|
from django.utils.six import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,3 +355,44 @@ class MultipleLocaleExtractionTests(ExtractorTests):
|
||||||
management.call_command('makemessages', locale='pt,de,ch', verbosity=0)
|
management.call_command('makemessages', locale='pt,de,ch', verbosity=0)
|
||||||
self.assertTrue(os.path.exists(self.PO_FILE_PT))
|
self.assertTrue(os.path.exists(self.PO_FILE_PT))
|
||||||
self.assertTrue(os.path.exists(self.PO_FILE_DE))
|
self.assertTrue(os.path.exists(self.PO_FILE_DE))
|
||||||
|
|
||||||
|
|
||||||
|
class CustomLayoutExtractionTests(ExtractorTests):
|
||||||
|
def setUp(self):
|
||||||
|
self._cwd = os.getcwd()
|
||||||
|
self.test_dir = os.path.join(os.path.dirname(upath(__file__)), 'project_dir')
|
||||||
|
|
||||||
|
def test_no_locale_raises(self):
|
||||||
|
os.chdir(self.test_dir)
|
||||||
|
with six.assertRaisesRegex(self, management.CommandError,
|
||||||
|
"Unable to find a locale path to store translations for file"):
|
||||||
|
management.call_command('makemessages', locale=LOCALE, verbosity=0)
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
LOCALE_PATHS=(os.path.join(os.path.dirname(upath(__file__)), 'project_dir/project_locale'),)
|
||||||
|
)
|
||||||
|
def test_project_locale_paths(self):
|
||||||
|
"""
|
||||||
|
Test that:
|
||||||
|
* translations for app containing locale folder are stored in that folder
|
||||||
|
* translations outside of that app are in LOCALE_PATHS[0]
|
||||||
|
"""
|
||||||
|
os.chdir(self.test_dir)
|
||||||
|
self.addCleanup(shutil.rmtree, os.path.join(settings.LOCALE_PATHS[0], LOCALE))
|
||||||
|
self.addCleanup(shutil.rmtree, os.path.join(self.test_dir, 'app_with_locale/locale', LOCALE))
|
||||||
|
|
||||||
|
management.call_command('makemessages', locale=LOCALE, verbosity=0)
|
||||||
|
project_de_locale = os.path.join(
|
||||||
|
self.test_dir, 'project_locale/de/LC_MESSAGES/django.po',)
|
||||||
|
app_de_locale = os.path.join(
|
||||||
|
self.test_dir, 'app_with_locale/locale/de/LC_MESSAGES/django.po',)
|
||||||
|
self.assertTrue(os.path.exists(project_de_locale))
|
||||||
|
self.assertTrue(os.path.exists(app_de_locale))
|
||||||
|
|
||||||
|
with open(project_de_locale, 'r') as fp:
|
||||||
|
po_contents = force_text(fp.read())
|
||||||
|
self.assertMsgId('This app has no locale directory', po_contents)
|
||||||
|
self.assertMsgId('This is a project-level string', po_contents)
|
||||||
|
with open(app_de_locale, 'r') as fp:
|
||||||
|
po_contents = force_text(fp.read())
|
||||||
|
self.assertMsgId('This app has a locale directory', po_contents)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This is a project-level string")
|
|
@ -0,0 +1,4 @@
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This app has no locale directory")
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This app has a locale directory")
|
||||||
|
|
|
@ -33,7 +33,7 @@ if can_run_extraction_tests:
|
||||||
JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests,
|
JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests,
|
||||||
CopyPluralFormsExtractorTests, NoWrapExtractorTests,
|
CopyPluralFormsExtractorTests, NoWrapExtractorTests,
|
||||||
NoLocationExtractorTests, KeepPotFileExtractorTests,
|
NoLocationExtractorTests, KeepPotFileExtractorTests,
|
||||||
MultipleLocaleExtractionTests)
|
MultipleLocaleExtractionTests, CustomLayoutExtractionTests)
|
||||||
if can_run_compilation_tests:
|
if can_run_compilation_tests:
|
||||||
from .commands.compilation import (PoFileTests, PoFileContentsTests,
|
from .commands.compilation import (PoFileTests, PoFileContentsTests,
|
||||||
PercentRenderingTests, MultipleLocaleCompilationTests)
|
PercentRenderingTests, MultipleLocaleCompilationTests)
|
||||||
|
|
Loading…
Reference in New Issue