summaryrefslogtreecommitdiffstats
path: root/pytqlupdate3
diff options
context:
space:
mode:
Diffstat (limited to 'pytqlupdate3')
-rw-r--r--pytqlupdate3/LICENSE.GPL280
-rw-r--r--pytqlupdate3/fetchtr.cpp455
-rw-r--r--pytqlupdate3/main.cpp155
-rw-r--r--pytqlupdate3/merge.cpp110
-rw-r--r--pytqlupdate3/metatranslator.cpp557
-rw-r--r--pytqlupdate3/metatranslator.h95
-rw-r--r--pytqlupdate3/numberh.cpp230
-rw-r--r--pytqlupdate3/proparser.cpp78
-rw-r--r--pytqlupdate3/proparser.h25
-rw-r--r--pytqlupdate3/pytqlupdate-prof.sbf24
-rw-r--r--pytqlupdate3/pytqlupdate.pro.in29
-rw-r--r--pytqlupdate3/pytqlupdate.sbf24
-rw-r--r--pytqlupdate3/sametexth.cpp78
13 files changed, 2140 insertions, 0 deletions
diff --git a/pytqlupdate3/LICENSE.GPL b/pytqlupdate3/LICENSE.GPL
new file mode 100644
index 0000000..c7aea18
--- /dev/null
+++ b/pytqlupdate3/LICENSE.GPL
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/pytqlupdate3/fetchtr.cpp b/pytqlupdate3/fetchtr.cpp
new file mode 100644
index 0000000..4fe5bc5
--- /dev/null
+++ b/pytqlupdate3/fetchtr.cpp
@@ -0,0 +1,455 @@
+/**********************************************************************
+** Copyright (C) 2002 Detlev Offenbach <detlev@die-offenbachs.de>
+**
+** This is a modified version of lupdate. The original is part of TQt-Linguist.
+** The copyright of the original file can be found below.
+**
+** This version is modified to handle python sources.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** fetchtr.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqfile.h>
+#include <tqregexp.h>
+#include <tqstring.h>
+#include <tqtextstream.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <metatranslator.h>
+#include <stdio.h>
+#include <string.h>
+/*#include <tqxml.h>*/
+
+
+static const char MagicComment[] = "TRANSLATOR ";
+
+static TQMap<TQCString, int> needs_Q_OBJECT;
+static TQMap<TQCString, int> lacks_Q_OBJECT;
+
+/*
+ The first part of this source file is the python tokenizer. We skip
+ most of python; the only tokens that interest us are defined here.
+*/
+
+enum { Tok_Eof, Tok_class, Tok_return, Tok_tr,
+ Tok_trUtf8, Tok_translate, Tok_Ident,
+ Tok_Comment, Tok_Dot, Tok_String,
+ Tok_LeftParen, Tok_RightParen,
+ Tok_Comma};
+
+/*
+ The tokenizer maintains the following global variables. The names
+ should be self-explanatory.
+*/
+static TQCString yyFileName;
+static int yyCh;
+static char yyIdent[128];
+static size_t yyIdentLen;
+static char yyComment[65536];
+static size_t yyCommentLen;
+static char yyString[16384];
+static size_t yyStringLen;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+
+// the file to read from (if reading from a file)
+static FILE *yyInFile;
+
+// the string to read from and current position in the string (otherwise)
+static TQString yyInStr;
+static int yyInPos;
+static int buf;
+
+static int (*getChar)();
+static int (*peekChar)();
+
+static int getCharFromFile()
+{
+ int c;
+
+ if ( buf < 0 )
+ c = getc( yyInFile );
+ else {
+ c = buf;
+ buf = -1;
+ }
+ if ( c == '\n' )
+ yyCurLineNo++;
+ return c;
+}
+
+static int peekCharFromFile()
+{
+ int c = getc( yyInFile );
+ buf = c;
+ return c;
+}
+
+static void startTokenizer( const char *fileName, int (*getCharFunc)(),
+ int (*peekCharFunc)() )
+{
+ yyInPos = 0;
+ buf = -1;
+ getChar = getCharFunc;
+ peekChar = peekCharFunc;
+
+ yyFileName = fileName;
+ yyCh = getChar();
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+}
+
+static int getToken()
+{
+ const char tab[] = "abfnrtv";
+ const char backTab[] = "\a\b\f\n\r\t\v";
+ uint n;
+
+ yyIdentLen = 0;
+ yyCommentLen = 0;
+ yyStringLen = 0;
+
+ while ( yyCh != EOF ) {
+ yyLineNo = yyCurLineNo;
+
+ if ( isalpha(yyCh) || yyCh == '_' ) {
+ do {
+ if ( yyIdentLen < sizeof(yyIdent) - 1 )
+ yyIdent[yyIdentLen++] = (char) yyCh;
+ yyCh = getChar();
+ } while ( isalnum(yyCh) || yyCh == '_' );
+ yyIdent[yyIdentLen] = '\0';
+
+ switch ( yyIdent[0] ) {
+ case 'Q':
+ if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) {
+ return Tok_tr;
+ } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) {
+ return Tok_translate;
+ }
+ break;
+ case 'c':
+ if ( strcmp(yyIdent + 1, "lass") == 0 )
+ return Tok_class;
+ break;
+ case 'r':
+ if ( strcmp(yyIdent + 1, "eturn") == 0 )
+ return Tok_return;
+ break;
+ case 't':
+ if ( strcmp(yyIdent + 1, "r") == 0 )
+ return Tok_tr;
+ else if ( strcmp(yyIdent + 1, "rUtf8") == 0 )
+ return Tok_trUtf8;
+ else if ( strcmp(yyIdent + 1, "ranslate") == 0 )
+ return Tok_translate;
+ case '_':
+ if ( strcmp(yyIdent + 1, "_tr") == 0 )
+ return Tok_tr;
+ else if ( strcmp(yyIdent + 1, "_trUtf8") == 0 )
+ return Tok_trUtf8;
+ }
+ return Tok_Ident;
+ } else {
+ switch ( yyCh ) {
+ case '#':
+ yyCh = getChar();
+ do {
+ yyCh = getChar();
+ } while ( yyCh != EOF && yyCh != '\n' );
+ break;
+ case '"':
+ case '\'':
+ int quoteChar;
+ int trippelQuote, singleQuote;
+ int in;
+
+ quoteChar = yyCh;
+ trippelQuote = 0;
+ singleQuote = 1;
+ in = 0;
+ yyCh = getChar();
+
+ while ( yyCh != EOF ) {
+ if ( singleQuote && (yyCh == '\n' || (in && yyCh == quoteChar)) )
+ break;
+
+ if ( yyCh == quoteChar ) {
+ if (peekChar() == quoteChar) {
+ yyCh = getChar();
+ if (!trippelQuote) {
+ trippelQuote = 1;
+ singleQuote = 0;
+ in = 1;
+ yyCh = getChar();
+ } else {
+ yyCh = getChar();
+ if (yyCh == quoteChar) {
+ trippelQuote = 0;
+ break;
+ }
+ }
+ } else if (trippelQuote) {
+ if ( yyStringLen < sizeof(yyString) - 1 )
+ yyString[yyStringLen++] = (char) yyCh;
+ yyCh = getChar();
+ continue;
+ } else
+ break;
+ } else
+ in = 1;
+
+ if ( yyCh == '\\' ) {
+ yyCh = getChar();
+
+ if ( yyCh == 'x' ) {
+ TQCString hex = "0";
+
+ yyCh = getChar();
+ while ( isxdigit(yyCh) ) {
+ hex += (char) yyCh;
+ yyCh = getChar();
+ }
+ sscanf( hex, "%x", &n );
+ if ( yyStringLen < sizeof(yyString) - 1 )
+ yyString[yyStringLen++] = (char) n;
+ } else if ( yyCh >= '0' && yyCh < '8' ) {
+ TQCString oct = "";
+
+ do {
+ oct += (char) yyCh;
+ yyCh = getChar();
+ } while ( yyCh >= '0' && yyCh < '8' );
+ sscanf( oct, "%o", &n );
+ if ( yyStringLen < sizeof(yyString) - 1 )
+ yyString[yyStringLen++] = (char) n;
+ } else {
+ const char *p = strchr( tab, yyCh );
+ if ( yyStringLen < sizeof(yyString) - 1 )
+ yyString[yyStringLen++] = ( p == 0 ) ?
+ (char) yyCh : backTab[p - tab];
+ yyCh = getChar();
+ }
+ } else {
+ if ( yyStringLen < sizeof(yyString) - 1 )
+ yyString[yyStringLen++] = (char) yyCh;
+ yyCh = getChar();
+ }
+ }
+ yyString[yyStringLen] = '\0';
+
+ if ( yyCh != quoteChar ) {
+ printf("%c\n", yyCh);
+ tqWarning( "%s:%d: Unterminated string",
+ (const char *) yyFileName, yyLineNo );
+ }
+
+ if ( yyCh == EOF ) {
+ return Tok_Eof;
+ } else {
+ yyCh = getChar();
+ return Tok_String;
+ }
+ break;
+ case '(':
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case '.':
+ yyCh = getChar();
+ return Tok_Dot;
+ default:
+ yyCh = getChar();
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+/*
+ The second part of this source file is the parser. It accomplishes
+ a very easy task: It finds all strings inside a tr() or translate()
+ call, and possibly finds out the context of the call. It supports
+ three cases:
+ (1) the context is specified, as in FunnyDialog.tr("Hello") or
+ translate("FunnyDialog", "Hello");
+ (2) the call appears within an inlined function;
+ (3) the call appears within a function defined outside the class definition.
+*/
+
+static int yyTok;
+
+static bool match( int t )
+{
+ bool matches = ( yyTok == t );
+ if ( matches )
+ yyTok = getToken();
+ return matches;
+}
+
+static bool matchString( TQCString *s )
+{
+ bool matches = ( yyTok == Tok_String );
+ *s = "";
+ while ( yyTok == Tok_String ) {
+ *s += yyString;
+ yyTok = getToken();
+ }
+ return matches;
+}
+
+static bool matchEncoding( bool *utf8 )
+{
+ if ( yyTok == Tok_Ident ) {
+ if ( strcmp(yyIdent, "TQApplication") == 0 ) {
+ yyTok = getToken();
+ }
+ *utf8 = TQString( yyIdent ).endsWith( TQString("UTF8") );
+ yyTok = getToken();
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static void parse( MetaTranslator *tor, const char *initialContext,
+ const char *defaultContext )
+{
+ TQMap<TQCString, TQCString> qualifiedContexts;
+ TQCString context;
+ TQCString text;
+ TQCString com;
+ TQCString functionContext = initialContext;
+ TQCString prefix;
+ bool utf8 = FALSE;
+
+ yyTok = getToken();
+ while ( yyTok != Tok_Eof ) {
+ switch ( yyTok ) {
+ case Tok_class:
+ yyTok = getToken();
+ functionContext = yyIdent;
+ yyTok = getToken();
+ break;
+ case Tok_tr:
+ case Tok_trUtf8:
+ utf8 = ( yyTok == Tok_trUtf8 );
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) && matchString(&text) ) {
+ com = "";
+ if ( match(Tok_RightParen) || (match(Tok_Comma) &&
+ matchString(&com) && match(Tok_RightParen)) ) {
+ if ( prefix.isNull() ) {
+ context = defaultContext;
+ } else if ( qstrcmp(prefix, "self") == 0 ) {
+ context = functionContext;
+ } else {
+ context = prefix;
+ }
+ prefix = (const char *) 0;
+
+ if ( qualifiedContexts.contains(context) )
+ context = qualifiedContexts[context];
+ tor->insert( MetaTranslatorMessage(context, text, com,
+ TQString::null, utf8) );
+ }
+ }
+ break;
+ case Tok_translate:
+ utf8 = FALSE;
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) &&
+ matchString(&context) &&
+ match(Tok_Comma) &&
+ matchString(&text) ) {
+ com = "";
+ if ( match(Tok_RightParen) ||
+ (match(Tok_Comma) &&
+ matchString(&com) &&
+ (match(Tok_RightParen) ||
+ match(Tok_Comma) &&
+ matchEncoding(&utf8) &&
+ match(Tok_RightParen))) )
+ tor->insert( MetaTranslatorMessage(context, text, com,
+ TQString::null, utf8) );
+ }
+ break;
+ case Tok_Ident:
+ if ( !prefix.isNull() )
+ prefix += ".";
+ prefix += yyIdent;
+ yyTok = getToken();
+ if ( yyTok != Tok_Dot )
+ prefix = (const char *) 0;
+ break;
+ case Tok_Comment:
+ com = yyComment;
+ com = com.simplifyWhiteSpace();
+ if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) {
+ com.remove( 0, sizeof(MagicComment) - 1 );
+ int k = com.find( ' ' );
+ if ( k == -1 ) {
+ context = com;
+ } else {
+ context = com.left( k );
+ com.remove( 0, k + 1 );
+ tor->insert( MetaTranslatorMessage(context, "", com,
+ TQString::null, FALSE) );
+ }
+ }
+ yyTok = getToken();
+ break;
+ default:
+ yyTok = getToken();
+ }
+ }
+
+ if ( yyParenDepth != 0 )
+ tqWarning( "%s: Unbalanced parentheses in Python code",
+ (const char *) yyFileName );
+}
+
+void fetchtr_py( const char *fileName, MetaTranslator *tor,
+ const char *defaultContext, bool mustExist )
+{
+ yyInFile = fopen( fileName, "r" );
+ if ( yyInFile == 0 ) {
+ if ( mustExist )
+ tqWarning( "pytqlupdate error: cannot open Python source file '%s': %s",
+ fileName, strerror(errno) );
+ return;
+ }
+
+ startTokenizer( fileName, getCharFromFile, peekCharFromFile );
+ parse( tor, 0, defaultContext );
+ fclose( yyInFile );
+}
diff --git a/pytqlupdate3/main.cpp b/pytqlupdate3/main.cpp
new file mode 100644
index 0000000..6c708fe
--- /dev/null
+++ b/pytqlupdate3/main.cpp
@@ -0,0 +1,155 @@
+/**********************************************************************
+** Copyright (C) 2002 Detlev Offenbach <detlev@die-offenbachs.de>
+**
+** This is a modified version of lupdate. The original is part of TQt-Linguist.
+** The copyright of the original file can be found below.
+**
+** This version is modified to handle python sources.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** main.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqfile.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqtextstream.h>
+
+#include <errno.h>
+#include <metatranslator.h>
+#include <proparser.h>
+#include <string.h>
+
+// defined in fetchtr.cpp
+extern void fetchtr_py( const char *fileName, MetaTranslator *tor,
+ const char *defaultContext, bool mustExist );
+
+// defined in merge.cpp
+extern void merge( MetaTranslator *tor, const MetaTranslator *virginTor,
+ bool verbose );
+
+typedef TQValueList<MetaTranslatorMessage> TML;
+
+static void printUsage()
+{
+ tqWarning( "Usage: pytqlupdate [options] file.pro...\n"
+ "Options:\n"
+ " -help Display this information and exits\n"
+ " -noobsolete\n"
+ " Drop all obsolete strings\n"
+ " -verbose\n"
+ " Explain what is being done\n"
+ " -version\n"
+ " Display the version of pytqlupdate and exits" );
+}
+
+int main( int argc, char **argv )
+{
+ bool verbose = FALSE;
+ bool noObsolete = FALSE;
+ bool metSomething = FALSE;
+ int numProFiles = 0;
+
+ for ( int i = 1; i < argc; i++ ) {
+ if ( qstrcmp(argv[i], "-help") == 0 ) {
+ printUsage();
+ return 0;
+ } else if ( qstrcmp(argv[i], "-noobsolete") == 0 ) {
+ noObsolete = TRUE;
+ continue;
+ } else if ( qstrcmp(argv[i], "-verbose") == 0 ) {
+ verbose = TRUE;
+ continue;
+ } else if ( qstrcmp(argv[i], "-version") == 0 ) {
+ tqWarning( "pytqlupdate version %s", TQT_VERSION_STR );
+ return 0;
+ }
+
+ numProFiles++;
+ TQFile f( argv[i] );
+ if ( !f.open(IO_ReadOnly) ) {
+ tqWarning( "pytqlupdate error: Cannot open project file '%s': %s",
+ argv[i], strerror(errno) );
+ return 1;
+ }
+
+ TQTextStream t( &f );
+ TQString fullText = t.read();
+ f.close();
+
+ MetaTranslator fetchedTor;
+ TQString defaultContext = "@default";
+ TQCString codec;
+ TQStringList translatorFiles;
+ TQStringList::Iterator tf;
+
+ TQMap<TQString, TQString> tagMap = proFileTagMap( fullText );
+ TQMap<TQString, TQString>::Iterator it;
+
+ for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
+ TQStringList toks = TQStringList::split( TQChar(' '), it.data() );
+ TQStringList::Iterator t;
+
+ for ( t = toks.begin(); t != toks.end(); ++t ) {
+ if ( it.key() == TQString("SOURCES") ) {
+ fetchtr_py( *t, &fetchedTor,
+ defaultContext, TRUE );
+ metSomething = TRUE;
+ } else if ( it.key() == TQString("TRANSLATIONS") ) {
+ translatorFiles.append( *t );
+ metSomething = TRUE;
+ } else if ( it.key() == TQString("CODEC") ) {
+ codec = (*t).utf8();
+ }
+ }
+ }
+
+ for ( tf = translatorFiles.begin(); tf != translatorFiles.end(); ++tf ) {
+ MetaTranslator tor;
+ tor.load( *tf );
+ if ( !codec.isEmpty() )
+ tor.setCodec( codec );
+ if ( verbose )
+ tqWarning( "Updating '%s'...", (*tf).utf8() );
+ merge( &tor, &fetchedTor, verbose );
+ if ( noObsolete )
+ tor.stripObsoleteMessages();
+ tor.stripEmptyContexts();
+ if ( !tor.save(*tf) )
+ tqWarning( "pytqlupdate error: Cannot save '%s': %s", (*tf).utf8(),
+ strerror(errno) );
+ }
+ if ( !metSomething ) {
+ tqWarning( "pytqlupdate warning: File '%s' does not look like a project"
+ " file", argv[i] );
+ } else if ( translatorFiles.isEmpty() ) {
+ tqWarning( "pytqlupdate warning: Met no 'TRANSLATIONS' entry in project"
+ " file '%s'", argv[i] );
+ }
+ }
+
+ if ( numProFiles == 0 ) {
+ printUsage();
+ return 1;
+ }
+ return 0;
+}
diff --git a/pytqlupdate3/merge.cpp b/pytqlupdate3/merge.cpp
new file mode 100644
index 0000000..8559830
--- /dev/null
+++ b/pytqlupdate3/merge.cpp
@@ -0,0 +1,110 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** merge.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <metatranslator.h>
+
+// defined in numberh.cpp
+extern void applyNumberHeuristic( MetaTranslator *tor, bool verbose );
+// defined in sametexth.cpp
+extern void applySameTextHeuristic( MetaTranslator *tor, bool verbose );
+
+typedef TQValueList<MetaTranslatorMessage> TML;
+
+/*
+ Merges two MetaTranslator objects into the first one. The first one is a set
+ of source texts and translations for a previous version of the
+ internationalized program; the second one is a set of fresh source text newly
+ extracted from the source code, without any translation yet.
+*/
+
+void merge( MetaTranslator *tor, const MetaTranslator *virginTor, bool verbose )
+{
+ int known = 0;
+ int neww = 0;
+ int obsoleted = 0;
+ TML all = tor->messages();
+ TML::Iterator it;
+
+ /*
+ The types of all the messages from the vernacular translator are updated
+ according to the virgin translator.
+ */
+ for ( it = all.begin(); it != all.end(); ++it ) {
+ MetaTranslatorMessage::Type newType = MetaTranslatorMessage::Unfinished;
+ MetaTranslatorMessage m = *it;
+
+ // skip context comment
+ if ( !TQCString((*it).sourceText()).isEmpty() ) {
+ if ( !virginTor->contains((*it).context(), (*it).sourceText(),
+ (*it).comment()) ) {
+ newType = MetaTranslatorMessage::Obsolete;
+ if ( m.type() != MetaTranslatorMessage::Obsolete )
+ obsoleted++;
+ } else {
+ switch ( m.type() ) {
+ case MetaTranslatorMessage::Finished:
+ newType = MetaTranslatorMessage::Finished;
+ known++;
+ break;
+ case MetaTranslatorMessage::Unfinished:
+ newType = MetaTranslatorMessage::Unfinished;
+ known++;
+ break;
+ case MetaTranslatorMessage::Obsolete:
+ newType = MetaTranslatorMessage::Unfinished;
+ neww++;
+ }
+ }
+
+ if ( newType != m.type() ) {
+ m.setType( newType );
+ tor->insert( m );
+ }
+ }
+ }
+
+ /*
+ Messages found only in the virgin translator are added to the
+ vernacular translator. Among these are all the context comments.
+ */
+ all = virginTor->messages();
+
+ for ( it = all.begin(); it != all.end(); ++it ) {
+ if ( !tor->contains((*it).context(), (*it).sourceText(),
+ (*it).comment()) ) {
+ tor->insert( *it );
+ if ( !TQCString((*it).sourceText()).isEmpty() )
+ neww++;
+ }
+ }
+
+ /*
+ The same-text heuristic handles cases where a message has an
+ obsolete counterpart with a different context or comment.
+ */
+ applySameTextHeuristic( tor, verbose );
+
+ /*
+ The number heuristic handles cases where a message has an
+ obsolete counterpart with mostly numbers differing in the
+ source text.
+ */
+ applyNumberHeuristic( tor, verbose );
+
+ if ( verbose )
+ tqWarning( " %d known, %d new and %d obsoleted messages",
+ known, neww, obsoleted );
+}
diff --git a/pytqlupdate3/metatranslator.cpp b/pytqlupdate3/metatranslator.cpp
new file mode 100644
index 0000000..f46ee9a
--- /dev/null
+++ b/pytqlupdate3/metatranslator.cpp
@@ -0,0 +1,557 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** metatranslator.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqapplication.h>
+#include <tqcstring.h>
+#include <tqfile.h>
+#include <tqmessagebox.h>
+#include <tqregexp.h>
+#include <tqtextcodec.h>
+#include <tqtextstream.h>
+#include <tqxml.h>
+
+#include "metatranslator.h"
+
+static bool encodingIsUtf8( const TQXmlAttributes& atts )
+{
+ for ( int i = 0; i < atts.length(); i++ ) {
+ // utf8="true" is a pre-3.0 syntax
+ if ( atts.qName(i) == TQString("utf8") ) {
+ return ( atts.value(i) == TQString("true") );
+ } else if ( atts.qName(i) == TQString("encoding") ) {
+ return ( atts.value(i) == TQString("UTF-8") );
+ }
+ }
+ return FALSE;
+}
+
+class TsHandler : public TQXmlDefaultHandler
+{
+public:
+ TsHandler( MetaTranslator *translator )
+ : tor( translator ), type( MetaTranslatorMessage::Finished ),
+ inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ),
+ messageIsUtf8( FALSE ) { }
+
+ virtual bool startElement( const TQString& namespaceURI,
+ const TQString& localName, const TQString& qName,
+ const TQXmlAttributes& atts );
+ virtual bool endElement( const TQString& namespaceURI,
+ const TQString& localName, const TQString& qName );
+ virtual bool characters( const TQString& ch );
+ virtual bool fatalError( const TQXmlParseException& exception );
+
+private:
+ MetaTranslator *tor;
+ MetaTranslatorMessage::Type type;
+ bool inMessage;
+ TQString context;
+ TQString source;
+ TQString comment;
+ TQString translation;
+
+ TQString accum;
+ int ferrorCount;
+ bool contextIsUtf8;
+ bool messageIsUtf8;
+};
+
+bool TsHandler::startElement( const TQString& /* namespaceURI */,
+ const TQString& /* localName */,
+ const TQString& qName,
+ const TQXmlAttributes& atts )
+{
+ if ( qName == TQString("byte") ) {
+ for ( int i = 0; i < atts.length(); i++ ) {
+ if ( atts.qName(i) == TQString("value") ) {
+ TQString value = atts.value( i );
+ int base = 10;
+ if ( value.startsWith("x") ) {
+ base = 16;
+ value = value.mid( 1 );
+ }
+ int n = value.toUInt( 0, base );
+ if ( n != 0 )
+ accum += TQChar( n );
+ }
+ }
+ } else {
+ if ( qName == TQString("context") ) {
+ context.truncate( 0 );
+ source.truncate( 0 );
+ comment.truncate( 0 );
+ translation.truncate( 0 );
+ contextIsUtf8 = encodingIsUtf8( atts );
+ } else if ( qName == TQString("message") ) {
+ inMessage = TRUE;
+ type = MetaTranslatorMessage::Finished;
+ source.truncate( 0 );
+ comment.truncate( 0 );
+ translation.truncate( 0 );
+ messageIsUtf8 = encodingIsUtf8( atts );
+ } else if ( qName == TQString("translation") ) {
+ for ( int i = 0; i < atts.length(); i++ ) {
+ if ( atts.qName(i) == TQString("type") ) {
+ if ( atts.value(i) == TQString("unfinished") )
+ type = MetaTranslatorMessage::Unfinished;
+ else if ( atts.value(i) == TQString("obsolete") )
+ type = MetaTranslatorMessage::Obsolete;
+ else
+ type = MetaTranslatorMessage::Finished;
+ }
+ }
+ }
+ accum.truncate( 0 );
+ }
+ return TRUE;
+}
+
+bool TsHandler::endElement( const TQString& /* namespaceURI */,
+ const TQString& /* localName */,
+ const TQString& qName )
+{
+ if ( qName == TQString("codec") || qName == TQString("defaultcodec") ) {
+ // "codec" is a pre-3.0 syntax
+ tor->setCodec( accum );
+ } else if ( qName == TQString("name") ) {
+ context = accum;
+ } else if ( qName == TQString("source") ) {
+ source = accum;
+ } else if ( qName == TQString("comment") ) {
+ if ( inMessage ) {
+ comment = accum;
+ } else {
+ if ( contextIsUtf8 )
+ tor->insert( MetaTranslatorMessage(context.utf8(), "",
+ accum.utf8(), TQString::null, TRUE,
+ MetaTranslatorMessage::Unfinished) );
+ else
+ tor->insert( MetaTranslatorMessage(context.local8Bit(), "",
+ accum.local8Bit(), TQString::null, FALSE,
+ MetaTranslatorMessage::Unfinished) );
+ }
+ } else if ( qName == TQString("translation") ) {
+ translation = accum;
+ } else if ( qName == TQString("message") ) {
+ if ( messageIsUtf8 )
+ tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(),
+ comment.utf8(), translation,
+ TRUE, type) );
+ else
+ tor->insert( MetaTranslatorMessage(context.local8Bit(), source.local8Bit(),
+ comment.local8Bit(), translation,
+ FALSE, type) );
+ inMessage = FALSE;
+ }
+ return TRUE;
+}
+
+bool TsHandler::characters( const TQString& ch )
+{
+ TQString t = ch;
+ t.replace( TQRegExp(TQChar('\r')), "" );
+ accum += t;
+ return TRUE;
+}
+
+bool TsHandler::fatalError( const TQXmlParseException& exception )
+{
+ if ( ferrorCount++ == 0 ) {
+ TQString msg;
+ msg.sprintf( "Parse error at line %d, column %d (%s).",
+ exception.lineNumber(), exception.columnNumber(),
+ exception.message().utf8() );
+ if ( tqApp == 0 )
+ tqWarning( "XML error: %s", msg.utf8() );
+ else
+ TQMessageBox::information( tqApp->mainWidget(),
+ TQObject::tr("TQt Linguist"), msg );
+ }
+ return FALSE;
+}
+
+static TQString numericEntity( int ch )
+{
+ return TQString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" )
+ .arg( ch, 0, 16 );
+}
+
+static TQString protect( const TQCString& str )
+{
+ TQString result;
+ int len = (int) str.length();
+ for ( int k = 0; k < len; k++ ) {
+ switch( str[k] ) {
+ case '\"':
+ result += TQString( "&quot;" );
+ break;
+ case '&':
+ result += TQString( "&amp;" );
+ break;
+ case '>':
+ result += TQString( "&gt;" );
+ break;
+ case '<':
+ result += TQString( "&lt;" );
+ break;
+ case '\'':
+ result += TQString( "&apos;" );
+ break;
+ default:
+ if ( (uchar) str[k] < 0x20 && str[k] != '\n' )
+ result += numericEntity( (uchar) str[k] );
+ else
+ result += str[k];
+ }
+ }
+ return result;
+}
+
+static TQString evilBytes( const TQCString& str, bool utf8 )
+{
+ if ( utf8 ) {
+ return protect( str );
+ } else {
+ TQString result;
+ TQCString t = protect( str ).utf8();
+ int len = (int) t.length();
+ for ( int k = 0; k < len; k++ ) {
+ if ( (uchar) t[k] >= 0x7f )
+ result += numericEntity( (uchar) t[k] );
+ else
+ result += TQChar( t[k] );
+ }
+ return result;
+ }
+}
+
+MetaTranslatorMessage::MetaTranslatorMessage()
+ : utfeight( FALSE ), ty( Unfinished )
+{
+}
+
+MetaTranslatorMessage::MetaTranslatorMessage( const char *context,
+ const char *sourceText,
+ const char *comment,
+ const TQString& translation,
+ bool utf8, Type type )
+ : TQTranslatorMessage( context, sourceText, comment, translation ),
+ utfeight( FALSE ), ty( type )
+{
+ /*
+ Don't use UTF-8 if it makes no difference. UTF-8 should be
+ reserved for the real problematic case: non-ASCII (possibly
+ non-Latin-1) characters in .ui files.
+ */
+ if ( utf8 ) {
+ if ( sourceText != 0 ) {
+ int i = 0;
+ while ( sourceText[i] != '\0' ) {
+ if ( (uchar) sourceText[i] >= 0x80 ) {
+ utfeight = TRUE;
+ break;
+ }
+ i++;
+ }
+ }
+ if ( !utfeight && comment != 0 ) {
+ int i = 0;
+ while ( comment[i] != '\0' ) {
+ if ( (uchar) comment[i] >= 0x80 ) {
+ utfeight = TRUE;
+ break;
+ }
+ i++;
+ }
+ }
+ }
+}
+
+MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m )
+ : TQTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty )
+{
+}
+
+MetaTranslatorMessage& MetaTranslatorMessage::operator=(
+ const MetaTranslatorMessage& m )
+{
+ TQTranslatorMessage::operator=( m );
+ utfeight = m.utfeight;
+ ty = m.ty;
+ return *this;
+}
+
+bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const
+{
+ return qstrcmp( context(), m.context() ) == 0 &&
+ qstrcmp( sourceText(), m.sourceText() ) == 0 &&
+ qstrcmp( comment(), m.comment() ) == 0;
+}
+
+bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const
+{
+ int delta = qstrcmp( context(), m.context() );
+ if ( delta == 0 )
+ delta = qstrcmp( sourceText(), m.sourceText() );
+ if ( delta == 0 )
+ delta = qstrcmp( comment(), m.comment() );
+ return delta < 0;
+}
+
+MetaTranslator::MetaTranslator()
+ : codecName( "ISO-8859-1" ), codec( 0 )
+{
+}
+
+MetaTranslator::MetaTranslator( const MetaTranslator& tor )
+ : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec )
+{
+
+}
+
+MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor )
+{
+ mm = tor.mm;
+ codecName = tor.codecName;
+ codec = tor.codec;
+ return *this;
+}
+
+bool MetaTranslator::load( const TQString& filename )
+{
+ mm.clear();
+
+ TQFile f( filename );
+ if ( !f.open(IO_ReadOnly) )
+ return FALSE;
+
+ TQTextStream t( &f );
+ TQXmlInputSource in( t );
+ TQXmlSimpleReader reader;
+ // don't click on these!
+ reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
+ reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
+ reader.setFeature( "http://trolltech.com/xml/features/report-whitespace"
+ "-only-CharData", FALSE );
+ TQXmlDefaultHandler *hand = new TsHandler( this );
+ reader.setContentHandler( hand );
+ reader.setErrorHandler( hand );
+
+ bool ok = reader.parse( in );
+ reader.setContentHandler( 0 );
+ reader.setErrorHandler( 0 );
+ delete hand;
+ f.close();
+ if ( !ok )
+ mm.clear();
+ return ok;
+}
+
+bool MetaTranslator::save( const TQString& filename ) const
+{
+ TQFile f( filename );
+ if ( !f.open(IO_WriteOnly) )
+ return FALSE;
+
+ TQTextStream t( &f );
+ t.setCodec( TQTextCodec::codecForName("ISO-8859-1") );
+
+ t << "<!DOCTYPE TS><TS>\n";
+ if ( codecName != "ISO-8859-1" )
+ t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
+ TMM::ConstIterator m = mm.begin();
+ while ( m != mm.end() ) {
+ TMMInv inv;
+ TMMInv::Iterator i;
+ bool contextIsUtf8 = m.key().utf8();
+ TQCString context = m.key().context();
+ TQCString comment = "";
+
+ do {
+ if ( TQCString(m.key().sourceText()).isEmpty() ) {
+ if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
+ contextIsUtf8 = m.key().utf8();
+ comment = TQCString( m.key().comment() );
+ }
+ } else {
+ inv.insert( *m, m.key() );
+ }
+ } while ( ++m != mm.end() && TQCString(m.key().context()) == context );
+
+ t << "<context";
+ if ( contextIsUtf8 )
+ t << " encoding=\"UTF-8\"";
+ t << ">\n";
+ t << " <name>" << evilBytes( context, contextIsUtf8 )
+ << "</name>\n";
+ if ( !comment.isEmpty() )
+ t << " <comment>" << evilBytes( comment, contextIsUtf8 )
+ << "</comment>\n";
+
+ for ( i = inv.begin(); i != inv.end(); ++i ) {
+ t << " <message";
+ if ( (*i).utf8() )
+ t << " encoding=\"UTF-8\"";
+ t << ">\n"
+ << " <source>" << evilBytes( (*i).sourceText(),
+ (*i).utf8() )
+ << "</source>\n";
+ if ( !TQCString((*i).comment()).isEmpty() )
+ t << " <comment>" << evilBytes( (*i).comment(),
+ (*i).utf8() )
+ << "</comment>\n";
+ t << " <translation";
+ if ( (*i).type() == MetaTranslatorMessage::Unfinished )
+ t << " type=\"unfinished\"";
+ else if ( (*i).type() == MetaTranslatorMessage::Obsolete )
+ t << " type=\"obsolete\"";
+ t << ">" << protect( (*i).translation().utf8() )
+ << "</translation>\n";
+ t << " </message>\n";
+ }
+ t << "</context>\n";
+ }
+ t << "</TS>\n";
+ f.close();
+ return TRUE;
+}
+
+bool MetaTranslator::release( const TQString& filename, bool verbose ) const
+{
+ TQTranslator tor( 0 );
+ int finished = 0;
+ int unfinished = 0;
+ int untranslated = 0;
+ TMM::ConstIterator m;
+
+ for ( m = mm.begin(); m != mm.end(); ++m ) {
+ if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
+ if ( m.key().translation().isEmpty() ) {
+ untranslated++;
+ } else {
+ if ( m.key().type() == MetaTranslatorMessage::Unfinished )
+ unfinished++;
+ else
+ finished++;
+ tor.insert( m.key() );
+ }
+ }
+ }
+
+ bool saved = tor.save( filename, TQTranslator::Stripped );
+ if ( saved && verbose )
+ tqWarning( " %d finished, %d unfinished and %d untranslated messages",
+ finished, unfinished, untranslated );
+
+ return saved;
+}
+
+bool MetaTranslator::contains( const char *context, const char *sourceText,
+ const char *comment ) const
+{
+ return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) !=
+ mm.end();
+}
+
+void MetaTranslator::insert( const MetaTranslatorMessage& m )
+{
+ int pos = mm.count();
+ TMM::Iterator n = mm.find( m );
+ if ( n != mm.end() )
+ pos = *n;
+ mm.replace( m, pos );
+}
+
+void MetaTranslator::stripObsoleteMessages()
+{
+ TMM newmm;
+
+ TMM::Iterator m = mm.begin();
+ while ( m != mm.end() ) {
+ if ( m.key().type() != MetaTranslatorMessage::Obsolete )
+ newmm.insert( m.key(), *m );
+ ++m;
+ }
+ mm = newmm;
+}
+
+void MetaTranslator::stripEmptyContexts()
+{
+ TMM newmm;
+
+ TMM::Iterator m = mm.begin();
+ while ( m != mm.end() ) {
+ if ( TQCString(m.key().sourceText()).isEmpty() ) {
+ TMM::Iterator n = m;
+ ++n;
+ // the context comment is followed by other messages
+ if ( n != newmm.end() &&
+ qstrcmp(m.key().context(), n.key().context()) == 0 )
+ newmm.insert( m.key(), *m );
+ } else {
+ newmm.insert( m.key(), *m );
+ }
+ ++m;
+ }
+ mm = newmm;
+}
+
+void MetaTranslator::setCodec( const char *name )
+{
+ const int latin1 = 4;
+
+ codecName = name;
+ codec = TQTextCodec::codecForName( name );
+ if ( codec == 0 || codec->mibEnum() == latin1 )
+ codec = 0;
+}
+
+TQString MetaTranslator::toUnicode( const char *str, bool utf8 ) const
+{
+ if ( utf8 )
+ return TQString::fromUtf8( str );
+ else if ( codec == 0 )
+ return TQString( str );
+ else
+ return codec->toUnicode( str );
+}
+
+TQValueList<MetaTranslatorMessage> MetaTranslator::messages() const
+{
+ int n = mm.count();
+ TMM::ConstIterator *t = new TMM::ConstIterator[n + 1];
+ TMM::ConstIterator m;
+ for ( m = mm.begin(); m != mm.end(); ++m )
+ t[*m] = m;
+
+ TQValueList<MetaTranslatorMessage> val;
+ for ( int i = 0; i < n; i++ )
+ val.append( t[i].key() );
+
+ delete[] t;
+ return val;
+}
+
+TQValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const
+{
+ TQValueList<MetaTranslatorMessage> val;
+ TMM::ConstIterator m;
+ for ( m = mm.begin(); m != mm.end(); ++m ) {
+ if ( m.key().type() == MetaTranslatorMessage::Finished )
+ val.append( m.key() );
+ }
+ return val;
+}
diff --git a/pytqlupdate3/metatranslator.h b/pytqlupdate3/metatranslator.h
new file mode 100644
index 0000000..aab280f
--- /dev/null
+++ b/pytqlupdate3/metatranslator.h
@@ -0,0 +1,95 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** metatranslator.h
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#ifndef METATRANSLATOR_H
+#define METATRANSLATOR_H
+
+#include <tqmap.h>
+#include <tqstring.h>
+#include <tqtranslator.h>
+#include <tqvaluelist.h>
+
+class TQTextCodec;
+
+class MetaTranslatorMessage : public TQTranslatorMessage
+{
+public:
+ enum Type { Unfinished, Finished, Obsolete };
+
+ MetaTranslatorMessage();
+ MetaTranslatorMessage( const char *context, const char *sourceText,
+ const char *comment,
+ const TQString& translation = TQString::null,
+ bool utf8 = FALSE, Type type = Unfinished );
+ MetaTranslatorMessage( const MetaTranslatorMessage& m );
+
+ MetaTranslatorMessage& operator=( const MetaTranslatorMessage& m );
+
+ void setType( Type nt ) { ty = nt; }
+ Type type() const { return ty; }
+ bool utf8() const { return utfeight; }
+
+ bool operator==( const MetaTranslatorMessage& m ) const;
+ bool operator!=( const MetaTranslatorMessage& m ) const
+ { return !operator==( m ); }
+ bool operator<( const MetaTranslatorMessage& m ) const;
+ bool operator<=( const MetaTranslatorMessage& m )
+ { return !operator>( m ); }
+ bool operator>( const MetaTranslatorMessage& m ) const
+ { return this->operator<( m ); }
+ bool operator>=( const MetaTranslatorMessage& m ) const
+ { return !operator<( m ); }
+
+private:
+ bool utfeight;
+ Type ty;
+};
+
+class MetaTranslator
+{
+public:
+ MetaTranslator();
+ MetaTranslator( const MetaTranslator& tor );
+
+ MetaTranslator& operator=( const MetaTranslator& tor );
+
+ bool load( const TQString& filename );
+ bool save( const TQString& filename ) const;
+ bool release( const TQString& filename, bool verbose = FALSE ) const;
+
+ bool contains( const char *context, const char *sourceText,
+ const char *comment ) const;
+ void insert( const MetaTranslatorMessage& m );
+
+ void stripObsoleteMessages();
+ void stripEmptyContexts();
+
+ void setCodec( const char *name );
+ TQString toUnicode( const char *str, bool utf8 ) const;
+
+ TQValueList<MetaTranslatorMessage> messages() const;
+ TQValueList<MetaTranslatorMessage> translatedMessages() const;
+
+private:
+ typedef TQMap<MetaTranslatorMessage, int> TMM;
+ typedef TQMap<int, MetaTranslatorMessage> TMMInv;
+
+ TMM mm;
+ TQCString codecName;
+ TQTextCodec *codec;
+};
+
+#endif
diff --git a/pytqlupdate3/numberh.cpp b/pytqlupdate3/numberh.cpp
new file mode 100644
index 0000000..a52687b
--- /dev/null
+++ b/pytqlupdate3/numberh.cpp
@@ -0,0 +1,230 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** numberh.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqmemarray.h>
+#include <tqcstring.h>
+#include <tqmap.h>
+#include <tqstringlist.h>
+
+#include <ctype.h>
+#include <metatranslator.h>
+
+typedef TQMap<TQCString, MetaTranslatorMessage> TMM;
+typedef TQValueList<MetaTranslatorMessage> TML;
+
+static bool isDigitFriendly( int c )
+{
+ return ispunct( c ) || isspace( c );
+}
+
+static int numberLength( const char *s )
+{
+ int i = 0;
+
+ if ( isdigit(s[0]) ) {
+ do {
+ i++;
+ } while ( isdigit(s[i]) ||
+ (isDigitFriendly(s[i]) &&
+ (isdigit(s[i + 1]) ||
+ (isDigitFriendly(s[i + 1]) && isdigit(s[i + 2])))) );
+ }
+ return i;
+}
+
+/*
+ Returns a version of 'key' where all numbers have been replaced by zeroes. If
+ there were none, returns "".
+*/
+static TQCString zeroKey( const char *key )
+{
+ TQCString zeroed( strlen(key) + 1 );
+ char *z = zeroed.data();
+ int i = 0, j = 0;
+ int len;
+ bool metSomething = FALSE;
+
+ while ( key[i] != '\0' ) {
+ len = numberLength( key + i );
+ if ( len > 0 ) {
+ i += len;
+ z[j++] = '0';
+ metSomething = TRUE;
+ } else {
+ z[j++] = key[i++];
+ }
+ }
+ z[j] = '\0';
+
+ if ( metSomething )
+ return zeroed;
+ else
+ return "";
+}
+
+static TQString translationAttempt( const TQString& oldTranslation,
+ const char *oldSource,
+ const char *newSource )
+{
+ int p = zeroKey( oldSource ).contains( '0' );
+ int oldSourceLen = tqstrlen( oldSource );
+ TQString attempt;
+ TQStringList oldNumbers;
+ TQStringList newNumbers;
+ TQMemArray<bool> met( p );
+ TQMemArray<int> matchedYet( p );
+ int i, j;
+ int k = 0, ell, best;
+ int m, n;
+ int pass;
+
+ /*
+ This algorithm is hard to follow, so we'll consider an example
+ all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0"
+ and newSource is "XeT 3.1".
+
+ First, we set up two tables: oldNumbers and newNumbers. In our
+ example, oldNumber[0] is "3.0" and newNumber[0] is "3.1".
+ */
+ for ( i = 0, j = 0; i < oldSourceLen; i++, j++ ) {
+ m = numberLength( oldSource + i );
+ n = numberLength( newSource + j );
+ if ( m > 0 ) {
+ oldNumbers.append( TQCString(oldSource + i, m + 1) );
+ newNumbers.append( TQCString(newSource + j, n + 1) );
+ i += m;
+ j += n;
+ met[k] = FALSE;
+ matchedYet[k] = 0;
+ k++;
+ }
+ }
+
+ /*
+ We now go over the old translation, "XeT 3.0", one letter at a
+ time, looking for numbers found in oldNumbers. Whenever such a
+ number is met, it is replaced with its newNumber equivalent. In
+ our example, the "3.0" of "XeT 3.0" becomes "3.1".
+ */
+ for ( i = 0; i < (int) oldTranslation.length(); i++ ) {
+ attempt += oldTranslation[i];
+ for ( k = 0; k < p; k++ ) {
+ if ( oldTranslation[i] == oldNumbers[k][matchedYet[k]] )
+ matchedYet[k]++;
+ else
+ matchedYet[k] = 0;
+ }
+
+ /*
+ Let's find out if the last character ended a match. We make
+ two passes over the data. In the first pass, we try to
+ match only numbers that weren't matched yet; if that fails,
+ the second pass does the trick. This is useful in some
+ suspicious cases, flagged below.
+ */
+ for ( pass = 0; pass < 2; pass++ ) {
+ best = p; // an impossible value
+ for ( k = 0; k < p; k++ ) {
+ if ( (!met[k] || pass > 0) &&
+ matchedYet[k] == (int) oldNumbers[k].length() &&
+ numberLength(oldTranslation.utf8() + (i + 1) -
+ matchedYet[k]) == matchedYet[k] ) {
+ // the longer the better
+ if ( best == p || matchedYet[k] > matchedYet[best] )
+ best = k;
+ }
+ }
+ if ( best != p ) {
+ attempt.truncate( attempt.length() - matchedYet[best] );
+ attempt += newNumbers[best];
+ met[best] = TRUE;
+ for ( k = 0; k < p; k++ )
+ matchedYet[k] = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ We flag two kinds of suspicious cases. They are identified as
+ such with comments such as "{2000?}" at the end.
+
+ Example of the first kind: old source text "TeX 3.0" translated
+ as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the
+ new text is.
+ */
+ for ( k = 0; k < p; k++ ) {
+ if ( !met[k] )
+ attempt += TQString( " {" ) + newNumbers[k] + TQString( "?}" );
+ }
+
+ /*
+ Example of the second kind: "1 of 1" translated as "1 af 1",
+ with new source text "1 of 2", generates "1 af 2 {1 or 2?}"
+ because it's not clear which of "1 af 2" and "2 af 1" is right.
+ */
+ for ( k = 0; k < p; k++ ) {
+ for ( ell = 0; ell < p; ell++ ) {
+ if ( k != ell && oldNumbers[k] == oldNumbers[ell] &&
+ newNumbers[k] < newNumbers[ell] )
+ attempt += TQString( " {" ) + newNumbers[k] + TQString( " or " ) +
+ newNumbers[ell] + TQString( "?}" );
+ }
+ }
+ return attempt;
+}
+
+/*
+ Augments a MetaTranslator with translations easily derived from
+ similar existing (probably obsolete) translations.
+
+ For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1"
+ has no translation, "XeT 3.1" is added to the translator and is
+ marked Unfinished.
+*/
+void applyNumberHeuristic( MetaTranslator *tor, bool verbose )
+{
+ TMM translated, untranslated;
+ TMM::Iterator t, u;
+ TML all = tor->messages();
+ TML::Iterator it;
+ int inserted = 0;
+
+ for ( it = all.begin(); it != all.end(); ++it ) {
+ if ( (*it).type() == MetaTranslatorMessage::Unfinished ) {
+ if ( (*it).translation().isEmpty() )
+ untranslated.insert( zeroKey((*it).sourceText()), *it );
+ } else if ( !(*it).translation().isEmpty() ) {
+ translated.insert( zeroKey((*it).sourceText()), *it );
+ }
+ }
+
+ for ( u = untranslated.begin(); u != untranslated.end(); ++u ) {
+ t = translated.find( u.key() );
+ if ( t != translated.end() && !t.key().isEmpty() &&
+ qstrcmp((*t).sourceText(), (*u).sourceText()) != 0 ) {
+ MetaTranslatorMessage m( *u );
+ m.setTranslation( translationAttempt((*t).translation(),
+ (*t).sourceText(),
+ (*u).sourceText()) );
+ tor->insert( m );
+ inserted++;
+ }
+ }
+ if ( verbose && inserted != 0 )
+ tqWarning( " number heuristic provided %d translation%s",
+ inserted, inserted == 1 ? "" : "s" );
+}
diff --git a/pytqlupdate3/proparser.cpp b/pytqlupdate3/proparser.cpp
new file mode 100644
index 0000000..72256f3
--- /dev/null
+++ b/pytqlupdate3/proparser.cpp
@@ -0,0 +1,78 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** proparser.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqregexp.h>
+#include <tqstringlist.h>
+
+#include "proparser.h"
+
+TQMap<TQString, TQString> proFileTagMap( const TQString& text )
+{
+ TQString t = text;
+
+ /*
+ Strip comments, merge lines ending with backslash, add
+ spaces around '=' and '+=', replace '\n' with ';', and
+ simplify white spaces.
+ */
+ t.replace( TQRegExp(TQString("#[^\n]$")), TQString(" ") );
+ t.replace( TQRegExp(TQString("\\\\\\s*\n")), TQString(" ") );
+ t.replace( TQRegExp(TQString("=")), TQString(" = ") );
+ t.replace( TQRegExp(TQString("\\+ =")), TQString(" += ") );
+ t.replace( TQRegExp(TQString("\n")), TQString(";") );
+ t = t.simplifyWhiteSpace();
+
+ TQMap<TQString, TQString> tagMap;
+
+ TQStringList lines = TQStringList::split( TQChar(';'), t );
+ TQStringList::Iterator line;
+ for ( line = lines.begin(); line != lines.end(); ++line ) {
+ TQStringList toks = TQStringList::split( TQChar(' '), *line );
+
+ if ( toks.count() >= 3 &&
+ (toks[1] == TQString("=") || toks[1] == TQString("+=")) ) {
+ TQString tag = toks.first();
+ int k = tag.findRev( TQChar(':') ); // as in 'unix:'
+ if ( k != -1 )
+ tag = tag.mid( k + 1 );
+ toks.remove( toks.begin() );
+
+ TQString action = toks.first();
+ toks.remove( toks.begin() );
+
+ if ( tagMap.contains(tag) ) {
+ if ( action == TQString("=") )
+ tagMap.replace( tag, toks.join(TQChar(' ')) );
+ else
+ tagMap[tag] += TQChar( ' ' ) + toks.join( TQChar(' ') );
+ } else {
+ tagMap[tag] = toks.join( TQChar(' ') );
+ }
+ }
+ }
+
+ TQRegExp var( "\\$\\$[a-zA-Z0-9_]+" );
+ TQMap<TQString, TQString>::Iterator it;
+ for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
+ int i = 0;
+
+ while ( (i = var.search(it.data(), i)) != -1 ) {
+ int len = var.matchedLength();
+ (*it).replace( i, len, tagMap[(*it).mid(i + 2, len - 2)] );
+ }
+ }
+ return tagMap;
+}
diff --git a/pytqlupdate3/proparser.h b/pytqlupdate3/proparser.h
new file mode 100644
index 0000000..1cb7928
--- /dev/null
+++ b/pytqlupdate3/proparser.h
@@ -0,0 +1,25 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** proparser.h
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#ifndef PROPARSER_H
+#define PROPARSER_H
+
+#include <tqmap.h>
+#include <tqstring.h>
+
+TQMap<TQString, TQString> proFileTagMap( const TQString& text );
+
+#endif
diff --git a/pytqlupdate3/pytqlupdate-prof.sbf b/pytqlupdate3/pytqlupdate-prof.sbf
new file mode 100644
index 0000000..2acf080
--- /dev/null
+++ b/pytqlupdate3/pytqlupdate-prof.sbf
@@ -0,0 +1,24 @@
+# This is the build file for pytqlupdate for TQt v3 Professional Edition.
+#
+# Copyright (c) 2007
+# Riverbank Computing Limited <info@riverbankcomputing.co.uk>
+#
+# This file is part of PyTQt.
+#
+# This copy of PyTQt is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# PyTQt is supplied in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# PyTQt; see the file LICENSE. If not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+target = pytqlupdate
+sources = fetchtr.cpp main.cpp merge.cpp numberh.cpp sametexth.cpp metatranslator.cpp proparser.cpp qxml.cpp
+headers = metatranslator.h proparser.h
diff --git a/pytqlupdate3/pytqlupdate.pro.in b/pytqlupdate3/pytqlupdate.pro.in
new file mode 100644
index 0000000..9dc855a
--- /dev/null
+++ b/pytqlupdate3/pytqlupdate.pro.in
@@ -0,0 +1,29 @@
+# Copyright (c) 2002
+# Detlev Offenbach <detlev@die-offenbachs.de>
+#
+# The project file pytqlupdate for TQt v3.
+
+
+TEMPLATE = app
+CONFIG += tqt warn_on console release @PYTQT_RBPROF@
+INCLUDEPATH = @BLX_INCLUDEPATH@
+DEFINES = TQT_INTERNAL_XML @BLX_DEFINES@
+
+DESTDIR = @PYTQT_BINDIR@
+TARGET = pytqlupdate
+
+HEADERS = metatranslator.h \
+ proparser.h
+
+SOURCES = fetchtr.cpp \
+ main.cpp \
+ merge.cpp \
+ numberh.cpp \
+ sametexth.cpp \
+ metatranslator.cpp \
+ proparser.cpp
+
+rbprof:exists($(TQTDIR)/src/qt_professional.pri) {
+ TQT_SOURCE_TREE = $(TQTDIR)
+ include($(TQTDIR)/src/qt_professional.pri)
+}
diff --git a/pytqlupdate3/pytqlupdate.sbf b/pytqlupdate3/pytqlupdate.sbf
new file mode 100644
index 0000000..d97edd6
--- /dev/null
+++ b/pytqlupdate3/pytqlupdate.sbf
@@ -0,0 +1,24 @@
+# This is the build file for pytqlupdate for TQt v3 Professional Edition.
+#
+# Copyright (c) 2007
+# Riverbank Computing Limited <info@riverbankcomputing.co.uk>
+#
+# This file is part of PyTQt.
+#
+# This copy of PyTQt is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# PyTQt is supplied in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# PyTQt; see the file LICENSE. If not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+target = pytqlupdate
+sources = fetchtr.cpp main.cpp merge.cpp numberh.cpp sametexth.cpp metatranslator.cpp proparser.cpp
+headers = metatranslator.h proparser.h
diff --git a/pytqlupdate3/sametexth.cpp b/pytqlupdate3/sametexth.cpp
new file mode 100644
index 0000000..fd67a55
--- /dev/null
+++ b/pytqlupdate3/sametexth.cpp
@@ -0,0 +1,78 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** sametexth.cpp
+**
+** This file is part of TQt Linguist.
+**
+** See the file LICENSE included in the distribution for the usage
+** and distribution terms.
+**
+** The file is provided AS IS with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE.
+**
+**********************************************************************/
+
+#include <tqcstring.h>
+#include <tqmap.h>
+
+#include <metatranslator.h>
+
+typedef TQMap<TQCString, MetaTranslatorMessage> TMM;
+typedef TQValueList<MetaTranslatorMessage> TML;
+
+/*
+ Augments a MetaTranslator with trivially derived translations.
+
+ For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no
+ matter the context or the comment, "Eingeschaltet:" is added as the
+ translation of any untranslated "Enabled:" text and is marked Unfinished.
+*/
+
+void applySameTextHeuristic( MetaTranslator *tor, bool verbose )
+{
+ TMM translated, avoid;
+ TMM::Iterator t;
+ TML untranslated;
+ TML::Iterator u;
+ TML all = tor->messages();
+ TML::Iterator it;
+ int inserted = 0;
+
+ for ( it = all.begin(); it != all.end(); ++it ) {
+ if ( (*it).type() == MetaTranslatorMessage::Unfinished ) {
+ if ( (*it).translation().isEmpty() )
+ untranslated.append( *it );
+ } else {
+ TQCString key = (*it).sourceText();
+ t = translated.find( key );
+ if ( t != translated.end() ) {
+ /*
+ The same source text is translated at least two
+ different ways. Do nothing then.
+ */
+ if ( (*t).translation() != (*it).translation() ) {
+ translated.remove( key );
+ avoid.insert( key, *it );
+ }
+ } else if ( !avoid.contains(key) ) {
+ translated.insert( key, *it );
+ }
+ }
+ }
+
+ for ( u = untranslated.begin(); u != untranslated.end(); ++u ) {
+ TQCString key = (*u).sourceText();
+ t = translated.find( key );
+ if ( t != translated.end() ) {
+ MetaTranslatorMessage m( *u );
+ m.setTranslation( (*t).translation() );
+ tor->insert( m );
+ inserted++;
+ }
+ }
+ if ( verbose && inserted != 0 )
+ tqWarning( " same-text heuristic provided %d translation%s",
+ inserted, inserted == 1 ? "" : "s" );
+}