Commit 7fca4416 authored by Ramiro Morales's avatar Ramiro Morales
Browse files

Made (make|compile)messages check for availability of gettext commands.

Refs #19584.
parent 3f43f5f3
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ import os
from optparse import make_option

from django.core.management.base import BaseCommand, CommandError
from django.core.management.utils import popen_wrapper
from django.core.management.utils import find_command, popen_wrapper
from django.utils._os import npath

def has_bom(fn):
@@ -16,6 +16,10 @@ def has_bom(fn):
            sample.startswith(codecs.BOM_UTF16_BE)

def compile_messages(stderr, locale=None):
    program = 'msgfmt'
    if find_command(program) is None:
        raise CommandError("Can't find %s. Make sure you have GNU gettext tools 0.15 or newer installed." % program)

    basedirs = [os.path.join('conf', 'locale'), 'locale']
    if os.environ.get('DJANGO_SETTINGS_MODULE'):
        from django.conf import settings
@@ -42,7 +46,6 @@ def compile_messages(stderr, locale=None):
                    if has_bom(fn):
                        raise CommandError("The %s file has a BOM (Byte Order Mark). Django only supports .po files encoded in UTF-8 and without any BOM." % fn)
                    pf = os.path.splitext(fn)[0]
                    program = 'msgfmt'
                    args = [program, '--check-format', '-o', npath(pf + '.mo'), npath(pf + '.po')]
                    output, errors, status = popen_wrapper(args)
                    if status:
+77 −35
Original line number Diff line number Diff line
@@ -5,11 +5,11 @@ import re
import sys
from itertools import dropwhile
from optparse import make_option
from subprocess import PIPE, Popen

import django
from django.core.management.base import CommandError, NoArgsCommand
from django.core.management.utils import handle_extensions
from django.core.management.utils import (handle_extensions, find_command,
    popen_wrapper)
from django.utils.functional import total_ordering
from django.utils.text import get_text_list
from django.utils.jslex import prepare_js_for_gettext
@@ -18,6 +18,13 @@ plural_forms_re = re.compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILI
STATUS_OK = 0


def check_programs(*programs):
    for program in programs:
        if find_command(program) is None:
            raise CommandError("Can't find %s. Make sure you have GNU "
                    "gettext tools 0.15 or newer installed." % program)


@total_ordering
class TranslatableFile(object):
    def __init__(self, dirpath, file_name):
@@ -58,12 +65,24 @@ class TranslatableFile(object):
            work_file = os.path.join(self.dirpath, thefile)
            with open(work_file, "w") as fp:
                fp.write(src_data)
            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, command.wrap, command.location, work_file))
            args = [
                'xgettext',
                '-d', domain,
                '--language=C',
                '--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',
                '--output=-'
            ]
            if command.wrap:
                args.append(command.wrap)
            if command.location:
                args.append(command.location)
            args.append(work_file)
        elif domain == 'django' and (file_ext == '.py' or file_ext in command.extensions):
            thefile = self.file
            orig_file = os.path.join(self.dirpath, self.file)
@@ -76,18 +95,32 @@ class TranslatableFile(object):
                with open(os.path.join(self.dirpath, thefile), "w") as fp:
                    fp.write(content)
            work_file = os.path.join(self.dirpath, thefile)
            cmd = (
                'xgettext -d %s -L Python %s %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 - "%s"' %
                (domain, command.wrap, command.location, work_file))
            args = [
                'xgettext',
                '-d', domain,
                '--language=Python',
                '--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',
                '--output=-'
            ]
            if command.wrap:
                args.append(command.wrap)
            if command.location:
                args.append(command.location)
            args.append(work_file)
        else:
            return
        msgs, errors, status = _popen(cmd)
        msgs, errors, status = popen_wrapper(args)
        if errors:
            if status != STATUS_OK:
                if is_templatized:
@@ -109,15 +142,6 @@ class TranslatableFile(object):
        if is_templatized:
            os.unlink(work_file)


def _popen(cmd):
    """
    Friendly wrapper around Popen for Windows
    """
    p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
    output, errors = p.communicate()
    return output, errors, p.returncode

def write_pot_file(potfile, msgs):
    """
    Write the :param potfile: POT file with the :param msgs: contents,
@@ -225,8 +249,9 @@ class Command(NoArgsCommand):
                    "is not created automatically, you have to create it by hand "
                    "if you want to enable i18n for your project or application.")

        check_programs('xgettext')
        # We require gettext version 0.15 or newer.
        output, errors, status = _popen('xgettext --version')
        output, errors, status = popen_wrapper(['xgettext', '--version'])
        if status != STATUS_OK:
            raise CommandError("Error running xgettext. Note that Django "
                        "internationalization requires GNU gettext 0.15 or newer.")
@@ -248,6 +273,9 @@ class Command(NoArgsCommand):
            locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))
            locales = [os.path.basename(l) for l in locale_dirs]

        if locales:
            check_programs('msguniq', 'msgmerge', 'msgattrib')

        try:
            for locale in locales:
                if self.verbosity > 0:
@@ -307,8 +335,13 @@ class Command(NoArgsCommand):

        Uses mguniq, msgmerge, and msgattrib GNU gettext utilities.
        """
        msgs, errors, status = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
                                        (self.wrap, self.location, potfile))
        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(
@@ -324,8 +357,13 @@ class Command(NoArgsCommand):
        if os.path.exists(pofile):
            with open(potfile, 'w') as fp:
                fp.write(msgs)
            msgs, errors, status = _popen('msgmerge %s %s -q "%s" "%s"' %
                                            (self.wrap, self.location, pofile, potfile))
            args = ['msgmerge', '-q']
            if self.wrap:
                args.append(self.wrap)
            if self.location:
                args.append(self.location)
            args.extend([pofile, potfile])
            msgs, errors, status = popen_wrapper(args)
            if errors:
                if status != STATUS_OK:
                    raise CommandError(
@@ -340,9 +378,13 @@ class Command(NoArgsCommand):
            fp.write(msgs)

        if self.no_obsolete:
            msgs, errors, status = _popen(
                'msgattrib %s %s -o "%s" --no-obsolete "%s"' %
                (self.wrap, self.location, pofile, pofile))
            args = ['msgattrib', '-o', pofile, '--no-obsolete']
            if self.wrap:
                args.append(self.wrap)
            if self.location:
                args.append(self.location)
            args.append(pofile)
            msgs, errors, status = popen_wrapper(args)
            if errors:
                if status != STATUS_OK:
                    raise CommandError(
+37 −3
Original line number Diff line number Diff line
from __future__ import absolute_import

import os
from subprocess import PIPE, Popen
import sys

from django.utils.encoding import force_text, DEFAULT_LOCALE_ENCODING
from django.utils import six

from .base import CommandError


def popen_wrapper(args):
def popen_wrapper(args, os_err_exc_type=CommandError):
    """
    Friendly wrapper around Popen.

    Returns stdout output, stderr output and OS status code.
    """
    try:
        p = Popen(args, shell=False, stdout=PIPE, stderr=PIPE,
                close_fds=os.name != 'nt', universal_newlines=True)
    except OSError as e:
        six.reraise(os_err_exc_type, os_err_exc_type('Error executing %s: %s' %
                    (args[0], e.strerror)), sys.exc_info()[2])
    output, errors = p.communicate()
    return (
        output,
@@ -43,3 +53,27 @@ def handle_extensions(extensions=('html',), ignored=('py',)):
        if not ext.startswith('.'):
            ext_list[i] = '.%s' % ext_list[i]
    return set([x for x in ext_list if x.strip('.') not in ignored])

def find_command(cmd, path=None, pathext=None):
    if path is None:
        path = os.environ.get('PATH', []).split(os.pathsep)
    if isinstance(path, six.string_types):
        path = [path]
    # check if there are funny path extensions for executables, e.g. Windows
    if pathext is None:
        pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD').split(os.pathsep)
    # don't use extensions if the command ends with one of them
    for ext in pathext:
        if cmd.endswith(ext):
            pathext = ['']
            break
    # check if we find the command on PATH
    for p in path:
        f = os.path.join(p, cmd)
        if os.path.isfile(f):
            return f
        for ext in pathext:
            fext = f + ext
            if os.path.isfile(fext):
                return fext
    return None
+7 −2
Original line number Diff line number Diff line
@@ -131,8 +131,13 @@ class BasicExtractorTests(ExtractorTests):
        os.chdir(self.test_dir)
        shutil.copyfile('./code.sample', './code_sample.py')
        stdout = StringIO()
        try:
            management.call_command('makemessages', locale=LOCALE, stdout=stdout)
        finally:
            try:
                os.remove('./code_sample.py')
            except OSError:
                pass
        self.assertIn("code_sample.py:4", force_text(stdout.getvalue()))

    def test_template_message_context_extractor(self):
+1 −25
Original line number Diff line number Diff line
@@ -2,35 +2,11 @@ import os
import re
from subprocess import Popen, PIPE

from django.utils import six
from django.core.management.utils import find_command

can_run_extraction_tests = False
can_run_compilation_tests = False

def find_command(cmd, path=None, pathext=None):
    if path is None:
        path = os.environ.get('PATH', []).split(os.pathsep)
    if isinstance(path, six.string_types):
        path = [path]
    # check if there are funny path extensions for executables, e.g. Windows
    if pathext is None:
        pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD').split(os.pathsep)
    # don't use extensions if the command ends with one of them
    for ext in pathext:
        if cmd.endswith(ext):
            pathext = ['']
            break
    # check if we find the command on PATH
    for p in path:
        f = os.path.join(p, cmd)
        if os.path.isfile(f):
            return f
        for ext in pathext:
            fext = f + ext
            if os.path.isfile(fext):
                return fext
    return None

# checks if it can find xgettext on the PATH and
# imports the extraction tests if yes
xgettext_cmd = find_command('xgettext')
Loading