Apache2-API
view release on metacpan or search on metacpan
lib/Apache2/API/Headers/AcceptLanguage.pm view on Meta::CPAN
token => $token,
# The Locale::Unicode object
locale => $locale,
locale_lc => $locale_lc,
language_lc => lc( $lang ),
quality => $q + 0,
});
if( !exists( $best_q_for{ $locale_lc } ) || $q > $best_q_for{ $locale_lc } )
{
$best_q_for{ $locale_lc } = $q;
}
}
# Keep only the records that have the best 'q' for their exact tag.
@$elements = grep{
my $keep = 0;
if( exists( $best_q_for{ $_->{locale_lc} } ) )
{
$keep = ( $best_q_for{ $_->{locale_lc} } == $_->{quality} ) ? 1 : 0;
delete( $best_q_for{ $_->{locale_lc} } ) if( $keep );
}
$keep;
} @$elements;
return( $elements );
}
sub _partial_match
{
my( $self, $their, $our ) = @_;
# ex: "en" (language part of the locale) matches "en-GB"
return( ( $their->{language_lc} eq $our->{language_lc} ) ? 1 : 0 );
}
# For the locales, we do not use a granularity of numbered specificity;
# we return 2 for full, and 1 for partial in order to remain coherent with Accept.
sub _specificity
{
my( $self, $their, $our ) = @_;
return(2) if( $self->_full_match( $their, $our ) );
return(1) if( $self->_partial_match( $their, $our ) );
return(0);
}
1;
# NOTE: POD
__END__
=encoding utf-8
=head1 NAME
Apache2::API::Headers::AcceptLanguage - Parser and matcher for HTTP Accept-Language header
=head1 SYNOPSIS
use Apache2::API::Headers::AcceptLanguage;
my $al = Apache2::API::Headers::AcceptLanguage->new( 'fr-FR;q=0.9,en;q=0.8' );
my $locale = $al->match( ['en', 'fr-FR'] ); # => 'fr-FR'
my $prefs = $al->prefs; # => ['fr-FR', 'en']
=head1 DESCRIPTION
Parses HTTP C<Accept-Language> header and provides the L<Apache2::API::Headers::AcceptCommon/match> method to match against supported locales (languages).
Full tag matches (e.g. C<fr-FR>) trump primary-language matches (e.g. C<fr> matching C<fr-CA>), with quality values (C<q>) per RFC 7231 and RFC 9110. Language/locale parsing is done with L<Locale::Unicode>.
It inherits from L<Apache2::API::Headers::AcceptCommon>.
The algorithm is as follows:
=over 4
=item * Exact C<locale> match beats primary language match at the same C<q>. For example, C<fr-CA> beats C<fr>
=item * Primary language tokens, such as C<en>, can match more specific locales, such as C<en-GB>.
=item * C<*> wildcard is a low-specificity fallback and never outranks an equal-C<q> specific match.
=item * Duplicates keep highest C<q>; C<q=0> excludes a tag.
=back
=head1 CONSTRUCTOR
=head2 new( $header )
Creates a new instance with the given C<Accept-Language> header string, and returns it.
If an error occurred, it sets an error that can be retrieved with the L<error method|Module::Generic/error>, and it returns C<undef> in scalar context, or an empty list in list context.
=head1 METHODS
=head2 languages
As per BCP47, and Unicode CLDR, a C<language> is just a 2 to 3-characters code ths is possibly part of a L<locale|Locale::Unicode>. Yet, this method is defined here for convenience.
This is an alias to L</preferences>
=head2 locales
This is an alias to L</preferences>
=head2 match( \@supported_locales )
Returns the best matching L<locale|Locale::Unicode> from the provided list of supported locales.
It returns an empty string if nothing matched, or sets an L<error|Module::Generic/error> and returns C<undef> in scalar context, or returns an empty list in list context.
=head2 preferences
Read-only.
Returns an array reference of locales, submitted by the user C<Accept-Language> header in his HTTP request, sorted by decreasing quality, with duplicates removed (keeping highest C<q>).
If an error occurred, it sets an error that can be retrieved with the L<error method|Module::Generic/error>, and it returns C<undef> in scalar context, or an empty list in list context.
=head1 EXAMPLES
=head2 1. Exact beats primary language at same q
( run in 0.459 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )