Cataly...Manual(3pm)

πŸ“¦
version 0.10 Β· 4 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– NAME πŸ’–

So this is Cataly...Manual(3pm) from the User Contributed Perl Documentation section, and it's the full glow-up guide for Catalyst::Plugin::I18N::Manual β€” (Draft) I18N and L10N with Catalyst and Template Toolkit. πŸ¦‹

Yes bestie, the "(Draft)" is literally in the title, I'm not making that up. πŸ’…


✨ OUTLINE ✨

Here's the whole roadmap, and ngl it's giving "we're doing the entire journey in one sitting." 🌈

πŸŽ€ Prerequisites πŸŽ€

You need every single one of these first, no skips, no shortcuts:

  • perl >= 5.8.0
  • Catalyst >= 5.33
  • Catalyst::Plugin::I18N >= 0.04
  • Catalyst::Plugin::Unicode >= 0.2
  • Catlayst::View::TT && Template-Toolkit >= 2.14
  • GNU gettext utilities
  • An editor that understands UTF-8 and Byte Order Mark (BOM)
not me preserving Catlayst::View::TT character for character even though it's typo'd β€” the source says it that way, so I say it that way. do not @ me πŸ’€

🌸 Let's make an app 🌸

First move, literally just birth the whole project:

$ catalyst.pl MyApp
created "MyApp"
created "MyApp/script"
created "MyApp/lib"
created "MyApp/root"
created "MyApp/root/static"
created "MyApp/root/static/images"
created "MyApp/t"
created "MyApp/lib/MyApp"
created "MyApp/lib/MyApp/Model"
created "MyApp/lib/MyApp/View"
created "MyApp/lib/MyApp/Controller"
created "MyApp/lib/MyApp.pm"
created "MyApp/Makefile.PL"
created "MyApp/README"
created "MyApp/Changes"
created "MyApp/t/01app.t"
created "MyApp/t/02pod.t"
created "MyApp/t/03podcoverage.t"
created "MyApp/root/static/images/catalyst_logo.png"
created "MyApp/root/static/images/btn_120x50_built.png"
created "MyApp/root/static/images/btn_120x50_built_shadow.png"
created "MyApp/root/static/images/btn_120x50_powered.png"
created "MyApp/root/static/images/btn_120x50_powered_shadow.png"
created "MyApp/root/static/images/btn_88x31_built.png"
created "MyApp/root/static/images/btn_88x31_built_shadow.png"
created "MyApp/root/static/images/btn_88x31_powered.png"
created "MyApp/root/static/images/btn_88x31_powered_shadow.png"
created "MyApp/root/favicon.ico"
created "MyApp/script/myapp_cgi.pl"
created "MyApp/script/myapp_fastcgi.pl"
created "MyApp/script/myapp_server.pl"
created "MyApp/script/myapp_test.pl"
created "MyApp/script/myapp_create.pl"

Look at all those created lines, it's giving spring cleaning but make it ✨magical✨.


πŸ’œ Time to edit the main module πŸ’œ

Hop in, cd over, and open that file:

$ cd MyApp
$ vim lib/MyApp.pm

use Catalyst qw/-Debug I18N Unicode/;

sub begin : Private {
    my ( $self, $c ) = @_;

    my $locale = $c->request->param('locale');

    $c->response->headers->push_header( 'Vary' => 'Accept-Language' );  # hmm vary and param?
    $c->languages( $locale ? [ $locale ] : undef );
}

sub default : Private {
    my ( $self, $c ) = @_;

    my $name = $c->request->param('name') || $c->loc('Guest');

    $c->response->content_type('text/plain; charset=utf-8');
    $c->response->body( $c->loc( 'Welcome [_1]!', $name ) );
}

Obsessed with begin doing the locale detective work in the shadows while default just serves the vibes. main character energy fr.


🧚 Gettext time 🧚

Now we make a little home for our translations and scan everything:

$ mkdir lib/MyApp/I18N
$ xgettext.pl --output=lib/MyApp/I18N/messages.pot --directory=lib/
$ ls lib/MyApp/I18N/
messages.pot

Slay, we got a .pot. Now let's make a Swedish one because why not:

$ msginit --input=lib/MyApp/I18N/messages.pot --output=lib/MyApp/I18N/sv.po --locale=sv
Created lib/MyApp/I18N/sv.po.

πŸŽ€ Edit the .po file πŸŽ€

Open it up and fill in the actual translations:

$ vim lib/MyApp/I18N/sv.po

"Content-Type: text/plain; charset=utf-8\n"

#: lib/MyApp.pm:50
msgid "Guest"
msgstr "GΓ€st"

#. ($name)
#: lib/MyApp.pm:54
msgid "Welcome %1!"
msgstr "VΓ€lkommen %1!"

Look at that charset line β€” charset=utf-8 is doing the heavy lifting so GΓ€st doesn't turn into abstract art. 🎨


πŸ”₯ Run it πŸ”₯

Fire up the dev server and see the magic:

$ perl script/myapp_server.pl
[Fri Dec  2 03:52:45 2005] [catalyst] [debug] Debug messages enabled
[Fri Dec  2 03:52:47 2005] [catalyst] [debug] Loaded plugins:
.------------------------------------------------------------------------------.
| Catalyst::Plugin::I18N                                                       |
| Catalyst::Plugin::Unicode                                                    |
'------------------------------------------------------------------------------'

[Fri Dec  2 03:52:47 2005] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher"
[Fri Dec  2 03:52:47 2005] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP"
[Fri Dec  2 03:52:47 2005] [catalyst] [debug] Found home "/Users/chansen/MyApp"
[Fri Dec  2 03:52:48 2005] [catalyst] [debug] Initialized i18n "MyApp::I18N"
[Fri Dec  2 03:52:48 2005] [catalyst] [debug] Loaded Private actions:
.----------------------+----------------------------------------+--------------.
| Private              | Class                                  | Method       |
+----------------------+----------------------------------------+--------------+
| /default             | MyApp                                  | default      |
'----------------------+----------------------------------------+--------------'

[Fri Dec  2 03:52:48 2005] [catalyst] [info] MyApp powered by Catalyst 5.57
You can connect to your server at http://localhost:3000

# point your browser to http://localhost:3000/?name=Joe
# output should render:

VΓ€lkommen Joe!

It's giving multilingual and I am LIVING for it. Point your browser at it with ?name=Joe and it just... says hi in Swedish. Iconic.


πŸ’… Change the string, resync πŸ’…

Okay but what if we want different words? Edit the module:

$ vim lib/MyApp.pm

sub default : Private {

    # ...

    $c->response->body( $c->loc( 'Welcome to my homepage [_1]!', $name ) );
}

Then regenerate the template and merge it into the existing .po:

$ xgettext.pl --output=lib/MyApp/I18N/messages.pot --directory=lib/
$ msgmerge --update lib/MyApp/I18N/sv.po lib/MyApp/I18N/messages.pot
. done.

. done. β€” a tiny little victory message, we love to see it.

Then update the translation:

$ vim lib/MyApp/I18N/sv.po

#. ($name)
#: lib/MyApp.pm:54
msgid "Welcome to my homepage %1!"
msgstr "VΓ€lkommen till min hemsida %1!"

And run it again:

$ perl script/myapp_server.pl

# point your browser to http://localhost:3000/?name=Joe
# output should render:

VΓ€lkommen till min hemsida Joe!

Same server, new words, zero drama. We ate. πŸ“


πŸ¦‹ Now add Template Toolkit πŸ¦‹

Templates are where it's really at, so let's make a TT view:

$ perl script/myapp_create.pl view TT TT
 exists "/Users/chansen/MyApp/script/../lib/MyApp/View"
 exists "/Users/chansen/MyApp/script/../t"
created "/Users/chansen/MyApp/script/../lib/MyApp/View/TT.pm"
created "/Users/chansen/MyApp/script/../t/view_TT.t"
yes, exists really does have a single leading space and created doesn't. that's just how it is, bestie. πŸ™ˆ

Then wire it into your default action:

$ vim lib/MyApp.pm

sub default : Private {
    my ( $self, $c ) = @_;

    my $name = $c->request->param('name') || $c->loc('Guest');

    $c->response->content_type('text/plain; charset=utf-8');
    $c->stash(
        name     => $name,
        template => 'test.tt'
    );

    $c->forward('MyApp::View::TT');
}

And make the template itself:

$ vim root/test.tt # Save file in UTF-8 with BOM

[% c.loc( 'Welcome to my place [_1]!', c.stash.name ) %]
Heads up: that comment is not a suggestion β€” save it in UTF-8 with BOM. For real for real. That's why the prerequisites list wanted an editor that understands BOM. πŸ‘‘

Now rescan both lib/ and root/:

$ xgettext.pl --output=lib/MyApp/I18N/messages.pot --directory=lib/ --directory=root/
$ msgmerge --update lib/MyApp/I18N/sv.po lib/MyApp/I18N/messages.pot
. done.

Update the .po:

$ vim lib/MyApp/I18N/sv.po

#. (c.stash.name)
#: root/test.tt:1
msgid "Welcome to my place %1!"
msgstr "VΓ€lkommen till mitt stΓ€lle %1!"

And boot it up one more time:

$ perl script/myapp_server.pl
[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Debug messages enabled
[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Loaded plugins:
.------------------------------------------------------------------------------.
| Catalyst::Plugin::I18N                                                       |
| Catalyst::Plugin::Unicode                                                    |
'------------------------------------------------------------------------------'

[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher"
[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP"
[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Found home "/Users/chansen/MyApp"
[Fri Dec  2 05:12:58 2005] [catalyst] [debug] Initialized i18n "MyApp::I18N"
[Fri Dec  2 05:12:59 2005] [catalyst] [debug] Loaded components:
.-------------------------------------------------------------------+----------.
| Class                                                             | Type     |
+-------------------------------------------------------------------+----------+
| MyApp::View::TT                                                   | instance |
'-------------------------------------------------------------------+----------'

[Fri Dec  2 05:12:59 2005] [catalyst] [debug] Loaded Private actions:
.----------------------+----------------------------------------+--------------.
| Private              | Class                                  | Method       |
+----------------------+----------------------------------------+--------------+
| /default             | MyApp                                  | default      |
'----------------------+----------------------------------------+--------------'

[Fri Dec  2 05:12:59 2005] [catalyst] [info] MyApp powered by Catalyst 5.57
You can connect to your server at http://localhost:3000

# point your browser to http://localhost:3000/?name=Joe
# output should render:

VΓ€lkommen till mitt stΓ€lle Joe!

There it is. Templates + i18n + a whole Swedish homepage. Absolutely ate and left no crumbs. πŸ‘‘


🌈 INTRODUCTION 🌈

🌸 INTERNATIONALIZATION 🌸

πŸ’œ CONTENT NEGOTIATION πŸ’œ

πŸŽ€ Server-driven πŸŽ€

πŸ¦‹ Agent-driven πŸ¦‹

✨ LOCALIZATION ✨

πŸ”₯ STAYING IN SYNC πŸ”₯

πŸ’… TEMPLATE TOOLKIT πŸ’…


and that's where part 1 leaves us, bestie β€” the tea continues in the next part, don't go anywhere 🫢

🌸 RESOURCES 🌸

Okay bestie, this is the part where we hand you the entire link vault. Everything you need to actually do the i18n thing. Grab a snack. πŸ“


πŸ“š Documentation

✨ Definitions

🧚 Guides

πŸͺ Perl

🌈 Portals

πŸ“œ Standards

πŸ•ΈοΈ Web


πŸ—‚οΈ Locale Repositories


πŸ“¦ Modules

πŸ”€ Character Encoding, Collation and Normalization

πŸ’° Currency

πŸ“… Dates

🏷️ Language Tags, Identification and Negotiation

πŸ’¬ Message Catalogs

πŸ”’ Numbers


πŸ› οΈ Tools


πŸ‘‘ AUTHOR

Christian Hansen "ch@ngmedia.com"


This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself.


and that's the whole thing bestie, you made it to the end, so iconic of you πŸŽ€βœ¨

this page is from perl v5.36.0, dated 2022-11-27 β€” go forth and localize, you absolute legend πŸ’…πŸ¦‹

πŸ“¦ where this page comes from