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 )