-rw-r--r-- | development/translation/opie-lrelease/lrelease.pro | 16 | ||||
-rw-r--r-- | development/translation/opie-lrelease/main.cpp | 150 | ||||
-rw-r--r-- | development/translation/opie-lupdate/fetchtr.cpp | 784 | ||||
-rwxr-xr-x | development/translation/opie-lupdate/lupdate | bin | 0 -> 128093 bytes | |||
-rw-r--r-- | development/translation/opie-lupdate/main.cpp | 215 | ||||
-rw-r--r-- | development/translation/opie-lupdate/merge.cpp | 115 | ||||
-rw-r--r-- | development/translation/opie-lupdate/numberh.cpp | 235 | ||||
-rw-r--r-- | development/translation/opie-lupdate/opie-lupdate.pro | 21 | ||||
-rw-r--r-- | development/translation/opie-lupdate/sametexth.cpp | 84 | ||||
-rw-r--r-- | development/translation/shared/metatranslator.cpp | 586 | ||||
-rw-r--r-- | development/translation/shared/metatranslator.h | 99 | ||||
-rw-r--r-- | development/translation/shared/opie.cpp | 40 | ||||
-rw-r--r-- | development/translation/shared/opie.h | 21 | ||||
-rw-r--r-- | development/translation/shared/proparser.cpp | 87 | ||||
-rw-r--r-- | development/translation/shared/proparser.h | 29 |
15 files changed, 2482 insertions, 0 deletions
diff --git a/development/translation/opie-lrelease/lrelease.pro b/development/translation/opie-lrelease/lrelease.pro new file mode 100644 index 0000000..fa285cb --- a/dev/null +++ b/development/translation/opie-lrelease/lrelease.pro | |||
@@ -0,0 +1,16 @@ | |||
1 | TEMPLATE= app | ||
2 | CONFIG += qt warn_on console | ||
3 | HEADERS = ../shared/metatranslator.h \ | ||
4 | ../shared/proparser.h \ | ||
5 | ../shared/opie.h | ||
6 | SOURCES = main.cpp \ | ||
7 | ../shared/metatranslator.cpp \ | ||
8 | ../shared/proparser.cpp \ | ||
9 | ../shared/opie.cpp | ||
10 | |||
11 | DEFINES += QT_INTERNAL_XML | ||
12 | |||
13 | TARGET = opie-lrelease | ||
14 | INCLUDEPATH+= ../shared | ||
15 | #DESTDIR = ../../../bin | ||
16 | |||
diff --git a/development/translation/opie-lrelease/main.cpp b/development/translation/opie-lrelease/main.cpp new file mode 100644 index 0000000..6008c4e --- a/dev/null +++ b/development/translation/opie-lrelease/main.cpp | |||
@@ -0,0 +1,150 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | #include <proparser.h> | ||
23 | #include <opie.h> | ||
24 | |||
25 | #include <qfile.h> | ||
26 | #include <qregexp.h> | ||
27 | #include <qstring.h> | ||
28 | #include <qstringlist.h> | ||
29 | #include <qtextstream.h> | ||
30 | |||
31 | #include <errno.h> | ||
32 | |||
33 | typedef QValueList<MetaTranslatorMessage> TML; | ||
34 | |||
35 | static void printUsage() | ||
36 | { | ||
37 | fprintf( stderr, "Usage:\n" | ||
38 | " lrelease [options] project-file\n" | ||
39 | " lrelease [options] ts-files\n" | ||
40 | "Options:\n" | ||
41 | " -opie OPIE dir overrides $OPIEDIR\n" | ||
42 | " -help Display this information and exit\n" | ||
43 | " -verbose\n" | ||
44 | " Explain what is being done\n" | ||
45 | " -version\n" | ||
46 | " Display the version of lrelease and exit\n" ); | ||
47 | } | ||
48 | static void releaseQmFile( const QString& tsFileName, bool verbose ) | ||
49 | { | ||
50 | MetaTranslator tor; | ||
51 | QString qmFileName = tsFileName; | ||
52 | qmFileName.replace( QRegExp("\\.ts$"), "" ); | ||
53 | qmFileName += ".qm"; | ||
54 | |||
55 | if ( tor.load(tsFileName) ) { | ||
56 | if ( verbose ) | ||
57 | fprintf( stderr, "Updating '%s'...\n", qmFileName.latin1() ); | ||
58 | if ( !tor.release(qmFileName, verbose) ) | ||
59 | fprintf( stderr, | ||
60 | "lrelease warning: For some reason, I cannot save '%s'\n", | ||
61 | qmFileName.latin1() ); | ||
62 | } else { | ||
63 | fprintf( stderr, | ||
64 | "lrelease warning: For some reason, I cannot load '%s'\n", | ||
65 | tsFileName.latin1() ); | ||
66 | } | ||
67 | } | ||
68 | static void metaQmFile( const QString &opiedir, | ||
69 | const QStringList& lang, | ||
70 | const QString& basename, | ||
71 | bool isLib, bool verb ) { | ||
72 | QString target = basename + ".ts"; | ||
73 | if ( isLib ) target.prepend("lib"); | ||
74 | |||
75 | for ( QStringList::ConstIterator it = lang.begin(); it != lang.end(); | ||
76 | ++it ) { | ||
77 | QString fileName = opiedir + "/i18n/" + (*it) + "/" + target; | ||
78 | qWarning("Target is %s", fileName.latin1() ); | ||
79 | } | ||
80 | } | ||
81 | int main( int argc, char **argv ) | ||
82 | { | ||
83 | bool verbose = FALSE; | ||
84 | bool metTranslations = FALSE; | ||
85 | int numFiles = 0; | ||
86 | QString opiedir; | ||
87 | QStringList languageList = OPIE::self()->languageList( opiedir ); | ||
88 | |||
89 | for ( int i = 1; i < argc; i++ ) { | ||
90 | if ( qstrcmp(argv[i], "-help") == 0 ) { | ||
91 | printUsage(); | ||
92 | return 0; | ||
93 | } else if ( qstrcmp(argv[i], "-verbose") == 0 ) { | ||
94 | verbose = TRUE; | ||
95 | continue; | ||
96 | } else if ( qstrcmp(argv[i], "-version") == 0 ) { | ||
97 | fprintf( stderr, "lrelease version %s\n", QT_VERSION_STR ); | ||
98 | return 0; | ||
99 | } else if ( qstrcmp(argv[i], "-opie") == 0 ) { | ||
100 | if ( i+1 < argc ) { | ||
101 | opiedir = argv[i+1]; | ||
102 | languageList = OPIE::self()->languageList(opiedir); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | numFiles++; | ||
107 | QFile f( argv[i] ); | ||
108 | if ( !f.open(IO_ReadOnly) ) { | ||
109 | fprintf( stderr, | ||
110 | "lrelease error: Cannot open file '%s': %s\n", argv[i], | ||
111 | strerror(errno) ); | ||
112 | return 1; | ||
113 | } | ||
114 | |||
115 | QTextStream t( &f ); | ||
116 | QString fullText = t.read(); | ||
117 | f.close(); | ||
118 | |||
119 | if ( fullText.find(QString("<!DOCTYPE TS>")) >= 0 ) { | ||
120 | releaseQmFile( argv[i], verbose ); | ||
121 | } else { | ||
122 | QString target; | ||
123 | bool isLib = FALSE; | ||
124 | QMap<QString, QString> tagMap = proFileTagMap( fullText ); | ||
125 | QMap<QString, QString>::Iterator it; | ||
126 | |||
127 | for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { | ||
128 | QStringList toks = QStringList::split( ' ', it.data() ); | ||
129 | QStringList::Iterator t; | ||
130 | |||
131 | for ( t = toks.begin(); t != toks.end(); ++t ) { | ||
132 | if ( it.key() == "TARGET" ) { | ||
133 | target = *t; | ||
134 | }else if ( it.key() == "TEMPLATE" ) { | ||
135 | if ( (*t).stripWhiteSpace().lower() == "lib" ) | ||
136 | isLib = TRUE; | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | metaQmFile( OPIE::self()->opieDir(opiedir), | ||
141 | languageList, target, isLib, verbose ); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | if ( numFiles == 0 ) { | ||
146 | printUsage(); | ||
147 | return 1; | ||
148 | } | ||
149 | return 0; | ||
150 | } | ||
diff --git a/development/translation/opie-lupdate/fetchtr.cpp b/development/translation/opie-lupdate/fetchtr.cpp new file mode 100644 index 0000000..eb25555 --- a/dev/null +++ b/development/translation/opie-lupdate/fetchtr.cpp | |||
@@ -0,0 +1,784 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | |||
23 | #include <qfile.h> | ||
24 | #include <qregexp.h> | ||
25 | #include <qstring.h> | ||
26 | #include <qtextstream.h> | ||
27 | #include <qvaluestack.h> | ||
28 | #include <qxml.h> | ||
29 | |||
30 | #include <ctype.h> | ||
31 | #include <errno.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | |||
35 | /* qmake ignore Q_OBJECT */ | ||
36 | |||
37 | static const char MagicComment[] = "TRANSLATOR "; | ||
38 | |||
39 | static QMap<QCString, int> needs_Q_OBJECT; | ||
40 | static QMap<QCString, int> lacks_Q_OBJECT; | ||
41 | |||
42 | /* | ||
43 | The first part of this source file is the C++ tokenizer. We skip | ||
44 | most of C++; the only tokens that interest us are defined here. | ||
45 | Thus, the code fragment | ||
46 | |||
47 | int main() | ||
48 | { | ||
49 | printf( "Hello, world!\n" ); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | is broken down into the following tokens (Tok_ omitted): | ||
54 | |||
55 | Ident Ident LeftParen RightParen | ||
56 | LeftBrace | ||
57 | Ident LeftParen String RightParen Semicolon | ||
58 | return Semicolon | ||
59 | RightBrace. | ||
60 | |||
61 | The 0 doesn't produce any token. | ||
62 | */ | ||
63 | |||
64 | enum { Tok_Eof, Tok_class, Tok_namespace, Tok_return, Tok_tr, | ||
65 | Tok_trUtf8, Tok_translate, Tok_Q_OBJECT, Tok_Ident, | ||
66 | Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, | ||
67 | Tok_Gulbrandsen, Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, | ||
68 | Tok_RightParen, Tok_Comma, Tok_Semicolon }; | ||
69 | |||
70 | /* | ||
71 | The tokenizer maintains the following global variables. The names | ||
72 | should be self-explanatory. | ||
73 | */ | ||
74 | static QCString yyFileName; | ||
75 | static int yyCh; | ||
76 | static char yyIdent[128]; | ||
77 | static size_t yyIdentLen; | ||
78 | static char yyComment[65536]; | ||
79 | static size_t yyCommentLen; | ||
80 | static char yyString[16384]; | ||
81 | static size_t yyStringLen; | ||
82 | static QValueStack<int> yySavedBraceDepth; | ||
83 | static int yyBraceDepth; | ||
84 | static int yyParenDepth; | ||
85 | static int yyLineNo; | ||
86 | static int yyCurLineNo; | ||
87 | |||
88 | // the file to read from (if reading from a file) | ||
89 | static FILE *yyInFile; | ||
90 | |||
91 | // the string to read from and current position in the string (otherwise) | ||
92 | static QString yyInStr; | ||
93 | static int yyInPos; | ||
94 | |||
95 | static int (*getChar)(); | ||
96 | |||
97 | static int getCharFromFile() | ||
98 | { | ||
99 | int c = getc( yyInFile ); | ||
100 | if ( c == '\n' ) | ||
101 | yyCurLineNo++; | ||
102 | return c; | ||
103 | } | ||
104 | |||
105 | static int getCharFromString() | ||
106 | { | ||
107 | if ( yyInPos == (int) yyInStr.length() ) { | ||
108 | return EOF; | ||
109 | } else { | ||
110 | return yyInStr[yyInPos++].latin1(); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static void startTokenizer( const char *fileName, int (*getCharFunc)() ) | ||
115 | { | ||
116 | yyInPos = 0; | ||
117 | getChar = getCharFunc; | ||
118 | |||
119 | yyFileName = fileName; | ||
120 | yyCh = getChar(); | ||
121 | yySavedBraceDepth.clear(); | ||
122 | yyBraceDepth = 0; | ||
123 | yyParenDepth = 0; | ||
124 | yyCurLineNo = 1; | ||
125 | } | ||
126 | |||
127 | static int getToken() | ||
128 | { | ||
129 | const char tab[] = "abfnrtv"; | ||
130 | const char backTab[] = "\a\b\f\n\r\t\v"; | ||
131 | uint n; | ||
132 | |||
133 | yyIdentLen = 0; | ||
134 | yyCommentLen = 0; | ||
135 | yyStringLen = 0; | ||
136 | |||
137 | while ( yyCh != EOF ) { | ||
138 | yyLineNo = yyCurLineNo; | ||
139 | |||
140 | if ( isalpha(yyCh) || yyCh == '_' ) { | ||
141 | do { | ||
142 | if ( yyIdentLen < sizeof(yyIdent) - 1 ) | ||
143 | yyIdent[yyIdentLen++] = (char) yyCh; | ||
144 | yyCh = getChar(); | ||
145 | } while ( isalnum(yyCh) || yyCh == '_' ); | ||
146 | yyIdent[yyIdentLen] = '\0'; | ||
147 | |||
148 | switch ( yyIdent[0] ) { | ||
149 | case 'Q': | ||
150 | if ( strcmp(yyIdent + 1, "_OBJECT") == 0 ) { | ||
151 | return Tok_Q_OBJECT; | ||
152 | } else if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) { | ||
153 | return Tok_tr; | ||
154 | } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) { | ||
155 | return Tok_translate; | ||
156 | } | ||
157 | break; | ||
158 | case 'T': | ||
159 | // TR() for when all else fails | ||
160 | if ( qstricmp(yyIdent + 1, "R") == 0 ) | ||
161 | return Tok_tr; | ||
162 | break; | ||
163 | case 'c': | ||
164 | if ( strcmp(yyIdent + 1, "lass") == 0 ) | ||
165 | return Tok_class; | ||
166 | break; | ||
167 | case 'n': | ||
168 | if ( strcmp(yyIdent + 1, "amespace") == 0 ) | ||
169 | return Tok_namespace; | ||
170 | break; | ||
171 | case 'r': | ||
172 | if ( strcmp(yyIdent + 1, "eturn") == 0 ) | ||
173 | return Tok_return; | ||
174 | break; | ||
175 | case 's': | ||
176 | if ( strcmp(yyIdent + 1, "truct") == 0 ) | ||
177 | return Tok_class; | ||
178 | break; | ||
179 | case 't': | ||
180 | if ( strcmp(yyIdent + 1, "r") == 0 ) { | ||
181 | return Tok_tr; | ||
182 | } else if ( qstrcmp(yyIdent + 1, "rUtf8") == 0 ) { | ||
183 | return Tok_trUtf8; | ||
184 | } else if ( qstrcmp(yyIdent + 1, "ranslate") == 0 ) { | ||
185 | return Tok_translate; | ||
186 | } | ||
187 | } | ||
188 | return Tok_Ident; | ||
189 | } else { | ||
190 | switch ( yyCh ) { | ||
191 | case '#': | ||
192 | /* | ||
193 | Early versions of lupdate complained about | ||
194 | unbalanced braces in the following code: | ||
195 | |||
196 | #ifdef ALPHA | ||
197 | while ( beta ) { | ||
198 | #else | ||
199 | while ( gamma ) { | ||
200 | #endif | ||
201 | delta; | ||
202 | } | ||
203 | |||
204 | The code contains, indeed, two opening braces for | ||
205 | one closing brace; yet there's no reason to panic. | ||
206 | |||
207 | The solution is to remember yyBraceDepth as it was | ||
208 | when #if, #ifdef or #ifndef was met, and to set | ||
209 | yyBraceDepth to that value when meeting #elif or | ||
210 | #else. | ||
211 | */ | ||
212 | do { | ||
213 | yyCh = getChar(); | ||
214 | } while ( isspace(yyCh) && yyCh != '\n' ); | ||
215 | |||
216 | switch ( yyCh ) { | ||
217 | case 'i': | ||
218 | yyCh = getChar(); | ||
219 | if ( yyCh == 'f' ) { | ||
220 | // if, ifdef, ifndef | ||
221 | yySavedBraceDepth.push( yyBraceDepth ); | ||
222 | } | ||
223 | break; | ||
224 | case 'e': | ||
225 | yyCh = getChar(); | ||
226 | if ( yyCh == 'l' ) { | ||
227 | // elif, else | ||
228 | if ( !yySavedBraceDepth.isEmpty() ) | ||
229 | yyBraceDepth = yySavedBraceDepth.top(); | ||
230 | } else if ( yyCh == 'n' ) { | ||
231 | // endif | ||
232 | if ( !yySavedBraceDepth.isEmpty() ) | ||
233 | yySavedBraceDepth.pop(); | ||
234 | } | ||
235 | } | ||
236 | while ( isalnum(yyCh) || yyCh == '_' ) | ||
237 | yyCh = getChar(); | ||
238 | break; | ||
239 | case '/': | ||
240 | yyCh = getChar(); | ||
241 | if ( yyCh == '/' ) { | ||
242 | do { | ||
243 | yyCh = getChar(); | ||
244 | } while ( yyCh != EOF && yyCh != '\n' ); | ||
245 | } else if ( yyCh == '*' ) { | ||
246 | bool metAster = FALSE; | ||
247 | bool metAsterSlash = FALSE; | ||
248 | |||
249 | while ( !metAsterSlash ) { | ||
250 | yyCh = getChar(); | ||
251 | if ( yyCh == EOF ) { | ||
252 | fprintf( stderr, | ||
253 | "%s: Unterminated C++ comment starting at" | ||
254 | " line %d\n", | ||
255 | (const char *) yyFileName, yyLineNo ); | ||
256 | yyComment[yyCommentLen] = '\0'; | ||
257 | return Tok_Comment; | ||
258 | } | ||
259 | if ( yyCommentLen < sizeof(yyComment) - 1 ) | ||
260 | yyComment[yyCommentLen++] = (char) yyCh; | ||
261 | |||
262 | if ( yyCh == '*' ) | ||
263 | metAster = TRUE; | ||
264 | else if ( metAster && yyCh == '/' ) | ||
265 | metAsterSlash = TRUE; | ||
266 | else | ||
267 | metAster = FALSE; | ||
268 | } | ||
269 | yyCh = getChar(); | ||
270 | yyCommentLen -= 2; | ||
271 | yyComment[yyCommentLen] = '\0'; | ||
272 | return Tok_Comment; | ||
273 | } | ||
274 | break; | ||
275 | case '"': | ||
276 | yyCh = getChar(); | ||
277 | |||
278 | while ( yyCh != EOF && yyCh != '\n' && yyCh != '"' ) { | ||
279 | if ( yyCh == '\\' ) { | ||
280 | yyCh = getChar(); | ||
281 | |||
282 | if ( yyCh == '\n' ) { | ||
283 | yyCh = getChar(); | ||
284 | } else if ( yyCh == 'x' ) { | ||
285 | QCString hex = "0"; | ||
286 | |||
287 | yyCh = getChar(); | ||
288 | while ( isxdigit(yyCh) ) { | ||
289 | hex += (char) yyCh; | ||
290 | yyCh = getChar(); | ||
291 | } | ||
292 | sscanf( hex, "%x", &n ); | ||
293 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
294 | yyString[yyStringLen++] = (char) n; | ||
295 | } else if ( yyCh >= '0' && yyCh < '8' ) { | ||
296 | QCString oct = ""; | ||
297 | |||
298 | do { | ||
299 | oct += (char) yyCh; | ||
300 | yyCh = getChar(); | ||
301 | } while ( yyCh >= '0' && yyCh < '8' ); | ||
302 | sscanf( oct, "%o", &n ); | ||
303 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
304 | yyString[yyStringLen++] = (char) n; | ||
305 | } else { | ||
306 | const char *p = strchr( tab, yyCh ); | ||
307 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
308 | yyString[yyStringLen++] = ( p == 0 ) ? | ||
309 | (char) yyCh : backTab[p - tab]; | ||
310 | yyCh = getChar(); | ||
311 | } | ||
312 | } else { | ||
313 | if ( yyStringLen < sizeof(yyString) - 1 ) | ||
314 | yyString[yyStringLen++] = (char) yyCh; | ||
315 | yyCh = getChar(); | ||
316 | } | ||
317 | } | ||
318 | yyString[yyStringLen] = '\0'; | ||
319 | |||
320 | if ( yyCh != '"' ) | ||
321 | qWarning( "%s:%d: Unterminated C++ string", | ||
322 | (const char *) yyFileName, yyLineNo ); | ||
323 | |||
324 | if ( yyCh == EOF ) { | ||
325 | return Tok_Eof; | ||
326 | } else { | ||
327 | yyCh = getChar(); | ||
328 | return Tok_String; | ||
329 | } | ||
330 | break; | ||
331 | case '-': | ||
332 | yyCh = getChar(); | ||
333 | if ( yyCh == '>' ) { | ||
334 | yyCh = getChar(); | ||
335 | return Tok_Arrow; | ||
336 | } | ||
337 | break; | ||
338 | case ':': | ||
339 | yyCh = getChar(); | ||
340 | if ( yyCh == ':' ) { | ||
341 | yyCh = getChar(); | ||
342 | return Tok_Gulbrandsen; | ||
343 | } | ||
344 | return Tok_Colon; | ||
345 | case '\'': | ||
346 | yyCh = getChar(); | ||
347 | if ( yyCh == '\\' ) | ||
348 | yyCh = getChar(); | ||
349 | |||
350 | do { | ||
351 | yyCh = getChar(); | ||
352 | } while ( yyCh != EOF && yyCh != '\'' ); | ||
353 | yyCh = getChar(); | ||
354 | break; | ||
355 | case '{': | ||
356 | yyBraceDepth++; | ||
357 | yyCh = getChar(); | ||
358 | return Tok_LeftBrace; | ||
359 | case '}': | ||
360 | yyBraceDepth--; | ||
361 | yyCh = getChar(); | ||
362 | return Tok_RightBrace; | ||
363 | case '(': | ||
364 | yyParenDepth++; | ||
365 | yyCh = getChar(); | ||
366 | return Tok_LeftParen; | ||
367 | case ')': | ||
368 | yyParenDepth--; | ||
369 | yyCh = getChar(); | ||
370 | return Tok_RightParen; | ||
371 | case ',': | ||
372 | yyCh = getChar(); | ||
373 | return Tok_Comma; | ||
374 | case ';': | ||
375 | yyCh = getChar(); | ||
376 | return Tok_Semicolon; | ||
377 | default: | ||
378 | yyCh = getChar(); | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | return Tok_Eof; | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | The second part of this source file is the parser. It accomplishes | ||
387 | a very easy task: It finds all strings inside a tr() or translate() | ||
388 | call, and possibly finds out the context of the call. It supports | ||
389 | three cases: (1) the context is specified, as in | ||
390 | FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); | ||
391 | (2) the call appears within an inlined function; (3) the call | ||
392 | appears within a function defined outside the class definition. | ||
393 | */ | ||
394 | |||
395 | static int yyTok; | ||
396 | |||
397 | static bool match( int t ) | ||
398 | { | ||
399 | bool matches = ( yyTok == t ); | ||
400 | if ( matches ) | ||
401 | yyTok = getToken(); | ||
402 | return matches; | ||
403 | } | ||
404 | |||
405 | static bool matchString( QCString *s ) | ||
406 | { | ||
407 | bool matches = ( yyTok == Tok_String ); | ||
408 | *s = ""; | ||
409 | while ( yyTok == Tok_String ) { | ||
410 | *s += yyString; | ||
411 | yyTok = getToken(); | ||
412 | } | ||
413 | return matches; | ||
414 | } | ||
415 | |||
416 | static bool matchEncoding( bool *utf8 ) | ||
417 | { | ||
418 | if ( yyTok == Tok_Ident ) { | ||
419 | if ( strcmp(yyIdent, "QApplication") == 0 ) { | ||
420 | yyTok = getToken(); | ||
421 | if ( yyTok == Tok_Gulbrandsen ) | ||
422 | yyTok = getToken(); | ||
423 | } | ||
424 | *utf8 = QString( yyIdent ).endsWith( QString("UTF8") ); | ||
425 | yyTok = getToken(); | ||
426 | return TRUE; | ||
427 | } else { | ||
428 | return FALSE; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | static void parse( MetaTranslator *tor, const char *initialContext, | ||
433 | const char *defaultContext ) | ||
434 | { | ||
435 | QMap<QCString, QCString> qualifiedContexts; | ||
436 | QStringList namespaces; | ||
437 | QCString context; | ||
438 | QCString text; | ||
439 | QCString com; | ||
440 | QCString functionContext = initialContext; | ||
441 | QCString prefix; | ||
442 | bool utf8 = FALSE; | ||
443 | bool missing_Q_OBJECT = FALSE; | ||
444 | |||
445 | yyTok = getToken(); | ||
446 | while ( yyTok != Tok_Eof ) { | ||
447 | switch ( yyTok ) { | ||
448 | case Tok_class: | ||
449 | /* | ||
450 | Partial support for inlined functions. | ||
451 | */ | ||
452 | yyTok = getToken(); | ||
453 | if ( yyBraceDepth == (int) namespaces.count() && | ||
454 | yyParenDepth == 0 ) { | ||
455 | do { | ||
456 | /* | ||
457 | This code should execute only once, but we play | ||
458 | safe with impure definitions such as | ||
459 | 'class Q_EXPORT QMessageBox', in which case | ||
460 | 'QMessageBox' is the class name, not 'Q_EXPORT'. | ||
461 | */ | ||
462 | functionContext = yyIdent; | ||
463 | yyTok = getToken(); | ||
464 | } while ( yyTok == Tok_Ident ); | ||
465 | |||
466 | while ( yyTok == Tok_Gulbrandsen ) { | ||
467 | yyTok = getToken(); | ||
468 | functionContext += "::"; | ||
469 | functionContext += yyIdent; | ||
470 | yyTok = getToken(); | ||
471 | } | ||
472 | |||
473 | if ( yyTok == Tok_Colon ) { | ||
474 | missing_Q_OBJECT = TRUE; | ||
475 | } else { | ||
476 | functionContext = defaultContext; | ||
477 | } | ||
478 | } | ||
479 | break; | ||
480 | case Tok_namespace: | ||
481 | yyTok = getToken(); | ||
482 | if ( yyTok == Tok_Ident ) { | ||
483 | QCString ns = yyIdent; | ||
484 | yyTok = getToken(); | ||
485 | if ( yyTok == Tok_LeftBrace && | ||
486 | yyBraceDepth == (int) namespaces.count() + 1 ) | ||
487 | namespaces.append( QString(ns) ); | ||
488 | } | ||
489 | break; | ||
490 | case Tok_tr: | ||
491 | case Tok_trUtf8: | ||
492 | utf8 = ( yyTok == Tok_trUtf8 ); | ||
493 | yyTok = getToken(); | ||
494 | if ( match(Tok_LeftParen) && matchString(&text) ) { | ||
495 | com = ""; | ||
496 | if ( match(Tok_RightParen) || (match(Tok_Comma) && | ||
497 | matchString(&com) && match(Tok_RightParen)) ) { | ||
498 | if ( prefix.isNull() ) { | ||
499 | context = functionContext; | ||
500 | if ( !namespaces.isEmpty() ) | ||
501 | context.prepend( (namespaces.join(QString("::")) + | ||
502 | QString("::")).latin1() ); | ||
503 | } else { | ||
504 | context = prefix; | ||
505 | } | ||
506 | prefix = (const char *) 0; | ||
507 | |||
508 | if ( qualifiedContexts.contains(context) ) | ||
509 | context = qualifiedContexts[context]; | ||
510 | tor->insert( MetaTranslatorMessage(context, text, com, | ||
511 | QString::null, utf8) ); | ||
512 | |||
513 | if ( lacks_Q_OBJECT.contains(context) ) { | ||
514 | qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", | ||
515 | (const char *) yyFileName, yyLineNo, | ||
516 | (const char *) context ); | ||
517 | lacks_Q_OBJECT.remove( context ); | ||
518 | } else { | ||
519 | needs_Q_OBJECT.insert( context, 0 ); | ||
520 | } | ||
521 | } | ||
522 | } | ||
523 | break; | ||
524 | case Tok_translate: | ||
525 | utf8 = FALSE; | ||
526 | yyTok = getToken(); | ||
527 | if ( match(Tok_LeftParen) && | ||
528 | matchString(&context) && | ||
529 | match(Tok_Comma) && | ||
530 | matchString(&text) ) { | ||
531 | com = ""; | ||
532 | if ( match(Tok_RightParen) || | ||
533 | (match(Tok_Comma) && | ||
534 | matchString(&com) && | ||
535 | (match(Tok_RightParen) || | ||
536 | match(Tok_Comma) && | ||
537 | matchEncoding(&utf8) && | ||
538 | match(Tok_RightParen))) ) | ||
539 | tor->insert( MetaTranslatorMessage(context, text, com, | ||
540 | QString::null, utf8) ); | ||
541 | } | ||
542 | break; | ||
543 | case Tok_Q_OBJECT: | ||
544 | missing_Q_OBJECT = FALSE; | ||
545 | yyTok = getToken(); | ||
546 | break; | ||
547 | case Tok_Ident: | ||
548 | if ( !prefix.isNull() ) | ||
549 | prefix += "::"; | ||
550 | prefix += yyIdent; | ||
551 | yyTok = getToken(); | ||
552 | if ( yyTok != Tok_Gulbrandsen ) | ||
553 | prefix = (const char *) 0; | ||
554 | break; | ||
555 | case Tok_Comment: | ||
556 | com = yyComment; | ||
557 | com = com.simplifyWhiteSpace(); | ||
558 | if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) { | ||
559 | com.remove( 0, sizeof(MagicComment) - 1 ); | ||
560 | int k = com.find( ' ' ); | ||
561 | if ( k == -1 ) { | ||
562 | context = com; | ||
563 | } else { | ||
564 | context = com.left( k ); | ||
565 | com.remove( 0, k + 1 ); | ||
566 | tor->insert( MetaTranslatorMessage(context, "", com, | ||
567 | QString::null, FALSE) ); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | Provide a backdoor for people using "using | ||
572 | namespace". See the manual for details. | ||
573 | */ | ||
574 | k = 0; | ||
575 | while ( (k = context.find("::", k)) != -1 ) { | ||
576 | qualifiedContexts.insert( context.mid(k + 2), context ); | ||
577 | k++; | ||
578 | } | ||
579 | } | ||
580 | yyTok = getToken(); | ||
581 | break; | ||
582 | case Tok_Arrow: | ||
583 | yyTok = getToken(); | ||
584 | if ( yyTok == Tok_tr || yyTok == Tok_trUtf8 ) | ||
585 | qWarning( "%s:%d: Cannot invoke tr() like this", | ||
586 | (const char *) yyFileName, yyLineNo ); | ||
587 | break; | ||
588 | case Tok_Gulbrandsen: | ||
589 | // at top level? | ||
590 | if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) | ||
591 | functionContext = prefix; | ||
592 | yyTok = getToken(); | ||
593 | break; | ||
594 | case Tok_RightBrace: | ||
595 | case Tok_Semicolon: | ||
596 | if ( yyBraceDepth >= 0 && | ||
597 | yyBraceDepth + 1 == (int) namespaces.count() ) | ||
598 | namespaces.remove( namespaces.fromLast() ); | ||
599 | if ( yyBraceDepth == (int) namespaces.count() ) { | ||
600 | if ( missing_Q_OBJECT ) { | ||
601 | if ( needs_Q_OBJECT.contains(functionContext) ) { | ||
602 | qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", | ||
603 | (const char *) yyFileName, yyLineNo, | ||
604 | (const char *) functionContext ); | ||
605 | } else { | ||
606 | lacks_Q_OBJECT.insert( functionContext, 0 ); | ||
607 | } | ||
608 | } | ||
609 | functionContext = defaultContext; | ||
610 | missing_Q_OBJECT = FALSE; | ||
611 | } | ||
612 | yyTok = getToken(); | ||
613 | break; | ||
614 | default: | ||
615 | yyTok = getToken(); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | if ( yyBraceDepth != 0 ) | ||
620 | fprintf( stderr, | ||
621 | "%s: Unbalanced braces in C++ code (or abuse of the C++" | ||
622 | " preprocessor)\n", | ||
623 | (const char *) yyFileName ); | ||
624 | if ( yyParenDepth != 0 ) | ||
625 | fprintf( stderr, | ||
626 | "%s: Unbalanced parentheses in C++ code (or abuse of the C++" | ||
627 | " preprocessor)\n", | ||
628 | (const char *) yyFileName ); | ||
629 | } | ||
630 | |||
631 | void fetchtr_cpp( const char *fileName, MetaTranslator *tor, | ||
632 | const char *defaultContext, bool mustExist ) | ||
633 | { | ||
634 | yyInFile = fopen( fileName, "r" ); | ||
635 | if ( yyInFile == 0 ) { | ||
636 | if ( mustExist ) | ||
637 | fprintf( stderr, | ||
638 | "lupdate error: Cannot open C++ source file '%s': %s\n", | ||
639 | fileName, strerror(errno) ); | ||
640 | return; | ||
641 | } | ||
642 | |||
643 | startTokenizer( fileName, getCharFromFile ); | ||
644 | parse( tor, 0, defaultContext ); | ||
645 | fclose( yyInFile ); | ||
646 | } | ||
647 | |||
648 | /* | ||
649 | In addition to C++, we support Qt Designer UI files. | ||
650 | */ | ||
651 | |||
652 | /* | ||
653 | Fetches tr() calls in C++ code in UI files (inside "<function>" | ||
654 | tag). This mechanism is obsolete. | ||
655 | */ | ||
656 | void fetchtr_inlined_cpp( const char *fileName, const QString& in, | ||
657 | MetaTranslator *tor, const char *context ) | ||
658 | { | ||
659 | yyInStr = in; | ||
660 | startTokenizer( fileName, getCharFromString ); | ||
661 | parse( tor, context, 0 ); | ||
662 | yyInStr = QString::null; | ||
663 | } | ||
664 | |||
665 | class UiHandler : public QXmlDefaultHandler | ||
666 | { | ||
667 | public: | ||
668 | UiHandler( MetaTranslator *translator, const char *fileName ) | ||
669 | : tor( translator ), fname( fileName ), comment( "" ) { } | ||
670 | |||
671 | virtual bool startElement( const QString& namespaceURI, | ||
672 | const QString& localName, const QString& qName, | ||
673 | const QXmlAttributes& atts ); | ||
674 | virtual bool endElement( const QString& namespaceURI, | ||
675 | const QString& localName, const QString& qName ); | ||
676 | virtual bool characters( const QString& ch ); | ||
677 | virtual bool fatalError( const QXmlParseException& exception ); | ||
678 | |||
679 | private: | ||
680 | void flush(); | ||
681 | |||
682 | MetaTranslator *tor; | ||
683 | QCString fname; | ||
684 | QString context; | ||
685 | QString source; | ||
686 | QString comment; | ||
687 | |||
688 | QString accum; | ||
689 | }; | ||
690 | |||
691 | bool UiHandler::startElement( const QString& /* namespaceURI */, | ||
692 | const QString& /* localName */, | ||
693 | const QString& qName, | ||
694 | const QXmlAttributes& atts ) | ||
695 | { | ||
696 | if ( qName == QString("item") ) { | ||
697 | flush(); | ||
698 | if ( !atts.value(QString("text")).isEmpty() ) | ||
699 | source = atts.value( QString("text") ); | ||
700 | } else if ( qName == QString("string") ) { | ||
701 | flush(); | ||
702 | } | ||
703 | accum.truncate( 0 ); | ||
704 | return TRUE; | ||
705 | } | ||
706 | |||
707 | bool UiHandler::endElement( const QString& /* namespaceURI */, | ||
708 | const QString& /* localName */, | ||
709 | const QString& qName ) | ||
710 | { | ||
711 | accum.replace( QRegExp(QString("\r\n")), "\n" ); | ||
712 | |||
713 | if ( qName == QString("class") ) { | ||
714 | if ( context.isEmpty() ) | ||
715 | context = accum; | ||
716 | } else if ( qName == QString("string") ) { | ||
717 | source = accum; | ||
718 | } else if ( qName == QString("comment") ) { | ||
719 | comment = accum; | ||
720 | flush(); | ||
721 | } else if ( qName == QString("function") ) { | ||
722 | fetchtr_inlined_cpp( (const char *) fname, accum, tor, | ||
723 | context.latin1() ); | ||
724 | } else { | ||
725 | flush(); | ||
726 | } | ||
727 | return TRUE; | ||
728 | } | ||
729 | |||
730 | bool UiHandler::characters( const QString& ch ) | ||
731 | { | ||
732 | accum += ch; | ||
733 | return TRUE; | ||
734 | } | ||
735 | |||
736 | bool UiHandler::fatalError( const QXmlParseException& exception ) | ||
737 | { | ||
738 | QString msg; | ||
739 | msg.sprintf( "Parse error at line %d, column %d (%s).", | ||
740 | exception.lineNumber(), exception.columnNumber(), | ||
741 | exception.message().latin1() ); | ||
742 | fprintf( stderr, "XML error: %s\n", msg.latin1() ); | ||
743 | return FALSE; | ||
744 | } | ||
745 | |||
746 | void UiHandler::flush() | ||
747 | { | ||
748 | if ( !context.isEmpty() && !source.isEmpty() ) | ||
749 | tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), | ||
750 | comment.utf8(), QString::null, | ||
751 | TRUE) ); | ||
752 | source.truncate( 0 ); | ||
753 | comment.truncate( 0 ); | ||
754 | } | ||
755 | |||
756 | void fetchtr_ui( const char *fileName, MetaTranslator *tor, | ||
757 | const char * /* defaultContext */, bool mustExist ) | ||
758 | { | ||
759 | QFile f( fileName ); | ||
760 | if ( !f.open(IO_ReadOnly) ) { | ||
761 | if ( mustExist ) | ||
762 | fprintf( stderr, "lupdate error: cannot open UI file '%s': %s\n", | ||
763 | fileName, strerror(errno) ); | ||
764 | return; | ||
765 | } | ||
766 | |||
767 | QTextStream t( &f ); | ||
768 | QXmlInputSource in( t ); | ||
769 | QXmlSimpleReader reader; | ||
770 | reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE ); | ||
771 | reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE ); | ||
772 | reader.setFeature( "http://trolltech.com/xml/features/report-whitespace" | ||
773 | "-only-CharData", FALSE ); | ||
774 | QXmlDefaultHandler *hand = new UiHandler( tor, fileName ); | ||
775 | reader.setContentHandler( hand ); | ||
776 | reader.setErrorHandler( hand ); | ||
777 | |||
778 | if ( !reader.parse(in) ) | ||
779 | fprintf( stderr, "%s: Parse error in UI file\n", fileName ); | ||
780 | reader.setContentHandler( 0 ); | ||
781 | reader.setErrorHandler( 0 ); | ||
782 | delete hand; | ||
783 | f.close(); | ||
784 | } | ||
diff --git a/development/translation/opie-lupdate/lupdate b/development/translation/opie-lupdate/lupdate new file mode 100755 index 0000000..0e1604d --- a/dev/null +++ b/development/translation/opie-lupdate/lupdate | |||
Binary files differ | |||
diff --git a/development/translation/opie-lupdate/main.cpp b/development/translation/opie-lupdate/main.cpp new file mode 100644 index 0000000..ce65e7a --- a/dev/null +++ b/development/translation/opie-lupdate/main.cpp | |||
@@ -0,0 +1,215 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** Copyright (C) 2003 zecke | ||
4 | ** | ||
5 | ** This file is part of Qt Linguist. | ||
6 | ** | ||
7 | ** This file may be distributed and/or modified under the terms of the | ||
8 | ** GNU General Public License version 2 as published by the Free Software | ||
9 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
10 | ** packaging of this file. | ||
11 | ** | ||
12 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
13 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
14 | ** | ||
15 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
16 | ** | ||
17 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
18 | ** not clear to you. | ||
19 | ** | ||
20 | **********************************************************************/ | ||
21 | |||
22 | #include <metatranslator.h> | ||
23 | #include <proparser.h> | ||
24 | #include <opie.h> | ||
25 | |||
26 | #include <qfile.h> | ||
27 | #include <qfileinfo.h> | ||
28 | #include <qstring.h> | ||
29 | #include <qstringlist.h> | ||
30 | #include <qtextstream.h> | ||
31 | |||
32 | #include <errno.h> | ||
33 | #include <string.h> | ||
34 | |||
35 | // defined in fetchtr.cpp | ||
36 | extern void fetchtr_cpp( const char *fileName, MetaTranslator *tor, | ||
37 | const char *defaultContext, bool mustExist ); | ||
38 | extern void fetchtr_ui( const char *fileName, MetaTranslator *tor, | ||
39 | const char *defaultContext, bool mustExist ); | ||
40 | |||
41 | // defined in merge.cpp | ||
42 | extern void merge( MetaTranslator *tor, const MetaTranslator *virginTor, | ||
43 | bool verbose ); | ||
44 | |||
45 | typedef QValueList<MetaTranslatorMessage> TML; | ||
46 | |||
47 | static const char* LUPDATE_VERSION = "0.1"; | ||
48 | |||
49 | static void printUsage() | ||
50 | { | ||
51 | fprintf( stderr, "Usage:\n" | ||
52 | " opie-lupdate [options] project-file\n" | ||
53 | " opie-lupdate [options] source-files -ts ts-files\n" | ||
54 | "Options:\n" | ||
55 | " -opie The OPIE base dir if not supplied $OPIEDIR will be taken\n" | ||
56 | " -help Display this information and exit\n" | ||
57 | " -noobsolete\n" | ||
58 | " Drop all obsolete strings\n" | ||
59 | " -verbose\n" | ||
60 | " Explain what is being done\n" | ||
61 | " -version\n" | ||
62 | " Display the version of lupdate and exit\n" ); | ||
63 | } | ||
64 | |||
65 | static void updateTsFiles( const MetaTranslator& fetchedTor, | ||
66 | const QString& opiedir, | ||
67 | const QStringList& languages, | ||
68 | const QString& basename, | ||
69 | const QString& codec, | ||
70 | bool noObsolete, bool verbose ) | ||
71 | { | ||
72 | QStringList::ConstIterator it = languages.begin(); | ||
73 | for ( ; it != languages.end(); ++it ) { | ||
74 | QString fileName = opiedir + "/i18n/" + (*it) + "/" + basename; | ||
75 | MetaTranslator tor; | ||
76 | tor.load( fileName ); | ||
77 | if ( !codec.isEmpty() ) | ||
78 | tor.setCodec( codec ); | ||
79 | if ( verbose ) | ||
80 | fprintf( stderr, "Updating '%s'...\n", fileName.latin1() ); | ||
81 | merge( &tor, &fetchedTor, verbose ); | ||
82 | if ( noObsolete ) | ||
83 | tor.stripObsoleteMessages(); | ||
84 | tor.stripEmptyContexts(); | ||
85 | if ( !tor.save(fileName) ) | ||
86 | fprintf( stderr, "lupdate error: Cannot save '%s': %s\n", | ||
87 | fileName.latin1(), strerror(errno) ); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | int main( int argc, char **argv ) | ||
92 | { | ||
93 | QString defaultContext = "@default"; | ||
94 | MetaTranslator fetchedTor; | ||
95 | QCString codec; | ||
96 | QStringList tsFileNames; | ||
97 | QString opiedir; | ||
98 | QString translationBase; | ||
99 | QString target; | ||
100 | |||
101 | bool verbose = FALSE; | ||
102 | bool noObsolete = FALSE; | ||
103 | bool metSomething = FALSE; | ||
104 | bool isLib = FALSE; | ||
105 | int numFiles = 0; | ||
106 | |||
107 | int i; | ||
108 | |||
109 | QStringList languageList = OPIE::self()->languageList(opiedir); | ||
110 | |||
111 | for ( i = 1; i < argc; i++ ) { | ||
112 | if ( qstrcmp(argv[i], "-help") == 0 ) { | ||
113 | printUsage(); | ||
114 | return 0; | ||
115 | } else if ( qstrcmp(argv[i], "-noobsolete") == 0 ) { | ||
116 | noObsolete = TRUE; | ||
117 | continue; | ||
118 | } else if ( qstrcmp(argv[i], "-verbose") == 0 ) { | ||
119 | verbose = TRUE; | ||
120 | continue; | ||
121 | } else if ( qstrcmp(argv[i], "-version") == 0 ) { | ||
122 | fprintf( stderr, "lupdate version %s\n", LUPDATE_VERSION ); | ||
123 | return 0; | ||
124 | } else if ( qstrcmp(argv[i], "-opie") == 0 ) { | ||
125 | if( i+1 < argc ) { | ||
126 | opiedir = argv[i+1]; | ||
127 | languageList = OPIE::self()->languageList(opiedir); | ||
128 | } | ||
129 | i++; // UGLY but we want to skip the next argument | ||
130 | continue; | ||
131 | } | ||
132 | |||
133 | numFiles++; | ||
134 | |||
135 | QString fullText; | ||
136 | |||
137 | QFile f( argv[i] ); | ||
138 | if ( !f.open(IO_ReadOnly) ) { | ||
139 | fprintf( stderr, "lupdate error: Cannot open file '%s': %s\n", | ||
140 | argv[i], strerror(errno) ); | ||
141 | return 1; | ||
142 | } | ||
143 | |||
144 | QTextStream t( &f ); | ||
145 | fullText = t.read(); | ||
146 | f.close(); | ||
147 | |||
148 | fetchedTor = MetaTranslator(); | ||
149 | codec.truncate( 0 ); | ||
150 | tsFileNames.clear(); | ||
151 | isLib = FALSE; | ||
152 | |||
153 | QMap<QString, QString> tagMap = proFileTagMap( fullText ); | ||
154 | QMap<QString, QString>::Iterator it; | ||
155 | |||
156 | for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { | ||
157 | QStringList toks = QStringList::split( ' ', it.data() ); | ||
158 | QStringList::Iterator t; | ||
159 | |||
160 | for ( t = toks.begin(); t != toks.end(); ++t ) { | ||
161 | if ( it.key() == "HEADERS" || it.key() == "SOURCES" ) { | ||
162 | fetchtr_cpp( *t, &fetchedTor, defaultContext, TRUE ); | ||
163 | metSomething = TRUE; | ||
164 | } else if ( it.key() == "INTERFACES" || | ||
165 | it.key() == "FORMS" ) { | ||
166 | fetchtr_ui( *t, &fetchedTor, defaultContext, TRUE ); | ||
167 | fetchtr_cpp( *t + ".h", &fetchedTor, defaultContext, | ||
168 | FALSE ); | ||
169 | metSomething = TRUE; | ||
170 | } else if ( it.key() == "TRANSLATIONS" ) { | ||
171 | // we do not care for that attribute anymore | ||
172 | //tsFileNames.append( *t ); | ||
173 | metSomething = TRUE; | ||
174 | } else if ( it.key() == "CODEC" ) { | ||
175 | codec = (*t).latin1(); | ||
176 | } else if ( it.key() == "TARGET" ) { | ||
177 | target = *t; | ||
178 | metSomething = TRUE; | ||
179 | } else if ( it.key() == "TEMPLATE" ) { | ||
180 | if ( (*t).stripWhiteSpace().lower() == "lib" ) | ||
181 | isLib = true; | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | /** | ||
186 | * We know the $OPIEDIR or have opiedir | ||
187 | * we've a list of languages (de,en,gb,foo,bar) | ||
188 | * we've got the TARGET and we no it's the lib | ||
189 | * so let's do that | ||
190 | * $OPIEDIR/language[i]/ifLibAppendLib$TARGET.ts | ||
191 | */ | ||
192 | qWarning("TARGET %s IsLib:%d", target.latin1(), isLib ); | ||
193 | qWarning("LANGS %s", languageList.join(";").latin1() ); | ||
194 | qWarning("OPIEDIR %s", OPIE::self()->opieDir(opiedir).latin1() ); | ||
195 | if (isLib ) | ||
196 | target.prepend("lib"); | ||
197 | target += ".ts"; | ||
198 | updateTsFiles( fetchedTor, OPIE::self()->opieDir(opiedir), | ||
199 | languageList, target, codec, noObsolete, verbose ); | ||
200 | |||
201 | if ( !metSomething ) { | ||
202 | fprintf( stderr, | ||
203 | "lupdate warning: File '%s' does not look like a" | ||
204 | " project file\n", | ||
205 | argv[i] ); | ||
206 | } | ||
207 | |||
208 | } | ||
209 | |||
210 | if ( numFiles == 0 ) { | ||
211 | printUsage(); | ||
212 | return 1; | ||
213 | } | ||
214 | return 0; | ||
215 | } | ||
diff --git a/development/translation/opie-lupdate/merge.cpp b/development/translation/opie-lupdate/merge.cpp new file mode 100644 index 0000000..a96104e --- a/dev/null +++ b/development/translation/opie-lupdate/merge.cpp | |||
@@ -0,0 +1,115 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | |||
23 | // defined in numberh.cpp | ||
24 | extern void applyNumberHeuristic( MetaTranslator *tor, bool verbose ); | ||
25 | // defined in sametexth.cpp | ||
26 | extern void applySameTextHeuristic( MetaTranslator *tor, bool verbose ); | ||
27 | |||
28 | typedef QValueList<MetaTranslatorMessage> TML; | ||
29 | |||
30 | /* | ||
31 | Merges two MetaTranslator objects into the first one. The first one | ||
32 | is a set of source texts and translations for a previous version of | ||
33 | the internationalized program; the second one is a set of fresh | ||
34 | source texts newly extracted from the source code, without any | ||
35 | translation yet. | ||
36 | */ | ||
37 | |||
38 | void merge( MetaTranslator *tor, const MetaTranslator *virginTor, bool verbose ) | ||
39 | { | ||
40 | int known = 0; | ||
41 | int neww = 0; | ||
42 | int obsoleted = 0; | ||
43 | TML all = tor->messages(); | ||
44 | TML::Iterator it; | ||
45 | |||
46 | /* | ||
47 | The types of all the messages from the vernacular translator | ||
48 | are updated according to the virgin translator. | ||
49 | */ | ||
50 | for ( it = all.begin(); it != all.end(); ++it ) { | ||
51 | MetaTranslatorMessage::Type newType; | ||
52 | MetaTranslatorMessage m = *it; | ||
53 | |||
54 | // skip context comment | ||
55 | if ( !QCString((*it).sourceText()).isEmpty() ) { | ||
56 | if ( !virginTor->contains((*it).context(), (*it).sourceText(), | ||
57 | (*it).comment()) ) { | ||
58 | newType = MetaTranslatorMessage::Obsolete; | ||
59 | if ( m.type() != MetaTranslatorMessage::Obsolete ) | ||
60 | obsoleted++; | ||
61 | } else { | ||
62 | switch ( m.type() ) { | ||
63 | case MetaTranslatorMessage::Finished: | ||
64 | newType = MetaTranslatorMessage::Finished; | ||
65 | known++; | ||
66 | break; | ||
67 | case MetaTranslatorMessage::Unfinished: | ||
68 | newType = MetaTranslatorMessage::Unfinished; | ||
69 | known++; | ||
70 | break; | ||
71 | case MetaTranslatorMessage::Obsolete: | ||
72 | newType = MetaTranslatorMessage::Unfinished; | ||
73 | neww++; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if ( newType != m.type() ) { | ||
78 | m.setType( newType ); | ||
79 | tor->insert( m ); | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | Messages found only in the virgin translator are added to the | ||
86 | vernacular translator. Among these are all the context comments. | ||
87 | */ | ||
88 | all = virginTor->messages(); | ||
89 | |||
90 | for ( it = all.begin(); it != all.end(); ++it ) { | ||
91 | if ( !tor->contains((*it).context(), (*it).sourceText(), | ||
92 | (*it).comment()) ) { | ||
93 | tor->insert( *it ); | ||
94 | if ( !QCString((*it).sourceText()).isEmpty() ) | ||
95 | neww++; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | The same-text heuristic handles cases where a message has an | ||
101 | obsolete counterpart with a different context or comment. | ||
102 | */ | ||
103 | applySameTextHeuristic( tor, verbose ); | ||
104 | |||
105 | /* | ||
106 | The number heuristic handles cases where a message has an | ||
107 | obsolete counterpart with mostly numbers differing in the | ||
108 | source text. | ||
109 | */ | ||
110 | applyNumberHeuristic( tor, verbose ); | ||
111 | |||
112 | if ( verbose ) | ||
113 | fprintf( stderr, " %d known, %d new and %d obsoleted messages\n", known, | ||
114 | neww, obsoleted ); | ||
115 | } | ||
diff --git a/development/translation/opie-lupdate/numberh.cpp b/development/translation/opie-lupdate/numberh.cpp new file mode 100644 index 0000000..f7b7bf8 --- a/dev/null +++ b/development/translation/opie-lupdate/numberh.cpp | |||
@@ -0,0 +1,235 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | |||
23 | #include <qmemarray.h> | ||
24 | #include <qcstring.h> | ||
25 | #include <qmap.h> | ||
26 | #include <qstringlist.h> | ||
27 | |||
28 | #include <ctype.h> | ||
29 | |||
30 | typedef QMap<QCString, MetaTranslatorMessage> TMM; | ||
31 | typedef QValueList<MetaTranslatorMessage> TML; | ||
32 | |||
33 | static bool isDigitFriendly( int c ) | ||
34 | { | ||
35 | return ispunct( c ) || isspace( c ); | ||
36 | } | ||
37 | |||
38 | static int numberLength( const char *s ) | ||
39 | { | ||
40 | int i = 0; | ||
41 | |||
42 | if ( isdigit(s[0]) ) { | ||
43 | do { | ||
44 | i++; | ||
45 | } while ( isdigit(s[i]) || | ||
46 | (isDigitFriendly(s[i]) && | ||
47 | (isdigit(s[i + 1]) || | ||
48 | (isDigitFriendly(s[i + 1]) && isdigit(s[i + 2])))) ); | ||
49 | } | ||
50 | return i; | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | Returns a version of 'key' where all numbers have been replaced by zeroes. If | ||
55 | there were none, returns "". | ||
56 | */ | ||
57 | static QCString zeroKey( const char *key ) | ||
58 | { | ||
59 | QCString zeroed( strlen(key) + 1 ); | ||
60 | char *z = zeroed.data(); | ||
61 | int i = 0, j = 0; | ||
62 | int len; | ||
63 | bool metSomething = FALSE; | ||
64 | |||
65 | while ( key[i] != '\0' ) { | ||
66 | len = numberLength( key + i ); | ||
67 | if ( len > 0 ) { | ||
68 | i += len; | ||
69 | z[j++] = '0'; | ||
70 | metSomething = TRUE; | ||
71 | } else { | ||
72 | z[j++] = key[i++]; | ||
73 | } | ||
74 | } | ||
75 | z[j] = '\0'; | ||
76 | |||
77 | if ( metSomething ) | ||
78 | return zeroed; | ||
79 | else | ||
80 | return ""; | ||
81 | } | ||
82 | |||
83 | static QString translationAttempt( const QString& oldTranslation, | ||
84 | const char *oldSource, | ||
85 | const char *newSource ) | ||
86 | { | ||
87 | int p = zeroKey( oldSource ).contains( '0' ); | ||
88 | int oldSourceLen = qstrlen( oldSource ); | ||
89 | QString attempt; | ||
90 | QStringList oldNumbers; | ||
91 | QStringList newNumbers; | ||
92 | QMemArray<bool> met( p ); | ||
93 | QMemArray<int> matchedYet( p ); | ||
94 | int i, j; | ||
95 | int k = 0, ell, best; | ||
96 | int m, n; | ||
97 | int pass; | ||
98 | |||
99 | /* | ||
100 | This algorithm is hard to follow, so we'll consider an example | ||
101 | all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0" | ||
102 | and newSource is "XeT 3.1". | ||
103 | |||
104 | First, we set up two tables: oldNumbers and newNumbers. In our | ||
105 | example, oldNumber[0] is "3.0" and newNumber[0] is "3.1". | ||
106 | */ | ||
107 | for ( i = 0, j = 0; i < oldSourceLen; i++, j++ ) { | ||
108 | m = numberLength( oldSource + i ); | ||
109 | n = numberLength( newSource + j ); | ||
110 | if ( m > 0 ) { | ||
111 | oldNumbers.append( QCString(oldSource + i, m + 1) ); | ||
112 | newNumbers.append( QCString(newSource + j, n + 1) ); | ||
113 | i += m; | ||
114 | j += n; | ||
115 | met[k] = FALSE; | ||
116 | matchedYet[k] = 0; | ||
117 | k++; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | We now go over the old translation, "XeT 3.0", one letter at a | ||
123 | time, looking for numbers found in oldNumbers. Whenever such a | ||
124 | number is met, it is replaced with its newNumber equivalent. In | ||
125 | our example, the "3.0" of "XeT 3.0" becomes "3.1". | ||
126 | */ | ||
127 | for ( i = 0; i < (int) oldTranslation.length(); i++ ) { | ||
128 | attempt += oldTranslation[i]; | ||
129 | for ( k = 0; k < p; k++ ) { | ||
130 | if ( oldTranslation[i] == oldNumbers[k][matchedYet[k]] ) | ||
131 | matchedYet[k]++; | ||
132 | else | ||
133 | matchedYet[k] = 0; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | Let's find out if the last character ended a match. We make | ||
138 | two passes over the data. In the first pass, we try to | ||
139 | match only numbers that weren't matched yet; if that fails, | ||
140 | the second pass does the trick. This is useful in some | ||
141 | suspicious cases, flagged below. | ||
142 | */ | ||
143 | for ( pass = 0; pass < 2; pass++ ) { | ||
144 | best = p; // an impossible value | ||
145 | for ( k = 0; k < p; k++ ) { | ||
146 | if ( (!met[k] || pass > 0) && | ||
147 | matchedYet[k] == (int) oldNumbers[k].length() && | ||
148 | numberLength(oldTranslation.latin1() + (i + 1) - | ||
149 | matchedYet[k]) == matchedYet[k] ) { | ||
150 | // the longer the better | ||
151 | if ( best == p || matchedYet[k] > matchedYet[best] ) | ||
152 | best = k; | ||
153 | } | ||
154 | } | ||
155 | if ( best != p ) { | ||
156 | attempt.truncate( attempt.length() - matchedYet[best] ); | ||
157 | attempt += newNumbers[best]; | ||
158 | met[best] = TRUE; | ||
159 | for ( k = 0; k < p; k++ ) | ||
160 | matchedYet[k] = 0; | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | We flag two kinds of suspicious cases. They are identified as | ||
168 | such with comments such as "{2000?}" at the end. | ||
169 | |||
170 | Example of the first kind: old source text "TeX 3.0" translated | ||
171 | as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the | ||
172 | new text is. | ||
173 | */ | ||
174 | for ( k = 0; k < p; k++ ) { | ||
175 | if ( !met[k] ) | ||
176 | attempt += QString( " {" ) + newNumbers[k] + QString( "?}" ); | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | Example of the second kind: "1 of 1" translated as "1 af 1", | ||
181 | with new source text "1 of 2", generates "1 af 2 {1 or 2?}" | ||
182 | because it's not clear which of "1 af 2" and "2 af 1" is right. | ||
183 | */ | ||
184 | for ( k = 0; k < p; k++ ) { | ||
185 | for ( ell = 0; ell < p; ell++ ) { | ||
186 | if ( k != ell && oldNumbers[k] == oldNumbers[ell] && | ||
187 | newNumbers[k] < newNumbers[ell] ) | ||
188 | attempt += QString( " {" ) + newNumbers[k] + QString( " or " ) + | ||
189 | newNumbers[ell] + QString( "?}" ); | ||
190 | } | ||
191 | } | ||
192 | return attempt; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | Augments a MetaTranslator with translations easily derived from | ||
197 | similar existing (probably obsolete) translations. | ||
198 | |||
199 | For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1" | ||
200 | has no translation, "XeT 3.1" is added to the translator and is | ||
201 | marked Unfinished. | ||
202 | */ | ||
203 | void applyNumberHeuristic( MetaTranslator *tor, bool verbose ) | ||
204 | { | ||
205 | TMM translated, untranslated; | ||
206 | TMM::Iterator t, u; | ||
207 | TML all = tor->messages(); | ||
208 | TML::Iterator it; | ||
209 | int inserted = 0; | ||
210 | |||
211 | for ( it = all.begin(); it != all.end(); ++it ) { | ||
212 | if ( (*it).type() == MetaTranslatorMessage::Unfinished ) { | ||
213 | if ( (*it).translation().isEmpty() ) | ||
214 | untranslated.insert( zeroKey((*it).sourceText()), *it ); | ||
215 | } else if ( !(*it).translation().isEmpty() ) { | ||
216 | translated.insert( zeroKey((*it).sourceText()), *it ); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | for ( u = untranslated.begin(); u != untranslated.end(); ++u ) { | ||
221 | t = translated.find( u.key() ); | ||
222 | if ( t != translated.end() && !t.key().isEmpty() && | ||
223 | qstrcmp((*t).sourceText(), (*u).sourceText()) != 0 ) { | ||
224 | MetaTranslatorMessage m( *u ); | ||
225 | m.setTranslation( translationAttempt((*t).translation(), | ||
226 | (*t).sourceText(), | ||
227 | (*u).sourceText()) ); | ||
228 | tor->insert( m ); | ||
229 | inserted++; | ||
230 | } | ||
231 | } | ||
232 | if ( verbose && inserted != 0 ) | ||
233 | fprintf( stderr, " number heuristic provided %d translation%s\n", | ||
234 | inserted, inserted == 1 ? "" : "s" ); | ||
235 | } | ||
diff --git a/development/translation/opie-lupdate/opie-lupdate.pro b/development/translation/opie-lupdate/opie-lupdate.pro new file mode 100644 index 0000000..ea51c8b --- a/dev/null +++ b/development/translation/opie-lupdate/opie-lupdate.pro | |||
@@ -0,0 +1,21 @@ | |||
1 | TEMPLATE= app | ||
2 | CONFIG += qt warn_on console | ||
3 | HEADERS = ../shared/metatranslator.h \ | ||
4 | ../shared/proparser.h \ | ||
5 | ../shared/opie.h | ||
6 | |||
7 | SOURCES = fetchtr.cpp \ | ||
8 | main.cpp \ | ||
9 | merge.cpp \ | ||
10 | numberh.cpp \ | ||
11 | sametexth.cpp \ | ||
12 | ../shared/metatranslator.cpp \ | ||
13 | ../shared/proparser.cpp \ | ||
14 | ../shared/opie.cpp | ||
15 | |||
16 | DEFINES += QT_INTERNAL_XML | ||
17 | |||
18 | TARGET = opie-lupdate | ||
19 | INCLUDEPATH+= ../shared | ||
20 | #DESTDIR = | ||
21 | |||
diff --git a/development/translation/opie-lupdate/sametexth.cpp b/development/translation/opie-lupdate/sametexth.cpp new file mode 100644 index 0000000..574cfd5 --- a/dev/null +++ b/development/translation/opie-lupdate/sametexth.cpp | |||
@@ -0,0 +1,84 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <metatranslator.h> | ||
22 | |||
23 | #include <qcstring.h> | ||
24 | #include <qmap.h> | ||
25 | |||
26 | typedef QMap<QCString, MetaTranslatorMessage> TMM; | ||
27 | typedef QValueList<MetaTranslatorMessage> TML; | ||
28 | |||
29 | /* | ||
30 | Augments a MetaTranslator with trivially derived translations. | ||
31 | |||
32 | For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no | ||
33 | matter the context or the comment, "Eingeschaltet:" is added as the | ||
34 | translation of any untranslated "Enabled:" text and is marked Unfinished. | ||
35 | */ | ||
36 | |||
37 | void applySameTextHeuristic( MetaTranslator *tor, bool verbose ) | ||
38 | { | ||
39 | TMM translated; | ||
40 | TMM avoid; | ||
41 | TMM::Iterator t; | ||
42 | TML untranslated; | ||
43 | TML::Iterator u; | ||
44 | TML all = tor->messages(); | ||
45 | TML::Iterator it; | ||
46 | int inserted = 0; | ||
47 | |||
48 | for ( it = all.begin(); it != all.end(); ++it ) { | ||
49 | if ( (*it).type() == MetaTranslatorMessage::Unfinished ) { | ||
50 | if ( (*it).translation().isEmpty() ) | ||
51 | untranslated.append( *it ); | ||
52 | } else { | ||
53 | QCString key = (*it).sourceText(); | ||
54 | t = translated.find( key ); | ||
55 | if ( t != translated.end() ) { | ||
56 | /* | ||
57 | The same source text is translated at least two | ||
58 | different ways. Do nothing then. | ||
59 | */ | ||
60 | if ( (*t).translation() != (*it).translation() ) { | ||
61 | translated.remove( key ); | ||
62 | avoid.insert( key, *it ); | ||
63 | } | ||
64 | } else if ( !avoid.contains(key) && | ||
65 | !(*it).translation().isEmpty() ) { | ||
66 | translated.insert( key, *it ); | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | for ( u = untranslated.begin(); u != untranslated.end(); ++u ) { | ||
72 | QCString key = (*u).sourceText(); | ||
73 | t = translated.find( key ); | ||
74 | if ( t != translated.end() ) { | ||
75 | MetaTranslatorMessage m( *u ); | ||
76 | m.setTranslation( (*t).translation() ); | ||
77 | tor->insert( m ); | ||
78 | inserted++; | ||
79 | } | ||
80 | } | ||
81 | if ( verbose && inserted != 0 ) | ||
82 | fprintf( stderr, " same-text heuristic provided %d translation%s\n", | ||
83 | inserted, inserted == 1 ? "" : "s" ); | ||
84 | } | ||
diff --git a/development/translation/shared/metatranslator.cpp b/development/translation/shared/metatranslator.cpp new file mode 100644 index 0000000..a01e1eb --- a/dev/null +++ b/development/translation/shared/metatranslator.cpp | |||
@@ -0,0 +1,586 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include "metatranslator.h" | ||
22 | |||
23 | #include <qapplication.h> | ||
24 | #include <qcstring.h> | ||
25 | #include <qfile.h> | ||
26 | #include <qmessagebox.h> | ||
27 | #include <qtextcodec.h> | ||
28 | #include <qtextstream.h> | ||
29 | #include <qxml.h> | ||
30 | |||
31 | static bool encodingIsUtf8( const QXmlAttributes& atts ) | ||
32 | { | ||
33 | for ( int i = 0; i < atts.length(); i++ ) { | ||
34 | // utf8="true" is a pre-3.0 syntax | ||
35 | if ( atts.qName(i) == QString("utf8") ) { | ||
36 | return ( atts.value(i) == QString("true") ); | ||
37 | } else if ( atts.qName(i) == QString("encoding") ) { | ||
38 | return ( atts.value(i) == QString("UTF-8") ); | ||
39 | } | ||
40 | } | ||
41 | return FALSE; | ||
42 | } | ||
43 | |||
44 | class TsHandler : public QXmlDefaultHandler | ||
45 | { | ||
46 | public: | ||
47 | TsHandler( MetaTranslator *translator ) | ||
48 | : tor( translator ), type( MetaTranslatorMessage::Finished ), | ||
49 | inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ), | ||
50 | messageIsUtf8( FALSE ) { } | ||
51 | |||
52 | virtual bool startElement( const QString& namespaceURI, | ||
53 | const QString& localName, const QString& qName, | ||
54 | const QXmlAttributes& atts ); | ||
55 | virtual bool endElement( const QString& namespaceURI, | ||
56 | const QString& localName, const QString& qName ); | ||
57 | virtual bool characters( const QString& ch ); | ||
58 | virtual bool fatalError( const QXmlParseException& exception ); | ||
59 | |||
60 | private: | ||
61 | MetaTranslator *tor; | ||
62 | MetaTranslatorMessage::Type type; | ||
63 | bool inMessage; | ||
64 | QString context; | ||
65 | QString source; | ||
66 | QString comment; | ||
67 | QString translation; | ||
68 | |||
69 | QString accum; | ||
70 | int ferrorCount; | ||
71 | bool contextIsUtf8; | ||
72 | bool messageIsUtf8; | ||
73 | }; | ||
74 | |||
75 | bool TsHandler::startElement( const QString& /* namespaceURI */, | ||
76 | const QString& /* localName */, | ||
77 | const QString& qName, | ||
78 | const QXmlAttributes& atts ) | ||
79 | { | ||
80 | if ( qName == QString("byte") ) { | ||
81 | for ( int i = 0; i < atts.length(); i++ ) { | ||
82 | if ( atts.qName(i) == QString("value") ) { | ||
83 | QString value = atts.value( i ); | ||
84 | int base = 10; | ||
85 | if ( value.startsWith("x") ) { | ||
86 | base = 16; | ||
87 | value = value.mid( 1 ); | ||
88 | } | ||
89 | int n = value.toUInt( 0, base ); | ||
90 | if ( n != 0 ) | ||
91 | accum += QChar( n ); | ||
92 | } | ||
93 | } | ||
94 | } else { | ||
95 | if ( qName == QString("context") ) { | ||
96 | context.truncate( 0 ); | ||
97 | source.truncate( 0 ); | ||
98 | comment.truncate( 0 ); | ||
99 | translation.truncate( 0 ); | ||
100 | contextIsUtf8 = encodingIsUtf8( atts ); | ||
101 | } else if ( qName == QString("message") ) { | ||
102 | inMessage = TRUE; | ||
103 | type = MetaTranslatorMessage::Finished; | ||
104 | source.truncate( 0 ); | ||
105 | comment.truncate( 0 ); | ||
106 | translation.truncate( 0 ); | ||
107 | messageIsUtf8 = encodingIsUtf8( atts ); | ||
108 | } else if ( qName == QString("translation") ) { | ||
109 | for ( int i = 0; i < atts.length(); i++ ) { | ||
110 | if ( atts.qName(i) == QString("type") ) { | ||
111 | if ( atts.value(i) == QString("unfinished") ) | ||
112 | type = MetaTranslatorMessage::Unfinished; | ||
113 | else if ( atts.value(i) == QString("obsolete") ) | ||
114 | type = MetaTranslatorMessage::Obsolete; | ||
115 | else | ||
116 | type = MetaTranslatorMessage::Finished; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | accum.truncate( 0 ); | ||
121 | } | ||
122 | return TRUE; | ||
123 | } | ||
124 | |||
125 | bool TsHandler::endElement( const QString& /* namespaceURI */, | ||
126 | const QString& /* localName */, | ||
127 | const QString& qName ) | ||
128 | { | ||
129 | if ( qName == QString("codec") || qName == QString("defaultcodec") ) { | ||
130 | // "codec" is a pre-3.0 syntax | ||
131 | tor->setCodec( accum ); | ||
132 | } else if ( qName == QString("name") ) { | ||
133 | context = accum; | ||
134 | } else if ( qName == QString("source") ) { | ||
135 | source = accum; | ||
136 | } else if ( qName == QString("comment") ) { | ||
137 | if ( inMessage ) { | ||
138 | comment = accum; | ||
139 | } else { | ||
140 | if ( contextIsUtf8 ) | ||
141 | tor->insert( MetaTranslatorMessage(context.utf8(), "", | ||
142 | accum.utf8(), QString::null, TRUE, | ||
143 | MetaTranslatorMessage::Unfinished) ); | ||
144 | else | ||
145 | tor->insert( MetaTranslatorMessage(context.ascii(), "", | ||
146 | accum.ascii(), QString::null, FALSE, | ||
147 | MetaTranslatorMessage::Unfinished) ); | ||
148 | } | ||
149 | } else if ( qName == QString("translation") ) { | ||
150 | translation = accum; | ||
151 | } else if ( qName == QString("message") ) { | ||
152 | if ( messageIsUtf8 ) | ||
153 | tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), | ||
154 | comment.utf8(), translation, | ||
155 | TRUE, type) ); | ||
156 | else | ||
157 | tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(), | ||
158 | comment.ascii(), translation, | ||
159 | FALSE, type) ); | ||
160 | inMessage = FALSE; | ||
161 | } | ||
162 | return TRUE; | ||
163 | } | ||
164 | |||
165 | bool TsHandler::characters( const QString& ch ) | ||
166 | { | ||
167 | QString t = ch; | ||
168 | t.replace( "\r", "" ); | ||
169 | accum += t; | ||
170 | return TRUE; | ||
171 | } | ||
172 | |||
173 | bool TsHandler::fatalError( const QXmlParseException& exception ) | ||
174 | { | ||
175 | if ( ferrorCount++ == 0 ) { | ||
176 | QString msg; | ||
177 | msg.sprintf( "Parse error at line %d, column %d (%s).", | ||
178 | exception.lineNumber(), exception.columnNumber(), | ||
179 | exception.message().latin1() ); | ||
180 | if ( qApp == 0 ) | ||
181 | fprintf( stderr, "XML error: %s\n", msg.latin1() ); | ||
182 | else | ||
183 | QMessageBox::information( qApp->mainWidget(), | ||
184 | QObject::tr("Qt Linguist"), msg ); | ||
185 | } | ||
186 | return FALSE; | ||
187 | } | ||
188 | |||
189 | static QString numericEntity( int ch ) | ||
190 | { | ||
191 | return QString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" ) | ||
192 | .arg( ch, 0, 16 ); | ||
193 | } | ||
194 | |||
195 | static QString protect( const QCString& str ) | ||
196 | { | ||
197 | QString result; | ||
198 | int len = (int) str.length(); | ||
199 | for ( int k = 0; k < len; k++ ) { | ||
200 | switch( str[k] ) { | ||
201 | case '\"': | ||
202 | result += QString( """ ); | ||
203 | break; | ||
204 | case '&': | ||
205 | result += QString( "&" ); | ||
206 | break; | ||
207 | case '>': | ||
208 | result += QString( ">" ); | ||
209 | break; | ||
210 | case '<': | ||
211 | result += QString( "<" ); | ||
212 | break; | ||
213 | case '\'': | ||
214 | result += QString( "'" ); | ||
215 | break; | ||
216 | default: | ||
217 | if ( (uchar) str[k] < 0x20 && str[k] != '\n' ) | ||
218 | result += numericEntity( (uchar) str[k] ); | ||
219 | else | ||
220 | result += str[k]; | ||
221 | } | ||
222 | } | ||
223 | return result; | ||
224 | } | ||
225 | |||
226 | static QString evilBytes( const QCString& str, bool utf8 ) | ||
227 | { | ||
228 | if ( utf8 ) { | ||
229 | return protect( str ); | ||
230 | } else { | ||
231 | QString result; | ||
232 | QCString t = protect( str ).latin1(); | ||
233 | int len = (int) t.length(); | ||
234 | for ( int k = 0; k < len; k++ ) { | ||
235 | if ( (uchar) t[k] >= 0x7f ) | ||
236 | result += numericEntity( (uchar) t[k] ); | ||
237 | else | ||
238 | result += QChar( t[k] ); | ||
239 | } | ||
240 | return result; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | MetaTranslatorMessage::MetaTranslatorMessage() | ||
245 | : utfeight( FALSE ), ty( Unfinished ) | ||
246 | { | ||
247 | } | ||
248 | |||
249 | MetaTranslatorMessage::MetaTranslatorMessage( const char *context, | ||
250 | const char *sourceText, | ||
251 | const char *comment, | ||
252 | const QString& translation, | ||
253 | bool utf8, Type type ) | ||
254 | : QTranslatorMessage( context, sourceText, comment, translation ), | ||
255 | utfeight( FALSE ), ty( type ) | ||
256 | { | ||
257 | /* | ||
258 | Don't use UTF-8 if it makes no difference. UTF-8 should be | ||
259 | reserved for the real problematic case: non-ASCII (possibly | ||
260 | non-Latin-1) characters in .ui files. | ||
261 | */ | ||
262 | if ( utf8 ) { | ||
263 | if ( sourceText != 0 ) { | ||
264 | int i = 0; | ||
265 | while ( sourceText[i] != '\0' ) { | ||
266 | if ( (uchar) sourceText[i] >= 0x80 ) { | ||
267 | utfeight = TRUE; | ||
268 | break; | ||
269 | } | ||
270 | i++; | ||
271 | } | ||
272 | } | ||
273 | if ( !utfeight && comment != 0 ) { | ||
274 | int i = 0; | ||
275 | while ( comment[i] != '\0' ) { | ||
276 | if ( (uchar) comment[i] >= 0x80 ) { | ||
277 | utfeight = TRUE; | ||
278 | break; | ||
279 | } | ||
280 | i++; | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m ) | ||
287 | : QTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty ) | ||
288 | { | ||
289 | } | ||
290 | |||
291 | MetaTranslatorMessage& MetaTranslatorMessage::operator=( | ||
292 | const MetaTranslatorMessage& m ) | ||
293 | { | ||
294 | QTranslatorMessage::operator=( m ); | ||
295 | utfeight = m.utfeight; | ||
296 | ty = m.ty; | ||
297 | return *this; | ||
298 | } | ||
299 | |||
300 | bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const | ||
301 | { | ||
302 | return qstrcmp( context(), m.context() ) == 0 && | ||
303 | qstrcmp( sourceText(), m.sourceText() ) == 0 && | ||
304 | qstrcmp( comment(), m.comment() ) == 0; | ||
305 | } | ||
306 | |||
307 | bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const | ||
308 | { | ||
309 | int delta = qstrcmp( context(), m.context() ); | ||
310 | if ( delta == 0 ) | ||
311 | delta = qstrcmp( sourceText(), m.sourceText() ); | ||
312 | if ( delta == 0 ) | ||
313 | delta = qstrcmp( comment(), m.comment() ); | ||
314 | return delta < 0; | ||
315 | } | ||
316 | |||
317 | MetaTranslator::MetaTranslator() | ||
318 | : codecName( "ISO-8859-1" ), codec( 0 ) | ||
319 | { | ||
320 | } | ||
321 | |||
322 | MetaTranslator::MetaTranslator( const MetaTranslator& tor ) | ||
323 | : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec ) | ||
324 | { | ||
325 | |||
326 | } | ||
327 | |||
328 | MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor ) | ||
329 | { | ||
330 | mm = tor.mm; | ||
331 | codecName = tor.codecName; | ||
332 | codec = tor.codec; | ||
333 | return *this; | ||
334 | } | ||
335 | |||
336 | bool MetaTranslator::load( const QString& filename ) | ||
337 | { | ||
338 | mm.clear(); | ||
339 | |||
340 | QFile f( filename ); | ||
341 | if ( !f.open(IO_ReadOnly) ) | ||
342 | return FALSE; | ||
343 | |||
344 | QTextStream t( &f ); | ||
345 | QXmlInputSource in( t ); | ||
346 | QXmlSimpleReader reader; | ||
347 | // don't click on these! | ||
348 | reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE ); | ||
349 | reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE ); | ||
350 | reader.setFeature( "http://trolltech.com/xml/features/report-whitespace" | ||
351 | "-only-CharData", FALSE ); | ||
352 | QXmlDefaultHandler *hand = new TsHandler( this ); | ||
353 | reader.setContentHandler( hand ); | ||
354 | reader.setErrorHandler( hand ); | ||
355 | |||
356 | bool ok = reader.parse( in ); | ||
357 | reader.setContentHandler( 0 ); | ||
358 | reader.setErrorHandler( 0 ); | ||
359 | delete hand; | ||
360 | f.close(); | ||
361 | if ( !ok ) | ||
362 | mm.clear(); | ||
363 | return ok; | ||
364 | } | ||
365 | |||
366 | bool MetaTranslator::save( const QString& filename ) const | ||
367 | { | ||
368 | QFile f( filename ); | ||
369 | if ( !f.open(IO_WriteOnly) ) | ||
370 | return FALSE; | ||
371 | |||
372 | QTextStream t( &f ); | ||
373 | t.setCodec( QTextCodec::codecForName("ISO-8859-1") ); | ||
374 | |||
375 | t << "<!DOCTYPE TS><TS>\n"; | ||
376 | if ( codecName != "ISO-8859-1" ) | ||
377 | t << "<defaultcodec>" << codecName << "</defaultcodec>\n"; | ||
378 | TMM::ConstIterator m = mm.begin(); | ||
379 | while ( m != mm.end() ) { | ||
380 | TMMInv inv; | ||
381 | TMMInv::Iterator i; | ||
382 | bool contextIsUtf8 = m.key().utf8(); | ||
383 | QCString context = m.key().context(); | ||
384 | QCString comment = ""; | ||
385 | |||
386 | do { | ||
387 | if ( QCString(m.key().sourceText()).isEmpty() ) { | ||
388 | if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { | ||
389 | contextIsUtf8 = m.key().utf8(); | ||
390 | comment = QCString( m.key().comment() ); | ||
391 | } | ||
392 | } else { | ||
393 | inv.insert( *m, m.key() ); | ||
394 | } | ||
395 | } while ( ++m != mm.end() && QCString(m.key().context()) == context ); | ||
396 | |||
397 | t << "<context"; | ||
398 | if ( contextIsUtf8 ) | ||
399 | t << " encoding=\"UTF-8\""; | ||
400 | t << ">\n"; | ||
401 | t << " <name>" << evilBytes( context, contextIsUtf8 ) | ||
402 | << "</name>\n"; | ||
403 | if ( !comment.isEmpty() ) | ||
404 | t << " <comment>" << evilBytes( comment, contextIsUtf8 ) | ||
405 | << "</comment>\n"; | ||
406 | |||
407 | for ( i = inv.begin(); i != inv.end(); ++i ) { | ||
408 | // no need for such noise | ||
409 | if ( (*i).type() == MetaTranslatorMessage::Obsolete && | ||
410 | (*i).translation().isEmpty() ) | ||
411 | continue; | ||
412 | |||
413 | t << " <message"; | ||
414 | if ( (*i).utf8() ) | ||
415 | t << " encoding=\"UTF-8\""; | ||
416 | t << ">\n" | ||
417 | << " <source>" << evilBytes( (*i).sourceText(), | ||
418 | (*i).utf8() ) | ||
419 | << "</source>\n"; | ||
420 | if ( !QCString((*i).comment()).isEmpty() ) | ||
421 | t << " <comment>" << evilBytes( (*i).comment(), | ||
422 | (*i).utf8() ) | ||
423 | << "</comment>\n"; | ||
424 | t << " <translation"; | ||
425 | if ( (*i).type() == MetaTranslatorMessage::Unfinished ) | ||
426 | t << " type=\"unfinished\""; | ||
427 | else if ( (*i).type() == MetaTranslatorMessage::Obsolete ) | ||
428 | t << " type=\"obsolete\""; | ||
429 | t << ">" << protect( (*i).translation().utf8() ) | ||
430 | << "</translation>\n"; | ||
431 | t << " </message>\n"; | ||
432 | } | ||
433 | t << "</context>\n"; | ||
434 | } | ||
435 | t << "</TS>\n"; | ||
436 | f.close(); | ||
437 | return TRUE; | ||
438 | } | ||
439 | |||
440 | bool MetaTranslator::release( const QString& filename, bool verbose ) const | ||
441 | { | ||
442 | QTranslator tor( 0 ); | ||
443 | int finished = 0; | ||
444 | int unfinished = 0; | ||
445 | int untranslated = 0; | ||
446 | TMM::ConstIterator m; | ||
447 | |||
448 | for ( m = mm.begin(); m != mm.end(); ++m ) { | ||
449 | if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { | ||
450 | if ( m.key().translation().isEmpty() ) { | ||
451 | untranslated++; | ||
452 | } else { | ||
453 | if ( m.key().type() == MetaTranslatorMessage::Unfinished ) | ||
454 | unfinished++; | ||
455 | else | ||
456 | finished++; | ||
457 | |||
458 | QCString context = m.key().context(); | ||
459 | QCString sourceText = m.key().sourceText(); | ||
460 | QCString comment = m.key().comment(); | ||
461 | QString translation = m.key().translation(); | ||
462 | |||
463 | /* | ||
464 | Drop the comment in (context, sourceText, comment), | ||
465 | unless (context, sourceText, "") already exists, or | ||
466 | unless we already dropped the comment of (context, | ||
467 | sourceText, comment0). | ||
468 | */ | ||
469 | if ( comment.isEmpty() | ||
470 | || contains(context, sourceText, "") | ||
471 | || !tor.findMessage(context, sourceText, "").translation() | ||
472 | .isNull() ) { | ||
473 | tor.insert( m.key() ); | ||
474 | } else { | ||
475 | tor.insert( QTranslatorMessage(context, sourceText, "", | ||
476 | translation) ); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | |||
482 | bool saved = tor.save( filename, QTranslator::Stripped ); | ||
483 | if ( saved && verbose ) | ||
484 | fprintf( stderr, | ||
485 | " %d finished, %d unfinished and %d untranslated messages\n", | ||
486 | finished, unfinished, untranslated ); | ||
487 | |||
488 | return saved; | ||
489 | } | ||
490 | |||
491 | bool MetaTranslator::contains( const char *context, const char *sourceText, | ||
492 | const char *comment ) const | ||
493 | { | ||
494 | return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) != | ||
495 | mm.end(); | ||
496 | } | ||
497 | |||
498 | void MetaTranslator::insert( const MetaTranslatorMessage& m ) | ||
499 | { | ||
500 | int pos = mm.count(); | ||
501 | TMM::Iterator n = mm.find( m ); | ||
502 | if ( n != mm.end() ) | ||
503 | pos = *n; | ||
504 | mm.replace( m, pos ); | ||
505 | } | ||
506 | |||
507 | void MetaTranslator::stripObsoleteMessages() | ||
508 | { | ||
509 | TMM newmm; | ||
510 | |||
511 | TMM::Iterator m = mm.begin(); | ||
512 | while ( m != mm.end() ) { | ||
513 | if ( m.key().type() != MetaTranslatorMessage::Obsolete ) | ||
514 | newmm.insert( m.key(), *m ); | ||
515 | ++m; | ||
516 | } | ||
517 | mm = newmm; | ||
518 | } | ||
519 | |||
520 | void MetaTranslator::stripEmptyContexts() | ||
521 | { | ||
522 | TMM newmm; | ||
523 | |||
524 | TMM::Iterator m = mm.begin(); | ||
525 | while ( m != mm.end() ) { | ||
526 | if ( QCString(m.key().sourceText()).isEmpty() ) { | ||
527 | TMM::Iterator n = m; | ||
528 | ++n; | ||
529 | // the context comment is followed by other messages | ||
530 | if ( n != newmm.end() && | ||
531 | qstrcmp(m.key().context(), n.key().context()) == 0 ) | ||
532 | newmm.insert( m.key(), *m ); | ||
533 | } else { | ||
534 | newmm.insert( m.key(), *m ); | ||
535 | } | ||
536 | ++m; | ||
537 | } | ||
538 | mm = newmm; | ||
539 | } | ||
540 | |||
541 | void MetaTranslator::setCodec( const char *name ) | ||
542 | { | ||
543 | const int latin1 = 4; | ||
544 | |||
545 | codecName = name; | ||
546 | codec = QTextCodec::codecForName( name ); | ||
547 | if ( codec == 0 || codec->mibEnum() == latin1 ) | ||
548 | codec = 0; | ||
549 | } | ||
550 | |||
551 | QString MetaTranslator::toUnicode( const char *str, bool utf8 ) const | ||
552 | { | ||
553 | if ( utf8 ) | ||
554 | return QString::fromUtf8( str ); | ||
555 | else if ( codec == 0 ) | ||
556 | return QString( str ); | ||
557 | else | ||
558 | return codec->toUnicode( str ); | ||
559 | } | ||
560 | |||
561 | QValueList<MetaTranslatorMessage> MetaTranslator::messages() const | ||
562 | { | ||
563 | int n = mm.count(); | ||
564 | TMM::ConstIterator *t = new TMM::ConstIterator[n + 1]; | ||
565 | TMM::ConstIterator m; | ||
566 | for ( m = mm.begin(); m != mm.end(); ++m ) | ||
567 | t[*m] = m; | ||
568 | |||
569 | QValueList<MetaTranslatorMessage> val; | ||
570 | for ( int i = 0; i < n; i++ ) | ||
571 | val.append( t[i].key() ); | ||
572 | |||
573 | delete[] t; | ||
574 | return val; | ||
575 | } | ||
576 | |||
577 | QValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const | ||
578 | { | ||
579 | QValueList<MetaTranslatorMessage> val; | ||
580 | TMM::ConstIterator m; | ||
581 | for ( m = mm.begin(); m != mm.end(); ++m ) { | ||
582 | if ( m.key().type() == MetaTranslatorMessage::Finished ) | ||
583 | val.append( m.key() ); | ||
584 | } | ||
585 | return val; | ||
586 | } | ||
diff --git a/development/translation/shared/metatranslator.h b/development/translation/shared/metatranslator.h new file mode 100644 index 0000000..d35b202 --- a/dev/null +++ b/development/translation/shared/metatranslator.h | |||
@@ -0,0 +1,99 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #ifndef METATRANSLATOR_H | ||
22 | #define METATRANSLATOR_H | ||
23 | |||
24 | #include <qmap.h> | ||
25 | #include <qstring.h> | ||
26 | #include <qtranslator.h> | ||
27 | #include <qvaluelist.h> | ||
28 | |||
29 | class QTextCodec; | ||
30 | |||
31 | class MetaTranslatorMessage : public QTranslatorMessage | ||
32 | { | ||
33 | public: | ||
34 | enum Type { Unfinished, Finished, Obsolete }; | ||
35 | |||
36 | MetaTranslatorMessage(); | ||
37 | MetaTranslatorMessage( const char *context, const char *sourceText, | ||
38 | const char *comment, | ||
39 | const QString& translation = QString::null, | ||
40 | bool utf8 = FALSE, Type type = Unfinished ); | ||
41 | MetaTranslatorMessage( const MetaTranslatorMessage& m ); | ||
42 | |||
43 | MetaTranslatorMessage& operator=( const MetaTranslatorMessage& m ); | ||
44 | |||
45 | void setType( Type nt ) { ty = nt; } | ||
46 | Type type() const { return ty; } | ||
47 | bool utf8() const { return utfeight; } | ||
48 | |||
49 | bool operator==( const MetaTranslatorMessage& m ) const; | ||
50 | bool operator!=( const MetaTranslatorMessage& m ) const | ||
51 | { return !operator==( m ); } | ||
52 | bool operator<( const MetaTranslatorMessage& m ) const; | ||
53 | bool operator<=( const MetaTranslatorMessage& m ) | ||
54 | { return !operator>( m ); } | ||
55 | bool operator>( const MetaTranslatorMessage& m ) const | ||
56 | { return this->operator<( m ); } | ||
57 | bool operator>=( const MetaTranslatorMessage& m ) const | ||
58 | { return !operator<( m ); } | ||
59 | |||
60 | private: | ||
61 | bool utfeight; | ||
62 | Type ty; | ||
63 | }; | ||
64 | |||
65 | class MetaTranslator | ||
66 | { | ||
67 | public: | ||
68 | MetaTranslator(); | ||
69 | MetaTranslator( const MetaTranslator& tor ); | ||
70 | |||
71 | MetaTranslator& operator=( const MetaTranslator& tor ); | ||
72 | |||
73 | bool load( const QString& filename ); | ||
74 | bool save( const QString& filename ) const; | ||
75 | bool release( const QString& filename, bool verbose = FALSE ) const; | ||
76 | |||
77 | bool contains( const char *context, const char *sourceText, | ||
78 | const char *comment ) const; | ||
79 | void insert( const MetaTranslatorMessage& m ); | ||
80 | |||
81 | void stripObsoleteMessages(); | ||
82 | void stripEmptyContexts(); | ||
83 | |||
84 | void setCodec( const char *name ); | ||
85 | QString toUnicode( const char *str, bool utf8 ) const; | ||
86 | |||
87 | QValueList<MetaTranslatorMessage> messages() const; | ||
88 | QValueList<MetaTranslatorMessage> translatedMessages() const; | ||
89 | |||
90 | private: | ||
91 | typedef QMap<MetaTranslatorMessage, int> TMM; | ||
92 | typedef QMap<int, MetaTranslatorMessage> TMMInv; | ||
93 | |||
94 | TMM mm; | ||
95 | QCString codecName; | ||
96 | QTextCodec *codec; | ||
97 | }; | ||
98 | |||
99 | #endif | ||
diff --git a/development/translation/shared/opie.cpp b/development/translation/shared/opie.cpp new file mode 100644 index 0000000..c5c72d1 --- a/dev/null +++ b/development/translation/shared/opie.cpp | |||
@@ -0,0 +1,40 @@ | |||
1 | #include <stdlib.h> | ||
2 | |||
3 | #include <qdir.h> | ||
4 | |||
5 | #include "opie.h" | ||
6 | |||
7 | OPIE* OPIE::m_self = 0; | ||
8 | |||
9 | |||
10 | OPIE::OPIE() { | ||
11 | } | ||
12 | OPIE::~OPIE() { | ||
13 | } | ||
14 | OPIE* OPIE::self() { | ||
15 | if (!m_self ) m_self = new OPIE; | ||
16 | return m_self; | ||
17 | } | ||
18 | QStringList OPIE::languageList( const QString& _opieDir )const { | ||
19 | QString opieDi = opieDir( _opieDir ); | ||
20 | |||
21 | QStringList langs; | ||
22 | QDir dir( opieDi + "/i18n/"); | ||
23 | if (!dir.exists() ) return langs; | ||
24 | langs = dir.entryList( QDir::Dirs ); | ||
25 | |||
26 | langs.remove("CVS"); // hey this no language | ||
27 | langs.remove("unmaintained"); // remove this one too | ||
28 | langs.remove("."); | ||
29 | langs.remove(".."); | ||
30 | |||
31 | |||
32 | |||
33 | return langs; | ||
34 | } | ||
35 | QString OPIE::opieDir( const QString& _opieDir ) const{ | ||
36 | if (!_opieDir.isEmpty() ) return _opieDir; | ||
37 | char* dir = ::getenv("OPIEDIR"); | ||
38 | if (!dir ) return QString::null; | ||
39 | return QString::fromLatin1(dir); | ||
40 | } | ||
diff --git a/development/translation/shared/opie.h b/development/translation/shared/opie.h new file mode 100644 index 0000000..4646bb0 --- a/dev/null +++ b/development/translation/shared/opie.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef OPIE_H | ||
2 | #define OPIE_H | ||
3 | |||
4 | #include <qstring.h> | ||
5 | #include <qstringlist.h> | ||
6 | |||
7 | class OPIE { | ||
8 | public: | ||
9 | static OPIE* self(); | ||
10 | /** get the list of languages */ | ||
11 | QStringList languageList(const QString& opiedir = QString::null)const; | ||
12 | QString opieDir(const QString& opieDir)const; | ||
13 | |||
14 | private: | ||
15 | OPIE(); | ||
16 | ~OPIE(); | ||
17 | static OPIE* m_self; | ||
18 | |||
19 | }; | ||
20 | |||
21 | #endif | ||
diff --git a/development/translation/shared/proparser.cpp b/development/translation/shared/proparser.cpp new file mode 100644 index 0000000..21d2f86 --- a/dev/null +++ b/development/translation/shared/proparser.cpp | |||
@@ -0,0 +1,87 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include "proparser.h" | ||
22 | |||
23 | #include <qregexp.h> | ||
24 | #include <qstringlist.h> | ||
25 | |||
26 | QMap<QString, QString> proFileTagMap( const QString& text ) | ||
27 | { | ||
28 | QString t = text; | ||
29 | |||
30 | /* | ||
31 | Strip comments, merge lines ending with backslash, add | ||
32 | spaces around '=' and '+=', replace '\n' with ';', and | ||
33 | simplify white spaces. | ||
34 | */ | ||
35 | t.replace( QRegExp(QString("#[^\n]$")), QString(" ") ); | ||
36 | t.replace( QRegExp(QString("\\\\\\s*\n")), QString(" ") ); | ||
37 | t.replace( "=", QString(" = ") ); | ||
38 | t.replace( "+ =", QString(" += ") ); | ||
39 | t.replace( "\n", QString(";") ); | ||
40 | t = t.simplifyWhiteSpace(); | ||
41 | |||
42 | QMap<QString, QString> tagMap; | ||
43 | |||
44 | QStringList lines = QStringList::split( QChar(';'), t ); | ||
45 | QStringList::Iterator line; | ||
46 | for ( line = lines.begin(); line != lines.end(); ++line ) { | ||
47 | QStringList toks = QStringList::split( QChar(' '), *line ); | ||
48 | |||
49 | if ( toks.count() >= 3 && | ||
50 | (toks[1] == QString("=") || toks[1] == QString("+=")) ) { | ||
51 | QString tag = toks.first(); | ||
52 | int k = tag.findRev( QChar(':') ); // as in 'unix:' | ||
53 | if ( k != -1 ) | ||
54 | tag = tag.mid( k + 1 ); | ||
55 | toks.remove( toks.begin() ); | ||
56 | |||
57 | QString action = toks.first(); | ||
58 | toks.remove( toks.begin() ); | ||
59 | |||
60 | if ( tagMap.contains(tag) ) { | ||
61 | if ( action == QString("=") ) | ||
62 | tagMap.replace( tag, toks.join(QChar(' ')) ); | ||
63 | else | ||
64 | tagMap[tag] += QChar( ' ' ) + toks.join( QChar(' ') ); | ||
65 | } else { | ||
66 | tagMap[tag] = toks.join( QChar(' ') ); | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | QRegExp var( "\\$\\$[a-zA-Z0-9_]+" ); | ||
72 | QMap<QString, QString>::Iterator it; | ||
73 | for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { | ||
74 | int i = 0; | ||
75 | |||
76 | while ( (i = var.search(it.data(), i)) != -1 ) { | ||
77 | int len = var.matchedLength(); | ||
78 | QString invocation = (*it).mid( i + 2, len - 2 ); | ||
79 | QString after; | ||
80 | if ( tagMap.contains(invocation) ) | ||
81 | after = tagMap[invocation]; | ||
82 | (*it).replace( i, len, after ); | ||
83 | i += after.length(); | ||
84 | } | ||
85 | } | ||
86 | return tagMap; | ||
87 | } | ||
diff --git a/development/translation/shared/proparser.h b/development/translation/shared/proparser.h new file mode 100644 index 0000000..6a61d90 --- a/dev/null +++ b/development/translation/shared/proparser.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qt Linguist. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #ifndef PROPARSER_H | ||
22 | #define PROPARSER_H | ||
23 | |||
24 | #include <qmap.h> | ||
25 | #include <qstring.h> | ||
26 | |||
27 | QMap<QString, QString> proFileTagMap( const QString& text ); | ||
28 | |||
29 | #endif | ||