File Coverage

lib/Web/ComposableRequest/Role/L10N.pm
Criterion Covered Total %
statement 93 98 94.9
branch 21 30 70.0
condition 23 35 65.7
subroutine 22 22 100.0
pod 2 2 100.0
total 161 187 86.1


line stmt bran cond sub pod time code
1             package Web::ComposableRequest::Role::L10N;
2              
3 1     1   1172 use namespace::autoclean;
  1         3  
  1         10  
4              
5 1     1   105 use Web::ComposableRequest::Constants qw( NUL TRUE );
  1         3  
  1         9  
6 1         8 use Web::ComposableRequest::Util qw( extract_lang is_member
7 1     1   510 add_config_role );
  1         3  
8 1     1   819 use Unexpected::Functions qw( inflate_placeholders );
  1         3  
  1         14  
9 1         10 use Unexpected::Types qw( ArrayRef CodeRef NonEmptySimpleStr
10 1     1   479 Undef );
  1         2  
11 1     1   4024 use Moo::Role;
  1         3  
  1         11  
12              
13             requires qw( query_params _config _env );
14              
15             add_config_role __PACKAGE__.'::Config';
16              
17             # Attribute constructors
18             my $_build_locale = sub {
19 4     4   45 my $self = shift;
20 4         71 my $conf = $self->_config;
21 4         24 my $locale = $self->query_params->('locale', { optional => TRUE });
22              
23 4 100 100     35 return $locale if $locale and is_member $locale, $conf->locales;
24              
25 2         4 my $lang;
26              
27 2 100 66     9 if ($locale and $lang = extract_lang($locale)) {
28 1 50 33     5 return $lang if $lang ne $locale and is_member $lang, $conf->locales;
29             }
30              
31 2         4 for my $locale (@{$self->locales}) {
  2         40  
32 3 100       82 return $locale if is_member $locale, $conf->locales;
33             }
34              
35 1         3 for my $lang (map { extract_lang $_ } @{$self->locales}) {
  1         9  
  1         14  
36 1 50       6 return $lang if is_member $lang, $conf->locales;
37             }
38              
39 1         16 return $conf->locale;
40             };
41              
42             my $_build_locales = sub {
43 2     2   19 my $self = shift;
44 2   50     12 my $lang = $self->_env->{ 'HTTP_ACCEPT_LANGUAGE' } // NUL;
45              
46 4         13 return [ map { s{ _ \z }{}mx; $_ }
  4         30  
47 4   100     16 map { join '_', $_->[ 0 ], uc( $_->[ 1 ] // NUL ) }
48 4         10 map { [ split m{ - }mx, $_ ] }
49 2         10 map { ( split m{ ; }mx, $_ )[ 0 ] }
  4         11  
50             split m{ , }mx, lc $lang ];
51             };
52              
53             my $_build_localiser = sub {
54             return sub {
55 10     10   115 my ($key, $args) = @_;
56              
57 10 50 50     22 defined $key or return; $key = "${key}"; chomp $key; $args //= {};
  10         15  
  10         22  
  10         17  
58              
59 10         10 my $text = $key;
60              
61 10 100 66     64 if (defined $args->{params} and ref $args->{params} eq 'ARRAY') {
62 9 100       49 return $text if 0 > index $text, '[_';
63              
64             # Expand positional parameters of the form [_]
65             return inflate_placeholders
66             [ '[?]', '[]', $args->{no_quote_bind_values} ], $text,
67 8         21 @{ $args->{params} };
  8         39  
68             }
69              
70 1 50       5 return $text if 0 > index $text, '{';
71              
72             # Expand named parameters of the form {param_name}
73 1         3 my %args = %{ $args };
  1         5  
74 1         3 my $re = join '|', map { quotemeta $_ } keys %args;
  4         10  
75              
76 1 50       66 $text =~ s{ \{($re)\} }{ defined $args{$1} ? $args{$1} : "{${1}?}" }egmx;
  1         7  
77              
78 1         14 return $text;
79 4     4   106 };
80             };
81              
82             # Public attributes
83             has 'domain' => is => 'lazy', isa => NonEmptySimpleStr | Undef,
84       1     builder => sub {};
85              
86             has 'domain_prefix' => is => 'lazy', isa => NonEmptySimpleStr | Undef;
87              
88             has 'language' => is => 'lazy', isa => NonEmptySimpleStr,
89 1     1   703 builder => sub { extract_lang $_[ 0 ]->locale };
90              
91             has 'locale' => is => 'lazy', isa => NonEmptySimpleStr,
92             builder => $_build_locale;
93              
94             has 'locales' => is => 'lazy', isa => ArrayRef[NonEmptySimpleStr],
95             builder => $_build_locales;
96              
97             has 'localiser' => is => 'lazy', isa => CodeRef,
98             builder => $_build_localiser;
99              
100             my $_domains;
101              
102             # Public methods
103             sub loc {
104 9     9 1 29 my ($self, $key, @args) = @_;
105              
106 9         26 my $args = $self->_localise_args(@args);
107              
108 9   33     235 $args->{locale} //= $self->locale;
109              
110 9         267 return $self->localiser->($key, $args);
111             }
112              
113             sub loc_default {
114 1     1 1 4 my ($self, $key, @args) = @_;
115              
116 1         5 my $args = $self->_localise_args(@args);
117              
118 1         6 $args->{locale} = $self->_config->locale;
119              
120 1         32 return $self->localiser->($key, $args);
121             }
122              
123             # Private methods
124             sub _get_domains {
125 1     1   2 my $self = shift;
126 1   50     1 my $domains = [ @{$self->_config->l10n_attributes->{domains} // []} ];
  1         9  
127 1 50       24 my $domain = $self->domain or return $domains;
128 0         0 my $prefix = $self->domain_prefix;
129              
130 0 0       0 $domain = "${prefix}-${domain}" if $prefix;
131 0         0 push @{$domains}, $domain;
  0         0  
132              
133 0         0 return $domains;
134             }
135              
136             sub _localise_args {
137 10     10   14 my $self = shift;
138 10 100 100     78 my $args = ($_[0] && ref $_[0] eq 'HASH' ) ? { %{ $_[0] } }
  1 100 100     5  
139             : { params => ($_[0] && ref $_[0] eq 'ARRAY') ? $_[0] : [@_] };
140              
141             $args->{domains} = $_domains //= $self->_get_domains
142 10 50 66     49 unless exists $args->{domains};
143              
144 10   33     94 $args->{no_quote_bind_values} //= !$self->_config->quote_bind_values;
145              
146 10         18 return $args;
147             }
148              
149             package Web::ComposableRequest::Role::L10N::Config;
150              
151 1     1   2195 use namespace::autoclean;
  1         3  
  1         6  
152              
153 1     1   104 use Web::ComposableRequest::Constants qw( LANG TRUE );
  1         2  
  1         6  
154 1         6 use Unexpected::Types qw( ArrayRef Bool HashRef
155 1     1   465 NonEmptySimpleStr );
  1         2  
156 1     1   3906 use Moo::Role;
  1         3  
  1         57  
157              
158             # Public attributes
159             has 'l10n_attributes' => is => 'ro', isa => HashRef,
160 2     2   177 builder => sub { { domains => [ 'messages' ] } };
161              
162             has 'locale' => is => 'ro', isa => NonEmptySimpleStr,
163             default => LANG;
164              
165             has 'locales' => is => 'ro', isa => ArrayRef[NonEmptySimpleStr],
166 2     2   96 builder => sub { [ LANG ] };
167              
168             has 'quote_bind_values' => is => 'ro', isa => Bool, default => TRUE;
169              
170             1;
171              
172             __END__