Internationalization and Localization
Introduction
CppCMS has a sophisticated internationalization and localization support. During CppCMS development localization tools were submitted to Boost Project and accepted as Boost.Locale library.
So you can find exactly the same code in boost::locale
and booster::locale
namespace. The only difference is
the namespace. So I strongly recommend reading extensive
localization tutorials you can find for Boost.Locale
as they are applicable as-is for CppCMS's booster::locale
.
So please refer to Boost.Locale documentation for any problems or questions you have.
In this tutorial we would see how Boost.Locale is integrated to CppCMS and how to use it for CppCMS development.
Concepts
All locale related information is hold in std::locale
object. CppCMS allows to use context specific locale and
change it according to the needs.
Example
Lets create a simple web site that greets us in two languages English and Hebrew.
We would have following URLs:
/hello
- redirects to/hello/en_US
/hello/en_US
- greeting in English/hello/he_IL
- greeting in Hebrew
We would see how to set the application's locale and how to translate different messages from the templates and from the application itself.
Preparing the Sources
View
Let's define our view's render function
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > </head> <% if rtl %> <body dir="rtl"> <% else %> <body> <% end %> <h1><% gt "We want to say" %></h1> <p><%= message %></p> <p><a href="he_IL">he</a>/<a href="en_US">en</a></p> </body> <html>
Now let's note different parts step by step:
- Line 3: we use
UTF-8
encoding - which is the recommended default encoding Line 10:
<% gt "We want to say" %>
- show localized message."We want to say" would be translated to the locale we want to use using special dictionaries or if there is no translation - original string would be displayed.
line 5:
<% if rtl %>
- check if the language is Right-To-Left language like Hebrew. If it is we change the directionality of the text. Using this condition we can also alter different layout parameters.The condition would be true of the "LTR" message is translated to "RTL" message in our templates.
The rest is the usual.
Content
It would be very simple:
struct message : public cppcms::base_content { std::string message; };
We would see how we would be able to localize the message
as well.
Controller
We define our hello
class:
hello(cppcms::service &srv) : cppcms::application(srv) { dispatcher().assign("^/(en_US|he_IL)/?$",&hello::say_hello,this,1); dispatcher().assign("^(.*)$",&hello::redirect,this); }
In the first assignment we map between the URL and the function: say_hello
, we path the locale (he_IL or en_US) as a parameter.
We also define a simple redirect function that would redirects user to en_US
by default:
void redirect() { response().set_redirect_header(request().script_name() + "/en_US"); }
Now let's see how we handle a language:
void say_hello(std::string lang) { context().locale(lang + ".UTF-8"); content::message c; c.message=translate("Hello World"); render("message",c); }
First we define current locale as en_US.UTF-8
or he_IL.UTF-8
.
Now the correct locale would be associated with the output stream and with the current request/response context.
Then we translate a "Hello World" message using
cppcms::application::translate(...)
member functions
that use current context()
's locale.
Then we finally render the content. Because we setup the correct locale this would let the template engine know in which locale the message "We want to say" should be translated.
Please note, even when we do not define the locale explicitly, the default system locale would be used
or the first locale defined in localization.locales
list.
Extracting Messages
Now when we prepared all the sources we want to extract all the messages we want to translate.
Full Boost.Locale tutorial about message translation can be found here but let's see the process in few words.
The messages are distributed all over our source code: cpp files templates and so on. We can use xgettext
tool that
is a part of gettext
project to extract such messages,
however we need to have all C++ sources.
So we need to compile our view:
cppcms_tmpl_cc -d hello view.tmpl -o view.cpp
And then extract the messages from both view.cpp
and
our hello.cpp
using xgettext as following:
xgettext --keyword=translate:1,1t view.cpp hello.cpp
What we get is a file called messages.po
that would
look like:
msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" ... "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: view.tmpl:9 msgid "LTR" msgstr "" #: view.tmpl:14 msgid "We want to say" msgstr "" #: hello.cpp:25 msgid "Hello World" msgstr ""
That includes three messages:
- "LTR" - the directionality of the language.
- "We want to say" - extracted from the templates
- "Hello World" - extracted from the sources.
Now we can translate them to Hebrew and create a file "hello.po" that looks like:
msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: view.tmpl:6 msgid "LTR" msgstr "RTL" #: view.tmpl:11 msgid "We want to say" msgstr "אנחנו רוצים לומר" #: hello.cpp:21 msgid "Hello World" msgstr "שלום עולם"
Note: "LTR" is - translated to RTL as Hebrew is right-to-left language.
We create an appropriate directory structure to search files in:
locale/he/LC_MESSAGES/
Then we compile it to the binary mo file as:
msgfmt hello.po -o locale/he/LC_MESSAGES/hello.mo
This hello.mo
file can be used by the localization Engine.
Configuration
Now we need to tell CppCMS what locale to use and were to take the localization catalogs:
It is done via configuration file as following:
"localization" : { "messages" : { "paths" : [ "./locale" ], "domains" : [ "hello" ] }, "locales" : [ "he_IL.UTF-8", "en_US.UTF-8" ] }
localization.messages.paths
specifies the path to thelocale
directory the holdshe/LC_MESSAGES/hello.mo
catalog.localization.messages.domains
specifies the catalog name - in our casehello
andlocalization.locales
defines locales we want to support in our application. The first one becomes the default one.
Running Our Sample
So once we got following files:
./config.js # Configuration File ./hello # Executable ./locale ./locale/he ./locale/he/LC_MESSAGES ./locale/he/LC_MESSAGES/hello.mo # Messages Catalog
We can run our application
./hello -c config.js
And see how localization works :-)
by visiting
http://localhost:8080/hello URL.
← JSON RPC | Top | Advanced Caching →