summaryrefslogtreecommitdiffstats
path: root/tools/linguist
diff options
context:
space:
mode:
Diffstat (limited to 'tools/linguist')
-rw-r--r--tools/linguist/lconvert/main.cpp2
-rw-r--r--tools/linguist/linguist.pro2
-rw-r--r--tools/linguist/linguist/linguist.pro7
-rw-r--r--tools/linguist/linguist/mainwindow.cpp46
-rw-r--r--tools/linguist/linguist/mainwindow.ui2
-rw-r--r--tools/linguist/linguist/messagemodel.cpp2
-rw-r--r--tools/linguist/lupdate/lupdate.h1
-rw-r--r--tools/linguist/lupdate/lupdate.pro4
-rw-r--r--tools/linguist/lupdate/main.cpp144
-rw-r--r--tools/linguist/lupdate/qdeclarative.cpp240
-rw-r--r--tools/linguist/lupdate/qscript.cpp41
-rw-r--r--tools/linguist/shared/numerus.cpp65
-rw-r--r--tools/linguist/shared/po.cpp472
-rw-r--r--tools/linguist/shared/qm.cpp4
-rw-r--r--tools/linguist/shared/translator.cpp19
-rw-r--r--tools/linguist/shared/translator.h2
-rw-r--r--tools/linguist/shared/xliff.cpp2
17 files changed, 774 insertions, 281 deletions
diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp
index 3a30027..543c405 100644
--- a/tools/linguist/lconvert/main.cpp
+++ b/tools/linguist/lconvert/main.cpp
@@ -48,6 +48,8 @@
#include <iostream>
+QT_USE_NAMESPACE
+
static int usage(const QStringList &args)
{
Q_UNUSED(args);
diff --git a/tools/linguist/linguist.pro b/tools/linguist/linguist.pro
index 85ecd5a..248c89e 100644
--- a/tools/linguist/linguist.pro
+++ b/tools/linguist/linguist.pro
@@ -1,6 +1,6 @@
TEMPLATE = subdirs
SUBDIRS = \
- linguist \
lrelease \
lupdate \
lconvert
+!no-png:!contains(QT_CONFIG, no-gui):SUBDIRS += linguist
diff --git a/tools/linguist/linguist/linguist.pro b/tools/linguist/linguist/linguist.pro
index 693bc71..4f7ed8a 100644
--- a/tools/linguist/linguist/linguist.pro
+++ b/tools/linguist/linguist/linguist.pro
@@ -9,7 +9,7 @@ CONFIG += qt \
uitools
DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
-build_all:!build_pass {
+build_all:!build_pass {
CONFIG -= build_all
CONFIG += release
}
@@ -42,7 +42,7 @@ SOURCES += \
statistics.cpp \
translatedialog.cpp \
translationsettingsdialog.cpp \
- ../shared/simtexth.cpp
+ ../shared/simtexth.cpp
HEADERS += \
batchtranslationdialog.h \
@@ -71,7 +71,7 @@ contains(QT_PRODUCT, OpenSource.*):DEFINES *= QT_OPENSOURCE
DEFINES += QT_KEYWORDS
TARGET = linguist
win32:RC_FILE = linguist.rc
-mac {
+mac {
static:CONFIG -= global_init_link_order
ICON = linguist.icns
TARGET = Linguist
@@ -100,6 +100,7 @@ TRANSLATIONS = \
$$TR_DIR/linguist_cs.ts \
$$TR_DIR/linguist_de.ts \
$$TR_DIR/linguist_fr.ts \
+ $$TR_DIR/linguist_hu.ts \
$$TR_DIR/linguist_ja.ts \
$$TR_DIR/linguist_pl.ts \
$$TR_DIR/linguist_ru.ts \
diff --git a/tools/linguist/linguist/mainwindow.cpp b/tools/linguist/linguist/mainwindow.cpp
index 321fe8c..1611699 100644
--- a/tools/linguist/linguist/mainwindow.cpp
+++ b/tools/linguist/linguist/mainwindow.cpp
@@ -1810,28 +1810,50 @@ QString MainWindow::friendlyString(const QString& str)
return f.simplified();
}
+static inline void setThemeIcon(QAction *action, const char *name, const char *fallback)
+{
+ const QIcon fallbackIcon(MainWindow::resourcePrefix() + QLatin1String(fallback));
+#ifdef Q_WS_X11
+ action->setIcon(QIcon::fromTheme(QLatin1String(name), fallbackIcon));
+#else
+ Q_UNUSED(name)
+ action->setIcon(fallbackIcon);
+#endif
+}
+
void MainWindow::setupMenuBar()
{
+ // There are no fallback icons for these
+#ifdef Q_WS_X11
+ m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QLatin1String("document-open-recent")));
+ m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QLatin1String("window-close")));
+ m_ui.actionExit->setIcon(QIcon::fromTheme(QLatin1String("application-exit")));
+ m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QLatin1String("edit-select-all")));
+#endif
+
+ // Prefer theme icons when available for these actions
+ setThemeIcon(m_ui.actionOpen, "document-open", "/fileopen.png");
+ setThemeIcon(m_ui.actionOpenAux, "document-open", "/fileopen.png");
+ setThemeIcon(m_ui.actionSave, "document-save", "/filesave.png");
+ setThemeIcon(m_ui.actionSaveAll, "document-save", "/filesave.png");
+ setThemeIcon(m_ui.actionPrint, "document-print", "/print.png");
+ setThemeIcon(m_ui.actionRedo, "edit-redo", "/redo.png");
+ setThemeIcon(m_ui.actionUndo, "edit-undo", "/undo.png");
+ setThemeIcon(m_ui.actionCut, "edit-cut", "/editcut.png");
+ setThemeIcon(m_ui.actionCopy, "edit-copy", "/editcopy.png");
+ setThemeIcon(m_ui.actionPaste, "edit-paste", "/editpaste.png");
+ setThemeIcon(m_ui.actionFind, "edit-find", "/searchfind.png");
+
+ // No well defined theme icons for these actions
m_ui.actionAccelerators->setIcon(QIcon(resourcePrefix() + QLatin1String("/accelerator.png")));
m_ui.actionOpenPhraseBook->setIcon(QIcon(resourcePrefix() + QLatin1String("/book.png")));
m_ui.actionDoneAndNext->setIcon(QIcon(resourcePrefix() + QLatin1String("/doneandnext.png")));
- m_ui.actionCopy->setIcon(QIcon(resourcePrefix() + QLatin1String("/editcopy.png")));
- m_ui.actionCut->setIcon(QIcon(resourcePrefix() + QLatin1String("/editcut.png")));
- m_ui.actionPaste->setIcon(QIcon(resourcePrefix() + QLatin1String("/editpaste.png")));
- m_ui.actionOpen->setIcon(QIcon(resourcePrefix() + QLatin1String("/fileopen.png")));
- m_ui.actionOpenAux->setIcon(QIcon(resourcePrefix() + QLatin1String("/fileopen.png")));
- m_ui.actionSaveAll->setIcon(QIcon(resourcePrefix() + QLatin1String("/filesave.png")));
- m_ui.actionSave->setIcon(QIcon(resourcePrefix() + QLatin1String("/filesave.png")));
m_ui.actionNext->setIcon(QIcon(resourcePrefix() + QLatin1String("/next.png")));
m_ui.actionNextUnfinished->setIcon(QIcon(resourcePrefix() + QLatin1String("/nextunfinished.png")));
m_ui.actionPhraseMatches->setIcon(QIcon(resourcePrefix() + QLatin1String("/phrase.png")));
m_ui.actionEndingPunctuation->setIcon(QIcon(resourcePrefix() + QLatin1String("/punctuation.png")));
m_ui.actionPrev->setIcon(QIcon(resourcePrefix() + QLatin1String("/prev.png")));
m_ui.actionPrevUnfinished->setIcon(QIcon(resourcePrefix() + QLatin1String("/prevunfinished.png")));
- m_ui.actionPrint->setIcon(QIcon(resourcePrefix() + QLatin1String("/print.png")));
- m_ui.actionRedo->setIcon(QIcon(resourcePrefix() + QLatin1String("/redo.png")));
- m_ui.actionFind->setIcon(QIcon(resourcePrefix() + QLatin1String("/searchfind.png")));
- m_ui.actionUndo->setIcon(QIcon(resourcePrefix() + QLatin1String("/undo.png")));
m_ui.actionPlaceMarkerMatches->setIcon(QIcon(resourcePrefix() + QLatin1String("/validateplacemarkers.png")));
m_ui.actionWhatsThis->setIcon(QIcon(resourcePrefix() + QLatin1String("/whatsthis.png")));
@@ -2370,7 +2392,7 @@ static bool haveMnemonic(const QString &str)
// because we get a lot of false positives.
if (c != '&' && c != ' ' && QChar(c).isPrint()) {
const ushort *pp = p;
- for (; *p < 256 && ::isalpha(*p); p++) ;
+ for (; *p < 256 && isalpha(*p); p++) ;
if (pp == p || *p != ';')
return true;
// This looks like a HTML &entity;, so ignore it. As a HTML string
diff --git a/tools/linguist/linguist/mainwindow.ui b/tools/linguist/linguist/mainwindow.ui
index 450ce89..4edde5e 100644
--- a/tools/linguist/linguist/mainwindow.ui
+++ b/tools/linguist/linguist/mainwindow.ui
@@ -679,7 +679,7 @@
<string>About Qt</string>
</property>
<property name="whatsThis">
- <string>Display information about the Qt toolkit by Trolltech.</string>
+ <string>Display information about the Qt toolkit by Nokia.</string>
</property>
<property name="menuRole">
<enum>QAction::AboutQtRole</enum>
diff --git a/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp
index 4e2b473..39ba9fd 100644
--- a/tools/linguist/linguist/messagemodel.cpp
+++ b/tools/linguist/linguist/messagemodel.cpp
@@ -402,7 +402,7 @@ bool DataModel::setLanguageAndCountry(QLocale::Language lang, QLocale::Country c
if (lang == QLocale::C || uint(lang) > uint(QLocale::LastLanguage)) // XXX does this make any sense?
lang = QLocale::English;
QByteArray rules;
- bool ok = getNumerusInfo(lang, country, &rules, &m_numerusForms);
+ bool ok = getNumerusInfo(lang, country, &rules, &m_numerusForms, 0);
m_localizedLanguage = QCoreApplication::translate("MessageEditor", QLocale::languageToString(lang).toAscii());
m_countRefNeeds.clear();
for (int i = 0; i < rules.size(); ++i) {
diff --git a/tools/linguist/lupdate/lupdate.h b/tools/linguist/lupdate/lupdate.h
index 70332cd..136884b 100644
--- a/tools/linguist/lupdate/lupdate.h
+++ b/tools/linguist/lupdate/lupdate.h
@@ -79,6 +79,7 @@ void loadCPP(Translator &translator, const QStringList &filenames, ConversionDat
bool loadJava(Translator &translator, const QString &filename, ConversionData &cd);
bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd);
bool loadUI(Translator &translator, const QString &filename, ConversionData &cd);
+bool loadQml(Translator &translator, const QString &filename, ConversionData &cd);
QT_END_NAMESPACE
diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro
index ccc2d47..3ed909a 100644
--- a/tools/linguist/lupdate/lupdate.pro
+++ b/tools/linguist/lupdate/lupdate.pro
@@ -15,6 +15,9 @@ build_all:!build_pass {
include(../shared/formats.pri)
include(../shared/proparser.pri)
+include($$QT_SOURCE_TREE/src/declarative/qml/parser/parser.pri)
+INCLUDEPATH += $$QT_SOURCE_TREE/src/declarative/qml $$QT_BUILD_TREE/include/QtDeclarative
+
SOURCES += \
main.cpp \
merge.cpp \
@@ -23,6 +26,7 @@ SOURCES += \
cpp.cpp \
java.cpp \
qscript.cpp \
+ qdeclarative.cpp \
ui.cpp
HEADERS += \
diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp
index b2bfd7c..6c9157a 100644
--- a/tools/linguist/lupdate/main.cpp
+++ b/tools/linguist/lupdate/main.cpp
@@ -83,7 +83,7 @@ static void printUsage()
printOut(QObject::tr(
"Usage:\n"
" lupdate [options] [project-file]...\n"
- " lupdate [options] [source-file|path]... -ts ts-files\n\n"
+ " lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
"lupdate is part of Qt's Linguist tool chain. It extracts translatable\n"
"messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n"
"Extracted messages are stored in textual translation source files (typically\n"
@@ -132,6 +132,8 @@ static void printUsage()
" Specify the codec assumed for tr() calls. Effective only with -ts.\n"
" -version\n"
" Display the version of lupdate and exit.\n"
+ " @lst-file\n"
+ " Read additional file names (one per line) from lst-file.\n"
).arg(m_defaultExtensions));
}
@@ -267,6 +269,8 @@ static void processSources(Translator &fetchedTor,
else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
|| it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
loadQScript(fetchedTor, *it, cd);
+ else if (it->endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
+ loadQml(fetchedTor, *it, cd);
else
sourceFilesCpp << *it;
}
@@ -405,7 +409,7 @@ static void processProjects(
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
- m_defaultExtensions = QLatin1String("ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx");
+ m_defaultExtensions = QLatin1String("java,jui,ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml");
QStringList args = app.arguments();
QStringList tsFileNames;
@@ -560,73 +564,93 @@ int main(int argc, char **argv)
return 1;
}
+ QStringList files;
+ if (arg.startsWith(QLatin1String("@"))) {
+ QFile lstFile(arg.mid(1));
+ if (!lstFile.open(QIODevice::ReadOnly)) {
+ qWarning("lupdate error: List file '%s' is not readable",
+ qPrintable(lstFile.fileName()));
+ return 1;
+ }
+ while (!lstFile.atEnd())
+ files << QString::fromLocal8Bit(lstFile.readLine().trimmed());
+ } else {
+ files << arg;
+ }
if (metTsFlag) {
- bool found = false;
- foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
- if (arg.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
- QFileInfo fi(arg);
- if (!fi.exists() || fi.isWritable()) {
- tsFileNames.append(QFileInfo(arg).absoluteFilePath());
- } else {
- qWarning("lupdate warning: For some reason, '%s' is not writable.\n",
- qPrintable(arg));
+ foreach (const QString &file, files) {
+ bool found = false;
+ foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
+ if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
+ QFileInfo fi(file);
+ if (!fi.exists() || fi.isWritable()) {
+ tsFileNames.append(QFileInfo(file).absoluteFilePath());
+ } else {
+ qWarning("lupdate warning: For some reason, '%s' is not writable.\n",
+ qPrintable(file));
+ }
+ found = true;
+ break;
}
- found = true;
- break;
+ }
+ if (!found) {
+ qWarning("lupdate error: File '%s' has no recognized extension\n",
+ qPrintable(file));
+ return 1;
}
}
- if (!found) {
- qWarning("lupdate error: File '%s' has no recognized extension\n",
- qPrintable(arg));
- return 1;
- }
- } else if (arg.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
- || arg.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
- proFiles << arg;
numFiles++;
} else {
- QFileInfo fi(arg);
- if (!fi.exists()) {
- qWarning("lupdate error: File '%s' does not exists\n", qPrintable(arg));
- return 1;
- } else if (fi.isDir()) {
- if (options & Verbose)
- printOut(QObject::tr("Scanning directory '%1'...").arg(arg));
- QDir dir = QDir(fi.filePath());
- projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
- if (extensionsNameFilters.isEmpty()) {
- foreach (QString ext, extensions.split(QLatin1Char(','))) {
- ext = ext.trimmed();
- if (ext.startsWith(QLatin1Char('.')))
- ext.remove(0, 1);
- extensionsNameFilters.insert(ext);
- }
+ foreach (const QString &file, files) {
+ QFileInfo fi(file);
+ if (!fi.exists()) {
+ qWarning("lupdate error: File '%s' does not exists\n", qPrintable(file));
+ return 1;
}
- QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
- if (recursiveScan)
- filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
- QFileInfoList fileinfolist;
- recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
- int scanRootLen = dir.absolutePath().length();
- foreach (const QFileInfo &fi, fileinfolist) {
- QString fn = QDir::cleanPath(fi.absoluteFilePath());
- sourceFiles << fn;
-
- if (!fn.endsWith(QLatin1String(".java"))
- && !fn.endsWith(QLatin1String(".ui"))
- && !fn.endsWith(QLatin1String(".js"))
- && !fn.endsWith(QLatin1String(".qs"))) {
- int offset = 0;
- int depth = 0;
- do {
- offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
- QString ffn = fn.mid(offset + 1);
- allCSources.insert(ffn, fn);
- } while (++depth < 3 && offset > scanRootLen);
+ if (file.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
+ || file.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
+ proFiles << file;
+ } else if (fi.isDir()) {
+ if (options & Verbose)
+ printOut(QObject::tr("Scanning directory '%1'...").arg(file));
+ QDir dir = QDir(fi.filePath());
+ projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
+ if (extensionsNameFilters.isEmpty()) {
+ foreach (QString ext, extensions.split(QLatin1Char(','))) {
+ ext = ext.trimmed();
+ if (ext.startsWith(QLatin1Char('.')))
+ ext.remove(0, 1);
+ extensionsNameFilters.insert(ext);
+ }
+ }
+ QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
+ if (recursiveScan)
+ filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
+ QFileInfoList fileinfolist;
+ recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
+ int scanRootLen = dir.absolutePath().length();
+ foreach (const QFileInfo &fi, fileinfolist) {
+ QString fn = QDir::cleanPath(fi.absoluteFilePath());
+ sourceFiles << fn;
+
+ if (!fn.endsWith(QLatin1String(".java"))
+ && !fn.endsWith(QLatin1String(".jui"))
+ && !fn.endsWith(QLatin1String(".ui"))
+ && !fn.endsWith(QLatin1String(".js"))
+ && !fn.endsWith(QLatin1String(".qs"))
+ && !fn.endsWith(QLatin1String(".qml"))) {
+ int offset = 0;
+ int depth = 0;
+ do {
+ offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
+ QString ffn = fn.mid(offset + 1);
+ allCSources.insert(ffn, fn);
+ } while (++depth < 3 && offset > scanRootLen);
+ }
}
+ } else {
+ sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
}
- } else {
- sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
}
numFiles++;
}
diff --git a/tools/linguist/lupdate/qdeclarative.cpp b/tools/linguist/lupdate/qdeclarative.cpp
new file mode 100644
index 0000000..a734e99
--- /dev/null
+++ b/tools/linguist/lupdate/qdeclarative.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include "parser/qdeclarativejsengine_p.h"
+#include "parser/qdeclarativejsparser_p.h"
+#include "parser/qdeclarativejslexer_p.h"
+#include "parser/qdeclarativejsnodepool_p.h"
+#include "parser/qdeclarativejsastvisitor_p.h"
+#include "parser/qdeclarativejsast_p.h"
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QFileInfo>
+#include <QtDebug>
+#include <QStringList>
+
+#include <iostream>
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QDeclarativeJS;
+
+class FindTrCalls: protected AST::Visitor
+{
+public:
+ void operator()(Translator *translator, const QString &fileName, AST::Node *node)
+ {
+ m_translator = translator;
+ m_fileName = fileName;
+ m_component = QFileInfo(fileName).baseName(); //matches qsTr usage in QScriptEngine
+ accept(node);
+ }
+
+protected:
+ using AST::Visitor::visit;
+ using AST::Visitor::endVisit;
+
+ void accept(AST::Node *node)
+ { AST::Node::acceptChild(node, this); }
+
+ virtual void endVisit(AST::CallExpression *node)
+ {
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(node->base)) {
+ if (idExpr->name->asString() == QLatin1String("qsTr") ||
+ idExpr->name->asString() == QLatin1String("QT_TR_NOOP")) {
+ if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) {
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ const QString source = literal->value->asString();
+
+ QString comment;
+ bool plural = false;
+ AST::ArgumentList *commentNode = node->arguments->next;
+ if (commentNode) {
+ literal = AST::cast<AST::StringLiteral *>(commentNode->expression);
+ comment = literal->value->asString();
+
+ AST::ArgumentList *nNode = commentNode->next;
+ if (nNode) {
+ AST::NumericLiteral *numLiteral = AST::cast<AST::NumericLiteral *>(nNode->expression);
+ if (numLiteral) {
+ plural = true;
+ }
+ }
+ }
+
+ TranslatorMessage msg(m_component, source,
+ comment, QString(), m_fileName,
+ node->firstSourceLocation().startLine, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ m_translator->extend(msg);
+ }
+ } else if (idExpr->name->asString() == QLatin1String("qsTranslate") ||
+ idExpr->name->asString() == QLatin1String("QT_TRANSLATE_NOOP")) {
+ if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) {
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ const QString context = literal->value->asString();
+
+ QString source;
+ QString comment;
+ bool plural = false;
+ AST::ArgumentList *sourceNode = node->arguments->next;
+ if (sourceNode) {
+ literal = AST::cast<AST::StringLiteral *>(sourceNode->expression);
+ source = literal->value->asString();
+ AST::ArgumentList *commentNode = sourceNode->next;
+ if (commentNode) {
+ literal = AST::cast<AST::StringLiteral *>(commentNode->expression);
+ comment = literal->value->asString();
+
+ AST::ArgumentList *nNode = commentNode->next;
+ if (nNode) {
+ AST::NumericLiteral *numLiteral = AST::cast<AST::NumericLiteral *>(nNode->expression);
+ if (numLiteral) {
+ plural = true;
+ }
+ }
+ }
+ }
+
+ TranslatorMessage msg(context, source,
+ comment, QString(), m_fileName,
+ node->firstSourceLocation().startLine, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ m_translator->extend(msg);
+ }
+
+ }
+ }
+ }
+
+private:
+ Translator *m_translator;
+ QString m_fileName;
+ QString m_component;
+};
+
+QString createErrorString(const QString &filename, const QString &code, Parser &parser)
+{
+ // print out error
+ QStringList lines = code.split(QLatin1Char('\n'));
+ lines.append(QLatin1String("\n")); // sentinel.
+ QString errorString;
+
+ foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
+
+ if (m.isWarning())
+ continue;
+
+ QString error = filename + QLatin1Char(':') + QString::number(m.loc.startLine)
+ + QLatin1Char(':') + QString::number(m.loc.startColumn) + QLatin1String(": error: ")
+ + m.message + QLatin1Char('\n');
+
+ int line = 0;
+ if (m.loc.startLine > 0)
+ line = m.loc.startLine - 1;
+
+ const QString textLine = lines.at(line);
+
+ error += textLine + QLatin1Char('\n');
+
+ int column = m.loc.startColumn - 1;
+ if (column < 0)
+ column = 0;
+
+ column = qMin(column, textLine.length());
+
+ for (int i = 0; i < column; ++i) {
+ const QChar ch = textLine.at(i);
+ if (ch.isSpace())
+ error += ch.unicode();
+ else
+ error += QLatin1Char(' ');
+ }
+ error += QLatin1String("^\n");
+ errorString += error;
+ }
+ return errorString;
+}
+
+bool loadQml(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ cd.m_sourceFileName = filename;
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open %1: %2")
+ .arg(filename, file.errorString()));
+ return false;
+ }
+
+ const QString code = QTextStream(&file).readAll();
+
+ Engine driver;
+ Parser parser(&driver);
+
+ NodePool nodePool(filename, &driver);
+ driver.setNodePool(&nodePool);
+
+ Lexer lexer(&driver);
+ lexer.setCode(code, /*line = */ 1);
+ driver.setLexer(&lexer);
+
+ if (parser.parse()) {
+ FindTrCalls trCalls;
+ trCalls(&translator, filename, parser.ast());
+ } else {
+ QString error = createErrorString(filename, code, parser);
+ cd.appendError(error);
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp
index 6f34c2b..33276e6 100644
--- a/tools/linguist/lupdate/qscript.cpp
+++ b/tools/linguist/lupdate/qscript.cpp
@@ -40,6 +40,26 @@
**
****************************************************************************/
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE1 7
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE2 8
+
+#include <translator.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qvariant.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
class QScriptGrammar
{
public:
@@ -174,7 +194,6 @@ public:
}
};
-
const char *const QScriptGrammar::spell [] = {
"end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue",
"default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
@@ -747,26 +766,6 @@ const int QScriptGrammar::action_check [] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1};
-
-#define Q_SCRIPT_REGEXPLITERAL_RULE1 7
-
-#define Q_SCRIPT_REGEXPLITERAL_RULE2 8
-
-#include <translator.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qnumeric.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qtextcodec.h>
-#include <QtCore/qvariant.h>
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-QT_BEGIN_NAMESPACE
-
static void recordMessage(
Translator *tor, const QString &context, const QString &text, const QString &comment,
const QString &extracomment, bool plural, const QString &fileName, int lineNo)
diff --git a/tools/linguist/shared/numerus.cpp b/tools/linguist/shared/numerus.cpp
index d45dfed..6066732 100644
--- a/tools/linguist/shared/numerus.cpp
+++ b/tools/linguist/shared/numerus.cpp
@@ -64,7 +64,7 @@ static const uchar icelandicRules[] =
static const uchar irishStyleRules[] =
{ Q_EQ, 1, Q_NEWRULE,
Q_EQ, 2 };
-static const uchar slovakRules[] =
+static const uchar slovakStyleRules[] =
{ Q_EQ, 1, Q_NEWRULE,
Q_BETWEEN, 2, 4 };
static const uchar macedonianRules[] =
@@ -100,7 +100,7 @@ static const uchar arabicRules[] =
Q_EQ, 1, Q_NEWRULE,
Q_EQ, 2, Q_NEWRULE,
Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE,
- Q_MOD_100 | Q_NOT | Q_BETWEEN, 0, 2 };
+ Q_MOD_100 | Q_GEQ, 11 };
static const uchar tagalogRules[] =
{ Q_LEQ, 1, Q_NEWRULE,
Q_MOD_10 | Q_EQ, 4, Q_OR, Q_MOD_10 | Q_EQ, 6, Q_OR, Q_MOD_10 | Q_EQ, 9 };
@@ -114,7 +114,7 @@ static const char * const frenchStyleForms[] = { "Singular", "Plural", 0 };
static const char * const icelandicForms[] = { "Singular", "Plural", 0 };
static const char * const latvianForms[] = { "Singular", "Plural", "Nullar", 0 };
static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 };
-static const char * const slovakForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const slovakStyleForms[] = { "Singular", "Paucal", "Plural", 0 };
static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 };
static const char * const lithuanianForms[] = { "Singular", "Paucal", "Plural", 0 };
static const char * const russianStyleForms[] = { "Singular", "Dual", "Plural", 0 };
@@ -279,7 +279,7 @@ static const QLocale::Language irishStyleLanguages[] = {
QLocale::Sanskrit,
EOL
};
-static const QLocale::Language slovakLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL };
+static const QLocale::Language slovakStyleLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL };
static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL };
static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL };
static const QLocale::Language russianStyleLanguages[] = {
@@ -318,28 +318,45 @@ struct NumerusTableEntry {
const char * const *forms;
const QLocale::Language *languages;
const QLocale::Country *countries;
+ const char * const gettextRules;
};
static const NumerusTableEntry numerusTable[] = {
- { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0 },
- { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0 },
+ { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0, "nplurals=1; plural=0;" },
+ { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0,
+ "nplurals=2; plural=(n != 1);" },
{ frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages,
- frenchStyleCountries },
- { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0 },
- { icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0 },
- { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0 },
- { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguages, 0 },
- { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0 },
- { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0 },
- { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0 },
- { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0 },
- { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0 },
- { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0 },
- { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0 },
- { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0 },
- { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 },
- { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0 },
- { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0 }
+ frenchStyleCountries, "nplurals=2; plural=(n > 1);" },
+ { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
+ { icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0,
+ "nplurals=2; plural=(n%10==1 && n%100!=11 ? 0 : 1);" },
+ { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0,
+ "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" },
+ { slovakStyleRules, sizeof(slovakStyleRules), slovakStyleForms, slovakStyleLanguages, 0,
+ "nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);" },
+ { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0,
+ "nplurals=3; plural=(n%100==1 ? 0 : n%100==2 ? 1 : 2);" },
+ { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);" },
+ { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0,
+ "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" },
+ { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0,
+ "nplurals=4; plural=(n==1 ? 0 : (n==0 || (n%100>=1 && n%100<=10)) ? 1 : (n%100>=11 && n%100<=19) ? 2 : 3);" },
+ { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0,
+ "nplurals=5; plural=(n==0 ? 0 : n==1 ? 1 : (n>=2 && n<=5) ? 2 : n==6 ? 3 : 4);" },
+ { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0,
+ "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : (n%100>=3 && n%100<=10) ? 3 : n%100>=11 ? 4 : 5);" },
+ { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n%10==4 || n%10==6 || n%10== 9) ? 1 : 2);" },
+ { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n==11 || n/1000==11 || n/1000000==11 || n/1000000000==11) ? 1 : 2);" },
};
static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]);
@@ -352,7 +369,7 @@ static const uchar magic[MagicLength] = {
};
bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
- QByteArray *rules, QStringList *forms)
+ QByteArray *rules, QStringList *forms, const char **gettextRules)
{
while (true) {
for (int i = 0; i < NumerusTableSize; ++i) {
@@ -365,6 +382,8 @@ bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
*rules = QByteArray::fromRawData(reinterpret_cast<const char *>(entry.rules),
entry.rulesSize);
}
+ if (gettextRules)
+ *gettextRules = entry.gettextRules;
if (forms) {
forms->clear();
for (int k = 0; entry.forms[k]; ++k)
diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp
index 3354d61..99a8751 100644
--- a/tools/linguist/shared/po.cpp
+++ b/tools/linguist/shared/po.cpp
@@ -45,6 +45,7 @@
#include <QtCore/QIODevice>
#include <QtCore/QHash>
#include <QtCore/QString>
+#include <QtCore/QTextCodec>
#include <QtCore/QTextStream>
#include <ctype.h>
@@ -201,55 +202,54 @@ public:
public:
- QString id;
- QString context;
- QString tscomment;
- QString oldTscomment;
- QString lineNumber;
- QString fileName;
- QString references;
- QString translatorComments;
- QString automaticComments;
- QString msgId;
- QString oldMsgId;
- QStringList msgStr;
+ QByteArray id;
+ QByteArray context;
+ QByteArray tscomment;
+ QByteArray oldTscomment;
+ QByteArray lineNumber;
+ QByteArray fileName;
+ QByteArray references;
+ QByteArray translatorComments;
+ QByteArray automaticComments;
+ QByteArray msgId;
+ QByteArray oldMsgId;
+ QList<QByteArray> msgStr;
bool isPlural;
bool isFuzzy;
QHash<QString, QString> extra;
};
-static bool isTranslationLine(const QString &line)
+static bool isTranslationLine(const QByteArray &line)
{
- return line.startsWith(QLatin1String("#~ msgstr"))
- || line.startsWith(QLatin1String("msgstr"));
+ return line.startsWith("#~ msgstr") || line.startsWith("msgstr");
}
-static QString slurpEscapedString(const QStringList &lines, int & l,
- int offset, const QString &prefix, ConversionData &cd)
+static QByteArray slurpEscapedString(const QList<QByteArray> &lines, int &l,
+ int offset, const QByteArray &prefix, ConversionData &cd)
{
- QString msg;
+ QByteArray msg;
int stoff;
for (; l < lines.size(); ++l) {
- const QString &line = lines.at(l);
+ const QByteArray &line = lines.at(l);
if (line.isEmpty() || !line.startsWith(prefix))
break;
- while (line[offset].isSpace()) // No length check, as string has no trailing spaces.
+ while (isspace(line[offset])) // No length check, as string has no trailing spaces.
offset++;
- if (line[offset].unicode() != '"')
+ if (line[offset] != '"')
break;
offset++;
forever {
if (offset == line.length())
goto premature_eol;
- ushort c = line[offset++].unicode();
+ uchar c = line[offset++];
if (c == '"') {
if (offset == line.length())
break;
- while (line[offset].isSpace())
+ while (isspace(line[offset]))
offset++;
- if (line[offset++].unicode() != '"') {
+ if (line[offset++] != '"') {
cd.appendError(QString::fromLatin1(
"PO parsing error: extra characters on line %1.")
.arg(l + 1));
@@ -260,34 +260,34 @@ static QString slurpEscapedString(const QStringList &lines, int & l,
if (c == '\\') {
if (offset == line.length())
goto premature_eol;
- c = line[offset++].unicode();
+ c = line[offset++];
switch (c) {
case 'r':
- msg += QLatin1Char('\r'); // Maybe just throw it away?
+ msg += '\r'; // Maybe just throw it away?
break;
case 'n':
- msg += QLatin1Char('\n');
+ msg += '\n';
break;
case 't':
- msg += QLatin1Char('\t');
+ msg += '\t';
break;
case 'v':
- msg += QLatin1Char('\v');
+ msg += '\v';
break;
case 'a':
- msg += QLatin1Char('\a');
+ msg += '\a';
break;
case 'b':
- msg += QLatin1Char('\b');
+ msg += '\b';
break;
case 'f':
- msg += QLatin1Char('\f');
+ msg += '\f';
break;
case '"':
- msg += QLatin1Char('"');
+ msg += '"';
break;
case '\\':
- msg += QLatin1Char('\\');
+ msg += '\\';
break;
case '0':
case '1':
@@ -298,28 +298,28 @@ static QString slurpEscapedString(const QStringList &lines, int & l,
case '6':
case '7':
stoff = offset - 1;
- while ((c = line[offset].unicode()) >= '0' && c <= '7')
+ while ((c = line[offset]) >= '0' && c <= '7')
if (++offset == line.length())
goto premature_eol;
- msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 8));
+ msg += line.mid(stoff, offset - stoff).toUInt(0, 8);
break;
case 'x':
stoff = offset;
- while (isxdigit(line[offset].unicode()))
+ while (isxdigit(line[offset]))
if (++offset == line.length())
goto premature_eol;
- msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 16));
+ msg += line.mid(stoff, offset - stoff).toUInt(0, 16);
break;
default:
cd.appendError(QString::fromLatin1(
"PO parsing error: invalid escape '\\%1' (line %2).")
- .arg(QChar(c)).arg(l + 1));
- msg += QLatin1Char('\\');
- msg += QChar(c);
+ .arg(QChar((uint)c)).arg(l + 1));
+ msg += '\\';
+ msg += c;
break;
}
} else {
- msg += QChar(c);
+ msg += c;
}
}
offset = prefix.size();
@@ -330,36 +330,61 @@ static QString slurpEscapedString(const QStringList &lines, int & l,
premature_eol:
cd.appendError(QString::fromLatin1(
"PO parsing error: premature end of line %1.").arg(l + 1));
- return QString();
+ return QByteArray();
}
-static void slurpComment(QString &msg, const QStringList &lines, int & l)
+static void slurpComment(QByteArray &msg, const QList<QByteArray> &lines, int & l)
{
- const QChar newline = QLatin1Char('\n');
- QString prefix = lines.at(l);
+ QByteArray prefix = lines.at(l);
for (int i = 1; ; i++) {
- if (prefix.at(i).unicode() != ' ') {
+ if (prefix.at(i) != ' ') {
prefix.truncate(i);
break;
}
}
for (; l < lines.size(); ++l) {
- const QString &line = lines.at(l);
+ const QByteArray &line = lines.at(l);
if (line.startsWith(prefix))
msg += line.mid(prefix.size());
- else if (line != QLatin1String("#"))
+ else if (line != "#")
break;
- msg += newline;
+ msg += '\n';
}
--l;
}
+static QString makePoHeader(const QString &str)
+{
+ return QLatin1String("po-header-") + str.toLower().replace(QLatin1Char('-'), QLatin1Char('_'));
+}
+
+static QByteArray QByteArrayList_join(const QList<QByteArray> &that, char sep)
+{
+ int totalLength = 0;
+ const int size = that.size();
+
+ for (int i = 0; i < size; ++i)
+ totalLength += that.at(i).size();
+
+ if (size > 0)
+ totalLength += size - 1;
+
+ QByteArray res;
+ if (totalLength == 0)
+ return res;
+ res.reserve(totalLength);
+ for (int i = 0; i < that.size(); ++i) {
+ if (i)
+ res += sep;
+ res += that.at(i);
+ }
+ return res;
+}
+
bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
{
- const QChar quote = QLatin1Char('"');
- const QChar newline = QLatin1Char('\n');
- QTextStream in(&dev);
- in.setCodec(cd.m_codecForSource.isEmpty() ? QByteArray("UTF-8") : cd.m_codecForSource);
+ QTextCodec *codec = QTextCodec::codecForName(
+ cd.m_codecForSource.isEmpty() ? QByteArray("UTF-8") : cd.m_codecForSource);
bool error = false;
// format of a .po file entry:
@@ -380,25 +405,23 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
// ...
// we need line based lookahead below.
- QStringList lines;
- while (!in.atEnd())
- lines.append(in.readLine().trimmed());
- lines.append(QString());
+ QList<QByteArray> lines;
+ while (!dev.atEnd())
+ lines.append(dev.readLine().trimmed());
+ lines.append(QByteArray());
- int l = 0;
+ int l = 0, lastCmtLine = -1;
PoItem item;
for (; l != lines.size(); ++l) {
- QString line = lines.at(l);
+ QByteArray line = lines.at(l);
if (line.isEmpty())
continue;
if (isTranslationLine(line)) {
- bool isObsolete = line.startsWith(QLatin1String("#~ msgstr"));
- const QString prefix = QLatin1String(isObsolete ? "#~ " : "");
+ bool isObsolete = line.startsWith("#~ msgstr");
+ const QByteArray prefix = isObsolete ? "#~ " : "";
while (true) {
- int idx = line.indexOf(QLatin1Char(' '), prefix.length());
- QString str = slurpEscapedString(lines, l, idx, prefix, cd);
- str.replace(QChar(Translator::TextVariantSeparator),
- QChar(Translator::BinaryVariantSeparator));
+ int idx = line.indexOf(' ', prefix.length());
+ QByteArray str = slurpEscapedString(lines, l, idx, prefix, cd);
item.msgStr.append(str);
if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
break;
@@ -406,31 +429,109 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
line = lines.at(l);
}
if (item.msgId.isEmpty()) {
- QRegExp rx(QLatin1String("\\bX-Language: ([^\n]*)\n"));
- int idx = rx.indexIn(item.msgStr.first());
- if (idx >= 0) {
- translator.setLanguageCode(rx.cap(1));
- item.msgStr.first().remove(idx, rx.matchedLength());
+ QHash<QString, QByteArray> extras;
+ QList<QByteArray> hdrOrder;
+ QByteArray pluralForms;
+ foreach (const QByteArray &hdr, item.msgStr.first().split('\n')) {
+ if (hdr.isEmpty())
+ continue;
+ int idx = hdr.indexOf(':');
+ if (idx < 0) {
+ cd.appendError(QString::fromLatin1("Unexpected PO header format '%1'\n")
+ .arg(QString::fromLatin1(hdr)));
+ error = true;
+ break;
+ }
+ QByteArray hdrName = hdr.left(idx).trimmed();
+ QByteArray hdrValue = hdr.mid(idx + 1).trimmed();
+ hdrOrder << hdrName;
+ if (hdrName == "X-Language") {
+ translator.setLanguageCode(QString::fromLatin1(hdrValue));
+ } else if (hdrName == "X-Source-Language") {
+ translator.setSourceLanguageCode(QString::fromLatin1(hdrValue));
+ } else if (hdrName == "Plural-Forms") {
+ pluralForms = hdrValue;
+ } else if (hdrName == "MIME-Version") {
+ // just assume it is 1.0
+ } else if (hdrName == "Content-Type") {
+ if (cd.m_codecForSource.isEmpty()) {
+ if (!hdrValue.startsWith("text/plain; charset=")) {
+ cd.appendError(QString::fromLatin1("Unexpected Content-Type header '%1'\n")
+ .arg(QString::fromLatin1(hdrValue)));
+ error = true;
+ // This will avoid a flood of conversion errors.
+ codec = QTextCodec::codecForName("latin1");
+ } else {
+ QByteArray cod = hdrValue.mid(20);
+ QTextCodec *cdc = QTextCodec::codecForName(cod);
+ if (!cdc) {
+ cd.appendError(QString::fromLatin1("Unsupported codec '%1'\n")
+ .arg(QString::fromLatin1(cod)));
+ error = true;
+ // This will avoid a flood of conversion errors.
+ codec = QTextCodec::codecForName("latin1");
+ } else {
+ codec = cdc;
+ }
+ }
+ }
+ } else if (hdrName == "Content-Transfer-Encoding") {
+ if (hdrValue != "8bit") {
+ cd.appendError(QString::fromLatin1("Unexpected Content-Transfer-Encoding '%1'\n")
+ .arg(QString::fromLatin1(hdrValue)));
+ return false;
+ }
+ } else if (hdrName == "X-Virgin-Header") {
+ // legacy
+ } else {
+ extras[makePoHeader(QString::fromLatin1(hdrName))] = hdrValue;
+ }
}
- QRegExp rx2(QLatin1String("\\bX-Source-Language: ([^\n]*)\n"));
- int idx2 = rx2.indexIn(item.msgStr.first());
- if (idx2 >= 0) {
- translator.setSourceLanguageCode(rx2.cap(1));
- item.msgStr.first().remove(idx2, rx2.matchedLength());
+ if (!pluralForms.isEmpty()) {
+ if (translator.languageCode().isEmpty()) {
+ extras[makePoHeader(QLatin1String("Plural-Forms"))] = pluralForms;
+ } else {
+ // FIXME: have fun with making a consistency check ...
+ }
}
- if (item.msgStr.first().indexOf(
- QRegExp(QLatin1String("\\bX-Virgin-Header:[^\n]*\n"))) >= 0) {
- item = PoItem();
- continue;
+ // Eliminate the field if only headers we added are present in standard order.
+ // Keep in sync with savePO
+ static const char * const dfltHdrs[] = {
+ "MIME-Version", "Content-Type", "Content-Transfer-Encoding",
+ "Plural-Forms", "X-Language", "X-Source-Language"
+ };
+ uint cdh = 0;
+ for (int cho = 0; cho < hdrOrder.length(); cho++) {
+ for (;; cdh++) {
+ if (cdh == sizeof(dfltHdrs)/sizeof(dfltHdrs[0])) {
+ extras[QLatin1String("po-headers")] =
+ QByteArrayList_join(hdrOrder, ',');
+ goto doneho;
+ }
+ if (hdrOrder.at(cho) == dfltHdrs[cdh]) {
+ cdh++;
+ break;
+ }
+ }
}
+ doneho:
+ if (lastCmtLine != -1)
+ extras[QLatin1String("po-header_comment")] =
+ QByteArrayList_join(lines.mid(0, lastCmtLine + 1), '\n');
+ for (QHash<QString, QByteArray>::ConstIterator it = extras.constBegin(),
+ end = extras.constEnd();
+ it != end; ++it)
+ translator.setExtra(it.key(), codec->toUnicode(it.value()));
+ item = PoItem();
+ continue;
}
// build translator message
TranslatorMessage msg;
- msg.setContext(item.context);
+ msg.setContext(codec->toUnicode(item.context));
if (!item.references.isEmpty()) {
foreach (const QString &ref,
- item.references.split(QRegExp(QLatin1String("\\s")),
- QString::SkipEmptyParts)) {
+ codec->toUnicode(item.references).split(
+ QRegExp(QLatin1String("\\s")), QString::SkipEmptyParts)) {
int pos = ref.lastIndexOf(QLatin1Char(':'));
if (pos != -1)
msg.addReference(ref.left(pos), ref.mid(pos + 1).toInt());
@@ -438,18 +539,25 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
} else if (isObsolete) {
msg.setFileName(QLatin1String(MAGIC_OBSOLETE_REFERENCE));
}
- msg.setId(item.id);
- msg.setSourceText(item.msgId);
- msg.setOldSourceText(item.oldMsgId);
- msg.setComment(item.tscomment);
- msg.setOldComment(item.oldTscomment);
- msg.setExtraComment(item.automaticComments);
- msg.setTranslatorComment(item.translatorComments);
+ msg.setId(codec->toUnicode(item.id));
+ msg.setSourceText(codec->toUnicode(item.msgId));
+ msg.setOldSourceText(codec->toUnicode(item.oldMsgId));
+ msg.setComment(codec->toUnicode(item.tscomment));
+ msg.setOldComment(codec->toUnicode(item.oldTscomment));
+ msg.setExtraComment(codec->toUnicode(item.automaticComments));
+ msg.setTranslatorComment(codec->toUnicode(item.translatorComments));
msg.setPlural(item.isPlural || item.msgStr.size() > 1);
- msg.setTranslations(item.msgStr);
+ QStringList translations;
+ foreach (const QByteArray &bstr, item.msgStr) {
+ QString str = codec->toUnicode(bstr);
+ str.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
+ translations << str;
+ }
+ msg.setTranslations(translations);
if (isObsolete)
msg.setType(TranslatorMessage::Obsolete);
- else if (item.isFuzzy)
+ else if (item.isFuzzy || (!msg.sourceText().isEmpty() && !msg.isTranslated()))
msg.setType(TranslatorMessage::Unfinished);
else
msg.setType(TranslatorMessage::Finished);
@@ -460,18 +568,19 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
//qDebug() << flags << msg.m_extra;
translator.append(msg);
item = PoItem();
- } else if (line.startsWith(QLatin1Char('#'))) {
- switch(line.size() < 2 ? 0 : line.at(1).unicode()) {
+ } else if (line.startsWith('#')) {
+ switch (line.size() < 2 ? 0 : line.at(1)) {
case ':':
item.references += line.mid(3);
- item.references += newline;
+ item.references += '\n';
break;
case ',': {
QStringList flags =
- line.mid(2).split(QRegExp(QLatin1String("[, ]")),
- QString::SkipEmptyParts);
+ QString::fromLatin1(line.mid(2)).split(
+ QRegExp(QLatin1String("[, ]")), QString::SkipEmptyParts);
if (flags.removeOne(QLatin1String("fuzzy")))
item.isFuzzy = true;
+ flags.removeOne(QLatin1String("qt-format"));
TranslatorMessage::ExtraData::const_iterator it =
item.extra.find(QLatin1String("po-flags"));
if (it != item.extra.end())
@@ -481,103 +590,130 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
break;
}
case 0:
- item.translatorComments += newline;
+ item.translatorComments += '\n';
break;
case ' ':
slurpComment(item.translatorComments, lines, l);
break;
case '.':
- if (line.startsWith(QLatin1String("#. ts-context "))) {
+ if (line.startsWith("#. ts-context ")) {
item.context = line.mid(14);
- } else if (line.startsWith(QLatin1String("#. ts-id "))) {
+ } else if (line.startsWith("#. ts-id ")) {
item.id = line.mid(9);
} else {
item.automaticComments += line.mid(3);
- item.automaticComments += newline;
+ item.automaticComments += '\n';
}
break;
case '|':
- if (line.startsWith(QLatin1String("#| msgid "))) {
- item.oldMsgId = slurpEscapedString(lines, l, 9, QLatin1String("#| "), cd);
- } else if (line.startsWith(QLatin1String("#| msgid_plural "))) {
- QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#| "), cd);
+ if (line.startsWith("#| msgid ")) {
+ item.oldMsgId = slurpEscapedString(lines, l, 9, "#| ", cd);
+ } else if (line.startsWith("#| msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 16, "#| ", cd);
if (extra != item.oldMsgId)
- item.extra[QLatin1String("po-old_msgid_plural")] = extra;
- } else if (line.startsWith(QLatin1String("#| msgctxt "))) {
- item.oldTscomment = slurpEscapedString(lines, l, 11, QLatin1String("#| "), cd);
+ item.extra[QLatin1String("po-old_msgid_plural")] =
+ codec->toUnicode(extra);
+ } else if (line.startsWith("#| msgctxt ")) {
+ item.oldTscomment = slurpEscapedString(lines, l, 11, "#| ", cd);
} else {
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
- .arg(l + 1).arg(lines[l]));
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
error = true;
}
break;
case '~':
- if (line.startsWith(QLatin1String("#~ msgid "))) {
- item.msgId = slurpEscapedString(lines, l, 9, QLatin1String("#~ "), cd);
- } else if (line.startsWith(QLatin1String("#~ msgid_plural "))) {
- QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#~ "), cd);
+ if (line.startsWith("#~ msgid ")) {
+ item.msgId = slurpEscapedString(lines, l, 9, "#~ ", cd);
+ } else if (line.startsWith("#~ msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 16, "#~ ", cd);
if (extra != item.msgId)
- item.extra[QLatin1String("po-msgid_plural")] = extra;
+ item.extra[QLatin1String("po-msgid_plural")] =
+ codec->toUnicode(extra);
item.isPlural = true;
- } else if (line.startsWith(QLatin1String("#~ msgctxt "))) {
- item.tscomment = slurpEscapedString(lines, l, 11, QLatin1String("#~ "), cd);
+ } else if (line.startsWith("#~ msgctxt ")) {
+ item.tscomment = slurpEscapedString(lines, l, 11, "#~ ", cd);
} else {
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
- .arg(l + 1).arg(lines[l]));
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
error = true;
}
break;
default:
cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
- .arg(l + 1).arg(lines[l]));
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
error = true;
break;
}
- } else if (line.startsWith(QLatin1String("msgctxt "))) {
- item.tscomment = slurpEscapedString(lines, l, 8, QString(), cd);
- } else if (line.startsWith(QLatin1String("msgid "))) {
- item.msgId = slurpEscapedString(lines, l, 6, QString(), cd);
- } else if (line.startsWith(QLatin1String("msgid_plural "))) {
- QString extra = slurpEscapedString(lines, l, 13, QString(), cd);
+ lastCmtLine = l;
+ } else if (line.startsWith("msgctxt ")) {
+ item.tscomment = slurpEscapedString(lines, l, 8, QByteArray(), cd);
+ } else if (line.startsWith("msgid ")) {
+ item.msgId = slurpEscapedString(lines, l, 6, QByteArray(), cd);
+ } else if (line.startsWith("msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 13, QByteArray(), cd);
if (extra != item.msgId)
- item.extra[QLatin1String("po-msgid_plural")] = extra;
+ item.extra[QLatin1String("po-msgid_plural")] = codec->toUnicode(extra);
item.isPlural = true;
} else {
cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'\n"))
- .arg(l + 1).arg(lines[l]));
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
error = true;
}
}
return !error && cd.errors().isEmpty();
}
+static void addPoHeader(Translator::ExtraData &headers, QStringList &hdrOrder,
+ const char *name, const QString &value)
+{
+ QString qName = QLatin1String(name);
+ if (!hdrOrder.contains(qName))
+ hdrOrder << qName;
+ headers[makePoHeader(qName)] = value;
+}
+
bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
{
+ QString str_format = QLatin1String("-format");
+
bool ok = true;
QTextStream out(&dev);
out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec);
- bool first = true;
- if (translator.messages().isEmpty() || !translator.messages().first().sourceText().isEmpty()) {
- out <<
- "# SOME DESCRIPTIVE TITLE.\n"
- "# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n"
- "# This file is distributed under the same license as the PACKAGE package.\n"
- "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"
- "#\n"
- "#, fuzzy\n"
- "msgid \"\"\n"
- "msgstr \"\"\n"
- "\"X-Virgin-Header: remove this line if you change anything in the header.\\n\"\n";
- if (!translator.languageCode().isEmpty())
- out << "\"X-Language: " << translator.languageCode() << "\\n\"\n";
- if (!translator.sourceLanguageCode().isEmpty())
- out << "\"X-Source-Language: " << translator.sourceLanguageCode() << "\\n\"\n";
- first = false;
+ QString cmt = translator.extra(QLatin1String("po-header_comment"));
+ if (!cmt.isEmpty())
+ out << cmt << '\n';
+ out << "msgid \"\"\n";
+ Translator::ExtraData headers = translator.extras();
+ QStringList hdrOrder = translator.extra(QLatin1String("po-headers")).split(
+ QLatin1Char(','), QString::SkipEmptyParts);
+ // Keep in sync with loadPO
+ addPoHeader(headers, hdrOrder, "MIME-Version", QLatin1String("1.0"));
+ addPoHeader(headers, hdrOrder, "Content-Type",
+ QLatin1String("text/plain; charset=" + out.codec()->name()));
+ addPoHeader(headers, hdrOrder, "Content-Transfer-Encoding", QLatin1String("8bit"));
+ if (!translator.languageCode().isEmpty()) {
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(translator.languageCode(), &l, &c);
+ const char *gettextRules;
+ if (getNumerusInfo(l, c, 0, 0, &gettextRules))
+ addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules));
+ addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode());
}
+ if (!translator.sourceLanguageCode().isEmpty())
+ addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode());
+ QString hdrStr;
+ foreach (const QString &hdr, hdrOrder) {
+ hdrStr += hdr;
+ hdrStr += QLatin1String(": ");
+ hdrStr += headers.value(makePoHeader(hdr));
+ hdrStr += QLatin1Char('\n');
+ }
+ out << poEscapedString(QString(), QString::fromLatin1("msgstr"), true, hdrStr);
+
foreach (const TranslatorMessage &msg, translator.messages()) {
- if (!first)
- out << endl;
+ out << endl;
if (!msg.translatorComment().isEmpty())
out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment());
@@ -599,16 +735,36 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
}
bool noWrap = false;
+ bool skipFormat = false;
QStringList flags;
- if (msg.type() == TranslatorMessage::Unfinished)
+ if (msg.type() == TranslatorMessage::Unfinished && msg.isTranslated())
flags.append(QLatin1String("fuzzy"));
TranslatorMessage::ExtraData::const_iterator itr =
msg.extras().find(QLatin1String("po-flags"));
if (itr != msg.extras().end()) {
- if (itr->split(QLatin1String(", ")).contains(QLatin1String("no-wrap")))
+ QStringList atoms = itr->split(QLatin1String(", "));
+ foreach (const QString &atom, atoms)
+ if (atom.endsWith(str_format)) {
+ skipFormat = true;
+ break;
+ }
+ if (atoms.contains(QLatin1String("no-wrap")))
noWrap = true;
flags.append(*itr);
}
+ if (!skipFormat) {
+ QString source = msg.sourceText();
+ // This is fuzzy logic, as we don't know whether the string is
+ // actually used with QString::arg().
+ for (int off = 0; (off = source.indexOf(QLatin1Char('%'), off)) >= 0; ) {
+ if (++off >= source.length())
+ break;
+ if (source.at(off) == QLatin1Char('n') || source.at(off).isDigit()) {
+ flags.append(QLatin1String("qt-format"));
+ break;
+ }
+ }
+ }
if (!flags.isEmpty())
out << "#, " << flags.join(QLatin1String(", ")) << '\n';
@@ -626,11 +782,8 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText());
if (!msg.isPlural()) {
QString transl = msg.translation();
- if (first) {
- transl.remove(QRegExp(QLatin1String("\\bX-Language:[^\n]*\n")));
- if (!translator.languageCode().isEmpty())
- transl += QLatin1String("X-Language: ") + translator.languageCode() + QLatin1Char('\n');
- }
+ transl.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
out << poEscapedString(prefix, QLatin1String("msgstr"), noWrap, transl);
} else {
QString plural = msg.extra(QLatin1String("po-msgid_plural"));
@@ -646,11 +799,17 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
str);
}
}
- first = false;
}
return ok;
}
+static bool savePOT(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Translator ttor = translator;
+ ttor.dropTranslations();
+ return savePO(ttor, dev, cd);
+}
+
int initPO()
{
Translator::FileFormat format;
@@ -661,6 +820,13 @@ int initPO()
format.fileType = Translator::FileFormat::TranslationSource;
format.priority = 1;
Translator::registerFileFormat(format);
+ format.extension = QLatin1String("pot");
+ format.description = QObject::tr("GNU Gettext localization template files");
+ format.loader = &loadPO;
+ format.saver = &savePOT;
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ Translator::registerFileFormat(format);
return 1;
}
diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp
index de1284f..e2c4f4a 100644
--- a/tools/linguist/shared/qm.cpp
+++ b/tools/linguist/shared/qm.cpp
@@ -564,7 +564,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
Translator::languageAndCountry(translator.languageCode(), &l, &c);
QStringList numerusForms;
bool guessPlurals = true;
- if (getNumerusInfo(l, c, 0, &numerusForms))
+ if (getNumerusInfo(l, c, 0, &numerusForms, 0))
guessPlurals = (numerusForms.count() == 1);
QString context, contextUtf8;
@@ -704,7 +704,7 @@ static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData
QLocale::Country c;
Translator::languageAndCountry(translator.languageCode(), &l, &c);
QByteArray rules;
- if (getNumerusInfo(l, c, &rules, 0))
+ if (getNumerusInfo(l, c, &rules, 0, 0))
releaser.setNumerusRules(rules);
releaser.setCodecName(translator.codecName());
diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp
index 4331ce6..c86a9dd 100644
--- a/tools/linguist/shared/translator.cpp
+++ b/tools/linguist/shared/translator.cpp
@@ -45,8 +45,13 @@
#include <stdio.h>
#ifdef Q_OS_WIN
-#include <io.h> // required for _setmode, to avoid _O_TEXT streams...
-#include <fcntl.h> // for _O_BINARY
+// required for _setmode, to avoid _O_TEXT streams...
+# ifdef Q_OS_WINCE
+# include <stdlib.h>
+# else
+# include <io.h> // for _setmode
+# include <fcntl.h> // for _O_BINARY
+# endif
#endif
#include <QtCore/QDebug>
@@ -213,7 +218,11 @@ bool Translator::load(const QString &filename, ConversionData &cd, const QString
if (filename.isEmpty() || filename == QLatin1String("-")) {
#ifdef Q_OS_WIN
// QFile is broken for text files
+# ifdef Q_OS_WINCE
+ ::_setmode(stdin, _O_BINARY);
+# else
::_setmode(0, _O_BINARY);
+# endif
#endif
if (!file.open(stdin, QIODevice::ReadOnly)) {
cd.appendError(QString::fromLatin1("Cannot open stdin!? (%1)")
@@ -253,7 +262,11 @@ bool Translator::save(const QString &filename, ConversionData &cd, const QString
if (filename.isEmpty() || filename == QLatin1String("-")) {
#ifdef Q_OS_WIN
// QFile is broken for text files
+# ifdef Q_OS_WINCE
+ ::_setmode(stdout, _O_BINARY);
+# else
::_setmode(1, _O_BINARY);
+# endif
#endif
if (!file.open(stdout, QIODevice::WriteOnly)) {
cd.appendError(QString::fromLatin1("Cannot open stdout!? (%1)")
@@ -652,7 +665,7 @@ void Translator::normalizeTranslations(ConversionData &cd)
int numPlurals = 1;
if (l != QLocale::C) {
QStringList forms;
- if (getNumerusInfo(l, c, 0, &forms))
+ if (getNumerusInfo(l, c, 0, &forms, 0))
numPlurals = forms.count(); // includes singular
}
for (int i = 0; i < m_messages.count(); ++i) {
diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h
index 0b88c07..bb199f0 100644
--- a/tools/linguist/shared/translator.h
+++ b/tools/linguist/shared/translator.h
@@ -233,7 +233,7 @@ private:
};
bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
- QByteArray *rules, QStringList *forms);
+ QByteArray *rules, QStringList *forms, const char **gettextRules);
/*
This is a quick hack. The proper way to handle this would be
diff --git a/tools/linguist/shared/xliff.cpp b/tools/linguist/shared/xliff.cpp
index 20303ec..6411426 100644
--- a/tools/linguist/shared/xliff.cpp
+++ b/tools/linguist/shared/xliff.cpp
@@ -503,6 +503,8 @@ bool XLIFFHandler::startElement(const QString& namespaceURI,
m_language.replace(QLatin1Char('-'), QLatin1Char('_'));
m_sourceLanguage = atts.value(QLatin1String("source-language"));
m_sourceLanguage.replace(QLatin1Char('-'), QLatin1Char('_'));
+ if (m_sourceLanguage == QLatin1String("en"))
+ m_sourceLanguage.clear();
} else if (localName == QLatin1String("group")) {
if (atts.value(QLatin1String("restype")) == QLatin1String(restypeContext)) {
m_context = atts.value(QLatin1String("resname"));