Skip to content

Instantly share code, notes, and snippets.

@claudep
Created January 17, 2013 21:31
Show Gist options
  • Save claudep/4559957 to your computer and use it in GitHub Desktop.
Save claudep/4559957 to your computer and use it in GitHub Desktop.
diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
--- a/django/core/management/commands/makemessages.py
+++ b/django/core/management/commands/makemessages.py
@@ -112,75 +112,56 @@
break
return msgs
-def write_pot_file(potfile, msgs, file, work_file, is_templatized):
- """
- Write the :param potfile: POT file with the :param msgs: contents,
- previously making sure its format is valid.
- """
- if is_templatized:
- old = '#: ' + work_file[2:]
- new = '#: ' + file[2:]
- msgs = msgs.replace(old, new)
- if os.path.exists(potfile):
- # Strip the header
- msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
- else:
- msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
- f = open(potfile, 'ab')
- try:
- f.write(msgs)
- finally:
- f.close()
-def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
- location):
- """
- Extract translatable literals from :param file: for :param domain:
- creating or updating the :param potfile: POT file.
+class Domain(object):
- Uses the xgettext GNU gettext utility.
- """
+ def __init__(self, extensions):
+ self.extensions = extensions
- from django.utils.translation import templatize
+ def process_file(self, file_obj, verbosity, wrap, location):
+ if verbosity > 1:
+ sys.stdout.write('processing file %s in %s\n' % (file_obj.file, file_obj.dirpath))
+ cmd = self.work(file_obj, verbosity, wrap, location)
+ msgs, errors = _popen(cmd)
+ if errors:
+ raise CommandError(
+ "errors happened while running xgettext on %s\n%s" %
+ (file_obj.file, errors))
+ return msgs
- if verbosity > 1:
- sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
- _, file_ext = os.path.splitext(file)
- if domain == 'djangojs' and file_ext in extensions:
- is_templatized = True
- orig_file = os.path.join(dirpath, file)
- src_data = open(orig_file).read()
- src_data = prepare_js_for_gettext(src_data)
- thefile = '%s.c' % file
- work_file = os.path.join(dirpath, thefile)
- f = open(work_file, "w")
- try:
- f.write(src_data)
- finally:
- f.close()
- cmd = (
- 'xgettext -d %s -L C %s %s --keyword=gettext_noop '
- '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
- '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
- '--from-code UTF-8 --add-comments=Translators -o - "%s"' % (
- domain, wrap, location, work_file
- )
- )
- elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
- thefile = file
- orig_file = os.path.join(dirpath, file)
- is_templatized = file_ext in extensions
- if is_templatized:
+
+class DjangoDomain(Domain):
+ name = 'django'
+ xgettext_cmd = 'xgettext -d %(domain)s -L Python %(wrap)s %(location)s --keyword=gettext_noop ' + \
+ '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 ' + \
+ '--keyword=ugettext_noop --keyword=ugettext_lazy ' + \
+ '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 ' + \
+ '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 ' + \
+ '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 ' + \
+ '--add-comments=Translators -o - "%(work_file)s"'
+ templatize_format = '%s.py'
+
+ def is_mine(self, domain, file):
+ _, file_ext = os.path.splitext(file)
+ return domain == self.name and (file_ext == '.py' or file_ext in self.extensions)
+
+ def work(self, file_obj, verbosity, wrap, location):
+ from django.utils.translation import templatize
+
+ _, file_ext = os.path.splitext(file_obj.file)
+ thefile = file_obj.file
+ orig_file = os.path.join(file_obj.dirpath, file_obj.file)
+ file_obj.is_templatized = file_ext in self.extensions
+ if file_obj.is_templatized:
src_data = open(orig_file, "rU").read()
- thefile = '%s.py' % file
- content = templatize(src_data, orig_file[2:])
- f = open(os.path.join(dirpath, thefile), "w")
+ thefile = self.templatize_format % file_obj.file
+ f = open(os.path.join(file_obj.dirpath, thefile), "w")
try:
- f.write(content)
+ f.write(templatize(src_data, orig_file[2:]))
finally:
f.close()
- work_file = os.path.join(dirpath, thefile)
- cmd = (
+ file_obj.work_file = os.path.join(file_obj.dirpath, thefile)
+ return (
'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
'--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
'--keyword=ugettext_noop --keyword=ugettext_lazy '
@@ -188,73 +169,146 @@
'--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
'--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
'--add-comments=Translators -o - "%s"' % (
- domain, wrap, location, work_file)
+ self.name, wrap, location, file_obj.work_file)
)
- else:
- return
- msgs, errors = _popen(cmd)
- if errors:
- if is_templatized:
- os.unlink(work_file)
- if os.path.exists(potfile):
- os.unlink(potfile)
- raise CommandError(
- "errors happened while running xgettext on %s\n%s" %
- (file, errors))
- if msgs:
- write_pot_file(potfile, msgs, orig_file, work_file, is_templatized)
- if is_templatized:
- os.unlink(work_file)
-def write_po_file(pofile, potfile, domain, locale, verbosity,
- copy_pforms, wrap, location, no_obsolete):
- """
- Creates of updates the :param pofile: PO file for :param domain: and :param
- locale:. Uses contents of the existing :param potfile:.
- Uses mguniq, msgmerge, and msgattrib GNU gettext utilities.
- """
- msgs, errors = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
- (wrap, location, potfile))
- if errors:
- os.unlink(potfile)
- raise CommandError("errors happened while running msguniq\n%s" % errors)
- if os.path.exists(pofile):
- f = open(potfile, 'w')
+class DjangoJsDomain(Domain):
+ name = 'djangojs'
+ xgettext_cmd = 'xgettext -d %(domain)s -L C %(wrap)s %(location)s --keyword=gettext_noop ' + \
+ '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 ' + \
+ '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 ' + \
+ '--from-code UTF-8 --add-comments=Translators -o - "%(work_file)s"'
+ templatize_format = '%s.c'
+
+ def is_mine(self, domain, file):
+ _, file_ext = os.path.splitext(file)
+ return domain == self.name and file_ext in self.extensions
+
+ def work(self, file_obj, verbosity, wrap, location):
+ file_obj.is_templatized = True
+ orig_file = os.path.join(file_obj.dirpath, file_obj.file)
+ src_data = open(orig_file).read()
+ src_data = prepare_js_for_gettext(src_data)
+ thefile = self.templatize_format % file_obj.file
+ file_obj.work_file = os.path.join(file_obj.dirpath, thefile)
+ f = open(file_obj.work_file, "w")
+ try:
+ f.write(src_data)
+ finally:
+ f.close()
+ return (
+ 'xgettext -d %s -L C %s %s --keyword=gettext_noop '
+ '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
+ '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
+ '--from-code UTF-8 --add-comments=Translators -o - "%s"' % (
+ self.name, wrap, location, file_obj.work_file
+ )
+ )
+
+
+class Locale(object):
+ def __init__(self, domain, localedir, locale_name):
+ basedir = os.path.join(localedir, locale_name, 'LC_MESSAGES')
+ if not os.path.isdir(basedir):
+ os.makedirs(basedir)
+
+ self.domain = domain
+ self.locale_name = locale_name
+ self.pofile = os.path.join(basedir, '%s.po' % domain)
+ self.potfile = os.path.join(basedir, '%s.pot' % domain)
+
+ self.remove_potfile()
+
+ def write_pot_file(self, msgs, file_obj):
+ """
+ Write the POT file with the :param msgs: contents, previously making
+ sure its format is valid.
+ """
+ if file_obj.is_templatized:
+ old = '#: ' + file_obj.work_file[2:]
+ new = '#: ' + file_obj.file[2:]
+ msgs = msgs.replace(old, new)
+ if os.path.exists(self.potfile):
+ # Strip the header
+ msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
+ else:
+ msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
+ f = open(self.potfile, 'ab')
try:
f.write(msgs)
finally:
f.close()
- msgs, errors = _popen('msgmerge %s %s -q "%s" "%s"' %
- (wrap, location, pofile, potfile))
+
+ def write_po_file(self, verbosity, copy_pforms, wrap, location,
+ no_obsolete):
+ """
+ Write the PO file for this locale by creating or updating it. Uses
+ contents of the existing POT file.
+
+ Uses mguniq, msgmerge, and msgattrib GNU gettext utilities.
+ """
+ if not os.path.exists(self.potfile):
+ return
+ msgs, errors = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
+ (wrap, location, self.potfile))
if errors:
- os.unlink(potfile)
- raise CommandError(
- "errors happened while running msgmerge\n%s" % errors)
- elif copy_pforms:
- msgs = copy_plural_forms(msgs, locale, domain, verbosity)
- msgs = msgs.replace(
- "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "")
- f = open(pofile, 'wb')
- try:
- f.write(msgs)
- finally:
- f.close()
- os.unlink(potfile)
- if no_obsolete:
- msgs, errors = _popen('msgattrib %s %s -o "%s" --no-obsolete "%s"' %
- (wrap, location, pofile, pofile))
- if errors:
- raise CommandError(
- "errors happened while running msgattrib\n%s" % errors)
+ os.unlink(self.potfile)
+ raise CommandError("errors happened while running msguniq\n%s" % errors)
+ if os.path.exists(self.pofile):
+ f = open(self.potfile, 'w')
+ try:
+ f.write(msgs)
+ finally:
+ f.close()
+ msgs, errors = _popen('msgmerge %s %s -q "%s" "%s"' %
+ (wrap, location, self.pofile,self.potfile))
+ if errors:
+ os.unlink(self.potfile)
+ raise CommandError(
+ "errors happened while running msgmerge\n%s" % errors)
+ elif copy_pforms:
+ msgs = copy_plural_forms(msgs, self.locale_name, self.domain, verbosity)
+ msgs = msgs.replace(
+ "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "")
+ f = open(self.pofile, 'wb')
+ try:
+ f.write(msgs)
+ finally:
+ f.close()
+ os.unlink(self.potfile)
+ if no_obsolete:
+ msgs, errors = _popen('msgattrib %s %s -o "%s" --no-obsolete "%s"' %
+ (wrap, location, self.pofile, self.pofile))
+ if errors:
+ raise CommandError(
+ "errors happened while running msgattrib\n%s" % errors)
-def make_messages(locale=None, domain='django', verbosity=1, all=False,
+ def remove_potfile(self):
+ if os.path.exists(self.potfile):
+ os.unlink(self.potfile)
+
+
+class TranslatableFile(object):
+ def __init__(self, file, dirpath, domain, locale):
+ self.file = file
+ self.dirpath = dirpath
+ self.domain = domain
+ self.locale = locale
+ self.is_templatized = False
+
+ def cleanup(self):
+ if self.is_templatized:
+ os.unlink(self.work_file)
+
+
+def make_messages(locale=None, domain_name='django', verbosity=1, all=False,
extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False,
no_location=False, no_obsolete=False):
"""
Uses the ``locale/`` directory from the Django SVN tree or an
- application/project to process all files with translatable literals for
- the :param domain: domain and :param locale: locale.
+ application/project to process all the files with translatable literals for
+ the :param domain_name: domain and :param locale: locale.
"""
# Need to ensure that the i18n framework is enabled
from django.conf import settings
@@ -266,7 +320,6 @@
if ignore_patterns is None:
ignore_patterns = []
- invoked_for_django = False
if os.path.isdir(os.path.join('conf', 'locale')):
localedir = os.path.abspath(os.path.join('conf', 'locale'))
invoked_for_django = True
@@ -274,6 +327,7 @@
ignore_patterns += ['contrib/*']
elif os.path.isdir('locale'):
localedir = os.path.abspath('locale')
+ invoked_for_django = False
else:
raise CommandError("This script should be run from the Django SVN "
"tree or your project or app tree. If you did indeed run it "
@@ -283,10 +337,10 @@
"is not created automatically, you have to create it by hand "
"if you want to enable i18n for your project or application.")
- if domain not in ('django', 'djangojs'):
+ if domain_name not in ('django', 'djangojs'):
raise CommandError("currently makemessages only supports domains 'django' and 'djangojs'")
- if (locale is None and not all) or domain is None:
+ if (locale is None and not all) or domain_name is None:
message = "Type '%s help %s' for usage information." % (os.path.basename(sys.argv[0]), sys.argv[1])
raise CommandError(message)
@@ -310,27 +364,33 @@
wrap = '--no-wrap' if no_wrap else ''
location = '--no-location' if no_location else ''
- for locale in locales:
+ domains = (
+ DjangoJsDomain(extensions),
+ DjangoDomain(extensions),
+ )
+ for locale_name in locales:
if verbosity > 0:
- print "processing language", locale
- basedir = os.path.join(localedir, locale, 'LC_MESSAGES')
- if not os.path.isdir(basedir):
- os.makedirs(basedir)
-
- pofile = os.path.join(basedir, '%s.po' % domain)
- potfile = os.path.join(basedir, '%s.pot' % domain)
-
- if os.path.exists(potfile):
- os.unlink(potfile)
+ print "processing language", locale_name
+ locale = Locale(domain_name, localedir, locale_name)
for dirpath, file in find_files(".", ignore_patterns, verbosity,
symlinks=symlinks):
- process_file(file, dirpath, potfile, domain, verbosity, extensions,
- wrap, location)
+ for domain in domains:
+ if domain.is_mine(domain_name, file):
+ f = TranslatableFile(file, dirpath, domain, locale)
+ try:
+ msgs = domain.process_file(f, verbosity, wrap, location)
+ except CommandError:
+ locale.remove_potfile()
+ raise
+ else:
+ if msgs:
+ locale.write_pot_file(msgs, f)
+ finally:
+ f.cleanup()
- if os.path.exists(potfile):
- write_po_file(pofile, potfile, domain, locale, verbosity,
- not invoked_for_django, wrap, location, no_obsolete)
+ locale.write_po_file(verbosity, not invoked_for_django, wrap, location,
+ no_obsolete)
class Command(NoArgsCommand):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment