📄 translator.cpp
字号:
ok = true; } } ::close(fd); }#endif // QT_USE_MMAP if (!ok) { QFile file(realname); if (!file.exists()) return false; d->unmapLength = file.size(); d->unmapPointer = new char[d->unmapLength]; if (file.open(QIODevice::ReadOnly)) ok = (d->unmapLength == (uint)file.read(d->unmapPointer, d->unmapLength)); if (!ok) { delete [] d->unmapPointer; d->unmapPointer = 0; d->unmapLength = 0; return false; } } return d->do_load(reinterpret_cast<const uchar *>(d->unmapPointer), d->unmapLength);}/*! \overload \fn bool Translator::load(const uchar *data, int len) Loads the .qm file data \a data of length \a len into the translator. The data is not copied. The caller must be able to guarantee that \a data will not be deleted or modified.*/bool Translator::load(const uchar *data, int len){ clear(); return d->do_load(data, len);}bool TranslatorPrivate::do_load(const uchar *data, int len){ if (len < MagicLength || memcmp(data, magic, MagicLength) != 0) { q->clear(); return false; } QByteArray array = QByteArray::fromRawData((const char *) data, len); QDataStream s(&array, QIODevice::ReadOnly); bool ok = true; s.device()->seek(MagicLength); quint8 tag = 0; quint32 blockLen = 0; s >> tag >> blockLen; while (tag && blockLen) { if ((quint32) s.device()->pos() + blockLen > (quint32) len) { ok = false; break; } if (tag == TranslatorPrivate::Contexts) { contextArray = QByteArray(array.constData() + s.device()->pos(), blockLen); } else if (tag == TranslatorPrivate::Hashes) { offsetArray = QByteArray(array.constData() + s.device()->pos(), blockLen); } else if (tag == TranslatorPrivate::Messages) { messageArray = QByteArray(array.constData() + s.device()->pos(), blockLen); } if (!s.device()->seek(s.device()->pos() + blockLen)) { ok = false; break; } tag = 0; blockLen = 0; if (!s.atEnd()) s >> tag >> blockLen; } return ok;}#ifndef QT_NO_TRANSLATION_BUILDER/*! Saves this message file to \a filename, overwriting the previous contents of \a filename. If \a mode is \c Everything (the default), all the information is preserved. If \a mode is \c Stripped, any information that is not necessary for findMessage() is stripped away. \sa load()*/bool Translator::save(const QString & filename, SaveMode mode){ QFile file(filename); if (file.open(QIODevice::WriteOnly)) { squeeze(mode); QDataStream s(&file); s.writeRawData((const char *)magic, MagicLength); quint8 tag; if (!d->offsetArray.isEmpty()) { tag = (quint8)TranslatorPrivate::Hashes; quint32 oas = (quint32)d->offsetArray.size(); s << tag << oas; s.writeRawData(d->offsetArray, oas); } if (!d->messageArray.isEmpty()) { tag = (quint8)TranslatorPrivate::Messages; quint32 mas = (quint32)d->messageArray.size(); s << tag << mas; s.writeRawData(d->messageArray, mas); } if (!d->contextArray.isEmpty()) { tag = (quint8)TranslatorPrivate::Contexts; quint32 cas = (quint32)d->contextArray.size(); s << tag << cas; s.writeRawData(d->contextArray, cas); } return true; } return false;}#endif/*! Empties this translator of all contents. This function works with stripped translator files.*/void Translator::clear(){ if (d->unmapPointer && d->unmapLength) {#if defined(QT_USE_MMAP) munmap(d->unmapPointer, d->unmapLength);#else delete [] d->unmapPointer;#endif d->unmapPointer = 0; d->unmapLength = 0; } d->messageArray.clear(); d->offsetArray.clear(); d->contextArray.clear();#ifndef QT_NO_TRANSLATION_BUILDER d->messages.clear();#endif QEvent ev(QEvent::LanguageChange); QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);}#ifndef QT_NO_TRANSLATION_BUILDER/*! Converts this message file to the compact format used to store message files on disk. You should never need to call this directly; save() and other functions call it as necessary. \a mode is for internal use. \sa save() unsqueeze()*/void Translator::squeeze(SaveMode mode){ if (d->messages.isEmpty()) { if (mode == Stripped) unsqueeze(); else return; } QMap<TranslatorMessage, void *> messages = d->messages; clear(); QMap<TranslatorPrivate::Offset, void *> offsets; QDataStream ms(&d->messageArray, QIODevice::WriteOnly); QMap<TranslatorMessage, void *>::const_iterator it, next; int cpPrev = 0, cpNext = 0; for (it = messages.constBegin(); it != messages.constEnd(); ++it) { cpPrev = cpNext; next = it; ++next; if (next == messages.constEnd()) cpNext = 0; else cpNext = (int) it.key().commonPrefix(next.key()); offsets.insert(TranslatorPrivate::Offset(it.key(), ms.device()->pos()), (void *)0); it.key().write(ms, mode == Stripped, (TranslatorMessage::Prefix)qMax(cpPrev, cpNext + 1)); } QMap<TranslatorPrivate::Offset, void *>::Iterator offset; offset = offsets.begin(); QDataStream ds(&d->offsetArray, QIODevice::WriteOnly); while (offset != offsets.end()) { TranslatorPrivate::Offset k = offset.key(); ++offset; ds << (quint32)k.h << (quint32)k.o; } if (mode == Stripped) { QMap<QByteArray, int> contextSet; for (it = messages.constBegin(); it != messages.constEnd(); ++it) ++contextSet[it.key().context()]; quint16 hTableSize; if (contextSet.size() < 200) hTableSize = (contextSet.size() < 60) ? 151 : 503; else if (contextSet.size() < 2500) hTableSize = (contextSet.size() < 750) ? 1511 : 5003; else hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2; QMultiMap<int, const char *> hashMap; QMap<QByteArray, int>::const_iterator c; for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c) hashMap.insert(elfHash(c.key()) % hTableSize, c.key()); /* The contexts found in this translator are stored in a hash table to provide fast lookup. The context array has the following format: quint16 hTableSize; quint16 hTable[hTableSize]; quint8 contextPool[...]; The context pool stores the contexts as Pascal strings: quint8 len; quint8 data[len]; Let's consider the look-up of context "FunnyDialog". A hash value between 0 and hTableSize - 1 is computed, say h. If hTable[h] is 0, "FunnyDialog" is not covered by this translator. Else, we check in the contextPool at offset 2 * hTable[h] to see if "FunnyDialog" is one of the contexts stored there, until we find it or we meet the empty string. */ d->contextArray.resize(2 + (hTableSize << 1)); QDataStream t(&d->contextArray, QIODevice::WriteOnly); quint16 *hTable = new quint16[hTableSize]; memset(hTable, 0, hTableSize * sizeof(quint16)); t << hTableSize; t.device()->seek(2 + (hTableSize << 1)); t << (quint16)0; // the entry at offset 0 cannot be used uint upto = 2; QMap<int, const char *>::const_iterator entry = hashMap.constBegin(); while (entry != hashMap.constEnd()) { int i = entry.key(); hTable[i] = (quint16)(upto >> 1); do { const char *con = entry.value(); uint len = (uint)qstrlen(con); len = qMin(len, 255u); t << (quint8)len; t.writeRawData(con, len); upto += 1 + len; ++entry; } while (entry != hashMap.constEnd() && entry.key() == i); do { t << (quint8) 0; // empty string ++upto; } while ((upto & 0x1) != 0); // offsets have to be even } t.device()->seek(2); for (int j = 0; j < hTableSize; j++) t << hTable[j]; delete [] hTable; if (upto > 131072) { qWarning("Translator::squeeze: Too many contexts"); d->contextArray.clear(); } }}/*! Converts this message file into an easily modifiable data structure, less compact than the format used in the files. You should never need to call this function; it is called by insert() and friends as necessary. \sa squeeze()*/void Translator::unsqueeze(){ if (!d->messages.isEmpty() || d->messageArray.isEmpty()) return; QDataStream s(&d->messageArray, QIODevice::ReadOnly); for (;;) { TranslatorMessage m(s); if (m.hash() == 0) break; d->messages.insert(m, (void *)0); }}/*! Returns true if this message file contains a message with the key (\a context, \a sourceText, \a comment); otherwise returns false. This function works with stripped translator files. (This is is a one-liner that calls findMessage().)*/bool Translator::contains(const char* context, const char* sourceText, const char* comment) const{ return !findMessage(context, sourceText, comment).translation().isNull();}/*! Inserts \a message into this message file. This function does \e not work with stripped translator files. It may appear to, but that is not dependable. \sa remove()*/void Translator::insert(const TranslatorMessage& message){ unsqueeze(); d->messages.remove(message); // safer d->messages.insert(message, (void *) 0);}/*! \fn void Translator::insert(const char *context, const char *sourceText, const QString &translation) \overload \obsolete Inserts the \a sourceText and \a translation into the translator with the given \a context.*//*! Removes \a message from this translator. This function works with stripped translator files. \sa insert()*/void Translator::remove(const TranslatorMessage& message){ unsqueeze(); d->messages.remove(message);}/*! \fn void Translator::remove(const char *, const char *) \overload \obsolete Removes the translation associated to the key (\a context, \a sourceText, "") from this translator.*/#endif/*! Returns the TranslatorMessage for the key (\a context, \a sourceText, \a comment). If none is found, also tries (\a context, \a sourceText, "").*/TranslatorMessage Translator::findMessage(const char *context, const char *sourceText, const char *comment) const{ if (context == 0) context = ""; if (sourceText == 0) sourceText = ""; if (comment == 0) comment = "";#ifndef QT_NO_TRANSLATION_BUILDER if (!d->messages.isEmpty()) { QMap<TranslatorMessage, void *>::const_iterator it; it = d->messages.find(TranslatorMessage(context, sourceText, comment)); if (it != d->messages.constEnd()) return it.key(); if (comment[0]) { it = d->messages.find(TranslatorMessage(context, sourceText, "")); if (it != d->messages.constEnd()) return it.key(); } return TranslatorMessage(); }#endif if (d->offsetArray.isEmpty()) return TranslatorMessage(); /* Check if the context belongs to this Translator. If many translators are installed, this step is necessary. */ if (!d->contextArray.isEmpty()) { quint16 hTableSize = 0; QDataStream t(d->contextArray); t >> hTableSize; uint g = elfHash(context) % hTableSize; t.device()->seek(2 + (g << 1)); quint16 off; t >> off; if (off == 0) return TranslatorMessage(); t.device()->seek(2 + (hTableSize << 1) + (off << 1)); quint8 len; char con[256]; for (;;) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -