summaryrefslogtreecommitdiffstats
path: root/msguntypot
blob: d900c75f5be3732d90e81cb60b253eb12b554205 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#! /usr/bin/env perl
eval 'exec perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;

# msg untypo pot -- Update the PO files when you remove a typo in POT file not needing any translation update
#
# Copyright 2005 by Martin Quinson (mquinson#debian.fr)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of GPL v2.0 or later (see COPYING).

my $VERSION=$Locale::Po4a::TransTractor::VERSION;

=head1 NAME

msguntypot - update PO files when a typo is fixed in POT file

=head1 SYNOPSIS

B<msguntypot> B<-o> I<old_pot> B<-n> I<new_pot> I<pofiles> ...

=head1 DESCRIPTION

When you fix a trivial error which surely doesn't affect translations (e.g.
a typo) in a POT file, you should unfuzzy the corresponding msgstr in the
translated PO files to avoid so extra work to the translators.

This task is difficult and error prone when done manually, and this tool is
there to help doing so correctly. You just need to provide the two versions
of the POT file: before the edition and after as marked in the above
synopsis, and it all becomes automatic.

=head1 HOW TO USE IT

In short, when you discover a typo in one of your [english] message, do the
following:

=over

=item - Regenerate your POT and PO files.

  make -C po/ update-po # for message program translations
  debconf-updatepo      # for debconf translations
  po4a po4a.conf        # for po4a based documentation translations

or something else, depending on your project's building settings. You know
how to make sure your POT and PO files are uptodate, don't you??

=item - Make a copy of your POT file.

  cp myfile.pot myfile.pot.orig

=item - Make a copy of all your PO files.

  mkdir po_fridge; cp *.po po_fridge

=item - Fix your typo.

$EDITOR the_file_in_which_there_is_a_typo

=item - Regenerate your POT and PO files.

See above.

=back

At this point, the typo fix fuzzied all the translations, and this
unfortunate change is the only one between the PO files of your main
directory and the one from the fridge. Here is how to solve this.

=over

=item - Discard fuzzy translation, restore the ones from the fridge.

  cp po_fridge/*.po .

=item - Manually merge the PO files with the new POT file, but taking the useless fuzzy into account.

  msguntypot -o myfile.pot.orig -n myfile.pot *.po

=item - Cleanups.

  rm -rf myfile.pot.orig po_fridge

=back

You're done. The typo was eradicated from msgstr of both your POT and PO
files, and the PO files were not fuzzyied in the process. Your translators
love you already.

=head1 SEE ALSO

Despite its name, this tool is not part of the gettext tool suite. It is
instead part of po4a. More precisely, it's a random Perl script using the
fine po4a modules. For more information about po4a, please see:

L<po4a(7)>

=head1 AUTHORS

 Martin Quinson (mquinson#debian,org)

=head1 COPYRIGHT AND LICENSE

Copyright 2005 by SPI, inc.

This program is free software; you may redistribute it and/or modify it
under the terms of GPL v2.0 or later (see the COPYING file).

=cut

use 5.16.0;
use strict;
use warnings;

use Getopt::Long qw(GetOptions);

use Locale::Po4a::TransTractor;
use Locale::Po4a::Common;

use Pod::Usage qw(pod2usage);

use File::Temp;

Locale::Po4a::Common::textdomain('po4a');

sub show_version {
    Locale::Po4a::Common::show_version("msguntypot");
    exit 0;
}

my ($help,$debug,@verbose,$quiet,$noprevious);
@verbose = ();
$debug = 0;

my ($newfile,$oldfile)=("","");

Getopt::Long::config('bundling', 'no_getopt_compat', 'no_auto_abbrev');
GetOptions(
    'help|h'        => \$help,

    'new|n=s'       => \$newfile,
    'old|o=s'       => \$oldfile,

    'verbose|v'     => \@verbose,
    'debug|d'       => \$debug,
    'quiet|q'       => \$quiet,
    'no-previous'   => \$noprevious,
    'version|V'     => \&show_version
) or pod2usage();

# Argument check
$help && pod2usage (-verbose => 1, -exitval => 0);

my ($verbose) = (scalar @verbose);
$verbose = 1 if $debug;
$verbose = -1 if $quiet;
my %options = (
    "verbose" => $verbose,
    "debug" => $debug);

# Argument checking
defined($oldfile) && length($oldfile) || die wrap_msg(gettext("Mandatory argument '%s' missing."), "-o");
-e $oldfile || die wrap_msg(gettext("File %s does not exist."), $oldfile);
defined($newfile) && length($newfile) || die wrap_msg(gettext("Mandatory argument '%s' missing."), "-n");
-e $newfile || die wrap_msg(gettext("File %s does not exist."), $newfile);

# Parse files
my $newpot=Locale::Po4a::Po->new();
my $oldpot=Locale::Po4a::Po->new();
$newpot->read($newfile);
$oldpot->read($oldfile);

die wrap_msg(gettext("The new and old POT files have different amount of strings (%d != %d).".
                     " Something's seriously wrong here."),
    $newpot->count_entries(), $oldpot->count_entries())
  if ($newpot->count_entries() != $oldpot->count_entries());

# Compare them and find differences between them
my (%diff)=();

for (my ($o,$n)=(0,0) ;
    $o<$oldpot->count_entries() && $n<$newpot->count_entries();
    $o++,$n++) {

    my ($oldstr,$newstr)=($oldpot->msgid($o),$newpot->msgid($n));

    $diff{$oldstr} = $newstr
      if ($oldstr ne $newstr);

    }
print wrap_msg(gettext("Found %d modified entries."),scalar keys %diff) if $verbose;

my $msgmergeOpts = ($noprevious ? "" : "--previous");

# Get all po files and report differences in them
my ($pofile);
(undef,$pofile)=File::Temp::tempfile("po4aXXXX",
    DIR    => File::Spec->tmpdir(),
    SUFFIX => ".po",
    OPEN   => 0,
    UNLINK => 0)
  or die wrap_msg(gettext("Cannot create a temporary PO file: %s"), $!);

my $pocount = 0;
while (my $poarg = shift) {
    $pocount ++;
    print wrap_msg(gettext("Handling %s"),$poarg) if $verbose;
    my $cmd = "msgmerge $msgmergeOpts -o $pofile --silent $poarg $oldfile";
    if (system($cmd)) {
        my $msg = $!;
        unlink ($pofile);
        die wrap_msg(gettext("Could not run msgmerge: %s\nThe command was: %s"), $msg, $cmd);
    }
    my $po=Locale::Po4a::Po->new();
    $po->read($pofile);

    for (my $n=0 ; $n<$po->count_entries(); $n++) {
        my $str=$po->msgid($n);
      	next unless defined $str;
        my $newstr = $diff{$str};

        if (defined $newstr) {
            $po->{po}{ $newstr } = { %{ $po->{po}{ $str } } };
            $po->{po}{ $str } = ();
            delete $po->{po}{ $str };
            print wrap_msg(gettext("msguntypot changed msgid \"%s\" to \"%s\" in %s\n"),$str,$newstr,$poarg) if ($verbose);
        }
    }
    $po->write($poarg);
}
unlink($pofile);

print wrap_msg(gettext("Modified %d entries in %d files."),scalar keys %diff,$pocount);

exit 0;
__END__;