
Translation management
Software localization
Tornado is a Python web framework and an asynchronous networking library that relies on non-blocking network I/O to serve web applications. It is the preferred choice for any project that requires a long-lived connection to each user.
One of the main advantages of the Tornado web framework is its built-in internationalization support. This allows developers to build multilingual web applications right away—without going through the hassle of installing other libraries or frameworks for internationalization.
In this step-by-step tutorial, we'll cover everything you need to know about implementing i18n in Tornado. You can find the source code and all project files in our GitHub repo.
To start off, let's create a new virtual environment—it is a good habit to create a new virtual environment for each project.
Activate the virtual environment and run the following command:
pip install tornado
For your information, there are two ways to load language files in Tornado:
To keep things simple and short, this tutorial will stay focused CSV files.
Create a new folder called "locale" in your working directory. Then, create the following CSV files inside the "locale" folder:
Each translation file should have the following columns.
For example, a new translation file should look like this:
home,Home
"home" is the key while "Home" the translation text.
Add the following translations to the en_US.csv file.
home,Home plans,Plans ... promises-description,High-quality product to customers,singular promises-description,High-quality products to customers,plural created-by,Created by %(author)s total-view,%(view)d total views
Repeat the same step for the German translation using the following data:
home,Zuhause plans,Pläne ... promises-description,Hochwertige Produkt für Kunden,singular promises-description,Hochwertige Produkte für Kunden,plural created-by,Erstellt von %(author)s total-view,%(view)d Gesamtansichten
The landing page for this tutorial is based on a strip-down version of W3CSS marketing templates. You can find the complete code at the following link. It will look like this when rendered:
Create a new folder called templates and save the entire HTML file as index.html inside it.
Inside the HTML file, string translation is done via a global function with the following syntax:
_("key")
For example, to properly translate...
home,Home
...you should use the following syntax inside your HTML file:
<a href="...">{{ _("home") }}</a>
In addition, the global function also has another form that accepts three input parameters:
If the third argument is 1, it will return the first translation text. Otherwise, it will return the second translation text. The following code illustrates the code for pluralization in the HTML file, where "num" is an integer variable.
<p>{{ _("person liked this", "people liked this", num) }}</p>
The example given above only works for a single language. If you intend to support multiple languages, you should pass in a global function as an argument instead of a plain string.
<p>{{ _(_("liked-this"), _("liked-this"), num) }}</p>
"liked-this" represents the key for the translation based on the following translation data:
liked-this,person liked this,singular liked-this,people liked this,plural
Besides, you can insert a Python-style named placeholder inside any translation. It follows the syntax below:
%(name)s
"name" represents the placeholder's name while "s" is the string data type. If you have an integer, you should use the following instead:
%(count)d
Have a look at the following example that showcases a translation file with placeholders:
created-by,Created by %(author)s total-view,%(view)d total views
Previously, the following translations are defined inside the en_US.csv file.
created-by,Created by %(author)s total-view,%(view)d total views
In order to use it inside HTML, you should code it as follows:
<p>{{ _("created-by") % {"author": author} }} </p> <p>{{ _("total-view") % {"view": view} }} </p>
"author" and "view" are variables that will be passed directly from the main Python file later on.
Let's explore a few useful built-in functions that can be called programmatically inside any Python file.
The basics of internationalization are above all mirrored in loading all the translation files dynamically. You can do so via the "load_translation" function.
tornado.locale.load_translations('locale/')
It accepts two input parameters:
Please note that the "locale" directory is not a convention used by Tornado. You can name it anything that you preferred. Just make sure to provide the correct path when calling the "load_translations" function.
Supported locales are determined from the directory loaded by the "load_translations" function. You can check all the supported locales via the following function call:
tornado.locale.get_supported_locales()
It will return a frozenset. Each element represents a locale and is based on the name of the translation files.
frozenset({"de_DE", "en_US"})
Setting the default fallback locale is as simple as running the following function:
tornado.locale.set_default_locale('de_DE')
By default, it will use "en_US" if you have not specified the default locale.
Once you have loaded the translation files, you can get a locale object and obtain the corresponding translation as follows:
user_locale = tornado.locale.get("de_DE") text = user_locale.translate("home") # returns Zuhause
Optionally, the "translate" function also accepts "plural message" and "count".
text = user_locale.translate("promises-description", "promises-description", 3) # returns Hochwertige Produkte für Kunden
Once you are done with the basics of i18n, create a new Python file called "myapp.py".
Add the following import declaration at the top of your "myapp.py" file.
import tornado.ioloop import tornado.web
Next, add the following code below it:
class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") class LocaleHandler(tornado.web.RequestHandler): def get(self, locale): self.locale = tornado.locale.get(locale) self.render("index.html", product=1, author='Wai Foong', view=1234)
Both classes serve as route handlers. The first class returns "Hello, world" as a text response. By default, "tornado.web.RequestHandler" will capture the user's locale based on the "Accept-Language" header sent by the user's browser. You can obtain it via as follows:
class MainHandler(tornado.web.RequestHandler): def get(self): user_locale = self.get_user_locale() # user_locale is None if there is no Accept_Language header self.write("Hello, world")
On the other hand, the second class returns the rendered HTML page of "index.html". Unlike the first handler, the second one accepts another argument that represents the locale obtained via a RESTful API. Then, it will use the input locale and attempt to set the current locale via the "get" function.
The function also returns three additional variables:
Implement the following code that serves as application context for our "myapp.py" file.
def make_app(): return tornado.web.Application([ (r"/", MainHandler), (r"/([^/]+)/about-us", LocaleHandler), ], template_path='templates/')
The following regex is used:
([^/]+)
When serving the "about-us" route, this regex will capture any string and map it as the first argument to "LocaleHandler".
r"/([^/]+)/about-us" # capture en_US as locale for http://<ip>:<port>/en_US/about-us r/"about-us/([^/]+)" # capture en_US as locale for http://<ip>:<port>/about-us/en_US
Finally, add the following main function to the "myapp.py" file.
if __name__ == "__main__": tornado.locale.load_translations('locale/') app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
It will load translations from the "locale" folder and serve the app at port 8888.
🗒 Note » You can find the complete code for "myapp.py" via the following link.
Run the following command in the terminal to start the Tornado server.
python myapp.py
Open up a browser and head over to the following URL:
http://localhost:8888/en_US/about-us
You should see the following web interface:
The footer looks something like this:
Feel free to change the product variable to a number larger than one in the following line of code:
# change product to 3 self.render("index.html", product=1, author='Wai Foong', view=1234)
Re-run the server, and you should notice that the translation will be "3 High-quality products to customers".
Next, let's test out German translation by changing the URL as follows:
http://localhost:8888/de_DE/about-us
The web interface should be as follows:
The Tornado web framework is a powerful yet lightweight library to serve web applications in Python. Its built-in internationalization support is a big plus for multilingual software projects.
If you want to learn even more about Python i18n, make sure you check out the following guides as well:
Finally, if you want to improve your i18n process, consider signing up for Phrase, the most reliable software localization platform on the market. It comes with a 14-day free trial.
Phrase will equip you with everything you need to:
Last updated on October 20, 2022.