1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "translator.h"
30
31#include <QtCore/QCoreApplication>
32#include <QtCore/QDebug>
33#include <QtCore/QString>
34#include <QtCore/QStringList>
35#include <QtCore/QTranslator>
36#include <QtCore/QLibraryInfo>
37
38#include <iostream>
39
40QT_USE_NAMESPACE
41
42class LC {
43 Q_DECLARE_TR_FUNCTIONS(LConvert)
44};
45
46static int usage(const QStringList &args)
47{
48 Q_UNUSED(args);
49
50 QString loaders;
51 QString line(QLatin1String(" %1 - %2\n"));
52 foreach (Translator::FileFormat format, Translator::registeredFileFormats())
53 loaders += line.arg(a: format.extension, fieldWidth: -5).arg(a: format.description());
54
55 std::cout << qPrintable(LC::tr("\nUsage:\n"
56 " lconvert [options] <infile> [<infile>...]\n\n"
57 "lconvert is part of Qt's Linguist tool chain. It can be used as a\n"
58 "stand-alone tool to convert and filter translation data files.\n"
59 "The following file formats are supported:\n\n%1\n"
60 "If multiple input files are specified, they are merged with\n"
61 "translations from later files taking precedence.\n\n"
62 "Options:\n"
63 " -h\n"
64 " -help Display this information and exit.\n\n"
65 " -i <infile>\n"
66 " -input-file <infile>\n"
67 " Specify input file. Use if <infile> might start with a dash.\n"
68 " This option can be used several times to merge inputs.\n"
69 " May be '-' (standard input) for use in a pipe.\n\n"
70 " -o <outfile>\n"
71 " -output-file <outfile>\n"
72 " Specify output file. Default is '-' (standard output).\n\n"
73 " -if <informat>\n"
74 " -input-format <format>\n"
75 " Specify input format for subsequent <infile>s.\n"
76 " The format is auto-detected from the file name and defaults to 'ts'.\n\n"
77 " -of <outformat>\n"
78 " -output-format <outformat>\n"
79 " Specify output format. See -if.\n\n"
80 " -drop-tags <regexp>\n"
81 " Drop named extra tags when writing TS or XLIFF files.\n"
82 " May be specified repeatedly.\n\n"
83 " -drop-translations\n"
84 " Drop existing translations and reset the status to 'unfinished'.\n"
85 " Note: this implies --no-obsolete.\n\n"
86 " -source-language <language>[_<region>]\n"
87 " Specify/override the language of the source strings. Defaults to\n"
88 " POSIX if not specified and the file does not name it yet.\n\n"
89 " -target-language <language>[_<region>]\n"
90 " Specify/override the language of the translation.\n"
91 " The target language is guessed from the file name if this option\n"
92 " is not specified and the file contents name no language yet.\n\n"
93 " -no-obsolete\n"
94 " Drop obsolete messages.\n\n"
95 " -no-finished\n"
96 " Drop finished messages.\n\n"
97 " -no-untranslated\n"
98 " Drop untranslated messages.\n\n"
99 " -sort-contexts\n"
100 " Sort contexts in output TS file alphabetically.\n\n"
101 " -locations {absolute|relative|none}\n"
102 " Override how source code references are saved in TS files.\n"
103 " Default is absolute.\n\n"
104 " -no-ui-lines\n"
105 " Drop line numbers from references to UI files.\n\n"
106 " -verbose\n"
107 " be a bit more verbose\n\n"
108 "Long options can be specified with only one leading dash, too.\n\n"
109 "Return value:\n"
110 " 0 on success\n"
111 " 1 on command line parse failures\n"
112 " 2 on read failures\n"
113 " 3 on write failures\n").arg(loaders));
114 return 1;
115}
116
117struct File
118{
119 QString name;
120 QString format;
121};
122
123int main(int argc, char *argv[])
124{
125 QCoreApplication app(argc, argv);
126#ifndef QT_BOOTSTRAPPED
127#ifndef Q_OS_WIN32
128 QTranslator translator;
129 QTranslator qtTranslator;
130 QString sysLocale = QLocale::system().name();
131 QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
132 if (translator.load(filename: QLatin1String("linguist_") + sysLocale, directory: resourceDir)
133 && qtTranslator.load(filename: QLatin1String("qt_") + sysLocale, directory: resourceDir)) {
134 app.installTranslator(messageFile: &translator);
135 app.installTranslator(messageFile: &qtTranslator);
136 }
137#endif // Q_OS_WIN32
138#endif
139
140 QStringList args = app.arguments();
141 QList<File> inFiles;
142 QString inFormat(QLatin1String("auto"));
143 QString outFileName;
144 QString outFormat(QLatin1String("auto"));
145 QString targetLanguage;
146 QString sourceLanguage;
147 bool dropTranslations = false;
148 bool noObsolete = false;
149 bool noFinished = false;
150 bool noUntranslated = false;
151 bool verbose = false;
152 bool noUiLines = false;
153 Translator::LocationsType locations = Translator::DefaultLocations;
154
155 ConversionData cd;
156 Translator tr;
157
158 for (int i = 1; i < args.size(); ++i) {
159 if (args[i].startsWith(s: QLatin1String("--")))
160 args[i].remove(i: 0, len: 1);
161 if (args[i] == QLatin1String("-o")
162 || args[i] == QLatin1String("-output-file")) {
163 if (++i >= args.size())
164 return usage(args);
165 outFileName = args[i];
166 } else if (args[i] == QLatin1String("-of")
167 || args[i] == QLatin1String("-output-format")) {
168 if (++i >= args.size())
169 return usage(args);
170 outFormat = args[i];
171 } else if (args[i] == QLatin1String("-i")
172 || args[i] == QLatin1String("-input-file")) {
173 if (++i >= args.size())
174 return usage(args);
175 File file;
176 file.name = args[i];
177 file.format = inFormat;
178 inFiles.append(t: file);
179 } else if (args[i] == QLatin1String("-if")
180 || args[i] == QLatin1String("-input-format")) {
181 if (++i >= args.size())
182 return usage(args);
183 inFormat = args[i];
184 } else if (args[i] == QLatin1String("-drop-tag") || args[i] == QLatin1String("-drop-tags")) {
185 if (++i >= args.size())
186 return usage(args);
187 cd.m_dropTags.append(t: args[i]);
188 } else if (args[i] == QLatin1String("-drop-translations")) {
189 dropTranslations = true;
190 } else if (args[i] == QLatin1String("-target-language")) {
191 if (++i >= args.size())
192 return usage(args);
193 targetLanguage = args[i];
194 } else if (args[i] == QLatin1String("-source-language")) {
195 if (++i >= args.size())
196 return usage(args);
197 sourceLanguage = args[i];
198 } else if (args[i].startsWith(s: QLatin1String("-h"))) {
199 usage(args);
200 return 0;
201 } else if (args[i] == QLatin1String("-no-obsolete")) {
202 noObsolete = true;
203 } else if (args[i] == QLatin1String("-no-finished")) {
204 noFinished = true;
205 } else if (args[i] == QLatin1String("-no-untranslated")) {
206 noUntranslated = true;
207 } else if (args[i] == QLatin1String("-sort-contexts")) {
208 cd.m_sortContexts = true;
209 } else if (args[i] == QLatin1String("-locations")) {
210 if (++i >= args.size())
211 return usage(args);
212 if (args[i] == QLatin1String("none"))
213 locations = Translator::NoLocations;
214 else if (args[i] == QLatin1String("relative"))
215 locations = Translator::RelativeLocations;
216 else if (args[i] == QLatin1String("absolute"))
217 locations = Translator::AbsoluteLocations;
218 else
219 return usage(args);
220 } else if (args[i] == QLatin1String("-no-ui-lines")) {
221 noUiLines = true;
222 } else if (args[i] == QLatin1String("-verbose")) {
223 verbose = true;
224 } else if (args[i].startsWith(c: QLatin1Char('-'))) {
225 return usage(args);
226 } else {
227 File file;
228 file.name = args[i];
229 file.format = inFormat;
230 inFiles.append(t: file);
231 }
232 }
233
234 if (inFiles.isEmpty())
235 return usage(args);
236
237 tr.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName: inFiles[0].name));
238
239 if (!tr.load(filename: inFiles[0].name, err&: cd, format: inFiles[0].format)) {
240 std::cerr << qPrintable(cd.error());
241 return 2;
242 }
243 tr.reportDuplicates(dupes: tr.resolveDuplicates(), fileName: inFiles[0].name, verbose);
244
245 for (int i = 1; i < inFiles.size(); ++i) {
246 Translator tr2;
247 if (!tr2.load(filename: inFiles[i].name, err&: cd, format: inFiles[i].format)) {
248 std::cerr << qPrintable(cd.error());
249 return 2;
250 }
251 tr2.reportDuplicates(dupes: tr2.resolveDuplicates(), fileName: inFiles[i].name, verbose);
252 for (int j = 0; j < tr2.messageCount(); ++j)
253 tr.replaceSorted(msg: tr2.message(i: j));
254 }
255
256 if (!targetLanguage.isEmpty())
257 tr.setLanguageCode(targetLanguage);
258 if (!sourceLanguage.isEmpty())
259 tr.setSourceLanguageCode(sourceLanguage);
260 if (noObsolete)
261 tr.stripObsoleteMessages();
262 if (noFinished)
263 tr.stripFinishedMessages();
264 if (noUntranslated)
265 tr.stripUntranslatedMessages();
266 if (dropTranslations)
267 tr.dropTranslations();
268 if (noUiLines)
269 tr.dropUiLines();
270 if (locations != Translator::DefaultLocations)
271 tr.setLocationsType(locations);
272
273 tr.normalizeTranslations(cd);
274 if (!cd.errors().isEmpty()) {
275 std::cerr << qPrintable(cd.error());
276 cd.clearErrors();
277 }
278 if (!tr.save(filename: outFileName, err&: cd, format: outFormat)) {
279 std::cerr << qPrintable(cd.error());
280 return 3;
281 }
282 return 0;
283}
284

source code of qttools/src/linguist/lconvert/main.cpp