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   4631 use Mojo::Base 'Mojolicious::Plugin';
  5         11  
  5         41  
3              
4 5     5   1879 use Mojo::URL;
  5         14  
  5         56  
5 5     5   4026 use I18N::LangTags;
  5         9563  
  5         312  
6 5     5   3714 use I18N::LangTags::Detect;
  5         4241  
  5         6209  
7              
8             our $VERSION = '1.3';
9              
10             # "Can we have Bender burgers again?
11             # No, the cat shelter’s onto me."
12             sub register {
13 5     5 1 302 my ($plugin, $app, $conf) = @_;
14            
15             # Initialize
16 5   33     35 my $namespace = $conf->{namespace} || ( (ref $app) . '::I18N' );
17 5   100     38 my $default = $conf->{default } || 'en';
18 5         17 $default =~ tr/-A-Z/_a-z/;
19 5         12 $default =~ tr/_a-z0-9//cd;
20            
21 5         12 my $langs = $conf->{support_url_langs};
22 5         12 my $hosts = $conf->{support_hosts };
23            
24             # Default Handler
25             my $handler = sub {
26 42     42   435 shift->stash->{i18n} =
27             Mojolicious::Plugin::I18N::_Handler->new(namespace => $namespace, default => $default)
28             ;
29 5         25 };
30            
31             # Add hook
32             $app->hook(
33             before_dispatch => sub {
34 42     42   819310 my $self = shift;
35            
36             # Handler
37 42         188 $handler->( $self );
38            
39             # Header detection
40 42 50       1156 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     8192 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       159 $self->stash(lang_default => $languages[0]) if $languages[0];
61            
62             # URL detection
63 42 50       348 if (my $path = $self->req->url->path) {
64 42         3570 my $part = $path->parts->[0];
65            
66 42 100 100     6356 if ($part && $langs && grep { $part eq $_ } @$langs) {
  74   100     586  
67             # Ignore static files
68 12 50       59 return if $self->res->code;
69            
70 12         3315 $self->app->log->debug("Found language $part in URL $path");
71            
72 12         3312 unshift @languages, $part;
73            
74             # Save lang in stash
75 12         74 $self->stash(lang => $part);
76            
77             # Clean path
78 12         196 shift @{$path->parts};
  12         39  
79 12         115 $path->trailing_slash(0);
80             }
81             }
82            
83             # Languages
84 42         604 $self->languages(@languages, $default);
85             }
86 5         82 );
87            
88             # Add "languages" helper
89             $app->helper(languages => sub {
90 96     96   80122 my $self = shift;
91            
92 96 50       306 $handler->( $self ) unless $self->stash('i18n');
93            
94 96         1265 $self->stash->{i18n}->languages(@_);
95 5         379 });
96            
97             # Add "l" helper
98             $app->helper(l => sub {
99 48     48   79262 my $self = shift;
100            
101 48 50       165 $handler->( $self ) unless $self->stash('i18n');
102            
103 48         608 $self->stash->{i18n}->localize(@_);
104 5         725 });
105            
106             # Reimplement "url_for" helper
107 5         433 my $mojo_url_for = *Mojolicious::Controller::url_for{CODE};
108            
109             my $i18n_url_for = sub {
110 82     82   233622 my $self = shift;
111 82         271 my $url = $self->$mojo_url_for(@_);
112            
113             # Absolute URL
114 82 100       39272 return $url if $url->is_abs;
115            
116             # Discard target if present
117 51 100 100     1709 shift if (@_ % 2 && !ref $_[0]) || (@_ > 1 && ref $_[-1]);
      100        
      66        
118            
119             # Unveil params
120 51 100       176 my %params = @_ == 1 ? %{$_[0]} : @_;
  2         7  
121            
122             # Detect lang
123 51 100 100     404 if (my $lang = $params{lang} || $self->stash('lang')) {
124 16   50     158 my $path = $url->path || [];
125            
126             # Root
127 16 100 33     207 if (!$path->[0]) {
  30 50       1202  
128 6         71 $path->parts([ $lang ]);
129             }
130            
131             # No language detected
132             elsif ( ref $langs ne 'ARRAY' or not scalar grep { $path->contains("/$_") } @$langs ) {
133 10         501 unshift @{ $path->parts }, $lang;
  10         23  
134             }
135             }
136            
137 51         725 $url;
138 5         27 };
139            
140             {
141 5     5   34 no strict 'refs';
  5         9  
  5         203  
  5         11  
142 5     5   26 no warnings 'redefine';
  5         10  
  5         463  
143            
144 5         37 *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         10  
  5         44  
150              
151 5   50 5   650 use constant DEBUG => $ENV{MOJO_I18N_DEBUG} || 0;
  5         10  
  5         4701  
152              
153             # "Robot 1-X, save my friends! And Zoidberg!"
154             sub languages {
155 96     96   939 my ($self, @languages) = @_;
156            
157 96 100       274 unless (@languages) {
158 38         117 my $lang = $self->{language};
159            
160             # lang such as en-us
161 38         81 $lang =~ s/_/-/g;
162            
163 38         177 return $lang;
164             }
165            
166             # Handle
167 58         153 my $namespace = $self->{namespace};
168            
169             # Load Lang Module
170 58         258 $self->_load_module($namespace => $_) for @languages;
171            
172 58 50       419 if (my $handle = $namespace->get_handle(@languages)) {
173 58     6   20044 $handle->fail_with(sub { $_[1] });
  6         245  
174 58         570 $self->{handle} = $handle;
175 58         447 $self->{language} = $handle->language_tag;
176             }
177              
178 58         1213 return $self;
179             }
180              
181             sub localize {
182 48     48   436 my $self = shift;
183 48         98 my $key = shift;
184 48 50       165 return $key unless my $handle = $self->{handle};
185 48         333 return $handle->maketext($key, @_);
186             }
187              
188             sub _load_module {
189 98     98   272 my $self = shift;
190            
191 98         235 my($namespace, $lang) = @_;
192 98 50 33     477 return unless $namespace && $lang;
193            
194             # lang such as en-us
195 98         194 $lang =~ s/-/_/g;
196            
197 98 100       943 unless ($namespace->can('new')) {
198 3         4 DEBUG && warn("Load default namespace $namespace");
199            
200 3         33 (my $file = $namespace) =~ s{::|'}{/}g;
201 3         405 eval qq(require "$file.pm");
202            
203 3 100       14815 if ($@) {
204 2         16 DEBUG && warn("Create default namespace $namespace");
205            
206 2     2   214 eval "package $namespace; use base 'Locale::Maketext'; 1;";
  2         14  
  2         4  
  2         2717  
207 2 50       18 die qq/Couldn't initialize I18N default class "$namespace": $@/ if $@;
208             }
209             }
210            
211 98         253 for ($self->{default}, $lang) {
212 196         383 my $module = "${namespace}::$_";
213 196 100       1707 unless ($module->can('new')) {
214 15         22 DEBUG && warn("Load the I18N class $module");
215            
216 15         163 (my $file = $module) =~ s{::|'}{/}g;
217 15         1297 eval qq(require "$file.pm");
218            
219 15         8990 my $default = $self->{default};
220 15 100 66     591 if ($@ || not eval "\%${module}::Lexicon") {
221 14 100       198 if ($_ eq $default) {
222 1         2 DEBUG && warn("Create the I18N class $module");
223            
224 1     1   65 eval "package ${module}; use base '$namespace';" . 'our %Lexicon = (_AUTO => 1); 1;';
  1         6  
  1         2  
  1         356  
225 1 50       6 die qq/Couldn't initialize I18N class "$namespace": $@/ if $@;
226             }
227             }
228             }
229             }
230             }
231              
232             1;
233              
234             __END__