File Coverage

blib/lib/DateTime/Locale.pm
Criterion Covered Total %
statement 139 148 93.9
branch 57 76 75.0
condition 6 12 50.0
subroutine 21 23 91.3
pod 5 9 55.5
total 228 268 85.0


line stmt bran cond sub pod time code
1              
2             use 5.008004;
3 15     15   8924904  
  15         167  
4             use strict;
5 15     15   78 use warnings;
  15         23  
  15         314  
6 15     15   69 use namespace::autoclean;
  15         23  
  15         460  
7 15     15   6248  
  15         235798  
  15         64  
8             our $VERSION = '1.37';
9              
10             use DateTime::Locale::Data;
11 15     15   16378 use DateTime::Locale::FromData;
  15         360  
  15         1817  
12 15     15   9067 use DateTime::Locale::Util qw( parse_locale_code );
  15         55  
  15         508  
13 15     15   110 use Params::ValidationCompiler 0.13 qw( validation_for );
  15         32  
  15         804  
14 15     15   91 use Specio::Library::String;
  15         332  
  15         647  
15 15     15   6439  
  15         137925  
  15         152  
16             my %Class;
17             my %DataForCode;
18             my %NameToCode;
19             my %NativeNameToCode;
20             my %UserDefinedAlias;
21              
22             my %LoadCache;
23              
24             my $class = shift;
25              
26 8     8 0 8442 %LoadCache = ();
27              
28 8         112 if ( ref $_[0] ) {
29             $class->_register(%$_) foreach @_;
30 8 100       24 }
31 2         13 else {
32             $class->_register(@_);
33             }
34 6         17 }
35              
36             shift;
37             my %p = @_;
38              
39 9     9   11 my $id = $p{id};
40 9         32  
41             die q{'\@' or '=' are not allowed in locale ids}
42 9         16 if $id =~ /[\@=]/;
43              
44 9 100       47 die
45             "You cannot replace an existing locale ('$id') unless you also specify the 'replace' parameter as true\n"
46             if !delete $p{replace} && exists $DataForCode{$id};
47              
48             $p{native_language} = $p{en_language}
49 7 50 33     32 unless exists $p{native_language};
50              
51             my @en_pieces;
52 7 50       21 my @native_pieces;
53             foreach my $p (qw( language script territory variant )) {
54 7         13 push @en_pieces, $p{"en_$p"} if exists $p{"en_$p"};
55             push @native_pieces, $p{"native_$p"} if exists $p{"native_$p"};
56 7         14 }
57 28 100       63  
58 28 100       55 $p{en_complete_name} = join q{ }, @en_pieces;
59             $p{native_complete_name} = join q{ }, @native_pieces;
60              
61 7         21 $id =~ s/_/-/g;
62 7         15  
63             $DataForCode{$id} = \%p;
64 7         25  
65             $NameToCode{ $p{en_complete_name} } = $id;
66 7         17 $NativeNameToCode{ $p{native_complete_name} } = $id;
67              
68 7         16 $Class{$id} = $p{class} if defined exists $p{class};
69 7         13 }
70              
71 7 50       32 shift;
72              
73             %LoadCache = ();
74              
75 1     1 1 15 my %p = ref $_[0] ? %{ $_[0] } : @_;
76              
77 1         3 my $code = $p{code};
78              
79 1 50       13 die q{'\@' or '=' are not allowed in locale codes}
  0         0  
80             if $code =~ /[\@=]/;
81 1         2  
82             $code =~ s/_/-/g;
83 1 50       6  
84             DateTime::Locale::Data::add_locale( $code, \%p );
85             return $LoadCache{$code} = DateTime::Locale::FromData->new( \%p );
86 1         3 }
87              
88 1         6 shift;
89 1         4  
90             %LoadCache = ();
91              
92             my $aliases = ref $_[0] ? $_[0] : {@_};
93 5     5 0 5075  
94             for my $alias ( keys %{$aliases} ) {
95 5         8 my $code = $aliases->{$alias};
96              
97 5 50       14 die q{Can't alias an id to itself}
98             if $alias eq $code;
99 5         8  
  5         12  
100 5         8 # check for overwrite?
101              
102 5 100       15 my %seen = ( $alias => 1, $code => 1 );
103             my $copy = $code;
104             while ( $copy = $UserDefinedAlias{$copy} ) {
105             die
106             "Creating an alias from $alias to $code would create a loop.\n"
107 4         8 if $seen{$copy};
108 4         5  
109 4         10 $seen{$copy} = 1;
110             }
111              
112 4 100       14 $UserDefinedAlias{$alias} = $code;
113             }
114 3         6 }
115              
116             shift;
117 3         8  
118             %LoadCache = ();
119              
120             my $alias = shift;
121              
122 1     1 0 2 return delete $UserDefinedAlias{$alias};
123             }
124 1         2  
125             # deprecated
126 1         15 shift->codes;
127             }
128 1         6  
129             ## no critic (Variables::ProhibitPackageVars)
130             wantarray
131             ? keys %DateTime::Locale::Data::Codes
132             : [ keys %DateTime::Locale::Data::Codes ];
133 0     0 0 0 }
134              
135             wantarray
136             ? keys %DateTime::Locale::Data::Names
137             : [ keys %DateTime::Locale::Data::Names ];
138             }
139 2 50   2 1 1449  
140             wantarray
141             ? keys %DateTime::Locale::Data::NativeNames
142             : [ keys %DateTime::Locale::Data::NativeNames ];
143             }
144              
145 1 50   1 1 216 # These are hard-coded for backwards comaptibility with the DateTime::Language
146             # code.
147             my %DateTimeLanguageAliases = (
148              
149             # 'Afar' => 'aa',
150             'Amharic' => 'am-ET',
151 0 0   0 1 0 'Austrian' => 'de-AT',
152             'Brazilian' => 'pt-BR',
153             'Czech' => 'cs-CZ',
154             'Danish' => 'da-DK',
155             'Dutch' => 'nl-NL',
156             'English' => 'en-US',
157             'French' => 'fr-FR',
158              
159             # 'Gedeo' => undef, # XXX
160             'German' => 'de-DE',
161             'Italian' => 'it-IT',
162             'Norwegian' => 'no-NO',
163             'Oromo' => 'om-ET', # Maybe om-KE or plain om ?
164             'Portugese' => 'pt-PT',
165              
166             # 'Sidama' => 'sid',
167             'Somali' => 'so-SO',
168             'Spanish' => 'es-ES',
169             'Swedish' => 'sv-SE',
170              
171             # 'Tigre' => 'tig',
172             'TigrinyaEthiopian' => 'ti-ET',
173             'TigrinyaEritrean' => 'ti-ER',
174             );
175              
176             my %POSIXAliases = (
177             C => 'en-US',
178             POSIX => 'en-US',
179             );
180              
181             {
182             my $validator = validation_for(
183             name => '_check_load_params',
184             name_is_optional => 1,
185             params => [
186             { type => t('NonEmptyStr') },
187             ],
188             );
189              
190             my $class = shift;
191             my ($code) = $validator->(@_);
192              
193             # We used to use underscores in codes instead of dashes. We want to
194             # support both indefinitely.
195             $code =~ tr/_/-/;
196              
197             # Strip off charset for LC_* codes : en_GB.UTF-8 etc
198             $code =~ s/\..*$//;
199              
200             return $LoadCache{$code} if exists $LoadCache{$code};
201 879     879 1 3283274  
202 879         23901 while ( exists $UserDefinedAlias{$code} ) {
203             $code = $UserDefinedAlias{$code};
204             }
205              
206 879         11409 $code = $DateTimeLanguageAliases{$code}
207             if exists $DateTimeLanguageAliases{$code};
208             $code = $POSIXAliases{$code} if exists $POSIXAliases{$code};
209 879         2204 $code = $DateTime::Locale::Data::ISO639Aliases{$code}
210             if exists $DateTime::Locale::Data::ISO639Aliases{$code};
211 879 100       3252  
212             if ( exists $DateTime::Locale::Data::Codes{$code} ) {
213 869         2460 return $class->_locale_object_for($code);
214 3         6 }
215              
216             if ( exists $DateTime::Locale::Data::Names{$code} ) {
217             return $class->_locale_object_for(
218 869 100       2711 $DateTime::Locale::Data::Names{$code} );
219 869 100       2537 }
220              
221 869 100       2423 if ( exists $DateTime::Locale::Data::NativeNames{$code} ) {
222             return $class->_locale_object_for(
223 869 100       2622 $DateTime::Locale::Data::NativeNames{$code} );
224 852         2952 }
225              
226             if ( my $locale = $class->_registered_locale_for($code) ) {
227 17 100       50 return $locale;
228             }
229 2         14  
230             if ( my $guessed = $class->_guess_code($code) ) {
231             return $class->_locale_object_for($guessed);
232 15 100       41 }
233              
234 1         4 die "Invalid locale code or name: $code\n";
235             }
236             }
237 14 100       40  
238 7         22 shift;
239             my $code = shift;
240              
241 7 100       16 my %codes = parse_locale_code($code);
242 5         9  
243             my @guesses;
244              
245 2         12 if ( $codes{script} ) {
246             my $guess = join q{-}, $codes{language}, $codes{script};
247              
248             push @guesses, $guess;
249              
250 7     7   11 $guess .= q{-} . $codes{territory} if defined $codes{territory};
251 7         8  
252             # version with script comes first
253 7         32 unshift @guesses, $guess;
254             }
255 7         11  
256             if ( $codes{variant} ) {
257 7 100       18 push @guesses, join q{-}, $codes{language}, $codes{territory},
258 1         4 $codes{variant};
259             }
260 1         2  
261             if ( $codes{territory} ) {
262 1 50       5 push @guesses, join q{-}, $codes{language}, $codes{territory};
263             }
264              
265 1         2 push @guesses, $codes{language};
266              
267             for my $code (@guesses) {
268 7 50       41 return $code
269             if exists $DateTime::Locale::Data::Codes{$code}
270 0         0 || exists $DateTime::Locale::Data::Names{$code};
271             }
272             }
273 7 100       16  
274 4         28 shift;
275             my $code = shift;
276              
277 7         16 my $data = DateTime::Locale::Data::locale_data($code)
278             or return;
279 7         15  
280             # We want to make a copy of the data just in case ...
281             return $LoadCache{$code} = DateTime::Locale::FromData->new( \%{$data} );
282 7 100 66     46 }
283              
284             my $class = shift;
285             my $code = shift;
286              
287 860     860   1374 # Custom locale registered by user
288 860         1395 if ( $Class{$code} ) {
289             return $LoadCache{$code}
290 860 50       2887 = $class->_load_class_from_code( $code, $Class{$code} );
291             }
292              
293             if ( $DataForCode{$code} ) {
294 860         1907 return $LoadCache{$code} = $class->_load_class_from_code($code);
  860         6886  
295             }
296              
297             if ( $NameToCode{$code} ) {
298 14     14   21 return $LoadCache{$code}
299 14         20 = $class->_load_class_from_code( $NameToCode{$code} );
300             }
301              
302 14 100       34 if ( $NativeNameToCode{$code} ) {
303             return $LoadCache{$code}
304 2         6 = $class->_load_class_from_code( $NativeNameToCode{$code} );
305             }
306             }
307 12 100       34  
308 5         15 my $class = shift;
309             my $code = shift;
310             my $real_class = shift;
311 7 50       15  
312             # We want the first alias for which there is data, even if it has
313 0         0 # no corresponding .pm file. There may be multiple levels of
314             # alias to go through.
315             my $data_code = $code;
316 7 50       22 while ( exists $UserDefinedAlias{$data_code}
317             && !exists $DataForCode{$data_code} ) {
318 0         0  
319             $data_code = $UserDefinedAlias{$data_code};
320             }
321              
322             ( my $underscore_code = $data_code ) =~ s/-/_/g;
323 7     7   11 $real_class ||= "DateTime::Locale::$underscore_code";
324 7         9  
325 7         10 unless ( $real_class->can('new') ) {
326             ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval)
327             eval "require $real_class";
328             die $@ if $@;
329             ## use critic
330 7         11 }
331 7   33     19  
332             my $locale = $real_class->new(
333             %{ $DataForCode{$data_code} },
334 0         0 code => $code,
335             );
336              
337 7         21 if ( $locale->can('cldr_version') ) {
338 7   66     30 my $object_version = $locale->cldr_version;
339              
340 7 50       92 if ( $object_version ne $DateTime::Locale::Data::CLDRVersion ) {
341             warn
342 0         0 "Loaded $real_class, which is from an older version ($object_version)"
343 0 0       0 . ' of the CLDR database than this installation of'
344             . " DateTime::Locale ($DateTime::Locale::Data::CLDRVersion).\n";
345             }
346             }
347              
348 7         14 return $locale;
  7         104  
349             }
350             ## use critic
351              
352 7 100       38 1;
353 1         4  
354             # ABSTRACT: Localization support for DateTime.pm
355 1 50       6  
356 1         13  
357             =pod
358              
359             =encoding UTF-8
360              
361             =head1 NAME
362              
363 7         54 DateTime::Locale - Localization support for DateTime.pm
364              
365             =head1 VERSION
366              
367             version 1.37
368              
369             =head1 SYNOPSIS
370              
371             use DateTime::Locale;
372              
373             my $loc = DateTime::Locale->load('en-GB');
374              
375             print $loc->native_name, "\n", $loc->datetime_format_long, "\n";
376              
377             # but mostly just things like ...
378              
379             my $dt = DateTime->now( locale => 'fr' );
380             print "Aujourd'hui le mois est " . $dt->month_name, "\n";
381              
382             =head1 DESCRIPTION
383              
384             DateTime::Locale is primarily a factory for the various locale subclasses. It
385             also provides some functions for getting information on all the available
386             locales.
387              
388             If you want to know what methods are available for locale objects, then please
389             read the L<DateTime::Locale::FromData> documentation.
390              
391             =head1 USAGE
392              
393             This module provides the following class methods:
394              
395             =head2 DateTime::Locale->load( $locale_code | $locale_name )
396              
397             Returns the locale object for the specified locale code or name - see the
398             L<DateTime::Locale::Catalog> documentation for the list of available codes and
399             names. The name provided may be either the English or native name.
400              
401             If the requested locale is not found, a fallback search takes place to find a
402             suitable replacement.
403              
404             The fallback search order is:
405              
406             {language}-{script}-{territory}
407             {language}-{script}
408             {language}-{territory}-{variant}
409             {language}-{territory}
410             {language}
411              
412             Eg. For the locale code C<es-XX-UNKNOWN> the fallback search would be:
413              
414             es-XX-UNKNOWN # Fails - no such locale
415             es-XX # Fails - no such locale
416             es # Found - the es locale is returned as the
417             # closest match to the requested id
418              
419             Eg. For the locale code C<es-Latn-XX> the fallback search would be:
420              
421             es-Latn-XX # Fails - no such locale
422             es-Latn # Fails - no such locale
423             es-XX # Fails - no such locale
424             es # Found - the es locale is returned as the
425             # closest match to the requested id
426              
427             If no suitable replacement is found, then an exception is thrown.
428              
429             The loaded locale is cached, so that B<locale objects may be singletons>.
430             Calling C<< DateTime::Locale->register_from_data >>, C<<
431             DateTime::Locale->add_aliases >>, or C<< DateTime::Locale->remove_alias >>
432             clears the cache.
433              
434             =head2 DateTime::Locale->codes
435              
436             my @codes = DateTime::Locale->codes;
437             my $codes = DateTime::Locale->codes;
438              
439             Returns an unsorted list of the available locale codes, or an array reference
440             if called in a scalar context. This list does not include aliases.
441              
442             =head2 DateTime::Locale->names
443              
444             my @names = DateTime::Locale->names;
445             my $names = DateTime::Locale->names;
446              
447             Returns an unsorted list of the available locale names in English, or an array
448             reference if called in a scalar context.
449              
450             =head2 DateTime::Locale->native_names
451              
452             my @names = DateTime::Locale->native_names;
453             my $names = DateTime::Locale->native_names;
454              
455             Returns an unsorted list of the available locale names in their native
456             language, or an array reference if called in a scalar context. All native names
457             use UTF-8 as appropriate.
458              
459             =head2 DateTime::Locale->register_from_data( $locale_data )
460              
461             This method allows you to register a custom locale. The data for the locale is
462             specified as a hash (or hashref) where the keys match the method names given in
463             L<DateTime::Locale::FromData>.
464              
465             If you just want to make some small changes on top of an existing locale you
466             can get that locale's data by calling C<< $locale->locale_data >>.
467              
468             Here is an example of making a custom locale based off of C<en-US>:
469              
470             my $locale = DateTime::Locale->load('en-US');
471             my %data = $locale->locale_data;
472             $data{code} = 'en-US-CUSTOM';
473             $data{time_format_medium} = 'HH:mm:ss';
474              
475             DateTime::Locale->register_from_data(%data);
476              
477             # Prints 18:24:38
478             say DateTime->now( locale => 'en-US-CUSTOM' )->strftime('%X');
479              
480             # Prints 6:24:38 PM
481             say DateTime->now( locale => 'en-US' )->strftime('%X');
482              
483             The keys that should be present in the hash are the same as the accessor
484             methods provided by L<DateTime::Locale::FromData>, except for the following:
485              
486             =over 4
487              
488             =item The C<*_code> methods
489              
490             While you should provide a C<code> key, the other methods like C<language_code>
491             and C<script_code> are determined by parsing the code.
492              
493             =item All C<id> returning methods
494              
495             These are aliases for the corresponding C<*code> methods.
496              
497             =item C<prefers_24_hour_time>
498              
499             This is determined by looking at the short time format to see how it formats
500             hours,
501              
502             =item C<date_format_default> and C<time_format_default>
503              
504             These are the corresponding medium formats.
505              
506             =item C<datetime_format> and C<datetime_format_default>
507              
508             This is the same as the medium format.
509              
510             =item C<date_formats> and C<time_formats>
511              
512             These are calculated as needed.
513              
514             =item C<available_formats>
515              
516             This should be provided as a hashref where the keys are things like C<Gy> or
517             C<MMMEd> and the values are an actual format like C<"y G"> or C<"E, MMM d">.
518              
519             =item C<locale_data>
520              
521             This is everything you pass in.
522              
523             =back
524              
525             =head1 LOADING LOCALES IN A PRE-FORKING SYSTEM
526              
527             If you are running an application that does pre-forking (for example with
528             Starman), then you should try to load all the locales that you'll need in the
529             parent process. Locales are loaded on-demand, so loading them once in each
530             child will waste memory that could otherwise be shared.
531              
532             =head1 CLDR DATA BUGS
533              
534             Please be aware that all locale data has been generated from the CLDR (Common
535             Locale Data Repository) project locales data). The data is incomplete, and may
536             contain errors in some locales.
537              
538             When reporting errors in data, please check the primary data sources first,
539             then where necessary report errors directly to the primary source via the CLDR
540             bug report system. See L<http://unicode.org/cldr/filing_bug_reports.html> for
541             details.
542              
543             Once these errors have been confirmed, please forward the error report and
544             corrections to the DateTime mailing list, datetime@perl.org.
545              
546             =head1 AUTHOR EMERITUS
547              
548             Richard Evans wrote the first version of DateTime::Locale, including the tools
549             to extract the CLDR data.
550              
551             =head1 SEE ALSO
552              
553             datetime@perl.org mailing list
554              
555             =head1 SUPPORT
556              
557             Bugs may be submitted at L<https://github.com/houseabsolute/DateTime-Locale/issues>.
558              
559             There is a mailing list available for users of this distribution,
560             L<mailto:datetime@perl.org>.
561              
562             =head1 SOURCE
563              
564             The source code repository for DateTime-Locale can be found at L<https://github.com/houseabsolute/DateTime-Locale>.
565              
566             =head1 DONATIONS
567              
568             If you'd like to thank me for the work I've done on this module, please
569             consider making a "donation" to me via PayPal. I spend a lot of free time
570             creating free software, and would appreciate any support you'd care to offer.
571              
572             Please note that B<I am not suggesting that you must do this> in order for me
573             to continue working on this particular software. I will continue to do so,
574             inasmuch as I have in the past, for as long as it interests me.
575              
576             Similarly, a donation made in this way will probably not make me work on this
577             software much more, unless I get so many donations that I can consider working
578             on free software full time (let's all have a chuckle at that together).
579              
580             To donate, log into PayPal and send money to autarch@urth.org, or use the
581             button at L<https://houseabsolute.com/foss-donations/>.
582              
583             =head1 AUTHOR
584              
585             Dave Rolsky <autarch@urth.org>
586              
587             =head1 CONTRIBUTORS
588              
589             =for stopwords Alexander Pankoff James Raspass Karen Etheridge Mohammad S Anwar Ryley Breiddal Sergey Leschenko yasu47b
590              
591             =over 4
592              
593             =item *
594              
595             Alexander Pankoff <ccntrq@screenri.de>
596              
597             =item *
598              
599             James Raspass <jraspass@gmail.com>
600              
601             =item *
602              
603             Karen Etheridge <ether@cpan.org>
604              
605             =item *
606              
607             Mohammad S Anwar <mohammad.anwar@yahoo.com>
608              
609             =item *
610              
611             Ryley Breiddal <rbreiddal@presinet.com>
612              
613             =item *
614              
615             Sergey Leschenko <Sergey.Leschenko@portaone.com>
616              
617             =item *
618              
619             yasu47b <nakayamayasuhiro1986@gmail.com>
620              
621             =back
622              
623             =head1 COPYRIGHT AND LICENSE
624              
625             This software is copyright (c) 2003 - 2022 by Dave Rolsky.
626              
627             This is free software; you can redistribute it and/or modify it under
628             the same terms as the Perl 5 programming language system itself.
629              
630             The full text of the license can be found in the
631             F<LICENSE> file included with this distribution.
632              
633             =cut