230 if (!m_language.isEmpty()) {
231 QByteArray lang = originalBytes(m_language);
232 quint32 las = quint32(lang.size());
234 s.writeRawData(lang.constData(), las);
236 if (!m_dependencyArray.isEmpty()) {
237 quint32 das = quint32(m_dependencyArray.size());
239 s.writeRawData(m_dependencyArray.constData(), das);
241 if (!m_offsetArray.isEmpty()) {
242 quint32 oas = quint32(m_offsetArray.size());
243 s << quint8(
Hashes) << oas;
244 s.writeRawData(m_offsetArray.constData(), oas);
246 if (!m_messageArray.isEmpty()) {
247 quint32 mas = quint32(m_messageArray.size());
249 s.writeRawData(m_messageArray.constData(), mas);
251 if (!m_contextArray.isEmpty()) {
252 quint32 cas = quint32(m_contextArray.size());
254 s.writeRawData(m_contextArray.constData(), cas);
256 if (!m_numerusRules.isEmpty()) {
257 quint32 nrs = m_numerusRules.size();
259 s.writeRawData(m_numerusRules.constData(), nrs);
266 m_dependencyArray.clear();
267 QDataStream depstream(&m_dependencyArray, QIODevice::WriteOnly);
268 for (
const QString &dep : std::as_const(m_dependencies))
271 if (m_messages.isEmpty() && mode == SaveEverything)
274 const auto messages = m_messages;
277 m_messageArray.clear();
278 m_offsetArray.clear();
279 m_contextArray.clear();
282 QMap<Offset,
void *> offsets;
284 QDataStream ms(&m_messageArray, QIODevice::WriteOnly);
285 int cpPrev = 0, cpNext = 0;
286 for (
auto it = messages.cbegin(), end = messages.cend(); it != end; ++it) {
288 const auto next = std::next(it);
292 cpNext = commonPrefix(it.key(), next.key());
293 offsets.insert(Offset(msgHash(it.key()), ms.device()->pos()), (
void *)0);
294 writeMessage(it.key(), ms, mode, Prefix(qMax(cpPrev, cpNext + 1)));
297 auto offset = offsets.cbegin();
298 QDataStream ds(&m_offsetArray, QIODevice::WriteOnly);
299 while (offset != offsets.cend()) {
302 ds << quint32(k.h) << quint32(k.o);
306 QMap<QByteArray,
int> contextSet;
307 for (
auto it = messages.cbegin(), end = messages.cend(); it != end; ++it)
308 ++contextSet[it.key().context()];
311 if (contextSet.size() < 200)
312 hTableSize = (contextSet.size() < 60) ? 151 : 503;
313 else if (contextSet.size() < 2500)
314 hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
316 hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
318 QMultiMap<
int, QByteArray> hashMap;
319 for (
auto c = contextSet.cbegin(), end = contextSet.cend(); c != end; ++c)
320 hashMap.insert(elfHash(c.key()) % hTableSize, c.key());
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 m_contextArray.resize(2 + (hTableSize << 1));
345 QDataStream t(&m_contextArray, QIODevice::WriteOnly);
347 quint16 *hTable =
new quint16[hTableSize];
348 memset(hTable, 0, hTableSize *
sizeof(quint16));
351 t.device()->seek(2 + (hTableSize << 1));
355 auto entry = hashMap.constBegin();
356 while (entry != hashMap.constEnd()) {
358 hTable[i] = quint16(upto >> 1);
361 const char *con = entry.value().constData();
362 uint len = uint(entry.value().size());
363 len = qMin(len, 255u);
365 t.writeRawData(con, len);
368 }
while (entry != hashMap.constEnd() && entry.key() == i);
376 for (
int j = 0; j < hTableSize; j++)
381 qWarning(
"Releaser::squeeze: Too many contexts");
382 m_contextArray.clear();
439 QByteArray ba = dev.readAll();
440 const uchar *data = (uchar*)ba.data();
443 cd.appendError(
"QM-Format error: magic marker missing"_L1);
447 enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96, Language = 0xa7 };
450 const uchar *messageArray =
nullptr;
451 uint messageLength = 0;
452 const uchar *offsetArray =
nullptr;
453 uint offsetLength = 0;
456 bool utf8Fail =
false;
457 const uchar *end = data + len;
461 while (data < end - 4) {
462 quint8 tag = read8(data++);
463 quint32 blockLen = read32(data);
466 if (!tag || !blockLen)
468 if (data + blockLen > end) {
475 offsetLength = blockLen;
477 }
else if (tag == Messages) {
479 messageLength = blockLen;
481 }
else if (tag == Dependencies) {
482 QStringList dependencies;
483 QDataStream stream(QByteArray::fromRawData((
const char*)data, blockLen));
485 while (!stream.atEnd()) {
487 dependencies.append(dep);
489 translator.setDependencies(dependencies);
490 }
else if (tag == Language) {
492 fromBytes((
const char *)data, blockLen, &language, &utf8Fail);
493 translator.setLanguageCode(language);
500 size_t numItems = offsetLength / (2 *
sizeof(quint32));
503 QString strProN =
"%n"_L1;
505 QLocale::Territory c;
506 Translator::languageAndTerritory(translator.languageCode(), &l, &c);
507 QStringList numerusForms;
508 bool guessPlurals =
true;
509 if (getNumerusInfo(l, c, 0, &numerusForms, 0))
510 guessPlurals = (numerusForms.size() == 1);
512 QString context, sourcetext, comment;
513 QStringList translations;
518 const uchar *
const messageArrayEnd = messageArray + messageLength;
519 const auto bytesAvailable = [&](
const uchar *p) -> qsizetype {
520 return (p >= messageArray && p < messageArrayEnd) ? messageArrayEnd - p : 0;
523 for (
const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
525 quint32 ro = read32(start + 4);
527 if (!messageArray || ro >= messageLength) {
528 cd.appendError(
"QM-Format error: message offset out of range"_L1);
531 const uchar *m = messageArray + ro;
534 if (bytesAvailable(m) < 1) {
535 cd.appendError(
"QM-Format error: message record truncated"_L1);
538 uchar tag = read8(m++);
544 if (bytesAvailable(m) < 4) {
545 cd.appendError(
"QM-Format error: message record truncated"_L1);
553 if ((len != -1) && (len & 1)) {
554 cd.appendError(
"QM-Format error"_L1);
559 if (len < 0 || bytesAvailable(m) < len) {
560 cd.appendError(
"QM-Format error: translation length out of range"_L1);
563 str = QString((
const QChar *)m, len / 2);
564 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
565 for (
int i = 0; i < str.size(); ++i)
566 str[i] = QChar((str.at(i).unicode() >> 8)
567 + ((str.at(i).unicode() << 8) & 0xff00));
575 if (bytesAvailable(m) < 4) {
576 cd.appendError(
"QM-Format error: message record truncated"_L1);
583 if (bytesAvailable(m) < 4) {
584 cd.appendError(
"QM-Format error: message record truncated"_L1);
587 quint32 len = read32(m);
589 if (len > quint32(bytesAvailable(m))) {
590 cd.appendError(
"QM-Format error: source length out of range"_L1);
595 fromBytes((
const char*)m, len, &sourcetext, &utf8Fail);
600 if (bytesAvailable(m) < 4) {
601 cd.appendError(
"QM-Format error: message record truncated"_L1);
604 quint32 len = read32(m);
606 if (len > quint32(bytesAvailable(m))) {
607 cd.appendError(
"QM-Format error: context length out of range"_L1);
612 fromBytes((
const char*)m, len, &context, &utf8Fail);
617 if (bytesAvailable(m) < 4) {
618 cd.appendError(
"QM-Format error: message record truncated"_L1);
621 quint32 len = read32(m);
623 if (len > quint32(bytesAvailable(m))) {
624 cd.appendError(
"QM-Format error: comment length out of range"_L1);
629 fromBytes((
const char*)m, len, &comment, &utf8Fail);
641 if (translations.size() > 1) {
645 }
else if (guessPlurals) {
647 if (sourcetext.contains(strProN))
650 msg.setTranslations(translations);
651 translations.clear();
652 msg.setContext(context);
653 msg.setSourceText(sourcetext);
654 msg.setComment(comment);
658 cd.appendError(
"Error: File contains invalid UTF-8 sequences."_L1);
678 Releaser releaser(translator.languageCode());
680 QLocale::Territory c;
681 Translator::languageAndTerritory(translator.languageCode(), &l, &c);
683 if (getNumerusInfo(l, c, &rules, 0, 0))
684 releaser.setNumerusRules(rules);
688 int untranslated = 0;
693 bool idBased = msg.context().isEmpty() && !msg.id().isEmpty();
697 if (msg.translation().isEmpty() && !idBased && cd.m_unTrPrefix.isEmpty()) {
708 QStringList tlns = msg.translations();
710 && (idBased || !cd.m_unTrPrefix.isEmpty()))
711 for (
int j = 0; j < tlns.size(); ++j)
712 if (tlns.at(j).isEmpty())
713 tlns[j] = cd.m_unTrPrefix + msg.sourceText();
715 if (!msg.comment().isEmpty())
717 releaser.insertIdBased(msg, tlns);
725 msg.comment().isEmpty()
726 || msg.context().isEmpty()
728 releaser.insert(msg, tlns, forceComment);
734 cd.appendError(QCoreApplication::translate(
"LRelease",
735 "Excess context/disambiguation dropped from %n message(s).", 0,
738 releaser.setDependencies(translator.dependencies());
740 bool saved = releaser
.save(&dev
);
742 int generatedCount = finished + unfinished;
743 cd.appendError(QCoreApplication::translate(
"LRelease",
744 " Generated %n translation(s) (%1 finished and %2 unfinished)", 0,
745 generatedCount).arg(finished).arg(unfinished));
747 cd.appendError(QCoreApplication::translate(
"LRelease",
748 " Ignored %n untranslated source text(s)", 0,