Annotation of loncom/localize/lonlocal.pm, revision 1.41

1.1       www         1: # The LearningOnline Network with CAPA
                      2: # Localization routines
                      3: #
1.41    ! albertel    4: # $Id: lonlocal.pm,v 1.40 2006/06/27 14:20:55 albertel Exp $
1.1       www         5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: ######################################################################
                     29: ######################################################################
1.10      bowersj2   30: 
                     31: =pod
                     32: 
                     33: =head1 NAME
                     34: 
                     35: Apache::lonlocal - provides localization services
                     36: 
                     37: =head1 SYNOPSIS
                     38: 
                     39: lonlocal provides localization services for LON-CAPA programmers based
                     40: on Locale::Maketext. See
                     41: C<http://search.cpan.org/dist/Locale-Maketext/lib/Locale/Maketext.pod>
                     42: for more information on Maketext.
                     43: 
                     44: =head1 OVERVIEWX<internationalization>
                     45: 
                     46: As of LON-CAPA 1.1, we've started to localize LON-CAPA using the
                     47: Locale::Maketext module. Internationalization is the bulk of the work
                     48: right now (pre-1.1); localizing can be done anytime, and involves 
                     49: little or no programming.
                     50: 
                     51: The internationalization process involves putting a wrapper around
                     52: on-screen user messages and menus and turning them into keys,
                     53: which the MaketextX<Maketext> library translates into the desired
                     54: language output using a look-up table ("lexicon").X<lexicon>
                     55: 
                     56: As keys we are currently using the plain English messages, and
                     57: Maketext is configured to replace the message by its own key if no
                     58: translation is found. This makes it easy to phase in the
                     59: internationalization without disturbing the screen output.
                     60: 
                     61: Internationalization is somewhat tedious and effectively impossible
                     62: for a non-fluent speaker to perform, but is fairly easy to create
                     63: translations, requiring no programming skill. As a result, this is one
                     64: area where you can really help LON-CAPA out, even if you aren't a
                     65: programmer, and we'd really appreciate it.
                     66: 
                     67: =head1 How To Localize Handlers For Programmers
                     68: 
                     69: Into the "use" section of a module, we need to insert
                     70: 
                     71:  use Apache::lonlocal;
                     72: 
                     73: Note that there are B<no parentheses>, we B<want> to pollute our
                     74: namespace. 
                     75: 
                     76: Inside might be something like this
                     77: 
                     78:  sub message {
                     79:      my $status=shift;
                     80:      my $message='Status unknown';
                     81:      if ($status eq 'WON') {
                     82:         $message='You have won.';
                     83:      } elsif ($status eq 'LOST') {
                     84:         $message='You are a total looser.';
                     85:      }
                     86:      return $message;
                     87:  }
                     88:  ...
                     89:  $r->print('<h3>Gamble your Homework Points</h3>');
                     90:  ...
                     91:  $r->print(<<ENDMSG);
                     92:  <font size="1">Rules:</font>
                     93:  <font size="0">No purchase necessary. Illegal where not allowed.</font>
                     94:  ENDMSG
                     95: 
                     96: We have to now wrap the subroutine &mt()X<mt> ("maketext") around our 
                     97: messages, but not around markup, etc. We also want minimal disturbance. 
                     98: The first two examples are easy:
                     99: 
                    100:  sub message {
                    101:      my $status=shift;
                    102:      my $message='Status unknown';
                    103:      if ($status eq 'WON') {
                    104:         $message='You have won.';
                    105:      } elsif ($status eq 'LOST') {
                    106:         $message='You are a total looser.';
                    107:      }
                    108:      return &mt($message);
                    109:  }
                    110:  ...
                    111:  $r->print('<h3>'.&mt('Gamble your Homework Points').'</h3>');
                    112: 
                    113: The last one is a bummer, since you cannot call subroutines inside of 
                    114: (<<MARKER). I have written a little subroutine to generate a translated 
                    115: hash for that purpose:
                    116: 
                    117:  my %lt=&Apache::lonlocal::texthash('header' => 'Rules', 'disclaimer' => 
                    118:  'No purchase necessary. Illegal where not allowed.');
                    119:  $r->print(<<ENDMSG);
                    120:  <font size="1">$lt{'header'}:</font>
                    121:  <font size="0">$lt{'disclaimer'}</font>
                    122:  ENDMSG
                    123: 
                    124: As a programmer, your job is done here. If everything worked, you 
                    125: should see no changes on the screen.
                    126: 
                    127: =head1 How To Localize LON-CAPA for Translators
                    128: 
                    129: As a translator, you need to provide the lexicon for the keys, which in 
                    130: this case is the plain text message. The lexicons sit in 
                    131: loncom/localize/localize, with the language code as filename, for 
                    132: example de.pm for the German translation. The file then simply looks 
                    133: like this:
                    134: 
                    135:     'You have won.'
                    136:  => 'Sie haben gewonnen.',
                    137: 
                    138:     'You are a total looser.'
                    139:  => 'Sie sind der totale Verlierer.',
                    140: 
                    141:     'Rules'
                    142:  => 'Regeln',
                    143: 
                    144:     'No purchase necessary. Illegal where not allowed.'
                    145:  => 'Es ist erlaubt, einfach zu verlieren, und das ist Ihre Schuld.'
                    146: 
                    147: 
                    148: Comments may be added with the # symbol, which outside of a string
                    149: (the things with the apostrophe surrounding them, which are the 
                    150: keys and translations) will cause the translation routines to
                    151: ignore the rest of the line.
                    152: 
                    153: This is a relatively easy task, and any help is appreciated.
                    154: 
                    155: Maketext can do a whole lot more, see
                    156: C<http://search.cpan.org/dist/Locale-Maketext/lib/Locale/Maketext.pod>
                    157: but for most purposes, we do not have to mess with that.
                    158: 
                    159: =cut
1.1       www       160: 
                    161: package Apache::lonlocal;
                    162: 
                    163: use strict;
                    164: use Apache::localize;
1.14      www       165: use locale;
1.39      albertel  166: use POSIX qw(locale_h strftime);
1.1       www       167: 
                    168: require Exporter;
                    169: 
                    170: our @ISA = qw (Exporter);
1.22      bowersj2  171: our @EXPORT = qw(mt mtn ns);
1.1       www       172: 
                    173: # ========================================================= The language handle
                    174: 
                    175: use vars qw($lh);
                    176: 
                    177: # ===================================================== The "MakeText" function
                    178: 
                    179: sub mt (@) {
1.36      albertel  180: #    open(LOG,'>>/home/www/loncapa/loncom/localize/localize/newphrases.txt');
                    181: #    print LOG (@_[0]."\n");
                    182: #    close(LOG);
1.26      www       183:     if ($lh) {
                    184: 	return $lh->maketext(@_);
1.3       www       185:     } else {
1.31      albertel  186: 	if (wantarray) {
                    187: 	    return @_;
                    188: 	} else {
                    189: 	    return $_[0];
                    190: 	}
1.4       www       191:     }
                    192: }
                    193: 
1.6       www       194: # ============================================================== What language?
                    195: 
                    196: sub current_language {
1.20      albertel  197:     if ($lh) {
                    198: 	my $lang=$lh->maketext('language_code');
                    199: 	return ($lang eq 'language_code'?'en':$lang);
                    200:     }
1.21      www       201:     return 'en';
1.6       www       202: }
                    203: 
1.8       www       204: # ============================================================== What encoding?
                    205: 
                    206: sub current_encoding {
1.33      albertel  207:     my $default='UTF-8';
                    208:     if ($Apache::lonnet::env{'browser.os'} eq 'win' && 
                    209: 	$Apache::lonnet::env{'browser.type'} eq 'explorer') {
1.34      albertel  210:         $default='ISO-8859-1';
1.33      albertel  211:     }
1.12      albertel  212:     if ($lh) {
                    213: 	my $enc=$lh->maketext('char_encoding');
1.33      albertel  214: 	return ($enc eq 'char_encoding'?$default:$enc);
1.12      albertel  215:     } else {
1.33      albertel  216: 	return $default;
1.12      albertel  217:     }
1.8       www       218: }
                    219: 
1.15      www       220: # =============================================================== Which locale?
                    221: # Refer to locale -a
                    222: #
                    223: sub current_locale {
                    224:     if ($lh) {
                    225: 	my $enc=$lh->maketext('lang_locale');
                    226: 	return ($enc eq 'lang_locale'?'':$enc);
                    227:     } else {
                    228: 	return undef;
                    229:     }
                    230: }
                    231: 
1.4       www       232: # ============================================================== Translate hash
                    233: 
                    234: sub texthash {
                    235:     my %hash=@_;
                    236:     foreach (keys %hash) {
                    237: 	$hash{$_}=&mt($hash{$_});
                    238:     }
                    239:     return %hash;
1.1       www       240: }
                    241: 
                    242: # ========= Get a handle (do not invoke in vain, leave this to access handlers)
                    243: 
                    244: sub get_language_handle {
1.9       www       245:     my $r=shift;
1.31      albertel  246:     if ($r) {
                    247: 	my $headers=$r->headers_in;
                    248: 	$ENV{'HTTP_ACCEPT_LANGUAGE'}=$headers->{'Accept-language'};
                    249:     }
1.29      www       250:     my @languages=&Apache::loncommon::preferred_languages;
                    251:     $ENV{'HTTP_ACCEPT_LANGUAGE'}='';
                    252:     $lh=Apache::localize->get_handle(@languages);
1.37      albertel  253:     if ($r) {
1.12      albertel  254: 	$r->content_languages([&current_language()]);
1.8       www       255:     }
1.16      www       256: ###    setlocale(LC_ALL,&current_locale);
1.18      www       257: }
                    258: 
                    259: # ========================================================== Localize localtime
1.35      www       260: sub gettimezone {
1.39      albertel  261:     my ($time) = @_;
                    262:     return ' ('.&strftime("%Z",localtime($time)).')';
1.35      www       263: }
1.18      www       264: 
                    265: sub locallocaltime {
                    266:     my $thistime=shift;
1.40      albertel  267:     if (!defined($thistime) || $thistime eq '') {
                    268: 	return &mt('Never');
                    269:     }
1.18      www       270:     if ((&current_language=~/^en/) || (!$lh)) {
1.39      albertel  271: 	return ''.localtime($thistime).&gettimezone($thistime);
1.18      www       272:     } else {
                    273: 	my $format=$lh->maketext('date_locale');
                    274: 	if ($format eq 'date_locale') {
                    275: 	    return ''.localtime($thistime);
                    276: 	}
                    277: 	my ($seconds,$minutes,$twentyfour,$day,$mon,$year,$wday,$yday,$isdst)=
                    278: 	    localtime($thistime);
                    279: 	my $month=(split(/\,/,$lh->maketext('date_months')))[$mon];
                    280: 	my $weekday=(split(/\,/,$lh->maketext('date_days')))[$wday];
                    281: 	if ($seconds<10) {
                    282: 	    $seconds='0'.$seconds;
                    283: 	}
                    284: 	if ($minutes<10) {
                    285: 	    $minutes='0'.$minutes;
                    286: 	}
                    287: 	$year+=1900;
                    288: 	my $twelve=$twentyfour;
1.19      www       289: 	my $ampm;
1.18      www       290: 	if ($twelve>12) {
                    291: 	    $twelve-=12;
1.19      www       292: 	    $ampm=$lh->maketext('date_pm');
1.18      www       293: 	} else {
1.19      www       294: 	    $ampm=$lh->maketext('date_am');
1.18      www       295: 	}
                    296: 	foreach 
                    297: 	('seconds','minutes','twentyfour','twelve','day','year',
1.19      www       298: 	 'month','weekday','ampm') {
1.18      www       299: 	    $format=~s/\$$_/eval('$'.$_)/gse;
                    300: 	}
1.39      albertel  301: 	return $format.&gettimezone($thistime);
1.18      www       302:     }
1.1       www       303: }
                    304: 
1.17      bowersj2  305: # ==================== Normalize string (reduce fragility in the lexicon files)
                    306: 
                    307: # This normalizes a string to reduce fragility in the lexicon files of
                    308: # huge messages (such as are used by the helper), and allow useful
                    309: # formatting: reduce all consecutive whitespace to a single space,
                    310: # and remove all HTML
                    311: sub normalize_string {
                    312:     my $s = shift;
                    313:     $s =~ s/\s+/ /g;
                    314:     $s =~ s/<[^>]+>//g;
1.22      bowersj2  315:     # Pop off beginning or ending spaces, which aren't good
                    316:     $s =~ s/^\s+//;
                    317:     $s =~ s/\s+$//;
1.17      bowersj2  318:     return $s;
                    319: }
1.22      bowersj2  320: 
                    321: # alias for normalize_string; recommend using it only in the lexicon
                    322: sub ns {
                    323:     return normalize_string(@_);
                    324: }
                    325: 
                    326: # mtn: call the mt function and the normalization function easily.
                    327: # Returns original non-normalized string if there was no translation
                    328: sub mtn (@) {
                    329:     my @args = @_; # don't want to modify caller's string; if we
                    330: 		   # didn't care about that we could set $_[0]
                    331: 		   # directly
                    332:     $args[0] = normalize_string($args[0]);
                    333:     my $translation = &mt(@args);
                    334:     if ($translation ne $args[0]) {
                    335: 	return $translation;
                    336:     } else {
                    337: 	return $_[0];
                    338:     }
1.27      www       339: }
                    340: 
                    341: # ---------------------------------------------------- Replace MT{...} in files
                    342: 
                    343: sub transstatic {
                    344:     my $strptr=shift;
                    345:     $$strptr=~s/MT\{([^\}]*)\}/&mt($1)/gse;
                    346: }
                    347: 
1.41    ! albertel  348: =pod 
        !           349: 
        !           350: =item * mt_escape
        !           351: 
        !           352: mt_escape takes a string reference and escape the [] in there so mt
        !           353: will leave them as is and not try to expand them
        !           354: 
        !           355: =cut
        !           356: 
        !           357: sub mt_escape {
        !           358:     my ($str_ref) = @_;
        !           359:     $$str_ref =~s/~/~~/g;
        !           360:     $$str_ref =~s/([\[\]])/~$1/g;
        !           361: }
        !           362: 
1.1       www       363: 1;
                    364: 
                    365: __END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.