]>
Commit | Line | Data |
---|---|---|
3f7b1143 DM |
1 | #!/usr/bin/perl |
2 | ||
3 | use strict; | |
4 | use warnings; | |
bb0ff4b0 | 5 | |
3f7b1143 | 6 | use Encode; |
bb0ff4b0 TL |
7 | use Getopt::Long; |
8 | use JSON; | |
9 | use Locale::PO; | |
3f7b1143 DM |
10 | |
11 | # current limits: | |
12 | # - we do not support plural. forms | |
13 | # - no message content support | |
14 | ||
9ce3f62e | 15 | my $options = {}; |
bb0ff4b0 | 16 | GetOptions($options, 't=s', 'o=s', 'v=s') or die "unable to parse options\n"; |
9ce3f62e | 17 | |
3f7b1143 DM |
18 | die "no files specified\n" if !scalar(@ARGV); |
19 | ||
3f7b1143 DM |
20 | # like FNV32a, but we only return 31 bits (positive numbers) |
21 | sub fnv31a { | |
22 | my ($string) = @_; | |
23 | ||
24 | my $hval = 0x811c9dc5; | |
df8c4aa4 | 25 | for my $c (unpack('C*', $string)) { |
3f7b1143 | 26 | $hval ^= $c; |
df8c4aa4 | 27 | $hval += ($hval << 1) + ($hval << 4) + ($hval << 7) + ($hval << 8) + ($hval << 24); |
3f7b1143 DM |
28 | $hval = $hval & 0xffffffff; |
29 | } | |
30 | return $hval & 0x7fffffff; | |
31 | } | |
32 | ||
fe2d4dee | 33 | my $catalog = {}; |
3f7b1143 DM |
34 | |
35 | foreach my $filename (@ARGV) { | |
36 | my $href = Locale::PO->load_file_ashash($filename) || | |
37 | die "unable to load '$filename'\n"; | |
df8c4aa4 | 38 | |
3f7b1143 DM |
39 | my $charset; |
40 | my $hpo = $href->{'""'} || die "no header"; | |
41 | my $header = $hpo->dequote($hpo->msgstr); | |
42 | if ($header =~ m|^Content-Type:\s+text/plain;\s+charset=(\S+)$|im) { | |
43 | $charset = $1; | |
44 | } else { | |
45 | die "unable to get charset\n" if !$charset; | |
46 | } | |
47 | ||
df8c4aa4 | 48 | for my $k (keys %$href) { |
3f7b1143 DM |
49 | my $po = $href->{$k}; |
50 | next if $po->fuzzy(); # skip fuzzy entries | |
df8c4aa4 | 51 | my $ref = $po->reference() or next; # skip unused entries |
c1ae7daa | 52 | |
df8c4aa4 | 53 | # skip entries if "t" is defined (pve/pmg) and the string is |
c1ae7daa DC |
54 | # not used there or in the widget toolkit |
55 | next if $options->{t} && $ref !~ m/($options->{t}|proxmox)\-/; | |
df8c4aa4 | 56 | |
3f7b1143 DM |
57 | my $qmsgid = decode($charset, $po->msgid); |
58 | my $msgid = $po->dequote($qmsgid); | |
59 | ||
60 | my $qmsgstr = decode($charset, $po->msgstr); | |
61 | my $msgstr = $po->dequote($qmsgstr); | |
62 | ||
63 | next if !length($msgid); # skip header | |
fe2d4dee | 64 | next if !length($msgstr); # skip untranslated entries |
3f7b1143 DM |
65 | |
66 | my $digest = fnv31a($msgid); | |
67 | ||
df8c4aa4 | 68 | die "duplicate digest '$digest' (msgid '$msgid')\n" if $catalog->{$digest}; |
3f7b1143 DM |
69 | |
70 | $catalog->{$digest} = [ $msgstr ]; | |
71 | # later, we can add plural forms to the array | |
72 | } | |
73 | } | |
74 | ||
75 | my $json = to_json($catalog, {canonical => 1, utf8 => 1}); | |
76 | ||
c08a2840 TL |
77 | my $version = $options->{v} // ("dev-build " . localtime()); |
78 | my $content = "// $version\n"; # write version to the beginning to better avoid stale cache | |
9ce3f62e DM |
79 | |
80 | my $outfile = $options->{o}; | |
81 | ||
82 | $content .= "// Proxmox Message Catalog: $outfile\n" if $outfile; | |
83 | ||
84 | $content .= <<__EOD; | |
7159a792 | 85 | const __proxmox_i18n_msgcat__ = $json; |
3f7b1143 DM |
86 | |
87 | function fnv31a(text) { | |
7159a792 TL |
88 | let hval = 0x811c9dc5; |
89 | for (const c of text) { | |
3f7b1143 DM |
90 | hval ^= c; |
91 | hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); | |
92 | } | |
93 | hval &= 0x7fffffff; | |
94 | return hval; | |
95 | } | |
96 | ||
97 | function gettext(buf) { | |
7159a792 TL |
98 | let digest = fnv31a(buf); |
99 | return __proxmox_i18n_msgcat__[digest]?.[0] ?? buf; | |
3f7b1143 | 100 | } |
3f7b1143 DM |
101 | __EOD |
102 | ||
9ce3f62e DM |
103 | if ($outfile) { |
104 | open(my $fh, '>', $outfile) || | |
105 | die "unable to open '$outfile' - $!\n"; | |
106 | print $fh $content; | |
107 | } else { | |
108 | print $content; | |
109 | } |