diff options
Diffstat (limited to 'pytqlupdate3')
-rw-r--r-- | pytqlupdate3/LICENSE.GPL | 280 | ||||
-rw-r--r-- | pytqlupdate3/fetchtr.cpp | 455 | ||||
-rw-r--r-- | pytqlupdate3/main.cpp | 155 | ||||
-rw-r--r-- | pytqlupdate3/merge.cpp | 110 | ||||
-rw-r--r-- | pytqlupdate3/metatranslator.cpp | 557 | ||||
-rw-r--r-- | pytqlupdate3/metatranslator.h | 95 | ||||
-rw-r--r-- | pytqlupdate3/numberh.cpp | 230 | ||||
-rw-r--r-- | pytqlupdate3/proparser.cpp | 78 | ||||
-rw-r--r-- | pytqlupdate3/proparser.h | 25 | ||||
-rw-r--r-- | pytqlupdate3/pytqlupdate-prof.sbf | 24 | ||||
-rw-r--r-- | pytqlupdate3/pytqlupdate.pro.in | 29 | ||||
-rw-r--r-- | pytqlupdate3/pytqlupdate.sbf | 24 | ||||
-rw-r--r-- | pytqlupdate3/sametexth.cpp | 78 |
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( """ ); + break; + case '&': + result += TQString( "&" ); + break; + case '>': + result += TQString( ">" ); + break; + case '<': + result += TQString( "<" ); + break; + case '\'': + result += TQString( "'" ); + 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" ); +} |