view Lib/IMPL/Resources/Strings.pm @ 245:7c517134c42f

Added Unsupported media type Web exception corrected resourceLocation setting in the resource Implemented localizable resources for text messages fixed TT view scopings, INIT block in controls now sets globals correctly.
author sergey
date Mon, 29 Oct 2012 03:15:22 +0400
parents 2b9b55cfb79b
children bbc0da7ef90e
line wrap: on
line source

use strict;
use warnings;

package IMPL::Resources::Strings;

use File::Spec;
use List::Util qw(first);
use IMPL::Resources::Format qw(FormatMessage);

our $Locale ||= 'default';
our $Encoding ||= 'utf-8';
our @Locations;

sub import {
    my ($self,$refStrings,%options) = @_;
    
    no strict 'refs';
    
    my $class = caller;
    
    if (ref $refStrings eq 'HASH') {
        my %map;
        while(my ($name,$format) = each %$refStrings) {
            $map{default}{$name} = $format;
            
            *{"${class}::$name"} = sub {
                my $args = @_ == 1 ? shift : { @_ };
                
                return _FormatMapMessage($class,$name,\%map,$Locale,$args);
            }
        }
    }    
}

sub _FormatMapMessage {
    my ($class,$msg,$map,$locale,$args) = @_;
    
    if (not exists $map->{$locale} ) {
        $map->{$locale} = LoadStrings($class,$locale);        
    }
    
    return FormatMessage( ($map->{$locale} || $map->{default})->{$msg}, $args );
}

sub LoadStrings {
    my ($class,$locale) = @_;
    
    # Foo::Bar -> ('Foo','Bar')
    my @classNamespace = split /::/,$class;
    
    my $classShortName = pop @classNamespace;

    # Foo::Bar -> 'Foo/Bar.pm'    
    my $classModuleName = File::Spec->catfile(@classNamespace,"${classShortName}.pm");
    
    # 'Foo/Bar.pm' -> '/full/path/to/Foo/Bar.pm'
    my $fullModulePath = first { -f } map( File::Spec->catfile($_,$classModuleName), @INC );
    
    my @ways = map {
        my @path = ($_);
        push @path,$Locale;
        
        File::Spec->catfile($_,$Locale,@classNamespace,$classShortName);
    } @Locations;
    
    
    if ($fullModulePath) {

        # '/full/path/to/Foo/Bar.pm' -> '/full/path/to/Foo' 
        my ($vol,$dir,$file) = File::Spec->splitpath($fullModulePath);
        my $baseDir = File::Spec->catpath($vol,$dir,'');

        # '/full/path/to/Foo' -> '/full/path/to/Foo/locale/En_US/Bar' 
        push @ways, File::Spec->catfile($baseDir,'locale',$Locale,$classShortName);
    }
    
    my $mapFile = first { -f } @ways;
    
    return unless $mapFile;
    
    return ParseStringsMap($mapFile);
}

sub ParseStringsMap {
    my ($fname) = @_;
    
    open my $hRes, "<:encoding($Encoding)", findFile($fname) or die "Failed to open file $fname: $!";
    
    my %Map;
    my $line = 1;
    while (<$hRes>) {
        chomp;
        $line ++ and next if /^\s*$/;
        
        if (/^(\w+)\s*=\s*(.*)$/) {
            $Map{$1} = $2;
        } else {
            die "Invalid resource format in $fname at $line";
        }
        $line ++;
    }
    
    return \%Map;
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Resources::Strings> - Строковые ресурсы

=head1 SYNOPSIS

=begin code

package Foo;

use IMPL::Resources::Strings {
    msg_say_hello => "Hello, %name%!",
    msg_module_name => "Simple Foo class"
}, auto => 1, locale => 'en-US';

sub InviteUser {
    my ($this,$uname) = @_;
    
    print msg_say_hello(name => $uname);
    
}

=end code

=head1 DESCRIPTION

Импортирует в целевой модуль функции, которые возвращают локализованные
параметризованные сообщения.

При импорте ищутся модули по следующему алгоритму:

В каталогах из массива C<@Locations> ищется файл с относительным путем
C<$Locale/$ModName>, где C<$Locale> - глобальная переменная
модуля C<IMPL::Resourses::Strings>, а переменная C<$ModName> получена
путем замены 'C<::>' в имени целевого модуля на 'C</>'.

Если файл не был найден, то производится поиск в каталоге, где
расположен сам модуль, файла с относительным путем C<locale/$Locale/$ShortModName>,
где C<$ShortModeName> - последняя часть после 'C<::>' из имени целевого модуля.

Если файл не найден, то используются строки, указанные при объявлении
сообщений в целевом модуле.

=head1 FORMAT

=begin code text

msg_name = any text with named %params%
msg_hello = hello, %name%!!!
msg_resolve = this is a value of the property: %user.age%

msg_short_err = %error.Message%
msg_full_err = %error% 

=end code text 

=cut