| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Dancer2::Plugin::Locale; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 324717 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 23 |  | 
| 4 | 1 |  |  | 1 |  | 3 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 19 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 1 |  |  | 1 |  | 493 | use Dancer2::Plugin; | 
|  | 1 |  |  |  |  | 10158 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 7 |  |  |  |  |  |  | $Dancer2::Plugin::Locale::VERSION = '0.06'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | package Dancer2::Plugin::Locale::Obj; | 
| 10 | 1 |  |  | 1 |  | 2618 | use Locales 0.33 unicode => 1; | 
|  | 1 |  |  |  |  | 6979 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 11 | 1 |  |  | 1 |  | 597 | use Locale::Maketext::Utils; | 
|  | 1 |  |  |  |  | 16781 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 12 | 1 |  |  | 1 |  | 5 | use base 'Locale::Maketext::Utils'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 86 |  | 
| 13 |  |  |  |  |  |  | our %Lexicon; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | package Dancer2::Plugin::Locale; | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 1 |  |  | 1 |  | 4 | use File::Spec; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 219 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | # use Tie::Hash::ReadonlyStack; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | plugin_keywords 'locale'; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub locale { | 
| 24 | 19 |  |  | 19 | 1 | 160994 | my $dsl = shift; | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 19 | 100 |  |  |  | 48 | if (@_) { | 
| 27 | 8 |  |  |  |  | 15 | return Dancer2::Plugin::Locale::Obj->get_handle( grep( { defined } (@_) ), 'en' );    # multiton already via Locale::Maketext::Utils | 
|  | 8 |  |  |  |  | 49 |  | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  |  | 
| 30 | 11 |  |  |  |  | 30 | my $app = $dsl->app; | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | # TODO 2: request locale via browser/HTP req after session and before default? | 
| 33 | 11 |  |  |  |  | 15 | return Dancer2::Plugin::Locale::Obj->get_handle( grep( { defined } ( eval { $app->session->read('locale') }, $dsl->config->{default_locale} ) ), 'en' );    # multiton already via Locale::Maketext::Utils | 
|  | 22 |  |  |  |  | 10757 |  | 
|  | 11 |  |  |  |  | 159 |  | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | sub BUILD { | 
| 37 | 1 |  |  | 1 | 0 | 1346 | my $dsl = shift; | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 1 |  |  |  |  | 3 | my @available_locales = ('en'); | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # read locale/ dir for available locales (via config also? likley YAGNI/overly comlicated-why?) | 
| 42 | 1 |  |  |  |  | 20 | my $locale_dir = File::Spec->catdir( $dsl->app->config->{'appdir'}, 'locale' );                                                                             # configurable? nah, why? | 
| 43 | 1 | 50 |  |  |  | 68 | if ( -d $locale_dir ) { | 
| 44 | 0 | 0 |  |  |  | 0 | if ( opendir my $dh, $locale_dir ) { | 
| 45 | 0 |  |  |  |  | 0 | while ( my $file = readdir($dh) ) { | 
| 46 | 0 | 0 |  |  |  | 0 | next if $file !~ m/\.json$/; | 
| 47 | 0 | 0 |  |  |  | 0 | next if $file eq 'en.json'; | 
| 48 | 0 |  |  |  |  | 0 | $file =~ s/\.json//; | 
| 49 | 0 | 0 |  |  |  | 0 | if ( Locales::normalize_tag($file) ne $file ) { | 
| 50 | 0 |  |  |  |  | 0 | warn "Skipping un-normalized locale named lexicon ($file.json) â¦\n";                                                                      # just no apparent need to complicate things by trying to deal with this | 
| 51 | 0 |  |  |  |  | 0 | next; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 | 0 | 0 |  |  |  | 0 | if ( !-f "$locale_dir/$file.json" ) { | 
| 55 | 0 |  |  |  |  | 0 | warn "Skipping non-file lexicon ($file.json) â¦\n"; | 
| 56 | 0 |  |  |  |  | 0 | next; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 0 |  |  |  |  | 0 | push @available_locales, $file; | 
| 60 |  |  |  |  |  |  | } | 
| 61 | 0 |  |  |  |  | 0 | closedir($dh); | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  | else { | 
| 64 | 0 |  |  |  |  | 0 | die "Could not read locale directory ($locale_dir): $!\n"; | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  | } | 
| 67 | 1 |  |  | 1 |  | 4 | no strict 'refs';          ## no critic | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 23 |  | 
| 68 | 1 |  |  | 1 |  | 3 | no warnings 'redefine';    ## no critic | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 98 |  | 
| 69 |  |  |  |  |  |  | *Locale::Maketext::Utils::list_available_locales = sub { | 
| 70 | 0 |  |  | 0 |  | 0 | return ( sort @available_locales ); | 
| 71 | 1 |  |  |  |  | 16 | }; | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | # create classes that Locale::Maketext uses | 
| 74 | 1 |  |  |  |  | 3 | for my $tag (@available_locales) { | 
| 75 | 1 |  |  |  |  | 10 | my $file = File::Spec->catfile( $locale_dir, "$tag.json" ); | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | # TODO 1: for en (and its alias) empty value means key *is* value ⦠| 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | # TODO 1: support tieing to CDB_File hash (e.g. if -f locale_cdb/$tag.cdb) so as not to load all the data into memory (see use_external_lex_cache)? | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | # TODO 2: POD app w/ charset !utf8 == ick | 
| 82 | 1 |  |  | 1 |  | 4 | eval "package Dancer2::Plugin::Locale::Obj::$tag;use base 'Dancer2::Plugin::Locale::Obj';our \$Encoding='utf8';our \%Lexicon;package Dancer2::Plugin::Locale;";    ## no critic | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 263 |  | 
|  | 1 |  |  |  |  | 59 |  | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 1 |  |  | 1 |  | 4 | no strict 'refs';                                                                                                                                                  ## no critic | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 160 |  | 
| 85 | 1 | 50 | 33 |  |  | 18 | %{"Dancer2::Plugin::Locale::Obj::$tag\::Lexicon"} = $tag eq 'en' && !-e $file ? () : ( %{ _from_json_file($file) } );                                              # TODO 1: instead: tie %{"Dancer2::Plugin::Locale::$tag\::Lexicon"}, 'Tie::Hash::ReadonlyStack', _from_json_file($file); | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | # TODO 2: Is there a better way to add template keyword? | 
| 89 |  |  |  |  |  |  | $dsl->app->add_hook( | 
| 90 |  |  |  |  |  |  | Dancer2::Core::Hook->new( | 
| 91 |  |  |  |  |  |  | name => 'before_template_render', | 
| 92 |  |  |  |  |  |  | code => sub { | 
| 93 | 6 |  |  | 6 |  | 22838 | $_[0]->{locale} = sub { $dsl->locale(@_) }; | 
|  | 6 |  |  |  |  | 41707 |  | 
| 94 |  |  |  |  |  |  | }, | 
| 95 |  |  |  |  |  |  | ) | 
| 96 | 1 |  |  |  |  | 29 | ); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub _from_json_file { | 
| 100 | 0 |  |  | 0 |  |  | my ($file) = @_; | 
| 101 | 0 | 0 |  |  |  |  | open( my $fh, '<', $file ) or die "Could not read â$fileâ: $!"; | 
| 102 | 1 |  |  | 1 |  | 400 | use Dancer2::Serializer::JSON; | 
|  | 1 |  |  |  |  | 7856 |  | 
|  | 1 |  |  |  |  | 86 |  | 
| 103 | 0 |  |  |  |  |  | my $ref = {}; | 
| 104 | 0 |  |  |  |  |  | eval { | 
| 105 |  |  |  |  |  |  | $ref = Dancer2::Serializer::JSON::from_json( | 
| 106 | 0 |  |  |  |  |  | do { local $/; <$fh> } | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | ); | 
| 108 |  |  |  |  |  |  | }; | 
| 109 | 0 | 0 |  |  |  |  | if ($@) { | 
| 110 | 0 |  |  |  |  |  | warn "Ignoring lexicon, $file, since it containes invalid JSON: $@"; | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 0 |  |  |  |  |  | return $ref; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | # TODO 2: localization tips | 
| 116 |  |  |  |  |  |  | # TODO 2: extractor/checker tool | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | 1; | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | __END__ | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =encoding utf8 | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =head1 NAME | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | Dancer2::Plugin::Locale - Localize your Dancer2 application | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =head1 VERSION | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | This document describes Dancer2::Plugin::Locale version 0.06 | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | In your app: | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | use Dancer2; | 
| 137 |  |  |  |  |  |  | use Dancer2::Plugin::Locale; | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | ⦠| 
| 140 |  |  |  |  |  |  | locale->maketext('You are [numf,_1] of [numf,_2].', 42, 99); | 
| 141 |  |  |  |  |  |  | ⦠| 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | and from template | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | <div id="req_msg">[% locale.maketext('You have [quant,_1,request,requests].', req_count) %]</div> | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | Adds a C<locale> keyword for your code and templates. | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =head1 INTERFACE | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | =head2 locale | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | A lazy façade to get a locale handle suitable to the request. The locale object is a CLDR aware maketext format object. | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | It will be based on the sessionâs locale value if possible, then a configured default if possible, then 'en'. | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =head2 The object, in more detail | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | The object is L<Locale::Maketext::Utils> based. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | L<Locale::Maketext::Utils> extends L<Locale::Maketext> a number of ways including: | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =over 4 | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =item * It shifts toward CLDR based functionality which means you no longer have to create locale specific variants of code in each localeâs class. | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | =item * Because of that and other utils it has, creating and dealing with the locale subclasses classes is much easier. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =item * The object is a multiton (AKA an argument based singleton):  L<Locale::Maketext::Utils/"Argument based singleton"> | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | =item * Adds a number of helpful methods: L<Locale::Maketext::Utils/METHODS> | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | =item * More sane fallback and lookup failure hooks: L<Locale::Maketext::Utils/"Automatically _AUTO'd Failure Handling with hooks"> | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =item * Adds a very handy set of bracket notation methods (CLDR when possible)  L</"Bracket Notation"> | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =back | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =head3 Available Locales | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | The locales available are determined by the lexicon files found in the appdirâs C<locale/> directory. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | The name of each file must be a normalized version (See Locales::normalize_tag() in L<Locales>) of an L<acceptable ISO tag|Locales/"Supported_Locale_Criteria">. and end in with the extension C<.json>. | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Tip: To make them available to your UI you could simply symlink C<â¦/locale/> to C<â¦/public/locale>. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head3 Lexicon | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | A lexicon is a simple key/value hash  where the key is the phrase and the value is the translation. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | For example,in pseudo code: | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # source phrase => target phrase | 
| 196 |  |  |  |  |  |  | 'Hello World' => 'Bonjour Monde' | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Each locale will have a lexicon hash in a file as desrcibed in L</"Available Locales">. | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | The hash must be written in JSON format and be utf8 encoded. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | Donât be afraid of non-ASCII characters, just put them in the file as the character and it will work fine (if it doesnât then it can help you track down bugs faster, win win!). | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | "I â¥ï¸ Dancer2!" : "ç§ã¯â¥ï¸ãã³ãµã¼2" | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | =head3 Bracket Notation | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | Bracket notation is described a bit more at L<Locale::Maketext/BRACKET NOTATION> but is essentially a format to allow you to notateâwithin left and right square brackets, hence the nameâdynamic portions of a phrase. | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | For example, include a non-translatable ever-changing name: | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | locale->maketext('Your email address, [_1], has been unsubscribed.', $email) | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | or a number formatted according to the objectâs localeâs CLDR data: | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | locale->maketext('You are user [numf,_1] of [numf,_2].', $place, $count) | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | The bracket notation improvements over the core L<Locale::Maketext> can be categoraized as follows: | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | =over 4 | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | =item * L<improved core Locale::Maketext bracket notation methods|http://search.cpan.org/perldoc?Locale::Maketext::Utils#Improved_Bracket_Notation> | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | =item * L<additional bracket notation methods|http://search.cpan.org/perldoc?Locale::Maketext::Utils#Additional_bracket_notation_methods> | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | =item * L<output() bracket notation methods|http://search.cpan.org/perldoc?Locale::Maketext::Utils#output()> | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =back | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | =head2 Misc Info | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =head3 Only load the lexicon data you need! | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | ⦠TODO, sorry I needed to get this on CPAN for a consumer but will finish it ASAP ⦠| 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | =head4 per app/route loading/unloading of specific lexicon | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | ⦠TODO, sorry I needed to get this on CPAN for a consumer but will finish it ASAP ⦠| 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =head4 only load the keys you use | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | =head3 Localization Principles | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | ⦠TODO, sorry I needed to get this on CPAN for a consumer but will finish it ASAP ⦠| 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =head3 Extracting and Vetting phrases from your app | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | ⦠TODO, sorry I needed to get this on CPAN for a consumer but will finish it ASAP ⦠| 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | =head2 TODOs | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | TODO items in the POD obviously, will list more if there are any left after the next version. | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =head1 DIAGNOSTICS | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | Throws no errors of its own. | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | The only warning is during startup if you have a mis-named file in your C<locale/> directory: | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | C<<Skipping un-normalized locale named lexicon (%s.json) â¦>> | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | =head1 CONFIGURATION AND ENVIRONMENT | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | The default locale is âenâ, you can change it via Dancer2 configuration like so: | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | plugins: | 
| 267 |  |  |  |  |  |  | Locales: | 
| 268 |  |  |  |  |  |  | default_locale: ja | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | =head1 DEPENDENCIES | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | L<Dancer2::Plugin> | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | L<Locale::Maketext::Utils> | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | L<Locales> | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | L<File::Spec> | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | =head1 INCOMPATIBILITIES | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | None reported. | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | =head1 BUGS AND FEATURES | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | Please report any bugs or feature requests (and a pull request for bonus points) | 
| 287 |  |  |  |  |  |  | through the issue tracker at L<https://github.com/drmuey/p5-Dancer2-Plugin-Locale/issues>. | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | =head1 AUTHOR | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | Daniel Muey  C<< <http://drmuey.com/cpan_contact.pl> >> | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | =head1 LICENCE AND COPYRIGHT | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | Copyright (c) 2015, Daniel Muey C<< <http://drmuey.com/cpan_contact.pl> >>. All rights reserved. | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | This module is free software; you can redistribute it and/or | 
| 298 |  |  |  |  |  |  | modify it under the same terms as Perl itself. See L<perlartistic>. | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | =head1 DISCLAIMER OF WARRANTY | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | 
| 303 |  |  |  |  |  |  | FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | 
| 304 |  |  |  |  |  |  | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | 
| 305 |  |  |  |  |  |  | PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER | 
| 306 |  |  |  |  |  |  | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
| 307 |  |  |  |  |  |  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE | 
| 308 |  |  |  |  |  |  | ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH | 
| 309 |  |  |  |  |  |  | YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL | 
| 310 |  |  |  |  |  |  | NECESSARY SERVICING, REPAIR, OR CORRECTION. | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | 
| 313 |  |  |  |  |  |  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | 
| 314 |  |  |  |  |  |  | REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE | 
| 315 |  |  |  |  |  |  | LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, | 
| 316 |  |  |  |  |  |  | OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE | 
| 317 |  |  |  |  |  |  | THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | 
| 318 |  |  |  |  |  |  | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | 
| 319 |  |  |  |  |  |  | FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | 
| 320 |  |  |  |  |  |  | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | 
| 321 |  |  |  |  |  |  | SUCH DAMAGES. |