File Coverage

lib/Web/ComposableRequest/Role/L10N.pm
Criterion Covered Total %
statement 82 82 100.0
branch 13 18 72.2
condition 15 24 62.5
subroutine 20 20 100.0
pod 2 2 100.0
total 132 146 90.4


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