File Coverage

lib/Mojolicious/Plugin/I18N.pm
Criterion Covered Total %
statement 124 128 96.8
branch 38 52 73.0
condition 27 39 69.2
subroutine 20 20 100.0
pod 1 1 100.0
total 210 240 87.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::I18N;
2 5     5   3601 use Mojo::Base 'Mojolicious::Plugin';
  5         7  
  5         32  
3              
4 5     5   944 use Mojo::URL;
  5         12  
  5         43  
5 5     5   2029 use I18N::LangTags;
  5         8567  
  5         266  
6 5     5   1708 use I18N::LangTags::Detect;
  5         3843  
  5         4808  
7              
8             our $VERSION = '1.4';
9              
10             # "Can we have Bender burgers again?
11             # No, the cat shelter’s onto me."
12             sub register {
13 5     5 1 253 my ($plugin, $app, $conf) = @_;
14              
15             # Initialize
16 5   33     23 my $namespace = $conf->{namespace} || ( (ref $app) . '::I18N' );
17 5   100     31 my $default = $conf->{default } || 'en';
18 5         11 $default =~ tr/-A-Z/_a-z/;
19 5         10 $default =~ tr/_a-z0-9//cd;
20              
21 5         9 my $langs = $conf->{support_url_langs};
22 5         7 my $hosts = $conf->{support_hosts };
23              
24             # Default Handler
25             my $handler = sub {
26 42     42   422 shift->stash->{i18n} =
27             Mojolicious::Plugin::I18N::_Handler->new(namespace => $namespace, default => $default)
28             ;
29 5         21 };
30              
31             # Add hook
32             $app->hook(
33             before_dispatch => sub {
34 42     42   823251 my $self = shift;
35              
36             # Handler
37 42         174 $handler->( $self );
38              
39             # Header detection
40 42 50       1217 my @languages = $conf->{no_header_detect}
41             ? ()
42             : I18N::LangTags::implicate_supers(
43             I18N::LangTags::Detect->http_accept_langs(
44             $self->req->headers->accept_language
45             )
46             )
47             ;
48              
49             # Host detection
50 42 50 33     7684 if ($conf->{support_hosts} and my $host = $self->req->headers->host) {
51 0         0 $host =~ s/^www\.//; # hack
52 0 0       0 if (my $lang = $conf->{support_hosts}->{ $host }) {
53 0         0 $self->app->log->debug("Found language $lang, Host header is $host");
54              
55 0         0 unshift @languages, $lang;
56             }
57             }
58              
59             # Set default language
60 42 100       201 $self->stash(lang_default => $languages[0]) if $languages[0];
61              
62             # URL detection
63 42 50       362 if (my $path = $self->req->url->path) {
64 42         3714 my $part = $path->parts->[0];
65              
66 42 100 100     4442 if ($part && $langs && grep { $part eq $_ } @$langs) {
  74   100     262  
67             # Ignore static files
68 12 50       72 return if $self->res->code;
69              
70 12         1529 $self->app->log->debug("Found language $part in URL $path");
71              
72 12         2691 unshift @languages, $part;
73              
74             # Save lang in stash
75 12         60 $self->stash(lang => $part);
76              
77             # Clean path
78 12         259 shift @{$path->parts};
  12         47  
79 12         127 $path->trailing_slash(0);
80             }
81             }
82              
83             # Languages
84 42         572 $self->languages(@languages, $default);
85             }
86 5         55 );
87              
88             # Add "languages" helper
89             $app->helper(languages => sub {
90 96     96   64068 my $self = shift;
91              
92 96 50       297 $handler->( $self ) unless $self->stash('i18n');
93              
94 96         1153 $self->stash->{i18n}->languages(@_);
95 5         253 });
96              
97             # Add "l" helper
98             $app->helper(l => sub {
99 48     48   82051 my $self = shift;
100              
101 48 50       171 $handler->( $self ) unless $self->stash('i18n');
102              
103 48         593 $self->stash->{i18n}->localize(@_);
104 5         563 });
105              
106             # Reimplement "url_for" helper
107 5         332 my $mojo_url_for = *Mojolicious::Controller::url_for{CODE};
108              
109             my $i18n_url_for = sub {
110 82     82   231771 my $self = shift;
111 82         278 my $url = $self->$mojo_url_for(@_);
112              
113             # Absolute URL
114 82 100       42352 return $url if $url->is_abs;
115              
116             # Discard target if present
117 51 100 100     2020 shift if (@_ % 2 && !ref $_[0]) || (@_ > 1 && ref $_[-1]);
      100        
      66        
118              
119             # Unveil params
120 51 100       229 my %params = @_ == 1 ? %{$_[0]} : @_;
  2         9  
121              
122             # Detect lang
123 51 100 100     268 if (my $lang = $params{lang} || $self->stash('lang')) {
124 16   50     158 my $path = $url->path || [];
125              
126             # Root
127 16 100 33     248 if (!$path->[0]) {
  30 50       1163  
128 6         73 $path->parts([ $lang ]);
129             }
130              
131             # No language detected
132             elsif ( ref $langs ne 'ARRAY' or not scalar grep { $path->contains("/$_") } @$langs ) {
133 10         416 unshift @{ $path->parts }, $lang;
  10         22  
134             }
135             }
136              
137 51         790 $url;
138 5         23 };
139              
140             {
141 5     5   32 no strict 'refs';
  5         7  
  5         150  
  5         8  
142 5     5   20 no warnings 'redefine';
  5         6  
  5         421  
143              
144 5         28 *Mojolicious::Controller::url_for = $i18n_url_for;
145             }
146             }
147              
148             package Mojolicious::Plugin::I18N::_Handler;
149 5     5   26 use Mojo::Base -base;
  5         6  
  5         37  
150              
151 5   50 5   575 use constant DEBUG => $ENV{MOJO_I18N_DEBUG} || 0;
  5         9  
  5         3992  
152              
153             # "Robot 1-X, save my friends! And Zoidberg!"
154             sub languages {
155 96     96   864 my ($self, @languages) = @_;
156              
157 96 100       257 unless (@languages) {
158 38         79 my $lang = $self->{language};
159              
160             # lang such as en-us
161 38         87 $lang =~ s/_/-/g;
162              
163 38         139 return $lang;
164             }
165              
166             # Handle
167 58         140 my $namespace = $self->{namespace};
168              
169             # Load Lang Module
170 58         266 $self->_load_module($namespace => $_) for @languages;
171              
172 58 50       370 if (my $handle = $namespace->get_handle(@languages)) {
173 58     6   18791 $handle->fail_with(sub { $_[1] });
  6         211  
174 58         598 $self->{handle} = $handle;
175 58         402 $self->{language} = $handle->language_tag;
176             }
177              
178 58         1315 return $self;
179             }
180              
181             sub localize {
182 48     48   390 my $self = shift;
183 48         76 my $key = shift;
184 48 50       162 return $key unless my $handle = $self->{handle};
185 48         336 return $handle->maketext($key, @_);
186             }
187              
188             sub _load_module {
189 98     98   280 my $self = shift;
190              
191 98         168 my($namespace, $lang) = @_;
192 98 50 33     475 return unless $namespace && $lang;
193              
194             # lang such as en-us
195 98         208 $lang =~ s/-/_/g;
196              
197 98 100       828 unless ($namespace->can('new')) {
198 3         6 DEBUG && warn("Load default namespace $namespace");
199              
200 3         46 (my $file = $namespace) =~ s{::|'}{/}g;
201 3         309 eval qq(require "$file.pm");
202              
203 3 100       10334 if ($@) {
204 2         15 DEBUG && warn("Create default namespace $namespace");
205              
206 2     2   250 eval "package $namespace; use base 'Locale::Maketext'; 1;";
  2         13  
  2         3  
  2         1874  
207 2 50       18 die qq/Couldn't initialize I18N default class "$namespace": $@/ if $@;
208             }
209             }
210              
211 98         263 for ($self->{default}, $lang) {
212 196         340 my $module = "${namespace}::$_";
213 196 100       1425 unless ($module->can('new')) {
214 15         19 DEBUG && warn("Load the I18N class $module");
215              
216 15         166 (my $file = $module) =~ s{::|'}{/}g;
217 15         1156 eval qq(require "$file.pm");
218              
219 15         7604 my $default = $self->{default};
220 15 100 66     550 if ($@ || not eval "\%${module}::Lexicon") {
221 14 100       197 if ($_ eq $default) {
222 1         2 DEBUG && warn("Create the I18N class $module");
223              
224 1     1   90 eval "package ${module}; use base '$namespace';" . 'our %Lexicon = (_AUTO => 1); 1;';
  1         6  
  1         2  
  1         291  
225 1 50       12 die qq/Couldn't initialize I18N class "$namespace": $@/ if $@;
226             }
227             }
228             }
229             }
230             }
231              
232             1;
233              
234             __END__