source: git/lib/po-to-msg.pl @ 2aa484e

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 2aa484e was d73ef23, checked in by Olly Betts <olly@…>, 12 years ago

lib/,src/Makefile.am,src/gettexttomsg.pl: Rename codes.po to po_codes
so it doesn't get picked up by tools looking for .po files. Add
survex.pot to the repo so launchpad's auto translation syncing can
work.

  • Property mode set to 100755
File size: 7.6 KB
Line 
1#!/usr/bin/perl -w
2require 5.008;
3use bytes;
4use strict;
5use Locale::PO;
6
7use integer;
8
9# Magic identifier (12 bytes)
10my $magic = "Svx\nMsg\r\n\xfe\xff\0";
11# Designed to be corrupted by ASCII ftp, top bit stripping (or
12# being used for parity).  Contains a zero byte so more likely
13# to be flagged as data (e.g. by perl's "-B" test).
14
15my $srcdir = $0;
16$srcdir =~ s!/[^/]+$!!;
17
18my $major = 0;
19my $minor = 8;
20
21# File format (multi-byte numbers in network order (bigendian)):
22# 12 bytes: Magic identifier
23# 1 byte:   File format major version number (0)
24# 1 byte:   File format minor version number (8)
25# 2 bytes:  Number of messages (N)
26# 4 bytes:  Offset from XXXX to end of file
27# XXXX:
28# N*:
29# <message> NUL
30
31my %msgs = ();
32${$msgs{'en'}}[0] = '©';
33
34# my %uses = ();
35
36my $num_list = Locale::PO->load_file_asarray("$srcdir/po_codes");
37foreach my $po_entry (@{$num_list}) {
38    my $msgno = $po_entry->dequote($po_entry->msgstr);
39    next if $msgno !~ /^\d+$/;
40    my $key = $po_entry->msgid;
41    my $msg = c_unescape($po_entry->dequote($key));
42    if (${$msgs{'en'}}[$msgno]) {
43        print STDERR "Warning: already had message $msgno for language 'en'\n";
44    }
45    ${$msgs{'en'}}[$msgno] = $msg;
46}
47
48for my $po_file (@ARGV) {
49    my $language = $po_file;
50    $language =~ s/\.po$//;
51
52    my $po_hash = Locale::PO->load_file_ashash("$srcdir/$po_file");
53
54    foreach my $po_entry (@{$num_list}) {
55        my $msgno = $po_entry->dequote($po_entry->msgstr);
56        next if $msgno !~ /^\d+$/;
57        my $key = $po_entry->msgid;
58        my $ent = $$po_hash{$key};
59        if (defined $ent) {
60            my $msg = c_unescape($po_entry->dequote($ent->msgstr));
61            next if $msg eq '';
62            if (${$msgs{$language}}[$msgno]) {
63                print STDERR "Warning: already had message $msgno for language $language\n";
64            }
65            ${$msgs{$language}}[$msgno] = $msg;
66        }
67    }
68
69#       local $_;
70#       open FINDUSES, "grep -no '*/$msgno\\>' \Q$srcdir\E/../src/*.cc \Q$srcdir\E../src/*.c \Q$srcdir\E/../src/*.h|" or die $!;
71#       while (<FINDUSES>) {
72#          push @{$uses{$msgno}}, $1 if /^([^:]*:\d+):/;
73#       }
74#       close FINDUSES;
75
76}
77
78my $lang;
79my @langs = sort grep ! /_\*$/, keys %msgs;
80
81my $num_msgs = -1;
82foreach $lang (@langs) {
83   my $aref = $msgs{$lang};
84   $num_msgs = scalar @$aref if scalar @$aref > $num_msgs;
85   unlink "$lang.todo";
86}
87
88foreach $lang (@langs) {
89   my $fnm = $lang;
90   $fnm =~ s/(_.*)$/\U$1/;
91   open OUT, ">$fnm.msg" or die $!;
92
93   my $aref = $msgs{$lang};
94
95   my $parentaref;
96   my $mainlang = $lang;
97   $parentaref = $msgs{$mainlang} if $mainlang =~ s/_.*$//;
98
99   print OUT $magic or die $!;
100   print OUT pack("CCn", $major, $minor, $num_msgs) or die $!;
101
102   my $buff = '';
103   my $missing = 0;
104   my $retranslate = 0;
105
106   for my $n (0 .. $num_msgs - 1) {
107      my $warned = 0;
108      my $msg = $$aref[$n];
109      if (!defined $msg) {
110         $msg = $$parentaref[$n] if defined $parentaref;
111         if (!defined $msg) {
112            $msg = ${$msgs{'en'}}[$n];
113            # don't report if we have a parent (as the omission will be
114            # reported there)
115            if (defined $msg && $msg ne '' && !defined $parentaref) {
116               ++$missing;
117               $warned = 1;
118            } else {
119               $msg = '' if !defined $msg;
120            }
121         }
122      } else {
123         if ($lang ne 'en') {
124             sanity_check("Message $n in language $lang", $msg, ${$msgs{'en'}}[$n]);
125         }
126      }
127      $buff .= $msg . "\0";
128   }
129
130   print OUT pack('N',length($buff)), $buff or die $!;
131   close OUT or die $!;
132
133   if ($missing || $retranslate) {
134       print STDERR "Warning: ";
135       if ($missing) {
136           print STDERR "$lang: $missing missing message(s)";
137           if ($retranslate) {
138               print STDERR " and $retranslate requiring retranslation";
139           }
140       } else {
141           print STDERR "$lang: $retranslate message(s) requiring retranslation";
142       }
143       print STDERR "\n";
144   }
145}
146
147sub sanity_check {
148   my ($where, $msg, $orig) = @_;
149   # check printf-like specifiers match
150   # allow valid printf specifiers, or %<any letter> to support strftime
151   # and other printf-like formats.
152   my @pcent_m = grep /\%/, split /(%(?:[-#0 +'I]*(?:[0-9]*|\*|\*m\$)(?:\.[0-9]*)?(?:hh|ll|[hlLqjzt])?[diouxXeEfFgGaAcsCSpn]|[a-zA-Z]))/, $msg;
153   my @pcent_o = grep /\%/, split /(%(?:[-#0 +'I]*(?:[0-9]*|\*|\*m\$)(?:\.[0-9]*)?(?:hh|ll|[hlLqjzt])?[diouxXeEfFgGaAcsCSpn]|[a-zA-Z]))/, $orig;
154   while (scalar @pcent_m || scalar @pcent_o) {
155       if (!scalar @pcent_m) {
156           print STDERR "Warning: $where misses out \%spec $pcent_o[0]\n";
157       } elsif (!scalar @pcent_o) {
158           print STDERR "Warning: $where has extra \%spec $pcent_m[0]\n";
159       } elsif ($pcent_m[0] ne $pcent_o[0]) {
160           print STDERR "Warning: $where has \%spec $pcent_m[0] instead of $pcent_o[0]\n";
161       }
162       pop @pcent_m;
163       pop @pcent_o;
164   }
165
166   # Check for missing (or added) ellipses (...)
167   if ($msg =~ /\.\.\./ && $orig !~ /\.\.\./) {
168       print STDERR "Warning: $where has ellipses but original doesn't\n";
169   } elsif ($msg !~ /\.\.\./ && $orig =~ /\.\.\./) {
170       print STDERR "Warning: $where is missing ellipses\n";
171   }
172
173   # Check for missing (or added) menu shortcut (@)
174   if ($msg =~ /\@[A-Za-z]/ && $orig !~ /\@[A-Za-z]/) {
175       print STDERR "Warning: $where has menu shortcut but original doesn't\n";
176   } elsif ($msg !~ /\@[A-Za-z]/ && $orig =~ /\@[A-Za-z]/) {
177       print STDERR "Warning: $where is missing menu shortcut\n";
178   }
179
180   # Check for missing (or added) menu shortcut (&)
181   if ($msg =~ /\&[A-Za-z]/ && $orig !~ /\&[A-Za-z]/) {
182       print STDERR "Warning: $where has menu shortcut but original doesn't\n";
183   } elsif ($msg !~ /\&[A-Za-z]/ && $orig =~ /\&[A-Za-z]/) {
184       print STDERR "Warning: $where is missing menu shortcut\n";
185   }
186
187   # Check for missing (or added) double quotes (“ and ”)
188   if (scalar($msg =~ s/“/$&/g) != scalar($orig =~ s/“/$&/g)) {
189       print STDERR "Warning: $where has different numbers of “\n";
190       print STDERR "$orig\n$msg\n\n";
191   }
192   if (scalar($msg =~ s/”/$&/g) != scalar($orig =~ s/”/$&/g)) {
193       print STDERR "Warning: $where has different numbers of ”\n";
194       print STDERR "$orig\n$msg\n\n";
195   }
196
197   # Check for missing (or added) menu accelerator "##"
198   if ($msg =~ /\#\#/ && $orig !~ /\#\#/) {
199       print STDERR "Warning: $where has menu accelerator but original doesn't\n";
200   } elsif ($msg !~ /\#\#/ && $orig =~ /\#\#/) {
201       print STDERR "Warning: $where is missing menu accelerator\n";
202   } elsif ($orig =~ /\#\#(.*)/) {
203       my $acc_o = $1;
204       my ($acc_m) = $msg =~ /\#\#(.*)/;
205       if ($acc_o ne $acc_m) {
206           print STDERR "Warning: $where has menu accelerator $acc_m instead of $acc_o\n";
207       }
208   }
209
210   # Check for missing (or added) menu accelerator "\t"
211   if ($msg =~ /\t/ && $orig !~ /\t/) {
212       print STDERR "Warning: $where has menu accelerator but original doesn't\n";
213   } elsif ($msg !~ /\t/ && $orig =~ /\t/) {
214       print STDERR "Warning: $where is missing menu accelerator\n";
215   } elsif ($orig =~ /\t(.*)/) {
216       my $acc_o = $1;
217       my ($acc_m) = $msg =~ /\t(.*)/;
218       if ($acc_o ne $acc_m) {
219           print STDERR "Warning: $where has menu accelerator $acc_m instead of $acc_o\n";
220       }
221   }
222}
223
224sub c_unescape {
225   my $str = shift @_;
226   $str =~ s/\\(x..|0|[0-7][0-7][0-7]|.)/&c_unescape_char($1)/ge;
227   return $str;
228}
229
230sub c_unescape_char {
231    my $ch = shift @_;
232    if ($ch eq '0' || $ch eq 'x00' || $ch eq '000') {
233        print STDERR "Nul byte in translation! (\\$ch)\n";
234        exit(1);
235    }
236    return $ch if $ch eq '"' || $ch eq '\\';
237    return "\n" if $ch eq "n";
238    return "\t" if $ch eq "t";
239    return "\r" if $ch eq "r";
240    return "\f" if $ch eq "f";
241    return chr(hex(substr($ch,1))) if $ch =~ /^x../;
242    return chr(oct($ch)) if $ch =~ /^[0-7][0-7][0-7]/;
243    print STDERR "Unknown C-escape in translation! (\\$ch)\n";
244    exit(1);
245}
Note: See TracBrowser for help on using the repository browser.