CGI-Lingua
NAME
CGI::Lingua - Create a multilingual web page
VERSION
Version 0.80
SYNOPSIS
CGI::Lingua is a powerful module for multilingual web applications offering extensive language/country detection strategies.
No longer does your website need to be in English only. CGI::Lingua provides a simple basis to determine which language to display a website. The website tells CGI::Lingua which languages it supports. Based on that list CGI::Lingua tells the application which language the user would like to use.
use CGI::Lingua;
# ...
my $l = CGI::Lingua->new(['en', 'fr', 'en-gb', 'en-us']);
my $language = $l->language();
if ($language eq 'English') {
print '<P>Hello</P>';
} elsif($language eq 'French') {
print '<P>Bonjour</P>';
} else { # $language eq 'Unknown'
my $rl = $l->requested_language();
print "<P>Sorry for now this page is not available in $rl.</P>";
}
my $c = $l->country();
if ($c eq 'us') {
# print contact details in the US
} elsif ($c eq 'ca') {
# print contact details in Canada
} else {
# print worldwide contact details
}
# ...
use CHI;
use CGI::Lingua;
# ...
my $cache = CHI->new(driver => 'File', root_dir => '/tmp/cache', namespace => 'CGI::Lingua-countries');
$l = CGI::Lingua->new({ supported => ['en', 'fr'], cache => $cache });
SUBROUTINES/METHODS
new
Creates a CGI::Lingua object.
API SPECIFICATION
Input:
supported => ArrayRef[Str] | Str # required; RFC-1766 language codes
cache => Object # optional; CHI-compatible (get/set)
config_file => Str # optional; YAML/XML/INI config path
logger => Object # optional; must implement warn/info/error
info => Object # optional; CGI::Info-compatible
data => Any # optional; forwarded to I18N::AcceptLanguage
dont_use_ip => Bool # optional; disable IP-based fallback
syslog => Bool | HashRef # optional; Sys::Syslog integration
debug => Bool # optional; enable debug logging
Returns: CGI::Lingua blessed hashref, or a clone when called on an object.
MESSAGES
"You must give a list of supported languages" - no 'supported' key provided
"List of supported languages must be an array ref" - supported is wrong ref type
"Supported languages must be the short code" - string too short or too long
"Logger must be a blessed object with warn/info/error methods" - bad logger arg
PSEUDOCODE
1. Normalise args via Params::Get and Object::Configure
2. Validate logger (must be blessed with warn/info/error) if provided
3. Validate supported (required, string or arrayref)
4. If cache and REMOTE_ADDR set, attempt to thaw a previously stored state
5. Bless and return fresh object with sentinel flags set to GEO_UNKNOWN
language
Tells the CGI application in what language to display its messages. The language is the natural name e.g. 'English' or 'Japanese'.
Sublanguages are handled sensibly, so that if a client requests U.S. English on a site that only serves British English, language() will return 'English'.
If none of the requested languages is included within the supported lists, language() returns 'Unknown'.
API SPECIFICATION
Input: none beyond $self
Returns: Str - human-readable language name, or 'Unknown'
preferred_language
Same as language().
name
Synonym for language, for compatibility with Locale::Object::Language.
sublanguage
Tells the CGI what variant to use e.g. 'United Kingdom', or undef if it can't be determined.
API SPECIFICATION
Input: none beyond $self
Returns: Str | undef
language_code_alpha2
Gives the two-character representation of the supported language, e.g. 'en' when you've asked for en-gb.
If none of the requested languages is included within the supported lists, language_code_alpha2() returns undef.
API SPECIFICATION
Input: none beyond $self
Returns: Str (2 chars) | undef
code_alpha2
Synonym for language_code_alpha2, kept for historical reasons.
sublanguage_code_alpha2
Gives the two-character representation of the supported language, e.g. 'gb' when you've asked for en-gb, or undef.
API SPECIFICATION
Input: none beyond $self
Returns: Str (2 chars) | undef
requested_language
Gives a human-readable rendition of what language the user asked for whether or not it is supported.
Returns the sublanguage (if appropriate) in parentheses, e.g. "English (United Kingdom)"
API SPECIFICATION
Input: none beyond $self
Returns: Str - e.g. "English (United Kingdom)" or "Unknown"
country
Returns the two-character country code of the remote end in lowercase.
If IP::Country, Geo::IPfree or Geo::IP is installed, CGI::Lingua will make use of that, otherwise, it will do a Whois lookup. If you do not have any of those installed I recommend you use the caching capability of CGI::Lingua.
API SPECIFICATION
Input: none beyond $self
Returns: Str (2 lowercase chars) | undef
'Unknown' is only returned in the Baidu-EU special case via _handle_eu_country.
MESSAGES
"GEOIP_COUNTRY_CODE contains an invalid country code; ignoring"
"HTTP_CF_IPCOUNTRY contains an invalid country code; ignoring"
"X.X.X.X isn't a valid IP address"
"Can't determine country from LAN connection X"
"Can't determine country from loopback connection X"
"cache contains a numeric country: N"
"IP matches to a numeric country"
locale
HTTP doesn't have a way of transmitting a browser's localisation information which would be useful for default currency, date formatting, etc.
This method attempts to detect the information, but it is a best guess and is not 100% reliable. But it's better than nothing ;-)
Returns a Locale::Object::Country object.
API SPECIFICATION
Input: none beyond $self
Returns: Locale::Object::Country | undef
time_zone
Returns the timezone of the web client.
If Geo::IP is installed, CGI::Lingua will make use of that, otherwise it will use ip-api.com
API SPECIFICATION
Input: none beyond $self
Returns: Str (IANA timezone name) | undef
MESSAGES
"Couldn't determine the timezone"
"LWP::Simple::WithCache and LWP::Simple are both absent; cannot contact ip-api.com"
Returns undef rather than croaking; install either LWP variant to enable ip-api lookups.
LIMITATIONS
-
Accept-Language left-to-right scan ignores q-values
The second and third pass in
_accept_language_match()scan the header left-to-right and ignore quality (q=0.x) values. A header such asde;q=0.9,en;q=0.1on a site that only supportsenwould currently fail to fall back to English. UseI18N::AcceptLanguagepasses only when possible. -
Logger must be a blessed object
The
loggerparameter is documented as accepting a code ref, array ref, or filename, but the current implementation calls$logger->$level()and will die on non-blessed values. Wrap alternative logger types in aLog::Abstractioninstance before passing them tonew(). -
es-419 sublanguage returns undef
Three-part regional codes such as
es-419(Latin American Spanish) do not resolve to asublanguage()value because ISO 3166-1 does not define '419'. This is a known limitation of the Locale::Object layer. -
Whois lookups are slow and unreliable
Without
IP::Country,Geo::IP, orGeo::IPfreeinstalled,country()falls back to Whois queries against live RIPE/ARIN/IANA servers. These can time out under load. Install at least one local geo-database module and enable the CHI cache to avoid this. -
Sub::Private not yet enforced
The
_*private methods are currently accessible from outside the package.Sub::Privateshould be added to enforce encapsulation once white-box tests are updated to call only the public API. -
IPv4-mapped IPv6 addresses are normalised to IPv4
REMOTE_ADDRvalues in the form::ffff:a.b.c.d(RFC 4291 section 2.5.5) are silently rewritten to the embeddeda.b.c.dIPv4 address before any geo-lookup. This is correct for country detection purposes but means the raw address string is not preserved in cache keys or log messages. -
EU country code is irresolvable (with one exception)
IP addresses that Whois reports as country
EUare mapped to'Unknown'unless they fall within Baidu's known subnet (RT-86809). There is no ISO 3166-1 country code for the European Union.
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
BUGS
Please report any bugs or feature requests to the author.
If HTTP_ACCEPT_LANGUAGE contains a sub-tag with a 3-digit UN M.49 region
code (e.g. es-419 for Latin American Spanish), sublanguage() returns
undef because ISO 3166-1 does not define numeric codes.
Please report any bugs or feature requests to bug-cgi-lingua at rt.cpan.org,
or through the web interface at
http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Lingua.
I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
Uses I18N::AcceptLanguage to find the highest priority accepted language. This means that if you support languages at a lower priority, it may be missed.
SEE ALSO
- Test Dashboard
- VWF - Versatile Web Framework https://github.com/nigelhorne/vwf
- HTTP::BrowserDetect
- I18N::AcceptLanguage
- Locale::Country
SUPPORT
This module is provided as-is without any warranty.
You can find documentation for this module with the perldoc command.
perldoc CGI::Lingua
You can also look for information at:
-
MetaCPAN
-
RT: CPAN's request tracker
-
CPANTS
-
CPAN Testers' Matrix
-
CPAN Testers Dependencies
FORMAL SPECIFICATION
new
new : Class × Params → CGI::Lingua
∀ p : Params • p.supported ≠ ∅ ⟹ result.language ∈ (p.supported ∪ {'Unknown'})
language
language : CGI::Lingua → Str
result ∈ {name(l) | l ∈ supported} ∪ {'Unknown'}
ACKNOWLEDGEMENTS
LICENSE AND COPYRIGHT
Copyright 2010-2026 Nigel Horne.
This program is released under the following licence: GPL2