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   3578 use Mojo::Base 'Mojolicious::Plugin';
  5         9  
  5         33  
3              
4 5     5   1060 use Mojo::URL;
  5         7  
  5         42  
5 5     5   1846 use I18N::LangTags;
  5         7326  
  5         313  
6 5     5   1874 use I18N::LangTags::Detect;
  5         3349  
  5         4212  
7              
8             our $VERSION = '1.5';
9              
10             # "Can we have Bender burgers again?
11             # No, the cat shelter’s onto me."
12             sub register {
13 5     5 1 270 my ($plugin, $app, $conf) = @_;
14              
15             # Initialize
16 5   33     26 my $namespace = $conf->{namespace} || ( (ref $app) . '::I18N' );
17 5   100     35 my $default = $conf->{default } || 'en';
18 5         15 $default =~ tr/-A-Z/_a-z/;
19 5         15 $default =~ tr/_a-z0-9//cd;
20              
21 5         10 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   400 shift->stash->{i18n} =
27             Mojolicious::Plugin::I18N::_Handler->new(namespace => $namespace, default => $default)
28             ;
29 5         27 };
30              
31             # Add hook
32             $app->hook(
33             before_dispatch => sub {
34 42     42   610091 my $self = shift;
35              
36             # Handler
37 42         161 $handler->( $self );
38              
39             # Header detection
40 42 50       1123 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     6956 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       307 if (my $path = $self->req->url->path) {
64 42         3047 my $part = $path->parts->[0];
65              
66 42 100 100     3753 if ($part && $langs && grep { $part eq $_ } @$langs) {
  74   100     302  
67             # Ignore static files
68 12 50       62 return if $self->res->code;
69              
70 12         1092 $self->app->log->debug("Found language $part in URL $path");
71              
72 12         2185 unshift @languages, $part;
73              
74             # Save lang in stash
75 12         57 $self->stash(lang => $part);
76              
77             # Clean path
78 12         229 shift @{$path->parts};
  12         50  
79 12         136 $path->trailing_slash(0);
80             }
81             }
82              
83             # Languages
84 42         523 $self->languages(@languages, $default);
85             }
86 5         70 );
87              
88             # Add "languages" helper
89             $app->helper(languages => sub {
90 96     96   62605 my $self = shift;
91              
92 96 50       252 $handler->( $self ) unless $self->stash('i18n');
93              
94 96         1059 $self->stash->{i18n}->languages(@_);
95 5         308 });
96              
97             # Add "l" helper
98             $app->helper(l => sub {
99 48     48   71672 my $self = shift;
100              
101 48 50       141 $handler->( $self ) unless $self->stash('i18n');
102              
103 48         528 $self->stash->{i18n}->localize(@_);
104 5         555 });
105              
106             # Reimplement "url_for" helper
107 5         299 my $mojo_url_for = *Mojolicious::Controller::url_for{CODE};
108              
109             my $i18n_url_for = sub {
110 82     82   187715 my $self = shift;
111 82         253 my $url = $self->$mojo_url_for(@_);
112              
113             # Absolute URL
114 82 100       29600 return $url if $url->is_abs;
115              
116             # Discard target if present
117 51 100 100     1658 shift if (@_ % 2 && !ref $_[0]) || (@_ > 1 && ref $_[-1]);
      100        
      66        
118              
119             # Unveil params
120 51 100       179 my %params = @_ == 1 ? %{$_[0]} : @_;
  2         8  
121              
122             # Detect lang
123 51 100 100     221 if (my $lang = $params{lang} || $self->stash('lang')) {
124 16   50     145 my $path = $url->path || [];
125              
126             # Root
127 16 100 33     171 if (!$path->[0]) {
  30 50       1092  
128 6         58 $path->parts([ $lang ]);
129             }
130              
131             # No language detected
132             elsif ( ref $langs ne 'ARRAY' or not scalar grep { $path->contains("/$_") } @$langs ) {
133 10         389 unshift @{ $path->parts }, $lang;
  10         20  
134             }
135             }
136              
137 51         649 $url;
138 5         22 };
139              
140             {
141 5     5   39 no strict 'refs';
  5         9  
  5         194  
  5         8  
142 5     5   23 no warnings 'redefine';
  5         7  
  5         437  
143              
144 5         29 *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         7  
  5         41  
150              
151 5   50 5   706 use constant DEBUG => $ENV{MOJO_I18N_DEBUG} || 0;
  5         11  
  5         3712  
152              
153             # "Robot 1-X, save my friends! And Zoidberg!"
154             sub languages {
155 96     96   749 my ($self, @languages) = @_;
156              
157 96 100       260 unless (@languages) {
158 38         81 my $lang = $self->{language};
159              
160             # lang such as en-us
161 38         75 $lang =~ s/_/-/g;
162              
163 38         139 return $lang;
164             }
165              
166             # Handle
167 58         126 my $namespace = $self->{namespace};
168              
169             # Load Lang Module
170 58         226 $self->_load_module($namespace => $_) for @languages;
171              
172 58 50       333 if (my $handle = $namespace->get_handle(@languages)) {
173 58     6   16724 $handle->fail_with(sub { $_[1] });
  6         184  
174 58         559 $self->{handle} = $handle;
175 58         377 $self->{language} = $handle->language_tag;
176             }
177              
178 58         1213 return $self;
179             }
180              
181             sub localize {
182 48     48   340 my $self = shift;
183 48         70 my $key = shift;
184 48 50       176 return $key unless my $handle = $self->{handle};
185 48         310 return $handle->maketext($key, @_);
186             }
187              
188             sub _load_module {
189 98     98   201 my $self = shift;
190              
191 98         128 my($namespace, $lang) = @_;
192 98 50 33     508 return unless $namespace && $lang;
193              
194             # lang such as en-us
195 98         210 $lang =~ s/-/_/g;
196              
197 98 100       779 unless ($namespace->can('new')) {
198 3         3 DEBUG && warn("Load default namespace $namespace");
199              
200 3         26 (my $file = $namespace) =~ s{::|'}{/}g;
201 3         282 eval qq(require "$file.pm");
202              
203 3 100       6526 if ($@) {
204 2         10 DEBUG && warn("Create default namespace $namespace");
205              
206 2     2   162 eval "package $namespace; use base 'Locale::Maketext'; 1;";
  2         10  
  2         8  
  2         1224  
207 2 50       15 die qq/Couldn't initialize I18N default class "$namespace": $@/ if $@;
208             }
209             }
210              
211 98         284 for ($self->{default}, $lang) {
212 196         310 my $module = "${namespace}::$_";
213 196 100       1286 unless ($module->can('new')) {
214 15         17 DEBUG && warn("Load the I18N class $module");
215              
216 15         161 (my $file = $module) =~ s{::|'}{/}g;
217 15         1152 eval qq(require "$file.pm");
218              
219 15         6893 my $default = $self->{default};
220 15 100 66     446 if ($@ || not eval "\%${module}::Lexicon") {
221 14 100       174 if ($_ eq $default) {
222 1         1 DEBUG && warn("Create the I18N class $module");
223              
224 1     1   64 eval "package ${module}; use base '$namespace';" . 'our %Lexicon = (_AUTO => 1); 1;';
  1         5  
  1         1  
  1         222  
225 1 50       6 die qq/Couldn't initialize I18N class "$namespace": $@/ if $@;
226             }
227             }
228             }
229             }
230             }
231              
232             1;
233              
234             __END__