File Coverage

lib/Text/PO/Element.pm
Criterion Covered Total %
statement 196 271 72.3
branch 68 124 54.8
condition 10 38 26.3
subroutine 44 54 81.4
pod 35 35 100.0
total 353 522 67.6


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## PO Files Manipulation - ~/lib/Text/PO/Element.pm
3             ## Version v0.4.2
4             ## Copyright(c) 2025 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2021/07/23
7             ## Modified 2025/12/05
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package Text::PO::Element;
14             BEGIN
15 0         0 {
16 5     5   44 use strict;
  5         10  
  5         255  
17 5     5   34 use warnings;
  5         12  
  5         434  
18 5     5   36 use parent qw( Module::Generic );
  5         10  
  5         91  
19 5     5   449 use vars qw( $VERSION );
  5         8  
  5         738  
20             use overload (
21 0     0   0 '==' => sub { _obj_eq(@_) },
22 0     0   0 '!=' => sub { !_obj_eq(@_) },
23 36     36   3175 'eq' => sub { _obj_eq(@_) },
24 14     14   84 'ne' => sub { !_obj_eq(@_) },
25 5         60 fallback => 1,
26 5     5   32 );
  5         10  
27 5     5   3617 use Text::Wrap ();
  5         17127  
  5         315  
28 5     5   910 our $VERSION = 'v0.4.2';
29 5     5   55 use open ':std' => ':utf8';
  5         9  
  5         49  
30             };
31              
32 5     5   36 use strict;
  5         27  
  5         122  
33 5     5   23 use warnings;
  5         9  
  5         20505  
34              
35             $Text::Wrap::columns = 80;
36              
37             sub init
38             {
39 83     83 1 12925 my $self = shift( @_ );
40 83         1431 $self->{msgid} = undef;
41 83         241 $self->{msgstr} = undef;
42 83         277 $self->{msgid_plural} = undef;
43 83         236 $self->{context} = undef;
44 83         207 $self->{fuzzy} = undef;
45 83         416 $self->{comment} = [];
46 83         256 $self->{auto_comment} = [];
47             # e.g.: c-format
48 83         195 $self->{flags} = [];
49             # Is it plural?
50 83         298 $self->{plural} = 0;
51             # reference
52 83         218 $self->{file} = undef;
53 83         337 $self->{line} = undef;
54 83         357 $self->{encoding} = undef;
55             # Parent po object
56 83         216 $self->{po} = undef;
57             # Whether this element is actually just an include directive
58             # If it is, the include file value is stored in the comment
59 83         149 $self->{is_include} = 0;
60 83         269 $self->{is_meta} = 0;
61 83         243 $self->{_po_line} = 0;
62 83         233 $self->{_init_strict_use_sub} = 1;
63 83         443 $self->SUPER::init( @_ );
64 83         22710 return( $self );
65             }
66              
67 1     1 1 10 sub add_auto_comment { return( shift->_add( 'auto_comment', @_ ) ); }
68              
69 5     5 1 71 sub add_comment { return( shift->_add( 'comment', @_ ) ); }
70              
71 4     4 1 26 sub add_msgid { return( shift->_add( 'msgid', @_ ) ); }
72              
73 0     0 1 0 sub add_msgid_plural { return( shift->_add( 'msgid_plural', @_ ) ); }
74              
75 61     61 1 273 sub add_msgstr { return( shift->_add( 'msgstr', @_ ) ); }
76              
77             sub add_reference
78             {
79 0     0 1 0 my $self = shift( @_ );
80 0 0       0 if( @_ )
81             {
82             ## If there is any existing value, convert it to array
83 0 0 0     0 $self->{file} = [$self->{file}] if( length( $self->{file} ) && $self->_is_array( $self->{file} ) );
84 0 0 0     0 $self->{line} = [$self->{line}] if( length( $self->{line} ) && $self->_is_array( $self->{line} ) );
85 0 0       0 if( $self->_is_array( $_[0] ) )
86             {
87 0         0 push( @{$self->{file}}, $_[0]->[0] );
  0         0  
88 0         0 push( @{$self->{line}}, $_[0]->[1] );
  0         0  
89             }
90             else
91             {
92 0         0 push( @{$self->{file}}, shift( @_ ) );
  0         0  
93 0         0 push( @{$self->{line}}, shift( @_ ) );
  0         0  
94             }
95             }
96 0         0 return( $self );
97             }
98              
99 0     0 1 0 sub auto_comment { return( shift->_set_get_array( 'auto_comment', @_ ) ); }
100              
101 8     8 1 45 sub comment { return( shift->_set_get_array( 'comment', @_ ) ); }
102              
103 19     19 1 86 sub context { return( shift->_set_get_scalar( 'context', @_ ) ); }
104              
105             sub delete
106             {
107 0     0 1 0 my $self = shift( @_ );
108 0         0 my $po = $self->po;
109 0 0       0 return( $self->error( "No Text::PO object set." ) ) if( !$po );
110 0         0 return( $po->remove_element( $self ) );
111             }
112              
113             sub dump
114             {
115 8     8 1 17 my $self = shift( @_ );
116 8         14 my @res = ();
117 8 50       18 if( $self->is_include )
118             {
119 0 0       0 push( @res, '# ' . join( "\n# ", @{$self->{comment}} ) ) if( scalar( @{$self->{comment}} ) );
  0         0  
  0         0  
120 0 0 0     0 push( @res, '#. $include "' . $self->{file} . '"' ) if( length( $self->{file} // '' ) );
121             }
122             else
123             {
124 8 50       7178 push( @res, '# ' . join( "\n# ", @{$self->{comment}} ) ) if( scalar( @{$self->{comment}} ) );
  0         0  
  8         40  
125 8 50       13 push( @res, '#. ' . join( "\n#. ", @{$self->{auto_comment}} ) ) if( scalar( @{$self->{auto_comment}} ) );
  0         0  
  8         25  
126 8         27 my $ref = $self->reference;
127 8 100       31 push( @res, "#: $ref" ) if( length( $ref ) );
128 8         18 my $flags = $self->flags;
129 8 50       263 if( scalar( @$flags ) )
130             {
131 0         0 push( @res, sprintf( '#, %s', join( ", ", @$flags ) ) );
132             }
133 8 50       41 push( @res, sprintf( 'msgctxt "%s"', $self->po->quote( $self->{context} ) ) ) if( length( $self->{context} ) );
134 8         22 foreach my $k ( qw( msgid msgid_plural ) )
135             {
136 16 50       83 if( $self->can( "${k}_as_string" ) )
137             {
138 16         31 my $sub = "${k}_as_string";
139 16         42 push( @res, $self->$sub() );
140             }
141             else
142             {
143 0 0 0     0 if( ref( $self->{ $k } ) && scalar( @{$self->{ $k }} ) )
  0 0 0     0  
144             {
145 0         0 push( @res, sprintf( '%s ""', $k ) );
146 0         0 push( @res, map( sprintf( '"%s"', $self->po->quote( $_ ) ), @{$self->{ $k }} ) );
  0         0  
147             }
148             elsif( !ref( $self->{ $k } ) && length( $self->{ $k } ) )
149             {
150 0         0 push( @res, sprintf( '%s "%s"', $k, $self->po->quote( $self->{ $k } ) ) );
151             }
152             }
153             }
154 8         19 push( @res, $self->msgstr_as_string );
155             }
156 8         62 return( join( "\n", @res ) );
157             }
158              
159 27     27 1 21686 sub encoding { return( shift->_set_get_scalar( 'encoding', @_ ) ); }
160              
161 1     1 1 37 sub file { return( shift->_set_get_scalar( 'file', @_ ) ); }
162              
163 16     16 1 68 sub flags { return( shift->_set_get_array( 'flags', @_ ) ); }
164              
165 0     0 1 0 sub fuzzy { return( shift->_set_get_boolean( 'fuzzy', @_ ) ); }
166              
167             sub id
168             {
169 95     95 1 182 my $self = shift( @_ );
170 95         314 my $msgid = $self->msgid;
171 95 100       2718 if( ref( $msgid ) )
172             {
173 4         40 return( CORE::join( '', @$msgid ) );
174             }
175             else
176             {
177 91         857 return( $msgid );
178             }
179             }
180              
181 74     74 1 330 sub is_include { return( shift->_set_get_boolean( 'is_include', @_ ) ); }
182              
183 52     52 1 524 sub is_meta { return( shift->_set_get_boolean( 'is_meta', @_ ) ); }
184              
185 0     0 1 0 sub line { return( shift->_set_get_number( 'line', @_ ) ); }
186              
187             sub merge
188             {
189 0     0 1 0 my $self = shift( @_ );
190 0   0     0 my $elem = shift( @_ ) || return( $self->error( "No element object was provided." ) );
191 0 0 0     0 return( $self->error( "Object provided ($elem) is not an Text::PO::Element object" ) ) if( !$self->_is_object( $elem ) || !$elem->isa( 'Text::PO::Element' ) );
192 0         0 my @k = grep( !/^po$/, keys( %$elem ) );
193 0         0 foreach( @k )
194             {
195 0 0       0 $self->{ $_ } = $elem->{ $_ } if( !length( $self->{ $_ } ) );
196             }
197 0         0 return( $self );
198             }
199              
200 161     161 1 2500 sub msgid { return( shift->_set_get( 'msgid', @_ ) ); }
201              
202             sub msgid_as_string
203             {
204 8     8 1 16 my $self = shift( @_ );
205 8         35 return( $self->normalise( 'msgid', $self->{msgid} ) );
206             }
207              
208             sub msgid_as_text
209             {
210 84     84 1 11722 my $self = shift( @_ );
211 84 100       331 my $msgid = $self->_is_array( $self->{msgid} ) ? join( '', @{$self->{msgid}} ) : $self->{msgid};
  4         77  
212 84         1280 return( $msgid );
213             }
214              
215 8     8 1 1071 sub msgid_plural { return( shift->_set_get( 'msgid_plural', @_ ) ); }
216              
217             sub msgid_plural_as_string
218             {
219 16     16 1 43 my $self = shift( @_ );
220             # Important to return undef and not an empty string if there is no plural msgid
221             # undef will not be added to the list, but empty string would
222 16 100       70 return if( !CORE::length( $self->{msgid_plural} ) );
223 2         16 return( $self->normalise( 'msgid_plural', $self->{msgid_plural} ) );
224             }
225              
226             sub msgstr
227             {
228 103     103 1 1072 my $self = shift( @_ );
229 103 100       328 if( @_ )
230             {
231 63 100       163 if( @_ == 2 )
232             {
233 13         55 my( $pos, $str ) = @_;
234 13 50       143 return( $self->error( "msgstr plural offset \"$pos\" is not an integer." ) ) if( $pos !~ /^\d+$/ );
235 13         32 $pos = int( $pos );
236 13 100       51 $self->{msgstr} = [] if( ref( $self->{msgstr} ) ne 'ARRAY' );
237 13 50       69 $self->{msgstr}->[ $pos ] = [] if( ref( $self->{msgstr}->[ $pos ] ) ne 'ARRAY' );
238 13         22 push( @{$self->{msgstr}->[ $pos ]}, $str );
  13         48  
239             }
240             else
241             {
242 50 100       145 if( !ref( $_[0] ) )
243             {
244 47         104 chomp( @_ );
245             }
246 50         131 $self->{msgstr} = shift( @_ );
247             }
248             }
249 103         427 return( $self->{msgstr} );
250             }
251              
252             sub msgstr_as_string
253             {
254 8     8 1 45 my $self = shift( @_ );
255 8         14 my @res = ();
256 8 100       17 if( $self->plural )
257             {
258 1         524 for( my $i = 0; $i < scalar( @{$self->{msgstr}} ); $i++ )
  3         8  
259             {
260 2         4 my $ref = $self->{msgstr}->[$i];
261             # Is this a multiline plural localised text?
262             # msgstr[0] ""
263             # "some long line text"
264             # "2nd line of localised text"
265 2 50       4 if( scalar( @$ref ) > 1 )
266             {
267 0         0 push( @res, sprintf( 'msgstr[%d] ""', $i ) );
268 0         0 push( @res, map( sprintf( '"%s"', $self->po->quote( $_ ) ), @$ref ) );
269             }
270             # Regular plural localised text msgstr[0] "some text"
271             else
272             {
273 2 50       6 push( @res, sprintf( 'msgstr[%d] "%s"', $i, $self->po->quote( $ref->[0] ) ) ) if( length( $ref->[0] ) );
274             }
275             }
276 1         5 return( join( "\n", @res ) );
277             }
278             else
279             {
280 7         6481 return( $self->normalise( 'msgstr', $self->{msgstr} ) );
281             }
282             }
283              
284             sub msgstr_as_text
285             {
286 15     15 1 33 my $self = shift( @_ );
287 15 100       47 my $msgstr = $self->_is_array( $self->{msgstr} ) ? join( '', @{$self->{msgstr}} ) : $self->{msgstr};
  4         74  
288 15         174 return( $msgstr );
289             }
290              
291             sub normalise
292             {
293 17     17 1 33 my $self = shift( @_ );
294 17         33 my $type = shift( @_ );
295 17         47 my $text = shift( @_ );
296 17         31 my @res = ();
297 17         22 my $lines;
298 17 100 100     171 if( ref( $text ) && scalar( @$text ) )
    50 33        
299             {
300 2         15 $lines = $self->wrap( join( '', @$text ) );
301             }
302             elsif( !ref( $text ) && length( $text ) )
303             {
304 15         48 $lines = $self->wrap( $text );
305             }
306            
307 17 100       52 if( scalar( @$lines ) > 1 )
308             {
309 2         6 push( @res, sprintf( '%s ""', $type ) );
310 2         12 push( @res, map( sprintf( '"%s"', $_ ), @$lines ) );
311             }
312             else
313             {
314 15         69 push( @res, sprintf( '%s "%s"', $type, $lines->[0] ) );
315             }
316 17         112 return( join( "\n", @res ) );
317             }
318              
319 50     50 1 278 sub plural { return( shift->_set_get_boolean( 'plural', @_ ) ); }
320              
321 121     121 1 18658 sub po { return( shift->_set_get_object( 'po', 'Text::PO', @_ ) ); }#
322              
323             sub reference
324             {
325 31     31 1 95 my $self = shift( @_ );
326 31 100       125 if( @_ )
327             {
328 15 50       134 if( $self->_is_array( $_[0] ) )
329             {
330             # Multi references:
331             # colorscheme.cpp:79 skycomponents/equator.cpp:31
332 0 0       0 if( $self->_is_array( $_[0]->[0] ) )
333             {
334 0         0 $self->{file} = [];
335 0         0 $self->{line} = [];
336 0         0 foreach my $a ( @{$_[0]} )
  0         0  
337             {
338 0         0 push( @{$self->{file}}, $a->[0] );
  0         0  
339 0         0 push( @{$self->{line}}, $a->[1] );
  0         0  
340             }
341             }
342             else
343             {
344 0         0 @$self{ qw( file line ) } = @{$_[0]};
  0         0  
345             }
346             }
347             else
348             {
349 15         231 @$self{ qw( file line ) } = split( /:/, shift( @_ ), 2 );
350             }
351             }
352 31 100       176 return( '' ) if( !length( $self->{file} ) );
353 21 50 33     81 return( '' ) if( $self->_is_array( $self->{file} ) && !scalar( @{$self->{file}} ) );
  0         0  
354 21 50       268 if( $self->_is_array( $self->{file} ) )
355             {
356 0         0 my @temp = ();
357 0         0 for( my $i = 0; $i < scalar( @{$self->{file}} ); $i++ )
  0         0  
358             {
359 0 0 0     0 push( @temp, join( ':', $self->{file}->[$i], ( $self->_is_array( $self->{line} ) ? ( $self->{line}->[$i] // '' ) : '' ) ) );
360             }
361 0         0 return( join( ' ', @temp ) );
362             }
363             else
364             {
365 21   100     350 return( join( ':', $self->{file}, ( $self->{line} // '' ) ) );
366             }
367             }
368              
369             sub wrap
370             {
371 17     17 1 28 my $self = shift( @_ );
372 17         30 my $text = shift( @_ );
373 17         27 my $max = 80;
374 17 100       52 if( length( $text ) > $max )
375             {
376 2         29 my $lines = [split( /\n/, $text )];
377 2         9 for( my $i = 0; $i < scalar( @$lines ); $i++ )
378             {
379 2 50       8 if( length( $lines->[$i] ) > $max )
380             {
381 2         8 my $newLines = $self->wrap_line( $lines->[$i] );
382 2         9 splice( @$lines, $i, 1, @$newLines );
383 2         21 $i += scalar( @$newLines ) - 1;
384             }
385             else
386             {
387 0         0 $lines->[$i] = $self->po->quote( $lines->[$i] . "\n" );
388             }
389             }
390 2         7 return( $lines );
391             }
392             else
393             {
394 15         47 return( [ $self->po->quote( $text ) ] );
395             }
396             }
397              
398             sub wrap_line
399             {
400 2     2 1 5 my $self = shift( @_ );
401 2         5 my $text = shift( @_ );
402 2 50       6 return( [] ) if( !length( $text ) );
403 2         16 my $new = Text::Wrap::wrap( '', '', $text );
404 2         2060 my $newLines = [split( /\n/, $new )];
405 2         11 for( my $j = 0; $j < scalar( @$newLines ); $j++ )
406             {
407 4 100       15 $newLines->[$j] .= ' ' unless( $j == $#${newLines} );
408 4         12 $newLines->[$j] = $self->po->quote( $newLines->[$j] );
409             }
410             #$newLines->[ $#${newLines} ] = $self->po->quote( $newLines->[ $#${newLines} ] );
411 2         6 return( $newLines );
412             }
413              
414             sub _add
415             {
416 71     71   154 my $self = shift( @_ );
417 71         217 my $what = shift( @_ );
418             #chomp( @_ );
419 71 100       663 $self->{ $what } = [] if( !ref( $self->{ $what } ) );
420 71         144 push( @{$self->{ $what }}, @_ );
  71         266  
421 71         375 return( $self );
422             }
423              
424             sub _obj_eq
425             {
426 5     5   49 no overloading;
  5         10  
  5         6961  
427 50     50   88 my $self = shift( @_ );
428 50         81 my $other = shift( @_ );
429 50 100       199 my $msgid = $self->_is_array( $self->{msgid} ) ? join( '', @{$self->{msgid}} ) : $self->{msgid};
  1         21  
430 50 100       658 if( $self->_is_a( $other => 'Text::PO::Element' ) )
    50          
431             {
432 14 100       804 my $msgstr = $self->_is_array( $self->{msgstr} ) ? join( '', @{$self->{msgstr}} ) : $self->{msgstr};
  3         72  
433 14 50       147 my $other_msgid = $self->_is_array( $other->{msgid} ) ? join( '', @{$other->{msgid}} ) : $other->{msgid};
  0         0  
434 14 100       129 my $other_msgstr = $self->_is_array( $other->{msgstr} ) ? join( '', @{$other->{msgstr}} ) : $other->{msgstr};
  2         99  
435 14 50 33     493 return( ( ( $msgid // '' ) eq ( $other_msgid // '' ) && ( $msgstr // '' ) eq ( $other_msgstr // '' ) ) ? 1 : 0 );
436             }
437             # Comparing an undefined value would trigger a Perl warning
438             elsif( !defined( $other ) )
439             {
440 0 0       0 return( !defined( $msgid ) ? 1 : 0 );
441             }
442             else
443             {
444 36 50 50     593 return( ( $msgid // '' ) eq $other ? 1 : 0 );
445             }
446             }
447              
448             sub _set_get
449             {
450 169     169   286 my $self = shift( @_ );
451 169         353 my $name = shift( @_ );
452 169 100       446 if( @_ )
453             {
454 62 100 66     487 if( !ref( $_[0] ) && length( $_[0] ) )
455             {
456 59         188 chomp( @_ );
457             }
458 62 100       204 $self->plural(1) if( $name eq 'msgid_plural' );
459 62         6750 return( $self->SUPER::_set_get( $name, @_ ) );
460             }
461 107         442 return( $self->SUPER::_set_get( $name ) );
462             }
463              
464             sub _set_get_msg_property
465             {
466 0     0     my $self = shift( @_ );
467 0           my $prop = shift( @_ );
468 0 0         $self->_set_get( $prop, @_ ) if( @_ );
469 0 0         if( ref( $self->{ $prop } ) )
470             {
471 0 0         return( wantarray() ? ( @{$self->{ $prop }} ) : join( '', @{$self->{ $prop }} ) );
  0            
  0            
472             }
473             else
474             {
475 0 0         return( wantarray() ? ( $self->{ $prop } ) : $self->{ $prop } );
476             }
477             }
478              
479             1;
480             # NOTE: POD
481             __END__
482              
483             =encoding utf-8
484              
485             =head1 NAME
486              
487             Text::PO::Element - PO Element
488              
489             =head1 SYNOPSIS
490              
491             use Text::PO::Element;
492             my $po = Text::PO::Element->new;
493             $po->debug(2);
494             $po->dump;
495              
496             =head1 VERSION
497              
498             v0.4.2
499              
500             =head1 DESCRIPTION
501              
502             This is the class for PO elements.
503              
504             A typical PO element might look like this:
505              
506             white-space
507             # translator-comments
508             #. extracted-comments
509             #: reference...
510             #, flag...
511             #| msgid previous-untranslated-string
512             msgid untranslated-string
513             msgstr translated-string
514              
515             See more information fromt he L<GNU documentation|https://www.gnu.org/software/gettext/manual/html_node/PO-File-Entries.html>
516              
517             =head2 CONSTRUCTOR
518              
519             =head2 new
520              
521             Create a new Text::PO::Element object acting as an accessor.
522              
523             You can pass it an hash or hash reference of the following keys. For more information on those, see their corresponding method:
524              
525             =over 4
526              
527             =item * C<msgid>
528              
529             =item * C<msgstr>
530              
531             =item * C<msgid_plural>
532              
533             =item * C<auto_comment>
534              
535             =item * C<comment>
536              
537             =item * C<context>
538              
539             =item * C<encoding>
540              
541             =item * C<file>
542              
543             =item * C<flags>
544              
545             =item * C<is_meta>
546              
547             =item * C<line>
548              
549             =item * C<fuzzy>
550              
551             =item * C<plural>
552              
553             =item * C<po>
554              
555             =back
556              
557             =head2 ATTRIBUTES
558              
559             A C<Text::PO::Element> object has the following fields :
560              
561             =over 4
562              
563             =item * C<msgid>
564              
565             The localisation id
566              
567             =item * C<msgstr>
568              
569             The localised string
570              
571             =item * C<msgid_plural>
572              
573             The optional localised string in plural
574              
575             =item * C<context>
576              
577             The optional context.
578              
579             The context serves to disambiguate messages with the same untranslated-string. It is possible to have several entries with the same untranslated-string in a PO file, provided that they each have a different context. Note that an empty context string and an absent msgctxt line do not mean the same thing.
580              
581             L<https://www.gnu.org/software/gettext/manual/html_node/Entries-with-Context.html>
582              
583             =item * C<fuzzy>
584              
585             The fuzzy flag set when the entry has been created but not yet translated
586              
587             See also the L<GNU PO documentation|https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#index-fuzzy-flag>
588              
589             =item * C<comment>
590              
591             # translator-comments
592              
593             The optional comment that can be added to provide some explanations to the translator
594              
595             =item * C<auto_comment>
596              
597             #. extracted-comments
598              
599             The optional comment added automatically
600              
601             =item * C<flags>
602              
603             #, flag...
604              
605             An optional set of flags, stored as an array reference
606              
607             For example:
608              
609             #: src/msgcmp.c:338 src/po-lex.c:699
610             #, c-format
611             msgid "found %d fatal error"
612             msgid_plural "found %d fatal errors"
613             msgstr[0] "s'ha trobat %d error fatal"
614             msgstr[1] "s'han trobat %d errors fatals"
615              
616             Here the flag would be C<c-format>
617              
618             See also the L<GNU PO documentation|https://www.gnu.org/software/gettext/manual/html_node/c_002dformat-Flag.html> and L<here|https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#index-no_002dc_002dformat-flag>, and the L<list of flags|https://www.gnu.org/software/gettext/manual/html_node/Sticky-flags.html>
619              
620             Known flags are:
621              
622             =over 8
623              
624             =item * C<c-format> or C<no-c-format>
625              
626             The C<c-format> flag indicates that the untranslated string and the translation are supposed to be C format strings. The C<no-c-format> flag indicates that they are not C format strings, even though the untranslated string happens to look like a C format string (with ‘%’ directives).
627              
628             =item * C<objc-format> or C<no-objc-format>
629              
630             Likewise for L<Objective C|https://www.gnu.org/software/gettext/manual/html_node/objc_002dformat.html>
631              
632             =item * C<c++-format> or C<no-c++-format>
633              
634             Likewise for L<C++|https://www.gnu.org/software/gettext/manual/html_node/c_002b_002b_002dformat.html>
635              
636             =item * C<python-format> or C<no-python-format>
637              
638             Likewise for L<Python|https://www.gnu.org/software/gettext/manual/html_node/python_002dformat.html>
639              
640             =item * C<python-brace-format> or C<no-python-brace-format>
641              
642             Likewise for L<Python brace|https://www.gnu.org/software/gettext/manual/html_node/python_002dformat.html>
643              
644             =item * C<java-format> or C<no-java-format>
645              
646             Likewise for L<Java MessageFormat format strings|https://www.gnu.org/software/gettext/manual/html_node/java_002dformat.html>
647              
648             =item * C<java-printf-format> or C<no-java-printf-format>
649              
650             Likewise for L<Java printf format strings|https://www.gnu.org/software/gettext/manual/html_node/java_002dformat.html>
651              
652             =item * C<csharp-format> or C<no-csharp-format>
653              
654             Likewise for L<C# Format Strings|https://www.gnu.org/software/gettext/manual/html_node/csharp_002dformat.html>
655              
656             =item * C<javascript-format> or C<no-javascript-format>
657              
658             Likewise for L<JavaScript Format Strings|https://www.gnu.org/software/gettext/manual/html_node/javascript_002dformat.html>
659              
660             =item * C<scheme-format> or C<no-scheme-format>
661              
662             Likewise for L<Scheme Format Strings|https://www.gnu.org/software/gettext/manual/html_node/scheme_002dformat.html>
663              
664             =item * C<lisp-format> or C<no-lisp-format>
665              
666             Likewise for L<Lisp Format Strings|https://www.gnu.org/software/gettext/manual/html_node/lisp_002dformat.html>
667              
668             =item * C<lisp-format> or C<no-lisp-format>
669              
670             Likewise for L<Lisp Format Strings|https://www.gnu.org/software/gettext/manual/html_node/lisp_002dformat.html>
671              
672             =item * C<elisp-format> or C<no-elisp-format>
673              
674             Likewise for L<Emacs Lisp Format Strings|https://www.gnu.org/software/gettext/manual/html_node/elisp_002dformat.html>
675              
676             =item * C<librep-format> or C<no-librep-format>
677              
678             Likewise for L<librep Format Strings|https://www.gnu.org/software/gettext/manual/html_node/librep_002dformat.html>
679              
680             =item * C<rust-format> or C<no-rust-format>
681              
682             Likewise for L<Rust Format Strings|https://www.gnu.org/software/gettext/manual/html_node/rust_002dformat.html>
683              
684             =item * C<go-format> or C<no-go-format>
685              
686             Likewise for L<Go Format Strings|https://www.gnu.org/software/gettext/manual/html_node/go_002dformat.html>
687              
688             =item * C<sh-format> or C<no-sh-format>
689              
690             Likewise for L<Shell Format Strings|https://www.gnu.org/software/gettext/manual/html_node/sh_002dformat.html>
691              
692             =item * C<sh-printf-format> or C<no-sh-printf-format>
693              
694             Likewise for L<Shell Format Strings|https://www.gnu.org/software/gettext/manual/html_node/sh_002dformat.html>
695              
696             =item * C<awk-format> C<no-awk-format>
697              
698             Likewise for L<awk Format Strings|https://www.gnu.org/software/gettext/manual/html_node/awk_002dformat.html>
699              
700             =item * C<lua-format> or C<no-lua-format>
701              
702             Likewise for L<Lua Format Strings|https://www.gnu.org/software/gettext/manual/html_node/lua_002dformat.html>
703              
704             =item * C<object-pascal-format> or C<no-object-pascal-format>
705              
706             Likewise for L<Object Pascal Format Strings|https://www.gnu.org/software/gettext/manual/html_node/object_002dpascal_002dformat.html>
707              
708             =item * C<modula2-format> or C<no-modula2-format>
709              
710             Likewise for L<Modula-2 Format Strings|https://www.gnu.org/software/gettext/manual/html_node/modula2_002dformat.html>
711              
712             =item * C<d-format> or C<no-d-format>
713              
714             Likewise for L<D Format Strings|https://www.gnu.org/software/gettext/manual/html_node/d_002dformat.html>
715              
716             =item * C<smalltalk-format> or C<no-smalltalk-format>
717              
718             Likewise for L<Smalltalk Format Strings|https://www.gnu.org/software/gettext/manual/html_node/smalltalk_002dformat.html>
719              
720             =item * C<qt-format> or C<no-qt-format>
721              
722             Likewise for L<Qt Format Strings|https://www.gnu.org/software/gettext/manual/html_node/qt_002dformat.html>
723              
724             =item * C<qt-plural-format> or C<no-qt-plural-format>
725              
726             Likewise for L<Qt plural forms Format Strings|https://www.gnu.org/software/gettext/manual/html_node/qt_002dplural_002dformat.html>
727              
728             =item * C<kde-format> or C<no-kde-format>
729              
730             Likewise for L<KDE Format Strings|https://www.gnu.org/software/gettext/manual/html_node/kde_002dformat.html>
731              
732             =item * C<boost-format> or C<no-boost-format>
733              
734             Likewise for L<Boost Format Strings|https://www.gnu.org/software/gettext/manual/html_node/boost_002dformat.html>
735              
736             =item * C<tcl-format> or C<no-tcl-format>
737              
738             Likewise for L<Tcl Format Strings|https://www.gnu.org/software/gettext/manual/html_node/tcl_002dformat.html>
739              
740             =item * C<perl-format> or C<no-perl-format>
741              
742             Likewise for L<Perl Format Strings|https://www.gnu.org/software/gettext/manual/html_node/perl_002dformat.html>
743              
744             =item * C<perl-brace-format> or C<no-perl-brace-format>
745              
746             Likewise for L<Perl brace Format Strings|https://www.gnu.org/software/gettext/manual/html_node/perl_002dformat.html>
747              
748             =item * C<php-format> or C<no-php-format>
749              
750             Likewise for L<PHP Format Strings|https://www.gnu.org/software/gettext/manual/html_node/php_002dformat.html>
751              
752             =item * C<gcc-internal-format> or C<no-gcc-internal-format>
753              
754             Likewise for the L<GCC internal Format Strings|https://www.gnu.org/software/gettext/manual/html_node/gcc_002dinternal_002dformat.html>
755              
756             =item * C<gfc-internal-format> or C<no-gfc-internal-format>
757              
758             Likewise for the L<GNU Fortran Compiler internal Format Strings|https://www.gnu.org/software/gettext/manual/html_node/gfc_002dinternal_002dformat.html> in sources up to GCC 14.x. These flags are deprecated.
759              
760             =item * C<ycp-format> or C<no-ycp-format>
761              
762             Likewise for L<YCP Format Strings|https://www.gnu.org/software/gettext/manual/html_node/ycp_002dformat.html>
763              
764             Other flags, assigned by the programmer, are:
765              
766             =item * C<no-wrap>
767              
768             This flag influences the presentation of the entry in the PO file. By default, when a PO file is output by a GNU gettext program and the option ‘--no-wrap’ is not specified, message lines that exceed the output page width are split into several lines. This flag inhibits this line breaking for the entry. This is useful for entries whose lines are close to 80 columns wide.
769              
770             =back
771              
772             =item * C<plural>
773              
774             Whether this has a plural form
775              
776             =item * C<encoding>
777              
778             The character encoding
779              
780             =item * C<file>
781              
782             The file in which this l10n string was found. This is set when automatic parsing was executed
783              
784             For example:
785              
786             #: lib/error.c:116
787             msgid "Unknown system error"
788             msgstr "Error desconegut del sistema"
789              
790             This would specify a file C<lib/error.c> and a line number C<116>
791              
792             =item * C<line>
793              
794             The line at which this l10n was found. This is set when automatic parsing was executed
795              
796             =item * C<po>
797              
798             The parent C<Text::PO> object
799              
800             =item * C<is_meta>
801              
802             An optional boolean value provided if this element represents a meta information
803              
804             =back
805              
806             =head1 METHODS
807              
808             =head2 add_auto_comment
809              
810             Add an auto comment
811              
812             =head2 add_comment
813              
814             Add a comment
815              
816             =head2 add_msgid
817              
818             Add a msgid
819              
820             =head2 add_msgid_plural
821              
822             Add a plural version of a msgid
823              
824             =head2 add_msgstr
825              
826             Add a msgstr
827              
828             =head2 add_reference
829              
830             Add a reference, which is a file and line number
831              
832             =head2 auto_comment
833              
834             Set or return the auto_comment field
835              
836             =head2 comment
837              
838             Set or return the comment field
839              
840             =head2 context
841              
842             Set or return the context field
843              
844             =head2 delete
845              
846             Remove the element from the list of elements in L<Text::PO>
847              
848             This only works if the element was added via L<Text::PO>, or else you need to have set yourself the L<Text::PO> object with the L</po> method.
849              
850             =head2 dump
851              
852             Return the element as a string formatted for a po file.
853              
854             =head2 encoding
855              
856             Set or get the encoding for this element. This defaults to an empty string
857              
858             =head2 file
859              
860             Set or get the file path where this PO element was initially be found.
861              
862             =head2 flags
863              
864             Set or return the flags as array reference
865              
866             =head2 fuzzy
867              
868             Set or gets whether this element has the C<fuzzy> flag. Default to false.
869              
870             =head2 id
871              
872             Return the value of L<msgid> as a string
873              
874             =head2 is_include
875              
876             Sets or gets the boolean value whether this is a include directive or not. Defaults to false.
877              
878             =head2 is_meta
879              
880             Set or gets the flag that this element represents the meta information for this PO (a.k.a portable object) file.
881              
882             Meta information for a po file is stored in a unique msgid whose value is null.
883              
884             =head2 line
885              
886             Set or get the line number at which this PO element was initially be found.
887              
888             =head2 merge( Text::PO::Element )
889              
890             Given a C<Text::PO::Element> object, it merge its content with our element object.
891              
892             The merge will not overwrite existing fields.
893              
894             It returns the current object
895              
896             =head2 msgid
897              
898             Sets or gets the C<msgid> for this element.
899              
900             In list context, this return the element as an array. Thus. if this element has multiple lines, it will return an array of lines. In scalar context, it returns this element as a string, or if it is a multi line element, it will return an array reference.
901              
902             =head2 msgid_plural
903              
904             Sets or gets the C<msgid> version for plural. This is typically a 2-elements array. The first one singular and the second one plural.
905              
906             =head2 msgid_as_string
907              
908             This returns the msgid escaped and with surrounding quotes, suitable for L</dump>
909              
910             =head2 msgid_as_text
911              
912             This returns a simple text representation of the C<msgid>. It differs from L<msgid_as_string|/msgid_as_string> in that this is simply the string representation of the C<msgid>, but would not be suitable for a PO file.
913              
914             =head2 msgid_plural_as_string
915              
916             Returns the C<msgid> property as a string when it has plural implemented.
917              
918             =head2 msgstr
919              
920             Set or return the msgstr as a value without surrounding quote and without escaping.
921              
922             =head2 msgstr_as_string
923              
924             This returns the msgstr escaped and with surrounding quotes, suitable for L</dump>
925              
926             =head2 msgstr_as_text
927              
928             This returns a simple text representation of the C<msgstr>. It differs from L<msgstr_as_string|/msgstr_as_string> in that this is simply the string representation of the C<msgstr>, but would not be suitable for a PO file.
929              
930             =head2 normalise
931              
932             L</normalise> will return a string properly formatted with double quotes, multi lines if necessary, suitable for L</dump>
933              
934             =head2 plural
935              
936             Boolean. Sets or gets whether this element is an element with plural version of its C<msgid>
937              
938             =head2 po
939              
940             Sets or gets the L<Text::PO> object associated with this element. Best that you know what you are doing if you change this.
941              
942             =head2 reference
943              
944             #: lib/error.c:116
945              
946             Given an array reference or a string separated by ':', it sets the file and line number for this element object.
947              
948             =head2 wrap
949              
950             Given a text, it returns an array reference of lines wrapped
951              
952             =head2 wrap_line
953              
954             Given a string, it returns an array reference of lines. This is called by L</wrap>
955              
956             =head1 THREAD-SAFETY
957              
958             This module is thread-safe. All state is stored on a per-object basis, and the underlying file operations and data structures do not share mutable global state.
959              
960             =head1 AUTHOR
961              
962             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
963              
964             =head1 SEE ALSO
965              
966             L<https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html>
967              
968             =head1 COPYRIGHT & LICENSE
969              
970             Copyright (c) 2020-2023 DEGUEST Pte. Ltd.
971              
972             You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.
973              
974             =cut