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:
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):
It outputs the input unchanged:#! /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();
What I was hoping for is more like:package My::L10N::en; use base 'My::L10N'; our %Lexicon = ( 'Some [_1] text' => 'Some [_1] text', );
(Yes, I know the example is missing the "1;" at the bottom, but it's just an example.) Thanks,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]', );
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.
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...)...#! /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();
In reply to Programmatically Updating Code with PPI by Tanktalus
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |