views:

531

answers:

8

I need to use some string replacement in Perl to ease translations, i.e. replace many

print "Outputting " . $n . " numbers";

by something like

printf ("Outputting %d numbers", $n);

However, I'd like to replace printf with something easier to parse for humans, like this:

printX ("Outputting {num} numbers", { num => $n });

or generally something more Perly.

Can you recommend something (from CPAN or not) you like and use?

+4  A: 

It seems you want to have a different way of parsing strings. I would advise you not to do this. The only one who is seeing the syntax with the %d in it is the developer and he will exactly understand what is meant. The printf syntax is powerful because of the options like padding, decimals etc.

I think you want to use more a replace method. It is perlish to do s/{num}/$n/.

Peter Smit
"The only one who is seeing the syntax with the %d in it is the developer" -> This is not the case, those strings will be seen by translators and pretty dumb ones too :) I'd be fine with printf myself but I have to think of other people...
rassie
+2  A: 

In light of your comment about being for translators I suggest writing a perl script that strips all printf() and tabulates them in an easier more translator friendly manner.

Something like this:

while(<>)
{
    #regex for striping printf

    #print in tabulated form
}

If you print out the line number too you can easily write another program to replace the translated text.

This solution wouldn't take you any longer than re-factoring away from printf() and it's reusable.


I would definitely stick with printf(), it's standard across many languages.

It has almost become a standard for string output. Like i is for for loops.

rjstelling
I use "loop" :-)
Blank Xavier
The problem with printf is that you also have to get the order right. That is, if a language reverses the order of items being substituted in, the translator is SOL with some printf's (say, C). To make translation easier, you need to use "%1$d" so they can easily move it around. Template Toolkit makes this trivial.
Tanktalus
+13  A: 

What about simply:

 print "Outputting $n numbers";

That's very Perly. If you don't need any kind of fancy formatting, string interpolation is definitely the way to go.

Greg Hewgill
Thanks for the suggestion, this is suitable idea in most cases I guess, however I don't always output interpolateble variables (Dumper(\%hash) comes to mind). Have to see how that works out, but I probably need something a bit more flexible...
rassie
There's an idiom to output arbitrary expressions in interpolation: print "Hash is: @{[Dumper(\%hash)]}"; It's not pretty but it works.
Greg Hewgill
@Greg Hewgill I had no idea you could do this--awesome!
cowgod
A: 

well, perl has printf function... wait, do you want something like python's string formatting with dict?

 >>> print '%(key)s' % {'key': 'value'}
 value

mmm, I don't know something like that exist in perl... at least not this "easy"... maybe Text::Sprintf::Named can be your friend

ZeD
In case it wasn't clear, I'd like to use something instead of Perl's printf, not C's printf.
rassie
+13  A: 

Most Templating modules on CPAN will probably do what you want. Here's an example using Template Toolkit...

use Template;
my $tt = Template->new;

$tt->process( \"Outputting [% num %] numbers\n", { num => 100 } );


And you can mimic your required example with something like this...

sub printX {
    use Template;
    my $tt = Template->new( START_TAG => '{', END_TAG => '}' );
    $tt->process( \( $_[0] . "\n" ), $_[1] );
}

and u've got...

printX 'Outputting {num} numbers' => { num => 100 };

/I3az/

draegtun
+5  A: 

The print builtin is very convenient for most situations. Besides variable interpolation:

print "Outputting $n numbers";    # These two lines
print "Outputting ${n} numbers";  # are equivalent

Remember that print can take multiple arguments, so there is no need to concatenate them first into a single string if you need to print the result of a subroutine call:

print "Output data: ", Dumper($data);

However, for outputting numbers other than simple integers, you'll probably want the formatting convenience of printf. Outputting other data types is easy with print, though.

You can use join to conveniently output arrays:

print join ', ', @array;

And combine with map and keys to output hashes:

print join ', ', map {"$_ : $hash{$_}"} keys %hash;

Use the qq operator if you want to output quotes around the data:

print join ', ', map {qq("$_" : "$hash{$_}"}) keys %hash;
nohat
+2  A: 

If you're looking to ease translations you should consider using one of the L10n/i18n CPAN modules that are available. Specifically, a good overview of why your approach will end up falling short is written up as part of the Local::Maketext docs.

Another great module that pairs nicely with Locale::Maketext is Locale::Maketext::Lexicon. This allows you to use more standard localization formats such as gettext's .po/.mo files which have GUI tools to help translators work through all the text that needs translating. Locale::Maketext::Lexicon also comes with a helper script (xgettext.pl) that helps keep your localization files up-to-date with your templates or modules that have text that need translating. I've had very good results with this kind of setup in the past.

Brian Phillips
I'm not talking about i18n here (apart from L::M::Lexicon being completely unsuitable, more to that later ;)), but rather preparing strings for translation. At my work we currently have to use a custom translation system where strings are extracted in a weird way.
rassie
+1  A: 

Generally answer from Draegtun is great, but if you'd need something smaller (i.e. less memory), and not as powerful you can easily do it using this function:

sub printX {
    my ( $format, $vars ) = @_;

    my @sorted_keys = sort { length($b) <=> length($a) } keys %{ $vars };
    my $re = join '|', map { "\Q$_\E" } @sorted_keys;

    $format =~ s/ \{ \s* ($re) \s* \} /$vars->{$1}/xg;

    print $format;
}
depesz