version 1.5, 2003/09/17 23:16:27
|
version 1.35, 2005/11/10 19:19:51
|
Line 28
|
Line 28
|
###################################################################### |
###################################################################### |
###################################################################### |
###################################################################### |
|
|
|
=pod |
|
|
|
=head1 NAME |
|
|
|
Apache::lonlocal - provides localization services |
|
|
|
=head1 SYNOPSIS |
|
|
|
lonlocal provides localization services for LON-CAPA programmers based |
|
on Locale::Maketext. See |
|
C<http://search.cpan.org/dist/Locale-Maketext/lib/Locale/Maketext.pod> |
|
for more information on Maketext. |
|
|
|
=head1 OVERVIEWX<internationalization> |
|
|
|
As of LON-CAPA 1.1, we've started to localize LON-CAPA using the |
|
Locale::Maketext module. Internationalization is the bulk of the work |
|
right now (pre-1.1); localizing can be done anytime, and involves |
|
little or no programming. |
|
|
|
The internationalization process involves putting a wrapper around |
|
on-screen user messages and menus and turning them into keys, |
|
which the MaketextX<Maketext> library translates into the desired |
|
language output using a look-up table ("lexicon").X<lexicon> |
|
|
|
As keys we are currently using the plain English messages, and |
|
Maketext is configured to replace the message by its own key if no |
|
translation is found. This makes it easy to phase in the |
|
internationalization without disturbing the screen output. |
|
|
|
Internationalization is somewhat tedious and effectively impossible |
|
for a non-fluent speaker to perform, but is fairly easy to create |
|
translations, requiring no programming skill. As a result, this is one |
|
area where you can really help LON-CAPA out, even if you aren't a |
|
programmer, and we'd really appreciate it. |
|
|
|
=head1 How To Localize Handlers For Programmers |
|
|
|
Into the "use" section of a module, we need to insert |
|
|
|
use Apache::lonlocal; |
|
|
|
Note that there are B<no parentheses>, we B<want> to pollute our |
|
namespace. |
|
|
|
Inside might be something like this |
|
|
|
sub message { |
|
my $status=shift; |
|
my $message='Status unknown'; |
|
if ($status eq 'WON') { |
|
$message='You have won.'; |
|
} elsif ($status eq 'LOST') { |
|
$message='You are a total looser.'; |
|
} |
|
return $message; |
|
} |
|
... |
|
$r->print('<h3>Gamble your Homework Points</h3>'); |
|
... |
|
$r->print(<<ENDMSG); |
|
<font size="1">Rules:</font> |
|
<font size="0">No purchase necessary. Illegal where not allowed.</font> |
|
ENDMSG |
|
|
|
We have to now wrap the subroutine &mt()X<mt> ("maketext") around our |
|
messages, but not around markup, etc. We also want minimal disturbance. |
|
The first two examples are easy: |
|
|
|
sub message { |
|
my $status=shift; |
|
my $message='Status unknown'; |
|
if ($status eq 'WON') { |
|
$message='You have won.'; |
|
} elsif ($status eq 'LOST') { |
|
$message='You are a total looser.'; |
|
} |
|
return &mt($message); |
|
} |
|
... |
|
$r->print('<h3>'.&mt('Gamble your Homework Points').'</h3>'); |
|
|
|
The last one is a bummer, since you cannot call subroutines inside of |
|
(<<MARKER). I have written a little subroutine to generate a translated |
|
hash for that purpose: |
|
|
|
my %lt=&Apache::lonlocal::texthash('header' => 'Rules', 'disclaimer' => |
|
'No purchase necessary. Illegal where not allowed.'); |
|
$r->print(<<ENDMSG); |
|
<font size="1">$lt{'header'}:</font> |
|
<font size="0">$lt{'disclaimer'}</font> |
|
ENDMSG |
|
|
|
As a programmer, your job is done here. If everything worked, you |
|
should see no changes on the screen. |
|
|
|
=head1 How To Localize LON-CAPA for Translators |
|
|
|
As a translator, you need to provide the lexicon for the keys, which in |
|
this case is the plain text message. The lexicons sit in |
|
loncom/localize/localize, with the language code as filename, for |
|
example de.pm for the German translation. The file then simply looks |
|
like this: |
|
|
|
'You have won.' |
|
=> 'Sie haben gewonnen.', |
|
|
|
'You are a total looser.' |
|
=> 'Sie sind der totale Verlierer.', |
|
|
|
'Rules' |
|
=> 'Regeln', |
|
|
|
'No purchase necessary. Illegal where not allowed.' |
|
=> 'Es ist erlaubt, einfach zu verlieren, und das ist Ihre Schuld.' |
|
|
|
|
|
Comments may be added with the # symbol, which outside of a string |
|
(the things with the apostrophe surrounding them, which are the |
|
keys and translations) will cause the translation routines to |
|
ignore the rest of the line. |
|
|
|
This is a relatively easy task, and any help is appreciated. |
|
|
|
Maketext can do a whole lot more, see |
|
C<http://search.cpan.org/dist/Locale-Maketext/lib/Locale/Maketext.pod> |
|
but for most purposes, we do not have to mess with that. |
|
|
|
=cut |
|
|
package Apache::lonlocal; |
package Apache::lonlocal; |
|
|
use strict; |
use strict; |
|
use Apache::Constants qw(:common); |
use Apache::localize; |
use Apache::localize; |
use Apache::File; |
use Apache::File; |
|
use locale; |
|
use POSIX qw(locale_h); |
|
|
require Exporter; |
require Exporter; |
|
|
our @ISA = qw (Exporter); |
our @ISA = qw (Exporter); |
our @EXPORT = qw(mt); |
our @EXPORT = qw(mt mtn ns); |
|
|
my $reroute; |
|
|
|
# ========================================================= The language handle |
# ========================================================= The language handle |
|
|
Line 48 use vars qw($lh);
|
Line 179 use vars qw($lh);
|
# ===================================================== The "MakeText" function |
# ===================================================== The "MakeText" function |
|
|
sub mt (@) { |
sub mt (@) { |
unless ($ENV{'environment.translator'}) { |
# my $fh=Apache::File->new('>>/home/www/loncapa/loncom/localize/localize/newphrases.txt'); |
|
# print $fh @_[0]."\n"; |
|
# $fh->close(); |
|
if ($lh) { |
return $lh->maketext(@_); |
return $lh->maketext(@_); |
} else { |
} else { |
my $trans=$lh->maketext(@_); |
if (wantarray) { |
my $link='<a target="trans" href="/cgi-bin/translator.pl?arg1='. |
return @_; |
&Apache::lonnet::escape($_[0]).'&arg2='. |
|
&Apache::lonnet::escape($_[1]).'&arg3='. |
|
&Apache::lonnet::escape($_[2]).'&lang='. |
|
$ENV{'environment.translator'}. |
|
'">[['.$trans.']]</a>'; |
|
if ($ENV{'transreroute'}) { |
|
$reroute.=$link; |
|
return $trans; |
|
} else { |
} else { |
return $link; |
return $_[0]; |
} |
} |
} |
} |
} |
} |
|
|
|
# ============================================================== What language? |
|
|
|
sub current_language { |
|
if ($lh) { |
|
my $lang=$lh->maketext('language_code'); |
|
return ($lang eq 'language_code'?'en':$lang); |
|
} |
|
return 'en'; |
|
} |
|
|
|
# ============================================================== What encoding? |
|
|
|
sub current_encoding { |
|
my $default='UTF-8'; |
|
if ($Apache::lonnet::env{'browser.os'} eq 'win' && |
|
$Apache::lonnet::env{'browser.type'} eq 'explorer') { |
|
$default='ISO-8859-1'; |
|
} |
|
if ($lh) { |
|
my $enc=$lh->maketext('char_encoding'); |
|
return ($enc eq 'char_encoding'?$default:$enc); |
|
} else { |
|
return $default; |
|
} |
|
} |
|
|
|
# =============================================================== Which locale? |
|
# Refer to locale -a |
|
# |
|
sub current_locale { |
|
if ($lh) { |
|
my $enc=$lh->maketext('lang_locale'); |
|
return ($enc eq 'lang_locale'?'':$enc); |
|
} else { |
|
return undef; |
|
} |
|
} |
|
|
# ============================================================== Translate hash |
# ============================================================== Translate hash |
|
|
sub texthash { |
sub texthash { |
Line 76 sub texthash {
|
Line 240 sub texthash {
|
} |
} |
return %hash; |
return %hash; |
} |
} |
# ======================================================== Re-route translation |
|
|
|
sub clearreroutetrans { |
# ========= Get a handle (do not invoke in vain, leave this to access handlers) |
&reroutetrans(); |
|
$reroute=''; |
sub get_language_handle { |
|
my $r=shift; |
|
if ($r) { |
|
my $headers=$r->headers_in; |
|
$ENV{'HTTP_ACCEPT_LANGUAGE'}=$headers->{'Accept-language'}; |
|
} |
|
my @languages=&Apache::loncommon::preferred_languages; |
|
$ENV{'HTTP_ACCEPT_LANGUAGE'}=''; |
|
$lh=Apache::localize->get_handle(@languages); |
|
if ($r && &Apache::lonnet::mod_perl_version == 1) { |
|
$r->content_languages([¤t_language()]); |
|
} |
|
### setlocale(LC_ALL,¤t_locale); |
|
} |
|
|
|
# ========================================================== Localize localtime |
|
sub gettimezone { |
|
return ' ('.$Apache::lonnet::env{'server.timezone'}.')'; |
|
} |
|
|
|
sub locallocaltime { |
|
my $thistime=shift; |
|
if ((¤t_language=~/^en/) || (!$lh)) { |
|
return ''.localtime($thistime).&gettimezone(); |
|
} else { |
|
my $format=$lh->maketext('date_locale'); |
|
if ($format eq 'date_locale') { |
|
return ''.localtime($thistime); |
|
} |
|
my ($seconds,$minutes,$twentyfour,$day,$mon,$year,$wday,$yday,$isdst)= |
|
localtime($thistime); |
|
my $month=(split(/\,/,$lh->maketext('date_months')))[$mon]; |
|
my $weekday=(split(/\,/,$lh->maketext('date_days')))[$wday]; |
|
if ($seconds<10) { |
|
$seconds='0'.$seconds; |
|
} |
|
if ($minutes<10) { |
|
$minutes='0'.$minutes; |
|
} |
|
$year+=1900; |
|
my $twelve=$twentyfour; |
|
my $ampm; |
|
if ($twelve>12) { |
|
$twelve-=12; |
|
$ampm=$lh->maketext('date_pm'); |
|
} else { |
|
$ampm=$lh->maketext('date_am'); |
|
} |
|
foreach |
|
('seconds','minutes','twentyfour','twelve','day','year', |
|
'month','weekday','ampm') { |
|
$format=~s/\$$_/eval('$'.$_)/gse; |
|
} |
|
return $format.&gettimezone(); |
|
} |
} |
} |
|
|
# ======================================================== Re-route translation |
# ==================== Normalize string (reduce fragility in the lexicon files) |
|
|
sub reroutetrans { |
# This normalizes a string to reduce fragility in the lexicon files of |
$ENV{'transreroute'}=1; |
# huge messages (such as are used by the helper), and allow useful |
|
# formatting: reduce all consecutive whitespace to a single space, |
|
# and remove all HTML |
|
sub normalize_string { |
|
my $s = shift; |
|
$s =~ s/\s+/ /g; |
|
$s =~ s/<[^>]+>//g; |
|
# Pop off beginning or ending spaces, which aren't good |
|
$s =~ s/^\s+//; |
|
$s =~ s/\s+$//; |
|
return $s; |
} |
} |
|
|
# ==================================================== End re-route translation |
# alias for normalize_string; recommend using it only in the lexicon |
sub endreroutetrans { |
sub ns { |
$ENV{'transreroute'}=0; |
return normalize_string(@_); |
if ($ENV{'environment.translator'}) { |
} |
return $reroute; |
|
|
# mtn: call the mt function and the normalization function easily. |
|
# Returns original non-normalized string if there was no translation |
|
sub mtn (@) { |
|
my @args = @_; # don't want to modify caller's string; if we |
|
# didn't care about that we could set $_[0] |
|
# directly |
|
$args[0] = normalize_string($args[0]); |
|
my $translation = &mt(@args); |
|
if ($translation ne $args[0]) { |
|
return $translation; |
} else { |
} else { |
return ''; |
return $_[0]; |
} |
} |
} |
} |
|
|
# ========= Get a handle (do not invoke in vain, leave this to access handlers) |
# ---------------------------------------------------- Replace MT{...} in files |
|
|
sub get_language_handle { |
sub transstatic { |
$lh=Apache::localize->get_handle(&Apache::loncommon::preferred_languages); |
my $strptr=shift; |
|
$$strptr=~s/MT\{([^\}]*)\}/&mt($1)/gse; |
|
} |
|
|
|
# ----------------------------------------------- Handler Routine /adm/localize |
|
sub handler { |
|
my $r=shift; |
|
&Apache::lonlocal::get_language_handle($r); |
|
&Apache::loncommon::content_type($r,'text/html'); |
|
$r->send_http_header; |
|
return OK if $r->header_only; |
|
|
|
my $uri=$r->uri; |
|
$uri=~s/^\/adm\/localize//; |
|
my $fn=$Apache::lonnet::perlvar{'lonDocRoot'}.$uri; |
|
|
|
my $file=&Apache::lonnet::getfile($fn); |
|
&transstatic(\$file); |
|
$r->print($file); |
|
return OK; |
} |
} |
|
|
1; |
1; |