/usr/share/perl5/Jifty/I18N.pm is in libjifty-perl 1.10518+dfsg-3ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | use strict;
use warnings;
package Jifty::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon ();
use Email::MIME::ContentType;
use Encode::Guess qw(iso-8859-1);
use Jifty::Util;
=head1 NAME
Jifty::I18N - Internationalization framework for Jifty
=head1 SYNOPSIS
# Whenever you need an internationalized string:
print _('Hello, %1!', 'World');
In your Mason templates:
<% _('Hello, %1!', 'World') %>
=head1 METHODS
=head2 C<_>
This module provides a method named C<_>, which allows you to quickly and easily include localized strings in your application. The first argument is the string to be translated. If that string contains placeholders, the remaining arguments are used to replace the placeholders. The placeholders in the form of "%1" where the number is the number of the argument used to replace it:
_('Welcome %1 to the %2', 'Bob', 'World');
This example would return the string "Welcome Bob to the World" if no translation is being performed.
=cut
=head2 new
Set up Jifty's internationalization for your application. This pulls
in Jifty's PO files, your PO files and then exports the _ function into
the wider world.
=cut
my $DynamicLH;
our $loaded;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
# XXX: this requires a full review, LML->get_handle is calling new
# on I18N::lang each time, but we really shouldn't need to rerun
# the import here.
return $self if $loaded;
my @import = map {( Gettext => $_ )} _get_file_patterns();
++$loaded;
Locale::Maketext::Lexicon->import(
{ '*' => \@import,
_decode => 1,
_auto => 1,
_style => 'gettext',
}
);
# Allow hard-coded languages in the config file
my $lang = Jifty->config->framework('L10N')->{'Lang'};
$lang = [defined $lang ? $lang : ()] unless ref($lang) eq 'ARRAY';
# Allow hard-coded allowed-languages in the config file
my $allowed_lang = Jifty->config->framework('L10N')->{'AllowedLang'};
$allowed_lang = [defined $allowed_lang ? $allowed_lang : ()] unless ref($allowed_lang) eq 'ARRAY';
if (@$allowed_lang) {
my $allowed_regex = join '|', map {
my $it = $_;
$it =~ tr<-A-Z><_a-z>; # lc, and turn - to _
$it =~ tr<_a-z0-9><>cd; # remove all but a-z0-9_
$it;
} @$allowed_lang;
foreach my $lang ($self->available_languages) {
# "AllowedLang: zh" should let both zh_tw and zh_cn survive,
# so we just check ^ but not $.
$lang =~ /^$allowed_regex/ or delete $Jifty::I18N::{$lang.'::'};
}
}
my $lh = $class->get_handle(@$lang);
$DynamicLH = \$lh unless @$lang;
$self->init;
__PACKAGE__->install_global_loc($DynamicLH);
return $self;
}
=head2 install_global_loc
=cut
sub install_global_loc {
my ($class, $dlh) = @_;
my $loc_method = sub {
# Retain compatibility with people using "-e _" etc.
return \*_ unless @_; # Needed for perl 5.8
# When $_[0] is undef, return undef. When it is '', return ''.
no warnings 'uninitialized';
return $_[0] unless (length $_[0]);
local $@;
# Force stringification to stop Locale::Maketext from choking on
# things like DateTime objects.
my @stringified_args = map {"$_"} @_;
my $result = eval { ${$dlh}->maketext(@stringified_args) };
if ($@) {
warn $@;
# Sometimes Locale::Maketext fails to localize a string and throws
# an exception instead. In that case, we just return the input.
return join(' ', @stringified_args);
}
return $result;
};
{
no strict 'refs';
no warnings 'redefine';
*_ = $loc_method;
}
}
=head2 available_languages
Return an array of available languages
=cut
sub available_languages {
return map { /^(\w+)::/ ? $1 : () } sort keys %Jifty::I18N::;
}
=head2 _get_file_patterns
Get list of patterns for all PO files in the project.
(Paths are gotten from the configuration variables and plugins).
=cut
sub _get_file_patterns {
my @ret;
push(@ret, Jifty->config->framework('L10N')->{'PoDir'});
push(@ret, Jifty->config->framework('L10N')->{'DefaultPoDir'});
# Convert relative paths to absolute ones
@ret = map { Jifty::Util->absolute_path($_) } @ret;
foreach my $plugin (Jifty->plugins) {
my $dir = $plugin->po_root;
next unless ($dir and -d $dir and -r $dir );
push @ret, $dir ;
}
# Unique-ify paths
my %seen;
@ret = grep {not $seen{$_}++} @ret;
return ( map { $_ . '/*.po' } @ret );
}
=head2 get_language_handle
Get the language handle for this request.
=cut
sub get_language_handle {
# XXX: subrequest should not need to get_handle again.
my $self = shift;
# optional argument makes it easy to disable I18N
# while comparing test strings (without loading session)
my $lang = shift || Jifty->web->session->get('jifty_lang');
if ( !$lang
&& Jifty->web->current_user
&& Jifty->web->current_user->id )
{
my $user = Jifty->web->current_user->user_object;
for my $column (qw/language lang/) {
if ( $user->can($column) ) {
$lang = $user->$column;
last;
}
}
}
# I18N::LangTags::Detect wants these for detecting
local $ENV{REQUEST_METHOD} = Jifty->web->request->method
if Jifty->web->request;
local $ENV{HTTP_ACCEPT_LANGUAGE} = Jifty->web->request->header("Accept-Language") || ""
if Jifty->web->request;
$$DynamicLH = $self->get_handle($lang ? $lang : ()) if $DynamicLH;
}
=head2 get_current_language
Get the current language for this request, formatted as a Locale::Maketext
subclass string (i.e., C<zh_tw> instead of C<zh-TW>).
=cut
sub get_current_language {
return unless $DynamicLH;
my ($lang) = ref($$DynamicLH) =~ m/::(\w+)$/;
return $lang;
}
=head2 refresh
Used by L<Jifty::Handler> in DevelMode to reload F<.po> files whenever they
are modified on disk.
=cut
my $LAST_MODIFED = '';
sub refresh {
if ( Jifty->config->framework('L10N')->{'Disable'} && !$loaded) {
# skip loading po, but still do the translation for maketext
require Locale::Maketext::Lexicon;
my $lh = __PACKAGE__->get_handle;
my $orig = Jifty::I18N::en->can('maketext');
no warnings 'redefine';
*Jifty::I18N::en::maketext = Locale::Maketext::Lexicon->_style_gettext($orig);
__PACKAGE__->install_global_loc(\$lh);
++$loaded;
return;
}
my $modified = join(
',',
# sort map { $_ => -M $_ } map { glob("$_/*.po") } ( Jifty->config->framework('L10N')->{'PoDir'}, Jifty->config->framework('L10N')->{'DefaultPoDir'}
sort map { $_ => -M $_ } map { glob($_) } _get_file_patterns()
);
if ($modified ne $LAST_MODIFED) {
Jifty::I18N->new;
$LAST_MODIFED = $modified;
}
}
=head2 promote_encoding STRING [CONTENT-TYPE]
Return STRING promoted to our best-guess of an appropriate
encoding. STRING should B<not> have the UTF-8 flag set when passed in.
Optionally, you can pass a MIME content-type string as a second
argument. If it contains a charset= parameter, we will use that
encoding. Failing that, we use Encode::Guess to guess between UTF-8
and iso-latin-1. If that fails, and the string validates as UTF-8, we
assume that. Finally, we fall back on returning the string as is.
=cut
# XXX TODO This possibly needs to be more clever and/or configurable
sub promote_encoding {
my $class = shift;
my $string = shift;
my $content_type = shift;
my $charset;
# Don't bother parsing the Content-Type header unless it mentions "charset".
# This is to avoid the "Unquoted / not allowed in Content-Type" warnings when
# the Base64-encoded MIME boundary string contains "/".
if ($content_type and $content_type =~ /charset/i) {
$content_type = Email::MIME::ContentType::parse_content_type($content_type);
$charset = $content_type->{attributes}->{charset};
}
# XXX TODO Is this the right thing? Maybe we should just return
# the string as-is.
Encode::_utf8_off($string);
if($charset) {
$string = Encode::decode($charset, $string);
} else {
my $encoding = Encode::Guess->guess($string);
if(!ref($encoding)) {
local $@;
eval {
# Try utf8
$string = Encode::decode_utf8($string, 1);
};
if($@) {
warn "Unknown encoding -- none specified, couldn't guess, not valid UTF-8";
}
} else {
$string = $encoding->decode($string) if $encoding;
}
}
return $string;
}
=head2 maybe_decode_utf8 STRING
Attempt to decode STRING as UTF-8. If STRING is not valid UTF-8, or
already contains wide characters, return it undecoded.
N.B: In an ideal world, we wouldn't need this function, since we would
know whether any given piece of input is UTF-8. However, the world is
not ideal.
=cut
sub maybe_decode_utf8 {
my $class = shift;
my $string = shift;
local $@;
eval {
$string = Encode::decode_utf8($string);
};
Carp::carp "Couldn't decode UTF-8: $@" if $@;
return $string;
}
package Jifty::I18N::en;
use base 'Locale::Maketext';
our %Lexicon = ( _fallback => 1, _AUTO => 1 );
1;
|