NAME

Apache2::API::Headers::AcceptLanguage - Parser and matcher for HTTP Accept-Language header

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']

DESCRIPTION

Parses HTTP Accept-Language header and provides the "match" in Apache2::API::Headers::AcceptCommon method to match against supported locales (languages).

Full tag matches (e.g. fr-FR) trump primary-language matches (e.g. fr matching fr-CA), with quality values (q) per RFC 7231 and RFC 9110. Language/locale parsing is done with Locale::Unicode.

It inherits from Apache2::API::Headers::AcceptCommon.

The algorithm is as follows:

  • Exact locale match beats primary language match at the same q. For example, fr-CA beats fr

  • Primary language tokens, such as en, can match more specific locales, such as en-GB.

  • * wildcard is a low-specificity fallback and never outranks an equal-q specific match.

  • Duplicates keep highest q; q=0 excludes a tag.

CONSTRUCTOR

new( $header )

Creates a new instance with the given Accept-Language header string, and returns it.

If an error occurred, it sets an error that can be retrieved with the error method, and it returns undef in scalar context, or an empty list in list context.

METHODS

languages

As per BCP47, and Unicode CLDR, a language is just a 2 to 3-characters code ths is possibly part of a locale. Yet, this method is defined here for convenience.

This is an alias to "preferences"

locales

This is an alias to "preferences"

match( \@supported_locales )

Returns the best matching locale from the provided list of supported locales.

It returns an empty string if nothing matched, or sets an error and returns undef in scalar context, or returns an empty list in list context.

preferences

Read-only.

Returns an array reference of locales, submitted by the user Accept-Language header in his HTTP request, sorted by decreasing quality, with duplicates removed (keeping highest q).

If an error occurred, it sets an error that can be retrieved with the error method, and it returns undef in scalar context, or an empty list in list context.

EXAMPLES

1. Exact beats primary language at same q

my $al = Apache2::API::Headers::AcceptLanguage->new('fr-FR;q=0.9, fr;q=0.9');
$al->match([ 'fr-CA', 'fr-FR' ]);
# "fr-FR"

2. Primary language matches a more specific server locale

my $al = Apache2::API::Headers::AcceptLanguage->new('en;q=0.9, fr;q=0.8');
$al->match([ 'fr-FR', 'en-GB' ]);
# "en-GB"

3. Wildcard at higher q picks first supported

my $al = Apache2::API::Headers::AcceptLanguage->new('*;q=1.0, en;q=0.9');
$al->match([ 'fr-FR', 'en-GB' ]);
# "fr-FR"

LEGACY MATCH PRIORITY

Set $Apache2::API::Headers::AcceptLanguage::MATCH_PRIORITY_0_01_STYLE to true to apply “offer order” tie-breaking within equal-q buckets (see "MATCH PRIORITY MODE" in Apache2::API::Headers::AcceptCommon for details).

NOTES ON TAGS

Tags are parsed using Locale::Unicode. Invalid tags are discarded. For robust behavior, pass your supported locales in the same syntax you intend to serve, such as en, en-GB, ja-JP.

PERFORMANCE

The matchers called with "match" in Apache2::API::Headers::AcceptCommon loops through the array reference of supported locales times the number of parsed acceptable locales as submitted by the client.

Typical HTTP Accept-Language headers are small, so the performance should be very good.

"preferences" in Apache2::API::Headers::AcceptCommon and sorted results are cached per object.

CREDITS

Based on HTTP::AcceptLanguage by Kazuhiro Osawa

AUTHOR

Jacques Deguest <jack@deguest.jp>

SEE ALSO

Apache2::API::Headers::Accept, Apache2::API::Headers::AcceptCommon, Locale::Unicode, RFC 5646 (BCP 47), RFC 7231, RFC 9110.

Apache2::API::DateTime, Apache2::API::Query, Apache2::API::Request, Apache2::API::Request::Params, Apache2::API::Request::Upload, Apache2::API::Response, Apache2::API::Status

Apache2::Request, Apache2::RequestRec, Apache2::RequestUtil

COPYRIGHT & LICENSE

Copyright (c) 2023 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.