Globalization of application-specific content

Globalize application-specific content by using Python Babel, Flask Babel, and Jinja2 templates to .

Use the links to the following technologies to help you globalize your application-specific content:

  • Babel (http://babel.pocoo.org/) is the standard globalization package for Python.
  • Flask-Babel (https://pythonhosted.org/Flask-Babel/) is an extension to Flask.
  • Jinja2/Babel integration (http://jinja.pocoo.org/docs/dev/integration/) provides instructions on how to use Babel with Jinja2 templates.

Key concepts

For this example, you install the Flask-Babel pip package and its dependents into your app's src_deps/pip/ directory. The Flask-Babel package, provides the pybabel tool, which you can use to create translation files for your Flask-based app.

QRadar® passes the user's preferred locale in the Accept-Languages header attribute through in the request header to your app.

To use the Flask-Babel package to create globalization text values that your app employs, use the following workflow:

  1. Use the pybabel tool to extract out locale keys, typically to a .pot file.
  2. Use the pybabel tool to build templated .po files for each language set you want to support.
  3. Edit and complete the .po file. In other words, translate all the keys to the language-specific variant of the text value.
  4. Use the pybabel tool to compile the completed .po files into a binary set of .mo files that can be employed by your Flask python code, or your Jinja2 templates.

Pre-requisites

To work through this example, you must install the following Python packages into your app's src_deps/pip/ directory.

  • pytz-2015.6-py2.py3-none-any.whl
  • Babel-2.1.1-py2.py3-none-any.whl
  • speaklater-1.3.tar.gz
  • Flask-Babel-0.9.tar.gz

You must also create an ordering.txt file in the src_deps/pip/ directory that contains the following content:

pytz-2015.6-py2.py3-none-any.whl
Babel-2.1.1-py2.py3-none-any.whl
speaklater-1.3.tar.gz
Flask-Babel-0.9.tar.gz
Note: Use the pip install -d src_deps/pip flask-babel command from within the virtual environment that the SDK provides to download the dependencies for Flask-Babel.

Bundle python wheel (whl) files instead of .tar file (tar.gz) source files wherever possible. Some raw python source package .tar files need to use the gcc compiler or other tools that the base docker container that hosts your app code might not have.

Build python wheel files from package source tarballs on your local system. Here's an example:

tar -xvzf some_package.tar.gz
python setup.py sdist bdist_wheel

Jinja2 template: app/templates/index.html

This example builds on the HelloWorld sample app. The original HTML template was built with hardcoded English language-specific text values. The following example wraps the English locale strings values with Jinja2 directives that use the gettext functions from Flask-Babel. Here's an example:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>{{title}} - Main</title>
  <link rel="stylesheet" href="static/css/style.css">
</head>

<body>
<div id="pageheader" class="pagebanner">
<p>{{ _( 'IBM QRadar Application : Hello World' ) }}</p>
</div>

<div id="contentpane">
  <p>{{ _( 'Hello! and Welcome to the first Qradar app
           served from the AppFramework/Docker instance on your console' ) }}</p>
</div>

<div class="news-wrapper">
    <p class="quote">{{ _( 'Hello World' ) }}</p>
</div>

</body>
</html>

This example uses the shorthand alias for gettext function. You can also use the full form. For example:

 {{ gettext( '...') }} 

This method provides a useful mechanism to quickly build and prototype your app. You can return later to make it locale aware. By using the actual initial text as keys, you keep the template readable.

Note: Eliminate white space around the directive. For example, consider the use of an HTML <span>, <div> or other element. The pybabel tool has some difficulties to extract all key values.

Configure pybabel

You must configure the pybabel tool so that it is aware of what source files to examine.

[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

In this example, pybabel is configured to examine all Python source files in the app/ folder, and all HTML files in any sub directory of the app/templates/ folder.

The pybabel tool uses the babel.pyfile to know which directories or files to examine within your app for potential translatable entries. It looks for gettext(..) and _(..) entries in your *.py files and your Jinja2 templates to build into a local .pot file.

Create the .pot file

To create a .pot file, open a command line and type the following command from within the app/ folder:

pybabel extract -F babel.cfg -o messages.pot

A message.pot file is created in the app/ folder. Its content is similar to the following file:

# Translations template for PROJECT.
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
#
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-10-07 21:27+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"

#: templates/index.html:11
msgid "IBM QRadar Application : Hello World"
msgstr ""

#: templates/index.html:15
msgid ""
"Hello! and Welcome to the first Qradar app served from the "
"AppFramework/Docker instance on your console"
msgstr ""

#: templates/index.html:19
msgid "Hello World"
msgstr ""

You can use the msgid entries as your keys.

Create the .po files

You create individual language-specific .po files for Spanish, French, and English. From a command line, type the following commands from within the app/ folder:

pybabel init -i messages.pot -d translations -l es
pybabel init -i messages.pot -d translations -l fr
pybabel init -i messages.pot -d translations -l en
pybabel init -i messages.pot -d translations -l ja

These commands are used to create the translation files in the following locations:

  • app/translations/es/LC_Messages/messages.po
  • app/translations/fr/LC_Messages/messages.po
  • app/translations/en/LC_Messages/messages.po
  • app/translations/ja/LC_Messages/messages.po

The following example is the app/translations/es/LC_Messages/messages.po file that is generated:

# Spanish translations for PROJECT.
# Copyright (C) 2015 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
#
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-10-07 16:02+0100\n"
"PO-Revision-Date: 2015-10-07 16:04+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: es <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"

#: templates/index.html:11
msgid "IBM QRadar Application : Hello World"
msgstr ""

#: templates/index.html:15
msgid "Hello! and Welcome to the first Qradar app served from the "
"AppFramework/Docker instance on your console"
msgstr ""

#: templates/index.html:19
msgid "Hello World"
msgstr ""


Edit the .po files

You edit the .po files to add the language-specific text strings that QRadar uses to translate your app's content. For each msgid in the .po file, you must enter a corresponding msgstr value in the target language.

The following example is a code snippet from the app/translations/es/LC_Messages/messages.po file:

#: templates/index.html:19
msgid "Hello World"
msgstr "Hola Mundo"

Create the .mo files

To create the .mo files, open a command line and type the following command from within the app/ folder:

pybabel compile -d translation

This command compiles all your .po files into .mo files in the following locations:

  • app/translations/es/LC_Messages/messages.mo
  • app/translations/fr/LC_Messages/messages.mo
  • app/translations/en/LC_Messages/messages.mo
  • app/translations/ja/LC_Messages/messages.mo

The QRadar GUI app framework provides a default Flask environment that looks for locale-specific files in the sub directories of the app/translations/ folder.

To specify the UTF-8 encoded locales that your app supports, you can add a config.py file to the app/ folder. The file contains content similar to the following example:

# -*- coding: utf-8 -*-
# ...
# available languages
LANGUAGES = {
    'en': 'English',
    'es': 'Español',
    'fr': 'Français',
    'ja': '日本語'
}

This globalization support file helps QRadar to find the .mo file for each locale you specify.

After you create the .mo files, you can remove the .po, .pot, and babel.py files if you do not want these resources to be packaged with your app.

views.py

__author__ = 'IBM'

from flask import render_template, send_from_directory, request
from app import app
from flask.ext.babel import gettext   1 
from config import LANGUAGES
from qpylib import qpylib

from flask.ext.babel import Babel   
babel = Babel(app)   2 

@babel.localeselector   3 
def get_locale():
    return request.accept_languages.best_match(LANGUAGES.keys())

@app.route('/')
@app.route('/index')
def index():
    qpylib.log(request.headers.get('Accept-Language', ''))
    return render_template("index.html", title = "QApp1 : Hello World !")

The following list describes content from the views.py code snippet:

  1. The gettext method is imported from the Babel package. This line is optional but it is useful if you want to use locale text away from the python tier. In the Jinja2 template for this example, the gettext methods were used to extract key values.
  2. The Flask app is injected into a Babel context so that your app can render locale-specific text.
  3. This code applies the Babel localeselector decorator pattern across all your routes (in other words, any request that comes in from QRadar). The decorator uses the locales that are defined in the app/config.py file to connect the best-fit language-specific keys file to the incoming request.