mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Enabled makemessages to support several translation directories
Thanks Rémy Hubscher, Ramiro Morales, Unai Zalakain and Tim Graham for the reviews. Also fixes #16084.
This commit is contained in:
parent
9af7e18f35
commit
50a8ab7cd1
@ -29,25 +29,28 @@ def check_programs(*programs):
|
|||||||
|
|
||||||
@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.
|
||||||
"""
|
"""
|
||||||
@ -127,8 +130,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))
|
||||||
@ -136,6 +137,8 @@ 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:
|
||||||
# Remove '.py' suffix
|
# Remove '.py' suffix
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
@ -147,6 +150,7 @@ class TranslatableFile(object):
|
|||||||
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)
|
||||||
|
|
||||||
@ -242,64 +246,94 @@ class Command(NoArgsCommand):
|
|||||||
% get_text_list(list(self.extensions), 'and'))
|
% get_text_list(list(self.extensions), 'and'))
|
||||||
|
|
||||||
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.")
|
if not os.path.exists(self.default_locale_path):
|
||||||
|
os.makedirs(self.default_locale_path)
|
||||||
|
|
||||||
check_programs('xgettext')
|
# Build locale list
|
||||||
|
|
||||||
potfile = self.build_pot_file(localedir)
|
|
||||||
|
|
||||||
# Build po files for each selected locale
|
|
||||||
locales = []
|
locales = []
|
||||||
if locale is not None:
|
if locale is not None:
|
||||||
locales = locale
|
locales = locale
|
||||||
elif process_all:
|
elif process_all:
|
||||||
locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))
|
locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path))
|
||||||
locales = [os.path.basename(l) for l in locale_dirs]
|
locales = [os.path.basename(l) for l in locale_dirs]
|
||||||
|
|
||||||
if locales:
|
if locales:
|
||||||
check_programs('msguniq', 'msgmerge', 'msgattrib')
|
check_programs('msguniq', 'msgmerge', 'msgattrib')
|
||||||
|
|
||||||
|
check_programs('xgettext')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
potfiles = self.build_potfiles()
|
||||||
|
|
||||||
|
# Build po files for each selected locale
|
||||||
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_potfiles(self):
|
||||||
|
"""
|
||||||
|
Build pot files and apply msguniq to them.
|
||||||
|
"""
|
||||||
file_list = self.find_files(".")
|
file_list = self.find_files(".")
|
||||||
|
self.remove_potfiles()
|
||||||
potfile = os.path.join(localedir, '%s.pot' % str(self.domain))
|
|
||||||
if os.path.exists(potfile):
|
|
||||||
# Remove a previous undeleted potfile, if any
|
|
||||||
os.unlink(potfile)
|
|
||||||
|
|
||||||
for f in file_list:
|
for f in file_list:
|
||||||
try:
|
try:
|
||||||
f.process(self, potfile, self.domain, self.keep_pot)
|
f.process(self, self.domain)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
self.stdout.write("UnicodeDecodeError: skipped file %s in %s" % (f.file, f.dirpath))
|
self.stdout.write("UnicodeDecodeError: skipped file %s in %s" % (f.file, f.dirpath))
|
||||||
return potfile
|
|
||||||
|
potfiles = []
|
||||||
|
for path in self.locale_paths:
|
||||||
|
potfile = os.path.join(path, '%s.pot' % str(self.domain))
|
||||||
|
if not os.path.exists(potfile):
|
||||||
|
continue
|
||||||
|
args = ['msguniq', '--to-code=utf-8']
|
||||||
|
if self.wrap:
|
||||||
|
args.append(self.wrap)
|
||||||
|
if self.location:
|
||||||
|
args.append(self.location)
|
||||||
|
args.append(potfile)
|
||||||
|
msgs, errors, status = popen_wrapper(args)
|
||||||
|
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 method 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):
|
||||||
@ -319,12 +353,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):
|
||||||
@ -332,30 +380,14 @@ 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.
|
||||||
"""
|
"""
|
||||||
args = ['msguniq', '--to-code=utf-8']
|
|
||||||
if self.wrap:
|
|
||||||
args.append(self.wrap)
|
|
||||||
if self.location:
|
|
||||||
args.append(self.location)
|
|
||||||
args.append(potfile)
|
|
||||||
msgs, errors, status = popen_wrapper(args)
|
|
||||||
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):
|
||||||
os.makedirs(basedir)
|
os.makedirs(basedir)
|
||||||
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)
|
|
||||||
args = ['msgmerge', '-q']
|
args = ['msgmerge', '-q']
|
||||||
if self.wrap:
|
if self.wrap:
|
||||||
args.append(self.wrap)
|
args.append(self.wrap)
|
||||||
@ -369,8 +401,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:
|
||||||
|
@ -192,7 +192,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).
|
||||||
|
@ -557,7 +557,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::
|
||||||
|
|
||||||
@ -584,7 +584,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.
|
||||||
|
|
||||||
makemigrations [<appname>]
|
makemigrations [<appname>]
|
||||||
|
@ -375,6 +375,11 @@ Internationalization
|
|||||||
in the corresponding entry in the PO file, which makes the translation
|
in the corresponding entry in the PO file, which makes the translation
|
||||||
process easier.
|
process easier.
|
||||||
|
|
||||||
|
* When you run :djadmin:`makemessages` from the root directory of your project,
|
||||||
|
any extracted strings will now be automatically distributed to the proper
|
||||||
|
app or project message file. See :ref:`how-to-create-language-files` for
|
||||||
|
details.
|
||||||
|
|
||||||
Management Commands
|
Management Commands
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -1256,6 +1256,17 @@ is configured correctly). It creates (or updates) a message file in the
|
|||||||
directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the file will be
|
directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the file will be
|
||||||
``locale/de/LC_MESSAGES/django.po``.
|
``locale/de/LC_MESSAGES/django.po``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
When you run ``makemessages`` from the root directory of your project, the
|
||||||
|
extracted strings will be automatically distributed to the proper message
|
||||||
|
files. That is, a string extracted from a file of an app containing a
|
||||||
|
``locale`` directory will go in a message file under that directory.
|
||||||
|
A string extracted from a file of an app without any ``locale`` directory
|
||||||
|
will either go in a message file under the directory listed first in
|
||||||
|
:setting:`LOCALE_PATHS` or will generate an error if :setting:`LOCALE_PATHS`
|
||||||
|
is empty.
|
||||||
|
|
||||||
By default :djadmin:`django-admin.py makemessages <makemessages>` examines every
|
By default :djadmin:`django-admin.py makemessages <makemessages>` examines every
|
||||||
file that has the ``.html`` or ``.txt`` file extension. In case you want to
|
file that has the ``.html`` or ``.txt`` file extension. In case you want to
|
||||||
override that default, use the ``--extension`` or ``-e`` option to specify the
|
override that default, use the ``--extension`` or ``-e`` option to specify the
|
||||||
@ -1730,24 +1741,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.
|
|
||||||
|
4
tests/i18n/project_dir/__init__.py
Normal file
4
tests/i18n/project_dir/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Sample project used by test_extraction.CustomLayoutExtractionTests
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This is a project-level string")
|
0
tests/i18n/project_dir/app_no_locale/__init__.py
Normal file
0
tests/i18n/project_dir/app_no_locale/__init__.py
Normal file
3
tests/i18n/project_dir/app_no_locale/models.py
Normal file
3
tests/i18n/project_dir/app_no_locale/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This app has no locale directory")
|
0
tests/i18n/project_dir/app_with_locale/__init__.py
Normal file
0
tests/i18n/project_dir/app_with_locale/__init__.py
Normal file
3
tests/i18n/project_dir/app_with_locale/models.py
Normal file
3
tests/i18n/project_dir/app_with_locale/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
string = _("This app has a locale directory")
|
0
tests/i18n/project_dir/project_locale/.gitkeep
Normal file
0
tests/i18n/project_dir/project_locale/.gitkeep
Normal file
@ -8,9 +8,11 @@ import shutil
|
|||||||
from unittest import SkipTest, skipUnless
|
from unittest import SkipTest, skipUnless
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.core.management.utils import find_command
|
from django.core.management.utils import find_command
|
||||||
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 import six
|
||||||
@ -497,3 +499,47 @@ class MultipleLocaleExtractionTests(ExtractorTests):
|
|||||||
management.call_command('makemessages', locale=['pt', 'de'], verbosity=0)
|
management.call_command('makemessages', locale=['pt', 'de'], 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 an app containing a 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), True)
|
||||||
|
self.addCleanup(shutil.rmtree,
|
||||||
|
os.path.join(self.test_dir, 'app_with_locale', 'locale', LOCALE), True)
|
||||||
|
|
||||||
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user