view Lib/IMPL/Resources/Strings.pm @ 250:129e48bb5afb

DOM refactoring ObjectToDOM methods are virtual QueryToDOM uses inflators Fixed transform for the complex values in the ObjectToDOM QueryToDOM doesn't allow to use complex values (HASHes) as values for nodes (overpost problem)
author sergey
date Wed, 07 Nov 2012 04:17:53 +0400
parents 7c517134c42f
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