I've been toying with Locale::Maketext for our next project at work. The biggest problem (remaining - it solves a lot of problems) I have is in populating the files that need to be sent to translation. On one hand, it's awesome that I don't need to go and update 10-30 different language files every time I want to add another string. The downside is that I still have to do that eventually, so that the translators know what to translate. So the first thing popping into my head is that every call to Locale::Maketext::maketext could be found automatically, its first parameter pulled out, and we instantly have the list of strings that need translating.

With PPI, this seems to have gone fairly smoothly. Getting the first Expression from the maketext call seems to work, and it conveniently keeps the quotes, which allows me to propagate them if there are special characters (\n, \t, etc.).

The next obvious part is to automatically insert the text into the language-specific module files. One possibility is to use a templating solution that builds each file. Unfortunately, that comes with a couple downsides:

The most obvious answer, then, to me, is to continue using PPI, this time to insert additional code. If it doesn't format well, I can always throw perltidy at it afterwards, so that's a nice-to-have but still quite secondary.

Going down this road, I got confused as to the proper incantation. There's a mental block between me and success on this, so I was hoping that someone else has gone down this road more successfully than I and could provide the missing piece. Here is what I have (ok, not really - this is just boiled down to the salient points):

#! /usr/bin/perl use strict; use warnings; use PPI; my $perlcode = << "END_PERLCODE"; package My::L10N::en; use base 'My::L10N'; our %Lexicon = ( 'Some [_1] text' => 'Some [_1] text', ); END_PERLCODE my $doc = PPI::Document->new(\$perlcode); my $lexicon = $doc->find(sub { $_[1]->content eq '%Lexicon' }); $lexicon = $lexicon->[0]; # returns an array ref, but in my case, ther +e's only ever one. my $newmsg = q{'The [_1] is in the [_2]'}; my $newtext = sprintf ' %s =>\n %s,', $newmsg, $newmsg; my $insert_this = PPI::Document->new(\$newtext); # insert $newtext into %Lexicon? $lexicon->insert_after($insert_this->tokens()); print $doc->serialize();
It outputs the input unchanged:
package My::L10N::en; use base 'My::L10N'; our %Lexicon = ( 'Some [_1] text' => 'Some [_1] text', );
What I was hoping for is more like:
package My::L10N::en; use base 'My::L10N'; our %Lexicon = ( 'Some [_1] text' => 'Some [_1] text', 'The [_1] is in the [_2]' => 'The [_1] is in the [_2]', );
(Yes, I know the example is missing the "1;" at the bottom, but it's just an example.) Thanks,

Update: ARGH. After getting Khen1950fx's "solution", I realised that I could simply mandate something like "### NEW TEXT GOES HERE" as a marker to find and insert on. But, because I still wanted to figure this out with PPI (so that people couldn't clobber my nice insert marker), I kept hacking. And then I got it.

#! /usr/bin/perl use strict; use warnings; use PPI; use PPI::Dumper; sub mydump { PPI::Dumper->new(shift)->print() } my $perlcode = << "END_PERLCODE"; package My::L10N::en; use base 'My::L10N'; our %Lexicon = ( 'Some [_1] text' => 'Some [_1] text', ); 1; END_PERLCODE my $doc = PPI::Document->new(\$perlcode); my $lexicon = $doc->find(sub { $_[1]->content eq '%Lexicon' }); $lexicon = $lexicon->[0]; # returns an array ref, but in my case, ther +e's only ever one. my $newmsg = q{'The [_1] is in the [_2]'}; my $newtext = sprintf " %s =>\n %s,\n", $newmsg, $newmsg; my $insert_this = PPI::Document->new(\$newtext); # insert $newtext into %Lexicon? my $stmt = $lexicon->statement(); my $list = $stmt->find('PPI::Structure::List')->[0]; for ($insert_this->tokens()) { $_->remove(); my $rc = $list->add_element($_); } print $doc->serialize();
Apparently, insert_after doesn't take multiple elements (yet?). So I have to do one after another. After trying a few things, I got to the point where I was re-finding each last-child, and inserting after that. But that got ugly fast, and didn't seem to work in my original code. And that's when I happened upon add_element. However, in that case, I found I had to remove the element from its existing doc first (which is fine, it's just the code snippet I created dynamically anyway), reminiscent of using XML::Twig. So, I simply remove and add. I also found I had to find the list - because it was the list's children where I was adding the element. Now off to "create" %Lexicon if it doesn't exist (should be much easier now that I have this working...)...


In reply to Programmatically Updating Code with PPI by Tanktalus

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.