← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:26:52 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/I18N.pm
StatementsExecuted 28 statements in 3.63ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
111804µs927µsFoswiki::I18N::::newFoswiki::I18N::new
11129µs42µsFoswiki::I18N::::BEGIN@13Foswiki::I18N::BEGIN@13
11123µs81µsFoswiki::I18N::::BEGIN@15Foswiki::I18N::BEGIN@15
11119µs38µsFoswiki::I18N::::BEGIN@14Foswiki::I18N::BEGIN@14
11114µs14µsFoswiki::I18N::::finishFoswiki::I18N::finish
11113µs13µsFoswiki::I18N::::BEGIN@58Foswiki::I18N::BEGIN@58
0000s0sFoswiki::I18N::::__ANON__[:200]Foswiki::I18N::__ANON__[:200]
0000s0sFoswiki::I18N::::_add_languageFoswiki::I18N::_add_language
0000s0sFoswiki::I18N::::_discover_languagesFoswiki::I18N::_discover_languages
0000s0sFoswiki::I18N::::_normalize_language_tagFoswiki::I18N::_normalize_language_tag
0000s0sFoswiki::I18N::::available_languagesFoswiki::I18N::available_languages
0000s0sFoswiki::I18N::::enabled_languagesFoswiki::I18N::enabled_languages
0000s0sFoswiki::I18N::::fromSiteCharSetFoswiki::I18N::fromSiteCharSet
0000s0sFoswiki::I18N::::languageFoswiki::I18N::language
0000s0sFoswiki::I18N::::maketextFoswiki::I18N::maketext
0000s0sFoswiki::I18N::::toSiteCharSetFoswiki::I18N::toSiteCharSet
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::I18N
6
7Support for strings translation and language detection.
8
9=cut
10
11package Foswiki::I18N;
12
13252µs255µs
# spent 42µs (29+13) within Foswiki::I18N::BEGIN@13 which was called: # once (29µs+13µs) by Foswiki::i18n at line 13
use strict;
# spent 42µs making 1 call to Foswiki::I18N::BEGIN@13 # spent 13µs making 1 call to strict::import
14256µs258µs
# spent 38µs (19+19) within Foswiki::I18N::BEGIN@14 which was called: # once (19µs+19µs) by Foswiki::i18n at line 14
use warnings;
# spent 38µs making 1 call to Foswiki::I18N::BEGIN@14 # spent 20µs making 1 call to warnings::import
152867µs2139µs
# spent 81µs (23+58) within Foswiki::I18N::BEGIN@15 which was called: # once (23µs+58µs) by Foswiki::i18n at line 15
use Assert;
# spent 81µs making 1 call to Foswiki::I18N::BEGIN@15 # spent 58µs making 1 call to Assert::import
16
1711µsour $initialised;
1812µsour @initErrors;
19
20=begin TML
21
22---++ ClassMethod available_languages
23
24Lists languages tags for languages available at Foswiki installation. Returns a
25list containing the tags of the available languages.
26
27__Note__: the languages available to users are determined in the =configure=
28interface.
29
30=cut
31
32sub available_languages {
33
34 my @available;
35
36 while ( my ( $langCode, $langOptions ) =
37 each %{ $Foswiki::cfg{Languages} } )
38 {
39 if ( $langOptions->{Enabled} ) {
40 push( @available, _normalize_language_tag($langCode) );
41 }
42 }
43
44 return @available;
45}
46
47# utility function: normalize language tags like ab_CD to ab-cd
48# also renove any character there is not a letter [a-z] or a hyphen.
49sub _normalize_language_tag {
50 my $tag = shift;
51 $tag = lc( $tag || '' );
52 $tag =~ s/\_/-/g;
53 $tag =~ s/[^a-z-]//g;
54 return $tag;
55}
56
57# initialisation block
58
# spent 13µs within Foswiki::I18N::BEGIN@58 which was called: # once (13µs+0s) by Foswiki::i18n at line 129
BEGIN {
59
60 # we only need to proceed if user wants internationalisation support
61113µs return unless $Foswiki::cfg{UserInterfaceInternationalisation};
62
63 # no languages enabled is the same as disabling
64 # {UserInterfaceInternationalisation}
65 my @languages = available_languages();
66 return unless ( scalar(@languages) );
67
68 # we first assume it's ok
69 $initialised = 1;
70
71 eval "use Locale::Maketext ()";
72 if ($@) {
73 $initialised = 0;
74 push( @initErrors,
75 "I18N: Couldn't load required perl module Locale::Maketext: "
76 . $@
77 . "\nInstall the module or turn off {UserInterfaceInternationalisation}"
78 );
79 }
80 else {
81 @Foswiki::I18N::ISA = ('Locale::Maketext');
82 }
83
84 unless ( $Foswiki::cfg{LocalesDir} && -e $Foswiki::cfg{LocalesDir} ) {
85 push( @initErrors,
86'I18N: {LocalesDir} not configured. Define it or turn off {UserInterfaceInternationalisation}'
87 );
88 $initialised = 0;
89 }
90
91 # dynamically build languages to be loaded according to admin-enabled
92 # languages.
93 eval "use Locale::Maketext::Lexicon{ en => [ 'Auto' ] } ;";
94 if ($@) {
95 $initialised = 0;
96 push( @initErrors,
97 "I18N - Couldn't load default English messages: $@\n"
98 . "Install Locale::Maketext::Lexicon or turn off {UserInterfaceInternationalisation}"
99 );
100 }
101 foreach my $lang (@languages) {
102 my $langFile = "$Foswiki::cfg{LocalesDir}/$lang.po";
103
104 # Use the compressed version if it exists
105 if ( $langFile =~ m/^(.*)\.po$/
106 && -f "$1.mo" )
107 {
108 $langFile = "$1.mo";
109 }
110 if ( -f $langFile ) {
111 unless (
112 eval {
113 Locale::Maketext::Lexicon->import(
114 { $lang => [ Gettext => $langFile ] } );
115 1;
116 }
117 )
118 {
119 push( @initErrors,
120 "I18N - Error loading language $lang: $@\n" );
121 }
122 }
123 else {
124 push( @initErrors,
125"I18N - Ignoring enabled language $lang as $langFile does not exist.\n"
126 );
127 }
128 }
12912.42ms113µs}
# spent 13µs making 1 call to Foswiki::I18N::BEGIN@58
130
131=begin TML
132
133---++ ClassMethod new ( $session )
134
135Constructor. Gets the language object corresponding to the current users
136language. If $session is not a Foswiki object reference, just calls
137Local::Maketext::new (the superclass constructor)
138
139=cut
140
141
# spent 927µs (804+123) within Foswiki::I18N::new which was called: # once (804µs+123µs) by Foswiki::i18n at line 1973 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm
sub new {
1421151µs my $class = shift;
143 my ($session) = @_;
144
14514µs unless ( ref($session) && $session->isa('Foswiki') ) {
# spent 4µs making 1 call to UNIVERSAL::isa
146
147 # it's recursive
148 return $class->SUPER::new(@_);
149 }
150
151 if (@initErrors) {
152 foreach my $error (@initErrors) {
153 $session->logger->log( $initialised ? 'warning' : 'error', $error );
154 }
155 }
156
157 # guesses the language from the CGI environment
158 # TODO:
159 # web/user/session setting must override the language detected from the
160 # browser.
161 my $this;
1623138µs if ($initialised) {
163 $session->enterContext('i18n_enabled');
164 my $userLanguage = _normalize_language_tag(
165 $session->{prefs}->getPreference('LANGUAGE') );
166 if ($userLanguage) {
167 $this = Foswiki::I18N->get_handle($userLanguage);
168 }
169 else {
170 $this = Foswiki::I18N->get_handle();
171 }
172 }
173 else {
174 require Foswiki::I18N::Fallback;
175
176119µs $this = new Foswiki::I18N::Fallback();
# spent 19µs making 1 call to Foswiki::I18N::Fallback::new
177
178 # we couldn't initialise 'optional' I18N infrastructure, warn that we
179 # can only use English if I18N has been requested with configure
180 $session->logger->log( 'warning',
181 'Could not load I18N infrastructure; falling back to English' )
182 if $Foswiki::cfg{UserInterfaceInternationalisation};
183 }
184
185 # keep a reference to the session object
186 $this->{session} = $session;
187
188 # languages we know about
189 $this->{enabled_languages} = { en => 'English' };
190 $this->{checked_enabled} = undef;
191
192 # what to do with failed translations (only needed when already initialised
193 # and language is not English);
194 if ( $initialised and ( $this->language ne 'en' ) ) {
195 my $fallback_handle = Foswiki::I18N->get_handle('en');
196 $this->fail_with(
197 sub {
198 shift; # get rid of the handle
199 return $fallback_handle->maketext(@_);
200 }
201 );
202 }
203
204 # finally! :-p
205 return $this;
206}
207
208=begin TML
209
210---++ ObjectMethod finish()
211Break circular references.
212
213=cut
214
215# Note to developers; please undef *all* fields in the object explicitly,
216# whether they are references or not. That way this method is "golden
217# documentation" of the live fields in the object.
218
# spent 14µs within Foswiki::I18N::finish which was called: # once (14µs+0s) by Foswiki::I18N::Fallback::finish at line 24 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/I18N/Fallback.pm
sub finish {
219419µs my $this = shift;
220 undef $this->{enabled_languages};
221 undef $this->{checked_enabled};
222 undef $this->{session};
223}
224
225=begin TML
226
227---++ ObjectMethod maketext( $text ) -> $translation
228
229Translates the given string (assumed to be written in English) into the
230current language, as detected in the constructor, and converts it into
231the site charset.
232
233Wraps around Locale::Maketext's maketext method, adding charset conversion and checking
234
235Return value: translated string, or the argument itself if no translation is
236found for thet argument.
237
238=cut
239
240sub maketext {
241 my ( $this, $text, @args ) = @_;
242
243 # these can be user-supplied data. They can be in {Site}{CharSet}. Convert
244 # into "internal representation" as expected by Foswiki::I18N::maketext
245 @args = map { $this->fromSiteCharSet($_) } @args;
246
247 if ( $text =~ /^_/ && $text ne '_language_name' ) {
248 require CGI;
249 import CGI();
250
251 return CGI::span(
252 { -class => 'foswikiAlert' },
253 "Error: MAKETEXT argument's can't start with an underscore (\"_\")."
254 );
255 }
256
257 my $result = $this->SUPER::maketext( $text, @args );
258 if ( $result && $this->{session} ) {
259
260 # external calls get the resultant text in the right charset:
261 $result = $this->toSiteCharSet($result);
262 }
263
264 return $result;
265}
266
267=begin TML
268
269---++ ObjectMethod language() -> $language_tag
270
271Indicates the language tag of the current user's language, as detected from the
272information sent by the browser. Returns the empty string if the language
273could not be determined.
274
275=cut
276
277sub language {
278 my $this = shift;
279
280 return $this->language_tag();
281}
282
283=begin TML
284
285---++ ObjectMethod enabled_languages() -> %languages
286
287Returns an array with language tags as keys and language (native) names as
288values, for all the languages enabled in this site. Useful for
289listing available languages to the user.
290
291=cut
292
293sub enabled_languages {
294 my $this = shift;
295
296 unless ( $this->{checked_enabled} ) {
297 _discover_languages($this);
298 }
299
300 $this->{checked_enabled} = 1;
301 return $this->{enabled_languages};
302
303}
304
305# discovers the available language.
306sub _discover_languages {
307 my $this = shift;
308
309 #use the cache, if available
310 if ( open LANGUAGE, '<', "$Foswiki::cfg{WorkingDir}/languages.cache" ) {
311 foreach my $line (<LANGUAGE>) {
312 my ( $key, $name ) = split( '=', $line );
313
314 # Filter on enabled languages
315 next
316 unless ( $Foswiki::cfg{Languages}{$key}
317 && $Foswiki::cfg{Languages}{$key}{Enabled} );
318 chop($name);
319 _add_language( $this, $key, $name );
320 }
321 }
322 else {
323
324 # Rebuild the cache, filtering on enabled languages.
325 open LANGUAGE, '>', "$Foswiki::cfg{WorkingDir}/languages.cache";
326 foreach my $tag ( available_languages() ) {
327 my $h = Foswiki::I18N->get_handle($tag);
328 my $name = eval { $h->maketext("_language_name") } or next;
329 $name = $this->toSiteCharSet($name);
330 print LANGUAGE "$tag=$name\n";
331
332 # Filter on enabled languages
333 next
334 unless ( $Foswiki::cfg{Languages}{$tag}
335 && $Foswiki::cfg{Languages}{$tag}{Enabled} );
336 _add_language( $this, $tag, $name );
337 }
338 }
339
340 close LANGUAGE;
341 $this->{checked_enabled} = 1;
342
343}
344
345=begin TML
346
347---++ ObjectMethod fromSiteCharSet ( $text ) -> $encoded
348
349This method receives =$text=, assumed to be encoded in {Site}{CharSet}, and
350converts it to a internal representation.
351
352Currently this representation will be a UTF-8 string, but this may change in
353the future. This way, you can't assume any property on the returned value, and
354should only use the returned value of this function as input to toSiteCharSet.
355If you change the returnd value, either by removing, updating or appending
356characters, be sure to touch only ASCII characters (i.e., characters that have
357ord() less than 128).
358
359=cut
360
361sub fromSiteCharSet {
362 my ( $this, $text ) = @_;
363
364 return $text
365 if ( !defined $Foswiki::cfg{Site}{CharSet}
366 || $Foswiki::cfg{Site}{CharSet} =~ m/^utf-?8$/i );
367
368 if ( $] < 5.008 ) {
369
370 # use Unicode::MapUTF8 for Perl older than 5.8
371 require Unicode::MapUTF8;
372 my $encoding = $Foswiki::cfg{Site}{CharSet};
373 if ( Unicode::MapUTF8::utf8_supported_charset($encoding) ) {
374 return Unicode::MapUTF8::to_utf8(
375 {
376 -string => $text,
377 -charset => $encoding
378 }
379 );
380 }
381 else {
382 $this->{session}->logger->log( 'warning',
383 'Conversion from $encoding no supported, '
384 . 'or name not recognised - check perldoc Unicode::MapUTF8' );
385 return $text;
386 }
387 }
388 else {
389
390 # good Perl version, just use Encode
391 require Encode;
392 import Encode;
393 my $encoding = Encode::resolve_alias( $Foswiki::cfg{Site}{CharSet} );
394 if ( not $encoding ) {
395 $this->{session}->logger->log( 'warning',
396 'Conversion to "'
397 . $Foswiki::cfg{Site}{CharSet}
398 . '" not supported, or name not recognised - check '
399 . '"perldoc Encode::Supported"' );
400 return;
401 }
402 else {
403 my $octets =
404 Encode::decode( $encoding, $text, &Encode::FB_PERLQQ() );
405 return Encode::encode( 'utf-8', $octets );
406 }
407 }
408}
409
410=begin TML
411
412
413---++ ObjectMethod toSiteCharSet ( $encoded ) -> $text
414
415This method receives a string, assumed to be encoded in Foswiki's internal string
416representation (as generated by the fromSiteCharSet method, and converts it
417into {Site}{CharSet}.
418
419When converting into {Site}{CharSet}, characters that are not present at that
420charset are represented as HTML numerical character entities (NCR's), in the
421format <code>&amp;#NNNN;</code>, where NNNN is the character's Unicode
422codepoint.
423
424See also: the =fromSiteCharSet= method.
425
426=cut
427
428sub toSiteCharSet {
429 my ( $this, $encoded ) = @_;
430
431 return $encoded
432 if ( !defined $Foswiki::cfg{Site}{CharSet}
433 || $Foswiki::cfg{Site}{CharSet} =~ m/^utf-?8$/i );
434
435 if ( $] < 5.008 ) {
436
437 # use Unicode::MapUTF8 for Perl older than 5.8
438 require Unicode::MapUTF8;
439 my $encoding = $Foswiki::cfg{Site}{CharSet};
440 if ( Unicode::MapUTF8::utf8_supported_charset($encoding) ) {
441 return Unicode::MapUTF8::from_utf8(
442 {
443 -string => $encoded,
444 -charset => $encoding
445 }
446 );
447 }
448 else {
449 $this->{session}->logger->log( 'warning',
450 'Conversion to $encoding no supported, '
451 . 'or name not recognised - check perldoc Unicode::MapUTF8' );
452 return $encoded;
453 }
454 }
455 else {
456 require Encode;
457 import Encode;
458 my $encoding = Encode::resolve_alias( $Foswiki::cfg{Site}{CharSet} );
459 if ( not $encoding ) {
460 $this->{session}->logger->log( 'warning',
461 'Conversion from "'
462 . $Foswiki::cfg{Site}{CharSet}
463 . '" not supported, or name not recognised - check '
464 . '"perldoc Encode::Supported"' );
465 return $encoded;
466 }
467 else {
468
469 # converts to {Site}{CharSet}, generating HTML NCR's when needed
470 my $octets = Encode::decode( 'utf-8', $encoded );
471 return Encode::encode( $encoding, $octets, &Encode::FB_HTMLCREF() );
472 }
473 }
474}
475
476# private utility method: add a pair tag/language name
477sub _add_language {
478 my ( $this, $tag, $name ) = @_;
479 $this->{enabled_languages}->{$tag} = $name;
480}
481
48217µs1;
483__END__