318
|
1 package IMPL::Resources::Strings;
|
49
|
2 use strict;
|
245
|
3
|
49
|
4 use File::Spec;
|
245
|
5 use List::Util qw(first);
|
|
6 use IMPL::Resources::Format qw(FormatMessage);
|
318
|
7 use IMPL::require {
|
|
8 Resources => 'IMPL::Resources'
|
|
9 };
|
49
|
10
|
|
11 our $Encoding ||= 'utf-8';
|
|
12 our @Locations;
|
292
|
13 my %maps;
|
49
|
14
|
|
15 sub import {
|
|
16 my ($self,$refStrings,%options) = @_;
|
|
17
|
245
|
18 no strict 'refs';
|
|
19
|
|
20 my $class = caller;
|
292
|
21 my $methods = $options{methods};
|
245
|
22
|
|
23 if (ref $refStrings eq 'HASH') {
|
292
|
24 my $map = ( $maps{$class} ||= {} );
|
245
|
25 while(my ($name,$format) = each %$refStrings) {
|
292
|
26 $map->{default}{$name} = $format;
|
245
|
27
|
|
28 *{"${class}::$name"} = sub {
|
292
|
29 shift if $methods;
|
245
|
30 my $args = @_ == 1 ? shift : { @_ };
|
|
31
|
318
|
32 return _FormatMapMessage($class,$name,$map,Resources->currentLocale,$args);
|
245
|
33 }
|
|
34 }
|
|
35 }
|
|
36 }
|
|
37
|
|
38 sub _FormatMapMessage {
|
|
39 my ($class,$msg,$map,$locale,$args) = @_;
|
49
|
40
|
245
|
41 if (not exists $map->{$locale} ) {
|
|
42 $map->{$locale} = LoadStrings($class,$locale);
|
|
43 }
|
|
44
|
|
45 return FormatMessage( ($map->{$locale} || $map->{default})->{$msg}, $args );
|
|
46 }
|
|
47
|
|
48 sub LoadStrings {
|
|
49 my ($class,$locale) = @_;
|
49
|
50
|
245
|
51 # Foo::Bar -> ('Foo','Bar')
|
|
52 my @classNamespace = split /::/,$class;
|
|
53
|
|
54 my $classShortName = pop @classNamespace;
|
|
55
|
|
56 # Foo::Bar -> 'Foo/Bar.pm'
|
|
57 my $classModuleName = File::Spec->catfile(@classNamespace,"${classShortName}.pm");
|
|
58
|
|
59 # 'Foo/Bar.pm' -> '/full/path/to/Foo/Bar.pm'
|
|
60 my $fullModulePath = first { -f } map( File::Spec->catfile($_,$classModuleName), @INC );
|
49
|
61
|
|
62 my @ways = map {
|
|
63 my @path = ($_);
|
318
|
64 push @path,Resources->currentLocale;
|
49
|
65
|
318
|
66 File::Spec->catfile($_,Resources->currentLocale,@classNamespace,$classShortName);
|
49
|
67 } @Locations;
|
|
68
|
|
69
|
245
|
70 if ($fullModulePath) {
|
|
71
|
|
72 # '/full/path/to/Foo/Bar.pm' -> '/full/path/to/Foo'
|
|
73 my ($vol,$dir,$file) = File::Spec->splitpath($fullModulePath);
|
|
74 my $baseDir = File::Spec->catpath($vol,$dir,'');
|
|
75
|
|
76 # '/full/path/to/Foo' -> '/full/path/to/Foo/locale/En_US/Bar'
|
318
|
77 push @ways, File::Spec->catfile($baseDir,'locale',Resources->currentLocale,$classShortName);
|
245
|
78 }
|
49
|
79
|
245
|
80 my $mapFile = first { -f } @ways;
|
|
81
|
|
82 return unless $mapFile;
|
|
83
|
|
84 return ParseStringsMap($mapFile);
|
49
|
85 }
|
|
86
|
245
|
87 sub ParseStringsMap {
|
49
|
88 my ($fname) = @_;
|
|
89
|
|
90 open my $hRes, "<:encoding($Encoding)", findFile($fname) or die "Failed to open file $fname: $!";
|
|
91
|
|
92 my %Map;
|
|
93 my $line = 1;
|
|
94 while (<$hRes>) {
|
|
95 chomp;
|
|
96 $line ++ and next if /^\s*$/;
|
|
97
|
|
98 if (/^(\w+)\s*=\s*(.*)$/) {
|
|
99 $Map{$1} = $2;
|
|
100 } else {
|
|
101 die "Invalid resource format in $fname at $line";
|
|
102 }
|
|
103 $line ++;
|
|
104 }
|
|
105
|
|
106 return \%Map;
|
|
107 }
|
|
108
|
|
109 1;
|
|
110
|
|
111 __END__
|
|
112
|
|
113 =pod
|
|
114
|
66
|
115 =head1 NAME
|
|
116
|
180
|
117 C<IMPL::Resources::Strings> - Строковые ресурсы
|
66
|
118
|
49
|
119 =head1 SYNOPSIS
|
|
120
|
66
|
121 =begin code
|
|
122
|
49
|
123 package Foo;
|
|
124
|
|
125 use IMPL::Resources::Strings {
|
245
|
126 msg_say_hello => "Hello, %name%!",
|
49
|
127 msg_module_name => "Simple Foo class"
|
267
|
128 };
|
49
|
129
|
|
130 sub InviteUser {
|
|
131 my ($this,$uname) = @_;
|
|
132
|
|
133 print msg_say_hello(name => $uname);
|
|
134
|
|
135 }
|
|
136
|
66
|
137 =end code
|
|
138
|
|
139 =head1 DESCRIPTION
|
|
140
|
180
|
141 Импортирует в целевой модуль функции, которые возвращают локализованные
|
|
142 параметризованные сообщения.
|
66
|
143
|
180
|
144 При импорте ищутся модули по следующему алгоритму:
|
66
|
145
|
180
|
146 В каталогах из массива C<@Locations> ищется файл с относительным путем
|
245
|
147 C<$Locale/$ModName>, где C<$Locale> - глобальная переменная
|
180
|
148 модуля C<IMPL::Resourses::Strings>, а переменная C<$ModName> получена
|
|
149 путем замены 'C<::>' в имени целевого модуля на 'C</>'.
|
66
|
150
|
180
|
151 Если файл не был найден, то производится поиск в каталоге, где
|
|
152 расположен сам модуль, файла с относительным путем C<locale/$Locale/$ShortModName>,
|
|
153 где C<$ShortModeName> - последняя часть после 'C<::>' из имени целевого модуля.
|
66
|
154
|
180
|
155 Если файл не найден, то используются строки, указанные при объявлении
|
|
156 сообщений в целевом модуле.
|
66
|
157
|
|
158 =head1 FORMAT
|
|
159
|
|
160 =begin code text
|
|
161
|
|
162 msg_name = any text with named %params%
|
|
163 msg_hello = hello, %name%!!!
|
|
164 msg_resolve = this is a value of the property: %user.age%
|
|
165
|
|
166 msg_short_err = %error.Message%
|
|
167 msg_full_err = %error%
|
|
168
|
|
169 =end code text
|
|
170
|
49
|
171 =cut
|