i18n.py 7.45 KB
Newer Older
1 2 3
"""
Internationalization tasks
"""
4 5

import re
6 7
import sys
import subprocess
8

9
from path import Path as path
10
from paver.easy import task, cmdopts, needs, sh
11

12
from .utils.cmd import django_cmd
13
from .utils.timer import timed
14 15 16 17

try:
    from pygments.console import colorize
except ImportError:
18
    colorize = lambda color, text: text
19

20 21
DEFAULT_SETTINGS = 'devstack'

22 23 24

@task
@needs(
25
    "pavelib.prereqs.install_prereqs",
26 27 28 29 30 31
    "pavelib.i18n.i18n_validate_gettext",
    "pavelib.assets.compile_coffeescript",
)
@cmdopts([
    ("verbose", "v", "Sets 'verbose' to True"),
])
32
@timed
33 34 35 36 37
def i18n_extract(options):
    """
    Extract localizable strings from sources
    """
    verbose = getattr(options, "verbose", None)
38
    cmd = "i18n_tool extract"
39 40 41 42 43 44 45 46

    if verbose:
        cmd += " -vv"

    sh(cmd)


@task
47
@timed
louyihua committed
48 49 50 51
def i18n_fastgenerate():
    """
    Compile localizable strings from sources without re-extracting strings first.
    """
52
    sh("i18n_tool generate")
louyihua committed
53 54 55


@task
56
@needs("pavelib.i18n.i18n_extract")
57
@timed
58 59 60 61
def i18n_generate():
    """
    Compile localizable strings from sources, extracting strings first.
    """
62
    sh("i18n_tool generate")
63 64 65 66


@task
@needs("pavelib.i18n.i18n_extract")
67
@timed
68 69 70 71 72
def i18n_generate_strict():
    """
    Compile localizable strings from sources, extracting strings first.
    Complains if files are missing.
    """
73
    sh("i18n_tool generate --strict")
74 75 76 77


@task
@needs("pavelib.i18n.i18n_extract")
78 79 80
@cmdopts([
    ("settings=", "s", "The settings to use (defaults to devstack)"),
])
81
@timed
82
def i18n_dummy(options):
83 84 85 86
    """
    Simulate international translation by generating dummy strings
    corresponding to source strings.
    """
87 88
    settings = options.get('settings', DEFAULT_SETTINGS)

89
    sh("i18n_tool dummy")
90
    # Need to then compile the new dummy strings
91
    sh("i18n_tool generate")
92

93 94
    # Generate static i18n JS files.
    for system in ['lms', 'cms']:
95
        sh(django_cmd(system, settings, 'compilejsi18n'))
96

97 98

@task
99
@timed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
def i18n_validate_gettext():
    """
    Make sure GNU gettext utilities are available
    """

    returncode = subprocess.call(['which', 'xgettext'])

    if returncode != 0:
        msg = colorize(
            'red',
            "Cannot locate GNU gettext utilities, which are "
            "required by django for internationalization.\n (see "
            "https://docs.djangoproject.com/en/dev/topics/i18n/"
            "translation/#message-files)\nTry downloading them from "
            "http://www.gnu.org/software/gettext/ \n"
        )

        sys.stderr.write(msg)
        sys.exit(1)


@task
122
@timed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
def i18n_validate_transifex_config():
    """
    Make sure config file with username/password exists
    """
    home = path('~').expanduser()
    config = home / '.transifexrc'

    if not config.isfile or config.getsize == 0:
        msg = colorize(
            'red',
            "Cannot connect to Transifex, config file is missing"
            " or empty: {config} \nSee "
            "http://help.transifex.com/features/client/#transifexrc \n".format(
                config=config,
            )
        )

        sys.stderr.write(msg)
        sys.exit(1)


@task
@needs("pavelib.i18n.i18n_validate_transifex_config")
146
@timed
147 148 149 150
def i18n_transifex_push():
    """
    Push source strings to Transifex for translation
    """
151
    sh("i18n_tool transifex push")
152 153 154 155


@task
@needs("pavelib.i18n.i18n_validate_transifex_config")
156
@timed
157 158 159 160
def i18n_transifex_pull():
    """
    Pull translated strings from Transifex
    """
161
    sh("i18n_tool transifex pull")
162 163 164


@task
165
@timed
Sarina Canelake committed
166
def i18n_rtl():
Sarina Canelake committed
167 168 169
    """
    Pull all RTL translations (reviewed AND unreviewed) from Transifex
    """
170
    sh("i18n_tool transifex rtl")
Sarina Canelake committed
171

172
    print "Now generating langugage files..."
Sarina Canelake committed
173

174
    sh("i18n_tool generate --rtl")
Sarina Canelake committed
175

176
    print "Committing translations..."
Sarina Canelake committed
177 178 179
    sh('git clean -fdX conf/locale')
    sh('git add conf/locale')
    sh('git commit --amend')
Sarina Canelake committed
180 181 182


@task
183
@timed
Sarina Canelake committed
184 185 186 187
def i18n_ltr():
    """
    Pull all LTR translations (reviewed AND unreviewed) from Transifex
    """
188
    sh("i18n_tool transifex ltr")
Sarina Canelake committed
189

190
    print "Now generating langugage files..."
Sarina Canelake committed
191

192
    sh("i18n_tool generate --ltr")
Sarina Canelake committed
193

194
    print "Committing translations..."
Sarina Canelake committed
195 196 197
    sh('git clean -fdX conf/locale')
    sh('git add conf/locale')
    sh('git commit --amend')
Sarina Canelake committed
198

199

Sarina Canelake committed
200
@task
201
@needs(
202
    "pavelib.i18n.i18n_clean",
203 204 205 206 207
    "pavelib.i18n.i18n_transifex_pull",
    "pavelib.i18n.i18n_extract",
    "pavelib.i18n.i18n_dummy",
    "pavelib.i18n.i18n_generate_strict",
)
208
@timed
209 210 211 212
def i18n_robot_pull():
    """
    Pull source strings, generate po and mo files, and validate
    """
213

214 215 216
    # sh('paver test_i18n')
    # Tests were removed from repo, but there should still be tests covering the translations
    # TODO: Validate the recently pulled translations, and give a bail option
217 218
    sh('git clean -fdX conf/locale/rtl')
    sh('git clean -fdX conf/locale/eo')
219
    print "\n\nValidating translations with `i18n_tool validate`..."
220
    sh("i18n_tool validate")
221 222 223 224 225

    con = raw_input("Continue with committing these translations (y/n)? ")

    if con.lower() == 'y':
        sh('git add conf/locale')
226 227
        sh('git add cms/static/js/i18n')
        sh('git add lms/static/js/i18n')
228 229

        sh(
230 231
            'git commit --message='
            '"Update translations (autogenerated message)" --edit'
232
        )
233 234 235


@task
236
@timed
237 238 239 240 241 242 243 244
def i18n_clean():
    """
    Clean the i18n directory of artifacts
    """
    sh('git clean -fdX conf/locale')


@task
245 246 247 248
@needs(
    "pavelib.i18n.i18n_extract",
    "pavelib.i18n.i18n_transifex_push",
)
249
@timed
250 251 252 253 254
def i18n_robot_push():
    """
    Extract new strings, and push to transifex
    """
    pass
255 256 257 258 259 260 261


@task
@needs(
    "pavelib.i18n.i18n_validate_transifex_config",
    "pavelib.i18n.i18n_generate",
)
262
@timed
263 264 265 266 267 268 269 270 271 272 273 274
def i18n_release_push():
    """
    Push release-specific resources to Transifex.
    """
    resources = find_release_resources()
    sh("i18n_tool transifex push " + " ".join(resources))


@task
@needs(
    "pavelib.i18n.i18n_validate_transifex_config",
)
275
@timed
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
def i18n_release_pull():
    """
    Pull release-specific translations from Transifex.
    """
    resources = find_release_resources()
    sh("i18n_tool transifex pull " + " ".join(resources))


def find_release_resources():
    """
    Validate the .tx/config file for release files, returning the resource names.

    For working with release files, the .tx/config file should have exactly
    two resources defined named "release-*".  Check that this is true.  If
    there's a problem, print messages about it.

292 293
    Returns a list of resource names, or raises ValueError if .tx/config
    doesn't have two resources.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

    """
    # An entry in .tx/config for a release will look like this:
    #
    #    [edx-platform.release-dogwood]
    #    file_filter = conf/locale/<lang>/LC_MESSAGES/django.po
    #    source_file = conf/locale/en/LC_MESSAGES/django.po
    #    source_lang = en
    #    type = PO
    #
    #    [edx-platform.release-dogwood-js]
    #    file_filter = conf/locale/<lang>/LC_MESSAGES/djangojs.po
    #    source_file = conf/locale/en/LC_MESSAGES/djangojs.po
    #    source_lang = en
    #    type = PO

    rx_release = r"^\[([\w-]+\.release-[\w-]+)\]$"
    with open(".tx/config") as tx_config:
        resources = re.findall(rx_release, tx_config.read(), re.MULTILINE)

    if len(resources) == 2:
        return resources

    if len(resources) == 0:
318
        raise ValueError("You need two release-* resources defined to use this command.")
319
    else:
320 321
        msg = "Strange Transifex config! Found these release-* resources:\n" + "\n".join(resources)
        raise ValueError(msg)