| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Locale::Meta; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # ABSTRACT: Localization tool based on Locale::Wolowitz. | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 3 |  |  | 3 |  | 28586 | use strict; | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 70 |  | 
| 6 | 3 |  |  | 3 |  | 9 | use warnings; | 
|  | 3 |  |  |  |  | 3 |  | 
|  | 3 |  |  |  |  | 56 |  | 
| 7 | 3 |  |  | 3 |  | 487 | use utf8; | 
|  | 3 |  |  |  |  | 8 |  | 
|  | 3 |  |  |  |  | 11 |  | 
| 8 | 3 |  |  | 3 |  | 49 | use Carp; | 
|  | 3 |  |  |  |  | 3 |  | 
|  | 3 |  |  |  |  | 166 |  | 
| 9 | 3 |  |  | 3 |  | 1119 | use JSON::MaybeXS qw/JSON/; | 
|  | 3 |  |  |  |  | 14772 |  | 
|  | 3 |  |  |  |  | 2086 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | our $VERSION = "0.008"; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | =head1 NAME | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | Locale::Meta - Multilanguage support loading json structures based on Locale::Wolowitz. | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | =head1 VERSION | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | version 0.008 | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | #in ./i18n/file.json | 
| 24 |  |  |  |  |  |  | { | 
| 25 |  |  |  |  |  |  | "en": { | 
| 26 |  |  |  |  |  |  | "color": { | 
| 27 |  |  |  |  |  |  | "trans" : "color" | 
| 28 |  |  |  |  |  |  | "meta": { | 
| 29 |  |  |  |  |  |  | "searchable": 1, | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  | }, | 
| 33 |  |  |  |  |  |  | "en_gb": { | 
| 34 |  |  |  |  |  |  | "color": { | 
| 35 |  |  |  |  |  |  | "trans": "colour" | 
| 36 |  |  |  |  |  |  | } | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  | } | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | # in your app | 
| 41 |  |  |  |  |  |  | use Locale::Meta | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | my $lm = Locale::Meta->new('./i18n'); | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | print $lm->loc('color', 'en_gb'); # prints 'colour' | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | Locale::Meta has been inspired by Locale::Wolowitz, and the base code, documentation, | 
| 50 |  |  |  |  |  |  | and function has been taken from it.  The main goal of Locale::Meta is to | 
| 51 |  |  |  |  |  |  | provide the same functionality as Locale::Wolowitz, but removing the dependency of the | 
| 52 |  |  |  |  |  |  | file names as part of the definition of the language, to manage a new json data structure | 
| 53 |  |  |  |  |  |  | for the .json files definitions, and also, add a meta field in order to be | 
| 54 |  |  |  |  |  |  | able to extend the use of the locate to other purposes, like search. | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | The objective of the package is to take different json structures, transform the | 
| 57 |  |  |  |  |  |  | data into key/value structure and build a big repository into memory to be use as | 
| 58 |  |  |  |  |  |  | base point to localize language definitions. | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | The metadata attribute "meta" defined on the json file is optional and is used | 
| 61 |  |  |  |  |  |  | to maintain information related to the definition of the term. | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | package Locale::Meta; | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =head1 CONSTRUCTOR | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =head2 new( [ $path / $filename, \%options ] ) | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | Creates a new instance of this module. A path to a directory in | 
| 70 |  |  |  |  |  |  | which JSON localization files exist, or a path to a specific localization | 
| 71 |  |  |  |  |  |  | file. If you pass a directory, all JSON localization files | 
| 72 |  |  |  |  |  |  | in it will be loaded and merged. If you pass one file, only that file will be loaded. | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | Note that C will ignore dotfiles in the provided path (e.g. | 
| 75 |  |  |  |  |  |  | hidden files, backups files, etc.). | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | A hash-ref of options can also be provided. The only option currently supported | 
| 79 |  |  |  |  |  |  | is C, which is on by default. If on, all JSON files are assumed to be in | 
| 80 |  |  |  |  |  |  | UTF-8 character set and will be automatically decoded. Provide a false value | 
| 81 |  |  |  |  |  |  | if your files are not UTF-8 encoded, for example: | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | Locale::Meta->new( '/path/to/files', { utf8 => 0 } ); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | =cut | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | sub new { | 
| 89 | 2 |  |  | 2 | 1 | 129961 | my ($class, $path, $options) = @_; | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 2 |  | 50 |  |  | 14 | $options ||= {}; | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 2 |  |  |  |  | 4 | my $self = bless {}, $class; | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 2 |  |  |  |  | 10 | $self->{json} = JSON->new->relaxed; | 
| 96 | 2 |  |  |  |  | 47 | $self->{json}->utf8; | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 2 | 100 |  |  |  | 10 | $self->load_path($path) | 
| 99 |  |  |  |  |  |  | if $path; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 2 |  |  |  |  | 5 | return $self; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =head1 OBJECT METHODS | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =head2 load_path( $path / $filename ) | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | Receives a path to a directory in which JSON localization files exist, or a | 
| 109 |  |  |  |  |  |  | path to a specific localization file, and loads (and merges) the localization | 
| 110 |  |  |  |  |  |  | data from the file(s). If localization data was already loaded previously, | 
| 111 |  |  |  |  |  |  | the structure will be merged, with the new data taking precedence. | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | You can call this method and L | 
| 114 |  |  |  |  |  |  | as much as you want, the data from each call will be merged with existing data. | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | =cut | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | sub load_path { | 
| 119 | 1 |  |  | 1 | 1 | 1 | my ($self, $path) = @_; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 1 | 50 |  |  |  | 4 | croak "You must provide a path to localization directory." | 
| 122 |  |  |  |  |  |  | unless $path; | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 1 |  | 50 |  |  | 4 | $self->{locales} ||= {}; | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 1 |  |  |  |  | 1 | my @files; | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 1 | 50 |  |  |  | 23 | if (-d $path) { | 
|  |  | 0 |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | # open the locales directory | 
| 130 | 1 | 50 |  |  |  | 28 | opendir(PATH, $path) | 
| 131 |  |  |  |  |  |  | || croak "Can't open localization directory: $!"; | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | # get all JSON files | 
| 134 | 1 |  |  |  |  | 17 | @files = grep {/^[^.].*\.json$/} readdir PATH; | 
|  | 4 |  |  |  |  | 13 |  | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 1 | 50 |  |  |  | 10 | closedir PATH | 
| 137 |  |  |  |  |  |  | || carp "Can't close localization directory: $!"; | 
| 138 |  |  |  |  |  |  | } elsif (-e $path) { | 
| 139 | 0 |  |  |  |  | 0 | my ($file) = ($path =~ m{/([^/]+)$})[0]; | 
| 140 | 0 |  |  |  |  | 0 | $path = $`; | 
| 141 | 0 |  |  |  |  | 0 | @files = ($file); | 
| 142 |  |  |  |  |  |  | } else { | 
| 143 | 0 |  |  |  |  | 0 | croak "Path must be to a directory or a JSON file."; | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | # load the files | 
| 147 | 1 |  |  |  |  | 2 | foreach (@files) { | 
| 148 |  |  |  |  |  |  | # read the file's contents and parse it as json | 
| 149 | 2 | 50 |  |  |  | 43 | open(FILE, "$path/$_") | 
| 150 |  |  |  |  |  |  | || croak "Can't open localization file $_: $!"; | 
| 151 | 2 |  |  |  |  | 5 | local $/; | 
| 152 | 2 |  |  |  |  | 24 | my $json = ; | 
| 153 | 2 | 50 |  |  |  | 46 | close FILE | 
| 154 |  |  |  |  |  |  | || carp "Can't close localization file $_: $!"; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 2 |  |  |  |  | 23 | my $data = $self->{json}->decode($json); | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | #Get the language definitions | 
| 159 | 2 |  |  |  |  | 6 | foreach my $lang (keys %$data){ | 
| 160 | 4 |  |  |  |  | 4 | foreach my $key (keys %{$data->{$lang}}){ | 
|  | 4 |  |  |  |  | 7 |  | 
| 161 | 4 |  | 100 |  |  | 12 | $self->{locales}->{$key} ||= {}; | 
| 162 | 4 |  | 33 |  |  | 13 | $self->{locales}->{$key}->{$lang} = $data->{$lang}->{$key}->{trans} || $data->{$lang}->{$key}; | 
| 163 | 4 |  | 100 |  |  | 18 | $self->{locales}->{$key}->{meta} ||={}; | 
| 164 | 4 |  |  |  |  | 4 | foreach my $meta_key ( keys %{$data->{$lang}->{$key}->{meta}} ) { | 
|  | 4 |  |  |  |  | 17 |  | 
| 165 | 1 |  |  |  |  | 4 | $self->{locales}->{$key}->{meta}->{$meta_key} = $data->{$lang}->{$key}->{meta}->{$meta_key}; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | }; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  |  | 
| 171 | 1 |  |  |  |  | 2 | return 1; | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | =head2 charge($structure) | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | Load #structure into $self->{locales} | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =cut | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | sub charge{ | 
| 182 | 1 |  |  | 1 | 1 | 343 | my ($self, $structure) = @_; | 
| 183 | 1 |  | 50 |  |  | 9 | $self->{locales} ||= {}; | 
| 184 | 1 | 50 |  |  |  | 6 | if ( (ref $structure) =~ /HASH/ ){ | 
| 185 | 1 |  |  |  |  | 12 | foreach my $lang ( keys %{$structure} ){ | 
|  | 1 |  |  |  |  | 4 |  | 
| 186 | 1 |  |  |  |  | 2 | foreach my $key ( keys %{$structure->{$lang}} ){ | 
|  | 1 |  |  |  |  | 12 |  | 
| 187 | 1 |  | 50 |  |  | 5 | $self->{locales}->{$key} ||= {}; | 
| 188 | 1 |  | 33 |  |  | 4 | $self->{locales}->{$key}->{$lang} = $structure->{$lang}->{$key}->{trans} || $structure->{$lang}->{$key}; | 
| 189 | 1 |  | 50 |  |  | 4 | $self->{locales}->{$key}->{meta} ||={}; | 
| 190 | 1 |  |  |  |  | 1 | foreach my $meta_key ( keys %{$structure->{$lang}->{$key}->{meta}} ) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 191 | 1 |  |  |  |  | 3 | $structure->{$lang}->{$key}->{meta}->{$meta_key} = $structure->{$lang}->{$key}->{meta}->{$meta_key}; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  | else{ | 
| 197 | 0 |  |  |  |  | 0 | croak "Structure received by charge method isn't a Hash"; | 
| 198 |  |  |  |  |  |  | } | 
| 199 | 1 |  |  |  |  | 3 | return; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =head2 loc( $msg, $lang, [ @args ] ) | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | Returns the string C<$msg>, translated to the requested language (if such | 
| 205 |  |  |  |  |  |  | a translation exists, otherwise no traslation occurs). Any other parameters | 
| 206 |  |  |  |  |  |  | passed to the method (C<@args>) are injected to the placeholders in the string | 
| 207 |  |  |  |  |  |  | (if present). | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | =cut | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub loc { | 
| 212 | 5 |  |  | 5 | 1 | 339 | my ($self, $key, $lang, @args) = @_; | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 5 | 100 |  |  |  | 15 | return unless defined $key; # undef strings are passed back as-is | 
| 215 | 4 | 50 |  |  |  | 7 | return $key unless $lang; | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 4 | 100 | 66 |  |  | 20 | my $ret = $self->{locales}->{$key} && $self->{locales}->{$key}->{$lang} ? $self->{locales}->{$key}->{$lang} : $key; | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 4 | 50 |  |  |  | 10 | if (scalar @args) { | 
| 220 | 0 |  |  |  |  | 0 | for (my $i = 1; $i <= scalar @args; $i++) { | 
| 221 | 0 |  |  |  |  | 0 | $ret =~ s/%$i/$args[$i-1]/g; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 4 |  |  |  |  | 17 | return $ret; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =head1 DIAGNOSTICS | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | The following exceptions are thrown by this module: | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =over | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | =item C<< "You must provide a path to localization directory." >> | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | This exception is thrown if you haven't provided the C subroutine | 
| 237 |  |  |  |  |  |  | a path to a localization file, or a directory of localization files. Read | 
| 238 |  |  |  |  |  |  | the documentation for the C subroutine above. | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =item C<< "Can't open localization directory: %s" and "Can't close localization directory: %s" >> | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | This exception is thrown if Locale::Meta failed to open/close the directory | 
| 243 |  |  |  |  |  |  | of the localization files. This will probably happen due to permission | 
| 244 |  |  |  |  |  |  | problems. The error message should include the actual reason for the failure. | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =item C<< "Path must be to a directory or a JSON file." >> | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | This exception is thrown if you passed a wrong value to the C subroutine | 
| 249 |  |  |  |  |  |  | as the path to the localization directory/file. Either the path is wrong and thus | 
| 250 |  |  |  |  |  |  | does not exist, or the path does exist, but is not a directory and not a file. | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | =item C<< "Can't open localization file %s: %s" and "Can't close localization file %s: %s" >> | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | This exception is thrown if Locale::Wolowitz fails to open/close a specific localization | 
| 255 |  |  |  |  |  |  | file. This will usually happen because of permission problems. The error message | 
| 256 |  |  |  |  |  |  | will include both the name of the file, and the actual reason for the failure. | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | =back | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =head1 CONFIGURATION AND ENVIRONMENT | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | C requires no configuration files or environment variables. | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =head1 DEPENDENCIES | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | C B on the following CPAN modules: | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | =over | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | =item * L | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | =item * L | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | =back | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | C recommends L or L for faster | 
| 277 |  |  |  |  |  |  | parsing of JSON files. | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | =head1 INCOMPATIBILITIES WITH OTHER MODULES | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | None reported. | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | =head1 BUGS AND LIMITATIONS | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | No bugs have been reported. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | Please report any bugs or feature requests to | 
| 288 |  |  |  |  |  |  | C | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | =head1 DISCLAIMER OF WARRANTY | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | 
| 293 |  |  |  |  |  |  | FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | 
| 294 |  |  |  |  |  |  | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | 
| 295 |  |  |  |  |  |  | PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER | 
| 296 |  |  |  |  |  |  | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
| 297 |  |  |  |  |  |  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE | 
| 298 |  |  |  |  |  |  | ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH | 
| 299 |  |  |  |  |  |  | YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL | 
| 300 |  |  |  |  |  |  | NECESSARY SERVICING, REPAIR, OR CORRECTION. | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | 
| 303 |  |  |  |  |  |  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | 
| 304 |  |  |  |  |  |  | REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE | 
| 305 |  |  |  |  |  |  | LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, | 
| 306 |  |  |  |  |  |  | OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE | 
| 307 |  |  |  |  |  |  | THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | 
| 308 |  |  |  |  |  |  | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | 
| 309 |  |  |  |  |  |  | FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | 
| 310 |  |  |  |  |  |  | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | 
| 311 |  |  |  |  |  |  | SUCH DAMAGES. | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =cut | 
| 314 |  |  |  |  |  |  | 1; |