source: branches/vendor/trolltech/qt/current/tools/linguist/shared/ts.cpp @ 2

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 29.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@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::DefaultVariantSeparator);
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::DefaultVariantSeparator))) >= 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::DefaultVariantSeparator), 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                    QStringList translns = translator.normalizedTranslations(msg, cd, &result);
697                    for (int j = 0; j < qMax(1, 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.