23 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
24 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
228 if (!m_language.isEmpty()) {
229 QByteArray lang = originalBytes(m_language);
230 quint32 las = quint32(lang.size());
232 s.writeRawData(lang, las);
234 if (!m_dependencyArray.isEmpty()) {
235 quint32 das = quint32(m_dependencyArray.size());
237 s.writeRawData(m_dependencyArray.constData(), das);
239 if (!m_offsetArray.isEmpty()) {
240 quint32 oas = quint32(m_offsetArray.size());
241 s << quint8(
Hashes) << oas;
242 s.writeRawData(m_offsetArray.constData(), oas);
244 if (!m_messageArray.isEmpty()) {
245 quint32 mas = quint32(m_messageArray.size());
247 s.writeRawData(m_messageArray.constData(), mas);
249 if (!m_contextArray.isEmpty()) {
250 quint32 cas = quint32(m_contextArray.size());
252 s.writeRawData(m_contextArray.constData(), cas);
254 if (!m_numerusRules.isEmpty()) {
255 quint32 nrs = m_numerusRules.size();
257 s.writeRawData(m_numerusRules.constData(), nrs);
264 m_dependencyArray.clear();
265 QDataStream depstream(&m_dependencyArray, QIODevice::WriteOnly);
266 for (
const QString &dep : std::as_const(m_dependencies))
269 if (m_messages.isEmpty() && mode == SaveEverything)
272 const auto messages = m_messages;
275 m_messageArray.clear();
276 m_offsetArray.clear();
277 m_contextArray.clear();
280 QMap<Offset,
void *> offsets;
282 QDataStream ms(&m_messageArray, QIODevice::WriteOnly);
283 int cpPrev = 0, cpNext = 0;
284 for (
auto it = messages.cbegin(), end = messages.cend(); it != end; ++it) {
286 const auto next = std::next(it);
290 cpNext = commonPrefix(it.key(), next.key());
291 offsets.insert(Offset(msgHash(it.key()), ms.device()->pos()), (
void *)0);
292 writeMessage(it.key(), ms, mode, Prefix(qMax(cpPrev, cpNext + 1)));
295 auto offset = offsets.cbegin();
296 QDataStream ds(&m_offsetArray, QIODevice::WriteOnly);
297 while (offset != offsets.cend()) {
300 ds << quint32(k.h) << quint32(k.o);
304 QMap<QByteArray,
int> contextSet;
305 for (
auto it = messages.cbegin(), end = messages.cend(); it != end; ++it)
306 ++contextSet[it.key().context()];
309 if (contextSet.size() < 200)
310 hTableSize = (contextSet.size() < 60) ? 151 : 503;
311 else if (contextSet.size() < 2500)
312 hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
314 hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
316 QMultiMap<
int, QByteArray> hashMap;
317 for (
auto c = contextSet.cbegin(), end = contextSet.cend(); c != end; ++c)
318 hashMap.insert(elfHash(c.key()) % hTableSize, c.key());
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 m_contextArray.resize(2 + (hTableSize << 1));
343 QDataStream t(&m_contextArray, QIODevice::WriteOnly);
345 quint16 *hTable =
new quint16[hTableSize];
346 memset(hTable, 0, hTableSize *
sizeof(quint16));
349 t.device()->seek(2 + (hTableSize << 1));
353 auto entry = hashMap.constBegin();
354 while (entry != hashMap.constEnd()) {
356 hTable[i] = quint16(upto >> 1);
359 const char *con = entry.value().constData();
360 uint len = uint(entry.value().size());
361 len = qMin(len, 255u);
363 t.writeRawData(con, len);
366 }
while (entry != hashMap.constEnd() && entry.key() == i);
374 for (
int j = 0; j < hTableSize; j++)
379 qWarning(
"Releaser::squeeze: Too many contexts");
380 m_contextArray.clear();
388 originalBytes(message.sourceText()),
389 originalBytes(message.comment()),
393 bmsg.context(), bmsg.sourceText(), QByteArray(
""), bmsg.translations());
394 if (!m_messages.contains(bmsg2)) {
395 m_messages.insert(bmsg2, 0);
399 m_messages.insert(bmsg, 0);
437 QByteArray ba = dev.readAll();
438 const uchar *data = (uchar*)ba.data();
441 cd.appendError(QLatin1String(
"QM-Format error: magic marker missing"));
445 enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96, Language = 0xa7 };
448 const uchar *messageArray =
nullptr;
449 const uchar *offsetArray =
nullptr;
450 uint offsetLength = 0;
453 bool utf8Fail =
false;
454 const uchar *end = data + len;
458 while (data < end - 4) {
459 quint8 tag = read8(data++);
460 quint32 blockLen = read32(data);
463 if (!tag || !blockLen)
465 if (data + blockLen > end) {
472 offsetLength = blockLen;
474 }
else if (tag == Messages) {
477 }
else if (tag == Dependencies) {
478 QStringList dependencies;
479 QDataStream stream(QByteArray::fromRawData((
const char*)data, blockLen));
481 while (!stream.atEnd()) {
483 dependencies.append(dep);
485 translator.setDependencies(dependencies);
486 }
else if (tag == Language) {
488 fromBytes((
const char *)data, blockLen, &language, &utf8Fail);
489 translator.setLanguageCode(language);
496 size_t numItems = offsetLength / (2 *
sizeof(quint32));
499 QString strProN = QLatin1String(
"%n");
501 QLocale::Territory c;
502 Translator::languageAndTerritory(translator.languageCode(), &l, &c);
503 QStringList numerusForms;
504 bool guessPlurals =
true;
505 if (getNumerusInfo(l, c, 0, &numerusForms, 0))
506 guessPlurals = (numerusForms.size() == 1);
508 QString context, sourcetext, comment;
509 QStringList translations;
511 for (
const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
513 quint32 ro = read32(start + 4);
515 const uchar *m = messageArray + ro;
518 uchar tag = read8(m++);
529 if ((len != -1) && (len & 1)) {
530 cd.appendError(QLatin1String(
"QM-Format error"));
535 str = QString((
const QChar *)m, len / 2);
536 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
537 for (
int i = 0; i < str.size(); ++i)
538 str[i] = QChar((str.at(i).unicode() >> 8) +
539 ((str.at(i).unicode() << 8) & 0xff00));
550 quint32 len = read32(m);
554 fromBytes((
const char*)m, len, &sourcetext, &utf8Fail);
559 quint32 len = read32(m);
563 fromBytes((
const char*)m, len, &context, &utf8Fail);
568 quint32 len = read32(m);
572 fromBytes((
const char*)m, len, &comment, &utf8Fail);
584 if (translations.size() > 1) {
588 }
else if (guessPlurals) {
590 if (sourcetext.contains(strProN))
593 msg.setTranslations(translations);
594 translations.clear();
595 msg.setContext(context);
596 msg.setSourceText(sourcetext);
597 msg.setComment(comment);
601 cd.appendError(QLatin1String(
"Error: File contains invalid UTF-8 sequences."));
621 Releaser releaser(translator.languageCode());
623 QLocale::Territory c;
624 Translator::languageAndTerritory(translator.languageCode(), &l, &c);
626 if (getNumerusInfo(l, c, &rules, 0, 0))
627 releaser.setNumerusRules(rules);
631 int untranslated = 0;
644 if (msg.translation().isEmpty() && !cd
.m_idBased && cd.m_unTrPrefix.isEmpty()) {
655 QStringList tlns = msg.translations();
657 && (cd
.m_idBased || !cd.m_unTrPrefix.isEmpty()))
658 for (
int j = 0; j < tlns.size(); ++j)
659 if (tlns.at(j).isEmpty())
660 tlns[j] = cd.m_unTrPrefix + msg.sourceText();
662 if (!msg.context().isEmpty() || !msg.comment().isEmpty())
664 releaser.insertIdBased(msg, tlns);
672 msg.comment().isEmpty()
673 || msg.context().isEmpty()
675 releaser.insert(msg, tlns, forceComment);
681 cd.appendError(QCoreApplication::translate(
"LRelease",
682 "Dropped %n message(s) which had no ID.", 0,
685 cd.appendError(QCoreApplication::translate(
"LRelease",
686 "Excess context/disambiguation dropped from %n message(s).", 0,
689 releaser.setDependencies(translator.dependencies());
691 bool saved = releaser
.save(&dev
);
693 int generatedCount = finished + unfinished;
694 cd.appendError(QCoreApplication::translate(
"LRelease",
695 " Generated %n translation(s) (%1 finished and %2 unfinished)", 0,
696 generatedCount).arg(finished).arg(unfinished));
698 cd.appendError(QCoreApplication::translate(
"LRelease",
699 " Ignored %n untranslated source text(s)", 0,