source: trunk/tools/linguist/shared/ts.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 29.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the Qt Linguist of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "translator.h"
43
44#include <QtCore/QByteArray>
45#include <QtCore/QDebug>
46#include <QtCore/QTextCodec>
47#include <QtCore/QTextStream>
48
49#include <QtXml/QXmlStreamReader>
50#include <QtXml/QXmlStreamAttribute>
51
52#define STRINGIFY_INTERNAL(x) #x
53#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
54#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
55
56QT_BEGIN_NAMESPACE
57
58/*
59 * The encodings are a total mess.
60 * A Translator has a codecForTr(). Each message's text will be passed to tr()
61 * in that encoding or as UTF-8 to trUtf8() if it is flagged as such.
62 * For ts 2.0, the file content is always uniformly in UTF-8. The file stores
63 * the codecForTr default and marks deviating messages accordingly.
64 * For ts 1.1, the file content is in mixed encoding. Each message is encoded
65 * the way it will be passed to tr() (with 8-bit characters encoded as numeric
66 * entities) or trUtf8(). The file stores the encoding and codecForTr in one
67 * attribute, for both the default and each deviating message.
68 */
69
70
71QDebug &operator<<(QDebug &d, const QXmlStreamAttribute &attr)
72{
73 return d << "[" << attr.name().toString() << "," << attr.value().toString() << "]";
74}
75
76
77class TSReader : public QXmlStreamReader
78{
79public:
80 TSReader(QIODevice &dev, ConversionData &cd)
81 : QXmlStreamReader(&dev), m_cd(cd)
82 {}
83
84 // the "real thing"
85 bool read(Translator &translator);
86
87private:
88 bool elementStarts(const QString &str) const
89 {
90 return isStartElement() && name() == str;
91 }
92
93 bool isWhiteSpace() const
94 {
95 return isCharacters() && text().toString().trimmed().isEmpty();
96 }
97
98 // needed to expand <byte ... />
99 QString readContents();
100 // needed to join <lengthvariant>s
101 QString readTransContents();
102
103 void handleError();
104
105 ConversionData &m_cd;
106};
107
108void TSReader::handleError()
109{
110 if (isComment())
111 return;
112 if (hasError() && error() == CustomError) // raised by readContents
113 return;
114
115 const QString loc = QString::fromLatin1("at %3:%1:%2")
116 .arg(lineNumber()).arg(columnNumber()).arg(m_cd.m_sourceFileName);
117
118 switch (tokenType()) {
119 case NoToken: // Cannot happen
120 default: // likewise
121 case Invalid:
122 raiseError(QString::fromLatin1("Parse error %1: %2").arg(loc, errorString()));
123 break;
124 case StartElement:
125 raiseError(QString::fromLatin1("Unexpected tag <%1> %2").arg(name().toString(), loc));
126 break;
127 case Characters:
128 {
129 QString tok = text().toString();
130 if (tok.length() > 30)
131 tok = tok.left(30) + QLatin1String("[...]");
132 raiseError(QString::fromLatin1("Unexpected characters '%1' %2").arg(tok, loc));
133 }
134 break;
135 case EntityReference:
136 raiseError(QString::fromLatin1("Unexpected entity '&%1;' %2").arg(name().toString(), loc));
137 break;
138 case ProcessingInstruction:
139 raiseError(QString::fromLatin1("Unexpected processing instruction %1").arg(loc));
140 break;
141 }
142}
143
144static QString byteValue(QString value)
145{
146 int base = 10;
147 if (value.startsWith(QLatin1String("x"))) {
148 base = 16;
149 value.remove(0, 1);
150 }
151 int n = value.toUInt(0, base);
152 return (n != 0) ? QString(QChar(n)) : QString();
153}
154
155QString TSReader::readContents()
156{
157 STRING(byte);
158 STRING(value);
159
160 QString result;
161 while (!atEnd()) {
162 readNext();
163 if (isEndElement()) {
164 break;
165 } else if (isCharacters()) {
166 result += text();
167 } else if (elementStarts(strbyte)) {
168 // <byte value="...">
169 result += byteValue(attributes().value(strvalue).toString());
170 readNext();
171 if (!isEndElement()) {
172 handleError();
173 break;
174 }
175 } else {
176 handleError();
177 break;
178 }
179 }
180 //qDebug() << "TEXT: " << result;
181 return result;
182}
183
184QString TSReader::readTransContents()
185{
186 STRING(lengthvariant);
187 STRING(variants);
188 STRING(yes);
189
190 if (attributes().value(strvariants) == stryes) {
191 QString result;
192 while (!atEnd()) {
193 readNext();
194 if (isEndElement()) {
195 break;
196 } else if (isWhiteSpace()) {
197 // ignore these, just whitespace
198 } else if (elementStarts(strlengthvariant)) {
199 if (!result.isEmpty())
200 result += QChar(Translator::BinaryVariantSeparator);
201 result += readContents();
202 } else {
203 handleError();
204 break;
205 }
206 }
207 return result;
208 } else {
209 return readContents();
210 }
211}
212
213bool TSReader::read(Translator &translator)
214{
215 STRING(both);
216 STRING(byte);
217 STRING(comment);
218 STRING(context);
219 STRING(defaultcodec);
220 STRING(encoding);
221 STRING(extracomment);
222 STRING(filename);
223 STRING(id);
224 STRING(language);
225 STRING(line);
226 STRING(location);
227 STRING(message);
228 STRING(name);
229 STRING(numerus);
230 STRING(numerusform);
231 STRING(obsolete);
232 STRING(oldcomment);
233 STRING(oldsource);
234 STRING(source);
235 STRING(sourcelanguage);
236 STRING(translation);
237 STRING(translatorcomment);
238 STRING(true);
239 STRING(TS);
240 STRING(type);
241 STRING(unfinished);
242 STRING(userdata);
243 STRING(utf8);
244 STRING(value);
245 //STRING(version);
246 STRING(yes);
247
248 static const QString strextrans(QLatin1String("extra-"));
249 static const QString strUtf8(QLatin1String("UTF-8"));
250
251 while (!atEnd()) {
252 readNext();
253 if (isStartDocument()) {
254 // <!DOCTYPE TS>
255 //qDebug() << attributes();
256 } else if (isEndDocument()) {
257 // <!DOCTYPE TS>
258 //qDebug() << attributes();
259 } else if (isDTD()) {
260 // <!DOCTYPE TS>
261 //qDebug() << tokenString();
262 } else if (elementStarts(strTS)) {
263 // <TS>
264 //qDebug() << "TS " << attributes();
265 QHash<QString, int> currentLine;
266 QString currentFile;
267
268 QXmlStreamAttributes atts = attributes();
269 //QString version = atts.value(strversion).toString();
270 translator.setLanguageCode(atts.value(strlanguage).toString());
271 translator.setSourceLanguageCode(atts.value(strsourcelanguage).toString());
272 while (!atEnd()) {
273 readNext();
274 if (isEndElement()) {
275 // </TS> found, finish local loop
276 break;
277 } else if (isWhiteSpace()) {
278 // ignore these, just whitespace
279 } else if (elementStarts(strdefaultcodec)) {
280 // <defaultcodec>
281 const QString &codec = readElementText();
282 if (!codec.isEmpty())
283 translator.setCodecName(codec.toLatin1());
284 // </defaultcodec>
285 } else if (isStartElement()
286 && name().toString().startsWith(strextrans)) {
287 // <extra-...>
288 QString tag = name().toString();
289 translator.setExtra(tag.mid(6), readContents());
290 // </extra-...>
291 } else if (elementStarts(strcontext)) {
292 // <context>
293 QString context;
294 while (!atEnd()) {
295 readNext();
296 if (isEndElement()) {
297 // </context> found, finish local loop
298 break;
299 } else if (isWhiteSpace()) {
300 // ignore these, just whitespace
301 } else if (elementStarts(strname)) {
302 // <name>
303 context = readElementText();
304 // </name>
305 } else if (elementStarts(strmessage)) {
306 // <message>
307 TranslatorMessage::References refs;
308 QString currentMsgFile = currentFile;
309
310 TranslatorMessage msg;
311 msg.setId(attributes().value(strid).toString());
312 msg.setContext(context);
313 msg.setType(TranslatorMessage::Finished);
314 msg.setPlural(attributes().value(strnumerus) == stryes);
315 const QStringRef &utf8Attr = attributes().value(strutf8);
316 msg.setNonUtf8(utf8Attr == strboth);
317 msg.setUtf8(msg.isNonUtf8() || utf8Attr == strtrue
318 || attributes().value(strencoding) == strUtf8);
319 while (!atEnd()) {
320 readNext();
321 if (isEndElement()) {
322 // </message> found, finish local loop
323 msg.setReferences(refs);
324 translator.append(msg);
325 break;
326 } else if (isWhiteSpace()) {
327 // ignore these, just whitespace
328 } else if (elementStarts(strsource)) {
329 // <source>...</source>
330 msg.setSourceText(readContents());
331 } else if (elementStarts(stroldsource)) {
332 // <oldsource>...</oldsource>
333 msg.setOldSourceText(readContents());
334 } else if (elementStarts(stroldcomment)) {
335 // <oldcomment>...</oldcomment>
336 msg.setOldComment(readContents());
337 } else if (elementStarts(strextracomment)) {
338 // <extracomment>...</extracomment>
339 msg.setExtraComment(readContents());
340 } else if (elementStarts(strtranslatorcomment)) {
341 // <translatorcomment>...</translatorcomment>
342 msg.setTranslatorComment(readContents());
343 } else if (elementStarts(strlocation)) {
344 // <location/>
345 QXmlStreamAttributes atts = attributes();
346 QString fileName = atts.value(strfilename).toString();
347 if (fileName.isEmpty()) {
348 fileName = currentMsgFile;
349 } else {
350 if (refs.isEmpty())
351 currentFile = fileName;
352 currentMsgFile = fileName;
353 }
354 const QString lin = atts.value(strline).toString();
355 if (lin.isEmpty()) {
356 translator.setLocationsType(Translator::RelativeLocations);
357 refs.append(TranslatorMessage::Reference(fileName, -1));
358 } else {
359 bool bOK;
360 int lineNo = lin.toInt(&bOK);
361 if (bOK) {
362 if (lin.startsWith(QLatin1Char('+')) || lin.startsWith(QLatin1Char('-'))) {
363 lineNo = (currentLine[fileName] += lineNo);
364 translator.setLocationsType(Translator::RelativeLocations);
365 } else {
366 translator.setLocationsType(Translator::AbsoluteLocations);
367 }
368 refs.append(TranslatorMessage::Reference(fileName, lineNo));
369 }
370 }
371 readContents();
372 } else if (elementStarts(strcomment)) {
373 // <comment>...</comment>
374 msg.setComment(readContents());
375 } else if (elementStarts(struserdata)) {
376 // <userdata>...</userdata>
377 msg.setUserData(readContents());
378 } else if (elementStarts(strtranslation)) {
379 // <translation>
380 QXmlStreamAttributes atts = attributes();
381 QStringRef type = atts.value(strtype);
382 if (type == strunfinished)
383 msg.setType(TranslatorMessage::Unfinished);
384 else if (type == strobsolete)
385 msg.setType(TranslatorMessage::Obsolete);
386 if (msg.isPlural()) {
387 QStringList translations;
388 while (!atEnd()) {
389 readNext();
390 if (isEndElement()) {
391 break;
392 } else if (isWhiteSpace()) {
393 // ignore these, just whitespace
394 } else if (elementStarts(strnumerusform)) {
395 translations.append(readTransContents());
396 } else {
397 handleError();
398 break;
399 }
400 }
401 msg.setTranslations(translations);
402 } else {
403 msg.setTranslation(readTransContents());
404 }
405 // </translation>
406 } else if (isStartElement()
407 && name().toString().startsWith(strextrans)) {
408 // <extra-...>
409 QString tag = name().toString();
410 msg.setExtra(tag.mid(6), readContents());
411 // </extra-...>
412 } else {
413 handleError();
414 }
415 }
416 // </message>
417 } else {
418 handleError();
419 }
420 }
421 // </context>
422 } else {
423 handleError();
424 }
425 } // </TS>
426 } else {
427 handleError();
428 }
429 }
430 if (hasError()) {
431 m_cd.appendError(errorString());
432 return false;
433 }
434 return true;
435}
436
437static QString numericEntity(int ch)
438{
439 return QString(ch <= 0x20 ? QLatin1String("<byte value=\"x%1\"/>")
440 : QLatin1String("&#x%1;")) .arg(ch, 0, 16);
441}
442
443static QString protect(const QString &str)
444{
445 QString result;
446 result.reserve(str.length() * 12 / 10);
447 for (int i = 0; i != str.size(); ++i) {
448 uint c = str.at(i).unicode();
449 switch (c) {
450 case '\"':
451 result += QLatin1String("&quot;");
452 break;
453 case '&':
454 result += QLatin1String("&amp;");
455 break;
456 case '>':
457 result += QLatin1String("&gt;");
458 break;
459 case '<':
460 result += QLatin1String("&lt;");
461 break;
462 case '\'':
463 result += QLatin1String("&apos;");
464 break;
465 default:
466 if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
467 result += numericEntity(c);
468 else // this also covers surrogates
469 result += QChar(c);
470 }
471 }
472 return result;
473}
474
475static QString evilBytes(const QString& str,
476 bool isUtf8, int format, const QByteArray &codecName)
477{
478 //qDebug() << "EVIL: " << str << isUtf8 << format << codecName;
479 if (isUtf8)
480 return protect(str);
481 if (format == 20)
482 return protect(str);
483 if (codecName == "UTF-8")
484 return protect(str);
485 QTextCodec *codec = QTextCodec::codecForName(codecName);
486 if (!codec)
487 return protect(str);
488 QString t = QString::fromLatin1(codec->fromUnicode(protect(str)).data());
489 int len = (int) t.length();
490 QString result;
491 // FIXME: Factor is sensible only for latin scripts, probably.
492 result.reserve(t.length() * 2);
493 for (int k = 0; k < len; k++) {
494 if (t[k].unicode() >= 0x7f)
495 result += numericEntity(t[k].unicode());
496 else
497 result += t[k];
498 }
499 return result;
500}
501
502static void writeExtras(QTextStream &t, const char *indent,
503 const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
504{
505 for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
506 if (!drops.exactMatch(it.key())) {
507 t << indent << "<extra-" << it.key() << '>'
508 << protect(it.value())
509 << "</extra-" << it.key() << ">\n";
510 }
511 }
512}
513
514static void writeVariants(QTextStream &t, const char *indent, const QString &input)
515{
516 int offset;
517 if ((offset = input.indexOf(QChar(Translator::BinaryVariantSeparator))) >= 0) {
518 t << " variants=\"yes\">";
519 int start = 0;
520 forever {
521 t << "\n " << indent << "<lengthvariant>"
522 << protect(input.mid(start, offset - start))
523 << "</lengthvariant>";
524 if (offset == input.length())
525 break;
526 start = offset + 1;
527 offset = input.indexOf(QChar(Translator::BinaryVariantSeparator), start);
528 if (offset < 0)
529 offset = input.length();
530 }
531 t << "\n" << indent;
532 } else {
533 t << ">" << protect(input);
534 }
535}
536
537bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, int format)
538{
539 bool result = true;
540 QTextStream t(&dev);
541 t.setCodec(QTextCodec::codecForName("UTF-8"));
542 bool trIsUtf8 = (translator.codecName() == "UTF-8");
543 //qDebug() << translator.codecName();
544 bool fileIsUtf8 = (format == 20 || trIsUtf8);
545
546 // The xml prolog allows processors to easily detect the correct encoding
547 t << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n";
548
549 if (format == 11)
550 t << "<TS version=\"1.1\"";
551 else
552 t << "<TS version=\"2.0\"";
553
554 QString languageCode = translator.languageCode();
555 if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
556 t << " language=\"" << languageCode << "\"";
557 if (format == 20) {
558 languageCode = translator.sourceLanguageCode();
559 if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
560 t << " sourcelanguage=\"" << languageCode << "\"";
561 }
562 t << ">\n";
563
564 QByteArray codecName = translator.codecName();
565 if (codecName != "ISO-8859-1")
566 t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
567
568 QRegExp drops(cd.dropTags().join(QLatin1String("|")));
569
570 if (format == 20)
571 writeExtras(t, " ", translator.extras(), drops);
572
573 QHash<QString, QList<TranslatorMessage> > messageOrder;
574 QList<QString> contextOrder;
575 foreach (const TranslatorMessage &msg, translator.messages()) {
576 // no need for such noise
577 if (msg.type() == TranslatorMessage::Obsolete && msg.translation().isEmpty())
578 continue;
579
580 QList<TranslatorMessage> &context = messageOrder[msg.context()];
581 if (context.isEmpty())
582 contextOrder.append(msg.context());
583 context.append(msg);
584 }
585 if (cd.sortContexts())
586 qSort(contextOrder);
587
588 QHash<QString, int> currentLine;
589 QString currentFile;
590 foreach (const QString &context, contextOrder) {
591 const TranslatorMessage &firstMsg = messageOrder[context].first();
592 t << "<context" << ((!fileIsUtf8 && firstMsg.isUtf8()) ? " encoding=\"UTF-8\"" : "") << ">\n";
593
594 t << " <name>"
595 << evilBytes(context, firstMsg.isUtf8() || fileIsUtf8, format, codecName)
596 << "</name>\n";
597 foreach (const TranslatorMessage &msg, messageOrder[context]) {
598 //msg.dump();
599
600 bool isUtf8 = msg.isUtf8();
601 bool second = false;
602 forever {
603
604 t << " <message";
605 if (!msg.id().isEmpty())
606 t << " id=\"" << msg.id() << "\"";
607 if (!trIsUtf8) {
608 if (format == 11) {
609 if (isUtf8)
610 t << " encoding=\"UTF-8\"";
611 } else {
612 if (msg.isUtf8()) {
613 if (msg.isNonUtf8())
614 t << " utf8=\"both\"";
615 else
616 t << " utf8=\"true\"";
617 }
618 }
619 }
620 if (msg.isPlural())
621 t << " numerus=\"yes\"";
622 t << ">\n";
623 if (translator.locationsType() != Translator::NoLocations) {
624 QString cfile = currentFile;
625 bool first = true;
626 foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) {
627 QString fn = cd.m_targetDir.relativeFilePath(ref.fileName())
628 .replace(QLatin1Char('\\'),QLatin1Char('/'));
629 int ln = ref.lineNumber();
630 QString ld;
631 if (translator.locationsType() == Translator::RelativeLocations) {
632 if (ln != -1) {
633 int dlt = ln - currentLine[fn];
634 if (dlt >= 0)
635 ld.append(QLatin1Char('+'));
636 ld.append(QString::number(dlt));
637 currentLine[fn] = ln;
638 }
639
640 if (fn != cfile) {
641 if (first)
642 currentFile = fn;
643 cfile = fn;
644 } else {
645 fn.clear();
646 }
647 first = false;
648 } else {
649 if (ln != -1)
650 ld = QString::number(ln);
651 }
652 t << " <location";
653 if (!fn.isEmpty())
654 t << " filename=\"" << fn << "\"";
655 if (!ld.isEmpty())
656 t << " line=\"" << ld << "\"";
657 t << "/>\n";
658 }
659 }
660
661 t << " <source>"
662 << evilBytes(msg.sourceText(), isUtf8, format, codecName)
663 << "</source>\n";
664
665 if (format != 11 && !msg.oldSourceText().isEmpty())
666 t << " <oldsource>" << protect(msg.oldSourceText()) << "</oldsource>\n";
667
668 if (!msg.comment().isEmpty()) {
669 t << " <comment>"
670 << evilBytes(msg.comment(), isUtf8, format, codecName)
671 << "</comment>\n";
672 }
673
674 if (format != 11) {
675
676 if (!msg.oldComment().isEmpty())
677 t << " <oldcomment>" << protect(msg.oldComment()) << "</oldcomment>\n";
678
679 if (!msg.extraComment().isEmpty())
680 t << " <extracomment>" << protect(msg.extraComment())
681 << "</extracomment>\n";
682
683 if (!msg.translatorComment().isEmpty())
684 t << " <translatorcomment>" << protect(msg.translatorComment())
685 << "</translatorcomment>\n";
686
687 }
688
689 t << " <translation";
690 if (msg.type() == TranslatorMessage::Unfinished)
691 t << " type=\"unfinished\"";
692 else if (msg.type() == TranslatorMessage::Obsolete)
693 t << " type=\"obsolete\"";
694 if (msg.isPlural()) {
695 t << ">";
696 const QStringList &translns = msg.translations();
697 for (int j = 0; j < translns.count(); ++j) {
698 t << "\n <numerusform";
699 writeVariants(t, " ", translns[j]);
700 t << "</numerusform>";
701 }
702 t << "\n ";
703 } else {
704 writeVariants(t, " ", msg.translation());
705 }
706 t << "</translation>\n";
707
708 if (format != 11)
709 writeExtras(t, " ", msg.extras(), drops);
710
711 if (!msg.userData().isEmpty())
712 t << " <userdata>" << msg.userData() << "</userdata>\n";
713 t << " </message>\n";
714
715 if (format != 11 || second || !msg.isUtf8() || !msg.isNonUtf8())
716 break;
717 isUtf8 = false;
718 second = true;
719 }
720 }
721 t << "</context>\n";
722 }
723
724 t << "</TS>\n";
725 return result;
726}
727
728bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd)
729{
730 translator.setLocationsType(Translator::NoLocations);
731 TSReader reader(dev, cd);
732 return reader.read(translator);
733}
734
735bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd)
736{
737 return saveTS(translator, dev, cd, 11);
738}
739
740bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd)
741{
742 return saveTS(translator, dev, cd, 20);
743}
744
745int initTS()
746{
747 Translator::FileFormat format;
748
749 format.extension = QLatin1String("ts11");
750 format.fileType = Translator::FileFormat::TranslationSource;
751 format.priority = -1;
752 format.description = QObject::tr("Qt translation sources (format 1.1)");
753 format.loader = &loadTS;
754 format.saver = &saveTS11;
755 Translator::registerFileFormat(format);
756
757 format.extension = QLatin1String("ts20");
758 format.fileType = Translator::FileFormat::TranslationSource;
759 format.priority = -1;
760 format.description = QObject::tr("Qt translation sources (format 2.0)");
761 format.loader = &loadTS;
762 format.saver = &saveTS20;
763 Translator::registerFileFormat(format);
764
765 // "ts" is always the latest. right now it's ts20.
766 format.extension = QLatin1String("ts");
767 format.fileType = Translator::FileFormat::TranslationSource;
768 format.priority = 0;
769 format.description = QObject::tr("Qt translation sources (latest format)");
770 format.loader = &loadTS;
771 format.saver = &saveTS20;
772 Translator::registerFileFormat(format);
773
774 return 1;
775}
776
777Q_CONSTRUCTOR_FUNCTION(initTS)
778
779QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.