summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES162
-rw-r--r--CHANGES.md176
-rw-r--r--MANIFEST3
-rw-r--r--Makefile.PL4
-rw-r--r--README.md91
-rwxr-xr-xactions.sh40
-rw-r--r--lib/JWebmail/Model/WriteMails.pm6
-rw-r--r--lib/JWebmail/Plugin/I18N2.pm230
-rw-r--r--t/I18N2.t31
9 files changed, 428 insertions, 315 deletions
diff --git a/CHANGES b/CHANGES
deleted file mode 100644
index 7be26fd..0000000
--- a/CHANGES
+++ /dev/null
@@ -1,162 +0,0 @@
-Past and Upcoming Changes
-=========================
-
-v1.0.0 release plan
--------------------
-✓ consider renaming, relicensing
- ✓ License
- ✓ GPLv3+ and enter copyright info
- * Maybe the translation/documentation can be made available under a different license
- * may relicense this under the AGPL.
- ✓ Rename
- ✓ JWebmail
-✓ make github ready
- ✓ remove sensitive files (gitignore)
- ✓ add git vcs
- ✓ remove part of the english translation
-* check legal requirements
-✓ BUG: home not displaying
-✓ show new messages per folder
-✓ BUG: empty folder not displaying correctly
-✓ better documentation
- ✓ document i18n snippets
- ✓ cleanup comments
- ✓ list functionality for ReadMails#communicate
- ✓ OMail
- ✓ OMail::Helper
- ✓ OMail::Controller::All
- ✓ OMail::Plugin::I18N
- ✓ OMail::Plugin::INIConfig
- ✓ OMail::Plugin::ServerSideSessionData
- ✓ OMail::Model::WriteMails
- ✓ OMail::Model::ReadMails
- ✓ OMail::Model::Driver::QMailAuthuser
- ✓ OMail::Model::Driver::QMailAuthuser::Extract
-✓ better pagination
- ✓ BUG: pagination forward -> backward is shifting by 1 (page start needs to be decremented)
- ✓ move out to helper
- ✓ more generic names
-✓ advance ini config plugin
- ✓ set global section to global scope
- ✓ introduce arrays
- ✓ make nesting sections more explicit
-✓ write more tests
- ✓ test pagination
- ✓ test mail_line
- ✓ test for ini parser
- ✓ basic test for application
-✓ improve i18n
- ✓ german translation
- ✓ look into i18n configuration
- ✓ remove TXT alias
-✓ more configuration (for model)
- ✓ disable cram
- ✓ select mock read model
- ✓ lazy init for mock model
- ✓ add switch disabling message send
- ✓ Extract: user to switch to
- ✓ Extract: adjustable maildir directory
-✓ read secret from config file
-✓ Extract: configurable perl lib
-✓ Extract: encoding issues
-✓ improve session data security
- ✓ use a server side cookie implementation
- ✓ use a one time pad
- ✓ resolve server/client session duration issues
- ✓ use cryptographically secure random data
- ✓ hide password length
-✓ handle empty folders
-✓ logging support for Extract.pm
-✓ true perl 5.16 support
-✓ cpan build and deploy script
-✓ remove prefs
-✓ file upload for attachment
- ✓ file type detection
- ✓ move WriteMails from Email::Simple to Email::MIME
-✓ configuration as plugin (Mojo::Plugin::Config)
-✓ model as helpers, initialized in startup
-✓ send
- ✓ multiple mails for cc etc.
- * content-transfer encoding, research (currently 8bit)
-✓ better design for send and read
- ✓ send
- ✓ read
-✓ sandbox html mails
-✓ i18n as ini files
-✓ rework mail folders
-✓ rewrite about
-✓ search in subject
-
-
-v1.1.0 release plan
--------------------
-* INV: wrong subject being shown
-* INV: new mails are not highlighted
-* INV: displayheaders table does not fill outer container
-✓ better back buttons
- ✓ writemail
- ✓ read mail
-✓ improve server side session cleanup process coordination
-✓ consider using Crypt::URandom instead of Crypt::Random
-✓ factor out date format function
-✓ add a delete session function for s3d, maybe
- ✓ simply remove key from cookie
-* separate development and production configuration
-* create base configuration
-* repurpose status field in displayheader
- ✓ currently just renamed
-* improve performance
- * async read for extract
- ✓ async version of driver
- ✓ async version of model
- * async version of controller
- * async wait for send
- * add wait_for_child to event loop
-* consider using more mojo functions
- ✓ use Mojolicious::Types to replace File::Type
- ✓ Helper
- * QMailAuthuser
-* moving mails to other folders
- * creating new folders
- * backend
-* add more mime types
- * jpeg
- * png
- * gif
-
-
-v1.2.0 release plan
--------------------
-* advance ini config plugin
- * allow non-leaf nodes to be arrays
- * allow quotes
- * allow continuation over multiple lines
- * warn about overrides
- * add template support, maybe
-* improve i18n
- * add localization of dates and time
-* better pagination
- * merge with partial templates, maybe
-* improve performance, consider alternatives to Extract.pm
- * based on Maildir::Light
-* add config validation
-* click on sender to answer
-* mobile optimize
-* download mail and attachments
-* cleanup css
-* allow multiple attachments
-* consider using more mojo functions
- * base64
- * encoding
- * json
- * filepaths
- * dump
- * mail?
-* add mails to Sent folder
-
-
-v1.3.0 release plan
--------------------
-* smtp send model, maybe
-* pop read model, maybe
-* add icons for navigation
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..ca2d303
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,176 @@
+Past and Upcoming Changes
+=========================
+
+v1.0.0 release plan
+-------------------
+- [x] consider renaming, relicensing
+ - [x] License
+ - [x] GPLv3+ and enter copyright info
+ - [ ] Maybe the translation/documentation can be made available under a
+ different license
+ - [ ] may relicense this under the AGPL.
+ - [x] Rename
+ - [x] JWebmail
+- [x] make github ready
+ - [x] remove sensitive files (gitignore)
+ - [x] add git vcs
+ - [x] remove part of the english translation
+- [ ] check legal requirements
+- [x] BUG: home not displaying
+- [x] show new messages per folder
+- [x] BUG: empty folder not displaying correctly
+- [x] better documentation
+ - [x] document i18n snippets
+ - [x] cleanup comments
+ - [x] list functionality for ReadMails#communicate
+ - [x] OMail
+ - [x] OMail::Helper
+ - [x] OMail::Controller::All
+ - [x] OMail::Plugin::I18N
+ - [x] OMail::Plugin::INIConfig
+ - [x] OMail::Plugin::ServerSideSessionData
+ - [x] OMail::Model::WriteMails
+ - [x] OMail::Model::ReadMails
+ - [x] OMail::Model::Driver::QMailAuthuser
+ - [x] OMail::Model::Driver::QMailAuthuser::Extract
+- [x] better pagination
+ - [x] BUG: pagination forward -> backward is shifting by 1
+ (page start needs to be decremented)
+ - [x] move out to helper
+ - [x] more generic names
+- [x] advance ini config plugin
+ - [x] set global section to global scope
+ - [x] introduce arrays
+ - [x] make nesting sections more explicit
+- [x] write more tests
+ - [x] test pagination
+ - [x] test mail_line
+ - [x] test for ini parser
+ - [x] basic test for application
+- [x] improve i18n
+ - [x] german translation
+ - [x] look into i18n configuration
+ - [x] remove TXT alias
+- [x] more configuration (for model)
+ - [x] disable cram
+ - [x] select mock read model
+ - [x] lazy init for mock model
+ - [x] add switch disabling message send
+ - [x] Extract: user to switch to
+ - [x] Extract: adjustable maildir directory
+- [x] read secret from config file
+- [x] Extract: configurable perl lib
+- [x] Extract: encoding issues
+- [x] improve session data security
+ - [x] use a server side cookie implementation
+ - [x] use a one time pad
+ - [x] resolve server/client session duration issues
+ - [x] use cryptographically secure random data
+ - [x] hide password length
+- [x] handle empty folders
+- [x] logging support for Extract.pm
+- [x] true perl 5.16 support
+- [x] cpan build and deploy script
+- [x] remove prefs
+- [x] file upload for attachment
+ - [x] file type detection
+ - [x] move WriteMails from Email::Simple to Email::MIME
+- [x] configuration as plugin (Mojo::Plugin::Config)
+- [x] model as helpers, initialized in startup
+- [x] send
+ - [x] multiple mails for cc etc.
+ - [ ] content-transfer encoding, research (currently 8bit)
+- [x] better design for send and read
+ - [x] send
+ - [x] read
+- [x] sandbox html mails
+- [x] i18n as ini files
+- [x] rework mail folders
+- [x] rewrite about
+- [x] search in subject
+
+Current v1.1.0
+--------------
+- [ ] From v1.0.0
+ - [ ] Maybe the translation/documentation can be made available under a
+ different license
+ - [ ] may relicense this under the AGPL.
+ - [ ] research content-transfer encoding for sending (currently 8bit)
+ - [ ] check legal requirements (cookies and DSGVO)
+- [x] separate development and production configuration
+- [x] better back buttons
+ - [x] writemail
+ - [x] read mail
+- [x] improve server side session cleanup process coordination
+- [x] consider using Crypt::URandom instead of Crypt::Random
+- [x] factor out date format function
+- [x] add a delete session function for s3d, maybe
+ - [x] simply remove key from cookie
+- [x] add actions script
+- [ ] repurpose status field in displayheader
+ - [x] currently just renamed
+- [ ] advance ini config plugin
+ - [ ] allow non-leaf nodes to be arrays
+ - [ ] allow quotes
+ - [ ] allow continuation over multiple lines
+ - [ ] warn about overrides
+ - [ ] add template support, maybe
+- [ ] improve i18n
+ - [ ] add localization of dates and time
+- [ ] improve performance, consider alternatives to Extract.pm
+ - [ ] based on Maildir::Light
+ - [ ] reimplementation in Rust
+- [x] refactor I18N plugin to allow independent provider
+- [ ] refactor driver into a role
+- [ ] merge read and row (with content type)
+- [ ] fix tests
+- [ ] consider using more mojo functions
+ - [x] use Mojolicious::Types to replace File::Type
+ - [x] Helper
+ - [ ] QMailAuthuser
+- [ ] moving mails to other folders
+ - [ ] creating new folders
+ - [ ] backend
+- [ ] specify protocol for backend interaction
+- [ ] cleanup README
+
+Future
+------
+- [ ] INV: wrong subject being shown
+- [ ] INV: new mails are not highlighted
+- [ ] INV: displayheaders table does not fill outer container
+- [ ] consider using more mojo functions
+ - [ ] base64
+ - [ ] encoding
+ - [ ] json
+ - [ ] filepaths
+ - [ ] dump
+ - [ ] mail?
+- [ ] create base configuration
+- [ ] improve performance
+ - [ ] async read for extract
+ - [x] async version of driver
+ - [x] async version of model
+ - [ ] async version of controller
+ - [ ] async wait for send
+ - [ ] add wait_for_child to event loop
+- [ ] add more mime types to read
+ - [ ] jpeg
+ - [ ] png
+ - [ ] gif
+- [ ] better pagination
+ - [ ] merge with partial templates, maybe
+- [ ] add config validation
+- [ ] click on sender to answer
+- [ ] mobile optimize
+- [ ] download mail and attachments
+- [ ] cleanup css
+- [ ] allow multiple attachments
+- [ ] add mails to Sent folder
+- [ ] smtp send model, maybe
+- [ ] pop read model, maybe
+- [ ] add icons for navigation
+- [ ] allow changing password
+- [ ] think about forgot password feature
+- [ ] address book support
+ - [ ] add links on email addresses in header : click = add into addressbook
diff --git a/MANIFEST b/MANIFEST
index e13ab59..9488d70 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -11,7 +11,7 @@ lib/JWebmail/Plugin/INIConfig.pm
lib/JWebmail/Plugin/I18N.pm
lib/JWebmail/Plugin/I18N2.pm
lib/JWebmail/Plugin/ServerSideSessionData.pm
-README
+README.md
LICENSE
lang/de.lang
lang/en.lang
@@ -20,7 +20,6 @@ t/Webmail.t
t/INI.t
t/Helper.t
script/jwebmail
-CREDITS
MANIFEST
public/style.css
templates/headers/_display_top_nav.html.ep
diff --git a/Makefile.PL b/Makefile.PL
index 3633f63..b51c6b4 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -8,7 +8,7 @@ WriteMakefile(
MIN_PERL_VERSION => 'v5.22',
NAME => 'JWebmail',
VERSION_FROM => 'lib/JWebmail.pm',
- LICENSE => 'GPL',
+ LICENSE => 'gpl_3',
PREREQ_PM => {
'Mojolicious' => '8.64',
'Config::Tiny' => 'v2.24',
@@ -17,4 +17,4 @@ WriteMakefile(
'Mail::Box::Manager' => 'v3.9',
},
test => {TESTS => 't/*.t'}
-);
+)
diff --git a/README.md b/README.md
index 9b2eac8..07bcb27 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,74 @@
JWebmail
========
-This is a rewrite of oMail by Oliver Müller <om@omnis.ch>.
+This is based on concepts of oMail by Oliver Müller <om@omnis.ch>.
-oMail has not seen much progress in the last two decades
-so my <jannis@fehcom.de> goal is to bring it up to date.
-OMail was tied to qmail. JWebmail is not so tightly bound.
+JWebmail is a webmail server build on top of Mojolicious.
This includes:
-- Using a perl web framework and leave the deprecated CGI behind.
- You can still use it in a cgi setup if you like but you now
- have the option of plack/psgi and fcgi as well as the
- build in server hypnotoad.
+- Using a Perl web framework and leave the deprecated CGI behind. You can
+ still use it in a CGI setup if you like but you now have the option of
+ plack/psgi and fcgi as well as the build in server hypnotoad.
- Set up a MVC architecture instead of a single file.
-- Improve security by only running a small part of the
- model with elevated privileges.
-- Make sure it works well with sqmail and its authuser
- authenticator and maildir but also permit other setups
- (currently not supported but adding more should be easy).
- Maybe I even add a POP or IMAP based backends instead
+- Improve security by only running a small part of the model with elevated
+ privileges.
+- Make sure it works well with sqmail and its authuser authenticator and
+ maildir but also permit other setups (currently not supported but adding
+ more should be easy). Maybe I even add a POP or IMAP based back-ends instead
of reading them from disk.
## License
-JWebmail is available under the GNU General Public License
-version 3 or later.
+JWebmail is available under the GNU General Public License version 3.
## JWebmail-webmail - INSTALL
-You still need to install sqmail and setup
-an external web server.
-
-## Future feature list
-- [ ] address book support
-
-### Read
-- [ ] bounce
-- [ ] add links on email addresses in header : click = add into addressbook
+You need a moderately new version of Perl (v5.22+)
+You need access to CPAN.
+You need to install sqmail.
+It is recommended to use an external web server like Apache or Nginx.
Posts
-----
-* Complain about IPC::Open2 ignoring 'open' pragma
-* Complain about undef references causing errors, and non-fine granular switch no strict 'refs'
-* Thank for perldoc.org
+- Complain about IPC::Open2 ignoring 'open' pragma
+- Complain about undef references causing errors, and non-fine granular
+ switch no strict 'refs'
+- Thank for perldoc.org
I18N patch url_for
------------------
-I have taken the monkey patching approach that was taken by Mojolicious::Plugin::I18N.
-I used `can` to get the old method for looser coupling and used the Mojo::Util::monkey_patch
-instead of manually doing it. This is probably overkill.
-
-I'm desperately looking for a different approach. Yeah the monkey patching works great,
-but it violates the open-closed principal. But I cannot find an appropriate alternative.
-Extending the controller and overriding url_for does not work cleanly as the user directly
-inherits from Mojolicious::Controller.
-Also going the 'better' approach of solving at the root by using an extension of
-Mojolicious::Routes::Match->path_for and supplying it to the controller by setting
-match does not work as it is on the one hand extremely difficult to inject it in
-the first place and the attribute is overwritten in the dispatching process anyways.
-One issue when taking the Match approach is that it needs knowledge of the stash
-values which can cause cyclic references.
+I have taken the monkey patching approach that was taken by
+Mojolicious::Plugin::I18N. I used `can` to get the old method for looser
+coupling and used the Mojo::Util::monkey_patch instead of manually doing it.
+This is probably overkill.
+
+I'm desperately looking for a different approach. Yeah the monkey patching
+works great, but it violates the open-closed principal. But I cannot find
+an appropriate alternative. Extending the controller and overriding url_for
+does not work cleanly as the user directly inherits from Mojolicious::Controller.
+Also going the 'better' approach of solving at the root by using an extension
+of Mojolicious::Routes::Match->path_for and supplying it to the controller
+by setting match does not work as it is on the one hand extremely difficult
+to inject it in the first place and the attribute is overwritten in the
+dispatching process anyways. One issue when taking the Match approach is
+that it needs knowledge of the stash values which can cause cyclic references.
I thought of three approaches injecting the modified Match instance into the class:
1. Extending the Mojolicious::Controller and overriding the new method.
This has the issue that inheritance is static but one can use Roles that
are dynamically consumed.
-2. Overriding build_controller in Mojolicious. To make this cleanly it needs to be monkey patched
- by the plugin which is exactly what we want to avoid. :(
-3. The matcher can be set in a hook relatively early in its lifetime
- and hooks compose well.
-
-A completely different option is to use the router directly and register a global
-route that has the language as parameter. But omitting the language leads to problems.
+2. Overriding build_controller in Mojolicious. To make this cleanly it needs
+ to be monkey patched by the plugin which is exactly what we want to avoid. :(
+3. The matcher can be set in a hook relatively early in its lifetime and
+ hooks compose well.
+
+A completely different option is to use the router directly and register a
+global route that has the language as parameter. But omitting the language
+leads to problems.
One can use a redirect on root. Very easy but also not very effective.
diff --git a/actions.sh b/actions.sh
new file mode 100755
index 0000000..49fa4e2
--- /dev/null
+++ b/actions.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env sh
+
+set -euC
+
+test () {
+ eval "prove -l ${1-} t/"
+}
+
+start () {
+ script/jwebmail daemon
+}
+
+logrotate () {
+ mode=${1:-development}
+ mv -i "log/$mode.log" "log/${mode}_$(date --iso-8601=minutes).log"
+}
+
+linelength () {
+ files=${1:-'README.md CHANGES LICENSE'}
+ for file in $files
+ do
+ fold -s -w 85 "$file" | diff "$file" -
+ done
+}
+
+follow () {
+ mode=${1:-development}
+ tail -f "log/$mode.log"
+}
+
+check_manifest () {
+ perl -nE 'chomp; say unless -e' MANIFEST
+}
+
+cmd=$1
+shift
+if [ "$(command -v "$cmd")" = "$cmd" ]
+then eval "$cmd" "$@"
+else echo "unkown commad '$cmd'"; exit 1
+fi
diff --git a/lib/JWebmail/Model/WriteMails.pm b/lib/JWebmail/Model/WriteMails.pm
index aa2f1d4..7a50bcb 100644
--- a/lib/JWebmail/Model/WriteMails.pm
+++ b/lib/JWebmail/Model/WriteMails.pm
@@ -69,8 +69,10 @@ sub sendmail {
push @recipients, @{ $mail->{cc} } if $mail->{cc};
push @recipients, @{ $mail->{bcc} } if $mail->{bcc};
- say $mime if $Block_Writes;
- return 1 if $Block_Writes;
+ if ($Block_Writes) {
+ say $mime;
+ return 1;
+ }
return _send($mime, @recipients);
}
diff --git a/lib/JWebmail/Plugin/I18N2.pm b/lib/JWebmail/Plugin/I18N2.pm
index 5084c97..35367e9 100644
--- a/lib/JWebmail/Plugin/I18N2.pm
+++ b/lib/JWebmail/Plugin/I18N2.pm
@@ -2,11 +2,65 @@ package JWebmail::Plugin::I18N2;
use Mojo::Base 'Mojolicious::Plugin';
-use Mojolicious::Controller;
-use Mojo::File;
-use Mojo::Util 'monkey_patch';
-use Config::Tiny;
+package JWebmail::Plugin::I18N2::Translator {
+
+ use Mojo::File;
+
+ use Config::Tiny;
+
+ sub new {
+ my $cls = shift;
+ my $conf = @_ == 1 ? shift : {@_};
+ my $self = {};
+
+ 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 bless $self, $cls;
+ }
+
+ 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;
+ return $self->{$lang}{$word};
+ }
+
+ 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;
+ }
+}
package JWebmail::Plugin::I18N2::Match {
@@ -14,83 +68,85 @@ package JWebmail::Plugin::I18N2::Match {
has '_i18n2_stash';
+ sub __add_option_no_override {
+ my $key = shift;
+ my $value = shift;
+
+ if (ref $_[0] eq 'HASH' && @_ == 1) {
+ $_[0]->{$key} ||= $value;
+ }
+ elsif (ref $_[1] eq 'HASH' && @_ == 2) {
+ $_[1]->{$key} ||= $value;
+ }
+ else {
+ my ($dom, %args) = @_%2 == 0 ? (undef, @_) : @_;
+ $args{$key} ||= $value;
+ @_ = ($dom, %args);
+ shift @_ unless defined $_[0];
+ }
+ return @_;
+ }
+
sub path_for {
my $self = shift;
+ my @args = @_;
if (my $lang = $self->_i18n2_stash->{lang}) {
- if (ref $_[0] eq 'HASH') {
- $_[0]->{lang} ||= $lang;
- }
- elsif (ref $_[1] eq 'HASH') {
- $_[1]->{lang} ||= $lang;
- }
- else {
- my ($dom, %args) = @_%2 == 0 ? (undef, @_) : @_;
- $args{lang} ||= $lang;
- @_ = ($dom, %args);
- shift @_ unless defined $_[0];
- }
+ @args = __add_option_no_override(lang => $lang, @args);
}
- $self->next::method(@_)
+ return $self->SUPER::path_for(@args);
}
}
-has '_language_loaded' => sub { {} };
-
-
sub register {
my ($self, $app, $conf) = @_;
$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 $translator = $conf->{translator} || sub { JWebmail::Plugin::I18N2::Translator->new(@_) };
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} // {}};
+ ? $conf->{directory}
+ : $app->home->child($conf->{directory} || 'lang');
- unless (@languages) {
- @languages = map { $_ =~ s|^.*/(..)\.lang$|$1|r } glob("$fileLocation/*.lang");
- }
-
- $app->defaults(languages => [@languages]);
-
- # load languages
- my $TXT;
- for my $l (@languages) {
- $TXT->{$l} = _loadi18n($fileLocation, $l, $i18n_log);
- }
+ my $t = $translator->(
+ default_language => $defaultLang,
+ directory => $fileLocation,
+ %{$conf->{rest} // {}}
+ );
{
local $" = ',';
- $i18n_log->debug("loaded languages (@languages)");
+ $i18n_log->debug("loaded languages (@{[$t->languages]})");
+
+ if (keys $conf->{languages}->%* > $t->languages) {
+ $i18n_log->warn("missing languages");
+ }
}
- $self->_language_loaded( { map { $_ => 1 } @languages } );
+ $app->defaults(default_language => $defaultLang);
+ $app->defaults(languages => [$t->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) } );
+ $app->helper(l => sub {
+ my $c = shift;
+ my $lang = @_ == 2 ? $_[0] : $c->stash->{lang};
+ my $word = @_ == 2 ? $_[1] : $_[0];
+
+ my $res = $t->translate($lang, $word);
+ unless ($res) {
+ local $" = ' ';
+ $app->log->warn('[' . __PACKAGE__ . "] missing translation for '$lang':'$word' @{[ caller(1) ]}[0..2]");
+ }
+ return $res;
+ });
# modify incoming url
$app->hook(before_dispatch => sub {
my $c = shift;
unshift @{ $c->req->url->path->parts }, ''
- unless $self->_language_loaded->{$c->req->url->path->parts->[0] || ''};
+ unless $t->languages($c->req->url->path->parts->[0] || '');
});
# modify generated url
@@ -105,29 +161,6 @@ sub register {
return $app->routes->any('/:lang' => {lang => $defaultLang});
}
-
-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__
@@ -140,32 +173,32 @@ JWebmail::Plugin::I18N2 - Custom Made I18N Support an alternative to JWebmail::P
=head1 SYNOPSIS
- $app->plugin('I18N2', {
- languages => [qw(en de es)],
- default_language => 'en',
- directory => '/path/to/language/files/',
- })
+ $app->plugin('I18N2', {
+ languages => [qw(en de es)],
+ default_language => 'de',
+ directory => 'path/to/language/files/',
+ })
- # in your controller
- $c->l('hello')
+ # in your controller
+ $c->l('hello')
- # in your templates
- <%= l 'hello' %>
+ # in your templates
+ <%= l 'hello' %>
- @@ de.lang
- login = anmelden
- userid = nuzerkennung
- passwd = passwort
- failed = fehlgeschlagen
- about = über
+ @@ 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
+ 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
+ # 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
+ url_for('my_other_route', lang => 'es') #=> example.com/es/my_other_route
=head1 DESCRIPTION
@@ -177,6 +210,8 @@ Be carefult with colliding routes.
Mojolicious::Controller::url_for is patched so that the current language will be kept for
router named urls.
+This Plugin only works with Mojolicious version 8.64 or higher.
+
=head1 OPTIONS
=head2 default_language
@@ -190,7 +225,7 @@ Directory to look for language files.
=head2 languages
List of allowed languages.
-Files of the pattern "$lang.lang" will be looked for.
+As a default, files of the pattern "$lang.lang" will be looked for.
=head1 HELPERS
@@ -198,7 +233,6 @@ Files of the pattern "$lang.lang" will be looked for.
This is used for your translations.
- $c->l('hello')
- $app->helper('hello')->()
+ $c->l('hello')
=cut
diff --git a/t/I18N2.t b/t/I18N2.t
new file mode 100644
index 0000000..f9c6285
--- /dev/null
+++ b/t/I18N2.t
@@ -0,0 +1,31 @@
+use v5.22;
+use warnings;
+use utf8;
+
+use Test::More;
+
+use JWebmail::Plugin::I18N2;
+
+*add_option = \&JWebmail::Plugin::I18N2::Match::__add_option_no_override;
+
+
+subtest 'add_option' => sub {
+
+ is_deeply(add_option(a => 1, {}), {a => 1}, "empty-hash");
+ is_deeply(add_option(a => 5, {a => 1}), {a => 1}, "no-add-hash");
+ is_deeply(add_option(b => 2, {a => 1}), {a => 1, b => 2}, "add-hash");
+
+ is_deeply([add_option(a => 1, 'arg', {})], ['arg', {a => 1}], "empty-arg-hash");
+ is_deeply([add_option(a => 5, 'arg', {a => 1})], ['arg', {a => 1}], "no-add-arg-hash");
+ is_deeply([add_option(b => 2, 'arg', {a => 1})], ['arg', {a => 1, b => 2}], "add-arg-hash");
+
+ is_deeply([add_option(a => 1)], [a => 1], "empty-array");
+ is_deeply([add_option(a => 5, a => 1)], [a => 1], "no-add-array");
+ eq_set([add_option(b => 2, a => 1)], [a => 1, b => 2], "add-array");
+
+ is_deeply([add_option(a => 1, 'arg')], ['arg', a => 1], "empty-arg-array");
+ is_deeply([add_option(a => 5, 'arg', a => 1)], ['arg', a => 1], "no-add-arg-array");
+ eq_set([add_option(b => 2, 'arg', a => 1)], ['arg', a => 1, b => 2], "add-arg-array");
+};
+
+done_testing;