line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Locale::CLDR::Lite;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
24814
|
use strict;
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
44
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings;
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
5
|
1
|
|
|
1
|
|
5
|
use Carp;
|
|
1
|
|
|
|
|
14
|
|
|
1
|
|
|
|
|
77
|
|
6
|
1
|
|
|
1
|
|
1008
|
use File::ShareDir ':ALL';
|
|
1
|
|
|
|
|
9684
|
|
|
1
|
|
|
|
|
246
|
|
7
|
1
|
|
|
1
|
|
11
|
use vars qw( $AUTOLOAD $VERSION );
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1779
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
require XML::Simple;
|
10
|
|
|
|
|
|
|
my $xml = XML::Simple->new();
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 NAME
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Locale::CLDR::Lite - Simple access to the Unicode Common Locale Data Repository
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 VERSION
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Version 0.01_02
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=cut
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
$VERSION = '0.01_02';
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
NOTE: This is considered alpha code. Interface may well be subject to complete
|
29
|
|
|
|
|
|
|
change. I'm open to suggestions.
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This module aims to be very light, providing accessor methods to CLDR data and
|
32
|
|
|
|
|
|
|
managing the LDML inheritence model.
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
use Locale::CLDR::Lite;
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
my $locale = Locale::CLDR::Lite->new( 'en_GB' );
|
37
|
|
|
|
|
|
|
my $decimal = $locale->get->numbers->symbols->decimal(); # returns .
|
38
|
|
|
|
|
|
|
my $decimal = $locale->get->dates->calendars->calendar(type => 'gregorian')->
|
39
|
|
|
|
|
|
|
dateFormats->dateFormatLength(type => 'full')->dateFormat->pattern();
|
40
|
|
|
|
|
|
|
# returns EEEE, d MMMM y
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 METHODS
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head2 new
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Create a new accessor object from a given language tag.
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=cut
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub new {
|
52
|
0
|
|
|
0
|
|
|
my ( $class, $lang ) = @_;
|
53
|
0
|
0
|
|
|
|
|
croak( 'You must pass a language tag' ) unless $lang;
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
## Validate tag
|
56
|
0
|
0
|
|
|
|
|
croak( 'Language tags contain invalid characters' ) unless $lang =~ /^([a-z]+)(_[a-z]+)?(_[a-z]+)?$/i;
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Clean case
|
59
|
0
|
|
|
|
|
|
my $type = 'lang';
|
60
|
0
|
|
|
|
|
|
$lang = lc($1);
|
61
|
0
|
0
|
0
|
|
|
|
if ( $2 && $3 ) {
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
62
|
0
|
|
|
|
|
|
$lang .= lc $2 . uc $3;
|
63
|
0
|
|
|
|
|
|
$type = 'lang_script_region';
|
64
|
|
|
|
|
|
|
}
|
65
|
|
|
|
|
|
|
elsif ( $2 && length $2 > 3 ) {
|
66
|
0
|
|
|
|
|
|
$lang .= lc $2;
|
67
|
0
|
|
|
|
|
|
$type = 'lang_script';
|
68
|
|
|
|
|
|
|
}
|
69
|
|
|
|
|
|
|
elsif ( $2 ) {
|
70
|
0
|
|
|
|
|
|
$lang .= uc $2;
|
71
|
0
|
|
|
|
|
|
$type = 'lang_region';
|
72
|
|
|
|
|
|
|
}
|
73
|
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
|
my $self = {
|
75
|
|
|
|
|
|
|
lang => $lang,
|
76
|
|
|
|
|
|
|
};
|
77
|
|
|
|
|
|
|
# We need to know where we are in order to get to the data files
|
78
|
|
|
|
|
|
|
#( my $path = $INC{'Locale/CLDR/Lite.pm'} ) =~ s/\.pm$//;
|
79
|
0
|
|
|
|
|
|
my $path = dist_dir('Locale-CLDR-Lite');
|
80
|
0
|
|
|
|
|
|
my @data_files;
|
81
|
0
|
|
|
|
|
|
while ( $lang ) {
|
82
|
0
|
0
|
|
|
|
|
if ( -e "$path/common/main/$lang.xml" ) {
|
83
|
0
|
|
|
|
|
|
push( @data_files, $lang );
|
84
|
|
|
|
|
|
|
}
|
85
|
|
|
|
|
|
|
else {
|
86
|
0
|
|
|
|
|
|
warn( "No match for $lang, looking down inheritance" );
|
87
|
|
|
|
|
|
|
}
|
88
|
0
|
0
|
|
|
|
|
$lang = '' unless $lang =~ s/_\w+$//;
|
89
|
|
|
|
|
|
|
}
|
90
|
0
|
0
|
|
|
|
|
croak( "Could not match language $_[1]" ) unless @data_files;
|
91
|
0
|
|
|
|
|
|
$self->{files} = \@data_files;
|
92
|
0
|
|
|
|
|
|
$self->{path} = "$path/common/main";
|
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
return bless $self, $class;
|
95
|
|
|
|
|
|
|
}
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head2 get
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Get must be called first whenever you want to start a new request navigating
|
101
|
|
|
|
|
|
|
from a base node.
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=cut
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub get {
|
106
|
0
|
|
|
0
|
|
|
my $self = shift;
|
107
|
0
|
0
|
|
|
|
|
croak( 'You can only call get on the base object' ) if ref $self->{node};
|
108
|
0
|
|
|
|
|
|
my %clone = %$self;
|
109
|
0
|
|
|
|
|
|
$clone{node} = [];
|
110
|
0
|
|
|
|
|
|
return bless \%clone, ref $self;
|
111
|
|
|
|
|
|
|
}
|
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 generated on the fly
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
This script generates accessors as you use them. At this time it provides no
|
117
|
|
|
|
|
|
|
validation other than to return undef if your requested tree node doesn't exist.
|
118
|
|
|
|
|
|
|
Go to L for details of the
|
119
|
|
|
|
|
|
|
locale XML data structure.
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub AUTOLOAD {
|
124
|
0
|
|
|
0
|
|
|
my $current = shift;
|
125
|
0
|
|
|
|
|
|
my ( $attr, $value ) = @_;
|
126
|
0
|
0
|
|
|
|
|
croak( 'You must call the get method first' ) unless ref $current->{node};
|
127
|
0
|
|
|
|
|
|
$AUTOLOAD =~ m/([^:]*)$/;
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# Based on the current node
|
130
|
0
|
|
|
|
|
|
my $new = {
|
131
|
|
|
|
|
|
|
%$current,
|
132
|
|
|
|
|
|
|
name => $1,
|
133
|
|
|
|
|
|
|
attr => $attr,
|
134
|
|
|
|
|
|
|
value => $value,
|
135
|
|
|
|
|
|
|
};
|
136
|
0
|
|
|
|
|
|
bless $new, ref $current;
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Validate new node
|
139
|
0
|
|
|
|
|
|
my $found;
|
140
|
0
|
|
|
|
|
|
foreach my $file ( @{ $new->{files} }, 'root' ) {
|
|
0
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
my $locale;
|
142
|
0
|
0
|
|
|
|
|
if ( $new->{cache}->{$file} ) {
|
143
|
0
|
|
|
|
|
|
$locale = $new->{cache}->{$file};
|
144
|
|
|
|
|
|
|
}
|
145
|
|
|
|
|
|
|
else {
|
146
|
0
|
|
|
|
|
|
open( my $INF, "$new->{path}/$file.xml" );
|
147
|
0
|
|
|
|
|
|
$locale = $xml->XMLin( $INF );
|
148
|
0
|
|
|
|
|
|
close( $INF );
|
149
|
0
|
|
|
|
|
|
$new->{cache}->{$file} = $locale;
|
150
|
|
|
|
|
|
|
}
|
151
|
0
|
|
|
|
|
|
my $branch = $locale;
|
152
|
0
|
|
|
|
|
|
$found = 1;
|
153
|
0
|
|
|
|
|
|
my $pos = -1;
|
154
|
0
|
|
|
|
|
|
foreach my $node ( @{ $current->{node} }, $new ) {
|
|
0
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
$pos++;
|
156
|
0
|
0
|
|
|
|
|
if ( ref $branch->{ $node->{name} } ) {
|
|
|
0
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
$branch = $branch->{ $node->{name} };
|
158
|
0
|
0
|
|
|
|
|
if ( $node->{attr} ) {
|
|
|
0
|
|
|
|
|
|
159
|
0
|
0
|
|
|
|
|
if ( ref $branch eq 'HASH' ) {
|
160
|
0
|
0
|
0
|
|
|
|
$found = 0 if $branch->{ $node->{attr} } && $branch->{ $node->{attr} } ne $node->{value};
|
161
|
|
|
|
|
|
|
}
|
162
|
|
|
|
|
|
|
else {
|
163
|
0
|
|
|
|
|
|
$found = 0;
|
164
|
0
|
|
|
|
|
|
foreach my $hashref ( @$branch ) {
|
165
|
0
|
0
|
0
|
|
|
|
if ( $hashref->{ $node->{attr} } && $hashref->{ $node->{attr} } eq $node->{value} ) {
|
166
|
0
|
|
|
|
|
|
$branch = $hashref;
|
167
|
0
|
|
|
|
|
|
$found = 1;
|
168
|
0
|
|
|
|
|
|
last;
|
169
|
|
|
|
|
|
|
}
|
170
|
|
|
|
|
|
|
}#foreach
|
171
|
|
|
|
|
|
|
}#else
|
172
|
|
|
|
|
|
|
}#if
|
173
|
|
|
|
|
|
|
elsif ( ref $branch eq 'ARRAY' ) {
|
174
|
0
|
|
|
|
|
|
croak( "Array of hashes at node '$node->{name}', but no attribute selector supplied" );
|
175
|
|
|
|
|
|
|
}
|
176
|
|
|
|
|
|
|
# Check for alias
|
177
|
0
|
0
|
|
|
|
|
if ( $branch->{alias} ) {
|
178
|
|
|
|
|
|
|
# Figure out where it points to, and attach it to the tree
|
179
|
0
|
|
|
|
|
|
my $path = $branch->{alias}->{path};
|
180
|
0
|
|
|
|
|
|
my $back = $path =~ m#\.\./#g;
|
181
|
0
|
|
|
|
|
|
$path =~ s#^(\.\./){$back}##g;
|
182
|
0
|
|
|
|
|
|
my $count = 0;
|
183
|
0
|
|
|
|
|
|
while ( my ( $field, $pair ) = $path =~ /^(\w+)(\[\@\w+='[\w\-]+'\])?(\/)?/ ) {
|
184
|
0
|
|
|
|
|
|
$path =~ s/^\Q$&\E//;
|
185
|
0
|
|
|
|
|
|
$count++;
|
186
|
0
|
0
|
|
|
|
|
if ( $pair ) {
|
187
|
0
|
|
|
|
|
|
my ( $a, $v ) = $pair =~ /([\w\-]+)='([\w\-]+)/;
|
188
|
0
|
|
|
|
|
|
$new = $current->{node}->[$pos - $back]->$field($a,$v);
|
189
|
|
|
|
|
|
|
}
|
190
|
|
|
|
|
|
|
else {
|
191
|
0
|
|
|
|
|
|
$new = $current->{node}->[$pos - $back]->$field();
|
192
|
|
|
|
|
|
|
}
|
193
|
0
|
|
|
|
|
|
$branch = $new->{branch};
|
194
|
|
|
|
|
|
|
}#while
|
195
|
|
|
|
|
|
|
}#if
|
196
|
|
|
|
|
|
|
}#if
|
197
|
|
|
|
|
|
|
elsif ( defined $branch->{ $node->{name} } ) {
|
198
|
0
|
|
|
|
|
|
return $branch->{ $node->{name} };
|
199
|
|
|
|
|
|
|
}
|
200
|
|
|
|
|
|
|
else {
|
201
|
0
|
|
|
|
|
|
$found = 0;
|
202
|
0
|
|
|
|
|
|
last;
|
203
|
|
|
|
|
|
|
}
|
204
|
|
|
|
|
|
|
}#foreach
|
205
|
0
|
0
|
|
|
|
|
if ( $found ) {
|
206
|
0
|
|
|
|
|
|
$new->{branch} = $branch;
|
207
|
0
|
|
|
|
|
|
last;
|
208
|
|
|
|
|
|
|
}
|
209
|
|
|
|
|
|
|
}#foreach
|
210
|
0
|
0
|
|
|
|
|
return undef unless $found;
|
211
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
$new->{node} = [ @{ $current->{node} }, $new ];
|
|
0
|
|
|
|
|
|
|
213
|
0
|
|
|
|
|
|
return $new;
|
214
|
|
|
|
|
|
|
}
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# This is provided so AUTOLOAD isn't called instead.
|
218
|
0
|
|
|
0
|
|
|
sub DESTROY {}
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
1;
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 AUTHOR
|
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Lyle Hopkins, C<< >>
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head1 CAVEATS
|
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Not much has been tested. The current stable release (version 21 on 2012/08/30)
|
231
|
|
|
|
|
|
|
of the main CLDR XML is included, this is for convenience but makes the module
|
232
|
|
|
|
|
|
|
bloated.
|
233
|
|
|
|
|
|
|
As this module is indended to be very lightweight is doesn't use a much CPAN
|
234
|
|
|
|
|
|
|
so expect funny things in the code.
|
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head1 BUGS
|
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through
|
239
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll
|
240
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes.
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head1 TODO
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Write more tests and examples
|
245
|
|
|
|
|
|
|
Allow for CLDR xml files path overwrite
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head1 SUPPORT
|
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command.
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
perldoc Locale::CLDR::Lite
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
You can also look for information at:
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=over 4
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here)
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
L
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
L
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=item * CPAN Ratings
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
L
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=item * Search CPAN
|
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
L
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=back
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS
|
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
Thanks to John Imrie for giving advice and pointers.
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Thanks to everyone contributing to the CLDR project.
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Thanks to L for funding development.
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head1 SEE ALSO
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
L
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Copyright 2012 Lyle Hopkins.
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
294
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published
|
295
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License.
|
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information.
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
1; # End of Locale::CLDR::Lite
|