| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "translator.h" |
| #include "profileevaluator.h" |
| |
| #ifndef QT_BOOTSTRAPPED |
| #include <QtCore/QCoreApplication> |
| #include <QtCore/QTranslator> |
| #endif |
| #include <QtCore/QDebug> |
| #include <QtCore/QDir> |
| #include <QtCore/QFile> |
| #include <QtCore/QFileInfo> |
| #include <QtCore/QRegExp> |
| #include <QtCore/QString> |
| #include <QtCore/QStringList> |
| #include <QtCore/QTextStream> |
| |
| #include <iostream> |
| |
| QT_USE_NAMESPACE |
| |
| #ifdef QT_BOOTSTRAPPED |
| static void initBinaryDir( |
| #ifndef Q_OS_WIN |
| const char *argv0 |
| #endif |
| ); |
| |
| struct LR { |
| static inline QString tr(const char *sourceText, const char *comment = 0) |
| { |
| return QCoreApplication::translate("LRelease", sourceText, comment); |
| } |
| }; |
| #else |
| class LR { |
| Q_DECLARE_TR_FUNCTIONS(LRelease) |
| }; |
| #endif |
| |
| static void printOut(const QString & out) |
| { |
| QTextStream stream(stdout); |
| stream << out; |
| } |
| |
| static void printUsage() |
| { |
| printOut(LR::tr( |
| "Usage:\n" |
| " lrelease [options] project-file\n" |
| " lrelease [options] ts-files [-qm qm-file]\n\n" |
| "lrelease is part of Qt's Linguist tool chain. It can be used as a\n" |
| "stand-alone tool to convert XML-based translations files in the TS\n" |
| "format into the 'compiled' QM format used by QTranslator objects.\n\n" |
| "Options:\n" |
| " -help Display this information and exit\n" |
| " -idbased\n" |
| " Use IDs instead of source strings for message keying\n" |
| " -compress\n" |
| " Compress the QM files\n" |
| " -nounfinished\n" |
| " Do not include unfinished translations\n" |
| " -removeidentical\n" |
| " If the translated text is the same as\n" |
| " the source text, do not include the message\n" |
| " -markuntranslated <prefix>\n" |
| " If a message has no real translation, use the source text\n" |
| " prefixed with the given string instead\n" |
| " -silent\n" |
| " Do not explain what is being done\n" |
| " -version\n" |
| " Display the version of lrelease and exit\n" |
| )); |
| } |
| |
| static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbose */) |
| { |
| ConversionData cd; |
| bool ok = tor.load(tsFileName, cd, QLatin1String("auto")); |
| if (!ok) { |
| std::cerr << qPrintable(LR::tr("lrelease error: %1").arg(cd.error())); |
| } else { |
| if (!cd.errors().isEmpty()) |
| printOut(cd.error()); |
| } |
| cd.clearErrors(); |
| return ok; |
| } |
| |
| static bool releaseTranslator(Translator &tor, const QString &qmFileName, |
| ConversionData &cd, bool removeIdentical) |
| { |
| tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose()); |
| |
| if (cd.isVerbose()) |
| printOut(LR::tr("Updating '%1'...\n").arg(qmFileName)); |
| if (removeIdentical) { |
| if (cd.isVerbose()) |
| printOut(LR::tr("Removing translations equal to source text in '%1'...\n").arg(qmFileName)); |
| tor.stripIdenticalSourceTranslations(); |
| } |
| |
| QFile file(qmFileName); |
| if (!file.open(QIODevice::WriteOnly)) { |
| std::cerr << qPrintable(LR::tr("lrelease error: cannot create '%1': %2\n") |
| .arg(qmFileName, file.errorString())); |
| return false; |
| } |
| |
| tor.normalizeTranslations(cd); |
| bool ok = tor.release(&file, cd); |
| file.close(); |
| |
| if (!ok) { |
| std::cerr << qPrintable(LR::tr("lrelease error: cannot save '%1': %2") |
| .arg(qmFileName, cd.error())); |
| } else if (!cd.errors().isEmpty()) { |
| printOut(cd.error()); |
| } |
| cd.clearErrors(); |
| return ok; |
| } |
| |
| static bool releaseTsFile(const QString& tsFileName, |
| ConversionData &cd, bool removeIdentical) |
| { |
| Translator tor; |
| if (!loadTsFile(tor, tsFileName, cd.isVerbose())) |
| return false; |
| |
| QString qmFileName = tsFileName; |
| foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) { |
| if (qmFileName.endsWith(QLatin1Char('.') + fmt.extension)) { |
| qmFileName.chop(fmt.extension.length() + 1); |
| break; |
| } |
| } |
| qmFileName += QLatin1String(".qm"); |
| |
| return releaseTranslator(tor, qmFileName, cd, removeIdentical); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| #ifdef QT_BOOTSTRAPPED |
| initBinaryDir( |
| #ifndef Q_OS_WIN |
| argv[0] |
| #endif |
| ); |
| #else |
| QCoreApplication app(argc, argv); |
| #ifndef Q_OS_WIN32 |
| QTranslator translator; |
| QTranslator qtTranslator; |
| QString sysLocale = QLocale::system().name(); |
| QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); |
| if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir) |
| && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) { |
| app.installTranslator(&translator); |
| app.installTranslator(&qtTranslator); |
| } |
| #endif // Q_OS_WIN32 |
| #endif // QT_BOOTSTRAPPED |
| |
| ConversionData cd; |
| cd.m_verbose = true; // the default is true starting with Qt 4.2 |
| bool removeIdentical = false; |
| Translator tor; |
| QStringList inputFiles; |
| QString outputFile; |
| |
| for (int i = 1; i < argc; ++i) { |
| if (!strcmp(argv[i], "-compress")) { |
| cd.m_saveMode = SaveStripped; |
| continue; |
| } else if (!strcmp(argv[i], "-idbased")) { |
| cd.m_idBased = true; |
| continue; |
| } else if (!strcmp(argv[i], "-nocompress")) { |
| cd.m_saveMode = SaveEverything; |
| continue; |
| } else if (!strcmp(argv[i], "-removeidentical")) { |
| removeIdentical = true; |
| continue; |
| } else if (!strcmp(argv[i], "-nounfinished")) { |
| cd.m_ignoreUnfinished = true; |
| continue; |
| } else if (!strcmp(argv[i], "-markuntranslated")) { |
| if (i == argc - 1) { |
| printUsage(); |
| return 1; |
| } |
| cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]); |
| } else if (!strcmp(argv[i], "-silent")) { |
| cd.m_verbose = false; |
| continue; |
| } else if (!strcmp(argv[i], "-verbose")) { |
| cd.m_verbose = true; |
| continue; |
| } else if (!strcmp(argv[i], "-version")) { |
| printOut(LR::tr("lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR))); |
| return 0; |
| } else if (!strcmp(argv[i], "-qm")) { |
| if (i == argc - 1) { |
| printUsage(); |
| return 1; |
| } |
| outputFile = QString::fromLocal8Bit(argv[++i]); |
| } else if (!strcmp(argv[i], "-help")) { |
| printUsage(); |
| return 0; |
| } else if (argv[i][0] == '-') { |
| printUsage(); |
| return 1; |
| } else { |
| inputFiles << QString::fromLocal8Bit(argv[i]); |
| } |
| } |
| |
| if (inputFiles.isEmpty()) { |
| printUsage(); |
| return 1; |
| } |
| |
| foreach (const QString &inputFile, inputFiles) { |
| if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive) |
| || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) { |
| QFileInfo fi(inputFile); |
| ProFile pro(fi.absoluteFilePath()); |
| |
| ProFileEvaluator visitor; |
| visitor.setVerbose(cd.isVerbose()); |
| |
| if (!visitor.queryProFile(&pro)) { |
| std::cerr << qPrintable(LR::tr( |
| "lrelease error: cannot read project file '%1'.\n") |
| .arg(inputFile)); |
| continue; |
| } |
| if (!visitor.accept(&pro)) { |
| std::cerr << qPrintable(LR::tr( |
| "lrelease error: cannot process project file '%1'.\n") |
| .arg(inputFile)); |
| continue; |
| } |
| |
| QStringList translations = visitor.values(QLatin1String("TRANSLATIONS")); |
| if (translations.isEmpty()) { |
| std::cerr << qPrintable(LR::tr( |
| "lrelease warning: Met no 'TRANSLATIONS' entry in project file '%1'\n") |
| .arg(inputFile)); |
| } else { |
| QDir proDir(fi.absolutePath()); |
| foreach (const QString &trans, translations) |
| if (!releaseTsFile(QFileInfo(proDir, trans).filePath(), cd, removeIdentical)) |
| return 1; |
| } |
| } else { |
| if (outputFile.isEmpty()) { |
| if (!releaseTsFile(inputFile, cd, removeIdentical)) |
| return 1; |
| } else { |
| if (!loadTsFile(tor, inputFile, cd.isVerbose())) |
| return 1; |
| } |
| } |
| } |
| |
| if (!outputFile.isEmpty()) |
| return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1; |
| |
| return 0; |
| } |
| |
| #ifdef QT_BOOTSTRAPPED |
| |
| #ifdef Q_OS_WIN |
| # include <windows.h> |
| #endif |
| |
| static QString binDir; |
| |
| static void initBinaryDir( |
| #ifndef Q_OS_WIN |
| const char *_argv0 |
| #endif |
| ) |
| { |
| #ifdef Q_OS_WIN |
| wchar_t module_name[MAX_PATH]; |
| GetModuleFileName(0, module_name, MAX_PATH); |
| QFileInfo filePath = QString::fromWCharArray(module_name); |
| binDir = filePath.filePath(); |
| #else |
| QString argv0 = QFile::decodeName(QByteArray(_argv0)); |
| QString absPath; |
| |
| if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) { |
| /* |
| If argv0 starts with a slash, it is already an absolute |
| file path. |
| */ |
| absPath = argv0; |
| } else if (argv0.contains(QLatin1Char('/'))) { |
| /* |
| If argv0 contains one or more slashes, it is a file path |
| relative to the current directory. |
| */ |
| absPath = QDir::current().absoluteFilePath(argv0); |
| } else { |
| /* |
| Otherwise, the file path has to be determined using the |
| PATH environment variable. |
| */ |
| QByteArray pEnv = qgetenv("PATH"); |
| QDir currentDir = QDir::current(); |
| QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":")); |
| for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { |
| if ((*p).isEmpty()) |
| continue; |
| QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0); |
| QFileInfo candidate_fi(candidate); |
| if (candidate_fi.exists() && !candidate_fi.isDir()) { |
| binDir = candidate_fi.canonicalPath(); |
| return; |
| } |
| } |
| return; |
| } |
| |
| QFileInfo fi(absPath); |
| if (fi.exists()) |
| binDir = fi.canonicalPath(); |
| #endif |
| } |
| |
| QT_BEGIN_NAMESPACE |
| |
| // The name is hard-coded in QLibraryInfo |
| QString qmake_libraryInfoFile() |
| { |
| if (binDir.isEmpty()) |
| return QString(); |
| return QDir(binDir).filePath(QString::fromLatin1("qt.conf")); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_BOOTSTRAPPED |