1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the core module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qplatformdefs.h"
31
#include "qtranslator.h"
33
#ifndef QT_NO_TRANSLATION
35
#include "qfileinfo.h"
37
#include "qcoreapplication.h"
38
#include "qdatastream.h"
41
#include "qalgorithms.h"
45
#if defined(Q_OS_UNIX)
49
// most of the headers below are already included in qplatformdefs.h
50
// also this lacks Large File support but that's probably irrelevant
51
#if defined(QT_USE_MMAP)
59
#include "qobject_p.h"
61
enum Tag { Tag_End = 1, Tag_SourceText16, Tag_Translation, Tag_Context16,
62
Tag_Hash, Tag_SourceText, Tag_Context, Tag_Comment,
66
3cb86418caef9c95cd211cbf60a1bddd
70
// magic number for the file
71
static const int MagicLength = 16;
72
static const uchar magic[MagicLength] = {
73
0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
74
0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
77
static bool match(const uchar* found, const char* target, uint len)
79
// 0 means anything, "" means empty
80
return !found || qstrncmp((const char *)found, target, len) == 0 && target[len] == '\0';
83
static uint elfHash(const char * name)
90
k = (const uchar *) name;
93
if ((g = (h & 0xf0000000)) != 0)
103
extern bool qt_detectRTLLanguage();
105
class QTranslatorPrivate : public QObjectPrivate
107
Q_DECLARE_PUBLIC(QTranslator)
109
enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69 };
111
QTranslatorPrivate() : used_mmap(0), unmapPointer(0), unmapLength(0) {}
113
// for mmap'ed files, this is what needs to be unmapped.
116
unsigned int unmapLength;
118
// for squeezed but non-file data, this is what needs to be deleted
119
const uchar *messageArray;
120
const uchar *offsetArray;
121
const uchar *contextArray;
126
bool do_load(const uchar *data, int len);
135
\brief The QTranslator class provides internationalization support for text
142
An object of this class contains a set of translations from a
143
source language to a target language. QTranslator provides
144
functions to look up translations in a translation file.
145
Translation files are created using \l{Qt Linguist}.
147
The most common use of QTranslator is to: load a translation
148
file, install it using QApplication::installTranslator(), and use
149
it via QObject::tr(). Here's the \c main() function from the
150
\l{linguist/hellotr}{Hello tr()} example:
152
\quotefromfile linguist/hellotr/main.cpp
156
Note that the translator must be created \e before the
157
application's widgets.
159
Most applications will never need to do anything else with this
160
class. The other functions provided by this class are useful for
161
applications that work on translator files.
163
It is possible to lookup a translation using translate() (as tr()
164
and QApplication::translate() do). The translate() function takes
165
up to three parameters:
168
\o The \e context - usually the class name for the tr() caller.
169
\o The \e {source text} - usually the argument to tr().
170
\o The \e comment - an optional comment that helps disambiguate
171
different uses of the same text in the same context.
174
For example, the "Cancel" in a dialog might have "Anuluj" when the
175
program runs in Polish (in this case the source text would be
176
"Cancel"). The context would (normally) be the dialog's class
177
name; there would normally be no comment, and the translated text
180
But it's not always so simple. The Spanish version of a printer
181
dialog with settings for two-sided printing and binding would
182
probably require both "Activado" and "Activada" as translations
183
for "Enabled". In this case the source text would be "Enabled" in
184
both cases, and the context would be the dialog's class name, but
185
the two items would have disambiguating comments such as
186
"two-sided printing" for one and "binding" for the other. The
187
comment enables the translator to choose the appropriate gender
188
for the Spanish version, and enables Qt to distinguish between
191
\sa QApplication::installTranslator(), QApplication::removeTranslator(),
192
QObject::tr(), QApplication::translate()
196
Constructs an empty message file object with parent \a parent that
197
is not connected to any file.
200
QTranslator::QTranslator(QObject * parent)
201
: QObject(*new QTranslatorPrivate, parent)
210
QTranslator::QTranslator(QObject * parent, const char * name)
211
: QObject(*new QTranslatorPrivate, parent)
213
setObjectName(QString::fromAscii(name));
218
Destroys the object and frees any allocated resources.
221
QTranslator::~QTranslator()
223
if (QCoreApplication::instance())
224
QCoreApplication::instance()->removeTranslator(this);
230
Loads \a filename + \a suffix (".qm" if the \a suffix is
231
not specified), which may be an absolute file name or relative
232
to \a directory. The previous contents of this translator object
235
If the file name does not exist, other file names are tried
236
in the following order:
239
\o File name without \a suffix appended.
240
\o File name with text after a character in \a search_delimiters
241
stripped ("_." is the default for \a search_delimiters if it is
242
an empty string) and \a suffix.
243
\o File name stripped without \a suffix appended.
244
\o File name stripped further, etc.
247
For example, an application running in the fr_CA locale
248
(French-speaking Canada) might call load("foo.fr_ca",
249
"/opt/foolib"). load() would then try to open the first existing
250
readable file from this list:
253
\o \c /opt/foolib/foo.fr_ca.qm
254
\o \c /opt/foolib/foo.fr_ca
255
\o \c /opt/foolib/foo.fr.qm
256
\o \c /opt/foolib/foo.fr
257
\o \c /opt/foolib/foo.qm
258
\o \c /opt/foolib/foo
262
bool QTranslator::load(const QString & filename, const QString & directory,
263
const QString & search_delimiters,
264
const QString & suffix)
271
if (filename[0] == QLatin1Char('/')
273
|| (filename[0].isLetter() && filename[1] == QLatin1Char(':')) || filename[0] == QLatin1Char('\\')
276
prefix = QLatin1String("");
280
if (prefix.length()) {
281
if (prefix[int(prefix.length()-1)] != QLatin1Char('/'))
282
prefix += QLatin1Char('/');
285
QString fname = filename;
288
delims = search_delimiters.isNull() ? QString::fromLatin1("_.") : search_delimiters;
293
realname = prefix + fname + (suffix.isNull() ? QString::fromLatin1(".qm") : suffix);
294
fi.setFile(realname);
298
realname = prefix + fname;
299
fi.setFile(realname);
304
for (int i = 0; i < (int)delims.length(); i++) {
305
int k = fname.lastIndexOf(delims[i]);
310
// no truncations? fail
314
fname.truncate(rightmost);
317
// realname is now the fully qualified name of a readable file.
327
#define MAP_FAILED -1
331
if (!realname.startsWith(QLatin1String(":")))
332
fd = QT_OPEN(QFile::encodeName(realname), O_RDONLY,
333
#if defined(Q_OS_WIN)
341
if (!fstat(fd, &st)) {
343
ptr = reinterpret_cast<char *>(
344
mmap(0, st.st_size, // any address, whole file
345
PROT_READ, // read-only memory
346
MAP_FILE | MAP_PRIVATE, // swap-backed map from file
347
fd, 0)); // from offset 0 of fd
348
if (ptr && ptr != reinterpret_cast<char *>(MAP_FAILED)) {
350
d->unmapPointer = ptr;
351
d->unmapLength = st.st_size;
357
#endif // QT_USE_MMAP
360
QFile file(realname);
363
d->unmapLength = file.size();
364
d->unmapPointer = new char[d->unmapLength];
366
if (file.open(QIODevice::ReadOnly))
367
ok = (d->unmapLength == (uint)file.read(d->unmapPointer, d->unmapLength));
370
delete [] d->unmapPointer;
377
return d->do_load(reinterpret_cast<const uchar *>(d->unmapPointer), d->unmapLength);
382
\fn bool QTranslator::load(const uchar *data, int len)
384
Loads the .qm file data \a data of length \a len into the
387
The data is not copied. The caller must be able to guarantee that \a data
388
will not be deleted or modified.
390
bool QTranslator::load(const uchar *data, int len)
394
return d->do_load(data, len);
397
static quint8 read8(const uchar *data)
402
static quint16 read16(const uchar *data)
404
return (data[0] << 8) | (data[1]);
407
static quint32 read32(const uchar *data)
409
return (data[0] << 24)
415
bool QTranslatorPrivate::do_load(const uchar *data, int len)
417
if (len < MagicLength || memcmp(data, magic, MagicLength) != 0) {
423
const uchar *end = data + len;
427
while (data < end - 4) {
428
quint8 tag = read8(data++);
429
quint32 blockLen = read32(data);
431
if (!tag || !blockLen)
433
if (data + blockLen > end) {
438
if (tag == QTranslatorPrivate::Contexts) {
440
contextLength = blockLen;
441
} else if (tag == QTranslatorPrivate::Hashes) {
443
offsetLength = blockLen;
444
} else if (tag == QTranslatorPrivate::Messages) {
446
messageLength = blockLen;
456
Empties this translator of all contents.
458
This function works with stripped translator files.
461
void QTranslatorPrivate::clear()
463
if (unmapPointer && unmapLength) {
464
#if defined(QT_USE_MMAP)
466
munmap(unmapPointer, unmapLength);
469
delete [] unmapPointer;
482
QEvent ev(QEvent::LanguageChange);
483
QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);
487
static QString getMessage(const uchar *m, const uchar *end, const char *context, const char *sourceText, const char *comment)
499
case Tag_Translation:
500
tn_length = read32(m);
504
if (tn_length == 0xffffffff)
512
case Tag_SourceText: {
513
quint32 len = read32(m);
515
if (!match(m, sourceText, len))
521
quint32 len = read32(m);
523
if (*m && !match(m, context, len))
529
quint32 len = read32(m);
531
if (*m && !match(m, comment, len))
543
QString str = QString::fromUtf16((const ushort *)tn, tn_length/2);
544
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
545
for (int i = 0; i < str.length(); ++i)
546
str[i] = QChar((str.at(i).unicode() >> 8) + ((str.at(i).unicode() << 8) & 0xff00));
553
Returns the translation for the key (\a context, \a sourceText,
554
\a comment). If none is found, also tries (\a context, \a
555
sourceText, ""). If that still fails, returns an empty string.
559
QString QTranslator::translate(const char *context, const char *sourceText, const char *comment) const
561
Q_D(const QTranslator);
569
if (!d->offsetLength)
573
Check if the context belongs to this QTranslator. If many
574
translators are installed, this step is necessary.
576
if (d->contextLength) {
577
quint16 hTableSize = read16(d->contextArray);
578
uint g = elfHash(context) % hTableSize;
579
const uchar *c = d->contextArray + 2 + (g << 1);
580
quint16 off = read16(c);
584
c = d->contextArray + (2 + (hTableSize << 1) + (off << 1));
587
quint8 len = read8(c++);
590
if (match(c, context, len))
596
size_t numItems = d->offsetLength / (2 * sizeof(quint32));
601
quint32 h = elfHash(QByteArray(sourceText) + comment);
603
const uchar *start = d->offsetArray;
604
const uchar *end = start + ((numItems-1) << 3);
605
while (start <= end) {
606
const uchar *middle = start + (((end - start) >> 4) << 3);
607
uint hash = read32(middle);
611
} else if (hash < h) {
619
// go back on equal key
620
while (start != d->offsetArray && read32(start) == read32(start-8))
623
while (start < d->offsetArray + d->offsetLength) {
624
quint32 rh = read32(start);
628
quint32 ro = read32(start);
630
QString tn = getMessage(d->messageArray + ro, d->messageArray + d->messageLength, context, sourceText, comment);
643
Returns true if this translator is empty, otherwise returns false.
644
This function works with stripped and unstripped translation files.
646
bool QTranslator::isEmpty() const
648
Q_D(const QTranslator);
649
return !d->unmapPointer && !d->unmapLength && !d->messageArray &&
650
!d->offsetArray && !d->contextArray;
655
\fn QString QTranslator::find(const char *context, const char *sourceText, const char * comment = 0) const
657
Use translate(\a context, \a sourceText, \a comment) instead.
660
#endif // QT_NO_TRANSLATION