TPac: locale handling improvements
authorBill Erickson <berick@esilibrary.com>
Sun, 11 Sep 2011 15:20:39 +0000 (11:20 -0400)
committerBill Erickson <berick@esilibrary.com>
Sun, 11 Sep 2011 22:33:35 +0000 (18:33 -0400)
Allow Locale::Maketext to fall through to parent locales
(superordinate) when the a translation for a string in the given locale
is not present.  Locale::Maketext suppports arbitrary-length locale
tags, so it's possible to create locale hierarchies.

For example, you could create branch-specific translations that fall
through to system, then full locale, then base locale, then the default
template strings.

en_us_systemA_branchX => en_us_systemA => en_us => en => <template strings>

Each template string that needs translating will be tried against each
locale in order until it finds a translation.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/examples/oils_web.xml.example
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm

index 9961490..958228e 100644 (file)
@@ -42,9 +42,9 @@
         All locales will fall back to native strings when a given string is not in the catalog
     -->
     <locales>
-        <en_US/>
-        <en_CA>/openils/var/data/locale/messages.en_CA.po</en_CA>
-        <fr_CA>/openils/var/data/locale/messages.fr_CA.po</fr_CA>
+        <en_us/>
+        <en_ca>/openils/var/data/locale/messages.en_ca.po</en_ca>
+        <fr_ca>/openils/var/data/locale/messages.fr_ca.po</fr_ca>
     </locales>
 
     <!-- Where templates can be found.  Paths will be checked in the order entered here.
index 0db9912..de367f4 100644 (file)
@@ -17,7 +17,6 @@ use constant OILS_HTTP_COOKIE_LOCALE => 'eg_locale';
 my $web_config;
 my $web_config_file;
 my $web_config_edit_time;
-my %lh_cache; # locale handlers
 
 sub import {
     my $self = shift;
@@ -82,16 +81,13 @@ sub set_text_handler {
     my $r = shift;
 
     my $locale = $ctx->{locale};
-    $locale =~ s/-/_/g;
 
     $r->log->debug("egweb: messages locale = $locale");
 
-    unless($lh_cache{$locale}) {
-        $r->log->info("egweb: Unsupported locale: $locale");
-        $lh_cache{$locale} = $lh_cache{'en_US'};
-    }
-
-    return sub { return $lh_cache{$locale}->maketext(@_); };
+    return sub {
+        my $lh = OpenILS::WWW::EGWeb::I18N->get_handle($locale);
+        return $lh->maketext(@_);
+    };
 }
 
 
@@ -162,7 +158,7 @@ sub load_context {
 
     $ctx->{locale} = 
         $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || 
-        parse_accept_lang($r->headers_in->get('Accept-Language')) || 'en-US';
+        parse_accept_lang($r->headers_in->get('Accept-Language')) || 'en_us';
 
     my $mprefix = $ctx->{media_prefix};
     if($mprefix and $mprefix !~ /^http/ and $mprefix !~ /^\//) {
@@ -174,13 +170,14 @@ sub load_context {
 }
 
 # turn Accept-Language into sometihng EG can understand
+# TODO: try all langs, not just the first
 sub parse_accept_lang {
     my $al = shift;
     return undef unless $al;
     my ($locale) = split(/,/, $al);
     ($locale) = split(/;/, $locale);
     return undef unless $locale;
-    $locale =~ s/-(.*)/eval '-'.uc("$1")/e;
+    $locale =~ s/-/_/og;
     return $locale;
 }
 
@@ -268,16 +265,31 @@ sub load_locale_handlers {
     my $ctx = shift;
     my $locales = $ctx->{locales};
 
-    $locales->{en_US} = {} unless exists $locales->{en_US};
+    my @locale_tags = sort { length($a) <=> length($b) } keys %$locales;
+
+    for my $idx (0..$#locale_tags) {
+
+        my $tag = $locale_tags[$idx];
+        my $parent_tag = '';
+        my $sub_idx = $idx;
+
+        # find the parent locale if possible.  It will be 
+        # longest left-anchored substring of the current tag
+        while( --$sub_idx >= 0 ) {
+            my $ptag = $locale_tags[$sub_idx];
+            if( substr($tag, 0, length($ptag)) eq $ptag ) {
+                $parent_tag = "::$ptag";
+                last;
+            }
+        }
 
-    for my $lang (keys %$locales) {
-        my $messages = $locales->{$lang};
+        my $messages = $locales->{$tag};
         $messages = '' if ref $messages; # empty {}
 
         # TODO Can we do this without eval?
-        my $eval = <<EVAL;
-            package OpenILS::WWW::EGWeb::I18N::$lang;
-            use base 'OpenILS::WWW::EGWeb::I18N';
+        my $eval = <<"        EVAL";
+            package OpenILS::WWW::EGWeb::I18N::$tag;
+            use base 'OpenILS::WWW::EGWeb::I18N$parent_tag';
             if(\$messages) {
                 use Locale::Maketext::Lexicon::Gettext;
                 if(open F, '$messages') {
@@ -287,10 +299,9 @@ sub load_locale_handlers {
                     warn "EGWeb: unable to open messages file: $messages"; 
                 }
             }
-EVAL
+        EVAL
         eval $eval;
         warn "$@\n" if $@; # TODO better logging
-        $lh_cache{$lang} = "OpenILS::WWW::EGWeb::I18N::$lang"->new;
     }
 }