File Coverage

blib/lib/Locale/CLDR.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Locale::CLDR;
2              
3             =encoding utf8
4              
5             =head1 NAME
6              
7             Locale::CLDR - A Module to create locale objects with localisation data from the CLDR
8              
9             =head1 VERSION
10              
11             Version 0.26.1
12              
13             =head1 SYNOPSIS
14              
15             This module provides a locale object you can use to localise your output.
16             The localisation data comes from the Unicode Common Locale Data Repository.
17             Most of this code can be used with Perl version 5.10 or above. There are a
18             few parts of the code that require version 5.18 or above.
19              
20             =head1 USAGE
21              
22             my $locale = Locale::CLDR->new('en_GB');
23              
24             or
25              
26             my $locale = Locale::CLDR->new(language_id => 'en', territory_id => 'gb');
27            
28             A full locale identifier is
29            
30             C<language>_C<script>_C<territory>_C<variant>_u_C<extension name>_C<extension value>
31            
32             my $locale = Locale::CLDR->new('en_latn_GB_SCOUSE_u_nu_traditional');
33            
34             or
35            
36             my $locale = Locale::CLDR->new(language_id => 'en', script_id => 'latn', territory_id => 'gb', variant => 'SCOUSE', extensions => { nu => 'traditional' } );
37            
38             =cut
39              
40 21     21   482295 use v5.10;
  21         71  
  21         873  
41 21     21   10023 use version;
  21         34245  
  21         102  
42             our $VERSION = version->declare('v0.26.1');
43              
44 21     21   12316 use open ':encoding(utf8)';
  21         22552  
  21         118  
45 21     21   230875 use utf8;
  21         49  
  21         119  
46 21     21   1219 use if $^V ge v5.12.0, feature => 'unicode_strings';
  21         39  
  21         997  
47              
48 21     21   7228 use Moose;
  0            
  0            
49             use MooseX::ClassAttribute;
50             with 'Locale::CLDR::ValidCodes', 'Locale::CLDR::EraBoundries', 'Locale::CLDR::WeekData',
51             'Locale::CLDR::MeasurementSystem', 'Locale::CLDR::LikelySubtags', 'Locale::CLDR::NumberingSystems',
52             'Locale::CLDR::NumberFormatter', 'Locale::CLDR::TerritoryContainment', 'Locale::CLDR::CalendarPreferences',
53             'Locale::CLDR::Currencies', 'Locale::CLDR::Plurals';
54            
55             use Class::Load;
56             use namespace::autoclean;
57             use List::Util qw(first);
58             use Class::MOP;
59             use DateTime::Locale;
60             use Unicode::Normalize();
61             #use Locale::CLDR::Collator();
62             use File::Spec();
63              
64             # Backwards compatibility
65             BEGIN {
66             if (defined &CORE::fc) { #v5.16
67             *fc = \&CORE::fc;
68             }
69             else {
70             # This code taken from Unicode::CaseFold by Andrew Rodland
71             require Unicode::UCD;
72             *fc = sub {
73             my ($string) = @_;
74            
75             my $out = "";
76              
77             for my $codepoint (unpack "U*", $string) {
78             my $mapping = Unicode::UCD::casefold($codepoint);
79             my @cp;
80             if (!defined $mapping) {
81             @cp = ($codepoint);
82             } else {
83             @cp = map hex, split / /, $mapping->{'full'};
84             }
85             $out .= pack "U*", @cp;
86             }
87              
88             return $out;
89             };
90             }
91             }
92              
93             =head1 ATTRIBUTES
94              
95             These can be passed into the constructor and all are optional.
96              
97             =over 4
98              
99             =item language_id
100              
101             A valid language or language alias id, such as C<en>
102              
103             =cut
104              
105             has 'language_id' => (
106             is => 'ro',
107             isa => 'Str',
108             required => 1,
109             );
110              
111             # language aliases
112             around 'language_id' => sub {
113             my ($orig, $self) = @_;
114             my $value = $self->$orig;
115             return $self->language_aliases->{$value} // $value;
116             };
117              
118             =item script_id
119              
120             A valid script id, such as C<latn> or C<Ctcl>. The code will pick a likely script
121             depending on the given language if non is provided.
122              
123             =cut
124              
125             has 'script_id' => (
126             is => 'ro',
127             isa => 'Str',
128             default => '',
129             predicate => 'has_script',
130             );
131              
132             =item territory_id
133              
134             A valid territory id or territory alias such as C<GB>
135              
136             =cut
137              
138             has 'territory_id' => (
139             is => 'ro',
140             isa => 'Str',
141             default => '',
142             predicate => 'has_territory',
143             );
144              
145             # territory aliases
146             around 'territory_id' => sub {
147             my ($orig, $self) = @_;
148             my $value = $self->$orig;
149             return $value if defined $value;
150             my $alias = $self->territory_aliases->{$value};
151             return (split /\s+/, $alias)[0];
152             };
153              
154             =item variant_id
155              
156             A valid variant id. The code currently ignores this
157              
158             =cut
159              
160             has 'variant_id' => (
161             is => 'ro',
162             isa => 'Str',
163             default => '',
164             predicate => 'has_variant',
165             );
166              
167             =item extensions
168              
169             A Hashref of extension names and values. You can use this to override
170             the locales number formatting and calendar by passing in the Unicode
171             extension names or aliases as keys and the extension value as the hash
172             value.
173              
174             Currently supported extensions are
175              
176             =over 8
177              
178             =item nu
179              
180             =item numbers
181              
182             The number type can be one of
183              
184             =over 12
185              
186             =item arab
187              
188             Arabic-Indic Digits
189              
190             =item arabext
191              
192             Extended Arabic-Indic Digits
193              
194             =item armn
195              
196             Armenian Numerals
197              
198             =item armnlow
199              
200             Armenian Lowercase Numerals
201              
202             =item bali
203              
204             Balinese Digits
205              
206             =item beng
207              
208             Bengali Digits
209              
210             =item brah
211              
212             Brahmi Digits
213              
214             =item cakm
215              
216             Chakma Digits
217              
218             =item cham
219              
220             Cham Digits
221              
222             =item deva
223              
224             Devanagari Digits
225              
226             =item ethi
227              
228             Ethiopic Numerals
229              
230             =item finance
231              
232             Financial Numerals
233              
234             =item fullwide
235              
236             Full Width Digits
237              
238             =item geor
239              
240             Georgian Numerals
241              
242             =item grek
243              
244             Greek Numerals
245              
246             =item greklow
247              
248             Greek Lowercase Numerals
249              
250             =item gujr
251              
252             Gujarati Digits
253              
254             =item guru
255              
256             Gurmukhi Digits
257              
258             =item hanidays
259              
260             Chinese Calendar Day-of-Month Numerals
261              
262             =item hanidec
263              
264             Chinese Decimal Numerals
265              
266             =item hans
267              
268             Simplified Chinese Numerals
269              
270             =item hansfin
271              
272             Simplified Chinese Financial Numerals
273              
274             =item hant
275              
276             Traditional Chinese Numerals
277              
278             =item hantfin
279              
280             Traditional Chinese Financial Numerals
281              
282             =item hebr
283              
284             Hebrew Numerals
285              
286             =item java
287              
288             Javanese Digits
289              
290             =item jpan
291              
292             Japanese Numerals
293              
294             =item jpanfin
295              
296             Japanese Financial Numerals
297              
298             =item kali
299              
300             Kayah Li Digits
301              
302             =item khmr
303              
304             Khmer Digits
305              
306             =item knda
307              
308             Kannada Digits
309              
310             =item lana
311              
312             Tai Tham Hora Digits
313              
314             =item lanatham
315              
316             Tai Tham Tham Digits
317              
318             =item laoo
319              
320             Lao Digits
321              
322             =item latn
323              
324             Western Digits
325              
326             =item lepc
327              
328             Lepcha Digits
329              
330             =item limb
331              
332             Limbu Digits
333              
334             =item mlym
335              
336             Malayalam Digits
337              
338             =item mong
339              
340             Mongolian Digits
341              
342             =item mtei
343              
344             Meetei Mayek Digits
345              
346             =item mymr
347              
348             Myanmar Digits
349              
350             =item mymrshan
351              
352             Myanmar Shan Digits
353              
354             =item native
355              
356             Native Digits
357              
358             =item nkoo
359              
360             N'Ko Digits
361              
362             =item olck
363              
364             Ol Chiki Digits
365              
366             =item orya
367              
368             Oriya Digits
369              
370             =item osma
371              
372             Osmanya Digits
373              
374             =item roman
375              
376             Roman Numerals
377              
378             =item romanlow
379              
380             Roman Lowercase Numerals
381              
382             =item saur
383              
384             Saurashtra Digits
385              
386             =item shrd
387              
388             Sharada Digits
389              
390             =item sora
391              
392             Sora Sompeng Digits
393              
394             =item sund
395              
396             Sundanese Digits
397              
398             =item takr
399              
400             Takri Digits
401              
402             =item talu
403              
404             New Tai Lue Digits
405              
406             =item taml
407              
408             Traditional Tamil Numerals
409              
410             =item tamldec
411              
412             Tamil Digits
413              
414             =item telu
415              
416             Telugu Digits
417              
418             =item thai
419              
420             Thai Digits
421              
422             =item tibt
423              
424             Tibetan Digits
425              
426             =item traditional
427              
428             Traditional Numerals
429              
430             =item vaii
431              
432             Vai Digits
433              
434             =back
435              
436             =item ca
437              
438             =item calendar
439              
440             You can use this to override a locales default calendar. Valid values are
441              
442             =over 12
443              
444             =item buddhist
445              
446             Buddhist Calendar
447              
448             =item chinese
449              
450             Chinese Calendar
451              
452             =item coptic
453              
454             Coptic Calendar
455              
456             =item dangi
457              
458             Dangi Calendar
459              
460             =item ethiopic
461              
462             Ethiopic Calendar
463              
464             =item ethiopic-amete-alem
465              
466             Ethiopic Amete Alem Calendar
467              
468             =item gregorian
469              
470             Gregorian Calendar
471              
472             =item hebrew
473              
474             Hebrew Calendar
475              
476             =item indian
477              
478             Indian National Calendar
479              
480             =item islamic
481              
482             Islamic Calendar
483              
484             =item islamic-civil
485              
486             Islamic Calendar (tabular, civil epoch)
487              
488             =item islamic-rgsa
489              
490             Islamic Calendar (Saudi Arabia, sighting)
491              
492             =item islamic-tbla
493              
494             Islamic Calendar (tabular, astronomical epoch)
495              
496             =item islamic-umalqura
497              
498             Islamic Calendar (Umm al-Qura)
499              
500             =item iso8601
501              
502             ISO-8601 Calendar
503              
504             =item japanese
505              
506             Japanese Calendar
507              
508             =item persian
509              
510             Persian Calendar
511              
512             =item roc
513              
514             Minguo Calendar
515              
516             =back
517              
518             =back
519              
520             =cut
521              
522             has 'extensions' => (
523             is => 'ro',
524             isa => 'Undef|HashRef',
525             default => undef,
526             writer => '_set_extensions',
527             );
528              
529             =back
530              
531             =head1 Methods
532              
533             The following methods can be called on the locale object
534              
535             =over 4
536              
537             =item id()
538              
539             The local identifier. This is what you get if you attempt to
540             stringify a locale object.
541              
542             =item likely_language()
543              
544             Given a locale with no language passed in or with the explicit language
545             code of C<und>, this method attempts to use the script and territory
546             data to guess the locale's language.
547              
548             =cut
549              
550             has 'likely_language' => (
551             is => 'ro',
552             isa => 'Str',
553             init_arg => undef,
554             lazy => 1,
555             builder => '_build_likely_language',
556             );
557              
558             sub _build_likely_language {
559             my $self = shift;
560            
561             my $language = $self->language();
562            
563             return $language unless $language eq 'und';
564            
565             return $self->likely_subtag->language;
566             }
567              
568             =item likely_script()
569              
570             Given a locale with no script passed in this method attempts to use the
571             language and territory data to guess the locale's script.
572              
573             =cut
574              
575             has 'likely_script' => (
576             is => 'ro',
577             isa => 'Str',
578             init_arg => undef,
579             lazy => 1,
580             builder => '_build_likely_script',
581             );
582              
583             sub _build_likely_script {
584             my $self = shift;
585            
586             my $script = $self->script();
587            
588             return $script if $script;
589            
590             return $self->likely_subtag->script || '';
591             }
592              
593             =item likely_territory()
594              
595             Given a locale with no territory passed in this method attempts to use the
596             language and script data to guess the locale's territory.
597              
598             =back
599              
600             =cut
601              
602             has 'likely_territory' => (
603             is => 'ro',
604             isa => 'Str',
605             init_arg => undef,
606             lazy => 1,
607             builder => '_build_likely_territory',
608             );
609              
610             sub _build_likely_territory {
611             my $self = shift;
612            
613             my $territory = $self->territory();
614            
615             return $territory if $territory;
616            
617             return $self->likely_subtag->territory || '';
618             }
619              
620             has 'module' => (
621             is => 'ro',
622             isa => 'Object',
623             init_arg => undef,
624             lazy => 1,
625             builder => '_build_module',
626             );
627              
628             sub _build_module {
629             # Create the new path
630             my $self = shift;
631            
632             my @path = map { ucfirst lc }
633             map { $_ ? $_ : 'Any' } (
634             $self->language_id,
635             $self->script_id,
636             $self->territory_id,
637             );
638              
639             my @likely_path =
640             map { ucfirst lc } (
641             $self->has_likely_subtag ? $self->likely_subtag->language_id : 'Any',
642             $self->has_likely_subtag ? $self->likely_subtag->script_id : 'Any',
643             $self->has_likely_subtag ? $self->likely_subtag->territory_id : 'Any',
644             );
645            
646             for (my $i = 0; $i < @path; $i++) {
647             $likely_path[$i] = $path[$i] unless $path[$i] eq 'und' or $path[$i] eq 'Any';
648             }
649            
650             # Note the order we push these onto the stack is important
651             @path = join '::', @likely_path;
652             push @path, join '::', $likely_path[0], 'Any', $likely_path[2];
653             push @path, join '::', @likely_path[0 .. 1], 'Any';
654             push @path, join '::', $likely_path[0], 'Any', 'Any';
655            
656             # Now we go through the path loading each module
657             # And calling new on it.
658             my $module;
659             foreach my $module_name (@path) {
660             $module_name = "Locale::CLDR::$module_name";
661             eval { Class::Load::load_class($module_name); };
662             next if $@;
663             $module = $module_name->new;
664             last;
665             }
666              
667             # If we only have the root module then we have a problem as
668             # none of the language specific data is in the root. So we
669             # fall back to the en module
670             if (! $module || ref $module eq 'Locale::CLDR::Root') {
671             Class::Load::load_class('Locale::CLDR::En');
672             $module = Locale::CLDR::En->new
673             }
674              
675             return $module;
676             }
677              
678             class_has 'method_cache' => (
679             is => 'rw',
680             isa => 'HashRef[HashRef[ArrayRef[Object]]]',
681             init_arg => undef,
682             default => sub { return {}},
683             );
684              
685             has 'break_grapheme_cluster' => (
686             is => 'ro',
687             isa => 'ArrayRef',
688             init_arg => undef(),
689             lazy => 1,
690             default => sub {shift->_build_break('GraphemeClusterBreak')},
691             );
692              
693             has 'break_word' => (
694             is => 'ro',
695             isa => 'ArrayRef',
696             init_arg => undef(),
697             lazy => 1,
698             default => sub {shift->_build_break('WordBreak')},
699             );
700              
701             has 'break_line' => (
702             is => 'ro',
703             isa => 'ArrayRef',
704             init_arg => undef(),
705             lazy => 1,
706             default => sub {shift->_build_break('LineBreak')},
707             );
708              
709             has 'break_sentence' => (
710             is => 'ro',
711             isa => 'ArrayRef',
712             init_arg => undef(),
713             lazy => 1,
714             default => sub {shift->_build_break('SentenceBreak')},
715             );
716              
717             =head2 Meta Data
718              
719             The following methods return, in English, the names if the various
720             id's passed into the locales constructor. I.e. if you passed
721             C<language =E<gt> 'fr'> to the constructor you would get back C<French>
722             for the language.
723              
724             =over 4
725              
726             =item name
727              
728             The locale's name. This is usually built up out of the language,
729             script, territory and variant of the locale
730              
731             =item language
732              
733             The name of the locale's language
734              
735             =item script
736              
737             The name of the locale's script
738              
739             =item territory
740              
741             The name of the locale's territory
742              
743             =item variant
744              
745             The name of the locale's variant
746              
747             =back
748              
749             =head2 Native Meta Data
750              
751             Like Meta Data above this provides the names of the various id's
752             passed into the locale's constructor. However in this case the
753             names are formatted to match the locale. I.e. if you passed
754             C<language =E<gt> 'fr'> to the constructor you would get back
755             C<français> for the language.
756              
757             =over 4
758              
759             =item native_name
760              
761             The locale's name. This is usually built up out of the language,
762             script, territory and variant of the locale. Returned in the locale's
763             language and script
764              
765             =item native_language
766              
767             The name of the locale's language in the locale's language and script.
768              
769             =item native_script
770              
771             The name of the locale's script in the locale's language and script.
772              
773             =item native_territory
774              
775             The name of the locale's territory in the locale's language and script.
776              
777             =item native_variant
778              
779             The name of the locale's variant in the locale's language and script.
780              
781             =back
782              
783             =cut
784              
785             foreach my $property (qw( name language script territory variant)) {
786             has $property => (
787             is => 'ro',
788             isa => 'Str',
789             init_arg => undef,
790             lazy => 1,
791             builder => "_build_$property",
792             );
793              
794             no strict 'refs';
795             *{"native_$property"} = sub {
796             my ($self, $for) = @_;
797            
798             $for //= $self;
799             my $build = "_build_native_$property";
800             return $self->$build($for);
801             };
802             }
803              
804             =head2 Calenders
805              
806             The Calendar data is built to hook into L<DateTime::Locale> so that
807             all Locale::CLDR objects can be used as replacements for DateTime::Locale's
808             locale data. To use, say, the French data do
809              
810             my $french_locale = Locale::CLDR->new('fr_FR');
811             my $french_dt = DateTime->now(locale => $french_locale);
812             say "French month : ", $french_dt->month_name; # prints out the current month in French
813              
814             =over 4
815              
816             =item month_format_wide
817              
818             =item month_format_abbreviated
819              
820             =item month_format_narrow
821              
822             =item month_stand_alone_wide
823              
824             =item month_stand_alone_abbreviated
825              
826             =item month_stand_alone_narrow
827              
828             All the above return an arrayref of month names in the requested style.
829              
830             =item day_format_wide
831              
832             =item day_format_abbreviated
833              
834             =item day_format_narrow
835              
836             =item day_stand_alone_wide
837              
838             =item day_stand_alone_abbreviated
839              
840             =item day_stand_alone_narrow
841              
842             All the above return an array ref of day names in the requested style.
843              
844             =item quarter_format_wide
845              
846             =item quarter_format_abbreviated
847              
848             =item quarter_format_narrow
849              
850             =item quarter_stand_alone_wide
851              
852             =item quarter_stand_alone_abbreviated
853              
854             =item quarter_stand_alone_narrow
855              
856             All the above return an arrayref of quarter names in the requested style.
857              
858             =item am_pm_wide
859              
860             =item am_pm_abbreviated
861              
862             =item am_pm_narrow
863              
864             All the above return the date period name for AM and PM
865             in the requested style
866              
867             =item era_wide
868              
869             =item era_abbreviated
870              
871             =item era_narrow
872              
873             All the above return an array ref of era names. Note that these
874             return the first two eras which is what you normally want for
875             BC and AD etc. but won't work correctly for Japanese calendars.
876              
877             =back
878              
879             =cut
880              
881             foreach my $property (qw(
882             month_format_wide month_format_abbreviated month_format_narrow
883             month_stand_alone_wide month_stand_alone_abbreviated month_stand_alone_narrow
884             day_format_wide day_format_abbreviated day_format_narrow
885             day_stand_alone_wide day_stand_alone_abbreviated day_stand_alone_narrow
886             quarter_format_wide quarter_format_abbreviated quarter_format_narrow
887             quarter_stand_alone_wide quarter_stand_alone_abbreviated quarter_stand_alone_narrow
888             am_pm_wide am_pm_abbreviated am_pm_narrow
889             era_wide era_abbreviated era_narrow
890             era_format_wide era_format_abbreviated era_format_narrow
891             era_stand_alone_wide era_stand_alone_abbreviated era_stand_alone_narrow
892             )) {
893             has $property => (
894             is => 'ro',
895             isa => 'ArrayRef',
896             init_arg => undef,
897             lazy => 1,
898             builder => "_build_$property",
899             clearer => "_clear_$property",
900             );
901             }
902              
903             =pod
904              
905             The next set of methods are not used by DateTime::Locale but CLDR provide
906             the data and you might want it
907              
908             =over 4
909              
910             =item am_pm_format_wide
911              
912             =item am_pm_format_abbreviated
913              
914             =item am_pm_format_narrow
915              
916             =item am_pm_stand_alone_wide
917              
918             =item am_pm_stand_alone_abbreviated
919              
920             =item am_pm_stand_alone_narrow
921              
922             All the above return a hashref keyed on date period
923             with the value being the value for that date period
924              
925             =item era_format_wide
926              
927             =item era_format_abbreviated
928              
929             =item era_format_narrow
930            
931             =item era_stand_alone_wide
932              
933             =item era_stand_alone_abbreviated
934              
935             =item era_stand_alone_narrow
936              
937             All the above return an array ref with I<all> the era data for the
938             locale formatted to the requested width
939              
940             =cut
941              
942             foreach my $property (qw(
943             am_pm_format_wide am_pm_format_abbreviated am_pm_format_narrow
944             am_pm_stand_alone_wide am_pm_stand_alone_abbreviated am_pm_stand_alone_narrow
945             )) {
946             has $property => (
947             is => 'ro',
948             isa => 'HashRef',
949             init_arg => undef,
950             lazy => 1,
951             builder => "_build_$property",
952             clearer => "_clear_$property",
953             );
954             }
955              
956             =item date_format_full
957              
958             =item date_format_long
959              
960             =item date_format_medium
961              
962             =item date_format_short
963            
964             =item time_format_full
965              
966             =item time_format_long
967              
968             =item time_format_medium
969              
970             =item time_format_short
971            
972             =item datetime_format_full
973              
974             =item datetime_format_long
975            
976             =item datetime_format_medium
977              
978             =item datetime_format_short
979              
980             All the above return the CLDR I<date format pattern> for the given
981             element and width
982              
983             =cut
984              
985             foreach my $property (qw(
986             id
987             date_format_full date_format_long
988             date_format_medium date_format_short
989             time_format_full time_format_long
990             time_format_medium time_format_short
991             datetime_format_full datetime_format_long
992             datetime_format_medium datetime_format_short
993             )) {
994             has $property => (
995             is => 'ro',
996             isa => 'Str',
997             init_arg => undef,
998             lazy => 1,
999             builder => "_build_$property",
1000             clearer => "_clear_$property",
1001             );
1002             }
1003              
1004             has '_available_formats' => (
1005             traits => ['Array'],
1006             is => 'ro',
1007             isa => 'ArrayRef',
1008             init_arg => undef,
1009             lazy => 1,
1010             builder => "_build_available_formats",
1011             clearer => "_clear_available_formats",
1012             handles => {
1013             available_formats => 'elements',
1014             },
1015             );
1016              
1017             has 'format_data' => (
1018             is => 'ro',
1019             isa => 'HashRef',
1020             init_arg => undef,
1021             lazy => 1,
1022             builder => "_build_format_data",
1023             clearer => "_clear_format_data",
1024             );
1025              
1026             # default_calendar
1027             foreach my $property (qw(
1028             default_date_format_length default_time_format_length
1029             )) {
1030             has $property => (
1031             is => 'ro',
1032             isa => 'Str',
1033             init_arg => undef,
1034             lazy => 1,
1035             builder => "_build_$property",
1036             writer => "set_$property"
1037             );
1038             }
1039              
1040             =item prefers_24_hour_time()
1041              
1042             Returns a boolean value, true if the locale has a preference
1043             for 24 hour time over 12 hour
1044              
1045             =cut
1046              
1047             has 'prefers_24_hour_time' => (
1048             is => 'ro',
1049             isa => 'Bool',
1050             init_arg => undef,
1051             lazy => 1,
1052             builder => "_build_prefers_24_hour_time",
1053             );
1054              
1055             =item first_day_of_week()
1056              
1057             Returns the numeric representation of the first day of the week
1058             With 0 = Saturday
1059              
1060             =item get_day_period($time)
1061              
1062             This method will calculate the correct
1063             period for a given time and return the period name in
1064             the locale's language and script
1065              
1066             =item format_for($date_time_format)
1067              
1068             This method takes a CLDR datetime format and returns
1069             the localised version of the format.
1070              
1071             =cut
1072              
1073             has 'first_day_of_week' => (
1074             is => 'ro',
1075             isa => 'Int',
1076             init_arg => undef,
1077             lazy => 1,
1078             builder => "_build_first_day_of_week",
1079             );
1080              
1081             has 'likely_subtag' => (
1082             is => 'ro',
1083             isa => __PACKAGE__,
1084             init_arg => undef,
1085             writer => '_set_likely_subtag',
1086             predicate => 'has_likely_subtag',
1087             );
1088              
1089             sub _build_break {
1090             my ($self, $what) = @_;
1091              
1092             my $vars = $self->_build_break_vars($what);
1093             my $rules = $self->_build_break_rules($vars, $what);
1094             return $rules;
1095             }
1096              
1097             sub _build_break_vars {
1098             my ($self, $what) = @_;
1099              
1100             my $name = "${what}_variables";
1101             my @bundles = $self->_find_bundle($name);
1102             my @vars;
1103             foreach my $bundle (reverse @bundles) {
1104             push @vars, @{$bundle->$name};
1105             }
1106              
1107             my %vars = ();
1108             while (my ($name, $value) = (shift @vars, shift @vars)) {
1109             last unless defined $name;
1110             if (! defined $value) {
1111             delete $vars{$name};
1112             next;
1113             }
1114              
1115             $value =~ s{ ( \$ \p{ID_START} \p{ID_CONTINUE}* ) }{$vars{$1}}msxeg;
1116             $vars{$name} = $value;
1117             }
1118              
1119             return \%vars;
1120             }
1121              
1122             sub _build_break_rules {
1123             my ($self, $vars, $what) = @_;
1124              
1125             my $name = "${what}_rules";
1126             my @bundles = $self->_find_bundle($name);
1127              
1128             my %rules;
1129             foreach my $bundle (reverse @bundles) {
1130             %rules = (%rules, %{$bundle->$name});
1131             }
1132              
1133             my @rules;
1134             foreach my $rule_number ( sort { $a <=> $b } keys %rules ) {
1135             # Test for deleted rules
1136             next unless defined $rules{$rule_number};
1137              
1138             $rules{$rule_number} =~ s{ ( \$ \p{ID_START} \p{ID_CONTINUE}* ) }{$vars->{$1}}msxeg;
1139             my ($first, $opp, $second) = split /(×|÷)/, $rules{$rule_number};
1140              
1141             foreach my $operand ($first, $second) {
1142             if ($operand =~ m{ \S }msx) {
1143             $operand = _unicode_to_perl($operand);
1144             }
1145             else {
1146             $operand = '.';
1147             }
1148             }
1149            
1150             no warnings 'deprecated';
1151             push @rules, [qr{$first}msx, qr{$second}msx, ($opp eq '×' ? 1 : 0)];
1152             }
1153              
1154             push @rules, [ '.', '.', 0 ];
1155              
1156             return \@rules;
1157             }
1158              
1159             sub BUILDARGS {
1160             my $self = shift;
1161             my %args;
1162              
1163             # Used for arguments when we call new from our own code
1164             my %internal_args = ();
1165             if (@_ > 1 && ref $_[-1] eq 'HASH') {
1166             %internal_args = %{pop @_};
1167             }
1168              
1169             if (1 == @_ && ! ref $_[0]) {
1170             my ($language, $script, $territory, $variant, $extensions)
1171             = $_[0]=~/^
1172             ([a-zA-Z]+)
1173             (?:[-_]([a-zA-Z]{4}))?
1174             (?:[-_]([a-zA-Z]{2,3}))?
1175             (?:[-_]([a-zA-Z0-9]+))?
1176             (?:[-_]u[_-](.+))?
1177             $/x;
1178              
1179             foreach ($language, $script, $territory, $variant) {
1180             $_ = '' unless defined $_;
1181             }
1182            
1183             %args = (
1184             language_id => $language,
1185             script_id => $script,
1186             territory_id => $territory,
1187             variant_id => $variant,
1188             extensions => $extensions,
1189             );
1190             }
1191              
1192             if (! keys %args ) {
1193             %args = ref $_[0]
1194             ? %{$_[0]}
1195             : @_
1196             }
1197              
1198             # Split up the extensions
1199             if ( defined $args{extensions} && ! ref $args{extensions} ) {
1200             $args{extensions} = {
1201             map {lc}
1202             split /[_-]/, $args{extensions}
1203             };
1204             }
1205              
1206             # Fix casing of args
1207             $args{language_id} = lc $args{language_id} if defined $args{language_id};
1208             $args{script_id} = ucfirst lc $args{script_id} if defined $args{script_id};
1209             $args{territory_id} = uc $args{territory_id} if defined $args{territory_id};
1210             $args{variant_id} = uc $args{variant_id} if defined $args{variant_id};
1211            
1212             # Set up undefined language
1213             $args{language_id} //= 'und';
1214              
1215             $self->SUPER::BUILDARGS(%args, %internal_args);
1216             }
1217              
1218             sub BUILD {
1219             my ($self, $args) = @_;
1220              
1221             # Check that the args are valid
1222             # also check for aliases
1223             $args->{language_id} = $self->language_aliases->{$args->{language_id}}
1224             // $args->{language_id};
1225            
1226             die "Invalid language" if $args->{language_id}
1227             && ! first { $args->{language_id} eq $_ } $self->valid_languages;
1228              
1229             die "Invalid script" if $args->{script_id}
1230             && ! first { ucfirst lc $args->{script_id} eq $_ } $self->valid_scripts;
1231              
1232             die "Invalid territory" if $args->{territory_id}
1233             && ( ! ( first { uc $args->{territory_id} eq $_ } $self->valid_territories )
1234             && ( ! $self->territory_aliases->{$self->{territory_id}} )
1235             );
1236            
1237             die "Invalid variant" if $args->{variant_id}
1238             && ( ! ( first { uc $args->{variant_id} eq $_ } $self->valid_variants )
1239             && ( ! $self->variant_aliases->{lc $self->{variant_id}} )
1240             );
1241            
1242             if ($args->{extensions}) {
1243             my %valid_keys = $self->valid_keys;
1244             my %key_aliases = $self->key_names;
1245             my @keys = keys %{$args->{extensions}};
1246              
1247             foreach my $key ( @keys ) {
1248             my $canonical_key = $key_aliases{$key} if exists $key_aliases{$key};
1249             $canonical_key //= $key;
1250             if ($canonical_key ne $key) {
1251             $args->{extensions}{$canonical_key} = delete $args->{extensions}{$key};
1252             }
1253              
1254             $key = $canonical_key;
1255             die "Invalid extension name" unless exists $valid_keys{$key};
1256             die "Invalid extension value" unless
1257             first { $_ eq $args->{extensions}{$key} } @{$valid_keys{$key}};
1258              
1259             $self->_set_extensions($args->{extensions})
1260             }
1261             }
1262              
1263             # Check for variant aliases
1264             if ($args->{variant_id} && (my $variant_alias = $self->variant_aliases->{lc $self->variant_id})) {
1265             delete $args->{variant_id};
1266             my ($what) = keys %{$variant_alias};
1267             my ($value) = values %{$variant_alias};
1268             $args->{$what} = $value;
1269             }
1270            
1271             # Now set up the module
1272             $self->_build_module;
1273             }
1274              
1275             after 'BUILD' => sub {
1276              
1277             my $self = shift;
1278            
1279             # Fix up likely sub tags
1280            
1281             my $likely_subtags = $self->likely_subtags;
1282             my $likely_subtag;
1283             my ($language_id, $script_id, $territory_id) = ($self->language_id, $self->script_id, $self->territory_id);
1284            
1285             unless ($language_id ne 'und' && $script_id && $territory_id ) {
1286             $likely_subtag = $likely_subtags->{join '_', grep { length() } ($language_id, $script_id, $territory_id)};
1287            
1288             if (! $likely_subtag ) {
1289             $likely_subtag = $likely_subtags->{join '_', $language_id, $territory_id};
1290             }
1291            
1292             if (! $likely_subtag ) {
1293             $likely_subtag = $likely_subtags->{join '_', $language_id, $script_id};
1294             }
1295            
1296             if (! $likely_subtag ) {
1297             $likely_subtag = $likely_subtags->{$language_id};
1298             }
1299            
1300             if (! $likely_subtag ) {
1301             $likely_subtag = $likely_subtags->{join '_', 'und', $script_id};
1302             }
1303             }
1304            
1305             my ($likely_language_id, $likely_script_id, $likely_territory_id);
1306             if ($likely_subtag) {
1307             ($likely_language_id, $likely_script_id, $likely_territory_id) = split /_/, $likely_subtag;
1308             $likely_language_id = $language_id unless $language_id eq 'und';
1309             $likely_script_id = $script_id if length $script_id;
1310             $likely_territory_id = $territory_id if length $territory_id;
1311             $self->_set_likely_subtag(__PACKAGE__->new(join '_',$likely_language_id, $likely_script_id, $likely_territory_id));
1312             }
1313            
1314             # Fix up extension overrides
1315             my $extensions = $self->extensions;
1316             if (exists $extensions->{ca}) {
1317             $self->_set_default_ca(($territory_id // $likely_territory_id) => $extensions->{ca});
1318             }
1319              
1320             if (exists $extensions->{nu}) {
1321             $self->_clear_default_nu;
1322             $self->_set_default_nu($extensions->{nu});
1323             }
1324             };
1325              
1326             use overload
1327             'bool' => sub { 1 },
1328             '""' => sub {shift->id};
1329              
1330             sub _build_id {
1331             my $self = shift;
1332             my $string = lc $self->language_id;
1333              
1334             if ($self->script_id) {
1335             $string.= '_' . ucfirst lc $self->script_id;
1336             }
1337              
1338             if ($self->territory_id) {
1339             $string.= '_' . uc $self->territory_id;
1340             }
1341              
1342             if ($self->variant_id) {
1343             $string.= '_' . uc $self->variant_id;
1344             }
1345              
1346             if (defined $self->extensions) {
1347             $string.= '_u';
1348             foreach my $key (sort keys %{$self->extensions}) {
1349             my $value = $self->extensions->{$key};
1350             $string .= "_${key}_$value";
1351             }
1352             $string =~ s/_u$//;
1353             }
1354              
1355             return $string;
1356             }
1357              
1358             sub _get_english {
1359             my $self = shift;
1360             my $english;
1361             if ($self->language_id eq 'en') {
1362             $english = $self;
1363             }
1364             else {
1365             $english = Locale::CLDR->new('en_Latn_US');
1366             }
1367              
1368             return $english;
1369             }
1370              
1371             sub _build_name {
1372             my $self = shift;
1373              
1374             return $self->_get_english->native_name($self);
1375             }
1376              
1377             sub _build_native_name {
1378             my ($self, $for) = @_;
1379              
1380             return $self->locale_name($for);
1381             }
1382              
1383             sub _build_language {
1384             my $self = shift;
1385              
1386             return $self->_get_english->native_language($self);
1387             }
1388              
1389             sub _build_native_language {
1390             my ($self, $for) = @_;
1391              
1392             return $self->language_name($for) // '';
1393             }
1394              
1395             sub _build_script {
1396             my $self = shift;
1397              
1398             return $self->_get_english->native_script($self);
1399             }
1400              
1401             sub _build_native_script {
1402             my ($self, $for) = @_;
1403              
1404             return $self->script_name($for);
1405             }
1406              
1407             sub _build_territory {
1408             my $self = shift;
1409              
1410             return $self->_get_english->native_territory($self);
1411             }
1412              
1413             sub _build_native_territory {
1414             my ($self, $for) = @_;
1415              
1416             return $self->territory_name($for);
1417             }
1418              
1419             sub _build_variant {
1420             my $self = shift;
1421              
1422             return $self->_get_english->native_variant($self);
1423             }
1424              
1425             sub _build_native_variant {
1426             my ($self, $for) = @_;
1427              
1428             return $self->variant_name($for);
1429             }
1430              
1431             # Method to locate the resource bundle with the required data
1432             sub _find_bundle {
1433             my ($self, $method_name) = @_;
1434             my $id = $self->has_likely_subtag()
1435             ? $self->likely_subtag()->id()
1436             : $self->id();
1437            
1438            
1439             if ($self->method_cache->{$id}{$method_name}) {
1440             return wantarray
1441             ? @{$self->method_cache->{$id}{$method_name}}
1442             : $self->method_cache->{$id}{$method_name}[0];
1443             }
1444              
1445             foreach my $module ($self->module->meta->linearized_isa) {
1446             last if $module eq 'Moose::Object';
1447             if ($module->meta->has_method($method_name)) {
1448             push @{$self->method_cache->{$id}{$method_name}}, $module->new;
1449             }
1450             }
1451              
1452             return unless $self->method_cache->{$id}{$method_name};
1453             return wantarray
1454             ? @{$self->method_cache->{$id}{$method_name}}
1455             : $self->method_cache->{$id}{$method_name}[0];
1456             }
1457              
1458             =back
1459              
1460             =head2 Names
1461              
1462             These methods allow you to pass in a locale, either by C<id> or as a
1463             Locale::CLDR object and return an name formatted in the locale of $self.
1464             If you don't pass in a locale then it will use $self.
1465              
1466             =over 4
1467              
1468             =item locale_name($name)
1469              
1470             Returns the given locale name in the current locale's format. The name can be
1471             a locale id or a locale object or non existent. If a name is not passed in
1472             then the name of the current locale is returned.
1473              
1474             =cut
1475              
1476             sub locale_name {
1477             my ($self, $name) = @_;
1478             $name //= $self;
1479              
1480             my $code = ref $name
1481             ? join ( '_', $name->language_id, $name->territory_id ? $name->territory_id : () )
1482             : $name;
1483            
1484             my @bundles = $self->_find_bundle('display_name_language');
1485              
1486             foreach my $bundle (@bundles) {
1487             my $display_name = $bundle->display_name_language->($code);
1488             return $display_name if defined $display_name;
1489             }
1490              
1491             # $name can be a string or a Locale::CLDR::*
1492             if (! ref $name) {
1493             $name = Locale::CLDR->new($name);
1494             }
1495              
1496             # Now we have to process each individual element
1497             # to pass to the display name pattern
1498             my $language = $self->language_name($name);
1499             my $script = $self->script_name($name);
1500             my $territory = $self->territory_name($name);
1501             my $variant = $self->variant_name($name);
1502              
1503             my $bundle = $self->_find_bundle('display_name_pattern');
1504             return $bundle
1505             ->display_name_pattern($language, $territory, $script, $variant);
1506             }
1507              
1508             =item language_name($language)
1509              
1510             Returns the language name in the current locale's format. The name can be
1511             a locale language id or a locale object or non existent. If a name is not
1512             passed in then the language name of the current locale is returned.
1513              
1514             =cut
1515              
1516             sub language_name {
1517             my ($self, $name) = @_;
1518              
1519             $name //= $self;
1520              
1521             my $code = ref $name ? $name->language_id : eval { Locale::CLDR->new(language_id => $name)->language_id };
1522              
1523             my $language = undef;
1524             my @bundles = $self->_find_bundle('display_name_language');
1525             if ($code) {
1526             foreach my $bundle (@bundles) {
1527             my $display_name = $bundle->display_name_language->($code);
1528             if (defined $display_name) {
1529             $language = $display_name;
1530             last;
1531             }
1532             }
1533             }
1534             # If we don't have a display name for the language we try again
1535             # with the und tag
1536             if (! defined $language ) {
1537             foreach my $bundle (@bundles) {
1538             my $display_name = $bundle->display_name_language->('und');
1539             if (defined $display_name) {
1540             $language = $display_name;
1541             last;
1542             }
1543             }
1544             }
1545              
1546             return $language;
1547             }
1548              
1549             =item all_languages()
1550              
1551             Returns a hash ref keyed on language id of all the languages the system
1552             knows about. The values are the language names for the corresponding id's
1553              
1554             =cut
1555              
1556             sub all_languages {
1557             my $self = shift;
1558              
1559             my @bundles = $self->_find_bundle('display_name_language');
1560             my %languages;
1561             foreach my $bundle (@bundles) {
1562             my $languages = $bundle->display_name_language->();
1563              
1564             # Remove existing languages
1565             delete @{$languages}{keys %languages};
1566              
1567             # Assign new ones to the hash
1568             @languages{keys %$languages} = values %$languages;
1569             }
1570              
1571             return \%languages;
1572             }
1573              
1574             =item script_name($script)
1575              
1576             Returns the script name in the current locale's format. The script can be
1577             a locale script id or a locale object or non existent. If a script is not
1578             passed in then the script name of the current locale is returned.
1579              
1580             =cut
1581              
1582             sub script_name {
1583             my ($self, $name) = @_;
1584             $name //= $self;
1585              
1586             if (! ref $name ) {
1587             $name = eval {__PACKAGE__->new(script_id => $name)};
1588             }
1589              
1590             if ( ref $name && ! $name->script_id ) {
1591             return '';
1592             }
1593              
1594             my $script = undef;
1595             my @bundles = $self->_find_bundle('display_name_script');
1596             if ($name) {
1597             foreach my $bundle (@bundles) {
1598             $script = $bundle->display_name_script->($name->script_id);
1599             if (defined $script) {
1600             last;
1601             }
1602             }
1603             }
1604              
1605             if (! $script) {
1606             foreach my $bundle (@bundles) {
1607             $script = $bundle->display_name_script->('Zzzz');
1608             if (defined $script) {
1609             last;
1610             }
1611             }
1612             }
1613              
1614             return $script;
1615             }
1616              
1617             =item all_scripts()
1618              
1619             Returns a hash ref keyed on script id of all the scripts the system
1620             knows about. The values are the script names for the corresponding id's
1621              
1622             =cut
1623              
1624             sub all_scripts {
1625             my $self = shift;
1626              
1627             my @bundles = $self->_find_bundle('display_name_script');
1628             my %scripts;
1629             foreach my $bundle (@bundles) {
1630             my $scripts = $bundle->display_name_script->();
1631              
1632             # Remove existing scripts
1633             delete @{$scripts}{keys %scripts};
1634              
1635             # Assign new ones to the hash
1636             @scripts{keys %$scripts} = values %$scripts;
1637             }
1638              
1639             return \%scripts;
1640             }
1641              
1642             =item territory_name($territory)
1643              
1644             Returns the territory name in the current locale's format. The territory can be
1645             a locale territory id or a locale object or non existent. If a territory is not
1646             passed in then the territory name of the current locale is returned.
1647              
1648             =cut
1649              
1650             sub territory_name {
1651             my ($self, $name) = @_;
1652             $name //= $self;
1653              
1654             if (! ref $name ) {
1655             $name = eval { __PACKAGE__->new(language_id => 'und', territory_id => $name); };
1656             }
1657              
1658             if ( ref $name && ! $name->territory_id) {
1659             return '';
1660             }
1661              
1662             my $territory = undef;
1663             my @bundles = $self->_find_bundle('display_name_territory');
1664             if ($name) {
1665             foreach my $bundle (@bundles) {
1666             $territory = $bundle->display_name_territory->{$name->territory_id};
1667             if (defined $territory) {
1668             last;
1669             }
1670             }
1671             }
1672              
1673             if (! defined $territory) {
1674             foreach my $bundle (@bundles) {
1675             $territory = $bundle->display_name_territory->{'ZZ'};
1676             if (defined $territory) {
1677             last;
1678             }
1679             }
1680             }
1681              
1682             return $territory;
1683             }
1684              
1685             =item all_territories
1686              
1687             Returns a hash ref keyed on territory id of all the territory the system
1688             knows about. The values are the territory names for the corresponding ids
1689              
1690             =cut
1691              
1692             sub all_territories {
1693             my $self = shift;
1694              
1695             my @bundles = $self->_find_bundle('display_name_territory');
1696             my %territories;
1697             foreach my $bundle (@bundles) {
1698             my $territories = $bundle->display_name_territory;
1699              
1700             # Remove existing territories
1701             delete @{$territories}{keys %territories};
1702              
1703             # Assign new ones to the hash
1704             @territories{keys %$territories} = values %$territories;
1705             }
1706              
1707             return \%territories;
1708             }
1709              
1710             =item variant_name($variant)
1711              
1712             Returns the variant name in the current locale's format. The variant can be
1713             a locale variant id or a locale object or non existent. If a variant is not
1714             passed in then the variant name of the current locale is returned.
1715              
1716             =cut
1717              
1718             sub variant_name {
1719             my ($self, $name) = @_;
1720             $name //= $self;
1721              
1722             if (! ref $name ) {
1723             $name = __PACKAGE__->new(language_id=> 'und', variant_id => $name);
1724             }
1725              
1726             return '' unless $name->variant_id;
1727             my $variant = undef;
1728             if ($name->has_variant) {
1729             my @bundles = $self->_find_bundle('display_name_variant');
1730             foreach my $bundle (@bundles) {
1731             $variant= $bundle->display_name_variant->{$name->variant_id};
1732             if (defined $variant) {
1733             last;
1734             }
1735             }
1736             }
1737              
1738             return $variant // '';
1739             }
1740              
1741             =item key_name($key)
1742              
1743             Returns the key name in the current locale's format. The key must be
1744             a locale key id as a string
1745              
1746             =cut
1747              
1748             sub key_name {
1749             my ($self, $key) = @_;
1750              
1751             $key = lc $key;
1752            
1753             my %key_aliases = $self->key_aliases;
1754             my %key_names = $self->key_names;
1755             my %valid_keys = $self->valid_keys;
1756              
1757             my $alias = $key_aliases{$key} // '';
1758             my $name = $key_names{$key} // '';
1759              
1760             return '' unless exists $valid_keys{$key} || exists $valid_keys{$alias} || exists $valid_keys{$name};
1761             my @bundles = $self->_find_bundle('display_name_key');
1762             foreach my $bundle (@bundles) {
1763             my $return = $bundle->display_name_key->{$key};
1764             $return //= $bundle->display_name_key->{$alias};
1765             $return //= $bundle->display_name_key->{$name};
1766              
1767             return $return if defined $return && length $return;
1768             }
1769              
1770             return ucfirst ($key_names{$name} || $key_names{$alias} || $key_names{$key} || $key);
1771             }
1772              
1773             =item type_name($key, $type)
1774              
1775             Returns the type name in the current locale's format. The key and type must be
1776             a locale key id and type id as a string
1777              
1778             =cut
1779              
1780             sub type_name {
1781             my ($self, $key, $type) = @_;
1782              
1783             $key = lc $key;
1784             $type = lc $type;
1785              
1786             my %key_aliases = $self->key_aliases;
1787             my %valid_keys = $self->valid_keys;
1788             my %key_names = $self->key_names;
1789              
1790             my $alias = $key_aliases{$key} // '';
1791             my $name = $key_names{$key} // '';
1792              
1793             return '' unless exists $valid_keys{$key} || $valid_keys{$alias} || $valid_keys{$name};
1794             return '' unless first { $_ eq $type } @{$valid_keys{$key} || []}, @{$valid_keys{$alias} || []}, @{$valid_keys{$name} || []};
1795              
1796             my @bundles = $self->_find_bundle('display_name_type');
1797             foreach my $bundle (@bundles) {
1798             my $types = $bundle->display_name_type->{$key} // $bundle->display_name_type->{$alias} // $bundle->display_name_type->{$name};
1799             my $type = $types->{$type};
1800             return $type if defined $type;
1801             }
1802              
1803             return '';
1804             }
1805            
1806             =item measurement_system_name($measurement_system)
1807              
1808             Returns the measurement system name in the current locale's format. The measurement system must be
1809             a measurement system id as a string
1810              
1811             =cut
1812            
1813             sub measurement_system_name {
1814             my ($self, $name) = @_;
1815              
1816             # Fix case of code
1817             $name = uc $name;
1818             $name = 'metric' if $name eq 'METRIC';
1819              
1820             my @bundles = $self->_find_bundle('display_name_measurement_system');
1821             foreach my $bundle (@bundles) {
1822             my $system = $bundle->display_name_measurement_system->{$name};
1823             return $system if defined $system;
1824             }
1825              
1826             return '';
1827             }
1828              
1829             =item transform_name($name)
1830              
1831             Returns the transform (transliteration) name in the current locale's format. The transform must be
1832             a transform id as a string
1833              
1834             =cut
1835              
1836             sub transform_name {
1837             my ($self, $name) = @_;
1838              
1839             $name = lc $name;
1840              
1841             my @bundles = $self->_find_bundle('display_name_transform_name');
1842             foreach my $bundle (@bundles) {
1843             my $key = $bundle->display_name_transform_name->{$name};
1844             return $key if length $key;
1845             }
1846              
1847             return '';
1848             }
1849              
1850             =item code_pattern($type, $locale)
1851              
1852             This method formats a language, script or territory name, given as C<$type>
1853             from C<$locale> in a way expected by the current locale. If $locale is
1854             not passed in or is undef() the method uses the current locale.
1855              
1856             =cut
1857              
1858             sub code_pattern {
1859             my ($self, $type, $locale) = @_;
1860             $type = lc $type;
1861              
1862             # If locale is not passed in then we are using ourself
1863             $locale //= $self;
1864              
1865             # If locale is not an object then inflate it
1866             $locale = __PACKAGE__->new($locale) unless blessed $locale;
1867              
1868             return '' unless $type =~ m{ \A (?: language | script | territory ) \z }xms;
1869              
1870             my $method = $type . '_name';
1871             my $substitute = $self->$method($locale);
1872              
1873             my @bundles = $self->_find_bundle('display_name_code_patterns');
1874             foreach my $bundle (@bundles) {
1875             my $text = $bundle->display_name_code_patterns->{$type};
1876             next unless defined $text;
1877             my $match = qr{ \{ 0 \} }xms;
1878             $text=~ s{ $match }{$substitute}gxms;
1879             return $text;
1880             }
1881              
1882             return '';
1883             }
1884              
1885             =item text_orientation($type)
1886              
1887             Gets the text orientation for the locale. Type must be one of
1888             C<lines> or C<characters>
1889              
1890             =cut
1891              
1892             sub text_orientation {
1893             my $self = shift;
1894             my $type = shift;
1895              
1896             my @bundles = $self->_find_bundle('text_orientation');
1897             foreach my $bundle (@bundles) {
1898             my $orientation = $bundle->text_orientation;
1899             next unless defined $orientation;
1900             return $orientation->{$type};
1901             }
1902              
1903             return;
1904             }
1905              
1906             sub _set_casing {
1907             my ($self, $casing, $string) = @_;
1908              
1909             my @words = $self->split_words($string);
1910              
1911             if ($casing eq 'titlecase-firstword') {
1912             # Check to see whether $words[0] is white space or not
1913             my $firstword_location = 0;
1914             if ($words[0] =~ m{ \A \s }msx) {
1915             $firstword_location = 1;
1916             }
1917              
1918             $words[$firstword_location] = ucfirst $words[$firstword_location];
1919             }
1920             elsif ($casing eq 'titlecase-words') {
1921             @words = map{ ucfirst } @words;
1922             }
1923             elsif ($casing eq 'lowercase-words') {
1924             @words = map{ lc } @words;
1925             }
1926              
1927             return join '', @words;
1928             }
1929              
1930             =back
1931              
1932             =head2 Segmentation
1933              
1934             This group of methods allow you to split a string in various ways
1935             Note you need Perl 5.18 or above for this
1936              
1937             =over 4
1938              
1939             =item split_grapheme_clusters($string)
1940              
1941             Splits a string on grapheme clusters using the locale's segmentation rules.
1942             Returns a list of grapheme clusters.
1943              
1944             =cut
1945             # Need 5.18 and above
1946             sub _new_perl {
1947             die "You need Perl 5.18 or later for this functionality\n"
1948             if $^V lt v5.18.0;
1949             }
1950              
1951             sub split_grapheme_clusters {
1952             _new_perl();
1953            
1954             my ($self, $string) = @_;
1955              
1956             my $rules = $self->break_grapheme_cluster;
1957             my @clusters = $self->_split($rules, $string, 1);
1958              
1959             return @clusters;
1960             }
1961              
1962             =item split_words($string)
1963              
1964             Splits a string on word boundaries using the locale's segmentation rules.
1965             Returns a list of words.
1966              
1967             =cut
1968              
1969             sub split_words {
1970             _new_perl();
1971            
1972             my ($self, $string) = @_;
1973              
1974             my $rules = $self->break_word;
1975             my @words = $self->_split($rules, $string);
1976              
1977             return @words;
1978             }
1979              
1980             =item split_sentences($string)
1981              
1982             Splits a string on on all points where a sentence could
1983             end using the locale's segmentation rules. Returns a list
1984             the end of each list element is the point where a sentence
1985             could end.
1986              
1987             =cut
1988              
1989             sub split_sentences {
1990             _new_perl();
1991            
1992             my ($self, $string) = @_;
1993              
1994             my $rules = $self->break_sentence;
1995             my @sentences = $self->_split($rules, $string);
1996              
1997             return @sentences;
1998             }
1999              
2000             =item split_lines($string)
2001              
2002             Splits a string on on all points where a line could
2003             end using the locale's segmentation rules. Returns a list
2004             the end of each list element is the point where a line
2005             could end.
2006              
2007             =cut
2008              
2009             sub split_lines {
2010             _new_perl();
2011            
2012             my ($self, $string) = @_;
2013              
2014             my $rules = $self->break_line;
2015             my @lines = $self->_split($rules, $string);
2016              
2017             return @lines;
2018             }
2019              
2020             sub _split {
2021             my ($self, $rules, $string, $grapheme_split) = @_;
2022              
2023             my @split = (scalar @$rules) x (length($string) - 1);
2024              
2025             pos($string)=0;
2026             # The Unicode Consortium has deprecated LB=Surrogate but the CLDR still
2027             # uses it, at last in this version.
2028             no warnings 'deprecated';
2029             while (length($string) -1 != pos $string) {
2030             my $rule_number = 0;
2031             my $first;
2032             foreach my $rule (@$rules) {
2033             unless( ($first) = $string =~ m{
2034             \G
2035             ($rule->[0])
2036             $rule->[1]
2037             }msx) {
2038             $rule_number++;
2039             next;
2040             }
2041             my $location = pos($string) + length($first) -1;
2042             $split[$location] = $rule_number;
2043            
2044             # If the left hand side was part of a grapheme cluster
2045             # we have to jump past the entire cluster
2046             my $length = length $first;
2047             my ($gc) = $string =~ /\G(\X)/;
2048             $length = (! $grapheme_split && length($gc)) > $length ? length($gc) : $length;
2049             pos($string)+= $length;
2050             last;
2051             }
2052             }
2053              
2054             push @$rules,[undef,undef,1];
2055             @split = map {$rules->[$_][2] ? 1 : 0} @split;
2056             my $count = 0;
2057             my @sections = ('.');
2058             foreach my $split (@split) {
2059             $count++ unless $split;
2060             $sections[$count] .= '.';
2061             }
2062            
2063             my $regex = '(' . join(')(', @sections) . ')';
2064             $regex = qr{ \A $regex \z}msx;
2065             @split = $string =~ $regex;
2066              
2067             return @split;
2068             }
2069              
2070             =back
2071              
2072             =head2 Characters
2073              
2074             =over 4
2075              
2076             =item is_exemplar_character( $type, $character)
2077              
2078             =item is_exemplar_character($character)
2079              
2080             Tests if the given character is used in the locale. There are
2081             three possible types; C<main>, C<auxiliary> and C<punctuation>.
2082             If no type is given C<main> is assumed. Unless the C<index> type
2083             is given you will have to have a Perl version of 5.18 or above
2084             to use this method
2085              
2086             =cut
2087              
2088             sub is_exemplar_character {
2089             my ($self, @parameters) = @_;
2090             unshift @parameters, 'main' if @parameters == 1;
2091              
2092             _new_perl() unless $parameters[0] eq 'index';
2093            
2094             my @bundles = $self->_find_bundle('characters');
2095             foreach my $bundle (@bundles) {
2096             my $characters = $bundle->characters->{lc $parameters[0]};
2097             next unless defined $characters;
2098             return 1 if fc($parameters[1])=~$characters;
2099             }
2100              
2101             return;
2102             }
2103              
2104             =item index_characters()
2105              
2106             Returns an array ref of characters normally used when creating
2107             an index and ordered appropriatly.
2108              
2109             =cut
2110              
2111             sub index_characters {
2112             my $self = shift;
2113              
2114             my @bundles = $self->_find_bundle('characters');
2115             foreach my $bundle (@bundles) {
2116             my $characters = $bundle->characters->{index};
2117             next unless defined $characters;
2118             return $characters;
2119             }
2120             return [];
2121             }
2122              
2123             sub _truncated {
2124             my ($self, $type, @params) = @_;
2125              
2126             my @bundles = $self->_find_bundle('ellipsis');
2127             foreach my $bundle (@bundles) {
2128             my $ellipsis = $bundle->ellipsis->{$type};
2129             next unless defined $ellipsis;
2130             $ellipsis=~s{ \{ 0 \} }{$params[0]}msx;
2131             $ellipsis=~s{ \{ 1 \} }{$params[1]}msx;
2132             return $ellipsis;
2133             }
2134             }
2135              
2136             =back
2137              
2138             =head2 Truncation
2139              
2140             These methods format a string to show where part of the string has been removed
2141              
2142             =over 4
2143              
2144             =item truncated_beginning($string)
2145              
2146             Adds the locale specific marking to show that the
2147             string has been truncated at the beginning.
2148              
2149             =cut
2150              
2151             sub truncated_beginning {
2152             shift->_truncated(initial => @_);
2153             }
2154              
2155             =item truncated_between($string, $string)
2156              
2157             Adds the locale specific marking to show that something
2158             has been truncated between the two strings. Returns a
2159             string comprising of the concatenation of the first string,
2160             the mark and the second string
2161              
2162             =cut
2163              
2164             sub truncated_between {
2165             shift->_truncated(medial => @_);
2166             }
2167              
2168             =item truncated_end($string)
2169              
2170             Adds the locale specific marking to show that the
2171             string has been truncated at the end.
2172              
2173             =cut
2174              
2175             sub truncated_end {
2176             shift->_truncated(final => @_);
2177             }
2178              
2179             =item truncated_word_beginning($string)
2180              
2181             Adds the locale specific marking to show that the
2182             string has been truncated at the beginning. This
2183             should be used in preference to C<truncated_beginning>
2184             when the truncation occurs on a word boundary.
2185              
2186             =cut
2187              
2188             sub truncated_word_beginning {
2189             shift->_truncated('word-initial' => @_);
2190             }
2191              
2192             =item truncated_word_between($string, $string)
2193              
2194             Adds the locale specific marking to show that something
2195             has been truncated between the two strings. Returns a
2196             string comprising of the concatenation of the first string,
2197             the mark and the second string. This should be used in
2198             preference to C<truncated_between> when the truncation
2199             occurs on a word boundary.
2200              
2201             =cut
2202              
2203             sub truncated_word_between {
2204             shift->_truncated('word-medial' => @_);
2205             }
2206              
2207             =item truncated_word_end($string)
2208              
2209             Adds the locale specific marking to show that the
2210             string has been truncated at the end. This should be
2211             used in preference to C<truncated_end> when the
2212             truncation occurs on a word boundary.
2213              
2214             =cut
2215              
2216             sub truncated_word_end {
2217             shift->_truncated('word-final' => @_);
2218             }
2219              
2220             =back
2221              
2222             =head2 Quoting
2223              
2224             =over 4
2225              
2226             =item quote($string)
2227              
2228             Adds the locale's primary quotation marks to the ends of the string.
2229             Also scans the string for paired primary and auxiliary quotation
2230             marks and flips them.
2231              
2232             eg passing C<z “abc” z> to this method for the C<en_GB> locale
2233             gives C<“z ‘abc’ z”>
2234              
2235             =cut
2236              
2237             sub quote {
2238             my ($self, $text) = @_;
2239              
2240             my %quote;
2241             my @bundles = $self->_find_bundle('quote_start');
2242             foreach my $bundle (@bundles) {
2243             my $quote = $bundle->quote_start;
2244             next unless defined $quote;
2245             $quote{start} = $quote;
2246             last;
2247             }
2248              
2249             @bundles = $self->_find_bundle('quote_end');
2250             foreach my $bundle (@bundles) {
2251             my $quote = $bundle->quote_end;
2252             next unless defined $quote;
2253             $quote{end} = $quote;
2254             last;
2255             }
2256              
2257             @bundles = $self->_find_bundle('alternate_quote_start');
2258             foreach my $bundle (@bundles) {
2259             my $quote = $bundle->alternate_quote_start;
2260             next unless defined $quote;
2261             $quote{alternate_start} = $quote;
2262             last;
2263             }
2264              
2265             @bundles = $self->_find_bundle('alternate_quote_end');
2266             foreach my $bundle (@bundles) {
2267             my $quote = $bundle->alternate_quote_end;
2268             next unless defined $quote;
2269             $quote{alternate_end} = $quote;
2270             last;
2271             }
2272              
2273             # Check to see if we need to switch quotes
2274             foreach (qw( start end alternate_start alternate_end)) {
2275             $quote{$_} //= '';
2276             }
2277              
2278             my $from = join ' | ', map {quotemeta} @quote{qw( start end alternate_start alternate_end)};
2279             my %to;
2280             @to{@quote{qw( start end alternate_start alternate_end)}}
2281             = @quote{qw( alternate_start alternate_end start end)};
2282              
2283             my $outer = index($text, $quote{start});
2284             my $inner = index($text, $quote{alternate_start});
2285              
2286             if ($inner == -1 || ($outer > -1 && $inner > -1 && $outer < $inner)) {
2287             $text =~ s{ ( $from ) }{ $to{$1} }msxeg;
2288             }
2289              
2290             return "$quote{start}$text$quote{end}";
2291             }
2292              
2293             =back
2294              
2295             =head2 Miscellaneous
2296              
2297             =over 4
2298              
2299             =item more_information()
2300              
2301             The more information string is one that can be displayed
2302             in an interface to indicate that more information is
2303             available.
2304              
2305             =cut
2306              
2307             sub more_information {
2308             my $self = shift;
2309              
2310             my @bundles = $self->_find_bundle('more_information');
2311             foreach my $bundle (@bundles) {
2312             my $info = $bundle->more_information;
2313             next unless defined $info;
2314             return $info;
2315             }
2316             return '';
2317             }
2318              
2319              
2320             =item measurement()
2321              
2322             Returns the measurement type for the locale
2323              
2324             =cut
2325              
2326             sub measurement {
2327             my $self = shift;
2328            
2329             my $measurement_data = $self->measurement_system;
2330             my $territory = $self->territory_id // '001';
2331            
2332             my $data = $measurement_data->{$territory};
2333            
2334             until (defined $data) {
2335             $territory = $self->territory_contained_by->{$territory};
2336             $data = $measurement_data->{$territory};
2337             }
2338            
2339             return $data;
2340             }
2341              
2342             =item paper()
2343              
2344             Returns the paper type for the locale
2345              
2346             =cut
2347              
2348             sub paper {
2349             my $self = shift;
2350            
2351             my $paper_size = $self->paper_size;
2352             my $territory = $self->territory_id // '001';
2353            
2354             my $data = $paper_size->{$territory};
2355            
2356             until (defined $data) {
2357             $territory = $self->territory_contained_by->{$territory};
2358             $data = $paper_size->{$territory};
2359             }
2360            
2361             return $data;
2362             }
2363              
2364             =back
2365              
2366             =head2 Units
2367              
2368             =over 4
2369              
2370             =item all_units()
2371              
2372             Returns a list of all the unit identifiers for the locale
2373              
2374             =cut
2375              
2376             sub all_units {
2377             my $self = shift;
2378             my @bundles = $self->_find_bundle('units');
2379            
2380             my %units;
2381             foreach my $bundle (reverse @bundles) {
2382             %units = %units, $bundle->units;
2383             }
2384            
2385             return keys %units;
2386             }
2387              
2388             =item unit($number, $unit, $width)
2389              
2390             Returns the localised string for the given number and unit formatted for the
2391             required width. The number must not be the localized version of the number.
2392             The returned string will be in the locale's format, including the number.
2393              
2394             =cut
2395              
2396             sub unit {
2397             my ($self, $number, $what, $type) = @_;
2398             $type //= 'long';
2399            
2400             my $plural = $self->plural($number);
2401            
2402             my @bundles = $self->_find_bundle('units');
2403             my $format;
2404             foreach my $bundle (@bundles) {
2405             if (exists $bundle->units()->{$type}{$what}{$plural}) {
2406             $format = $bundle->units()->{$type}{$what}{$plural};
2407             last;
2408             }
2409            
2410             if (exists $bundle->units()->{$type}{$what}{other}) {
2411             $format = $bundle->units()->{$type}{$what}{other};
2412             last;
2413             }
2414             }
2415            
2416             # Check for aliases
2417             unless ($format) {
2418             my $original_type = $type;
2419             my @aliases = $self->_find_bundle('unit_alias');
2420             foreach my $alias (@aliases) {
2421             $type = $alias->unit_alias()->{$original_type};
2422             next unless $type;
2423             foreach my $bundle (@bundles) {
2424             if (exists $bundle->units()->{$type}{$what}{$plural}) {
2425             $format = $bundle->units()->{$type}{$what}{$plural};
2426             last;
2427             }
2428            
2429             if (exists $bundle->units()->{$type}{$what}{other}) {
2430             $format = $bundle->units()->{$type}{$what}{other};
2431             last;
2432             }
2433             }
2434             }
2435             $type = $original_type;
2436             }
2437            
2438             # Check for a compound unit that we don't specifically have
2439             if (! $format && (my ($dividend, $divisor) = $what =~ /^(.+)-per-(.+)$/)) {
2440             return $self->_unit_compound($number, $dividend, $divisor, $type);
2441             }
2442            
2443             $number = $self->format_number($number);
2444             return $number unless $format;
2445            
2446             $format =~ s/\{0\}/$number/g;
2447            
2448             return $format;
2449             }
2450              
2451             sub _unit_compound {
2452             my ($self, $number, $dividend_what, $divisor_what, $type) = @_;
2453            
2454             $type //= 'long';
2455            
2456             my $dividend = $self->unit($number, $dividend_what, $type);
2457             my $divisor = $self->unit(1, $divisor_what, $type);
2458            
2459             my $one = $self->format_number(1);
2460             $divisor =~ s/\s*$one\s*//;
2461              
2462             my @bundles = $self->_find_bundle('units');
2463             my $format;
2464             foreach my $bundle (@bundles) {
2465             if (exists $bundle->units()->{$type}{per}{''}) {
2466             $format = $bundle->units()->{$type}{per}{''};
2467             last;
2468             }
2469             }
2470              
2471             # Check for aliases
2472             unless ($format) {
2473             my $original_type = $type;
2474             my @aliases = $self->_find_bundle('unit_alias');
2475             foreach my $alias (@aliases) {
2476             $type = $alias->unit_alias()->{$original_type};
2477             foreach my $bundle (@bundles) {
2478             if (exists $bundle->units()->{$type}{per}{''}) {
2479             $format = $bundle->units()->{$type}{per}{''};
2480             last;
2481             }
2482             }
2483             }
2484             }
2485            
2486             $format =~ s/\{0\}/$dividend/g;
2487             $format =~ s/\{1\}/$divisor/g;
2488            
2489             return $format;
2490             }
2491              
2492             =item duration_unit($format, @data)
2493              
2494             This method formats a duration. The format must be one of
2495             C<hm>, C<hms> or C<ms> corresponding to C<hour minute>,
2496             C<hour minute second> and C<minute second> respectively.
2497             The data must correspond to the given format.
2498              
2499             =cut
2500              
2501             sub duration_unit {
2502             # data in hh,mm; hh,mm,ss or mm,ss
2503             my ($self, $format, @data) = @_;
2504            
2505             my $bundle = $self->_find_bundle('duration_units');
2506             my $parsed = $bundle->duration_units()->{$format};
2507            
2508             my $num_format = '#';
2509             foreach my $entry ( qr/(hh?)/, qr/(mm?)/, qr/(ss?)/) {
2510             $num_format = '00' if $parsed =~ s/$entry/$self->format_number(shift(@data), $num_format)/e;
2511             }
2512            
2513             return $parsed;
2514             }
2515              
2516             =back
2517              
2518             =head2 Yes or No?
2519              
2520             =over 4
2521              
2522             =item is_yes($string)
2523              
2524             Returns true if the passed in string matches the locale's
2525             idea of a string designating yes. Note that under POSIX
2526             rules unless the locale's word for yes starts with C<Y>
2527             (U+0079) then a single 'y' will also be accepted as yes.
2528             The string will be matched case insensitive.
2529              
2530             =cut
2531              
2532             sub is_yes {
2533             my ($self, $test_str) = @_;
2534            
2535             my $bundle = $self->_find_bundle('yesstr');
2536             return $test_str =~ $bundle->yesstr ? 1 : 0;
2537             }
2538              
2539             =item is_no($string)
2540              
2541             Returns true if the passed in string matches the locale's
2542             idea of a string designating no. Note that under POSIX
2543             rules unless the locale's word for no starts with C<n>
2544             (U+006E) then a single 'n' will also be accepted as no
2545             The string will be matched case insensitive.
2546              
2547             =cut
2548              
2549             sub is_no {
2550             my ($self, $test_str) = @_;
2551            
2552             my $bundle = $self->_find_bundle('nostr');
2553             return $test_str =~ $bundle->nostr ? 1 : 0;
2554             }
2555              
2556             =back
2557              
2558             =head2 Transliteration
2559              
2560             This method requires Perl version 5.18 or above to use
2561              
2562             =over 4
2563              
2564             =item transform(from => $from, to => $to, variant => $variant, text => $text)
2565              
2566             This method returns the transliterated string of C<text> from script C<from>
2567             to script C<to> using variant C<variant>. If C<from> is not given then the
2568             current locale's script is used. If C<text> is not given then it defaults to an
2569             empty string. The C<variant> is optional.
2570              
2571             =cut
2572              
2573             sub transform {
2574             _new_perl();
2575            
2576             my ($self, %params) = @_;
2577            
2578             my $from = $params{from} // $self;
2579             my $to = $params{to};
2580             my $variant = $params{variant} // 'Any';
2581             my $text = $params{text} // '';
2582            
2583             ($from, $to) = map {ref $_ ? $_->likely_script() : $_} ($from, $to);
2584             $_ = ucfirst(lc $_) foreach ($from, $to, $variant);
2585            
2586             my $package = __PACKAGE__ . "::Transformations::${variant}::${from}::${to}";
2587             eval { Class::Load::load_class($package); };
2588             warn $@ if $@;
2589             return $text if $@; # Can't load transform module so return original text
2590             use feature 'state';
2591             state $transforms;
2592             $transforms->{$variant}{$from}{$to} //= $package->new();
2593             my $rules = $transforms->{$variant}{$from}{$to}->transforms();
2594            
2595             # First get the filter rule
2596             my $filter = $rules->[0];
2597            
2598             # Break up the input on the filter
2599             my @text;
2600             pos($text) = 0;
2601             while (pos($text) < length($text)) {
2602             my $characters = '';
2603             while (my ($char) = $text =~ /($filter)/) {
2604             $characters .= $char;
2605             pos($text) = pos($text) + length $char;
2606             }
2607             push @text, $characters;
2608             last unless pos($text) < length $text;
2609            
2610             $characters = '';
2611             while ($text !~ /$filter/) {
2612             my ($char) = $text =~ /\G(\X)/;
2613             $characters .= $char;
2614             pos($text) = pos($text) + length $char;
2615             }
2616             push @text, $characters;
2617             }
2618            
2619             my $to_transform = 1;
2620            
2621             foreach my $characters (@text) {
2622             if ($to_transform) {
2623             foreach my $rule (@$rules[1 .. @$rules -1 ]) {
2624             if ($rule->{type} eq 'transform') {
2625             $characters = $self->_transformation_transform($characters, $rule->{data}, $variant);
2626             }
2627             else {
2628             $characters = $self->_transform_convert($characters, $rule->{data});
2629             }
2630             }
2631             }
2632             $to_transform = ! $to_transform;
2633             }
2634            
2635             return join '', @text;
2636             }
2637              
2638             sub _transformation_transform {
2639             my ($self, $text, $rules, $variant) = @_;
2640            
2641             foreach my $rule (@$rules) {
2642             for (lc $rule->{to}) {
2643             if ($_ eq 'nfc') {
2644             $text = Unicode::Normalize::NFC($text);
2645             }
2646             elsif($_ eq 'nfd') {
2647             $text = Unicode::Normalize::NFD($text);
2648             }
2649             elsif($_ eq 'nfkd') {
2650             $text = Unicode::Normalize::NFKD($text);
2651             }
2652             elsif($_ eq 'nfkc') {
2653             $text = Unicode::Normalize::NFKC($text);
2654             }
2655             elsif($_ eq 'lower') {
2656             $text = lc($text);
2657             }
2658             elsif($_ eq 'upper') {
2659             $text = uc($text);
2660             }
2661             elsif($_ eq 'title') {
2662             $text =~ s/(\X)/\u$1/g;
2663             }
2664             elsif($_ eq 'null') {
2665             }
2666             elsif($_ eq 'remove') {
2667             $text = '';
2668             }
2669             else {
2670             $text = $self->transform($text, $variant, $rule->{from}, $rule->to);
2671             }
2672             }
2673             }
2674             return $text;
2675             }
2676              
2677             sub _transform_convert {
2678             my ($self, $text, $rules) = @_;
2679            
2680             pos($text) = 0; # Make sure we start scanning at the beginning of the text
2681            
2682             CHARACTER: while (pos($text) < length($text)) {
2683             foreach my $rule (@$rules) {
2684             next if length $rule->{before} && $text !~ /$rule->{before}\G/;
2685             my $regex = $rule->{replace};
2686             $regex .= '(' . $rule->{after} . ')' if length $rule->{after};
2687             my $result = 'q(' . $rule->{result} . ')';
2688             $result .= '. $1' if length $rule->{after};
2689             if ($text =~ s/\G$regex/eval $result/e) {
2690             pos($text) += length($rule->{result}) - $rule->{revisit};
2691             next CHARACTER;
2692             }
2693             }
2694            
2695             pos($text)++;
2696             }
2697            
2698             return $text;
2699             }
2700              
2701             =back
2702              
2703             =head2 Lists
2704              
2705             =over 4
2706              
2707             =item list(@data)
2708              
2709             Returns C<data> as a string formatted by the locales idea of producing a list
2710             of elements. What is returned can be effected by the locale and the number
2711             of items in C<data>. Note that C<data> can contain 0 or more items.
2712              
2713             =cut
2714              
2715             sub list {
2716             my ($self, @data) = @_;
2717            
2718             # Short circuit on 0 or 1 entries
2719             return '' unless @data;
2720             return $data[0] if 1 == @data;
2721            
2722             my @bundles = $self->_find_bundle('listPatterns');
2723            
2724             my %list_data;
2725             foreach my $bundle (reverse @bundles) {
2726             %list_data = %{$bundle->listPatterns};
2727             }
2728            
2729             if (my $pattern = $list_data{scalar @data}) {
2730             $pattern=~s/\{([0-9]+)\}/$data[$1]/eg;
2731             return $pattern;
2732             }
2733            
2734             my ($start, $middle, $end) = @list_data{qw( start middle end )};
2735            
2736             # First do the end
2737             my $pattern = $end;
2738             $pattern=~s/\{1\}/pop @data/e;
2739             $pattern=~s/\{0\}/pop @data/e;
2740            
2741             # If there is any data left do the middle
2742             while (@data > 1) {
2743             my $current = $pattern;
2744             $pattern = $middle;
2745             $pattern=~s/\{1\}/$current/;
2746             $pattern=~s/\{0\}/pop @data/e;
2747             }
2748            
2749             # Now do the start
2750             my $current = $pattern;
2751             $pattern = $start;
2752             $pattern=~s/\{1\}/$current/;
2753             $pattern=~s/\{0\}/pop @data/e;
2754            
2755             return $pattern;
2756             }
2757              
2758             =back
2759              
2760             =head2 Pluralisation
2761              
2762             =over 4
2763              
2764             =item plural($number)
2765              
2766             This method takes a number and uses the locale's pluralisation
2767             rules to calculate the type of pluralisation required for
2768             units, currencies and other data that changes depending on
2769             the plural state of the number
2770              
2771             =item plural_range($start, $end)
2772              
2773             This method returns the plural type for the range $start to $end
2774             $start and $end can either be numbers or one of the plural types
2775             C<zero one two few many other>
2776              
2777             =cut
2778              
2779             sub _clear_calendar_data {
2780             my $self = shift;
2781              
2782             foreach my $property (qw(
2783             month_format_wide month_format_abbreviated month_format_narrow
2784             month_stand_alone_wide month_stand_alone_abbreviated
2785             month_stand_alone_narrow day_format_wide day_format_abbreviated
2786             day_format_narrow day_stand_alone_wide day_stand_alone_abreviated
2787             day_stand_alone_narrow quater_format_wide quater_format_abbreviated
2788             quater_format_narrow quater_stand_alone_wide
2789             quater_stand_alone_abreviated quater_stand_alone_narrow
2790             am_pm_wide am_pm_abbreviated am_pm_narrow am_pm_format_wide
2791             am_pm_format_abbreviated am_pm_format_narrow am_pm_stand_alone_wide
2792             am_pm_stand_alone_abbreviated am_pm_stand_alone_narrow era_wide
2793             era_abbreviated era_narrow date_format_full date_format_long date_format_medium
2794             date_format_short time_format_full
2795             time_format_long time_format_medium time_format_short
2796             datetime_format_full datetime_format_long
2797             datetime_format_medium datetime_format_short
2798             available_formats format_data
2799             )) {
2800             my $method = "_clear_$property";
2801             $self->$method;
2802             }
2803             }
2804              
2805             sub _build_any_month {
2806             my ($self, $type, $width) = @_;
2807             my $default_calendar = $self->default_calendar();
2808             my @bundles = $self->_find_bundle('calendar_months');
2809             BUNDLES: {
2810             foreach my $bundle (@bundles) {
2811             my $months = $bundle->calendar_months;
2812             if (exists $months->{$default_calendar}{alias}) {
2813             $default_calendar = $months->{$default_calendar}{alias};
2814             redo BUNDLES;
2815             }
2816              
2817             if (exists $months->{$default_calendar}{$type}{$width}{alias}) {
2818             ($type, $width) = @{$months->{$default_calendar}{$type}{$width}{alias}}{qw(context type)};
2819             redo BUNDLES;
2820             }
2821            
2822             my $result = $months->{$default_calendar}{$type}{$width}{nonleap};
2823             return $result if defined $result;
2824             }
2825             }
2826            
2827             return [];
2828             }
2829              
2830             sub _build_month_format_wide {
2831             my $self = shift;
2832             my ($type, $width) = (qw(format wide));
2833            
2834             return $self->_build_any_month($type, $width);
2835             }
2836              
2837             sub _build_month_format_abbreviated {
2838             my $self = shift;
2839             my ($type, $width) = (qw(format abbreviated));
2840            
2841             return $self->_build_any_month($type, $width);
2842             }
2843              
2844             sub _build_month_format_narrow {
2845             my $self = shift;
2846             my ($type, $width) = (qw(format narrow));
2847            
2848             return $self->_build_any_month($type, $width);
2849             }
2850              
2851             sub _build_month_stand_alone_wide {
2852             my $self = shift;
2853             my ($type, $width) = ('stand-alone', 'wide');
2854            
2855             return $self->_build_any_month($type, $width);
2856             }
2857              
2858             sub _build_month_stand_alone_abbreviated {
2859             my $self = shift;
2860             my ($type, $width) = ('stand-alone', 'abbreviated');
2861            
2862             return $self->_build_any_month($type, $width);
2863             }
2864              
2865             sub _build_month_stand_alone_narrow {
2866             my $self = shift;
2867             my ($type, $width) = ('stand-alone', 'narrow');
2868            
2869             return $self->_build_any_month($type, $width);
2870             }
2871              
2872             sub _build_any_day {
2873             my ($self, $type, $width) = @_;
2874            
2875             my $default_calendar = $self->default_calendar();
2876              
2877             my @bundles = $self->_find_bundle('calendar_days');
2878             BUNDLES: {
2879             foreach my $bundle (@bundles) {
2880             my $days= $bundle->calendar_days;
2881            
2882             if (exists $days->{$default_calendar}{alias}) {
2883             $default_calendar = $days->{$default_calendar}{alias};
2884             redo BUNDLES;
2885             }
2886              
2887             if (exists $days->{$default_calendar}{$type}{$width}{alias}) {
2888             ($type, $width) = @{$days->{$default_calendar}{$type}{$width}{alias}}{qw(context type)};
2889             redo BUNDLES;
2890             }
2891             my $result = $days->{$default_calendar}{$type}{$width};
2892             return [ @{$result}{qw( mon tue wed thu fri sat sun )} ] if keys %$result;
2893             }
2894             }
2895              
2896             return [];
2897             }
2898              
2899             sub _build_day_format_wide {
2900             my $self = shift;
2901             my ($type, $width) = (qw(format wide));
2902            
2903             return $self->_build_any_day($type, $width);
2904             }
2905              
2906             sub _build_day_format_abbreviated {
2907             my $self = shift;
2908             my ($type, $width) = (qw(format abbreviated));
2909            
2910             return $self->_build_any_day($type, $width);
2911             }
2912              
2913             sub _build_day_format_narrow {
2914             my $self = shift;
2915             my ($type, $width) = (qw(format narrow));
2916            
2917             return $self->_build_any_day($type, $width);
2918             }
2919              
2920             sub _build_day_stand_alone_wide {
2921             my $self = shift;
2922             my ($type, $width) = ('stand-alone', 'wide');
2923            
2924             return $self->_build_any_day($type, $width);
2925             }
2926              
2927             sub _build_day_stand_alone_abbreviated {
2928             my $self = shift;
2929             my ($type, $width) = ('stand-alone', 'abbreviated');
2930              
2931             return $self->_build_any_day($type, $width);
2932             }
2933              
2934             sub _build_day_stand_alone_narrow {
2935             my $self = shift;
2936             my ($type, $width) = ('stand-alone', 'narrow');
2937            
2938             return $self->_build_any_day($type, $width);
2939             }
2940              
2941             sub _build_any_quarter {
2942             my ($self, $type, $width) = @_;
2943            
2944             my $default_calendar = $self->default_calendar();
2945              
2946             my @bundles = $self->_find_bundle('calendar_quarters');
2947             BUNDLES: {
2948             foreach my $bundle (@bundles) {
2949             my $quarters= $bundle->calendar_quarters;
2950            
2951             if (exists $quarters->{$default_calendar}{alias}) {
2952             $default_calendar = $quarters->{$default_calendar}{alias};
2953             redo BUNDLES;
2954             }
2955              
2956             if (exists $quarters->{$default_calendar}{$type}{$width}{alias}) {
2957             ($type, $width) = @{$quarters->{$default_calendar}{$type}{$width}{alias}}{qw(context type)};
2958             redo BUNDLES;
2959             }
2960            
2961             my $result = $quarters->{$default_calendar}{$type}{$width};
2962             return [ @{$result}{qw( 0 1 2 3 )} ] if keys %$result;
2963             }
2964             }
2965              
2966             return [];
2967             }
2968              
2969             sub _build_quarter_format_wide {
2970             my $self = shift;
2971             my ($type, $width) = (qw( format wide ));
2972            
2973             return $self->_build_any_quarter($type, $width);
2974             }
2975              
2976             sub _build_quarter_format_abbreviated {
2977             my $self = shift;
2978             my ($type, $width) = (qw(format abbreviated));
2979              
2980             return $self->_build_any_quarter($type, $width);
2981             }
2982              
2983             sub _build_quarter_format_narrow {
2984             my $self = shift;
2985             my ($type, $width) = (qw(format narrow));
2986              
2987             return $self->_build_any_quarter($type, $width);
2988             }
2989              
2990             sub _build_quarter_stand_alone_wide {
2991             my $self = shift;
2992             my ($type, $width) = ('stand-alone', 'wide');
2993              
2994             return $self->_build_any_quarter($type, $width);
2995             }
2996              
2997             sub _build_quarter_stand_alone_abbreviated {
2998             my $self = shift;
2999             my ($type, $width) = ('stand-alone', 'abbreviated');
3000            
3001             return $self->_build_any_quarter($type, $width);
3002             }
3003              
3004             sub _build_quarter_stand_alone_narrow {
3005             my $self = shift;
3006             my ($type, $width) = ('stand-alone', 'narrow');
3007              
3008             return $self->_build_any_quarter($type, $width);
3009             }
3010              
3011             sub get_day_period {
3012             # Time in hhmm
3013             my ($self, $time) = @_;
3014            
3015             my $default_calendar = $self->default_calendar();
3016            
3017             my $bundle = $self->_find_bundle('day_period_data');
3018            
3019             my $day_period = $bundle->day_period_data;
3020             $day_period = $self->$day_period($default_calendar, $time);
3021            
3022             my $am_pm = $self->am_pm_format_abbreviated;
3023            
3024             return $am_pm->{$day_period};
3025             }
3026              
3027             sub _build_any_am_pm {
3028             my ($self, $type, $width) = @_;
3029              
3030             my $default_calendar = $self->default_calendar();
3031             my @result;
3032             my @bundles = $self->_find_bundle('day_periods');
3033             my %return;
3034              
3035             BUNDLES: {
3036             foreach my $bundle (@bundles) {
3037             my $am_pm = $bundle->day_periods;
3038            
3039             if (exists $am_pm->{$default_calendar}{alias}) {
3040             $default_calendar = $am_pm->{$default_calendar}{alias};
3041             redo BUNDLES;
3042             }
3043              
3044             if (exists $am_pm->{$default_calendar}{$type}{alias}) {
3045             $type = $am_pm->{$default_calendar}{$type}{alias};
3046             redo BUNDLES;
3047             }
3048            
3049             if (exists $am_pm->{$default_calendar}{$type}{$width}{alias}) {
3050             $width = $am_pm->{$default_calendar}{$type}{$width}{alias};
3051             redo BUNDLES;
3052             }
3053            
3054             my $result = $am_pm->{$default_calendar}{$type}{$width};
3055            
3056             foreach (keys %$result) {
3057             $return{$_} = $result->{$_} unless exists $return{$_};
3058             }
3059             }
3060             }
3061              
3062             return \%return;
3063             }
3064              
3065             # The first 3 are to link in with Date::Time::Locale
3066             sub _build_am_pm_wide {
3067             my $self = shift;
3068             my ($type, $width) = (qw( format wide ));
3069            
3070             my $result = $self->_build_any_am_pm($type, $width);
3071            
3072             return [ @$result{qw( am pm )} ];
3073             }
3074              
3075             sub _build_am_pm_abbreviated {
3076             my $self = shift;
3077             my ($type, $width) = (qw( format abbreviated ));
3078              
3079             my $result = $self->_build_any_am_pm($type, $width);
3080            
3081             return [ @$result{qw( am pm )} ];
3082             }
3083              
3084             sub _build_am_pm_narrow {
3085             my $self = shift;
3086             my ($type, $width) = (qw( format narrow ));
3087            
3088             my $result = $self->_build_any_am_pm($type, $width);
3089            
3090             return [ @$result{qw( am pm )} ];
3091             }
3092              
3093             # Now we do the full set of data
3094             sub _build_am_pm_format_wide {
3095             my $self = shift;
3096             my ($type, $width) = (qw( format wide ));
3097            
3098             return $self->_build_any_am_pm($type, $width);
3099             }
3100              
3101             sub _build_am_pm_format_abbreviated {
3102             my $self = shift;
3103             my ($type, $width) = (qw( format abbreviated ));
3104              
3105             return $self->_build_any_am_pm($type, $width);
3106             }
3107              
3108             sub _build_am_pm_format_narrow {
3109             my $self = shift;
3110             my ($type, $width) = (qw( format narrow ));
3111            
3112             return $self->_build_any_am_pm($type, $width);
3113             }
3114              
3115             sub _build_am_pm_stand_alone_wide {
3116             my $self = shift;
3117             my ($type, $width) = ('stand-alone', 'wide');
3118            
3119             return $self->_build_any_am_pm($type, $width);
3120             }
3121              
3122             sub _build_am_pm_stand_alone_abbreviated {
3123             my $self = shift;
3124             my ($type, $width) = ('stand-alone', 'abbreviated');
3125              
3126             return $self->_build_any_am_pm($type, $width);
3127             }
3128              
3129             sub _build_am_pm_stand_alone_narrow {
3130             my $self = shift;
3131             my ($type, $width) = ('stand-alone', 'narrow');
3132            
3133             return $self->_build_any_am_pm($type, $width);
3134             }
3135              
3136             sub _build_any_era {
3137             my ($self, $width) = @_;
3138              
3139             my $default_calendar = $self->default_calendar();
3140             my @bundles = $self->_find_bundle('eras');
3141             BUNDLES: {
3142             foreach my $bundle (@bundles) {
3143             my $eras = $bundle->eras;
3144            
3145             if (exists $eras->{$default_calendar}{alias}) {
3146             $default_calendar = $eras->{$default_calendar}{alias};
3147             redo BUNDLES;
3148             }
3149              
3150             if (exists $eras->{$default_calendar}{$width}{alias}) {
3151             $width = $eras->{$default_calendar}{$width}{alias};
3152             redo BUNDLES;
3153             }
3154            
3155             my $result = $eras->{$default_calendar}{$width};
3156            
3157             my @result = map {$result->{$_}} sort { $a <=> $b } keys %$result;
3158            
3159             return \@result if keys %$result;
3160             }
3161             }
3162              
3163             return [];
3164             }
3165            
3166             # The next three are for DateDime::Locale
3167             sub _build_era_wide {
3168             my $self = shift;
3169             my ($width) = (qw( wide ));
3170              
3171             my $result = $self->_build_any_era($width);
3172            
3173             return [@$result[0, 1]];
3174             }
3175              
3176             sub _build_era_abbreviated {
3177             my $self = shift;
3178             my ($width) = (qw( abbreviated ));
3179              
3180             my $result = $self->_build_any_era($width);
3181            
3182             return [@$result[0, 1]];
3183             }
3184              
3185             sub _build_era_narrow {
3186             my $self = shift;
3187             my ($width) = (qw( narrow ));
3188              
3189             my $result = $self->_build_any_era($width);
3190            
3191             return [@$result[0, 1]];
3192             }
3193              
3194             # Now get all the era data
3195             sub _build_era_format_wide {
3196             my $self = shift;
3197             my ($width) = (qw( wide ));
3198              
3199             return $self->_build_any_era($width);
3200             }
3201              
3202             sub _build_era_format_abbreviated {
3203             my $self = shift;
3204             my ($width) = (qw( abbreviated ));
3205              
3206             return $self->_build_any_era($width);
3207             }
3208              
3209             sub _build_era_format_narrow {
3210             my $self = shift;
3211             my ($type, $width) = (qw( narrow ));
3212              
3213             return $self->_build_any_era($type, $width);
3214             }
3215              
3216             *_build_era_stand_alone_wide = \&_build_era_format_wide;
3217             *_build_era_stand_alone_abbreviated = \&_build_era_format_abbreviated;
3218             *_build_era_stand_alone_narrow = \&_build_era_format_narrow;
3219              
3220             sub _build_any_date_format {
3221             my ($self, $width) = @_;
3222             my $default_calendar = $self->default_calendar();
3223            
3224             my @bundles = $self->_find_bundle('date_formats');
3225              
3226             BUNDLES: {
3227             foreach my $bundle (@bundles) {
3228             my $date_formats = $bundle->date_formats;
3229             if (exists $date_formats->{alias}) {
3230             $default_calendar = $date_formats->{alias};
3231             redo BUNDLES;
3232             }
3233            
3234             my $result = $date_formats->{$default_calendar}{$width};
3235             return $result if $result;
3236             }
3237             }
3238             return '';
3239             }
3240              
3241             sub _build_date_format_full {
3242             my $self = shift;
3243            
3244             my ($width) = ('full');
3245             return $self->_build_any_date_format($width);
3246             }
3247              
3248             sub _build_date_format_long {
3249             my $self = shift;
3250            
3251             my ($width) = ('long');
3252             return $self->_build_any_date_format($width);
3253             }
3254              
3255             sub _build_date_format_medium {
3256             my $self = shift;
3257            
3258             my ($width) = ('medium');
3259             return $self->_build_any_date_format($width);
3260             }
3261              
3262             sub _build_date_format_short {
3263             my $self = shift;
3264            
3265             my ($width) = ('short');
3266             return $self->_build_any_date_format($width);
3267             }
3268              
3269             sub _build_any_time_format {
3270             my ($self, $width) = @_;
3271             my $default_calendar = $self->default_calendar();
3272            
3273             my @bundles = $self->_find_bundle('time_formats');
3274              
3275             BUNDLES: {
3276             foreach my $bundle (@bundles) {
3277             my $time_formats = $bundle->time_formats;
3278             if (exists $time_formats->{$default_calendar}{alias}) {
3279             $default_calendar = $time_formats->{$default_calendar}{alias};
3280             redo BUNDLES;
3281             }
3282            
3283             my $result = $time_formats->{$default_calendar}{$width};
3284             return $result if $result;
3285             }
3286             }
3287             return '';
3288             }
3289              
3290             sub _build_time_format_full {
3291             my $self = shift;
3292             my $width = 'full';
3293            
3294             return $self->_build_any_time_format($width);
3295             }
3296              
3297             sub _build_time_format_long {
3298             my $self = shift;
3299            
3300             my $width = 'long';
3301             return $self->_build_any_time_format($width);
3302             }
3303              
3304             sub _build_time_format_medium {
3305             my $self = shift;
3306            
3307             my $width = 'medium';
3308             return $self->_build_any_time_format($width);
3309             }
3310              
3311             sub _build_time_format_short {
3312             my $self = shift;
3313            
3314             my $width = 'short';
3315             return $self->_build_any_time_format($width);
3316             }
3317              
3318             sub _build_any_datetime_format {
3319             my ($self, $width) = @_;
3320             my $default_calendar = $self->default_calendar();
3321            
3322             my @bundles = $self->_find_bundle('datetime_formats');
3323              
3324             BUNDLES: {
3325             foreach my $bundle (@bundles) {
3326             my $datetime_formats = $bundle->datetime_formats;
3327             if (exists $datetime_formats->{$default_calendar}{alias}) {
3328             $default_calendar = $datetime_formats->{$default_calendar}{alias};
3329             redo BUNDLES;
3330             }
3331            
3332             my $result = $datetime_formats->{$default_calendar}{$width};
3333             return $result if $result;
3334             }
3335             }
3336            
3337             return '';
3338             }
3339              
3340             sub _build_datetime_format_full {
3341             my $self = shift;
3342            
3343             my $width = 'full';
3344             my $format = $self->_build_any_datetime_format($width);
3345            
3346             my $date = $self->_build_any_date_format($width);
3347             my $time = $self->_build_any_time_format($width);
3348            
3349             $format =~ s/\{0\}/$time/;
3350             $format =~ s/\{1\}/$date/;
3351            
3352             return $format;
3353             }
3354              
3355             sub _build_datetime_format_long {
3356             my $self = shift;
3357            
3358             my $width = 'long';
3359             my $format = $self->_build_any_datetime_format($width);
3360            
3361             my $date = $self->_build_any_date_format($width);
3362             my $time = $self->_build_any_time_format($width);
3363            
3364             $format =~ s/\{0\}/$time/;
3365             $format =~ s/\{1\}/$date/;
3366            
3367             return $format;
3368             }
3369              
3370             sub _build_datetime_format_medium {
3371             my $self = shift;
3372            
3373             my $width = 'medium';
3374             my $format = $self->_build_any_datetime_format($width);
3375            
3376             my $date = $self->_build_any_date_format($width);
3377             my $time = $self->_build_any_time_format($width);
3378            
3379             $format =~ s/\{0\}/$time/;
3380             $format =~ s/\{1\}/$date/;
3381            
3382             return $format;
3383             }
3384              
3385             sub _build_datetime_format_short {
3386             my $self = shift;
3387            
3388             my $width = 'short';
3389             my $format = $self->_build_any_datetime_format($width);
3390            
3391             my $date = $self->_build_any_date_format($width);
3392             my $time = $self->_build_any_time_format($width);
3393            
3394             $format =~ s/\{0\}/$time/;
3395             $format =~ s/\{1\}/$date/;
3396            
3397             return $format;
3398             }
3399              
3400             sub _build_format_data {
3401             my $self = shift;
3402             my $default_calendar = $self->default_calendar();
3403              
3404             my @bundles = $self->_find_bundle('datetime_formats_available_formats');
3405             foreach my $calendar ($default_calendar, 'gregorian') {
3406             foreach my $bundle (@bundles) {
3407             my $datetime_formats_available_formats = $bundle->datetime_formats_available_formats;
3408             my $result = $datetime_formats_available_formats->{$calendar};
3409             return $result if $result;
3410             }
3411             }
3412              
3413             return {};
3414             }
3415              
3416             sub format_for {
3417             my ($self, $format) = @_;
3418              
3419             my $format_data = $self->format_data;
3420              
3421             return $format_data->{$format} // '';
3422             }
3423              
3424             sub _build_available_formats {
3425             my $self = shift;
3426              
3427             my $format_data = $self->format_data;
3428              
3429             return [keys %$format_data];
3430             }
3431              
3432             sub _build_default_date_format_length {
3433             my $self = shift;
3434            
3435             my $default_calendar = $self->default_calendar();
3436              
3437             my @bundles = $self->_find_bundle('date_formats');
3438             foreach my $calendar ($default_calendar, 'gregorian') {
3439             foreach my $bundle (@bundles) {
3440             my $date_formats = $bundle->date_formats;
3441             my $result = $date_formats->{$calendar}{default};
3442             return $result if $result;
3443             }
3444             }
3445             }
3446              
3447             sub _build_default_time_format_length {
3448             my $self = shift;
3449            
3450             my $default_calendar = $self->default_calendar();
3451              
3452             my @bundles = $self->_find_bundle('time_formats');
3453             foreach my $calendar ($default_calendar, 'gregorian') {
3454             foreach my $bundle (@bundles) {
3455             my $time_formats = $bundle->time_formats;
3456             my $result = $time_formats->{$calendar}{default};
3457             return $result if $result;
3458             }
3459             }
3460             }
3461              
3462             sub _build_prefers_24_hour_time {
3463             my $self = shift;
3464              
3465             return $self->time_format_short() =~ /h|K/ ? 0 : 1;
3466             }
3467              
3468             {
3469             my %days_2_number = (
3470             mon => 1,
3471             tue => 2,
3472             wen => 3,
3473             thu => 4,
3474             fri => 5,
3475             sat => 6,
3476             sun => 7,
3477             );
3478              
3479             sub _build_first_day_of_week {
3480              
3481             my $self = shift;
3482              
3483             my $first_day = $self->week_data_first_day;
3484            
3485             return $days_2_number{$first_day};
3486             }
3487             }
3488              
3489             # Sub to mangle Unicode regex to Perl regex
3490             # Backwards compatibility hack
3491             *_unicode_to_perl = eval <<'EOT' || \&_new_perl;
3492             sub {
3493             my $regex = shift;
3494              
3495             return '' unless length $regex;
3496             $regex =~ s/
3497             (?:\\\\)*+ # Pairs of \
3498             (?!\\) # Not followed by \
3499             \K # But we don't want to keep that
3500             (?<set> # Capture this
3501             \[ # Start a set
3502             (?:
3503             [^\[\]\\]+ # One or more of not []\
3504             | # or
3505             (?:
3506             (?:\\\\)*+ # One or more pairs of \ without back tracking
3507             \\. # Followed by an escaped character
3508             )
3509             | # or
3510             (?&set) # An inner set
3511             )++ # Do the inside set stuff one or more times without backtracking
3512             \] # End the set
3513             )
3514             / _convert($1) /xeg;
3515             no warnings "experimental::regex_sets";
3516             no warnings "deprecated"; # Because CLDR uses surrogates
3517             return qr/$regex/x;
3518             };
3519              
3520             EOT
3521              
3522             # Backwards compatibility hack
3523             *_convert = eval <<'EOT' || \&_new_perl;
3524             sub {
3525             my $set = shift;
3526            
3527             # Some definitions
3528             my $posix = qr/(?(DEFINE)
3529             (?<posix> (?> \[: .+? :\] ) )
3530             )/x;
3531            
3532             # Convert Unicode escapes \u1234 to characters
3533             $set =~ s/\\u(\p{Ahex}+)/chr(hex($1))/egx;
3534            
3535             # Check to see if this is a normal character set
3536             my $normal = 0;
3537            
3538             $normal = 1 if $set =~ /^
3539             \s* # Possible white space
3540             \[ # Opening set
3541             ^? # Possible negation
3542             (?: # One of
3543             [^\[\]]++ # Not an open or close set
3544             | # Or
3545             (?<=\\)[\[\]] # An open or close set preceded by \
3546             | # Or
3547             (?:
3548             \s* # Possible white space
3549             (?&posix) # A posix class
3550             (?! # Not followed by
3551             \s* # Possible white space
3552             [&-] # A Unicode regex op
3553             \s* # Possible white space
3554             \[ # A set opener
3555             )
3556             )
3557             )+
3558             \] # Close the set
3559             \s* # Possible white space
3560             $
3561             $posix
3562             /x;
3563            
3564             # Convert posix to perl
3565             $set =~ s/\[:(.*?):\]/\\p{$1}/g;
3566            
3567             if ($normal) {
3568             return "$set";
3569             }
3570            
3571             # Fix up [abc[de]] to [[abc][de]]
3572             $set =~ s/\[ ( (?>\^? \s*) [^\]]+? ) \s* \[/[[$1][/gx;
3573            
3574             # Fix up [[ab]cde] to [[ab][cde]]
3575             $set =~ s/\[ \^?+ \s* \[ [^\]]+? \] \K \s* ( [^\[]+ ) \]/[$1]]/gx;
3576            
3577             # Unicode uses ^ to compliment the set where as Perl uses !
3578             $set =~ s/\[ \^ \s*/[!/gx;
3579            
3580             # The above can leave us with empty sets. Strip them out
3581             $set =~ s/\[\]//g;
3582            
3583             # Fixup inner sets with no operator
3584             1 while $set =~ s/ \] \s* \[ /] + [/gx;
3585             1 while $set =~ s/ \] \s * (\\p\{.*?\}) /] + $1/xg;
3586             1 while $set =~ s/ \\p\{.*?\} \s* \K \[ / + [/xg;
3587             1 while $set =~ s/ \\p\{.*?\} \s* \K (\\p\{.*?\}) / + $1/xg;
3588            
3589             # Unicode uses [] for grouping as well as starting an inner set
3590             # Perl uses ( ) So fix that up now
3591            
3592             $set =~ s/. \K \[ (?> (!?) \s*) \[ /($1\[/gx;
3593             $set =~ s/ \] \s* \] (.) /])$1/gx;
3594            
3595             return "(?$set)";
3596             }
3597              
3598             EOT
3599              
3600             # The following pod is for methods defined in the Moose Role
3601             # files that are automatically generated from the data
3602             =back
3603              
3604             =head2 Valid codes
3605              
3606             =over 4
3607              
3608             =item valid_languages()
3609              
3610             This method returns a list containing all the valid language codes
3611              
3612             =item valid_scripts()
3613              
3614             This method returns a list containing all the valid script codes
3615              
3616             =item valid_territories()
3617              
3618             This method returns a list containing all the valid territory codes
3619              
3620             =item valid_variants()
3621              
3622             This method returns a list containing all the valid variant codes
3623              
3624             =item key_aliases()
3625              
3626             This method returns a hash that maps valid keys to their valid aliases
3627              
3628             =item key_names()
3629              
3630             This method returns a hash that maps valid key aliases to their valid keys
3631              
3632             =item valid_keys()
3633              
3634             This method returns a hash of valid keys and the valid type codes you
3635             can have with each key
3636              
3637             =item language_aliases()
3638              
3639             This method returns a hash that maps valid language codes to their valid aliases
3640              
3641             =item territory_aliases()
3642              
3643             This method returns a hash that maps valid territory codes to their valid aliases
3644              
3645             =item variant_aliases()
3646              
3647             This method returns a hash that maps valid variant codes to their valid aliases
3648              
3649             =back
3650              
3651             =head2 Information about weeks
3652              
3653             There are no standard codes for the days of the weeks so CLDR uses the following
3654             three tetter codes to represent unlocalised days
3655              
3656             =over 4
3657              
3658             =item sun
3659              
3660             Sunday
3661              
3662             =item mon
3663              
3664             Monday
3665              
3666             =item tue
3667              
3668             Tuesday
3669              
3670             =item wed
3671              
3672             Wednesday
3673              
3674             =item thu
3675              
3676             Thursday
3677              
3678             =item fri
3679              
3680             Friday
3681              
3682             =item sat
3683              
3684             Saturday
3685              
3686             =back
3687              
3688             =cut
3689              
3690             sub _week_data {
3691             my ($self, $territory_id, $week_data_hash) = @_;
3692            
3693             $territory_id //= ( $self->territory_id || $self->likely_subtag->territory_id );
3694            
3695             return $week_data_hash->{$territory_id} if exists $week_data_hash->{$territory_id};
3696            
3697             while (1) {
3698             $territory_id = $self->territory_contained_by()->{$territory_id};
3699             return unless defined $territory_id;
3700             return $week_data_hash->{$territory_id} if exists $week_data_hash->{$territory_id};
3701             }
3702             }
3703              
3704             =over 4
3705              
3706             =item week_data_min_days($territory_id)
3707              
3708             This method takes an optional territory id and returns a the minimum number of days
3709             a week must have to count as the starting week of the new year. It uses the current
3710             locale's territory if no territory id is passed in.
3711              
3712             =cut
3713              
3714             sub week_data_min_days {
3715             my ($self, $territory_id) = @_;
3716            
3717             my $week_data_hash = $self->_week_data_min_days();
3718             return _week_data($self, $territory_id, $week_data_hash);
3719             }
3720              
3721             =item week_data_first_day($territory_id)
3722              
3723             This method takes an optional territory id and returns the three letter code of the
3724             first day of the week for that territory. If no territory id is passed in then it
3725             uses the current locale's territory.
3726              
3727             =cut
3728              
3729             sub week_data_first_day {
3730             my ($self, $territory_id) = @_;
3731            
3732             my $week_data_hash = $self->_week_data_first_day();
3733             return _week_data($self, $territory_id, $week_data_hash);
3734             }
3735              
3736             =item week_data_weekend_start()
3737              
3738             This method takes an optional territory id and returns the three letter code of the
3739             first day of the week end for that territory. If no territory id is passed in then it
3740             uses the current locale's territory.
3741              
3742             =cut
3743              
3744             sub week_data_weekend_start {
3745             my ($self, $territory_id) = @_;
3746             my $week_data_hash = $self->_week_data_weekend_start();
3747            
3748             return _week_data($self, $territory_id, $week_data_hash);
3749             }
3750              
3751             =item week_data_weekend_end()
3752              
3753             This method takes an optional territory id and returns the three letter code of the
3754             first day of the week end for that territory. If no territory id is passed in then it
3755             uses the current locale's territory.
3756              
3757             =cut
3758              
3759             sub week_data_weekend_end {
3760             my ($self, $territory_id) = @_;
3761             my $week_data_hash = $self->_week_data_weekend_end();
3762            
3763             return _week_data($self, $territory_id, $week_data_hash);
3764             }
3765              
3766             =back
3767              
3768             =head2 Territory Containment
3769              
3770             =over 4
3771              
3772             =item territory_contains()
3773              
3774             This method returns a hash ref keyed on territory id. The value is an array ref.
3775             Each element of the array ref is a territory id of a territory immediately
3776             contained in the territory used as the key
3777              
3778             =item territory_contained_by()
3779              
3780             This method returns a hash ref keyed on territory id. The value of the hash
3781             is the territory id of the immediately containing territory.
3782              
3783             =back
3784              
3785             =head2 Numbering Systems
3786              
3787             =over 4
3788              
3789             =item numbering_system()
3790              
3791             This method returns a hash ref keyed on numbering system id which, for a given
3792             locale, can be got by calling the default_numbering_system() method. The values
3793             of the hash are a two element hash ref the keys being C<type> and C<data>. If the
3794             type is C<numeric> then the data is an array ref of characters. The position in the
3795             array matches the numeric value of the character. If the type is C<algorithmic>
3796             then data is the name of the algorithm used to display numbers in that format.
3797              
3798             =back
3799              
3800             =head2 Number Formatting
3801              
3802             =over 4
3803              
3804             =item format_number($number, $format, $currency, $for_cash)
3805              
3806             This method formats the number $number using the format $format. If the format contains
3807             the currency symbol C<¤> then the currency symbol for the currency code in $currency
3808             will be used. If $currency is undef() then the default currency code for the locale
3809             will be used.
3810              
3811             Note that currency codes are based on territory so if you do not pass in a currency
3812             and your locale did not get passed a territory in the constructor you are going
3813             to end up with the L<likely sub tag's|/likely_subtags> idea of the currency. This
3814             functionality may be removed or at least changed to emit a warning in future
3815             releases.
3816              
3817             $for_cash is only used during currency formatting. If true then cash rounding
3818             will be used otherwise financial rounding will be used.
3819              
3820             This function also handles rule based number formatting. If $format is string equivilant
3821             to one of the current locale's public rule based number formats then $number will be
3822             formatted according to that rule.
3823              
3824             =item add_currency_symbol($format, $symbol)
3825              
3826             This method returns the format with the currency symbol $symbol correctly inserted
3827             into the format
3828              
3829             =item parse_number_format($format, $currency, $currency_data, $for_cash)
3830              
3831             This method parses a CLDR numeric format string into a hash ref containing data used to
3832             format a number. If a currency is being formatted then $currency contains the
3833             currency code, $currency_data is a hashref containing the currency rounding
3834             information and $for_cash is a flag to signal cash or financial rounding.
3835              
3836             This should probably be a private function.
3837              
3838             =item round($number, $increment, $decimal_digits)
3839              
3840             This method returns $number rounded to the nearest $increment with $decimal_digits
3841             digits after the decimal point
3842              
3843             =item get_formatted_number($number, $format, $currency_data, $for_cash)
3844              
3845             This method takes the $format produced by parse_number_format() and uses it to
3846             parse $number. It returns a string containing the parsed number. If a currency
3847             is being formatted then $currency_data is a hashref containing the currency
3848             rounding information and $for_cash is a flag to signal cash or financial rounding.
3849              
3850             =item get_digits()
3851              
3852             This method returns an array containing the digits used by the locale, The order of the
3853             array is the order of the digits. It the locale's numbering system is C<algorithmic> it
3854             will return C<[0,1,2,3,4,5,6,7,8,9]>
3855              
3856             =item default_numbering_system()
3857              
3858             This method returns the numbering system id for the locale.
3859              
3860             =back
3861              
3862             =head2 Measurement Information
3863              
3864             =over 4
3865              
3866             =item measurement_system()
3867              
3868             This method returns a hash ref keyed on territory, the value being the measurement system
3869             id for the territory. If the territory you are interested in is not listed use the
3870             territory_contained_by() method until you find an entry.
3871              
3872             =item paper_size()
3873              
3874             This method returns a hash ref keyed on territory, the value being the paper size used
3875             in that territory. If the territory you are interested in is not listed use the
3876             territory_contained_by() method until you find an entry.
3877              
3878             =back
3879              
3880             =head2 Likely Tags
3881              
3882             =over 4
3883              
3884             =item likely_subtags()
3885              
3886             A full locale tag requires, as a minimum, a language, script and territory code. However for
3887             some locales it is possible to infer the missing element if the other two are given, e.g.
3888             given C<en_GB> you can infer the script will be latn. It is also possible to fill in the
3889             missing elements of a locale with sensible defaults given sufficient knowledge of the layout
3890             of the CLDR data and usage patterns of locales around the world.
3891              
3892             This function returns a hash ref keyed on partial locale id's with the value being the locale
3893             id for the most likely language, script and territory code for the key.
3894              
3895             =back
3896              
3897             =head2 Currency Information
3898              
3899             =over 4
3900              
3901             =item currency_fractions()
3902              
3903             This method returns a hash ref keyed on currency id. The value is a hash ref containing four keys.
3904             The keys are
3905              
3906             =over 8
3907              
3908             =item digits
3909              
3910             The number of decimal digits normally formatted.
3911              
3912             =item rounding
3913              
3914             The rounding increment, in units of 10^-digits.
3915              
3916             =item cashdigits
3917              
3918             The number of decimal digits to be used when formatting quantities used in cash transactions (as opposed
3919             to a quantity that would appear in a more formal setting, such as on a bank statement).
3920              
3921             =item cashrounding
3922              
3923             The cash rounding increment, in units of 10^-cashdigits.
3924              
3925             =back
3926              
3927             =item default_currency($territory_id)
3928              
3929             This method returns the default currency id for the territory id.
3930             If no territory id is given then the current locale's is used
3931              
3932             =cut
3933              
3934             sub default_currency {
3935             my ($self, $territory_id) = @_;
3936            
3937             $territory_id //= $self->territory_id;
3938            
3939             if (! $territory_id) {
3940             $territory_id = $self->likely_subtag->territory_id;
3941             warn "Locale::CLDR::default_currency:- No territory given using $territory_id at ";
3942             }
3943            
3944             my $default_currencies = $self->_default_currency;
3945            
3946             return $default_currencies->{$territory_id} if exists $default_currencies->{$territory_id};
3947            
3948             while (1) {
3949             $territory_id = $self->territory_contained_by($territory_id);
3950             last unless $territory_id;
3951             return $default_currencies->{$territory_id} if exists $default_currencies->{$territory_id};
3952             }
3953             }
3954              
3955             =item currency_symbol($currency_id)
3956              
3957             This method returns the currency symbol for the given currency id in the current locale.
3958             If no currency id is given it uses the locale's default currency
3959              
3960             =cut
3961              
3962             sub currency_symbol {
3963             my ($self, $currency_id) = @_;
3964            
3965             $currency_id //= $self->default_currency;
3966            
3967             my @bundles = reverse $self->_find_bundle('curriencies');
3968             foreach my $bundle (@bundles) {
3969             my $symbol = $bundle->curriencies()->{$currency_id}{symbol};
3970             return $symbol if $symbol;
3971             }
3972            
3973             return '';
3974             }
3975              
3976             =back
3977              
3978             =head2 Calendar Information
3979              
3980             =over 4
3981              
3982             =item calendar_preferences()
3983              
3984             This method returns a hash ref keyed on territory id. The values are array refs containing the preferred
3985             calendar id's in order of preference.
3986              
3987             =item default_calendar($territory)
3988              
3989             This method returns the default calendar id for the given territory. If no territory id given it
3990             used the territory of the current locale.
3991              
3992             =back
3993              
3994             =for comment
3995              
3996             =head2 Collation
3997              
3998             =over 4
3999              
4000             =item collation()
4001              
4002             This method returns a Locale::CLDR::Collator object. This is still in development. Future releases will
4003             try and match the API from L<Unicode::Collate> as much as possible and add tailoring for locales.
4004              
4005             =back
4006              
4007             =end
4008              
4009             =cut
4010              
4011             =for comment
4012              
4013             sub collation {
4014             my ($self, %params) = @_;
4015            
4016             $params{type} //= $self->_default_collation;
4017             $params{strength} //= $self->_default_collation_strength;
4018            
4019             return Locale::CLDR::Collator->new(locale => $self, %params);
4020             }
4021              
4022             sub collation_overrides {
4023             my ($self, $type) = @_;
4024            
4025             my @bundles = reverse $self->_find_bundle('collation');
4026            
4027             my $override = '';
4028             foreach my $bundle (@bundles) {
4029             last if $override = $bundle->collation()->{$type};
4030             }
4031            
4032             if ($type ne 'standard' && ! $override) {
4033             foreach my $bundle (@bundles) {
4034             last if $override = $bundle->collation()->{standard};
4035             }
4036             }
4037            
4038             return $override || [];
4039             }
4040            
4041             sub _default_collation {
4042             return 'standard';
4043             }
4044              
4045             sub _default_collation_strength {
4046             return 3;
4047             }
4048              
4049             =end
4050              
4051             =head1 AUTHOR
4052              
4053             John Imrie, C<< <john dot imrie1 at gmail dot com> >>
4054              
4055             =head1 BUGS
4056              
4057             Please report any bugs or feature requests to C<bug-locale-cldr at rt.cpan.org>, or through
4058             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Locale-CLDR>. I will be notified, and then you'll
4059             automatically be notified of progress on your bug as I make changes.
4060              
4061             =head1 SUPPORT
4062              
4063             You can find documentation for this module with the perldoc command.
4064              
4065             perldoc Locale::CLDR
4066              
4067             You can also look for information at:
4068              
4069             =over 4
4070              
4071             =item * RT: CPAN's request tracker
4072              
4073             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Locale-CLDR>
4074              
4075             =item * AnnoCPAN: Annotated CPAN documentation
4076              
4077             L<http://annocpan.org/dist/Locale-CLDR>
4078              
4079             =item * CPAN Ratings
4080              
4081             L<http://cpanratings.perl.org/d/Locale-CLDR>
4082              
4083             =item * Search CPAN
4084              
4085             L<http://search.cpan.org/dist/Locale-CLDR/>
4086              
4087             =back
4088              
4089              
4090             =head1 ACKNOWLEDGEMENTS
4091              
4092             Everyone at the Unicode Consortium for providing the data.
4093              
4094             Karl Williams for his tireless work on Unicode in the Perl
4095             regex engine.
4096              
4097             Andrew Rodland for his L<Unicode::CaseFold> module that I
4098             pinched the fc() code from for early versions of Perl that
4099             don't have this function.
4100              
4101              
4102             =head1 COPYRIGHT & LICENSE
4103              
4104             Copyright 2009-2014 John Imrie.
4105             Backwards compatible Case Folding Copyright Andrew Rodland ARODLAND@cpan.org
4106              
4107             This program is free software; you can redistribute it and/or modify it
4108             under the terms of either: the GNU General Public License as published
4109             by the Free Software Foundation; or the Artistic License.
4110              
4111             See http://dev.perl.org/licenses/ for more information.
4112              
4113             =cut
4114              
4115             1; # End of Locale::CLDR