1Locale::TextDomain::OO(U3s)er Contributed Perl DocumentatLioocnale::TextDomain::OO(3)
2
3
4
6 Locale::TextDomain::OO - Perl OO Interface to Uniforum Message
7 Translation
8
9 $Id: OO.pm 724 2018-12-24 11:50:32Z steffenw $
10
11 $HeadURL:
12 svn+ssh://steffenw@svn.code.sf.net/p/perl-gettext-oo/code/module/trunk/lib/Locale/TextDomain/OO.pm
13 $
14
16 1.036
17
18 Starting with version 1.000 the interface has changed.
19
21 This module provides a high-level interface to Perl message
22 translation.
23
24 It is based on Singleton objects for translator and lexicon. So it is
25 easy to attach that without any Web or Cmd plugins. There is no
26 conflict between singleton and more than 1 project at the same time.
27 The lexicon has a project key to split.
28
29 You are able to use the translator singleton earlier then language
30 initialization but do not call the translation methods before. Create
31 the lexicon singleton during compilation time.
32
33 Maketext is implemented here for moving existing projects to Gettext.
34
35 BabelFish is implemented for multi-plural in one phrase. But use
36 BabelFish in that case only. BabelFish is much harder to translate
37 (similar to maketext) by translation office or automatic translation.
38
39 For combinded plurals watch the Gettext multiplural example. In that
40 case you have to write that awful long formulas but it is possibel.
41
42 Never use keys instead of English language in code. You can't read,
43 execute (with placeholders) or test your code without translation.
44
45 Why a new module?
46 This module is similar to Locale::TextDomain and Locale::Maketext.
47
48 This module is not using/changing any system locale like
49 Locale::TextDomain.
50
51 This module has no magic in how to get the language like
52 Locale::Maketext. You decide what you need.
53
54 There are some plugins, so it is possible to use different the Gettext,
55 BabelFish and/or Maketext styles.
56
57 Locale::TextDomain::OO has a flexible object oriented interface based
58 on Moo.
59
60 Creating the Lexicon and translating are two split things. So it is
61 possible to create the lexicon during the compilation phase. The
62 connection between both is the singleton mechanism of the lexicon
63 module using MooX::Singleton.
64
65 Plugin for the web framework?
66 This module was first used in a Catalyst based web project.
67
68 It was great to call everything on the Catalyst context object. It was
69 great until we used module in web and shell scripts together and called
70 the translation methods there.
71
72 What we have done was removing the Catalyst Maketext plugin. We are
73 using a role in the Catalyst application and so we can call all the
74 translation methods on the Catalyst context object like before.
75
76 For the shell scripts we have a role for this Cmd's to call all the
77 methods on $self.
78
79 In that roles we delegate a bunch of methods to Locale::TextDomain::OO.
80 We also added our project specific methods there.
81
82 Write you own role. There is no need for a plugin for any framework.
83
84 Who is sets the languages?
85 Not this module.
86
87 In a web application are lots of ideas to do that. But never we use
88 the server settings, never.
89
90 The HTTP request contains that information. Extend this with super and
91 panic languages. Normally you support only a bunch of world languages,
92 so reduce. This module will find the first match.
93
94 Or you store and get the language in the session.
95
96 Or your application has a language selection menu. (Never use flags
97 for that. There are multilanguage countries and more countries have
98 the same language.)
99
100 How to extract the project files?
101 Use module Locale::TextDomain::OO::Extract. This is a base class for
102 all source scanner to create po/pot files. Use this base class and
103 give this module the rules or use one of the already extended classes.
104 Locale::TextDomain::OO::Extract::Perl is a extension for Perl code and
105 so on.
106
107 How to build the lexicon - without Gettext tools?
108 Use module Locale::TextDomain::OO::Extract::Process. This is able to
109 read, strip, extract, merge, write and clean PO and MO files.
110
111 There is a module named
112 Locale::TextDomain::OO::Util::JoinSplitLexiconKeys. That helps you to
113 build the lexicon keys. Do not join the keys by yourself, it can break
114 in future versions. Use the category, domain, language and project
115 part to describe your lexicon. category, domain and language you can
116 use during translation. The only reason for project is to separate
117 your different lexicons.
118
119 How to use the lexicon?
120 The lexicons or all lexicons are stored in memory. The idea to build a
121 combined lexicon is overwriting hash values by same key. So read and
122 overwrite common stuff with specials. In an example case the region
123 file en-gb.mo contains only the differences. The common file en.mo
124 contains all messages. So it is possible to create full lexicons for
125 any language and region. What you have to describe is: Where are the
126 files - directories. Pick up the common file en.mo as en. Pick up the
127 common file en.mo and the special en-gb.mo as en-gb.
128
129 Do not follow the dead end of Locale::Maketext!
130 But it is allowed to use that writing for backward compatiblity.
131
132 What is the problem of Maketext?
133
134 • Locale::Maketext allows 2 plural forms (and zero) only. This is
135 changeable, but the developer has to control the plural forms. He
136 is not an omniscient translator.
137
138 Gettext allows as much as needed plural forms at destination
139 language.
140
141 • Maketext has a plural formula as placeholder in strings to
142 translate. Gettext has fully readable strings with simple
143 placeholders. Then a very good automatic translation is possible
144 and this module helps you.
145
146 (Some projects have keywords instead of English language in code.
147 This module helps you to write readable, uncrypic code.
148 Programmers optimisation is a very bad idea for translations.
149 Never split a sentence.)
150
151 • 'quant' inside a phrase is the end of the automatic translation
152 because quant is an 'or'-construct.
153
154 begin of phrase [quant,_1,singular,plural,zero] end of phrase
155
156 Gettext used full qualified sentences.
157
158 • The plural form is allowed after a number, followed by a
159 whitespace, not a non-breaking whitespace.
160
161 1 book
162 2 books
163
164 A plural form can not be before a number.
165
166 It is 1 book.
167 These are 2 books.
168
169 Gettext is using full qualified sentences.
170
171 • There is no plural form without a number in the phrase.
172
173 I like this book.
174 I like these books.
175
176 Gettext used an extra count to select the plural form.
177 Placeholders are placeholders not combined things.
178
179 • Placeholders are numbered serially. It is difficult to translate
180 this because the sense of the phrase could be lost.
181
182 Still [_1] [_2] to [_3].
183
184 Still 5 hours to midnight.
185 Still 15 days to Olympics.
186
187 Locale::TextDomain::OO used named placeholders like
188
189 Still {count :num} {datetime unit} to {event}.
190
191 • But there are lots of modules around Locale::Maketext.
192
193 This is the reason for another module to have:
194
195 • Endless (real: up to 6) plural forms controlled by the translator
196 and not by the developer.
197
198 • Named placeholders.
199
200 More information
201 Run the examples of this distribution (folder example).
202
203 Overview
204 Application calls Application calls Application calls
205 Gettext methods Gettext and Maketext methods
206 (Hint: select this) Maketext methods |
207 | (Hint: Maketext already |
208 | exists in your project) |
209 | | |
210 v v v
211 .----------------------------------------------------------------------.
212 | Locale::TextDomain::OO |
213 | with plugin LanguageOfLanguages |
214 | with plugins Locale::TextDomain::OO::Plugin::Expand::... |
215 |----------------------------------------------------------------------|
216 | Gettext |\ /| Maketext |
217 | Gettext::DomainAndCategory | | | | |
218 | Gettext::Loc (Hint: select this) | | | | Maketext::Loc |
219 | Gettext::Loc::DomainAndCategory | > and < | (Hint: select this) |
220 | Gettext::Named (experimental) | | | | |
221 | BabelFish::Loc | | | | Maketext::Localise |
222 | BabelFish::Loc::DomainAndCategory |/ \| Maketext::Localize |
223 `----------------------------------------------------------------------'
224 ^
225 |
226 .--------------------------'-----------------.
227 | Locale::TextDomain::OO::Singleton::Lexicon |-------------------------.
228 `--------------------------------------------' |
229 ^ |
230 | |
231 .--------------------------'-------------------------------. |
232 | build lexicon using Locale::TextDomain::OO::Lexicon::... | |
233 |----------------------------------------------------------| |
234 | Hash | File::PO | File::MO | |
235 `----------------------------------------------------------' |
236 ^ ^ ^ ^ |
237 | | | | |
238 .-----'-----. _____|_____ .-----'----. .-----'----. |
239 | Perl | /_ _ _ _ _ _\ | po files |-. | mo files |-. |
240 | data | | | `----------' |-. `----------' |-. |
241 | structure | | Database | `----------' | `----------' | |
242 `-----------' `-----------' `----------' `----------' |
243 ^ ^ ^ ^ |
244 | | | | |
245 | .---------------------------' | | |
246 _____|___|_____ | | |
247 /_ _ _ _ _ _ _ _\ .----------------------------------------------. |
248 | Autotranslate | | build using PO/MO | |
249 | Cache | | Locale::TextDomain::OO::Extract | |
250 | CacheDatabase | | and Locale::TextDomain::OO::Extract::Process | |
251 `---------------' `----------------------------------------------' |
252 |
253 .-------------------------------------'
254 v
255 .---------------------------------------------------------------------.
256 | build JSON lexicon using Locale::TextDomain::OO::Lexicon::StoreJSON |
257 |---------------------------------------------------------------------|
258 | to_json | to_javascript | to_html |
259 `---------------------------------------------------------------------'
260 |
261 v
262 .---------------------------------------.
263 | var localeTextDomainOOLexicon = json; |
264 `---------------------------------------'
265 ^
266 |
267 .--------------------------------'-------------------------------------------------.
268 | requires: |
269 | - http://jquery.com/ |
270 | - javascript/Locale/TextDomain/OO/Util/Constants.js |
271 | - javascript/Locale/TextDomain/OO/Util/JoinSplitLexiconKeys.js |
272 | - javascript/Locale/Utils/PlaceholderNamed.js |
273 | |
274 | implemented: |
275 | javascript/Locale/TextDomain/OO.js |
276 | javascript/Locale/TextDomain/OO/Plugin/Expand/BabelFish.js (under development) |
277 | javascript/Locale/TextDomain/OO/Plugin/Expand/Gettext.js |
278 | javascript/Locale/TextDomain/OO/Plugin/Expand/Gettext/DomainAndCategory.js |
279 | javascript/Locale/TextDomain/OO/Plugin/Expand/Gettext/Loc.js (Hint: select this) |
280 | javascript/Locale/TextDomain/OO/Plugin/Expand/Gettext/Loc/DomainAndCategory.js |
281 | |
282 | not implemented: |
283 | javascript/Locale/TextDomain/OO/Expand/Maketext.js |
284 | javascript/Locale/TextDomain/OO/Expand/Maketext/Loc.js |
285 | javascript/Locale/TextDomain/OO/Expand/Maketext/Localise.js |
286 | javascript/Locale/TextDomain/OO/Expand/Maketext/Localize.js |
287 | |
288 | Example: |
289 | javascript/Example.html |
290 `----------------------------------------------------------------------------------'
291 ^ ^ ^
292 | | |
293 JavaScript calls JavaScript calls JavaScript calls
294 Gettext methods Gettext and Maketext methods
295 (Hint: select this) Maketext methods (Maketext not implemented)
296 (Maketext not implemented)
297
299 require Locale::TextDomain::OO;
300 use Log::Any qw($log);
301 my $loc = Locale::TextDomain::OO->new(
302 # all parameters are optional
303 plugins => [ qw(
304 Expand::Gettext::Loc
305 +My::Special::Plugin
306 ) ],
307 language => 'de', # default is i-default
308 category => 'LC_MESSAGES', # default is q{}
309 domain => 'MyDomain', # default is q{}
310 project => 'MyProject', # default is undef
311 filter => sub {
312 my ($self, $translation_ref) = @_;
313 # encode if needed
314 # run a formatter if needed, e.g.
315 ${$translation_ref} =~ s{__ ( .+? ) __}{<b>$1</b>}xmsg;
316 return;
317 },
318 logger => sub {
319 my ($message, $arg_ref) = @_;
320 my $type = $arg_ref->{type}; # debug or warn
321 $log->$type($message);
322 return;
323 },
324 );
325
326 This configuration would be use Lexicon
327 "de:LC_MESSAGES:MyDomain::MyProject". That lexicon should be filled
328 with data.
329
330 as singleton
331 Instead of method new call method instance to get a singleton.
332
333 my $instance = Locale::TextDomain::OO->instance(
334 # Same parameters like new,
335 # Initialization on first call only.
336 );
337 $same_instance = Locale::TextDomain::OO->instance;
338
339 Attributes handled e.g. with plugin Expand::Gettext::Loc
340 This plugin can handle named placeholders like "{name}". For numeric
341 placeholders add attribute ":num". That allows easier automatic
342 translation and to localize numbers. E.g. write "{books :numf}" to
343 msgid, msgid_plural.
344
345 Grammar rules for string placeholders are able to handle affixes. The
346 translation office can add attributes, e.g. "{town :accusative}" to
347 msgstr, msgstr[n].
348
349 Add the modifier code like that to handle that attributes.
350
351 $loc->expand_gettext->modifier_code(
352 sub {
353 my ( $value, $attribute ) = @_;
354 if ( $loc->language eq 'ru' ) {
355 if ( $attribute eq 'accusative' ) {
356 ...
357 }
358 }
359 elsif ( $loc->language eq 'de' }xms ) {
360 if ( $attribute eq 'numf' ) {
361 ...
362 $value =~ tr{.,}{,.};
363 }
364 }
365 return $value;
366 },
367 );
368
369 Attributes handled with plugin Expand::Gettext
370 Like before but a little difference. Instead of
371
372 $loc->expand_gettext_loc->modifier_code(...
373
374 write
375
376 $loc->expand_gettext->modifier_code(...
377
378 Localize numbers with plugin Expand::Maketext
379 Add the code to localize numbers.
380
381 $loc->expand_maketext->formatter_code(
382 sub {
383 my $value = shift;
384 if ( $loc->language eq 'de' ) {
385 ...
386 $value =~ tr{.,}{,.};
387 }
388 return $value;
389 },
390 );
391
393 How to use the JavaScript framework see
394 Locale::TextDomain::OO::JavaScript.
395
397 method new
398 see SYNOPSIS
399
400 It is not possible to call method new with different plugins parameter.
401 The first call will load the plugins. Later call set parameter plugins
402 equal or do not set.
403
404 method instance
405 When using the singleton mechanism, the object need not be transported
406 through all the subroutines.
407
408 Instead of
409
410 my $loc = Locale::TextDomain::OO->new(...);
411 ...
412 $loc->any_method(...);
413
414 write
415
416 Locale::TextDomain::OO->instance(...);
417 ...
418 my $loc = Locale::TextDomain::OO->instance;
419 $loc->any_method(...);
420
421 In case of webserver child or similar, set the language for every
422 request not as parameter of the first instance call.
423
424 method language
425 Set the language an prepare the translation. You know exactly how to
426 set. This module is stupid.
427
428 $loc->language( $language );
429
430 Get back
431
432 $language = $loc->language;
433
434 method category
435 You are able to ignore or set the category. That depends on your
436 project.
437
438 $loc->category($category || q{} );
439 $category = $loc->category;
440
441 method domain
442 You are able to ignore or set the domain. That depends on your
443 project.
444
445 $loc->domain($domain || q{} );
446 $domain = $loc->domain;
447
448 method filter
449 You are allowed to run code after each translation.
450
451 $loc->filter( sub {
452 my ( $self, $translation_ref ) = @_;
453
454 # $self is $loc
455 # manipulate ${$translation_ref}
456 # do not undef ${$translation_ref}
457
458 return;
459 } );
460
461 Switch off the filter
462
463 $loc->filter(undef);
464
465 method logger
466 Set the logger
467
468 $loc->logger(
469 sub {
470 my ($message, $arg_ref) = @_;
471 my $type = $arg_ref->{type};
472 $log->$type($message);
473 return;
474 },
475 );
476
477 $arg_ref contains
478
479 object => $loc, # the object itself
480 type => 'debug', # or 'warn'
481 event => 'language,selection', # 'language,selection,fallback'
482 # or 'translation,fallback'
483
484 method translate
485 Do never call that method in your project. This method was called from
486 expand plugins only.
487
488 $translation
489 = $self->translate($msgctxt, $msgid, $msgid_plural, $count, $is_n);
490
491 method run_filter
492 Do never call that method in your project. This method was called from
493 expand plugins only.
494
495 $self->filter(\$translation);
496
498 Inside of this distribution is a directory named example. Read the
499 file README there. Then run the *.pl files.
500
502 confess
503
505 none
506
508 Locale::TextDomain::OO::Translator
509
511 not known
512
514 In the Gettext manual you can read at "15.5.18.9 Bugs, Pitfalls, And
515 Things That Do Not Work" something that is not working with Perl. The
516 examples there are rewritten and explained here.
517
518 string interpolation and joined strings
519 print <<"EOT";
520 $loc->loc_(
521 'The dot operator'
522 . ' does not work'
523 . ' here!'
524 )
525 Likewise, you cannot @{[ $loc->loc_('interpolate function calls') ]}
526 inside quoted strings or quote-like expressions.
527 EOT
528
529 The fist call can not work. Methods are not callable in interpolated
530 strings/"here documents". The . operator is normally not implemented
531 at the extractor. The first parameter of method loc_ must be a
532 constant.
533
534 There is no problem for the second call because the extractor extracts
535 the Perl file as text and did not parse the code.
536
537 Regex eval
538 This example is no problem here, because the file is extracted as text.
539
540 s/<!--START_OF_WEEK-->/$loc->loc_('Sunday')/e;
541
542 named placeholders
543 Method loc_ is an alias for method loc_x. But {OPTIONS} is not a
544 placeholder because key "OPTIONS" is not in parameters.
545
546 die $loc->loc_("usage: $0 {OPTIONS} FILENAME...\n");
547
548 die $loc->loc_x("usage: {program} {OPTIONS} FILENAME...\n", program => $0);
549
551 Locale::TextDoamin
552
553 Locale::Maketext
554
555 <http://www.gnu.org/software/gettext/manual/gettext.html>
556
557 <http://en.wikipedia.org/wiki/Gettext>
558
559 <http://translate.sourceforge.net/wiki/l10n/pluralforms>
560
561 <http://rassie.org/archives/247> The choice of the right module for the
562 translation.
563
565 Steffen Winkler
566
568 Copyright (c) 2009 - 2019, Steffen Winkler "<steffenw at cpan.org>".
569 All rights reserved.
570
571 This module is free software; you can redistribute it and/or modify it
572 under the same terms as Perl itself.
573
574
575
576perl v5.34.0 2021-07-22 Locale::TextDomain::OO(3)