summaryrefslogtreecommitdiff
path: root/lib/JWebmail
diff options
context:
space:
mode:
authorJannis M. Hoffmann <jannis@fehcom.de>2022-11-16 23:11:42 +0100
committerJannis M. Hoffmann <jannis@fehcom.de>2022-11-16 23:11:42 +0100
commita52a7d7c27440a7c2716af033a6113abcaa2bd46 (patch)
tree0b40912e689dfed35306558b3b41e6a98f169c39 /lib/JWebmail
parentb36eada764601355be4616fb92822ffabdcb1dc8 (diff)
split I18N2 plugin into multiple files and added translate role
Diffstat (limited to 'lib/JWebmail')
-rw-r--r--lib/JWebmail/Plugin/I18N2.pm212
-rw-r--r--lib/JWebmail/Plugin/I18N2/INI.pm84
-rw-r--r--lib/JWebmail/Plugin/I18N2/Maketext.pm55
-rw-r--r--lib/JWebmail/Plugin/I18N2/Role.pm39
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.