351 msg += '\n'; |
351 msg += '\n'; |
352 } |
352 } |
353 --l; |
353 --l; |
354 } |
354 } |
355 |
355 |
|
356 static void splitContext(QByteArray *comment, QByteArray *context) |
|
357 { |
|
358 char *data = comment->data(); |
|
359 int len = comment->size(); |
|
360 int sep = -1, j = 0; |
|
361 |
|
362 for (int i = 0; i < len; i++, j++) { |
|
363 if (data[i] == '~' && i + 1 < len) |
|
364 i++; |
|
365 else if (data[i] == '|') |
|
366 sep = j; |
|
367 data[j] = data[i]; |
|
368 } |
|
369 if (sep >= 0) { |
|
370 QByteArray tmp = comment->mid(sep + 1, j - sep - 1); |
|
371 comment->truncate(sep); |
|
372 *context = *comment; |
|
373 *comment = tmp; |
|
374 } else { |
|
375 comment->truncate(j); |
|
376 } |
|
377 } |
|
378 |
356 static QString makePoHeader(const QString &str) |
379 static QString makePoHeader(const QString &str) |
357 { |
380 { |
358 return QLatin1String("po-header-") + str.toLower().replace(QLatin1Char('-'), QLatin1Char('_')); |
381 return QLatin1String("po-header-") + str.toLower().replace(QLatin1Char('-'), QLatin1Char('_')); |
359 } |
382 } |
360 |
383 |
447 hdrOrder << hdrName; |
471 hdrOrder << hdrName; |
448 if (hdrName == "X-Language") { |
472 if (hdrName == "X-Language") { |
449 translator.setLanguageCode(QString::fromLatin1(hdrValue)); |
473 translator.setLanguageCode(QString::fromLatin1(hdrValue)); |
450 } else if (hdrName == "X-Source-Language") { |
474 } else if (hdrName == "X-Source-Language") { |
451 translator.setSourceLanguageCode(QString::fromLatin1(hdrValue)); |
475 translator.setSourceLanguageCode(QString::fromLatin1(hdrValue)); |
|
476 } else if (hdrName == "X-Qt-Contexts") { |
|
477 qtContexts = (hdrValue == "true"); |
452 } else if (hdrName == "Plural-Forms") { |
478 } else if (hdrName == "Plural-Forms") { |
453 pluralForms = hdrValue; |
479 pluralForms = hdrValue; |
454 } else if (hdrName == "MIME-Version") { |
480 } else if (hdrName == "MIME-Version") { |
455 // just assume it is 1.0 |
481 // just assume it is 1.0 |
456 } else if (hdrName == "Content-Type") { |
482 } else if (hdrName == "Content-Type") { |
457 if (cd.m_codecForSource.isEmpty()) { |
483 if (cd.m_codecForSource.isEmpty()) { |
458 if (!hdrValue.startsWith("text/plain; charset=")) { |
484 if (!hdrValue.startsWith("text/plain; charset=")) { |
459 cd.appendError(QString::fromLatin1("Unexpected Content-Type header '%1'\n") |
485 cd.appendError(QString::fromLatin1("Unexpected Content-Type header '%1'") |
460 .arg(QString::fromLatin1(hdrValue))); |
486 .arg(QString::fromLatin1(hdrValue))); |
461 error = true; |
487 error = true; |
462 // This will avoid a flood of conversion errors. |
488 // This will avoid a flood of conversion errors. |
463 codec = QTextCodec::codecForName("latin1"); |
489 codec = QTextCodec::codecForName("latin1"); |
464 } else { |
490 } else { |
465 QByteArray cod = hdrValue.mid(20); |
491 QByteArray cod = hdrValue.mid(20); |
466 QTextCodec *cdc = QTextCodec::codecForName(cod); |
492 QTextCodec *cdc = QTextCodec::codecForName(cod); |
467 if (!cdc) { |
493 if (!cdc) { |
468 cd.appendError(QString::fromLatin1("Unsupported codec '%1'\n") |
494 cd.appendError(QString::fromLatin1("Unsupported codec '%1'") |
469 .arg(QString::fromLatin1(cod))); |
495 .arg(QString::fromLatin1(cod))); |
470 error = true; |
496 error = true; |
471 // This will avoid a flood of conversion errors. |
497 // This will avoid a flood of conversion errors. |
472 codec = QTextCodec::codecForName("latin1"); |
498 codec = QTextCodec::codecForName("latin1"); |
473 } else { |
499 } else { |
496 } |
522 } |
497 // Eliminate the field if only headers we added are present in standard order. |
523 // Eliminate the field if only headers we added are present in standard order. |
498 // Keep in sync with savePO |
524 // Keep in sync with savePO |
499 static const char * const dfltHdrs[] = { |
525 static const char * const dfltHdrs[] = { |
500 "MIME-Version", "Content-Type", "Content-Transfer-Encoding", |
526 "MIME-Version", "Content-Type", "Content-Transfer-Encoding", |
501 "Plural-Forms", "X-Language", "X-Source-Language" |
527 "Plural-Forms", "X-Language", "X-Source-Language", "X-Qt-Contexts" |
502 }; |
528 }; |
503 uint cdh = 0; |
529 uint cdh = 0; |
504 for (int cho = 0; cho < hdrOrder.length(); cho++) { |
530 for (int cho = 0; cho < hdrOrder.length(); cho++) { |
505 for (;; cdh++) { |
531 for (;; cdh++) { |
506 if (cdh == sizeof(dfltHdrs)/sizeof(dfltHdrs[0])) { |
532 if (cdh == sizeof(dfltHdrs)/sizeof(dfltHdrs[0])) { |
594 break; |
620 break; |
595 case ' ': |
621 case ' ': |
596 slurpComment(item.translatorComments, lines, l); |
622 slurpComment(item.translatorComments, lines, l); |
597 break; |
623 break; |
598 case '.': |
624 case '.': |
599 if (line.startsWith("#. ts-context ")) { |
625 if (line.startsWith("#. ts-context ")) { // legacy |
600 item.context = line.mid(14); |
626 item.context = line.mid(14); |
601 } else if (line.startsWith("#. ts-id ")) { |
627 } else if (line.startsWith("#. ts-id ")) { |
602 item.id = line.mid(9); |
628 item.id = line.mid(9); |
603 } else { |
629 } else { |
604 item.automaticComments += line.mid(3); |
630 item.automaticComments += line.mid(3); |
613 if (extra != item.oldMsgId) |
639 if (extra != item.oldMsgId) |
614 item.extra[QLatin1String("po-old_msgid_plural")] = |
640 item.extra[QLatin1String("po-old_msgid_plural")] = |
615 codec->toUnicode(extra); |
641 codec->toUnicode(extra); |
616 } else if (line.startsWith("#| msgctxt ")) { |
642 } else if (line.startsWith("#| msgctxt ")) { |
617 item.oldTscomment = slurpEscapedString(lines, l, 11, "#| ", cd); |
643 item.oldTscomment = slurpEscapedString(lines, l, 11, "#| ", cd); |
|
644 if (qtContexts) |
|
645 splitContext(&item.oldTscomment, &item.context); |
618 } else { |
646 } else { |
619 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) |
647 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'")) |
620 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
648 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
621 error = true; |
649 error = true; |
622 } |
650 } |
623 break; |
651 break; |
624 case '~': |
652 case '~': |
631 codec->toUnicode(extra); |
659 codec->toUnicode(extra); |
632 item.isPlural = true; |
660 item.isPlural = true; |
633 } else if (line.startsWith("#~ msgctxt ")) { |
661 } else if (line.startsWith("#~ msgctxt ")) { |
634 item.tscomment = slurpEscapedString(lines, l, 11, "#~ ", cd); |
662 item.tscomment = slurpEscapedString(lines, l, 11, "#~ ", cd); |
635 } else { |
663 } else { |
636 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) |
664 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'")) |
637 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
665 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
638 error = true; |
666 error = true; |
639 } |
667 } |
640 break; |
668 break; |
641 default: |
669 default: |
642 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) |
670 cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'")) |
643 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
671 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
644 error = true; |
672 error = true; |
645 break; |
673 break; |
646 } |
674 } |
647 lastCmtLine = l; |
675 lastCmtLine = l; |
648 } else if (line.startsWith("msgctxt ")) { |
676 } else if (line.startsWith("msgctxt ")) { |
649 item.tscomment = slurpEscapedString(lines, l, 8, QByteArray(), cd); |
677 item.tscomment = slurpEscapedString(lines, l, 8, QByteArray(), cd); |
|
678 if (qtContexts) |
|
679 splitContext(&item.tscomment, &item.context); |
650 } else if (line.startsWith("msgid ")) { |
680 } else if (line.startsWith("msgid ")) { |
651 item.msgId = slurpEscapedString(lines, l, 6, QByteArray(), cd); |
681 item.msgId = slurpEscapedString(lines, l, 6, QByteArray(), cd); |
652 } else if (line.startsWith("msgid_plural ")) { |
682 } else if (line.startsWith("msgid_plural ")) { |
653 QByteArray extra = slurpEscapedString(lines, l, 13, QByteArray(), cd); |
683 QByteArray extra = slurpEscapedString(lines, l, 13, QByteArray(), cd); |
654 if (extra != item.msgId) |
684 if (extra != item.msgId) |
655 item.extra[QLatin1String("po-msgid_plural")] = codec->toUnicode(extra); |
685 item.extra[QLatin1String("po-msgid_plural")] = codec->toUnicode(extra); |
656 item.isPlural = true; |
686 item.isPlural = true; |
657 } else { |
687 } else { |
658 cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'\n")) |
688 cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'")) |
659 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
689 .arg(l + 1).arg(codec->toUnicode(lines[l]))); |
660 error = true; |
690 error = true; |
661 } |
691 } |
662 } |
692 } |
663 return !error && cd.errors().isEmpty(); |
693 return !error && cd.errors().isEmpty(); |
670 if (!hdrOrder.contains(qName)) |
700 if (!hdrOrder.contains(qName)) |
671 hdrOrder << qName; |
701 hdrOrder << qName; |
672 headers[makePoHeader(qName)] = value; |
702 headers[makePoHeader(qName)] = value; |
673 } |
703 } |
674 |
704 |
|
705 static QString escapeComment(const QString &in, bool escape) |
|
706 { |
|
707 QString out = in; |
|
708 if (escape) { |
|
709 out.replace(QLatin1Char('~'), QLatin1String("~~")); |
|
710 out.replace(QLatin1Char('|'), QLatin1String("~|")); |
|
711 } |
|
712 return out; |
|
713 } |
|
714 |
675 bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd) |
715 bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd) |
676 { |
716 { |
677 QString str_format = QLatin1String("-format"); |
717 QString str_format = QLatin1String("-format"); |
678 |
718 |
679 bool ok = true; |
719 bool ok = true; |
680 QTextStream out(&dev); |
720 QTextStream out(&dev); |
681 out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec); |
721 out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec); |
|
722 |
|
723 bool qtContexts = false; |
|
724 foreach (const TranslatorMessage &msg, translator.messages()) |
|
725 if (!msg.context().isEmpty()) { |
|
726 qtContexts = true; |
|
727 break; |
|
728 } |
682 |
729 |
683 QString cmt = translator.extra(QLatin1String("po-header_comment")); |
730 QString cmt = translator.extra(QLatin1String("po-header_comment")); |
684 if (!cmt.isEmpty()) |
731 if (!cmt.isEmpty()) |
685 out << cmt << '\n'; |
732 out << cmt << '\n'; |
686 out << "msgid \"\"\n"; |
733 out << "msgid \"\"\n"; |
701 addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules)); |
748 addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules)); |
702 addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode()); |
749 addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode()); |
703 } |
750 } |
704 if (!translator.sourceLanguageCode().isEmpty()) |
751 if (!translator.sourceLanguageCode().isEmpty()) |
705 addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode()); |
752 addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode()); |
|
753 if (qtContexts) |
|
754 addPoHeader(headers, hdrOrder, "X-Qt-Contexts", QLatin1String("true")); |
706 QString hdrStr; |
755 QString hdrStr; |
707 foreach (const QString &hdr, hdrOrder) { |
756 foreach (const QString &hdr, hdrOrder) { |
708 hdrStr += hdr; |
757 hdrStr += hdr; |
709 hdrStr += QLatin1String(": "); |
758 hdrStr += QLatin1String(": "); |
710 hdrStr += headers.value(makePoHeader(hdr)); |
759 hdrStr += headers.value(makePoHeader(hdr)); |
719 out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment()); |
768 out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment()); |
720 |
769 |
721 if (!msg.extraComment().isEmpty()) |
770 if (!msg.extraComment().isEmpty()) |
722 out << poEscapedLines(QLatin1String("#."), true, msg.extraComment()); |
771 out << poEscapedLines(QLatin1String("#."), true, msg.extraComment()); |
723 |
772 |
724 if (!msg.context().isEmpty()) |
|
725 out << QLatin1String("#. ts-context ") << msg.context() << '\n'; |
|
726 if (!msg.id().isEmpty()) |
773 if (!msg.id().isEmpty()) |
727 out << QLatin1String("#. ts-id ") << msg.id() << '\n'; |
774 out << QLatin1String("#. ts-id ") << msg.id() << '\n'; |
728 |
775 |
729 if (!msg.fileName().isEmpty() && msg.fileName() != QLatin1String(MAGIC_OBSOLETE_REFERENCE)) { |
776 if (!msg.fileName().isEmpty() && msg.fileName() != QLatin1String(MAGIC_OBSOLETE_REFERENCE)) { |
730 QStringList refs; |
777 QStringList refs; |
768 if (!flags.isEmpty()) |
815 if (!flags.isEmpty()) |
769 out << "#, " << flags.join(QLatin1String(", ")) << '\n'; |
816 out << "#, " << flags.join(QLatin1String(", ")) << '\n'; |
770 |
817 |
771 QString prefix = QLatin1String("#| "); |
818 QString prefix = QLatin1String("#| "); |
772 if (!msg.oldComment().isEmpty()) |
819 if (!msg.oldComment().isEmpty()) |
773 out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.oldComment()); |
820 out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, |
|
821 escapeComment(msg.oldComment(), qtContexts)); |
774 if (!msg.oldSourceText().isEmpty()) |
822 if (!msg.oldSourceText().isEmpty()) |
775 out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.oldSourceText()); |
823 out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.oldSourceText()); |
776 QString plural = msg.extra(QLatin1String("po-old_msgid_plural")); |
824 QString plural = msg.extra(QLatin1String("po-old_msgid_plural")); |
777 if (!plural.isEmpty()) |
825 if (!plural.isEmpty()) |
778 out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural); |
826 out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural); |
779 prefix = QLatin1String((msg.type() == TranslatorMessage::Obsolete) ? "#~ " : ""); |
827 prefix = QLatin1String((msg.type() == TranslatorMessage::Obsolete) ? "#~ " : ""); |
780 if (!msg.comment().isEmpty()) |
828 if (!msg.context().isEmpty()) |
781 out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.comment()); |
829 out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, |
|
830 escapeComment(msg.context(), true) + QLatin1Char('|') |
|
831 + escapeComment(msg.comment(), true)); |
|
832 else if (!msg.comment().isEmpty()) |
|
833 out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, |
|
834 escapeComment(msg.comment(), qtContexts)); |
782 out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText()); |
835 out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText()); |
783 if (!msg.isPlural()) { |
836 if (!msg.isPlural()) { |
784 QString transl = msg.translation(); |
837 QString transl = msg.translation(); |
785 transl.replace(QChar(Translator::BinaryVariantSeparator), |
838 transl.replace(QChar(Translator::BinaryVariantSeparator), |
786 QChar(Translator::TextVariantSeparator)); |
839 QChar(Translator::TextVariantSeparator)); |