| 1 |
#!/usr/bin/perl -w |
| 2 |
# vi:wrap: |
| 3 |
|
| 4 |
# See Qt I18N documentation for usage information. |
| 5 |
|
| 6 |
use POSIX qw(strftime); |
| 7 |
|
| 8 |
$projectid='PROJECT VERSION'; |
| 9 |
$datetime = strftime "%Y-%m-%d %X %Z", localtime; |
| 10 |
$charset='iso-8859-1'; |
| 11 |
$translator='FULLNAME <EMAIL@ADDRESS>'; |
| 12 |
$revision_date='YYYY-MM-DD'; |
| 13 |
|
| 14 |
$real_mark = "tr"; |
| 15 |
$noop_mark = "QT_TR_NOOP"; |
| 16 |
$scoped_mark = "qApp->translate"; |
| 17 |
$noop_scoped_mark = "QT_TRANSLATE_NOOP"; |
| 18 |
|
| 19 |
$header= |
| 20 |
'# This is a Qt message file in .po format. Each msgid starts with |
| 21 |
# a scope. This scope should *NOT* be translated - eg. translating |
| 22 |
# from French to English, "Foo::Bar" would be translated to "Pub", |
| 23 |
# not "Foo::Pub". |
| 24 |
msgid "" |
| 25 |
msgstr "" |
| 26 |
"Project-Id-Version: '.$projectid.'\n" |
| 27 |
"POT-Creation-Date: '.$datetime.'\n" |
| 28 |
"PO-Revision-Date: '.$revision_date.'\n" |
| 29 |
"Last-Translator: '.$translator.'\n" |
| 30 |
"Content-Type: text/plain; charset='.$charset.'\n" |
| 31 |
|
| 32 |
'; |
| 33 |
|
| 34 |
|
| 35 |
|
| 36 |
|
| 37 |
$scope = ""; |
| 38 |
|
| 39 |
if ( $#ARGV < 0 ) { |
| 40 |
print STDERR "Usage: findtr sourcefile ... >project.po\n"; |
| 41 |
exit 1; |
| 42 |
} |
| 43 |
|
| 44 |
|
| 45 |
sub outmsg { |
| 46 |
my ($file, $line, $scope, $msgid) = @_; |
| 47 |
# unesc |
| 48 |
$msgid =~ s/$esc:$esc:$esc/::/gs; |
| 49 |
$msgid =~ s|$esc/$esc/$esc|//|gs; |
| 50 |
|
| 51 |
# Remove blank lines |
| 52 |
$msgid =~ s/\n\n+/\n/gs; |
| 53 |
$msgid = "\"\"\n$msgid" if $msgid =~ /\n/s; |
| 54 |
print "#: $file:$line\n"; |
| 55 |
$msgid =~ s/^"//; #"emacs bug |
| 56 |
print "msgid \"${scope}::$msgid\n"; |
| 57 |
#print "msgstr \"$msgid\n"; |
| 58 |
print "msgstr \"\"\n"; |
| 59 |
print "\n"; |
| 60 |
} |
| 61 |
|
| 62 |
sub justlines { |
| 63 |
my $l = @_; |
| 64 |
$l =~ tr|\n||dc; |
| 65 |
return $l; |
| 66 |
} |
| 67 |
|
| 68 |
print $header; |
| 69 |
|
| 70 |
|
| 71 |
foreach $file ( @ARGV ) { |
| 72 |
next unless open( I, "< $file" ); |
| 73 |
|
| 74 |
$source = join( "", <I> ); |
| 75 |
|
| 76 |
# Find esc. Avoid bad case 1/// -> 1/1/1/1 -> ///1 |
| 77 |
$esc = 1; |
| 78 |
while ( $source =~ m!(?:$esc/$esc/$esc)|(?:$esc///) |
| 79 |
|(?:$esc:$esc:$esc)|(?:$esc:\:\:)! ) { |
| 80 |
$esc++; |
| 81 |
} |
| 82 |
|
| 83 |
# Hide quoted :: in practically all strings |
| 84 |
$source =~ s/\"([^"\n]*)::([^"\n]*)\"/\"$1$esc:$esc:$esc$2\"/g; |
| 85 |
|
| 86 |
# Hide quoted // in practically all strings |
| 87 |
$source =~ s|\"([^"\n]*)//([^"\n]*)\"|\"$1$esc/$esc/$esc$2\"|g; |
| 88 |
|
| 89 |
|
| 90 |
# strip comments -- does not handle "/*" in strings |
| 91 |
while( $source =~ s|/\*(.*?)\*/|justlines($1)|ges ) { } |
| 92 |
while( $source =~ s|//(.*?)\n|\n|g ) { } |
| 93 |
|
| 94 |
while( $source =~ / |
| 95 |
(?: |
| 96 |
# Some doublequotes are "escaped" to help vim syntax highlight |
| 97 |
|
| 98 |
# $1 = scope; $2 = parameters etc. |
| 99 |
(?: |
| 100 |
# Scoped function name ($1 is scope). |
| 101 |
(\w+)::(?:\w+) |
| 102 |
\s* |
| 103 |
# Parameters etc up to open-curly - no semicolons |
| 104 |
\(([^();]*)\) |
| 105 |
\s* |
| 106 |
(?:\{|:) |
| 107 |
) |
| 108 |
| |
| 109 |
# $3 - one-argument msgid |
| 110 |
(?:\b |
| 111 |
# One of the marks |
| 112 |
(?:$real_mark|$noop_mark) |
| 113 |
\s* |
| 114 |
# The parameter |
| 115 |
\(\s*((?:"(?:[^"]|[^\\]\\")*"\s*)+)\) |
| 116 |
) |
| 117 |
| |
| 118 |
# $4,$5 - two-argument msgid |
| 119 |
(?:\b |
| 120 |
# One of the scoped marks |
| 121 |
(?:$scoped_mark|$noop_scoped_mark) |
| 122 |
\s* |
| 123 |
# The parameters |
| 124 |
\( |
| 125 |
# The scope parameter |
| 126 |
\s*"([^\"]*)" |
| 127 |
\s*,\s* |
| 128 |
# The msgid parameter |
| 129 |
\s*((?:\"(?:[^"]|[^\\]\\")*"\s*)+) #"emacs |
| 130 |
\) |
| 131 |
) |
| 132 |
| |
| 133 |
# $6,$7 - scoped one-argument msgid |
| 134 |
(?:\b |
| 135 |
# The scope |
| 136 |
(\w+):: |
| 137 |
# One of the marks |
| 138 |
(?:$real_mark) |
| 139 |
\s* |
| 140 |
# The parameter |
| 141 |
\(\s*((?:"(?:[^"]|[^\\]\\")*"\s*)+)\) |
| 142 |
) |
| 143 |
)/gsx ) |
| 144 |
{ |
| 145 |
@lines = split /^/m, "$`"; |
| 146 |
$line = @lines; |
| 147 |
if ( defined( $1 ) ) { |
| 148 |
if ( $scope ne $1 ) { |
| 149 |
$sc=$1; |
| 150 |
$etc=$2; |
| 151 |
# remove strings |
| 152 |
$etc =~ s/"(?:[^"]|[^\\]\\")"//g; |
| 153 |
# count ( and ) |
| 154 |
@open = split /\(/m, $etc; |
| 155 |
@close = split /\)/m, $etc; |
| 156 |
if ( $#open == $#close ) { |
| 157 |
$scope = $sc; |
| 158 |
} |
| 159 |
} |
| 160 |
next; |
| 161 |
} |
| 162 |
|
| 163 |
if ( defined( $3 ) ) { |
| 164 |
$this_scope = $scope; |
| 165 |
$msgid = $3; |
| 166 |
} elsif ( defined( $4 ) ) { |
| 167 |
$this_scope = $4; |
| 168 |
$msgid = $5; |
| 169 |
} elsif ( defined( $6 ) ) { |
| 170 |
$this_scope = $6; |
| 171 |
$msgid = $7; |
| 172 |
} else { |
| 173 |
next; |
| 174 |
} |
| 175 |
|
| 176 |
$msgid =~ s/^\s*//; |
| 177 |
$msgid =~ s/\s*$//; |
| 178 |
|
| 179 |
# Might still be non-unique eg. tr("A" "B") vs. tr("A" "B"). |
| 180 |
|
| 181 |
$location{"${this_scope}::${msgid}"} = "$file:$line"; |
| 182 |
} |
| 183 |
} |
| 184 |
|
| 185 |
for $scoped_msgid ( sort keys %location ) { |
| 186 |
($scope,$msgid) = $scoped_msgid =~ m/([^:]*)::(.*)/s; |
| 187 |
($file,$line) = $location{$scoped_msgid} =~ m/([^:]*):(.*)/s; |
| 188 |
outmsg($file,$line,$scope,$msgid); |
| 189 |
} |