File Coverage

blib/lib/Getopt/EX/i18n.pm
Criterion Covered Total %
statement 36 96 37.5
branch 7 50 14.0
condition 3 18 16.6
subroutine 15 19 78.9
pod 0 7 0.0
total 61 190 32.1


line stmt bran cond sub pod time code
1 2     2   447906 use v5.14;
  2         11  
2             package Getopt::EX::i18n;
3              
4             our $VERSION = '1.02';
5              
6 2     2   19 use warnings;
  2         5  
  2         176  
7 2     2   1533 use Data::Dumper;
  2         21903  
  2         1057  
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 provides an easy way to set locale environment
22             before executing arbitrary command. Locale list is taken from the
23             system by C command. The following list shows 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             For Japanese locale C, the following options are defined by
81             default, and set C environment to C. The environment
82             variable name can be changed by B option.
83              
84             LOCALE: --ja_JP (raw)
85             --ja-JP (dash)
86             --jaJP (long)
87             --jajp (long_lc)
88             LANGUAGE: --ja (language)
89             TERRITORY: --JP (territory)
90             --jp (territory_lc)
91              
92             Short language option (C<--ja>) is defined in alphabetical order
93             of the territory code, so the option C<--en> is assigned to C.
94             However, if the same territory name is found as language, it takes
95             precedence; German is used in three locales (C, C,
96             C) but option C<--de> is defined as C.
97              
98             Territory options (C<--JP> and C<--jp>) are defined only when the same
99             language option is not defined by other entry, and only a single entry
100             can be found for the territory. Options for Switzerland are not defined
101             because there are three entries (C, C, C).
102             Territory option C<--AM> is assigned to C, but language option
103             C<--am> is assigned to C.
104              
105             =head1 OPTION
106              
107             Option parameter can be given with B function called with
108             module declaration.
109              
110             command -Mi18n::setopt(name[=value])
111              
112             =over 7
113              
114             =item B
115              
116             =item B
117              
118             =item B
119              
120             =item B
121              
122             =item B
123              
124             =item B
125              
126             =item B
127              
128             These parameters tell which options are defined. All options are
129             enabled by default. You can disable territory options like this:
130              
131             command -Mi18n::setopt(territory=0,territory_lc=0)
132              
133             command -Mi18n::setopt=territory=0,territory_lc=0
134              
135             =item B
136              
137             Show locale information.
138              
139             $ optex -Mi18n::setopt=verbose date --it
140             LANG=it_IT (Italian / Italy)
141             Gio 4 Giu 2020 16:47:33 JST
142              
143             =item B
144              
145             Show option list.
146              
147             =item B=I
148              
149             Set the option to display the option list and exit. You can introduce a
150             new option B<-l> to show the available option list:
151              
152             -Mi18n::setopt(listopt=-l)
153              
154             =item B=I
155              
156             Specify prefix string. Default is C<-->.
157              
158             =item B=I
159              
160             Specify environment variable name to be set. Default is C.
161              
162             =back
163              
164             =head1 DEPENDENCIES
165              
166             This module uses L and L
167             to provide language and country names for locale codes.
168              
169             =head1 BUGS
170              
171             Support only UTF-8.
172              
173             =head1 SEE ALSO
174              
175             =over 7
176              
177             =item L
178              
179             L
180              
181             =item L
182              
183             You can execute arbitrary command on the system getting the benefit of
184             B using B.
185              
186             $ optex -Mi18n cal 2020 --am
187              
188             L
189              
190             L
191              
192             =back
193              
194             =head1 LICENSE
195              
196             Copyright (C) 2020-2025 Kazumasa Utashiro.
197              
198             This library is free software; you can redistribute it and/or modify
199             it under the same terms as Perl itself.
200              
201             =head1 AUTHOR
202              
203             Kazumasa Utashiro
204              
205             =cut
206              
207             my %opt = (
208             env => 'LANG',
209             raw => 1,
210             dash => 1,
211             long => 1,
212             long_lc => 1,
213             language => 1,
214             territory => 1,
215             territory_lc => 1,
216             verbose => 0,
217             list => 0,
218             prefix => '--',
219             listopt => undef,
220             );
221              
222             my $module;
223              
224             sub initialize {
225 0 0   0 0 0 return if state $called++;
226 0         0 my($obj, $argv) = @_;
227 0         0 $module = $obj;
228 0         0 setup();
229             }
230              
231             my @locale;
232             my %locale;
233             my %lang;
234             my %cc;
235             my %opthash;
236              
237             package #
238             Local::LocaleObj {
239             sub new {
240 1     1   10 my($class, %hash) = @_;
241 1         5 bless \%hash, $class;
242             }
243 1   50 1   615 sub name { $_[0]->{name} // '' }
244 3   50 3   22 sub lang { $_[0]->{lang} // '' }
245 3   50 3   21 sub cc { $_[0]->{cc} // '' }
246             sub create {
247 1     1   138777 (my $class, local $_) = @_;
248 1 50       13 /^(?(?[a-z][a-z])_(?[A-Z][A-Z]))/ or die;
249 1         19 $class->new(%+);
250             }
251              
252 2     2   1496 use Locale::Codes::Language;
  2         628479  
  2         588  
253 2 50   2   6 sub lang_name { code2language(+shift->lang) || 'UNKNOWN' }
254              
255 2     2   1111 use Locale::Codes::Country;
  2         85694  
  2         3430  
256 2 50   2   8 sub cc_name { code2country(+shift->cc) || 'UNKNOWN' }
257             }
258              
259             sub finalize {
260 0     0 0 0 my($obj, $argv) = @_;
261 0         0 for my $locale (sort @locale) {
262 0 0       0 $locale =~ /^(?\w\w)_(?\w\w)$/ or next;
263 0         0 my($lang, $cc) = @+{qw(lang cc)};;
264 0         0 my @list;
265 0 0       0 push @list, "$locale" if $opt{raw};
266 0 0       0 push @list, "$lang-$cc" if $opt{dash};
267 0 0       0 push @list, "$lang$cc" if $opt{long};
268 0         0 $cc = lc $cc;
269 0 0       0 push @list, "$lang$cc" if $opt{long_lc};
270 0 0       0 if ($opt{language}) {
271 0 0 0     0 if (!$opthash{$lang} or $lang eq $cc) {
272 0         0 push @list, $lang;
273             }
274             }
275 0 0 0     0 if ($lang eq $cc or @{$cc{$cc}} == 1) {
  0         0  
276 0 0       0 push @list, uc $cc if $opt{territory};
277 0 0 0     0 push @list, $cc if $opt{territory_lc} and !$lang{$cc};
278             }
279 0         0 for (@list) {
280 0         0 $opthash{$_} = Local::LocaleObj->create($locale);
281             }
282             }
283              
284 0         0 $obj->mode(function => 1);
285 0 0       0 if (my $listopt = $opt{listopt}) {
286 0         0 $obj->setopt($listopt, "&options(show,exit)");
287             }
288 0         0 &options(set => 1, show => $opt{list});
289 0         0 return;
290             }
291              
292             sub options {
293 0     0 0 0 my %arg = (
294             set => 0, # set option
295             show => 0, # print option
296             exit => 0, # exit at the end
297             @_);
298 0         0 my @keys = do {
299 0         0 map { $_->[0] }
300 0 0 0     0 sort { $a->[1] cmp $b->[1] ||
301             lc $a->[0] cmp lc $b->[0] || $a->[0] cmp $b->[0] }
302 0         0 map { [ $_, $opthash{$_}->cc ] }
  0         0  
303             keys %opthash;
304             };
305 0         0 for my $opt (@keys) {
306 0         0 my $obj = $opthash{$opt};
307 0         0 my $option = $opt{prefix} . $opt;
308 0         0 my $name = $obj->name;
309 0         0 my $locale = $locale{$name};
310 0         0 my $call = "&setenv($opt{env}=$locale)";
311 0 0       0 $module->setopt($option, $call) if $arg{set};
312 0 0       0 if ($arg{show}) {
313             printf "option %-*s %s # %s / %s\n",
314 0         0 (state $optwidth = length($opt{prefix}) + length($name)),
315             $option, $call,
316             $obj->cc_name, $obj->lang_name;
317             }
318             }
319 0 0       0 exit if $arg{exit};
320 0         0 return ();
321             }
322              
323             sub setup {
324 1 50   1 0 1404 return if state $called++;
325 1 50       14 grep { -x "$_/locale" } split /:/, $ENV{PATH} or return;
  9         58  
326 1         11113 for (`locale -a`) {
327 3         24 chomp;
328 3 50       40 /^(([a-z][a-z])_([A-Z][A-Z]))(?=(?i:$|\.utf))/ or next;
329 0         0 my($name, $lang, $cc) = ($1, $2, lc $3);
330 0 0       0 if (my $last = $locale{$name}) {
331 0 0       0 $locale{$name} = $_ if length($_) < length($last);
332 0         0 next;
333             }
334 0         0 $locale{$name} = $_;
335 0         0 push @locale, $name;
336 0         0 push @{ $lang{$lang} }, $name;
  0         0  
337 0         0 push @{ $cc{$cc} }, $name;
  0         0  
338             }
339             }
340              
341             sub locales {
342 0     0 0 0 chomp( my @locale = `locale -a` );
343 0         0 grep { /^\w\w_\w\w$/ } @locale;
  0         0  
344             }
345              
346             sub setopt {
347 2     2 0 717 %opt = (%opt, @_);
348             }
349              
350             sub setenv {
351 2     2 0 719 while (@_ >= 2) {
352 3         9 my($key, $value) = splice @_, 0, 2;
353 3 50       7 if ($opt{verbose}) {
354 0         0 my $l = Local::LocaleObj->create($value);
355 0         0 warn sprintf("%s=%s (%s / %s)\n",
356             $key, $value, $l->lang_name, $l->cc_name);
357             }
358 3         21 $ENV{$key} = $value;
359             }
360 2         3 return ();
361             }
362              
363             1;
364              
365             __DATA__