Skip to content

Commit fef575a

Browse files
committed
Fixed #6380 - Follow symlinks when examining source code and templates for translation strings.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12443 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 71da5f6 commit fef575a

File tree

6 files changed

+83
-14
lines changed

6 files changed

+83
-14
lines changed

django/core/management/commands/makemessages.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,31 @@ def _popen(cmd):
4343
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
4444
return p.communicate()
4545

46-
def make_messages(locale=None, domain='django', verbosity='1', all=False, extensions=None):
46+
def walk(root, topdown=True, onerror=None, followlinks=False):
47+
"""
48+
A version of os.walk that can follow symlinks for Python < 2.6
49+
"""
50+
for dirpath, dirnames, filenames in os.walk(root, topdown, onerror):
51+
yield (dirpath, dirnames, filenames)
52+
if followlinks:
53+
for d in dirnames:
54+
p = os.path.join(dirpath, d)
55+
if os.path.islink(p):
56+
for link_dirpath, link_dirnames, link_filenames in walk(p):
57+
yield (link_dirpath, link_dirnames, link_filenames)
58+
59+
def find_files(root, symlinks=False):
60+
"""
61+
Helper function to get all files in the given root.
62+
"""
63+
all_files = []
64+
for (dirpath, dirnames, filenames) in walk(".", followlinks=symlinks):
65+
all_files.extend([(dirpath, f) for f in filenames])
66+
all_files.sort()
67+
return all_files
68+
69+
def make_messages(locale=None, domain='django', verbosity='1', all=False,
70+
extensions=None, symlinks=False):
4771
"""
4872
Uses the locale directory from the Django SVN tree or an application/
4973
project to process all
@@ -103,11 +127,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False, extens
103127
if os.path.exists(potfile):
104128
os.unlink(potfile)
105129

106-
all_files = []
107-
for (dirpath, dirnames, filenames) in os.walk("."):
108-
all_files.extend([(dirpath, f) for f in filenames])
109-
all_files.sort()
110-
for dirpath, file in all_files:
130+
for dirpath, file in find_files(".", symlinks=symlinks):
111131
file_base, file_ext = os.path.splitext(file)
112132
if domain == 'djangojs' and file_ext in extensions:
113133
if verbosity > 1:
@@ -184,6 +204,8 @@ class Command(BaseCommand):
184204
help='The domain of the message files (default: "django").'),
185205
make_option('--all', '-a', action='store_true', dest='all',
186206
default=False, help='Reexamines all source code and templates for new translation strings and updates all message files for all available languages.'),
207+
make_option('--symlinks', '-s', action='store_true', dest='symlinks',
208+
default=False, help='Follows symlinks to directories when examining source code and templates for translation strings.'),
187209
make_option('--extension', '-e', dest='extensions',
188210
help='The file extension(s) to examine (default: ".html", separate multiple extensions with commas, or use -e multiple times)',
189211
action='append'),
@@ -202,6 +224,7 @@ def handle(self, *args, **options):
202224
verbosity = int(options.get('verbosity'))
203225
process_all = options.get('all')
204226
extensions = options.get('extensions')
227+
symlinks = options.get('symlinks')
205228

206229
if domain == 'djangojs':
207230
extensions = handle_extensions(extensions or ['js'])
@@ -211,4 +234,4 @@ def handle(self, *args, **options):
211234
if verbosity > 1:
212235
sys.stdout.write('examining files with the extensions: %s\n' % get_text_list(list(extensions), 'and'))
213236

214-
make_messages(locale, domain, verbosity, process_all, extensions)
237+
make_messages(locale, domain, verbosity, process_all, extensions, symlinks)

docs/man/django-admin.1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Executes
4646
.B sqlall
4747
for the given app(s) in the current database.
4848
.TP
49-
.BI "makemessages [" "\-\-locale=LOCALE" "] [" "\-\-domain=DOMAIN" "] [" "\-\-extension=EXTENSION" "] [" "\-\-all" "]"
49+
.BI "makemessages [" "\-\-locale=LOCALE" "] [" "\-\-domain=DOMAIN" "] [" "\-\-extension=EXTENSION" "] [" "\-\-all" "] [" "\-\-symlinks" "]"
5050
Runs over the entire source tree of the current directory and pulls out all
5151
strings marked for translation. It creates (or updates) a message file in the
5252
conf/locale (in the django tree) or locale (for project and application) directory.
@@ -155,6 +155,10 @@ The domain of the message files (default: "django") when using makemessages.
155155
The file extension(s) to examine (default: ".html", separate multiple
156156
extensions with commas, or use -e multiple times).
157157
.TP
158+
.I \-e, \-\-symlinks
159+
Follows symlinks to directories when examining source code and templates for
160+
translation strings.
161+
.TP
158162
.I \-a, \-\-all
159163
Process all available locales when using makemessages..SH "ENVIRONMENT"
160164
.TP

docs/ref/django-admin.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,17 @@ Example usage::
471471
Use the ``--domain`` or ``-d`` option to change the domain of the messages files.
472472
Currently supported:
473473

474-
* ``django`` for all ``*.py`` and ``*.html`` files (default)
475-
* ``djangojs`` for ``*.js`` files
474+
* ``django`` for all ``*.py`` and ``*.html`` files (default)
475+
* ``djangojs`` for ``*.js`` files
476+
477+
.. django-admin-option:: --symlinks
478+
479+
Use the ``--symlinks`` or ``-s`` option to follow symlinks to directories when
480+
looking for new translation strings.
481+
482+
Example usage::
483+
484+
django-admin.py makemessages --locale=de --symlinks
476485

477486
reset <appname appname ...>
478487
---------------------------

docs/topics/i18n/localization.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ The script should be run from one of two places:
7878
* The root directory of your Django project.
7979
* The root directory of your Django app.
8080

81-
Th script runs over your project source tree or your application source tree and
82-
pulls out all strings marked for translation. It creates (or updates) a message
83-
file in the directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the
84-
file will be ``locale/de/LC_MESSAGES/django.po``.
81+
The script runs over your project source tree or your application source tree
82+
and pulls out all strings marked for translation. It creates (or updates) a
83+
message file in the directory ``locale/LANG/LC_MESSAGES``. In the ``de``
84+
example, the file will be ``locale/de/LC_MESSAGES/django.po``.
8585

8686
By default ``django-admin.py makemessages`` examines every file that has the
8787
``.html`` file extension. In case you want to override that default, use the
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{% load i18n %}
2+
{% trans "This literal should be included." %}

tests/regressiontests/makemessages/tests.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,34 @@ def test_javascript_literals(self):
4040
po_contents = open(self.PO_FILE, 'r').read()
4141
self.assertMsgId('This literal should be included.', po_contents)
4242
self.assertMsgId('This one as well.', po_contents)
43+
44+
class SymlinkExtractorTests(ExtractorTests):
45+
46+
PO_FILE='locale/%s/LC_MESSAGES/django.po' % LOCALE
47+
48+
def setUp(self):
49+
self._cwd = os.getcwd()
50+
self.test_dir = os.path.abspath(os.path.dirname(__file__))
51+
self.symlinked_dir = os.path.join(self.test_dir, 'templates_symlinked')
52+
53+
def tearDown(self):
54+
super(SymlinkExtractorTests, self).tearDown()
55+
os.chdir(self.test_dir)
56+
try:
57+
os.remove(self.symlinked_dir)
58+
except OSError:
59+
pass
60+
os.chdir(self._cwd)
61+
62+
def test_symlink(self):
63+
if hasattr(os, 'symlink'):
64+
if os.path.exists(self.symlinked_dir):
65+
self.assert_(os.path.islink(self.symlinked_dir))
66+
else:
67+
os.symlink(os.path.join(self.test_dir, 'templates'), self.symlinked_dir)
68+
os.chdir(self.test_dir)
69+
management.call_command('makemessages', locale=LOCALE, verbosity=0, symlinks=True)
70+
self.assert_(os.path.exists(self.PO_FILE))
71+
po_contents = open(self.PO_FILE, 'r').read()
72+
self.assertMsgId('This literal should be included.', po_contents)
73+
self.assert_('templates_symlinked/test.html' in po_contents)

0 commit comments

Comments
 (0)