From ee43823179ee627ac16ea9da8168e5f1bf9619c0 Mon Sep 17 00:00:00 2001 From: "Jannis M. Hoffmann" Date: Thu, 29 Oct 2020 12:13:04 +0100 Subject: Initial commit; Stable version --- lib/JWebmail/Plugin/I18N.pm | 212 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 lib/JWebmail/Plugin/I18N.pm (limited to 'lib/JWebmail/Plugin/I18N.pm') diff --git a/lib/JWebmail/Plugin/I18N.pm b/lib/JWebmail/Plugin/I18N.pm new file mode 100644 index 0000000..dc10fdd --- /dev/null +++ b/lib/JWebmail/Plugin/I18N.pm @@ -0,0 +1,212 @@ +package JWebmail::Plugin::I18N; + +use Mojo::Base 'Mojolicious::Plugin'; + +use Mojolicious::Controller; +use Mojo::File; +use Mojo::Util 'monkey_patch'; + + +has '_language_loaded' => sub { {} }; + + +sub register { + my ($self, $app, $conf) = @_; + + my $i18n_log = $app->log->context('[' . __PACKAGE__ . ']'); + + # config + # 1. what languages + # 2. where are the files + # 3. fallback language + # + # look for languages automatically + my $defaultLang = $conf->{default_language} || 'en'; + my $fileLocation = $conf->{directory} && Mojo::File->new($conf->{directory})->is_abs + ? $conf->{directory} + : $app->home->child($conf->{directory} || 'lang'); + my @languages = keys %{$conf->{languages} // {}}; + + unless (@languages) { + @languages = map { $_ =~ s|^.*/(..)\.lang$|$1|r } glob("$fileLocation/*.lang"); + } + + $app->defaults(lang => $defaultLang); + $app->defaults(languages => [@languages]); + + # load languages + my $TXT; + for my $l (@languages) { + $TXT->{$l} = _loadi18n($fileLocation, $l, $i18n_log); + } + + { + local $" = ','; + $i18n_log->debug("loaded languages (@languages)"); + } + + $self->_language_loaded( { map { $_ => 1 } @languages } ); + + # add translator as helper + my $i18n = sub { + my ($lang, $word) = @_; + $TXT->{$lang}{$word} || scalar( + local $" = ' ', + $lang && $word ? $app->log->debug('[' . __PACKAGE__ . "] missing translation for $lang:$word @{[ caller(2) ]}[0..2]") : (), + '', + ) + }; + $app->helper( l => sub { my $c = shift; $i18n->($c->stash->{lang}, shift) } ); + + # rewrite url + $app->hook(before_dispatch => sub { $self->read_language_hook(@_) }); + + # patch url_for + my $mojo_url_for = Mojolicious::Controller->can('url_for'); + my $i18n_url_for = sub { + my $c = shift; + my $url = $mojo_url_for->($c, @_); + + my $args = (ref $_[0] eq 'HASH' and $_[0]) || (ref $_[1] eq 'HASH' and $_[1]) || do { my %x = @_[(@_ % 2) .. $#_]; \%x }; + my $lang = $args->{lang} // $c->stash->{lang}; + + if ( $lang && (ref $_[0] eq 'HASH' || !ref $_[0] && ($_[0]//'') !~ m![:@/.]!) ) { + unshift @{ $url->path->parts }, $lang + if ($url->path->parts->[0] // '') ne $lang; + $url = $url->to_abs(Mojo::URL->new('/')); + } + + return $url; + }; + monkey_patch 'Mojolicious::Controller', url_for => $i18n_url_for; + + 0 +} + + +sub read_language_hook { + my $self = shift; + my $c = shift; + + # URL detection + if (my $path = $c->req->url->path) { + + my $part = $path->parts->[0]; + + if ( $part && $self->_language_loaded->{$part} ) { + # Ignore static files + return if $c->res->code; + + $c->app->log->debug('[' . __PACKAGE__ . "] Found language $part in URL $path"); + + # Save lang in stash + $c->stash(lang => $part); + + if ( @{ $path->parts } == 1 && !$path->trailing_slash ) { + return $c->redirect_to($c->req->url->path->trailing_slash(1)); # default controller adds language back + } + + # Clean path + shift @{$path->parts}; + $path->trailing_slash(0); + } + } +} + + +sub _loadi18n { + + my $langsubdir = shift; + my $lang = shift; + my $log = shift; + + my $langFile = "$langsubdir/$lang.lang"; + my $TXT; + + if ( -f $langFile ) { + $TXT = Config::Tiny->read($langFile, 'utf8')->{'_'}; + if ($@ || !defined $TXT) { + $log->error("error reading file $langFile: $@"); + } + } + else { + $log->warn("language file $langFile does not exist!"); + } + return $TXT; +} + + +1 + +__END__ + +=encoding utf8 + +=head1 NAME + +JWebmail::Plugin::I18N - Custom Made I18N Support Inspired by Mojolicious::Plugin::I18N + +=head1 SYNOPSIS + + $app->plugin('I18N', { + languages => [qw(en de es)], + default_language => 'en', + directory => '/path/to/language/files/', + }) + + # in your controller + $c->l('hello') + + # in your templates + <%= l 'hello' %> + + @@ de.lang + login = anmelden + userid = nuzerkennung + passwd = passwort + failed = fehlgeschlagen + about = über + + example.com/de/myroute # $c->stash('lang') eq 'de' + example.com/myroute # $c->stash('lang') eq $defaultLanguage + + # on example.com/de/myroute + url_for('my_other_route') #=> example.com/de/my_other_route + + url_for('my_other_route', lang => 'es') #=> example.com/es/my_other_route + +=head1 DESCRIPTION + +L provides I18N support. + +The language will be taken from the first path segment of the url. +Be carefult with colliding routes. + +Mojolicious::Controller::url_for is patched so that the current language will be kept for +router named urls. + +=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. +Files of the pattern "$lang.lang" will be looked for. + +=head1 HELPERS + +=head2 l + +This is used for your translations. + + $c->l('hello') + $app->helper('hello')->() + +=cut \ No newline at end of file -- cgit v1.2.3