Localizing dates in a Perl web application
Tags: perl, webdev, l10n, mojolicious
Published: 2021-04-20 14:06:37

---
title: Localizing dates in a Perl web application
published: true
date: 2021-04-20 14:00:00 UTC
tags: Perl,webdev,L10n,Mojolicious
canonical_url: https://phoenixtrap.com/2021/04/20/localizing-dates-in-a-perl-web-application/?utm_source=rss&utm_medium=rss&utm_campaign=localizing-dates-in-a-perl-web-application
---

When we’re writing software for a global audience, it’s nice if we can provide it according to their native languages and conventions. Translating all of the text can be a huge undertaking, but **we can start small** by making sure that when we show the day and date it appears as the user expects. For example, to me it’s Tuesday, April 20, 2021; to my friend [Paul](http://www.leonerd.org.uk/) in the UK it’s Tuesday, 20 April 2021 (note the difference in order), and to my other friend [Gábor](https://szabgab.com/) in Israel it’s יום שלישי, 20 באפריל 2021 (note the different direction of the text, different language, and character set).

Thankfully, we have a number of tools to assist us:

- The [DateTime::Locale](https://metacpan.org/pod/DateTime::Locale) library, which enables our Perl software to **represent dates and times globally** and contains a [catalog of locales](https://metacpan.org/pod/DateTime::Locale::Catalog). It works with the [DateTime](https://metacpan.org/pod/DateTime) library for storing our dates as objects that can be easily manipulated and formatted.
- The [HTTP Accept-Language header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language), which lets a web browser communicate to the server **what natural languages and locale variants** the user understands.
- The [HTTP::AcceptLanguage](https://metacpan.org/pod/HTTP::AcceptLanguage) module, which helps us **parse the Accept-Language header** and select a compatible locale.

Our sample code uses the [Mojolicious](https://mojolicious.org/) framework and is very simple; almost half of it is just HTML web page templates. You could easily adapt it to other frameworks or templating systems.

{% gist https://gist.github.com/mjgardner/77e8e8d7e1d91dab99b73c0a78111d9f %}

**Lines 1 through 6** tell our code to use the Perl interpreter in our execution `PATH` and load our prerequisite modules. Note we’re using the micro version of Mojolicious, [Mojolicious::Lite](https://docs.mojolicious.org/Mojolicious/Lite); later you can [grow your application](https://docs.mojolicious.org/Mojolicious/Guides/Growing) into a well-structured Mojolicious app. We’re also using Perl [subroutine signatures](https://perldoc.pl/perlsub#Signatures), which requires Perl 5.20 or later (released in 2014).

**Lines 8 and 9** preload all of the available [DateTime::Locale](https://metacpan.org/pod/DateTime::Locale) objects so that we can serve requests faster without having to load a new locale every time. We create a hash where the keys are the locale identifiers (for example, `en-US` for United States English), and the values are the locale objects.

**Lines 11 and 12** begin our route handler for HTTP GET requests on the default `/` route in our web application. When a browser hits the home page of our app, it will execute the code in the anonymous sub in line 11, which is passed the [controller](https://docs.mojolicious.org/Mojolicious/Controller) object as `$c`. It’s a very simple handler that renders a template called `index` (described below), passing it a `date` object with today’s date.

**Lines 14 through 22** are where the smarts of our application lie. It’s a [helper](https://docs.mojolicious.org/Mojolicious/Lite#helper) that we’ll call from our template to localize a date object, and it’s another anonymous sub. This time it’s passed a [Mojolicious controller](https://docs.mojolicious.org/Mojolicious/Controller) as `$c`, a `$date` parameter that defaults to today, and a `$format` parameter that defaults to ‘full’.

**Lines 16 and 17** in the helper get our locale. Working from the inside out, we get the [HTTP Accept-Language](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) header from the request on line 17, create a new [HTTP::AcceptLanguage](https://metacpan.org/pod/HTTP::AcceptLanguage) object in line 16 for parsing that header, and then match it against the keys in our global `%locales` hash in line 17. That matched key then looks up the appropriate [DateTime::Locale](https://metacpan.org/pod/DateTime::Locale) object from the hash.

With **line 19**, we’re selecting the method name to call on our `$locale` object based on the `$format` passed into the helper. Finally, in **lines 20 and 21** we clone our `$date` (because it would be rude to modify it unexpectedly), set its locale, and format it based on the [CLDR (Common Locale Data Repository) formatting pattern](https://metacpan.org/pod/DateTime#CLDR-Patterns) returned by the method selected in the line above.

At last, **line 24** starts the application. To run it using the [development server](https://docs.mojolicious.org/morbo) included with Mojolicious, do this at the command line:

```
$ morbo perl_date_locale.pl
```

There are [other options for deploying your application](https://docs.mojolicious.org/Mojolicious/Guides/Cookbook#DEPLOYMENT), including Mojolicious’ built-in web server, inside a container, using other web servers, etc.

The rest of the above script is in the [` __DATA__ ` portion](https://perldoc.pl/perldata#Special-Literals) and contains two pseudo-files that Mojolicious [knows how to read](https://docs.mojolicious.org/Mojo/Loader) in the absence of actual templates and layouts. **First on line 27** is the actual `index.html.ep` HTML page, which uses Mojolicious’ [Embedded Perl](https://docs.mojolicious.org/Mojolicious/Guides/Rendering#Embedded-Perl) (ep) templating system to select a layout of shared HTML to use (the `layouts/default.html.ep` file **starting on line 38**).

**Lines 31 through 36** render an HTML unordered list that runs through the various formatting options available to our `localize_date` helper, first with the default ‘full’ formatting, and then a loop through ‘long’, ‘medium’, and ‘short’. Note that we call our helper as an expression, with an equals (`=`) sign after the percent (`%`) sign.

If you want to test different locales without changing your browser or operating system settings, you can invoke the script from the command line along with the HTTP request and headers to pass along. Here’s an example using German:

```
$ perl perl_date_locale.pl get -H 'Accept-Language: de' /
[2021-04-17 16:39:57.81379] [5425] [debug] [LcCSBKMVd90t] GET "/"
[2021-04-17 16:39:57.81408] [5425] [debug] [LcCSBKMVd90t] Routing to a callback
[2021-04-17 16:39:57.81610] [5425] [debug] [LcCSBKMVd90t] Rendering template "index.html.ep" from DATA section
[2021-04-17 16:39:57.81714] [5425] [debug] [LcCSBKMVd90t] Rendering template "layouts/default.html.ep" from DATA section
[2021-04-17 16:39:57.81792] [5425] [debug] [LcCSBKMVd90t] 200 OK (0.004118s, 242.836/s)
<!DOCTYPE html>
<html>
    <head><title>Today</title></head>
    <body>
<ul>
    <li>Dienstag, 20. April 2021</li>
    <li>20. April 2021</li>
    <li>20.04.2021</li>
    <li>20.04.21</li>
</ul>
</body>
</html>
```

And here’s Japanese:

```
$ perl perl_date_locale.pl get -H 'Accept-Language: ja' /
[2021-04-17 16:40:56.10840] [5478] [debug] [Wmr6cN5KUJlP] GET "/"
[2021-04-17 16:40:56.10874] [5478] [debug] [Wmr6cN5KUJlP] Routing to a callback
[2021-04-17 16:40:56.11101] [5478] [debug] [Wmr6cN5KUJlP] Rendering template "index.html.ep" from DATA section
[2021-04-17 16:40:56.11255] [5478] [debug] [Wmr6cN5KUJlP] Rendering template "layouts/default.html.ep" from DATA section
[2021-04-17 16:40:56.11360] [5478] [debug] [Wmr6cN5KUJlP] 200 OK (0.005164s, 193.648/s)
<!DOCTYPE html>
<html>
    <head><title>Today</title></head>
    <body>
<ul>
    <li>2021年4月20日火曜日</li>
    <li>2021年4月20日</li>
    <li>2021/04/20</li>
    <li>2021/04/20</li>
</ul>
</body>
</html>
```

A full list of supported locales is provided in the [DateTime::Locale::Catalog](https://metacpan.org/pod/DateTime::Locale::Catalog) documentation.

I hope this article has helped demonstrate that it’s not too hard to make your Perl web applications **respect global audiences**, if only with dates. For more on localization and Perl, start with the [Locale::Maketext](https://metacpan.org/pod/Locale::Maketext) framework.