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   695 use namespace::autoclean;
  1         2  
  1         7  
4              
5 1     1   64 use Web::ComposableRequest::Constants qw( NUL TRUE );
  1         1  
  1         5  
6 1         5 use Web::ComposableRequest::Util qw( extract_lang is_member
7 1     1   191 add_config_role );
  1         1  
8 1     1   293 use Unexpected::Functions qw( inflate_placeholders );
  1         2  
  1         9  
9 1         5 use Unexpected::Types qw( ArrayRef CodeRef NonEmptySimpleStr
10 1     1   206 Undef );
  1         1  
11 1     1   539 use Moo::Role;
  1         1  
  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   71 my $self = shift;
20 4         7 my $conf = $self->_config;
21 4         14 my $locale = $self->query_params->( 'locale', { optional => TRUE } );
22              
23 4 100 100     32 $locale and is_member $locale, $conf->locales and return $locale;
24              
25 2 50 66     3 my $lang; $locale and $lang = extract_lang( $locale )
  2   66     10  
      33        
26             and $lang ne $locale and is_member $lang, $conf->locales and return $lang;
27              
28 2         5 for my $locale (@{ $self->locales }) {
  2         40  
29 3 100       72 is_member $locale, $conf->locales and return $locale;
30             }
31              
32 1         2 for my $lang (map { extract_lang $_ } @{ $self->locales }) {
  1         6  
  1         16  
33 1 50       4 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   20 my $self = shift; my $lang = $self->_env->{ 'HTTP_ACCEPT_LANGUAGE' } // NUL;
  2         13  
41              
42 4         8 return [ map { s{ _ \z }{}mx; $_ }
  4         34  
43 4   100     17 map { join '_', $_->[ 0 ], uc( $_->[ 1 ] // NUL ) }
44 4         9 map { [ split m{ - }mx, $_ ] }
45 2         8 map { ( split m{ ; }mx, $_ )[ 0 ] }
  4         10  
46             split m{ , }mx, lc $lang ];
47             };
48              
49             my $_build_localiser = sub {
50             return sub {
51 8     8   76 my ($key, $args) = @_;
52              
53 8 50 50     15 defined $key or return; $key = "${key}"; chomp $key; $args //= {};
  8         9  
  8         9  
  8         12  
54              
55 8         7 my $text = $key;
56              
57 8 100 66     33 if (defined $args->{params} and ref $args->{params} eq 'ARRAY') {
58 7 100       19 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         11 @{ $args->{params} };
  6         20  
64             }
65              
66 1 50       4 0 > index $text, '{' and return $text;
67              
68             # Expand named parameters of the form {param_name}
69 1         1 my %args = %{ $args }; my $re = join '|', map { quotemeta $_ } keys %args;
  1         5  
  1         2  
  4         7  
70              
71 1 50       43 $text =~ s{ \{($re)\} }{ defined $args{$1} ? $args{$1} : "{${1}?}" }egmx;
  1         5  
72              
73 1         7 return $text;
74 4     4   84 };
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   372 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 12 my ($self, $key, @args) = @_; my $args = $_localise_args->( $self, @args );
  7         12  
126              
127 7   33     125 $args->{locale} //= $self->locale;
128              
129 7         220 return $self->localiser->( $key, $args );
130             }
131              
132             sub loc_default {
133 1     1 1 3 my ($self, $key, @args) = @_; my $args = $_localise_args->( $self, @args );
  1         48  
134              
135 1         15 $args->{locale} = $self->_config->locale;
136              
137 1         22 return $self->localiser->( $key, $args );
138             }
139              
140             package Web::ComposableRequest::Role::L10N::Config;
141              
142 1     1   1052 use namespace::autoclean;
  1         3  
  1         3  
143              
144 1     1   44 use Web::ComposableRequest::Constants qw( LANG TRUE );
  1         2  
  1         3  
145 1         3 use Unexpected::Types qw( ArrayRef Bool HashRef
146 1     1   188 NonEmptySimpleStr );
  1         1  
147 1     1   510 use Moo::Role;
  1         1  
  1         4  
148              
149             # Public attributes
150             has 'l10n_attributes' => is => 'ro', isa => HashRef,
151 2     2   146 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   142 builder => sub { [ LANG ] };
158              
159             has 'quote_bind_values' => is => 'ro', isa => Bool, default => TRUE;
160              
161             1;
162              
163             __END__