4#include "qplatformdefs.h"
8#ifndef QT_NO_TRANSLATION
26#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
28# include "private/qcore_unix_p.h"
51
52
53
54
59 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
60 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
63static inline QString
dotQmLiteral() {
return QStringLiteral(
".qm"); }
65static bool match(
const uchar *found, uint foundLen,
const char *target, uint targetLen)
69 if (foundLen > 0 && found[foundLen-1] ==
'\0')
71 return ((targetLen == foundLen) && memcmp(found, target, foundLen) == 0);
79 k = (
const uchar *) name;
82 if ((g = (h & 0xf0000000)) != 0)
103
104
105
106
107
118 uchar opcode = rules[offset];
119 uchar op = opcode & Q_OP_MASK;
124 if (++offset == rulesSize)
138 if (offset != rulesSize) {
150 if (offset == rulesSize)
153 }
while (((rules[offset] == Q_AND)
154 || (rules[offset] == Q_OR)
155 || (rules[offset] == Q_NEWRULE))
156 && ++offset != rulesSize);
163
164
165
166
167
168
169
170
171
172
173
174
175
185 bool orExprTruthValue =
false;
188 bool andExprTruthValue =
true;
191 bool truthValue =
true;
192 int opcode = rules[i++];
195 if (opcode & Q_MOD_10) {
197 }
else if (opcode & Q_MOD_100) {
199 }
else if (opcode & Q_LEAD_1000) {
200 while (leftOperand >= 1000)
204 int op = opcode & Q_OP_MASK;
205 int rightOperand = rules[i++];
209 truthValue = (leftOperand == rightOperand);
212 truthValue = (leftOperand < rightOperand);
215 truthValue = (leftOperand <= rightOperand);
218 int bottom = rightOperand;
219 int top = rules[i++];
220 truthValue = (leftOperand >= bottom && leftOperand <= top);
224 truthValue = !truthValue;
226 andExprTruthValue = andExprTruthValue && truthValue;
228 if (i == rulesSize || rules[i] != Q_AND)
233 orExprTruthValue = orExprTruthValue || andExprTruthValue;
235 if (i == rulesSize || rules[i] != Q_OR)
240 if (orExprTruthValue)
257 Q_DECLARE_PUBLIC(QTranslator)
262#if defined(QT_USE_MMAP)
269#if defined(QT_USE_MMAP)
295 bool do_load(
const QString &filename,
const QString &directory);
296 bool do_load(
const uchar *data, qsizetype len,
const QString &directory);
297 QString
do_translate(
const char *context,
const char *sourceText,
const char *comment,
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
382
383
384
386QTranslator::QTranslator(QObject * parent)
387 : QObject(*
new QTranslatorPrivate, parent)
392
393
395QTranslator::~QTranslator()
397 if (QCoreApplication::instance())
398 QCoreApplication::removeTranslator(
this);
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
448bool QTranslator::load(
const QString & filename,
const QString & directory,
449 const QString & search_delimiters,
450 const QString & suffix)
456 if (QFileInfo(filename).isRelative()) {
458 if (prefix.size() && !prefix.endsWith(u'/'))
462 const QString suffixOrDotQM = suffix.isNull() ? dotQmLiteral() : suffix;
463 QStringView fname(filename);
465 const QString delims = search_delimiters.isNull() ? QStringLiteral(
"_.") : search_delimiters;
470 realname = prefix + fname + suffixOrDotQM;
471 fi.setFile(realname);
472 if (fi.isReadable() && fi.isFile())
475 realname = prefix + fname;
476 fi.setFile(realname);
477 if (fi.isReadable() && fi.isFile())
480 qsizetype rightmost = 0;
481 for (
auto ch : delims)
482 rightmost = std::max(rightmost, fname.lastIndexOf(ch));
488 fname.truncate(rightmost);
492 return d->do_load(realname, directory);
500 if (realname.startsWith(u':')) {
503 Q_ASSERT(!d->resource);
505 if (resource->isValid() && resource->compressionAlgorithm() == QResource::NoCompression
506 && resource->size() >= MagicLength
507 && !memcmp(resource->data(), magic, MagicLength)) {
508 d->unmapLength = resource->size();
509 d->unmapPointer =
reinterpret_cast<
char *>(
const_cast<uchar *>(resource->data()));
510#if defined(QT_USE_MMAP)
511 d->used_mmap =
false;
520 QFile file(realname);
521 if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
524 qint64 fileSize = file.size();
525 if (fileSize < MagicLength || fileSize > std::numeric_limits<qsizetype>::max())
530 if (MagicLength != file.read(magicBuffer, MagicLength)
531 || memcmp(magicBuffer, magic, MagicLength))
535 d->unmapLength = qsizetype(fileSize);
543#define MAP_FAILED reinterpret_cast<void *>(-1
)
546 int fd = file.handle();
548 int protection = PROT_READ;
549 int flags = MAP_FILE | MAP_PRIVATE;
550 void *ptr = QT_MMAP(
nullptr, d->unmapLength,
553 if (ptr != MAP_FAILED) {
556 d->unmapPointer =
static_cast<
char *>(ptr);
566 qint64 readResult = file.read(d->unmapPointer, d->unmapLength);
567 if (readResult == qint64(unmapLength))
574 const QString base_dir =
575 !directory.isEmpty() ? directory : QFileInfo(realname).absolutePath();
578 d->filePath = realname;
583#if defined(QT_USE_MMAP)
586 munmap(unmapPointer, unmapLength);
592 d->resource =
nullptr;
602 const QFileInfo fi(name);
603 const bool isReadableFile = fi.isReadable() && fi.isFile();
604 qCDebug(lcTranslator) <<
"Testing file" << name << isReadableFile;
606 return isReadableFile;
610 const QString & filename,
611 const QString & prefix,
612 const QString & directory,
613 const QString & suffix)
615 qCDebug(lcTranslator).noquote().nospace() <<
"Searching translation for "
616 << filename << prefix << locale << suffix
617 <<
" in " << directory;
621 if (!path.isEmpty() && !path.endsWith(u'/'))
624 const QString suffixOrDotQM = suffix.isNull() ? dotQmLiteral() : suffix;
627 realname += path + filename + prefix;
628 const qsizetype realNameBaseSize = realname.size();
638 const QStringList languages = locale.uiLanguages(QLocale::TagSeparator::Underscore);
639 qCDebug(lcTranslator) <<
"Requested UI languages" << languages;
641 for (
const QString &localeName : languages) {
642 QString loc = localeName;
646 realname += loc + suffixOrDotQM;
647 if (is_readable_file(realname))
651 realname.truncate(realNameBaseSize + loc.size());
652 if (is_readable_file(realname))
655 realname.truncate(realNameBaseSize);
658 if (loc != localeName)
660 loc = std::move(loc).toLower();
661 if (loc == localeName)
666 const qsizetype realNameBaseSizeFallbacks = path.size() + filename.size();
669 if (!suffix.isNull()) {
670 realname.replace(realNameBaseSizeFallbacks, prefix.size(), suffix);
672 if (is_readable_file(realname))
674 realname.replace(realNameBaseSizeFallbacks, suffix.size(), prefix);
678 if (is_readable_file(realname))
681 realname.truncate(realNameBaseSizeFallbacks);
683 if (is_readable_file(realname))
686 realname.truncate(0);
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733bool QTranslator::load(
const QLocale & locale,
734 const QString & filename,
735 const QString & prefix,
736 const QString & directory,
737 const QString & suffix)
741 QString fname = find_translation(locale, filename, prefix, directory, suffix);
742 return !fname.isEmpty() && d->do_load(fname, directory);
746
747
748
749
750
751
752
753
754
755
756
757bool QTranslator::load(
const uchar *data,
int len,
const QString &directory)
762 if (!data || len < MagicLength || memcmp(data, magic, MagicLength))
765 return d->do_load(data, len, directory);
770 return qFromBigEndian<quint8>(data);
775 return qFromBigEndian<quint16>(data);
780 return qFromBigEndian<quint32>(data);
786 const uchar *end = data + len;
790 QStringList dependencies;
791 while (data < end - 5) {
792 quint8 tag = read8(data++);
793 quint32 blockLen = read32(data);
795 if (!tag || !blockLen)
797 if (quint32(end - data) < blockLen) {
802 if (tag == QTranslatorPrivate::Language) {
803 language = QString::fromUtf8((
const char *)data, blockLen);
804 }
else if (tag == QTranslatorPrivate::Contexts) {
807 }
else if (tag == QTranslatorPrivate::Hashes) {
810 }
else if (tag == QTranslatorPrivate::Messages) {
813 }
else if (tag == QTranslatorPrivate::NumerusRules) {
814 numerusRulesArray = data;
816 }
else if (tag == QTranslatorPrivate::Dependencies) {
817 QDataStream stream(QByteArray::fromRawData((
const char*)data, blockLen));
819 while (!stream.atEnd()) {
821 dependencies.append(dep);
828 if (ok && !isValidNumerusRules(numerusRulesArray, numerusRulesLength))
831 subTranslators.reserve(std::size_t(dependencies.size()));
832 for (
const QString &dependency : std::as_const(dependencies)) {
833 auto translator = std::make_unique<QTranslator>();
834 ok = translator->load(dependency, directory);
837 subTranslators.push_back(std::move(translator));
842 subTranslators.clear();
846 messageArray =
nullptr;
847 contextArray =
nullptr;
848 offsetArray =
nullptr;
849 numerusRulesArray =
nullptr;
859static QString
getMessage(
const uchar *m,
const uchar *end,
const char *context,
860 const char *sourceText,
const char *comment, uint numerus)
862 const uchar *tn =
nullptr;
864 const uint sourceTextLen = uint(strlen(sourceText));
865 const uint contextLen = uint(strlen(context));
866 const uint commentLen = uint(strlen(comment));
891 quint32 len = read32(m);
893 if (!match(m, len, sourceText, sourceTextLen))
899 quint32 len = read32(m);
901 if (!match(m, len, context, contextLen))
907 quint32 len = read32(m);
909 if (*m && !match(m, len, comment, commentLen))
921 QString str(tn_length / 2, Qt::Uninitialized);
922 qFromBigEndian<
char16_t>(tn, str.size(), str.data());
927 const char *comment,
int n)
const
929 if (context ==
nullptr)
931 if (sourceText ==
nullptr)
933 if (comment ==
nullptr)
940 goto searchDependencies;
943
944
945
947 quint16 hTableSize = read16(contextArray);
949 const uchar *c = contextArray + 2 + (g << 1);
950 quint16 off = read16(c);
954 c = contextArray + (2 + (hTableSize << 1) + (off << 1));
956 const uint contextLen = uint(strlen(context));
958 quint8 len = read8(c++);
961 if (match(c, len, context, contextLen))
967 numItems = offsetLength / (2 *
sizeof(quint32));
969 goto searchDependencies;
972 numerus = numerusHelper(n, numerusRulesArray, numerusRulesLength);
976 elfHash_continue(sourceText, h);
977 elfHash_continue(comment, h);
980 const uchar *start = offsetArray;
981 const uchar *end = start + ((numItems - 1) << 3);
982 while (start <= end) {
983 const uchar *middle = start + (((end - start) >> 4) << 3);
984 uint hash = read32(middle);
988 }
else if (hash < h) {
997 while (start != offsetArray && read32(start) == read32(start - 8))
1000 while (start < offsetArray + offsetLength) {
1001 quint32 rh = read32(start);
1005 quint32 ro = read32(start);
1007 QString tn = getMessage(messageArray + ro, messageArray + messageLength, context,
1008 sourceText, comment, numerus);
1019 for (
const auto &translator : subTranslators) {
1020 QString tn = translator->translate(context, sourceText, comment, n);
1028
1029
1030
1031
1036 if (unmapPointer && unmapLength) {
1037#if defined(QT_USE_MMAP)
1040 munmap(unmapPointer, unmapLength);
1050 messageArray =
nullptr;
1051 contextArray =
nullptr;
1052 offsetArray =
nullptr;
1053 numerusRulesArray =
nullptr;
1059 subTranslators.clear();
1064 if (QCoreApplicationPrivate::isTranslatorInstalled(q))
1065 QCoreApplication::postEvent(QCoreApplication::instance(),
1066 new QEvent(QEvent::LanguageChange));
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087QString QTranslator::translate(
const char *context,
const char *sourceText,
const char *disambiguation,
1090 Q_D(
const QTranslator);
1091 return d->do_translate(context, sourceText, disambiguation, n);
1095
1096
1097
1098bool QTranslator::isEmpty()
const
1100 Q_D(
const QTranslator);
1101 return !d->messageArray && !d->offsetArray && !d->contextArray
1102 && d->subTranslators.empty();
1106
1107
1108
1109
1110QString QTranslator::language()
const
1112 Q_D(
const QTranslator);
1117
1118
1119
1120
1121
1122
1123
1124
1125QString QTranslator::filePath()
const
1127 Q_D(
const QTranslator);
1133#include "moc_qtranslator.cpp"
bool do_load(const QString &filename, const QString &directory)
const uchar * contextArray
bool do_load(const uchar *data, qsizetype len, const QString &directory)
const uchar * messageArray
QString do_translate(const char *context, const char *sourceText, const char *comment, int n) const
const uchar * offsetArray
std::unique_ptr< QResource > resource
std::vector< std::unique_ptr< QTranslator > > subTranslators
const uchar * numerusRulesArray
Combined button and popup list for selecting options.
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
static const int MagicLength
static QString getMessage(const uchar *m, const uchar *end, const char *context, const char *sourceText, const char *comment, uint numerus)
static quint32 read32(const uchar *data)
static void elfHash_continue(const char *name, uint &h)
static bool isValidNumerusRules(const uchar *rules, uint rulesSize)
static Q_NEVER_INLINE bool is_readable_file(const QString &name)
static QString dotQmLiteral()
static uint elfHash(const char *name)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
static uint numerusHelper(int n, const uchar *rules, uint rulesSize)
static QString find_translation(const QLocale &locale, const QString &filename, const QString &prefix, const QString &directory, const QString &suffix)
static quint8 read8(const uchar *data)
static quint16 read16(const uchar *data)
static const uchar magic[MagicLength]
static void elfHash_finish(uint &h)