IBM Support

Globalization of Application-Specific Content

How To


Summary

Learn how to globalize application-specific content by using Python Babel, Flask Babel, and Jinja2 templates

Steps

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

Key Concepts

For this example, you install the Flask-Babel pip package and its dependents into your app's container/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.

Prerequisites

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

  • pytz
  • Babel
  • speaklater
  • Flask-Babel

You must also create an ordering.txt file in the container/pip/ directory that contains the above python packages listed in the order they are to be installed.

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/hello.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>
  <head>
    <title>{{ _( 'Hello from Flask' ) }}</title>
    <link href="static/styles.css" rel="stylesheet">
  </head>
  <body>
    {% if name %}
    <p>{{ _( 'Hello' ) }}, {{ name }}!</p>
    {% else %}
    <p>{{ _( 'Hello, World!' ) }}</p>
    {% endif %}
  </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. Create a file called babel.cfg and add it under the app directory with the following contents

[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.cfg file 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) 2020 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-09-03 14:53+0000\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 2.8.0\n"

#: templates/hello.html:4
msgid "Hello from Flask"
msgstr ""

#: templates/hello.html:9
msgid "Hello"
msgstr ""

#: templates/hello.html:11
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) 2020 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-09-03 14:53+0000\n"
"PO-Revision-Date: 2020-09-03 14:59+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es\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 2.8.0\n"

#: templates/hello.html:4
msgid "Hello from Flask"
msgstr ""

#: templates/hello.html:9
msgid "Hello"
msgstr ""

#: templates/hello.html:11
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:11
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 translations

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.cfg files if you do not want these resources to be packaged with your app.

Update init.py

__author__ = 'IBM'

# pylint: disable=import-outside-toplevel, unused-variable

from flask import Flask
from flask import request
from qpylib import qpylib
from flask_babel import Babel
from .config import LANGUAGES

babel = Babel()

def get_locale():
    return request.accept_languages.best_match(list(LANGUAGES.keys()))

# Flask application factory.
def create_app():
    # Create a Flask instance.
    qflask = Flask(__name__)
    babel.init_app(qflask)
    babel.localeselector(get_locale)

    # Retrieve QRadar app id.
    qradar_app_id = qpylib.get_app_id()

    # Create unique session cookie name for this app.
    qflask.config['SESSION_COOKIE_NAME'] = 'session_{0}'.format(qradar_app_id)

    # Hide server details in endpoint responses.
    @qflask.after_request
    def obscure_server_header(resp):
        resp.headers['Server'] = 'QRadar App {0}'.format(qradar_app_id)
        return resp

    # Register q_url_for function for use with Jinja2 templates.
    qflask.add_template_global(qpylib.q_url_for, 'q_url_for')

    # Import blueprint endpoints.
    from . import views
    qflask.register_blueprint(views.views_blueprint)
    from . import dev
    qflask.register_blueprint(dev.dev_blueprint)

    # Initialize logging.
    qpylib.create_log()

    # To enable app health checking, the QRadar App Framework
    # requires every Flask app to define a /debug endpoint.
    # The endpoint function should contain a trivial implementation
    # that returns a simple confirmation response message.
    @qflask.route('/debug')
    def debug():
        return 'Pong!'

    return qflask

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

  1. The Flask app is injected into a Babel context so that your app can render locale-specific text.
  2. 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.

Document Location

Worldwide

[{"Line of Business":{"code":"LOB24","label":"Security Software"},"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Product":{"code":"SSBQAC","label":"IBM Security QRadar SIEM"},"ARM Category":[{"code":"a8m0z000000cwt3AAA","label":"QRadar Apps"}],"ARM Case Number":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"All Version(s)"}]

Document Information

Modified date:
30 March 2021

UID

ibm16437435