[2] | 1 | /****************************************************************************
|
---|
| 2 | ** $Id: qtranslator.cpp 8 2005-11-16 19:36:46Z dmik $
|
---|
| 3 | **
|
---|
| 4 | ** Localization database support.
|
---|
| 5 | **
|
---|
| 6 | ** Created : 980906
|
---|
| 7 | **
|
---|
| 8 | ** Copyright (C) 1998-2003 Trolltech AS. All rights reserved.
|
---|
| 9 | **
|
---|
| 10 | ** This file is part of the kernel module of the Qt GUI Toolkit.
|
---|
| 11 | **
|
---|
| 12 | ** This file may be distributed under the terms of the Q Public License
|
---|
| 13 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
| 14 | ** LICENSE.QPL included in the packaging of this file.
|
---|
| 15 | **
|
---|
| 16 | ** This file may be distributed and/or modified under the terms of the
|
---|
| 17 | ** GNU General Public License version 2 as published by the Free Software
|
---|
| 18 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
| 19 | ** packaging of this file.
|
---|
| 20 | **
|
---|
| 21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
| 22 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
| 23 | ** Agreement provided with the Software.
|
---|
| 24 | **
|
---|
| 25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
| 26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
| 27 | **
|
---|
| 28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
| 29 | ** information about Qt Commercial License Agreements.
|
---|
| 30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
| 31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
| 32 | **
|
---|
| 33 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
| 34 | ** not clear to you.
|
---|
| 35 | **
|
---|
| 36 | **********************************************************************/
|
---|
| 37 |
|
---|
| 38 | #include "qplatformdefs.h"
|
---|
| 39 |
|
---|
| 40 | // POSIX Large File Support redefines open -> open64
|
---|
| 41 | static inline int qt_open(const char *pathname, int flags, mode_t mode)
|
---|
| 42 | { return ::open(pathname, flags, mode); }
|
---|
| 43 | #if defined(open)
|
---|
| 44 | # undef open
|
---|
| 45 | #endif
|
---|
| 46 |
|
---|
| 47 | // POSIX Large File Support redefines truncate -> truncate64
|
---|
| 48 | #if defined(truncate)
|
---|
| 49 | # undef truncate
|
---|
| 50 | #endif
|
---|
| 51 |
|
---|
| 52 | #include "qtranslator.h"
|
---|
| 53 |
|
---|
| 54 | #ifndef QT_NO_TRANSLATION
|
---|
| 55 |
|
---|
| 56 | #include "qfileinfo.h"
|
---|
| 57 | #include "qwidgetlist.h"
|
---|
| 58 | #include "qintdict.h"
|
---|
| 59 | #include "qstring.h"
|
---|
| 60 | #include "qapplication.h"
|
---|
| 61 | #include "qfile.h"
|
---|
| 62 | #include "qbuffer.h"
|
---|
| 63 | #include "qdatastream.h"
|
---|
| 64 | #include "qmap.h"
|
---|
| 65 | #include "qtl.h"
|
---|
| 66 |
|
---|
| 67 | #if defined(Q_OS_UNIX)
|
---|
| 68 | #define QT_USE_MMAP
|
---|
| 69 | #endif
|
---|
| 70 |
|
---|
| 71 | // most of the headers below are already included in qplatformdefs.h
|
---|
| 72 | // also this lacks Large File support but that's probably irrelevant
|
---|
| 73 | #if defined(QT_USE_MMAP)
|
---|
| 74 | // for mmap
|
---|
| 75 | #include <sys/types.h>
|
---|
| 76 | #include <sys/stat.h>
|
---|
| 77 | #include <sys/mman.h>
|
---|
| 78 | #include <fcntl.h>
|
---|
| 79 | #include <errno.h>
|
---|
| 80 | // for htonl
|
---|
| 81 | #include <netinet/in.h>
|
---|
| 82 | #endif
|
---|
| 83 |
|
---|
| 84 | #include <stdlib.h>
|
---|
| 85 |
|
---|
| 86 | /*
|
---|
| 87 | $ mcookie
|
---|
| 88 | 3cb86418caef9c95cd211cbf60a1bddd
|
---|
| 89 | $
|
---|
| 90 | */
|
---|
| 91 |
|
---|
| 92 | // magic number for the file
|
---|
| 93 | static const int MagicLength = 16;
|
---|
| 94 | static const uchar magic[MagicLength] = {
|
---|
| 95 | 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
|
---|
| 96 | 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
|
---|
| 97 | };
|
---|
| 98 |
|
---|
| 99 | static bool match( const char* found, const char* target )
|
---|
| 100 | {
|
---|
| 101 | // 0 means anything, "" means empty
|
---|
| 102 | return found == 0 || qstrcmp( found, target ) == 0;
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | #if defined(Q_C_CALLBACKS)
|
---|
| 106 | extern "C" {
|
---|
| 107 | #endif
|
---|
| 108 |
|
---|
| 109 | /*
|
---|
| 110 | Yes, unfortunately, we have code here that depends on endianness.
|
---|
| 111 | The candidate is big endian (it comes from a .qm file) whereas the
|
---|
| 112 | target endianness depends on the system Qt is running on.
|
---|
| 113 | */
|
---|
| 114 | #ifdef Q_OS_TEMP
|
---|
| 115 | static int __cdecl cmp_uint32_little( const void* target, const void* candidate )
|
---|
| 116 | #else
|
---|
| 117 | static int cmp_uint32_little( const void* target, const void* candidate )
|
---|
| 118 | #endif
|
---|
| 119 | {
|
---|
| 120 | const uchar* t = (const uchar*) target;
|
---|
| 121 | const uchar* c = (const uchar*) candidate;
|
---|
| 122 | return t[3] != c[0] ? (int) t[3] - (int) c[0]
|
---|
| 123 | : t[2] != c[1] ? (int) t[2] - (int) c[1]
|
---|
| 124 | : t[1] != c[2] ? (int) t[1] - (int) c[2]
|
---|
| 125 | : (int) t[0] - (int) c[3];
|
---|
| 126 | }
|
---|
| 127 |
|
---|
| 128 | #ifdef Q_OS_TEMP
|
---|
| 129 | static int __cdecl cmp_uint32_big( const void* target, const void* candidate )
|
---|
| 130 | #else
|
---|
| 131 | static int cmp_uint32_big( const void* target, const void* candidate )
|
---|
| 132 | #endif
|
---|
| 133 | {
|
---|
| 134 | const uchar* t = (const uchar*) target;
|
---|
| 135 | const uchar* c = (const uchar*) candidate;
|
---|
| 136 | return t[0] != c[0] ? (int) t[0] - (int) c[0]
|
---|
| 137 | : t[1] != c[1] ? (int) t[1] - (int) c[1]
|
---|
| 138 | : t[2] != c[2] ? (int) t[2] - (int) c[2]
|
---|
| 139 | : (int) t[3] - (int) c[3];
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | #if defined(Q_C_CALLBACKS)
|
---|
| 143 | }
|
---|
| 144 | #endif
|
---|
| 145 |
|
---|
| 146 | static int systemWordSize = 0;
|
---|
| 147 | static bool systemBigEndian;
|
---|
| 148 |
|
---|
| 149 | static uint elfHash( const char * name )
|
---|
| 150 | {
|
---|
| 151 | const uchar *k;
|
---|
| 152 | uint h = 0;
|
---|
| 153 | uint g;
|
---|
| 154 |
|
---|
| 155 | if ( name ) {
|
---|
| 156 | k = (const uchar *) name;
|
---|
| 157 | while ( *k ) {
|
---|
| 158 | h = ( h << 4 ) + *k++;
|
---|
| 159 | if ( (g = (h & 0xf0000000)) != 0 )
|
---|
| 160 | h ^= g >> 24;
|
---|
| 161 | h &= ~g;
|
---|
| 162 | }
|
---|
| 163 | }
|
---|
| 164 | if ( !h )
|
---|
| 165 | h = 1;
|
---|
| 166 | return h;
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | extern bool qt_detectRTLLanguage();
|
---|
| 170 |
|
---|
| 171 | class QTranslatorPrivate {
|
---|
| 172 | public:
|
---|
| 173 | struct Offset {
|
---|
| 174 | Offset()
|
---|
| 175 | : h( 0 ), o( 0 ) { }
|
---|
| 176 | Offset( const QTranslatorMessage& m, int offset )
|
---|
| 177 | : h( m.hash() ), o( offset ) { }
|
---|
| 178 |
|
---|
| 179 | bool operator<( const Offset&k ) const {
|
---|
| 180 | return ( h != k.h ) ? h < k.h : o < k.o;
|
---|
| 181 | }
|
---|
| 182 | Q_DUMMY_COMPARISON_OPERATOR(QTranslatorPrivate::Offset)
|
---|
| 183 | uint h;
|
---|
| 184 | uint o;
|
---|
| 185 | };
|
---|
| 186 |
|
---|
| 187 | enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69 };
|
---|
| 188 |
|
---|
| 189 | QTranslatorPrivate() :
|
---|
| 190 | unmapPointer( 0 ), unmapLength( 0 ),
|
---|
| 191 | messageArray( 0 ), offsetArray( 0 ), contextArray( 0 )
|
---|
| 192 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 193 | , messages( 0 )
|
---|
| 194 | #endif
|
---|
| 195 | { }
|
---|
| 196 | // QTranslator must finalize this before deallocating it
|
---|
| 197 |
|
---|
| 198 | // for mmap'ed files, this is what needs to be unmapped.
|
---|
| 199 | char * unmapPointer;
|
---|
| 200 | unsigned int unmapLength;
|
---|
| 201 |
|
---|
| 202 | // for squeezed but non-file data, this is what needs to be deleted
|
---|
| 203 | QByteArray * messageArray;
|
---|
| 204 | QByteArray * offsetArray;
|
---|
| 205 | QByteArray * contextArray;
|
---|
| 206 |
|
---|
| 207 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 208 | QMap<QTranslatorMessage, void *> * messages;
|
---|
| 209 | #endif
|
---|
| 210 | };
|
---|
| 211 |
|
---|
| 212 |
|
---|
| 213 | /*!
|
---|
| 214 | \class QTranslator
|
---|
| 215 |
|
---|
| 216 | \brief The QTranslator class provides internationalization support for text
|
---|
| 217 | output.
|
---|
| 218 |
|
---|
| 219 | \ingroup i18n
|
---|
| 220 | \ingroup environment
|
---|
| 221 | \mainclass
|
---|
| 222 |
|
---|
| 223 | An object of this class contains a set of QTranslatorMessage
|
---|
| 224 | objects, each of which specifies a translation from a source
|
---|
| 225 | language to a target language. QTranslator provides functions to
|
---|
| 226 | look up translations, add new ones, remove them, load and save
|
---|
| 227 | them, etc.
|
---|
| 228 |
|
---|
| 229 | The most common use of QTranslator is to: load a translator file
|
---|
| 230 | created with \link linguist-manual.book Qt Linguist\endlink,
|
---|
| 231 | install it using QApplication::installTranslator(), and use it via
|
---|
| 232 | QObject::tr(). For example:
|
---|
| 233 |
|
---|
| 234 | \code
|
---|
| 235 | int main( int argc, char ** argv )
|
---|
| 236 | {
|
---|
| 237 | QApplication app( argc, argv );
|
---|
| 238 |
|
---|
| 239 | QTranslator translator( 0 );
|
---|
| 240 | translator.load( "french.qm", "." );
|
---|
| 241 | app.installTranslator( &translator );
|
---|
| 242 |
|
---|
| 243 | MyWidget m;
|
---|
| 244 | app.setMainWidget( &m );
|
---|
| 245 | m.show();
|
---|
| 246 |
|
---|
| 247 | return app.exec();
|
---|
| 248 | }
|
---|
| 249 | \endcode
|
---|
| 250 | Note that the translator must be created \e before the
|
---|
| 251 | application's main window.
|
---|
| 252 |
|
---|
| 253 | Most applications will never need to do anything else with this
|
---|
| 254 | class. The other functions provided by this class are useful for
|
---|
| 255 | applications that work on translator files.
|
---|
| 256 |
|
---|
| 257 | We call a translation a "messsage". For this reason, translation
|
---|
| 258 | files are sometimes referred to as "message files".
|
---|
| 259 |
|
---|
| 260 | It is possible to lookup a translation using findMessage() (as
|
---|
| 261 | tr() and QApplication::translate() do) and contains(), to insert a
|
---|
| 262 | new translation messsage using insert(), and to remove one using
|
---|
| 263 | remove().
|
---|
| 264 |
|
---|
| 265 | Translation tools often need more information than the bare source
|
---|
| 266 | text and translation, for example, context information to help
|
---|
| 267 | the translator. But end-user programs that are using translations
|
---|
| 268 | usually only need lookup. To cater for these different needs,
|
---|
| 269 | QTranslator can use stripped translator files that use the minimum
|
---|
| 270 | of memory and which support little more functionality than
|
---|
| 271 | findMessage().
|
---|
| 272 |
|
---|
| 273 | Thus, load() may not load enough information to make anything more
|
---|
| 274 | than findMessage() work. save() has an argument indicating
|
---|
| 275 | whether to save just this minimum of information or to save
|
---|
| 276 | everything.
|
---|
| 277 |
|
---|
| 278 | "Everything" means that for each translation item the following
|
---|
| 279 | information is kept:
|
---|
| 280 |
|
---|
| 281 | \list
|
---|
| 282 | \i The \e {translated text} - the return value from tr().
|
---|
| 283 | \i The input key:
|
---|
| 284 | \list
|
---|
| 285 | \i The \e {source text} - usually the argument to tr().
|
---|
| 286 | \i The \e context - usually the class name for the tr() caller.
|
---|
| 287 | \i The \e comment - a comment that helps disambiguate different uses
|
---|
| 288 | of the same text in the same context.
|
---|
| 289 | \endlist
|
---|
| 290 | \endlist
|
---|
| 291 |
|
---|
| 292 | The minimum for each item is just the information necessary for
|
---|
| 293 | findMessage() to return the right text. This may include the
|
---|
| 294 | source, context and comment, but usually it is just a hash value
|
---|
| 295 | and the translated text.
|
---|
| 296 |
|
---|
| 297 | For example, the "Cancel" in a dialog might have "Anuluj" when the
|
---|
| 298 | program runs in Polish (in this case the source text would be
|
---|
| 299 | "Cancel"). The context would (normally) be the dialog's class
|
---|
| 300 | name; there would normally be no comment, and the translated text
|
---|
| 301 | would be "Anuluj".
|
---|
| 302 |
|
---|
| 303 | But it's not always so simple. The Spanish version of a printer
|
---|
| 304 | dialog with settings for two-sided printing and binding would
|
---|
| 305 | probably require both "Activado" and "Activada" as translations
|
---|
| 306 | for "Enabled". In this case the source text would be "Enabled" in
|
---|
| 307 | both cases, and the context would be the dialog's class name, but
|
---|
| 308 | the two items would have disambiguating comments such as
|
---|
| 309 | "two-sided printing" for one and "binding" for the other. The
|
---|
| 310 | comment enables the translator to choose the appropriate gender
|
---|
| 311 | for the Spanish version, and enables Qt to distinguish between
|
---|
| 312 | translations.
|
---|
| 313 |
|
---|
| 314 | Note that when QTranslator loads a stripped file, most functions
|
---|
| 315 | do not work. The functions that do work with stripped files are
|
---|
| 316 | explicitly documented as such.
|
---|
| 317 |
|
---|
| 318 | \sa QTranslatorMessage QApplication::installTranslator()
|
---|
| 319 | QApplication::removeTranslator() QObject::tr() QApplication::translate()
|
---|
| 320 | */
|
---|
| 321 |
|
---|
| 322 | /*!
|
---|
| 323 | \enum QTranslator::SaveMode
|
---|
| 324 |
|
---|
| 325 | This enum type defines how QTranslator writes translation
|
---|
| 326 | files. There are two modes:
|
---|
| 327 |
|
---|
| 328 | \value Everything files are saved with all available information
|
---|
| 329 | \value Stripped files are saved with just enough information for
|
---|
| 330 | end-user applications
|
---|
| 331 |
|
---|
| 332 | Note that when QTranslator loads a stripped file, most functions do
|
---|
| 333 | not work. The functions that do work with stripped files are
|
---|
| 334 | explicitly documented as such.
|
---|
| 335 | */
|
---|
| 336 |
|
---|
| 337 | /*!
|
---|
| 338 | Constructs an empty message file object that is not connected to
|
---|
| 339 | any file. The object is called \a name with parent \a parent.
|
---|
| 340 | */
|
---|
| 341 |
|
---|
| 342 | QTranslator::QTranslator( QObject * parent, const char * name )
|
---|
| 343 | : QObject( parent, name )
|
---|
| 344 | {
|
---|
| 345 | d = new QTranslatorPrivate;
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 |
|
---|
| 349 | /*!
|
---|
| 350 | Destroys the object and frees any allocated resources.
|
---|
| 351 | */
|
---|
| 352 |
|
---|
| 353 | QTranslator::~QTranslator()
|
---|
| 354 | {
|
---|
| 355 | if ( qApp )
|
---|
| 356 | qApp->removeTranslator( this );
|
---|
| 357 | clear();
|
---|
| 358 | delete d;
|
---|
| 359 | }
|
---|
| 360 |
|
---|
| 361 |
|
---|
| 362 | extern bool qt_detectRTLLanguage();
|
---|
| 363 |
|
---|
| 364 | /*!
|
---|
| 365 | Loads \a filename, which may be an absolute file name or relative
|
---|
| 366 | to \a directory. The previous contents of this translator object
|
---|
| 367 | is discarded.
|
---|
| 368 |
|
---|
| 369 | If the full file name does not exist, other file names are tried
|
---|
| 370 | in the following order:
|
---|
| 371 |
|
---|
| 372 | \list 1
|
---|
| 373 | \i File name with \a suffix appended (".qm" if the \a suffix is
|
---|
| 374 | QString::null).
|
---|
| 375 | \i File name with text after a character in \a search_delimiters
|
---|
| 376 | stripped ("_." is the default for \a search_delimiters if it is
|
---|
| 377 | QString::null).
|
---|
| 378 | \i File name stripped and \a suffix appended.
|
---|
| 379 | \i File name stripped further, etc.
|
---|
| 380 | \endlist
|
---|
| 381 |
|
---|
| 382 | For example, an application running in the fr_CA locale
|
---|
| 383 | (French-speaking Canada) might call load("foo.fr_ca",
|
---|
| 384 | "/opt/foolib"). load() would then try to open the first existing
|
---|
| 385 | readable file from this list:
|
---|
| 386 |
|
---|
| 387 | \list 1
|
---|
| 388 | \i /opt/foolib/foo.fr_ca
|
---|
| 389 | \i /opt/foolib/foo.fr_ca.qm
|
---|
| 390 | \i /opt/foolib/foo.fr
|
---|
| 391 | \i /opt/foolib/foo.fr.qm
|
---|
| 392 | \i /opt/foolib/foo
|
---|
| 393 | \i /opt/foolib/foo.qm
|
---|
| 394 | \endlist
|
---|
| 395 |
|
---|
| 396 | \sa save()
|
---|
| 397 | */
|
---|
| 398 |
|
---|
| 399 | bool QTranslator::load( const QString & filename, const QString & directory,
|
---|
| 400 | const QString & search_delimiters,
|
---|
| 401 | const QString & suffix )
|
---|
| 402 | {
|
---|
| 403 | clear();
|
---|
| 404 |
|
---|
| 405 | QString prefix;
|
---|
| 406 |
|
---|
| 407 | if ( filename[0] == '/'
|
---|
[8] | 408 | #if defined(Q_WS_WIN) || defined(Q_WS_PM)
|
---|
[2] | 409 | || (filename[0] && filename[1] == ':') || filename[0] == '\\'
|
---|
| 410 | #endif
|
---|
| 411 | )
|
---|
| 412 | prefix = QString::fromLatin1( "" );
|
---|
| 413 | else
|
---|
| 414 | prefix = directory;
|
---|
| 415 |
|
---|
| 416 | if ( prefix.length() ) {
|
---|
| 417 | if ( prefix[int(prefix.length()-1)] != '/' )
|
---|
| 418 | prefix += QChar( '/' );
|
---|
| 419 | }
|
---|
| 420 |
|
---|
| 421 | QString fname = filename;
|
---|
| 422 | QString realname;
|
---|
| 423 | QString delims;
|
---|
| 424 | delims = search_delimiters.isNull() ?
|
---|
| 425 | QString::fromLatin1( "_." ) : search_delimiters;
|
---|
| 426 |
|
---|
| 427 | for ( ;; ) {
|
---|
| 428 | QFileInfo fi;
|
---|
| 429 |
|
---|
| 430 | realname = prefix + fname;
|
---|
| 431 | fi.setFile( realname );
|
---|
| 432 | if ( fi.isReadable() )
|
---|
| 433 | break;
|
---|
| 434 |
|
---|
| 435 | realname += suffix.isNull() ? QString::fromLatin1( ".qm" ) : suffix;
|
---|
| 436 | fi.setFile( realname );
|
---|
| 437 | if ( fi.isReadable() )
|
---|
| 438 | break;
|
---|
| 439 |
|
---|
| 440 | int rightmost = 0;
|
---|
| 441 | for ( int i = 0; i < (int)delims.length(); i++ ) {
|
---|
| 442 | int k = fname.findRev( delims[i] );
|
---|
| 443 | if ( k > rightmost )
|
---|
| 444 | rightmost = k;
|
---|
| 445 | }
|
---|
| 446 |
|
---|
| 447 | // no truncations? fail
|
---|
| 448 | if ( rightmost == 0 )
|
---|
| 449 | return FALSE;
|
---|
| 450 |
|
---|
| 451 | fname.truncate( rightmost );
|
---|
| 452 | }
|
---|
| 453 |
|
---|
| 454 | // realname is now the fully qualified name of a readable file.
|
---|
| 455 |
|
---|
| 456 | #if defined(QT_USE_MMAP)
|
---|
| 457 |
|
---|
| 458 | #ifndef MAP_FILE
|
---|
| 459 | #define MAP_FILE 0
|
---|
| 460 | #endif
|
---|
| 461 | #ifndef MAP_FAILED
|
---|
| 462 | #define MAP_FAILED -1
|
---|
| 463 | #endif
|
---|
| 464 |
|
---|
| 465 | int f;
|
---|
| 466 |
|
---|
| 467 | f = qt_open( QFile::encodeName(realname), O_RDONLY, 0666 );
|
---|
| 468 | if ( f < 0 ) {
|
---|
| 469 | // qDebug( "can't open %s: %s", realname.ascii(), strerror( errno ) );
|
---|
| 470 | return FALSE;
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | struct stat st;
|
---|
| 474 | if ( fstat( f, &st ) ) {
|
---|
| 475 | // qDebug( "can't stat %s: %s", realname.ascii(), strerror( errno ) );
|
---|
| 476 | return FALSE;
|
---|
| 477 | }
|
---|
| 478 | char * tmp;
|
---|
| 479 | tmp = (char*)mmap( 0, st.st_size, // any address, whole file
|
---|
| 480 | PROT_READ, // read-only memory
|
---|
| 481 | MAP_FILE | MAP_PRIVATE, // swap-backed map from file
|
---|
| 482 | f, 0 ); // from offset 0 of f
|
---|
| 483 | if ( !tmp || tmp == (char*)MAP_FAILED ) {
|
---|
| 484 | // qDebug( "can't mmap %s: %s", filename.ascii(), strerror( errno ) );
|
---|
| 485 | return FALSE;
|
---|
| 486 | }
|
---|
| 487 |
|
---|
| 488 | ::close( f );
|
---|
| 489 |
|
---|
| 490 | d->unmapPointer = tmp;
|
---|
| 491 | d->unmapLength = st.st_size;
|
---|
| 492 | #else
|
---|
| 493 | QFile f( realname );
|
---|
| 494 | if ( !f.exists() )
|
---|
| 495 | return FALSE;
|
---|
| 496 | d->unmapLength = f.size();
|
---|
| 497 | d->unmapPointer = new char[d->unmapLength];
|
---|
| 498 | bool ok = FALSE;
|
---|
| 499 | if ( f.open(IO_ReadOnly) ) {
|
---|
| 500 | ok = d->unmapLength ==
|
---|
| 501 | (uint)f.readBlock( d->unmapPointer, d->unmapLength );
|
---|
| 502 | f.close();
|
---|
| 503 | }
|
---|
| 504 | if ( !ok ) {
|
---|
| 505 | delete [] d->unmapPointer;
|
---|
| 506 | d->unmapPointer = 0;
|
---|
| 507 | return FALSE;
|
---|
| 508 | }
|
---|
| 509 | #endif
|
---|
| 510 |
|
---|
| 511 | return do_load( (const uchar *) d->unmapPointer, d->unmapLength );
|
---|
| 512 | }
|
---|
| 513 |
|
---|
| 514 | /*!
|
---|
| 515 | \overload
|
---|
| 516 | \fn bool QTranslator::load( const uchar *data, int len )
|
---|
| 517 |
|
---|
| 518 | Loads the .qm file data \a data of length \a len into the
|
---|
| 519 | translator.
|
---|
| 520 |
|
---|
| 521 | The data is not copied. The caller must be able to guarantee that \a data
|
---|
| 522 | will not be deleted or modified.
|
---|
| 523 | */
|
---|
| 524 |
|
---|
| 525 | bool QTranslator::do_load( const uchar *data, int len )
|
---|
| 526 | {
|
---|
| 527 | if ( len < MagicLength || memcmp( data, magic, MagicLength ) != 0 ) {
|
---|
| 528 | clear();
|
---|
| 529 | return FALSE;
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | QByteArray array;
|
---|
| 533 | array.setRawData( (const char *) data, len );
|
---|
| 534 | QDataStream s( array, IO_ReadOnly );
|
---|
| 535 | bool ok = TRUE;
|
---|
| 536 |
|
---|
| 537 | s.device()->at( MagicLength );
|
---|
| 538 |
|
---|
| 539 | Q_UINT8 tag = 0;
|
---|
| 540 | Q_UINT32 blockLen = 0;
|
---|
| 541 | s >> tag >> blockLen;
|
---|
| 542 | while ( tag && blockLen ) {
|
---|
| 543 | if ( (Q_UINT32) s.device()->at() + blockLen > (Q_UINT32) len ) {
|
---|
| 544 | ok = FALSE;
|
---|
| 545 | break;
|
---|
| 546 | }
|
---|
| 547 |
|
---|
| 548 | if ( tag == QTranslatorPrivate::Contexts && !d->contextArray ) {
|
---|
| 549 | d->contextArray = new QByteArray;
|
---|
| 550 | d->contextArray->setRawData( array.data() + s.device()->at(),
|
---|
| 551 | blockLen );
|
---|
| 552 | } else if ( tag == QTranslatorPrivate::Hashes && !d->offsetArray ) {
|
---|
| 553 | d->offsetArray = new QByteArray;
|
---|
| 554 | d->offsetArray->setRawData( array.data() + s.device()->at(),
|
---|
| 555 | blockLen );
|
---|
| 556 | } else if ( tag == QTranslatorPrivate::Messages && !d->messageArray ) {
|
---|
| 557 | d->messageArray = new QByteArray;
|
---|
| 558 | d->messageArray->setRawData( array.data() + s.device()->at(),
|
---|
| 559 | blockLen );
|
---|
| 560 | }
|
---|
| 561 |
|
---|
| 562 | if ( !s.device()->at(s.device()->at() + blockLen) ) {
|
---|
| 563 | ok = FALSE;
|
---|
| 564 | break;
|
---|
| 565 | }
|
---|
| 566 | tag = 0;
|
---|
| 567 | blockLen = 0;
|
---|
| 568 | if ( !s.atEnd() )
|
---|
| 569 | s >> tag >> blockLen;
|
---|
| 570 | }
|
---|
| 571 | array.resetRawData( (const char *) data, len );
|
---|
| 572 |
|
---|
| 573 | if ( qApp && qApp->translators && qApp->translators->contains(this) )
|
---|
| 574 | qApp->setReverseLayout( qt_detectRTLLanguage() );
|
---|
| 575 | return ok;
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 579 |
|
---|
| 580 | /*!
|
---|
| 581 | Saves this message file to \a filename, overwriting the previous
|
---|
| 582 | contents of \a filename. If \a mode is \c Everything (the
|
---|
| 583 | default), all the information is preserved. If \a mode is \c
|
---|
| 584 | Stripped, any information that is not necessary for findMessage()
|
---|
| 585 | is stripped away.
|
---|
| 586 |
|
---|
| 587 | \sa load()
|
---|
| 588 | */
|
---|
| 589 |
|
---|
| 590 | bool QTranslator::save( const QString & filename, SaveMode mode )
|
---|
| 591 | {
|
---|
| 592 | QFile f( filename );
|
---|
| 593 | if ( f.open( IO_WriteOnly ) ) {
|
---|
| 594 | squeeze( mode );
|
---|
| 595 |
|
---|
| 596 | QDataStream s( &f );
|
---|
| 597 | s.writeRawBytes( (const char *)magic, MagicLength );
|
---|
| 598 | Q_UINT8 tag;
|
---|
| 599 |
|
---|
| 600 | if ( d->offsetArray != 0 ) {
|
---|
| 601 | tag = (Q_UINT8) QTranslatorPrivate::Hashes;
|
---|
| 602 | Q_UINT32 oas = (Q_UINT32) d->offsetArray->size();
|
---|
| 603 | s << tag << oas;
|
---|
| 604 | s.writeRawBytes( d->offsetArray->data(), oas );
|
---|
| 605 | }
|
---|
| 606 | if ( d->messageArray != 0 ) {
|
---|
| 607 | tag = (Q_UINT8) QTranslatorPrivate::Messages;
|
---|
| 608 | Q_UINT32 mas = (Q_UINT32) d->messageArray->size();
|
---|
| 609 | s << tag << mas;
|
---|
| 610 | s.writeRawBytes( d->messageArray->data(), mas );
|
---|
| 611 | }
|
---|
| 612 | if ( d->contextArray != 0 ) {
|
---|
| 613 | tag = (Q_UINT8) QTranslatorPrivate::Contexts;
|
---|
| 614 | Q_UINT32 cas = (Q_UINT32) d->contextArray->size();
|
---|
| 615 | s << tag << cas;
|
---|
| 616 | s.writeRawBytes( d->contextArray->data(), cas );
|
---|
| 617 | }
|
---|
| 618 | return TRUE;
|
---|
| 619 | }
|
---|
| 620 | return FALSE;
|
---|
| 621 | }
|
---|
| 622 |
|
---|
| 623 | #endif
|
---|
| 624 |
|
---|
| 625 | /*!
|
---|
| 626 | Empties this translator of all contents.
|
---|
| 627 |
|
---|
| 628 | This function works with stripped translator files.
|
---|
| 629 | */
|
---|
| 630 |
|
---|
| 631 | void QTranslator::clear()
|
---|
| 632 | {
|
---|
| 633 | if ( d->unmapPointer && d->unmapLength ) {
|
---|
| 634 | #if defined(QT_USE_MMAP)
|
---|
| 635 | munmap( d->unmapPointer, d->unmapLength );
|
---|
| 636 | #else
|
---|
| 637 | delete [] d->unmapPointer;
|
---|
| 638 | #endif
|
---|
| 639 | d->unmapPointer = 0;
|
---|
| 640 | d->unmapLength = 0;
|
---|
| 641 | }
|
---|
| 642 |
|
---|
| 643 | if ( d->messageArray ) {
|
---|
| 644 | d->messageArray->resetRawData( d->messageArray->data(),
|
---|
| 645 | d->messageArray->size() );
|
---|
| 646 | delete d->messageArray;
|
---|
| 647 | d->messageArray = 0;
|
---|
| 648 | }
|
---|
| 649 | if ( d->offsetArray ) {
|
---|
| 650 | d->offsetArray->resetRawData( d->offsetArray->data(),
|
---|
| 651 | d->offsetArray->size() );
|
---|
| 652 | delete d->offsetArray;
|
---|
| 653 | d->offsetArray = 0;
|
---|
| 654 | }
|
---|
| 655 | if ( d->contextArray ) {
|
---|
| 656 | d->contextArray->resetRawData( d->contextArray->data(),
|
---|
| 657 | d->contextArray->size() );
|
---|
| 658 | delete d->contextArray;
|
---|
| 659 | d->contextArray = 0;
|
---|
| 660 | }
|
---|
| 661 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 662 | delete d->messages;
|
---|
| 663 | d->messages = 0;
|
---|
| 664 | #endif
|
---|
| 665 |
|
---|
| 666 | if ( qApp ) {
|
---|
| 667 | qApp->setReverseLayout( qt_detectRTLLanguage() );
|
---|
| 668 |
|
---|
| 669 | QWidgetList *list = QApplication::topLevelWidgets();
|
---|
| 670 | QWidgetListIt it( *list );
|
---|
| 671 | QWidget *w;
|
---|
| 672 | while ( ( w=it.current() ) != 0 ) {
|
---|
| 673 | ++it;
|
---|
| 674 | if (!w->isDesktop())
|
---|
| 675 | qApp->postEvent( w, new QEvent( QEvent::LanguageChange ) );
|
---|
| 676 | }
|
---|
| 677 | delete list;
|
---|
| 678 | }
|
---|
| 679 | }
|
---|
| 680 |
|
---|
| 681 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 682 |
|
---|
| 683 | /*!
|
---|
| 684 | Converts this message file to the compact format used to store
|
---|
| 685 | message files on disk.
|
---|
| 686 |
|
---|
| 687 | You should never need to call this directly; save() and other
|
---|
| 688 | functions call it as necessary. \a mode is for internal use.
|
---|
| 689 |
|
---|
| 690 | \sa save() unsqueeze()
|
---|
| 691 | */
|
---|
| 692 |
|
---|
| 693 | void QTranslator::squeeze( SaveMode mode )
|
---|
| 694 | {
|
---|
| 695 | if ( !d->messages ) {
|
---|
| 696 | if ( mode == Stripped )
|
---|
| 697 | unsqueeze();
|
---|
| 698 | else
|
---|
| 699 | return;
|
---|
| 700 | }
|
---|
| 701 |
|
---|
| 702 | QMap<QTranslatorMessage, void *> * messages = d->messages;
|
---|
| 703 |
|
---|
| 704 | d->messages = 0;
|
---|
| 705 | clear();
|
---|
| 706 |
|
---|
| 707 | d->messageArray = new QByteArray;
|
---|
| 708 | d->offsetArray = new QByteArray;
|
---|
| 709 |
|
---|
| 710 | QMap<QTranslatorPrivate::Offset, void *> offsets;
|
---|
| 711 |
|
---|
| 712 | QDataStream ms( *d->messageArray, IO_WriteOnly );
|
---|
| 713 | QMap<QTranslatorMessage, void *>::Iterator it = messages->begin(), next;
|
---|
| 714 | int cpPrev = 0, cpNext = 0;
|
---|
| 715 | for ( it = messages->begin(); it != messages->end(); ++it ) {
|
---|
| 716 | cpPrev = cpNext;
|
---|
| 717 | next = it;
|
---|
| 718 | ++next;
|
---|
| 719 | if ( next == messages->end() )
|
---|
| 720 | cpNext = 0;
|
---|
| 721 | else
|
---|
| 722 | cpNext = (int) it.key().commonPrefix( next.key() );
|
---|
| 723 | offsets.replace( QTranslatorPrivate::Offset(it.key(),
|
---|
| 724 | ms.device()->at()), (void*)0 );
|
---|
| 725 | it.key().write( ms, mode == Stripped,
|
---|
| 726 | (QTranslatorMessage::Prefix) QMAX(cpPrev, cpNext + 1) );
|
---|
| 727 | }
|
---|
| 728 |
|
---|
| 729 | d->offsetArray->resize( 0 );
|
---|
| 730 | QMap<QTranslatorPrivate::Offset, void *>::Iterator offset;
|
---|
| 731 | offset = offsets.begin();
|
---|
| 732 | QDataStream ds( *d->offsetArray, IO_WriteOnly );
|
---|
| 733 | while ( offset != offsets.end() ) {
|
---|
| 734 | QTranslatorPrivate::Offset k = offset.key();
|
---|
| 735 | ++offset;
|
---|
| 736 | ds << (Q_UINT32)k.h << (Q_UINT32)k.o;
|
---|
| 737 | }
|
---|
| 738 |
|
---|
| 739 | if ( mode == Stripped ) {
|
---|
| 740 | QAsciiDict<int> contextSet( 1511 );
|
---|
| 741 | int baudelaire;
|
---|
| 742 |
|
---|
| 743 | for ( it = messages->begin(); it != messages->end(); ++it )
|
---|
| 744 | contextSet.replace( it.key().context(), &baudelaire );
|
---|
| 745 |
|
---|
| 746 | Q_UINT16 hTableSize;
|
---|
| 747 | if ( contextSet.count() < 200 )
|
---|
| 748 | hTableSize = ( contextSet.count() < 60 ) ? 151 : 503;
|
---|
| 749 | else if ( contextSet.count() < 2500 )
|
---|
| 750 | hTableSize = ( contextSet.count() < 750 ) ? 1511 : 5003;
|
---|
| 751 | else
|
---|
| 752 | hTableSize = 15013;
|
---|
| 753 |
|
---|
| 754 | QIntDict<char> hDict( hTableSize );
|
---|
| 755 | QAsciiDictIterator<int> c = contextSet;
|
---|
| 756 | while ( c.current() != 0 ) {
|
---|
| 757 | hDict.insert( (long) (elfHash(c.currentKey()) % hTableSize),
|
---|
| 758 | c.currentKey() );
|
---|
| 759 | ++c;
|
---|
| 760 | }
|
---|
| 761 |
|
---|
| 762 | /*
|
---|
| 763 | The contexts found in this translator are stored in a hash
|
---|
| 764 | table to provide fast lookup. The context array has the
|
---|
| 765 | following format:
|
---|
| 766 |
|
---|
| 767 | Q_UINT16 hTableSize;
|
---|
| 768 | Q_UINT16 hTable[hTableSize];
|
---|
| 769 | Q_UINT8 contextPool[...];
|
---|
| 770 |
|
---|
| 771 | The context pool stores the contexts as Pascal strings:
|
---|
| 772 |
|
---|
| 773 | Q_UINT8 len;
|
---|
| 774 | Q_UINT8 data[len];
|
---|
| 775 |
|
---|
| 776 | Let's consider the look-up of context "FunnyDialog". A
|
---|
| 777 | hash value between 0 and hTableSize - 1 is computed, say h.
|
---|
| 778 | If hTable[h] is 0, "FunnyDialog" is not covered by this
|
---|
| 779 | translator. Else, we check in the contextPool at offset
|
---|
| 780 | 2 * hTable[h] to see if "FunnyDialog" is one of the
|
---|
| 781 | contexts stored there, until we find it or we meet the
|
---|
| 782 | empty string.
|
---|
| 783 | */
|
---|
| 784 | d->contextArray = new QByteArray;
|
---|
| 785 | d->contextArray->resize( 2 + (hTableSize << 1) );
|
---|
| 786 | QDataStream t( *d->contextArray, IO_WriteOnly );
|
---|
| 787 | Q_UINT16 *hTable = new Q_UINT16[hTableSize];
|
---|
| 788 | memset( hTable, 0, hTableSize * sizeof(Q_UINT16) );
|
---|
| 789 |
|
---|
| 790 | t << hTableSize;
|
---|
| 791 | t.device()->at( 2 + (hTableSize << 1) );
|
---|
| 792 | t << (Q_UINT16) 0; // the entry at offset 0 cannot be used
|
---|
| 793 | uint upto = 2;
|
---|
| 794 |
|
---|
| 795 | for ( int i = 0; i < hTableSize; i++ ) {
|
---|
| 796 | const char *con = hDict.find( i );
|
---|
| 797 | if ( con == 0 ) {
|
---|
| 798 | hTable[i] = 0;
|
---|
| 799 | } else {
|
---|
| 800 | hTable[i] = (Q_UINT16) ( upto >> 1 );
|
---|
| 801 | do {
|
---|
| 802 | uint len = (uint) qstrlen( con );
|
---|
| 803 | len = QMIN( len, 255 );
|
---|
| 804 | t << (Q_UINT8) len;
|
---|
| 805 | t.writeRawBytes( con, len );
|
---|
| 806 | upto += 1 + len;
|
---|
| 807 | hDict.remove( i );
|
---|
| 808 | } while ( (con = hDict.find(i)) != 0 );
|
---|
| 809 | do {
|
---|
| 810 | t << (Q_UINT8) 0; // empty string (at least one)
|
---|
| 811 | upto++;
|
---|
| 812 | } while ( (upto & 0x1) != 0 ); // offsets have to be even
|
---|
| 813 | }
|
---|
| 814 | }
|
---|
| 815 | t.device()->at( 2 );
|
---|
| 816 | for ( int j = 0; j < hTableSize; j++ )
|
---|
| 817 | t << hTable[j];
|
---|
| 818 | delete [] hTable;
|
---|
| 819 |
|
---|
| 820 | if ( upto > 131072 ) {
|
---|
| 821 | qWarning( "QTranslator::squeeze: Too many contexts" );
|
---|
| 822 | delete d->contextArray;
|
---|
| 823 | d->contextArray = 0;
|
---|
| 824 | }
|
---|
| 825 | }
|
---|
| 826 | delete messages;
|
---|
| 827 | }
|
---|
| 828 |
|
---|
| 829 |
|
---|
| 830 | /*!
|
---|
| 831 | Converts this message file into an easily modifiable data
|
---|
| 832 | structure, less compact than the format used in the files.
|
---|
| 833 |
|
---|
| 834 | You should never need to call this function; it is called by
|
---|
| 835 | insert() and friends as necessary.
|
---|
| 836 |
|
---|
| 837 | \sa squeeze()
|
---|
| 838 | */
|
---|
| 839 |
|
---|
| 840 | void QTranslator::unsqueeze()
|
---|
| 841 | {
|
---|
| 842 | if ( d->messages )
|
---|
| 843 | return;
|
---|
| 844 |
|
---|
| 845 | d->messages = new QMap<QTranslatorMessage, void *>;
|
---|
| 846 | if ( !d->messageArray )
|
---|
| 847 | return;
|
---|
| 848 |
|
---|
| 849 | QDataStream s( *d->messageArray, IO_ReadOnly );
|
---|
| 850 | for ( ;; ) {
|
---|
| 851 | QTranslatorMessage m( s );
|
---|
| 852 | if ( m.hash() == 0 )
|
---|
| 853 | break;
|
---|
| 854 | d->messages->insert( m, (void *) 0 );
|
---|
| 855 | }
|
---|
| 856 | }
|
---|
| 857 |
|
---|
| 858 |
|
---|
| 859 | /*!
|
---|
| 860 | Returns TRUE if this message file contains a message with the key
|
---|
| 861 | (\a context, \a sourceText, \a comment); otherwise returns FALSE.
|
---|
| 862 |
|
---|
| 863 | This function works with stripped translator files.
|
---|
| 864 |
|
---|
| 865 | (This is is a one-liner that calls findMessage().)
|
---|
| 866 | */
|
---|
| 867 |
|
---|
| 868 | bool QTranslator::contains( const char* context, const char* sourceText,
|
---|
| 869 | const char* comment ) const
|
---|
| 870 | {
|
---|
| 871 | return !findMessage( context, sourceText, comment ).translation().isNull();
|
---|
| 872 | }
|
---|
| 873 |
|
---|
| 874 |
|
---|
| 875 | /*!
|
---|
| 876 | Inserts \a message into this message file.
|
---|
| 877 |
|
---|
| 878 | This function does \e not work with stripped translator files. It
|
---|
| 879 | may appear to, but that is not dependable.
|
---|
| 880 |
|
---|
| 881 | \sa remove()
|
---|
| 882 | */
|
---|
| 883 |
|
---|
| 884 | void QTranslator::insert( const QTranslatorMessage& message )
|
---|
| 885 | {
|
---|
| 886 | unsqueeze();
|
---|
| 887 | d->messages->remove( message ); // safer
|
---|
| 888 | d->messages->insert( message, (void *) 0 );
|
---|
| 889 | }
|
---|
| 890 |
|
---|
| 891 |
|
---|
| 892 | /*!
|
---|
| 893 | \fn void QTranslator::insert( const char *, const char *, const QString & )
|
---|
| 894 | \overload
|
---|
| 895 | \obsolete
|
---|
| 896 | */
|
---|
| 897 |
|
---|
| 898 | /*!
|
---|
| 899 | Removes \a message from this translator.
|
---|
| 900 |
|
---|
| 901 | This function works with stripped translator files.
|
---|
| 902 |
|
---|
| 903 | \sa insert()
|
---|
| 904 | */
|
---|
| 905 |
|
---|
| 906 | void QTranslator::remove( const QTranslatorMessage& message )
|
---|
| 907 | {
|
---|
| 908 | unsqueeze();
|
---|
| 909 | d->messages->remove( message );
|
---|
| 910 | }
|
---|
| 911 |
|
---|
| 912 |
|
---|
| 913 | /*!
|
---|
| 914 | \fn void QTranslator::remove( const char *, const char * )
|
---|
| 915 | \overload
|
---|
| 916 | \obsolete
|
---|
| 917 |
|
---|
| 918 | Removes the translation associated to the key (\a context, \a sourceText,
|
---|
| 919 | "") from this translator.
|
---|
| 920 | */
|
---|
| 921 | #endif
|
---|
| 922 |
|
---|
| 923 | /*!
|
---|
| 924 | \fn QString QTranslator::find( const char*, const char*, const char* ) const
|
---|
| 925 | \obsolete
|
---|
| 926 |
|
---|
| 927 | Please use findMessage() instead.
|
---|
| 928 |
|
---|
| 929 | Returns the translation for the key (\a context, \a sourceText,
|
---|
| 930 | \a comment) or QString::null if there is none in this translator.
|
---|
| 931 | */
|
---|
| 932 |
|
---|
| 933 | /*! Returns the QTranslatorMessage for the key
|
---|
| 934 | (\a context, \a sourceText, \a comment). If none is found,
|
---|
| 935 | also tries (\a context, \a sourceText, "").
|
---|
| 936 | */
|
---|
| 937 |
|
---|
| 938 | QTranslatorMessage QTranslator::findMessage( const char* context,
|
---|
| 939 | const char* sourceText,
|
---|
| 940 | const char* comment ) const
|
---|
| 941 | {
|
---|
| 942 | if ( context == 0 )
|
---|
| 943 | context = "";
|
---|
| 944 | if ( sourceText == 0 )
|
---|
| 945 | sourceText = "";
|
---|
| 946 | if ( comment == 0 )
|
---|
| 947 | comment = "";
|
---|
| 948 |
|
---|
| 949 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 950 | if ( d->messages ) {
|
---|
| 951 | QMap<QTranslatorMessage, void *>::ConstIterator it;
|
---|
| 952 |
|
---|
| 953 | it = d->messages->find( QTranslatorMessage(context, sourceText,
|
---|
| 954 | comment) );
|
---|
| 955 | if ( it != d->messages->end() )
|
---|
| 956 | return it.key();
|
---|
| 957 |
|
---|
| 958 | if ( comment[0] ) {
|
---|
| 959 | it = d->messages->find( QTranslatorMessage(context, sourceText,
|
---|
| 960 | "") );
|
---|
| 961 | if ( it != d->messages->end() )
|
---|
| 962 | return it.key();
|
---|
| 963 | }
|
---|
| 964 | return QTranslatorMessage();
|
---|
| 965 | }
|
---|
| 966 | #endif
|
---|
| 967 |
|
---|
| 968 | if ( !d->offsetArray )
|
---|
| 969 | return QTranslatorMessage();
|
---|
| 970 |
|
---|
| 971 | /*
|
---|
| 972 | Check if the context belongs to this QTranslator. If many translators are
|
---|
| 973 | installed, this step is necessary.
|
---|
| 974 | */
|
---|
| 975 | if ( d->contextArray ) {
|
---|
| 976 | Q_UINT16 hTableSize = 0;
|
---|
| 977 | QDataStream t( *d->contextArray, IO_ReadOnly );
|
---|
| 978 | t >> hTableSize;
|
---|
| 979 | uint g = elfHash( context ) % hTableSize;
|
---|
| 980 | t.device()->at( 2 + (g << 1) );
|
---|
| 981 | Q_UINT16 off;
|
---|
| 982 | t >> off;
|
---|
| 983 | if ( off == 0 )
|
---|
| 984 | return QTranslatorMessage();
|
---|
| 985 | t.device()->at( 2 + (hTableSize << 1) + (off << 1) );
|
---|
| 986 |
|
---|
| 987 | Q_UINT8 len;
|
---|
| 988 | char con[256];
|
---|
| 989 | for ( ;; ) {
|
---|
| 990 | t >> len;
|
---|
| 991 | if ( len == 0 )
|
---|
| 992 | return QTranslatorMessage();
|
---|
| 993 | t.readRawBytes( con, len );
|
---|
| 994 | con[len] = '\0';
|
---|
| 995 | if ( qstrcmp(con, context) == 0 )
|
---|
| 996 | break;
|
---|
| 997 | }
|
---|
| 998 | }
|
---|
| 999 |
|
---|
| 1000 | size_t numItems = d->offsetArray->size() / ( 2 * sizeof(Q_UINT32) );
|
---|
| 1001 | if ( !numItems )
|
---|
| 1002 | return QTranslatorMessage();
|
---|
| 1003 |
|
---|
| 1004 | if ( systemWordSize == 0 )
|
---|
| 1005 | qSysInfo( &systemWordSize, &systemBigEndian );
|
---|
| 1006 |
|
---|
| 1007 | for ( ;; ) {
|
---|
| 1008 | Q_UINT32 h = elfHash( QCString(sourceText) + comment );
|
---|
| 1009 |
|
---|
| 1010 | char *r = (char *) bsearch( &h, d->offsetArray->data(), numItems,
|
---|
| 1011 | 2 * sizeof(Q_UINT32),
|
---|
| 1012 | systemBigEndian ? cmp_uint32_big
|
---|
| 1013 | : cmp_uint32_little );
|
---|
| 1014 | if ( r != 0 ) {
|
---|
| 1015 | // go back on equal key
|
---|
| 1016 | while ( r != d->offsetArray->data() &&
|
---|
| 1017 | cmp_uint32_big(r - 8, r) == 0 )
|
---|
| 1018 | r -= 8;
|
---|
| 1019 |
|
---|
| 1020 | QDataStream s( *d->offsetArray, IO_ReadOnly );
|
---|
| 1021 | s.device()->at( r - d->offsetArray->data() );
|
---|
| 1022 |
|
---|
| 1023 | Q_UINT32 rh, ro;
|
---|
| 1024 | s >> rh >> ro;
|
---|
| 1025 |
|
---|
| 1026 | QDataStream ms( *d->messageArray, IO_ReadOnly );
|
---|
| 1027 | while ( rh == h ) {
|
---|
| 1028 | ms.device()->at( ro );
|
---|
| 1029 | QTranslatorMessage m( ms );
|
---|
| 1030 | if ( match(m.context(), context)
|
---|
| 1031 | && match(m.sourceText(), sourceText)
|
---|
| 1032 | && match(m.comment(), comment) )
|
---|
| 1033 | return m;
|
---|
| 1034 | if ( s.atEnd() )
|
---|
| 1035 | break;
|
---|
| 1036 | s >> rh >> ro;
|
---|
| 1037 | }
|
---|
| 1038 | }
|
---|
| 1039 | if ( !comment[0] )
|
---|
| 1040 | break;
|
---|
| 1041 | comment = "";
|
---|
| 1042 | }
|
---|
| 1043 | return QTranslatorMessage();
|
---|
| 1044 | }
|
---|
| 1045 |
|
---|
| 1046 | /*!
|
---|
| 1047 | Returns TRUE if this translator is empty, otherwise returns FALSE.
|
---|
| 1048 | This function works with stripped and unstripped translation files.
|
---|
| 1049 | */
|
---|
| 1050 | bool QTranslator::isEmpty() const
|
---|
| 1051 | {
|
---|
| 1052 | return !( d->unmapPointer || d->unmapLength || d->messageArray ||
|
---|
| 1053 | d->offsetArray || d->contextArray ||
|
---|
| 1054 | (d->messages && d->messages->count())
|
---|
| 1055 | );
|
---|
| 1056 | }
|
---|
| 1057 |
|
---|
| 1058 |
|
---|
| 1059 | #ifndef QT_NO_TRANSLATION_BUILDER
|
---|
| 1060 |
|
---|
| 1061 | /*!
|
---|
| 1062 | Returns a list of the messages in the translator. This function is
|
---|
| 1063 | rather slow. Because it is seldom called, it's optimized for
|
---|
| 1064 | simplicity and small size, rather than speed.
|
---|
| 1065 |
|
---|
| 1066 | If you want to iterate over the list, you should iterate over a
|
---|
| 1067 | copy, e.g.
|
---|
| 1068 | \code
|
---|
| 1069 | QValueList<QTranslatorMessage> list = myTranslator.messages();
|
---|
| 1070 | QValueList<QTranslatorMessage>::Iterator it = list.begin();
|
---|
| 1071 | while ( it != list.end() ) {
|
---|
| 1072 | process_message( *it );
|
---|
| 1073 | ++it;
|
---|
| 1074 | }
|
---|
| 1075 | \endcode
|
---|
| 1076 | */
|
---|
| 1077 |
|
---|
| 1078 | QValueList<QTranslatorMessage> QTranslator::messages() const
|
---|
| 1079 | {
|
---|
| 1080 | ((QTranslator *) this)->unsqueeze();
|
---|
| 1081 | return d->messages->keys();
|
---|
| 1082 | }
|
---|
| 1083 |
|
---|
| 1084 | #endif
|
---|
| 1085 |
|
---|
| 1086 | /*!
|
---|
| 1087 | \class QTranslatorMessage
|
---|
| 1088 |
|
---|
| 1089 | \brief The QTranslatorMessage class contains a translator message and its
|
---|
| 1090 | properties.
|
---|
| 1091 |
|
---|
| 1092 | \ingroup i18n
|
---|
| 1093 | \ingroup environment
|
---|
| 1094 |
|
---|
| 1095 | This class is of no interest to most applications. It is useful
|
---|
| 1096 | for translation tools such as \link linguist-manual.book Qt
|
---|
| 1097 | Linguist\endlink. It is provided simply to make the API complete
|
---|
| 1098 | and regular.
|
---|
| 1099 |
|
---|
| 1100 | For a QTranslator object, a lookup key is a triple (\e context, \e
|
---|
| 1101 | {source text}, \e comment) that uniquely identifies a message. An
|
---|
| 1102 | extended key is a quadruple (\e hash, \e context, \e {source
|
---|
| 1103 | text}, \e comment), where \e hash is computed from the source text
|
---|
| 1104 | and the comment. Unless you plan to read and write messages
|
---|
| 1105 | yourself, you need not worry about the hash value.
|
---|
| 1106 |
|
---|
| 1107 | QTranslatorMessage stores this triple or quadruple and the relevant
|
---|
| 1108 | translation if there is any.
|
---|
| 1109 |
|
---|
| 1110 | \sa QTranslator
|
---|
| 1111 | */
|
---|
| 1112 |
|
---|
| 1113 | /*!
|
---|
| 1114 | Constructs a translator message with the extended key (0, 0, 0, 0)
|
---|
| 1115 | and QString::null as translation.
|
---|
| 1116 | */
|
---|
| 1117 |
|
---|
| 1118 | QTranslatorMessage::QTranslatorMessage()
|
---|
| 1119 | : h( 0 ), cx( 0 ), st( 0 ), cm( 0 )
|
---|
| 1120 | {
|
---|
| 1121 | }
|
---|
| 1122 |
|
---|
| 1123 |
|
---|
| 1124 | /*!
|
---|
| 1125 | Constructs an translator message with the extended key (\e h, \a
|
---|
| 1126 | context, \a sourceText, \a comment), where \e h is computed from
|
---|
| 1127 | \a sourceText and \a comment, and possibly with a \a translation.
|
---|
| 1128 | */
|
---|
| 1129 |
|
---|
| 1130 | QTranslatorMessage::QTranslatorMessage( const char * context,
|
---|
| 1131 | const char * sourceText,
|
---|
| 1132 | const char * comment,
|
---|
| 1133 | const QString& translation )
|
---|
| 1134 | : cx( context ), st( sourceText ), cm( comment ), tn( translation )
|
---|
| 1135 | {
|
---|
| 1136 | // 0 means we don't know, "" means empty
|
---|
| 1137 | if ( cx == (const char*)0 )
|
---|
| 1138 | cx = "";
|
---|
| 1139 | if ( st == (const char*)0 )
|
---|
| 1140 | st = "";
|
---|
| 1141 | if ( cm == (const char*)0 )
|
---|
| 1142 | cm = "";
|
---|
| 1143 | h = elfHash( st + cm );
|
---|
| 1144 | }
|
---|
| 1145 |
|
---|
| 1146 |
|
---|
| 1147 | /*!
|
---|
| 1148 | Constructs a translator message read from the \a stream. The
|
---|
| 1149 | resulting message may have any combination of content.
|
---|
| 1150 |
|
---|
| 1151 | \sa QTranslator::save()
|
---|
| 1152 | */
|
---|
| 1153 |
|
---|
| 1154 | QTranslatorMessage::QTranslatorMessage( QDataStream & stream )
|
---|
| 1155 | : cx( 0 ), st( 0 ), cm( 0 )
|
---|
| 1156 | {
|
---|
| 1157 | QString str16;
|
---|
| 1158 | char tag;
|
---|
| 1159 | Q_UINT8 obs1;
|
---|
| 1160 |
|
---|
| 1161 | for ( ;; ) {
|
---|
| 1162 | tag = 0;
|
---|
| 1163 | if ( !stream.atEnd() )
|
---|
| 1164 | stream.readRawBytes( &tag, 1 );
|
---|
| 1165 | switch( (Tag)tag ) {
|
---|
| 1166 | case Tag_End:
|
---|
| 1167 | if ( h == 0 )
|
---|
| 1168 | h = elfHash( st + cm );
|
---|
| 1169 | return;
|
---|
| 1170 | case Tag_SourceText16: // obsolete
|
---|
| 1171 | stream >> str16;
|
---|
| 1172 | st = str16.latin1();
|
---|
| 1173 | break;
|
---|
| 1174 | case Tag_Translation:
|
---|
| 1175 | stream >> tn;
|
---|
| 1176 | break;
|
---|
| 1177 | case Tag_Context16: // obsolete
|
---|
| 1178 | stream >> str16;
|
---|
| 1179 | cx = str16.latin1();
|
---|
| 1180 | break;
|
---|
| 1181 | case Tag_Hash:
|
---|
| 1182 | stream >> h;
|
---|
| 1183 | break;
|
---|
| 1184 | case Tag_SourceText:
|
---|
| 1185 | stream >> st;
|
---|
| 1186 | break;
|
---|
| 1187 | case Tag_Context:
|
---|
| 1188 | stream >> cx;
|
---|
| 1189 | if ( cx == "" ) // for compatibility
|
---|
| 1190 | cx = 0;
|
---|
| 1191 | break;
|
---|
| 1192 | case Tag_Comment:
|
---|
| 1193 | stream >> cm;
|
---|
| 1194 | break;
|
---|
| 1195 | case Tag_Obsolete1: // obsolete
|
---|
| 1196 | stream >> obs1;
|
---|
| 1197 | break;
|
---|
| 1198 | default:
|
---|
| 1199 | h = 0;
|
---|
| 1200 | st = 0;
|
---|
| 1201 | cx = 0;
|
---|
| 1202 | cm = 0;
|
---|
| 1203 | tn = QString::null;
|
---|
| 1204 | return;
|
---|
| 1205 | }
|
---|
| 1206 | }
|
---|
| 1207 | }
|
---|
| 1208 |
|
---|
| 1209 |
|
---|
| 1210 | /*!
|
---|
| 1211 | Constructs a copy of translator message \a m.
|
---|
| 1212 | */
|
---|
| 1213 |
|
---|
| 1214 | QTranslatorMessage::QTranslatorMessage( const QTranslatorMessage & m )
|
---|
| 1215 | : cx( m.cx ), st( m.st ), cm( m.cm ), tn( m.tn )
|
---|
| 1216 | {
|
---|
| 1217 | h = m.h;
|
---|
| 1218 | }
|
---|
| 1219 |
|
---|
| 1220 |
|
---|
| 1221 | /*!
|
---|
| 1222 | Assigns message \a m to this translator message and returns a
|
---|
| 1223 | reference to this translator message.
|
---|
| 1224 | */
|
---|
| 1225 |
|
---|
| 1226 | QTranslatorMessage & QTranslatorMessage::operator=(
|
---|
| 1227 | const QTranslatorMessage & m )
|
---|
| 1228 | {
|
---|
| 1229 | h = m.h;
|
---|
| 1230 | cx = m.cx;
|
---|
| 1231 | st = m.st;
|
---|
| 1232 | cm = m.cm;
|
---|
| 1233 | tn = m.tn;
|
---|
| 1234 | return *this;
|
---|
| 1235 | }
|
---|
| 1236 |
|
---|
| 1237 |
|
---|
| 1238 | /*!
|
---|
| 1239 | \fn uint QTranslatorMessage::hash() const
|
---|
| 1240 |
|
---|
| 1241 | Returns the hash value used internally to represent the lookup
|
---|
| 1242 | key. This value is zero only if this translator message was
|
---|
| 1243 | constructed from a stream containing invalid data.
|
---|
| 1244 |
|
---|
| 1245 | The hashing function is unspecified, but it will remain unchanged
|
---|
| 1246 | in future versions of Qt.
|
---|
| 1247 | */
|
---|
| 1248 |
|
---|
| 1249 | /*!
|
---|
| 1250 | \fn const char *QTranslatorMessage::context() const
|
---|
| 1251 |
|
---|
| 1252 | Returns the context for this message (e.g. "MyDialog").
|
---|
| 1253 | */
|
---|
| 1254 |
|
---|
| 1255 | /*!
|
---|
| 1256 | \fn const char *QTranslatorMessage::sourceText() const
|
---|
| 1257 |
|
---|
| 1258 | Returns the source text of this message (e.g. "&Save").
|
---|
| 1259 | */
|
---|
| 1260 |
|
---|
| 1261 | /*!
|
---|
| 1262 | \fn const char *QTranslatorMessage::comment() const
|
---|
| 1263 |
|
---|
| 1264 | Returns the comment for this message (e.g. "File|Save").
|
---|
| 1265 | */
|
---|
| 1266 |
|
---|
| 1267 | /*!
|
---|
| 1268 | \fn void QTranslatorMessage::setTranslation( const QString & translation )
|
---|
| 1269 |
|
---|
| 1270 | Sets the translation of the source text to \a translation.
|
---|
| 1271 |
|
---|
| 1272 | \sa translation()
|
---|
| 1273 | */
|
---|
| 1274 |
|
---|
| 1275 | /*!
|
---|
| 1276 | \fn QString QTranslatorMessage::translation() const
|
---|
| 1277 |
|
---|
| 1278 | Returns the translation of the source text (e.g., "&Sauvegarder").
|
---|
| 1279 |
|
---|
| 1280 | \sa setTranslation()
|
---|
| 1281 | */
|
---|
| 1282 |
|
---|
| 1283 | /*!
|
---|
| 1284 | \enum QTranslatorMessage::Prefix
|
---|
| 1285 |
|
---|
| 1286 | Let (\e h, \e c, \e s, \e m) be the extended key. The possible
|
---|
| 1287 | prefixes are
|
---|
| 1288 |
|
---|
| 1289 | \value NoPrefix no prefix
|
---|
| 1290 | \value Hash only (\e h)
|
---|
| 1291 | \value HashContext only (\e h, \e c)
|
---|
| 1292 | \value HashContextSourceText only (\e h, \e c, \e s)
|
---|
| 1293 | \value HashContextSourceTextComment the whole extended key, (\e
|
---|
| 1294 | h, \e c, \e s, \e m)
|
---|
| 1295 |
|
---|
| 1296 | \sa write() commonPrefix()
|
---|
| 1297 | */
|
---|
| 1298 |
|
---|
| 1299 | /*!
|
---|
| 1300 | Writes this translator message to the \a stream. If \a strip is
|
---|
| 1301 | FALSE (the default), all the information in the message is
|
---|
| 1302 | written. If \a strip is TRUE, only the part of the extended key
|
---|
| 1303 | specified by \a prefix is written with the translation (\c
|
---|
| 1304 | HashContextSourceTextComment by default).
|
---|
| 1305 |
|
---|
| 1306 | \sa commonPrefix()
|
---|
| 1307 | */
|
---|
| 1308 |
|
---|
| 1309 | void QTranslatorMessage::write( QDataStream & stream, bool strip,
|
---|
| 1310 | Prefix prefix ) const
|
---|
| 1311 | {
|
---|
| 1312 | char tag;
|
---|
| 1313 |
|
---|
| 1314 | tag = (char)Tag_Translation;
|
---|
| 1315 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1316 | stream << tn;
|
---|
| 1317 |
|
---|
| 1318 | bool mustWriteHash = TRUE;
|
---|
| 1319 | if ( !strip )
|
---|
| 1320 | prefix = HashContextSourceTextComment;
|
---|
| 1321 |
|
---|
| 1322 | switch ( prefix ) {
|
---|
| 1323 | case HashContextSourceTextComment:
|
---|
| 1324 | tag = (char)Tag_Comment;
|
---|
| 1325 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1326 | stream << cm;
|
---|
| 1327 | // fall through
|
---|
| 1328 | case HashContextSourceText:
|
---|
| 1329 | tag = (char)Tag_SourceText;
|
---|
| 1330 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1331 | stream << st;
|
---|
| 1332 | // fall through
|
---|
| 1333 | case HashContext:
|
---|
| 1334 | tag = (char)Tag_Context;
|
---|
| 1335 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1336 | stream << cx;
|
---|
| 1337 | // fall through
|
---|
| 1338 | default:
|
---|
| 1339 | if ( mustWriteHash ) {
|
---|
| 1340 | tag = (char)Tag_Hash;
|
---|
| 1341 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1342 | stream << h;
|
---|
| 1343 | }
|
---|
| 1344 | }
|
---|
| 1345 |
|
---|
| 1346 | tag = (char)Tag_End;
|
---|
| 1347 | stream.writeRawBytes( &tag, 1 );
|
---|
| 1348 | }
|
---|
| 1349 |
|
---|
| 1350 |
|
---|
| 1351 | /*!
|
---|
| 1352 | Returns the widest lookup prefix that is common to this translator
|
---|
| 1353 | message and to message \a m.
|
---|
| 1354 |
|
---|
| 1355 | For example, if the extended key is for this message is (71,
|
---|
| 1356 | "PrintDialog", "Yes", "Print?") and that for \a m is (71,
|
---|
| 1357 | "PrintDialog", "No", "Print?"), this function returns \c
|
---|
| 1358 | HashContext.
|
---|
| 1359 |
|
---|
| 1360 | \sa write()
|
---|
| 1361 | */
|
---|
| 1362 |
|
---|
| 1363 | QTranslatorMessage::Prefix QTranslatorMessage::commonPrefix(
|
---|
| 1364 | const QTranslatorMessage& m ) const
|
---|
| 1365 | {
|
---|
| 1366 | if ( h != m.h )
|
---|
| 1367 | return NoPrefix;
|
---|
| 1368 | if ( cx != m.cx )
|
---|
| 1369 | return Hash;
|
---|
| 1370 | if ( st != m.st )
|
---|
| 1371 | return HashContext;
|
---|
| 1372 | if ( cm != m.cm )
|
---|
| 1373 | return HashContextSourceText;
|
---|
| 1374 | return HashContextSourceTextComment;
|
---|
| 1375 | }
|
---|
| 1376 |
|
---|
| 1377 |
|
---|
| 1378 | /*!
|
---|
| 1379 | Returns TRUE if the extended key of this object is equal to that of
|
---|
| 1380 | \a m; otherwise returns FALSE.
|
---|
| 1381 | */
|
---|
| 1382 |
|
---|
| 1383 | bool QTranslatorMessage::operator==( const QTranslatorMessage& m ) const
|
---|
| 1384 | {
|
---|
| 1385 | return h == m.h && cx == m.cx && st == m.st && cm == m.cm;
|
---|
| 1386 | }
|
---|
| 1387 |
|
---|
| 1388 |
|
---|
| 1389 | /*!
|
---|
| 1390 | \fn bool QTranslatorMessage::operator!=( const QTranslatorMessage& m ) const
|
---|
| 1391 |
|
---|
| 1392 | Returns TRUE if the extended key of this object is different from
|
---|
| 1393 | that of \a m; otherwise returns FALSE.
|
---|
| 1394 | */
|
---|
| 1395 |
|
---|
| 1396 |
|
---|
| 1397 | /*!
|
---|
| 1398 | Returns TRUE if the extended key of this object is
|
---|
| 1399 | lexicographically before than that of \a m; otherwise returns
|
---|
| 1400 | FALSE.
|
---|
| 1401 | */
|
---|
| 1402 |
|
---|
| 1403 | bool QTranslatorMessage::operator<( const QTranslatorMessage& m ) const
|
---|
| 1404 | {
|
---|
| 1405 | return h != m.h ? h < m.h
|
---|
| 1406 | : ( cx != m.cx ? cx < m.cx
|
---|
| 1407 | : (st != m.st ? st < m.st : cm < m.cm) );
|
---|
| 1408 | }
|
---|
| 1409 |
|
---|
| 1410 |
|
---|
| 1411 | /*!
|
---|
| 1412 | \fn bool QTranslatorMessage::operator<=( const QTranslatorMessage& m ) const
|
---|
| 1413 |
|
---|
| 1414 | Returns TRUE if the extended key of this object is
|
---|
| 1415 | lexicographically before that of \a m or if they are equal;
|
---|
| 1416 | otherwise returns FALSE.
|
---|
| 1417 | */
|
---|
| 1418 |
|
---|
| 1419 | /*!
|
---|
| 1420 | \fn bool QTranslatorMessage::operator>( const QTranslatorMessage& m ) const
|
---|
| 1421 |
|
---|
| 1422 | Returns TRUE if the extended key of this object is
|
---|
| 1423 | lexicographically after that of \a m; otherwise returns FALSE.
|
---|
| 1424 | */
|
---|
| 1425 |
|
---|
| 1426 | /*!
|
---|
| 1427 | \fn bool QTranslatorMessage::operator>=( const QTranslatorMessage& m ) const
|
---|
| 1428 |
|
---|
| 1429 | Returns TRUE if the extended key of this object is
|
---|
| 1430 | lexicographically after that of \a m or if they are equal;
|
---|
| 1431 | otherwise returns FALSE.
|
---|
| 1432 | */
|
---|
| 1433 |
|
---|
| 1434 | #endif // QT_NO_TRANSLATION
|
---|