4 from itertools
import dropwhile
5 from optparse
import make_option
6 from django
.core
.management
.base
import CommandError
, BaseCommand
11 from sets
import Set
as set # For Python 2.3
13 pythonize_re
= re
.compile(r
'\n\s*//')
15 def handle_extensions(extensions
=('html',)):
17 organizes multiple extensions that are separated with commas or passed by
18 using --extension/-e multiple times.
20 for example: running 'django-admin makemessages -e js,txt -e xhtml -a'
21 would result in a extension list: ['.js', '.txt', '.xhtml']
23 >>> handle_extensions(['.html', 'html,js,py,py,py,.py', 'py,.py'])
25 >>> handle_extensions(['.html, txt,.tpl'])
26 ['.html', '.tpl', '.txt']
29 for ext
in extensions
:
30 ext_list
.extend(ext
.replace(' ','').split(','))
31 for i
, ext
in enumerate(ext_list
):
32 if not ext
.startswith('.'):
33 ext_list
[i
] = '.%s' % ext_list
[i
]
35 # we don't want *.py files here because of the way non-*.py files
36 # are handled in make_messages() (they are copied to file.ext.py files to
37 # trick xgettext to parse them as Python files)
38 return set([x
for x
in ext_list
if x
!= '.py'])
40 def make_messages(locale
=None, domain
='django', verbosity
='1', all
=False, extensions
=None):
42 Uses the locale directory from the Django SVN tree or an application/
43 project to process all
45 # Need to ensure that the i18n framework is enabled
46 from django
.conf
import settings
47 if settings
.configured
:
48 settings
.USE_I18N
= True
50 settings
.configure(USE_I18N
= True)
52 from django
.utils
.translation
import templatize
54 if os
.path
.isdir(os
.path
.join('conf', 'locale')):
55 localedir
= os
.path
.abspath(os
.path
.join('conf', 'locale'))
56 elif os
.path
.isdir('locale'):
57 localedir
= os
.path
.abspath('locale')
59 raise CommandError("This script should be run from the Django SVN tree or your project or app tree. If you did indeed run it from the SVN checkout or your project or application, maybe you are just missing the conf/locale (in the django tree) or locale (for project and application) directory? It is not created automatically, you have to create it by hand if you want to enable i18n for your project or application.")
61 if domain
not in ('django', 'djangojs'):
62 raise CommandError("currently makemessages only supports domains 'django' and 'djangojs'")
64 if (locale
is None and not all
) or domain
is None:
65 # backwards compatible error message
66 if not sys
.argv
[0].endswith("make-messages.py"):
67 message
= "Type '%s help %s' for usage.\n" % (os
.path
.basename(sys
.argv
[0]), sys
.argv
[1])
69 message
= "usage: make-messages.py -l <language>\n or: make-messages.py -a\n"
70 raise CommandError(message
)
73 if locale
is not None:
74 languages
.append(locale
)
76 languages
= [el
for el
in os
.listdir(localedir
) if not el
.startswith('.')]
78 for locale
in languages
:
80 print "processing language", locale
81 basedir
= os
.path
.join(localedir
, locale
, 'LC_MESSAGES')
82 if not os
.path
.isdir(basedir
):
85 pofile
= os
.path
.join(basedir
, '%s.po' % domain
)
86 potfile
= os
.path
.join(basedir
, '%s.pot' % domain
)
88 if os
.path
.exists(potfile
):
92 for (dirpath
, dirnames
, filenames
) in os
.walk("."):
93 all_files
.extend([(dirpath
, f
) for f
in filenames
])
95 for dirpath
, file in all_files
:
96 file_base
, file_ext
= os
.path
.splitext(file)
97 if domain
== 'djangojs' and file_ext
== '.js':
99 sys
.stdout
.write('processing file %s in %s\n' % (file, dirpath
))
100 src
= open(os
.path
.join(dirpath
, file), "rU").read()
101 src
= pythonize_re
.sub('\n#', src
)
102 thefile
= '%s.py' % file
103 open(os
.path
.join(dirpath
, thefile
), "w").write(src
)
104 cmd
= 'xgettext -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (domain
, os
.path
.join(dirpath
, thefile
))
105 (stdin
, stdout
, stderr
) = os
.popen3(cmd
, 't')
107 errors
= stderr
.read()
109 raise CommandError("errors happened while running xgettext on %s\n%s" % (file, errors
))
110 old
= '#: '+os
.path
.join(dirpath
, thefile
)[2:]
111 new
= '#: '+os
.path
.join(dirpath
, file)[2:]
112 msgs
= msgs
.replace(old
, new
)
113 if os
.path
.exists(potfile
):
115 msgs
= '\n'.join(dropwhile(len, msgs
.split('\n')))
117 msgs
= msgs
.replace('charset=CHARSET', 'charset=UTF-8')
119 open(potfile
, 'ab').write(msgs
)
120 os
.unlink(os
.path
.join(dirpath
, thefile
))
121 elif domain
== 'django' and (file_ext
== '.py' or file_ext
in extensions
):
123 if file_ext
in extensions
:
124 src
= open(os
.path
.join(dirpath
, file), "rU").read()
125 thefile
= '%s.py' % file
126 open(os
.path
.join(dirpath
, thefile
), "w").write(templatize(src
))
128 sys
.stdout
.write('processing file %s in %s\n' % (file, dirpath
))
129 cmd
= 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (
130 domain
, os
.path
.join(dirpath
, thefile
))
131 (stdin
, stdout
, stderr
) = os
.popen3(cmd
, 't')
133 errors
= stderr
.read()
135 raise CommandError("errors happened while running xgettext on %s\n%s" % (file, errors
))
137 old
= '#: '+os
.path
.join(dirpath
, thefile
)[2:]
138 new
= '#: '+os
.path
.join(dirpath
, file)[2:]
139 msgs
= msgs
.replace(old
, new
)
140 if os
.path
.exists(potfile
):
142 msgs
= '\n'.join(dropwhile(len, msgs
.split('\n')))
144 msgs
= msgs
.replace('charset=CHARSET', 'charset=UTF-8')
146 open(potfile
, 'ab').write(msgs
)
148 os
.unlink(os
.path
.join(dirpath
, thefile
))
150 if os
.path
.exists(potfile
):
151 (stdin
, stdout
, stderr
) = os
.popen3('msguniq --to-code=utf-8 "%s"' % potfile
, 't')
153 errors
= stderr
.read()
155 raise CommandError("errors happened while running msguniq\n%s" % errors
)
156 open(potfile
, 'w').write(msgs
)
157 if os
.path
.exists(pofile
):
158 (stdin
, stdout
, stderr
) = os
.popen3('msgmerge -q "%s" "%s"' % (pofile
, potfile
), 't')
160 errors
= stderr
.read()
162 raise CommandError("errors happened while running msgmerge\n%s" % errors
)
163 open(pofile
, 'wb').write(msgs
)
167 class Command(BaseCommand
):
168 option_list
= BaseCommand
.option_list
+ (
169 make_option('--locale', '-l', default
=None, dest
='locale',
170 help='Creates or updates the message files only for the given locale (e.g. pt_BR).'),
171 make_option('--domain', '-d', default
='django', dest
='domain',
172 help='The domain of the message files (default: "django").'),
173 make_option('--verbosity', '-v', action
='store', dest
='verbosity',
174 default
='1', type='choice', choices
=['0', '1', '2'],
175 help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
176 make_option('--all', '-a', action
='store_true', dest
='all',
177 default
=False, help='Reexamines all source code and templates for new translation strings and updates all message files for all available languages.'),
178 make_option('--extension', '-e', dest
='extensions',
179 help='The file extension(s) to examine (default: ".html", separate multiple extensions with commas, or use -e multiple times)',
182 help = "Runs over the entire source tree of the current directory and pulls out all strings marked for translation. It creates (or updates) a message file in the conf/locale (in the django tree) or locale (for project and application) directory."
184 requires_model_validation
= False
185 can_import_settings
= False
187 def handle(self
, *args
, **options
):
189 raise CommandError("Command doesn't accept any arguments")
191 locale
= options
.get('locale')
192 domain
= options
.get('domain')
193 verbosity
= int(options
.get('verbosity'))
194 process_all
= options
.get('all')
195 extensions
= options
.get('extensions') or ['html']
197 if domain
== 'djangojs':
200 extensions
= handle_extensions(extensions
)
202 if '.js' in extensions
:
203 raise CommandError("JavaScript files should be examined by using the special 'djangojs' domain only.")
205 make_messages(locale
, domain
, verbosity
, process_all
, extensions
)