diff options
author | Jannis M. Hoffmann <jannis@fehcom.de> | 2022-11-16 23:11:42 +0100 |
---|---|---|
committer | Jannis M. Hoffmann <jannis@fehcom.de> | 2022-11-16 23:11:42 +0100 |
commit | a52a7d7c27440a7c2716af033a6113abcaa2bd46 (patch) | |
tree | 0b40912e689dfed35306558b3b41e6a98f169c39 | |
parent | b36eada764601355be4616fb92822ffabdcb1dc8 (diff) |
split I18N2 plugin into multiple files and added translate role
-rw-r--r-- | lib/JWebmail/Plugin/I18N2.pm | 212 | ||||
-rw-r--r-- | lib/JWebmail/Plugin/I18N2/INI.pm | 84 | ||||
-rw-r--r-- | lib/JWebmail/Plugin/I18N2/Maketext.pm | 55 | ||||
-rw-r--r-- | lib/JWebmail/Plugin/I18N2/Role.pm | 39 |
4 files changed, 234 insertions, 156 deletions
diff --git a/lib/JWebmail/Plugin/I18N2.pm b/lib/JWebmail/Plugin/I18N2.pm index 0d94f4b..68dcc15 100644 --- a/lib/JWebmail/Plugin/I18N2.pm +++ b/lib/JWebmail/Plugin/I18N2.pm @@ -4,140 +4,7 @@ use Mojo::Base 'Mojolicious::Plugin'; use List::Util 'any'; - -package JWebmail::Plugin::I18N2::Maketext { - - use JWebmail::I18N; - use File::Basename 'fileparse'; - - sub new { - my $class = shift; - my $conf = @_ == 1 ? shift : {@_}; - - my $lexica = $conf->{directory} || [fileparse(__FILE__)]->[1] . '../I18N'; - - my @languages = keys %{$conf->{languages} // {}}; - - unless (@languages) { - use autodie; - - opendir(my $dh, $lexica); - my @res = grep { /\.pm$/ && -f "$lexica/$_" } readdir $dh; - closedir($dh); - @languages = map { scalar fileparse $_, '.pm' } @res; - @languages = map { my ($l, $c) = split '_', $_, 2; $c ? "$l-\U$c" : $l } @languages; - } - - if (my $dl = $conf->{default_language}) { push @languages, $dl; }; - my $self = {}; - for (@languages) { - $self->{$_} = JWebmail::I18N->get_handle($_) || die "unable to load language $_"; - } - - if (my $l = $conf->{log}) { - $_->logger($l) for values %$self; - } - - return bless $self, $class; - } - - sub languages { - my $self = shift; - if (@_) { - return exists $self->{$_[0] || ''}; - } - return wantarray ? sort keys $self->%* : scalar keys $self->%*; - } - - sub translate { - my $self = shift; - my $lang = shift; - my $phrase = shift; - return $self->{$lang}->maketext($phrase, @_); - } -} - -package JWebmail::Plugin::I18N2::Translator { - - use Mojo::File; - - use Config::Tiny; - - sub new { - my $cls = shift; - my $conf = @_ == 1 ? shift : {@_}; - - my $self = bless {}, $cls; - - $self->_log($conf->{log} || sub { @_ = $_[0]->() if ref $_[0] eq 'CODE'; local $" = ' '; warn "@_" }); - - my @languages = keys %{$conf->{languages} // {}}; - - unless (@languages) { - @languages = map { s|^.*/(..)\.lang$|$1|r } glob("'$conf->{directory}/*.lang'"); - } - - # load languages - for my $l (@languages) { - if (my $dict = __loadi18n($conf->{directory}, $l)) { - $self->{$l} = $dict; - } - } - - return $self; - } - - sub languages { - my $self = shift; - if (@_) { - return exists $self->{$_[0] || ''}; - } - return wantarray ? sort keys $self->%* : scalar keys $self->%*; - } - - sub translate { - my $self = shift; - my $lang = shift; - my $word = shift; - - my $res = $self->{$lang}{$word}; - unless ($res) { - local $" = ' '; - $self->_log->("missing translation for $lang:'$word' @{[ caller(1) ]}[0..2]"); - } - if (@_) { - $res = sprintf($res, @_); - } - return $res; - } - - sub _log { - my $self = shift; - if (@_) { - $self->{_log} = $_[0]; - return $self; - } - else { - return $self->{_log}; - } - } - - sub __loadi18n { - my $langsubdir = shift; - my $lang = shift; - - my $langFile = "$langsubdir/$lang.lang"; - my $TXT; - - if ( -f $langFile ) { - $TXT = Config::Tiny->read($langFile, 'utf8')->{'_'}; - if ($@) { - die "error reading file $langFile: $@"; - } - } - return $TXT; - } -} +use JWebmail::Plugin::I18N2::Role; package JWebmail::Plugin::I18N2::Match::Role { @@ -187,7 +54,8 @@ sub register { my $i18n_log = $app->log->context('[' . __PACKAGE__ . ']'); - my $translator = $conf->{translator} || sub { JWebmail::Plugin::I18N2::Maketext->new(@_) }; + 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'); @@ -197,6 +65,8 @@ sub register { 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 $" = ','; @@ -248,64 +118,86 @@ __END__ =head1 NAME -JWebmail::Plugin::I18N2 - Custom Made I18N Support an alternative to JWebmail::Plugin::I18N +JWebmail::Plugin::I18N2 - Custom Made I18N Support for Mojolicious =head1 SYNOPSIS - $app->plugin('I18N2', { - languages => [qw(en de es)], + my $router = $app->plugin('I18N2', { + languages => [qw(en de es)], default_language => 'de', - directory => 'path/to/language/files/', - }) + }); # in your controller - $c->l('hello') + $c->l('hello') # 'el' # in your templates <%= l 'hello' %> - @@ de.lang - login = anmelden - userid = nuzerkennung - passwd = passwort - failed = fehlgeschlagen - about = über - + # 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<JWebmail::Plugin::I18N2> provides I18N support. -The language will be taken from the first path segment of the url. -Be carefult with colliding routes. +This is a complete re-implementation of JWebmail::Plugin::I18N that allows for +a composable matcher and custom translator. -Mojolicious::Controller::url_for is patched so that the current language will be kept for -router named urls. +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 directory - -Directory to look for language files. - =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<DOES> C<JWebmail::Plugin::I18N2::Role> +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<Locale::Maketext>. +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 @@ -314,4 +206,12 @@ 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<path_for> method with +one that injects the 'lang' option. + =cut diff --git a/lib/JWebmail/Plugin/I18N2/INI.pm b/lib/JWebmail/Plugin/I18N2/INI.pm new file mode 100644 index 0000000..29364bb --- /dev/null +++ b/lib/JWebmail/Plugin/I18N2/INI.pm @@ -0,0 +1,84 @@ +package JWebmail::Plugin::I18N2::INI; + +use v5.22; +use warnings; +use utf8; + +use Mojo::File; + +use Config::Tiny; + +use Role::Tiny::With; +with 'JWebmail::Plugin::I18N2::Role'; + + +sub new { + my $cls = shift; + my $conf = @_ == 1 ? shift : {@_}; + + my $self = bless {}, $cls; + + $self->_log($conf->{log} || sub { @_ = $_[0]->() if ref $_[0] eq 'CODE'; local $" = ' '; warn "@_" }); + + my @languages = keys %{$conf->{languages} // {}}; + + unless (@languages) { + @languages = map { s|^.*/(..)\.lang$|$1|r } glob("'$conf->{directory}/*.lang'"); + } + + # load languages + for my $l (@languages) { + if (my $dict = __loadi18n($conf->{directory}, $l)) { + $self->{$l} = $dict; + } + } + + return $self; +} + +sub loaded_languages { grep { $_ !~ /^_/ } keys $_[0]->%* } + +sub translate { + my $self = shift; + my $lang = shift; + my $word = shift; + + my $res = $self->{$lang}{$word}; + unless ($res) { + local $" = ' '; + $self->_log->("missing translation for $lang:'$word' @{[ caller(1) ]}[0..2]"); + } + if (@_) { + $res = sprintf($res, @_); + } + return $res; +} + +sub _log { + my $self = shift; + if (@_) { + $self->{_log} = $_[0]; + return $self; + } + else { + return $self->{_log}; + } +} + +sub __loadi18n { + my $langsubdir = shift; + my $lang = shift; + + my $langFile = "$langsubdir/$lang.lang"; + my $TXT; + + if ( -f $langFile ) { + $TXT = Config::Tiny->read($langFile, 'utf8')->{'_'}; + if ($@) { + die "error reading file $langFile: $@"; + } + } + return $TXT; +} + +1 diff --git a/lib/JWebmail/Plugin/I18N2/Maketext.pm b/lib/JWebmail/Plugin/I18N2/Maketext.pm new file mode 100644 index 0000000..a38bba7 --- /dev/null +++ b/lib/JWebmail/Plugin/I18N2/Maketext.pm @@ -0,0 +1,55 @@ +package JWebmail::Plugin::I18N2::Maketext; + +use v5.22; +use warnings; +use utf8; + +use JWebmail::I18N; + +use File::Basename 'fileparse'; + +use Role::Tiny::With; +with 'JWebmail::Plugin::I18N2::Role'; + + +sub new { + my $class = shift; + my $conf = @_ == 1 ? shift : {@_}; + + my $lexica = $conf->{directory} || [fileparse(__FILE__)]->[1] . '../I18N'; + + my @languages = keys %{$conf->{languages} // {}}; + + unless (@languages) { + use autodie; + + opendir(my $dh, $lexica); + my @res = grep { /\.pm$/ && -f "$lexica/$_" } readdir $dh; + closedir($dh); + @languages = map { scalar fileparse $_, '.pm' } @res; + @languages = map { my ($l, $c) = split '_', $_, 2; $c ? "$l-\U$c" : $l } @languages; + } + + if (my $dl = $conf->{default_language}) { push @languages, $dl; }; + my $self = {}; + for (@languages) { + $self->{$_} = JWebmail::I18N->get_handle($_) || die "unable to load language $_"; + } + + if (my $l = $conf->{log}) { + $_->logger($l) for values %$self; + } + + return bless $self, $class; +} + +sub loaded_languages { grep { $_ !~ /^_/ } keys $_[0]->%* } + +sub translate { + my $self = shift; + my $lang = shift; + my $phrase = shift; + return $self->{$lang}->maketext($phrase, @_); +} + +1 diff --git a/lib/JWebmail/Plugin/I18N2/Role.pm b/lib/JWebmail/Plugin/I18N2/Role.pm new file mode 100644 index 0000000..6ed75dc --- /dev/null +++ b/lib/JWebmail/Plugin/I18N2/Role.pm @@ -0,0 +1,39 @@ +package JWebmail::Plugin::I18N2::Role; + +use v5.22; +use warnings; +use utf8; + +use Role::Tiny; + +requires qw(translate); + + +sub languages { + require List::Util; + + my $self = shift; + my @ret = $self->loaded_languages; + + if (my $find = shift) { + return List::Util::any { $find eq $_ } @ret; + } + return wantarray ? sort @ret : @ret; +} + +1 + +__END__ + +=encoding utf-8 + +=head1 DESCRIPTION + +The translate method is called with self, the language and the word and +maybe additional arguments. + +The languages method shall report all loaded languages when called with no +argument and if a language is loaded when given an argument. + +A default languages method is provided that reads languages form the +loaded_language method. |