File Coverage

blib/lib/Getopt/EX/i18n.pm
Criterion Covered Total %
statement 17 99 17.1
branch 0 50 0.0
condition 0 18 0.0
subroutine 6 20 30.0
pod 0 7 0.0
total 23 194 11.8


line stmt bran cond sub pod time code
1 1     1   731 use v5.14;
  1         4  
2             package Getopt::EX::i18n;
3              
4             our $VERSION = '0.09';
5              
6 1     1   6 use warnings;
  1         2  
  1         25  
7 1     1   636 use Data::Dumper;
  1         6806  
  1         300  
8              
9             =encoding utf-8
10              
11             =head1 NAME
12              
13             Getopt::EX::i18n - General i18n module
14              
15             =head1 SYNOPSIS
16              
17             command -Mi18n [ options ]
18              
19             =head1 DESCRIPTION
20              
21             This module B provide an easy way to set locale environment
22             before executing arbitrary command. Locale list is taken from the
23             system by C command. Next list is a sample locales
24             available on macOS 10.15 (Catalina).
25              
26             af_ZA Afrikaans / South Africa
27             am_ET Amharic / Ethiopia
28             be_BY Belarusian / Belarus
29             bg_BG Bulgarian / Bulgaria
30             ca_ES Catalan; Valencian / Spain
31             cs_CZ Czech / Czech Republic
32             da_DK Danish / Denmark
33             de_AT German / Austria
34             de_CH German / Switzerland
35             de_DE German / Germany
36             el_GR Greek, Modern (1453-) / Greece
37             en_AU English / Australia
38             en_CA English / Canada
39             en_GB English / United Kingdom
40             en_IE English / Ireland
41             en_NZ English / New Zealand
42             en_US English / United States
43             es_ES Spanish / Spain
44             et_EE Estonian / Estonia
45             eu_ES Basque / Spain
46             fi_FI Finnish / Finland
47             fr_BE French / Belgium
48             fr_CA French / Canada
49             fr_CH French / Switzerland
50             fr_FR French / France
51             he_IL Hebrew / Israel
52             hr_HR Croatian / Croatia
53             hu_HU Hungarian / Hungary
54             hy_AM Armenian / Armenia
55             is_IS Icelandic / Iceland
56             it_CH Italian / Switzerland
57             it_IT Italian / Italy
58             ja_JP Japanese / Japan
59             kk_KZ Kazakh / Kazakhstan
60             ko_KR Korean / Korea, Republic of
61             lt_LT Lithuanian / Lithuania
62             nl_BE Dutch / Belgium
63             nl_NL Dutch / Netherlands
64             no_NO Norwegian / Norway
65             pl_PL Polish / Poland
66             pt_BR Portuguese / Brazil
67             pt_PT Portuguese / Portugal
68             ro_RO Romanian / Romania
69             ru_RU Russian / Russian Federation
70             sk_SK Slovak / Slovakia
71             sl_SI Slovenian / Slovenia
72             sr_YU Serbian / Yugoslavia
73             sv_SE Swedish / Sweden
74             tr_TR Turkish / Turkey
75             uk_UA Ukrainian / Ukraine
76             zh_CN Chinese / China
77             zh_HK Chinese / Hong Kong
78             zh_TW Chinese / Taiwan, Province of China
79              
80             As for Japanese locale C, following options are defined by
81             default, and set C environment as C.
82              
83             LOCALE: --ja_JP (raw)
84             --ja-JP (dash)
85             --jaJP (long)
86             --jajp (long_lc)
87             LANGUAGE: --ja (language)
88             TERRITORY: --JP (territory)
89             --jp (territory_lc)
90              
91             Short language option (C<--ja>) is defined in the alphabetical order
92             of the territory code, so the option C<--en> is assigned to C.
93             But if the same territory name is found as language, it takes
94             precedence; German is used in three locales (C, C,
95             C) but option C<--de> is defined as C.
96              
97             Territory options (C<--JP> and C<--jp>) are defined only when the same
98             language option is not defined by other entry, and only single entry
99             can be found for the territory. Option for Switzerland is not defined
100             because there are three entries (C, C, C).
101             Territory option C<--AM> is assigned to C, but language option
102             C<--am> is assigned to C.
103              
104             =head1 OPTION
105              
106             Option parameter can be given with B function called with
107             module declaration.
108              
109             command -Mi18n::setopt(name[=value])
110              
111             =over 7
112              
113             =item B
114              
115             =item B
116              
117             =item B
118              
119             =item B
120              
121             =item B
122              
123             =item B
124              
125             =item B
126              
127             These parameter tells which option is defined. All options are
128             enabled by default. You can disable territory option like this:
129              
130             command -Mi18n::setopt(territory=0,territory_lc=0)
131              
132             command -Mi18n::setopt=territory=0,territory_lc=0
133              
134             =item B
135              
136             Show locale information.
137              
138             $ optex -Mi18n::setopt=verbose date --it
139             LANG=it_IT (Italian / Italy)
140             Gio 4 Giu 2020 16:47:33 JST
141              
142             =item B
143              
144             Show option list.
145              
146             =item B=I
147              
148             Set the option to display option list and exit. You can introduce a
149             new option B<-l> to show available option list:
150              
151             -Mi18n::setopt(listopt=-l)
152              
153             =item B=I
154              
155             Specify prefix string. Default is C<-->.
156              
157             =back
158              
159             =head1 BUGS
160              
161             Support only UTF-8.
162              
163             =head1 SEE ALSO
164              
165             =over 7
166              
167             =item L
168              
169             L
170              
171             =item L
172              
173             You can execute arbitrary command on the system getting the benefit of
174             B using B.
175              
176             $ optex -Mi18n cal 2020 --am
177              
178             L
179              
180             L
181              
182             =back
183              
184             =head1 LICENSE
185              
186             Copyright (C) 2020 Kazumasa Utashiro.
187              
188             This library is free software; you can redistribute it and/or modify
189             it under the same terms as Perl itself.
190              
191             =head1 AUTHOR
192              
193             Kazumasa Utashiro Ekaz@utashiro.comE
194              
195             =cut
196              
197             my %opt = (
198             raw => 1,
199             dash => 1,
200             long => 1,
201             long_lc => 1,
202             language => 1,
203             territory => 1,
204             territory_lc => 1,
205             verbose => 0,
206             list => 0,
207             prefix => '--',
208             listopt => undef,
209             );
210              
211             my $module;
212              
213             sub initialize {
214 0 0   0 0   return if state $called++;
215 0           my($obj, $argv) = @_;
216 0           $module = $obj;
217 0           setup();
218             }
219              
220             my @locale;
221             my %locale;
222             my %lang;
223             my %cc;
224             my %opthash;
225              
226             package LocaleObj {
227             sub new {
228 0     0     my($class, %hash) = @_;
229 0           bless \%hash, $class;
230             }
231 0   0 0     sub name { $_[0]->{name} // '' }
232 0   0 0     sub lang { $_[0]->{lang} // '' }
233 0   0 0     sub cc { $_[0]->{cc} // '' }
234             sub create {
235 0     0     (my $class, local $_) = @_;
236 0 0         /^(?(?[a-z][a-z])_(?[A-Z][A-Z]))/ or die;
237 1     1   519 $class->new(%+);
  1         385  
  1         58  
  0            
238             }
239 1     1   437 use Getopt::EX::i18n::iso639 qw(%iso639);
  1         2  
  1         120  
240 1     1   467 use Getopt::EX::i18n::iso3361 qw(%iso3361);
  1         2  
  1         1231  
241 0 0   0     sub lang_name { $iso639 {+shift->lang} || 'UNKNOWN' }
242 0 0   0     sub cc_name { $iso3361{+shift->cc} || 'UNKNOWN' }
243             }
244              
245             sub finalize {
246 0     0 0   my($obj, $argv) = @_;
247 0           for my $locale (sort @locale) {
248 0 0         $locale =~ /^(?\w\w)_(?\w\w)$/ or next;
249 0           my($lang, $cc) = @+{qw(lang cc)};;
250 0           my @list;
251 0 0         push @list, "$locale" if $opt{raw};
252 0 0         push @list, "$lang-$cc" if $opt{dash};
253 0 0         push @list, "$lang$cc" if $opt{long};
254 0           $cc = lc $cc;
255 0 0         push @list, "$lang$cc" if $opt{long_lc};
256 0 0         if ($opt{language}) {
257 0 0 0       if (!$opthash{$lang} or $lang eq $cc) {
258 0           push @list, $lang;
259             }
260             }
261 0 0 0       if ($lang eq $cc or @{$cc{$cc}} == 1) {
  0            
262 0 0         push @list, uc $cc if $opt{territory};
263 0 0 0       push @list, $cc if $opt{territory_lc} and !$lang{$cc};
264             }
265 0           for (@list) {
266 0           $opthash{$_} = LocaleObj->create($locale);
267             }
268             }
269              
270 0           $obj->mode(function => 1);
271 0 0         if (my $listopt = $opt{listopt}) {
272 0           $obj->setopt($listopt, "&options(show,exit)");
273             }
274 0           &options(set => 1, show => $opt{list});
275 0           return;
276             }
277              
278             sub options {
279 0     0 0   my %arg = (
280             set => 0, # set option
281             show => 0, # print option
282             exit => 0, # exit at the end
283             @_);
284 0           my @keys = do {
285 0           map { $_->[0] }
286 0 0 0       sort { $a->[1] cmp $b->[1] ||
287             lc $a->[0] cmp lc $b->[0] || $a->[0] cmp $b->[0] }
288 0           map { [ $_, $opthash{$_}->cc ] }
  0            
289             keys %opthash;
290             };
291 0           for my $opt (@keys) {
292 0           my $obj = $opthash{$opt};
293 0           my $option = $opt{prefix} . $opt;
294 0           my $name = $obj->name;
295 0           my $locale = $locale{$name};
296 0           my $call = "&setenv(LANG=$locale)";
297 0 0         $module->setopt($option, $call) if $arg{set};
298 0 0         if ($arg{show}) {
299             printf "option %-*s %s # %s / %s\n",
300 0           (state $optwidth = length($opt{prefix}) + length($name)),
301             $option, $call,
302             $obj->cc_name, $obj->lang_name;
303             }
304             }
305 0 0         exit if $arg{exit};
306 0           return ();
307             }
308              
309             sub setup {
310 0 0   0 0   return if state $called++;
311 0 0         grep { -x "$_/locale" } split /:/, $ENV{PATH} or return;
  0            
312 0           for (`locale -a`) {
313 0           chomp;
314 0 0         /^(([a-z][a-z])_([A-Z][A-Z]))(?=(?i:$|\.utf))/ or next;
315 0           my($name, $lang, $cc) = ($1, $2, lc $3);
316 0 0         if (my $last = $locale{$name}) {
317 0 0         $locale{$name} = $_ if length($_) < length($last);
318 0           next;
319             }
320 0           $locale{$name} = $_;
321 0           push @locale, $name;
322 0           push @{ $lang{$lang} }, $name;
  0            
323 0           push @{ $cc{$cc} }, $name;
  0            
324             }
325             }
326              
327             sub locales {
328 0     0 0   chomp( my @locale = `locale -a` );
329 0           grep { /^\w\w_\w\w$/ } @locale;
  0            
330             }
331              
332             sub setopt {
333 0     0 0   %opt = (%opt, @_);
334             }
335              
336             sub setenv {
337 0     0 0   while (@_ >= 2) {
338 0           my($key, $value) = splice @_, 0, 2;
339 0 0         if ($opt{verbose}) {
340 0           my $l = LocaleObj->create($value);
341 0           warn sprintf("%s=%s (%s / %s)\n",
342             $key, $value, $l->lang_name, $l->cc_name);
343             }
344 0           $ENV{$key} = $value;
345             }
346 0           return ();
347             }
348              
349             1;
350              
351             __DATA__