package JWebmail::Plugin::I18N2; use Mojo::Base 'Mojolicious::Plugin'; use List::Util 'any'; use JWebmail::Plugin::I18N2::Role; package JWebmail::Plugin::I18N2::Match::Role { use Mojo::Base -role; has '_i18n2_stash'; # cases: ($) (\%) ($, \%) (%) ($, %) sub __i18n2_add_option_no_override { my $key = shift; my $value = shift; if (@_ == 1 && ref $_[0] eq 'HASH') { # handles (\%) $_[0]->{$key} ||= $value; } elsif (@_ == 2 && ref $_[1] eq 'HASH') { # handles ($, \%) $_[1]->{$key} ||= $value; } elsif (@_ % 2 == 0) { # handles (%) my %opts = @_; $opts{$key} ||= $value; @_ = %opts; } else { # handles ($, %) my ($primary, %opts) = @_; $opts{$key} ||= $value; @_ = ($primary, %opts); } return @_; } around 'path_for' => sub { my $orig = shift; my $self = shift; if (my $lang = $self->_i18n2_stash->{lang}) { @_ = __i18n2_add_option_no_override(lang => $lang, @_); } $orig->($self, @_) }; } sub register { my ($self, $app, $conf) = @_; $conf //= {}; my $i18n_log = $app->log->context('[' . __PACKAGE__ . ']'); my $translator = $conf->{translator} || sub { require JWebmail::Plugin::I18N2::Maketext; JWebmail::Plugin::I18N2::Maketext->new(@_) }; my $defaultLang = $conf->{default_language} || 'en'; my $fileLocation = $conf->{directory}; # ? Mojo::File->new($conf->{directory}) : $app->home->child('lang'); my $t = $translator->( default_language => $defaultLang, directory => $fileLocation, log => sub { $i18n_log->warn(@_) }, %{$conf->{rest} // {}} ); die "translator does not consume role JWebmail::Plugin::I18N2::Role" unless $t->DOES('JWebmail::Plugin::I18N2::Role'); { local $" = ','; $i18n_log->info("loaded languages (@{[$t->languages]})"); unless (any { $defaultLang eq $_ } $t->languages) { die "default language '$defaultLang' not loaded"; } if (keys $conf->{languages}->%* > $t->languages) { $i18n_log->warn("missing languages"); } } $app->defaults( default_language => $defaultLang, languages => [$t->languages], ); # add translator as helper $app->helper(l => sub { my $c = shift; my $word = shift; my $lang = $c->stash('lang'); return $t->translate($lang, $word, @_); }); # modify incoming and generated urls $app->hook(before_routes => sub { my $c = shift; unshift @{ $c->req->url->path->parts }, '' unless $t->languages($c->req->url->path->parts->[0]); my $ext_match = $c->match->with_roles('JWebmail::Plugin::I18N2::Match::Role'); $ext_match->_i18n2_stash($c->stash); $c->match($ext_match); }); return $app->routes->any('/:lang' => {lang => $defaultLang}); } 1 __END__ =encoding utf8 =head1 NAME JWebmail::Plugin::I18N2 - Custom Made I18N Support for Mojolicious =head1 SYNOPSIS my $router = $app->plugin('I18N2', { languages => [qw(en de es)], default_language => 'de', }); # in your controller $c->l('hello') # 'el' # in your templates <%= l 'hello' %> # reads the language of the first url component example.com/de/myroute # $c->stash('lang') eq 'de' example.com/myroute # $c->stash('lang') eq $defaultLanguage # adjusts url depending on the currently selected language # on example.com/de/myroute url_for('my_other_route') #=> example.com/de/my_other_route # you can pass the language explicitly as well url_for('my_other_route', lang => 'es') #=> example.com/es/my_other_route =head1 DESCRIPTION L provides I18N support. This is a complete re-implementation of JWebmail::Plugin::I18N that allows for a composable matcher and custom translator. The language will be taken from the first path segment of the url. Be careful with colliding routes. This Plugin only works with Mojolicious version 8.64 or higher. =head1 RETURNS The plugin returns an initial route that is meant to be used as the root for all endpoints that shall be translatable. =head1 OPTIONS =head2 default_language The default language when no other information is provided. =head2 languages List of allowed languages. As a default, files of the pattern "$lang.lang" will be looked for. =head2 translator This is a sub that returns an object that C C when given a HASH or HASHREF containing a 'log' and a 'default_language'. A custom implementation that uses simple files of key-value pairs is provided as well as one that uses L. An implementation for gettext is planned. Default is Maketext for now. =head1 STASH =head2 lang This value dictates what languages is actually used. You may change this before rendering the view. =head2 default_language The set default language. =head2 languages A list of all loaded languages. =head1 HELPERS =head2 l This is used for your translations. $c->l('hello') =head1 EXTENDS =head2 Mojolicious::Routes::Match->path_for This plugin creates a new dynamic class for every Match class that is used in the Mojolicious routing mechanism that extends the C method with one that injects the 'lang' option. =cut