LibreOffice Module l10ntools (master) 1
pocheck.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <sal/config.h>
11
12#include <cassert>
13#include <iostream>
14#include <map>
15#include <vector>
16#include <rtl/string.hxx>
17#include <rtl/ustring.hxx>
18#include <osl/file.hxx>
19#include <po.hxx>
20
21// Translated style names must be unique
22static void checkStyleNames(const OString& aLanguage)
23{
24 std::map<OString,sal_uInt16> aLocalizedStyleNames;
25 std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
26 std::vector<PoEntry> repeatedEntries;
27
28 OString aPoPath = OString::Concat(getenv("SRC_ROOT")) +
29 "/translations/source/" +
30 aLanguage + "/sw/messages.po";
31 PoIfstream aPoInput;
32 aPoInput.open(aPoPath);
33 if( !aPoInput.isOpen() )
34 {
35 std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
36 return;
37 }
38
39 for(;;)
40 {
41 PoEntry aPoEntry;
42 aPoInput.readEntry(aPoEntry);
43 bool bRepeated = false;
44 if( aPoInput.eof() )
45 {
46 break;
47 }
48
49 if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("STR_POOLCOLL") )
50 {
51 const OString& aMsgStr = aPoEntry.getMsgStr();
52 if( aMsgStr.isEmpty() )
53 continue;
54 if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
55 aLocalizedStyleNames[aMsgStr] = 1;
56 else {
57 aLocalizedStyleNames[aMsgStr]++;
58 bRepeated = true;
59 }
60 }
61 if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("STR_POOLNUMRULE") )
62 {
63 const OString& aMsgStr = aPoEntry.getMsgStr();
64 if( aMsgStr.isEmpty() )
65 continue;
66 if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
67 aLocalizedNumStyleNames[aMsgStr] = 1;
68 else {
69 aLocalizedNumStyleNames[aMsgStr]++;
70 bRepeated = true;
71 }
72 }
73 if (bRepeated)
74 repeatedEntries.push_back(aPoEntry);
75 }
76 aPoInput.close();
77
78 for (auto const& localizedStyleName : aLocalizedStyleNames)
79 {
80 if( localizedStyleName.second > 1 )
81 {
82 std::cout << "ERROR: Style name translations must be unique in:\n" <<
83 aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << localizedStyleName.first <<
84 "\nSee STR_POOLCOLL_*\n\n";
85 }
86 }
87 for (auto const& localizedNumStyleName : aLocalizedNumStyleNames)
88 {
89 if( localizedNumStyleName.second > 1 )
90 {
91 std::cout << "ERROR: Style name translations must be unique in:\n" <<
92 aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << localizedNumStyleName.first <<
93 "\nSee STR_POOLNUMRULE_*\n\n";
94 }
95 }
96 OString sPoHdrMsg;
97 aPoInput.open(aPoPath, sPoHdrMsg);
98 if( !aPoInput.isOpen() )
99 {
100 std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
101 return;
102 }
103 PoOfstream aPoOutput;
104 aPoOutput.open(aPoPath+".new");
105 PoHeader aTmp("sw/inc", sPoHdrMsg);
106 aPoOutput.writeHeader(aTmp);
107 bool bAnyError = false;
108
109 for(;;)
110 {
111 PoEntry aPoEntry;
112 bool bError = false;
113 aPoInput.readEntry(aPoEntry);
114 if( aPoInput.eof() )
115 break;
116 for (auto const& repeatedEntry : repeatedEntries)
117 {
118 if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt()) {
119 bError = true;
120 break;
121 }
122 }
123 if (bError) {
124 bAnyError = true;
125 } else {
126 aPoOutput.writeEntry(aPoEntry);
127 }
128 }
129 aPoInput.close();
130 aPoOutput.close();
131 OUString aPoPathURL;
132 osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
133 if( bAnyError )
134 osl::File::move(aPoPathURL + ".new", aPoPathURL);
135 else
136 osl::File::remove(aPoPathURL + ".new");
137}
138
139// Translated spreadsheet function names must be unique
140static void checkFunctionNames(const OString& aLanguage)
141{
142 std::map<OString,sal_uInt16> aLocalizedFunctionNames;
143 std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
144
145 std::vector<PoEntry> repeatedEntries;
146
147 OString aPoPaths[2];
148 OUString aPoPathURL;
149
150 aPoPaths[0] = OString::Concat(getenv("SRC_ROOT")) +
151 "/translations/source/" +
152 aLanguage +
153 "/formula/messages.po";
154 PoIfstream aPoInput;
155 OString sPoHdrMsg;
156 aPoInput.open(aPoPaths[0], sPoHdrMsg);
157 if( !aPoInput.isOpen() )
158 {
159 std::cerr << "Warning: Cannot open " << aPoPaths[0] << std::endl;
160 return;
161 }
162
163 for(;;)
164 {
165 PoEntry aPoEntry;
166 aPoInput.readEntry(aPoEntry);
167 if( aPoInput.eof() )
168 break;
169 if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt() == "RID_STRLIST_FUNCTION_NAMES" )
170 {
171 const OString& aMsgStr = aPoEntry.getMsgStr();
172 if( aMsgStr.isEmpty() )
173 continue;
174 if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
175 aLocalizedCoreFunctionNames[aMsgStr] = 1;
176 if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
177 aLocalizedFunctionNames[aMsgStr] = 1;
178 } else {
179 aLocalizedFunctionNames[aMsgStr]++;
180 repeatedEntries.push_back(aPoEntry);
181 }
182 }
183 }
184 aPoInput.close();
185
186 aPoPaths[1] = OString::Concat(getenv("SRC_ROOT")) +
187 "/translations/source/" +
188 aLanguage +
189 "/scaddins/messages.po";
190 aPoInput.open(aPoPaths[1]);
191 if( !aPoInput.isOpen() )
192 {
193 std::cerr << "Warning: Cannot open " << aPoPaths[1] << std::endl;
194 return;
195 }
196
197 for(;;)
198 {
199 PoEntry aPoEntry;
200 aPoInput.readEntry(aPoEntry);
201 if( aPoInput.eof() )
202 break;
203 if( !aPoEntry.isFuzzy() && aPoEntry.getMsgCtxt().startsWith("ANALYSIS_FUNCNAME") )
204 {
205 OString aMsgStr = aPoEntry.getMsgStr();
206 if( aMsgStr.isEmpty() )
207 continue;
208 if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
209 aMsgStr += "_ADD";
210 if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
211 aLocalizedFunctionNames[aMsgStr] = 1;
212 } else {
213 aLocalizedFunctionNames[aMsgStr]++;
214 repeatedEntries.push_back(aPoEntry);
215 }
216 }
217 }
218 aPoInput.close();
219
220 for (auto const& localizedFunctionName : aLocalizedFunctionNames)
221 {
222 if( localizedFunctionName.second > 1 )
223 {
224 std::cout
225 << ("ERROR: Spreadsheet function name translations must be"
226 " unique.\nLanguage: ")
227 << aLanguage << "\nDuplicated translation is: " << localizedFunctionName.first
228 << "\n\n";
229 }
230 }
231
232 for (int i=0;i<2;i++)
233 {
234 aPoInput.open(aPoPaths[i]);
235 if( !aPoInput.isOpen() )
236 std::cerr << "Warning: Cannot open " << aPoPaths[i] << std::endl;
237 PoOfstream aPoOutput;
238 aPoOutput.open(aPoPaths[i]+".new");
239
240 switch (i)
241 {
242 case 0:
243 {
244 PoHeader hd("formula/inc", sPoHdrMsg);
245 aPoOutput.writeHeader(hd);
246 break;
247 }
248 case 1:
249 {
250 PoHeader hd("scaddins/inc", sPoHdrMsg);
251 aPoOutput.writeHeader(hd);
252 break;
253 }
254 }
255 bool bAnyError = false;
256
257 for(;;)
258 {
259 PoEntry aPoEntry;
260 bool bError = false;
261 aPoInput.readEntry(aPoEntry);
262 if( aPoInput.eof() )
263 break;
264 for (auto const& repeatedEntry : repeatedEntries)
265 {
266 if (repeatedEntry.getMsgId() == aPoEntry.getMsgId() && repeatedEntry.getMsgCtxt() == aPoEntry.getMsgCtxt())
267 {
268 bError = true;
269 break;
270 }
271 }
272 if (bError)
273 {
274 bAnyError = true;
275 }
276 else
277 {
278 aPoOutput.writeEntry(aPoEntry);
279 }
280 }
281 aPoInput.close();
282 aPoOutput.close();
283 osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPaths[i], RTL_TEXTENCODING_UTF8), aPoPathURL);
284 if( bAnyError )
285 osl::File::move(aPoPathURL + ".new", aPoPathURL);
286 else
287 osl::File::remove(aPoPathURL + ".new");
288 }
289}
290
291// In instsetoo_native/inc_openoffice/windows/msi_languages.po
292// where an en-US string ends with '|', translation must end
293// with '|', too.
294static void checkVerticalBar(const OString& aLanguage)
295{
296 OString aPoPath = OString::Concat(getenv("SRC_ROOT")) +
297 "/translations/source/" +
298 aLanguage +
299 "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
300 PoIfstream aPoInput;
301 aPoInput.open(aPoPath);
302 if( !aPoInput.isOpen() )
303 {
304 std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
305 return;
306 }
307 PoOfstream aPoOutput;
308 aPoOutput.open(aPoPath+".new");
309 PoHeader aTmp("instsetoo_native/inc_openoffice/windows/msi_languages");
310 aPoOutput.writeHeader(aTmp);
311 bool bError = false;
312
313 for(;;)
314 {
315 PoEntry aPoEntry;
316 aPoInput.readEntry(aPoEntry);
317 if( aPoInput.eof() )
318 break;
319 if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
320 !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
321 {
322 std::cout
323 << ("ERROR: Missing '|' character at the end of translated"
324 " string.\nIt causes runtime error in installer.\nFile: ")
325 << aPoPath << std::endl
326 << "Language: " << aLanguage << std::endl
327 << "English: " << aPoEntry.getMsgId() << std::endl
328 << "Localized: " << aPoEntry.getMsgStr() << std::endl
329 << std::endl;
330 bError = true;
331 }
332 else
333 aPoOutput.writeEntry(aPoEntry);
334 }
335 aPoInput.close();
336 aPoOutput.close();
337 OUString aPoPathURL;
338 osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
339 if( bError )
340 osl::File::move(aPoPathURL + ".new", aPoPathURL);
341 else
342 osl::File::remove(aPoPathURL + ".new");
343}
344
345// In starmath/source.po Math symbol names (from symbol.src)
346// must not contain spaces
347static void checkMathSymbolNames(const OString& aLanguage)
348{
349 OString aPoPath = OString::Concat(getenv("SRC_ROOT")) +
350 "/translations/source/" +
351 aLanguage +
352 "/starmath/messages.po";
353 PoIfstream aPoInput;
354 aPoInput.open(aPoPath);
355 if( !aPoInput.isOpen() )
356 {
357 std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
358 return;
359 }
360 PoOfstream aPoOutput;
361 aPoOutput.open(aPoPath+".new");
362 PoHeader aTmp("starmath/inc");
363 aPoOutput.writeHeader(aTmp);
364 bool bError = false;
365
366 for(;;)
367 {
368 PoEntry aPoEntry;
369 aPoInput.readEntry(aPoEntry);
370 if( aPoInput.eof() )
371 break;
372 if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_UI_SYMBOL_NAMES" &&
373 !aPoEntry.getMsgStr().isEmpty() && (aPoEntry.getMsgStr().indexOf(" ") != -1) )
374 {
375 std::cout
376 << "ERROR: Math symbol names must not contain spaces.\nFile: "
377 << aPoPath << std::endl
378 << "Language: " << aLanguage << std::endl
379 << "English: " << aPoEntry.getMsgId() << std::endl
380 << "Localized: " << aPoEntry.getMsgStr() << std::endl
381 << std::endl;
382 bError = true;
383 }
384 else
385 aPoOutput.writeEntry(aPoEntry);
386 }
387 aPoInput.close();
388 aPoOutput.close();
389 OUString aPoPathURL;
390 osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
391 if( bError )
392 osl::File::move(aPoPathURL + ".new", aPoPathURL);
393 else
394 osl::File::remove(aPoPathURL + ".new");
395}
396
397int main()
398{
399 try
400 {
401 auto const env = getenv("ALL_LANGS");
402 assert(env != nullptr);
403 OString aLanguages(env);
404 if( aLanguages.isEmpty() )
405 {
406 std::cerr << "Usage: LD_LIBRARY_PATH=instdir/program make cmd cmd=workdir/LinkTarget/Executable/pocheck\n";
407 return 1;
408 }
409 for(sal_Int32 i = 1;;++i) // skip en-US
410 {
411 OString aLanguage = aLanguages.getToken(i,' ');
412 if( aLanguage.isEmpty() )
413 break;
414 if( aLanguage == "qtz" )
415 continue;
416 checkStyleNames(aLanguage);
417 checkFunctionNames(aLanguage);
418 checkVerticalBar(aLanguage);
419 checkMathSymbolNames(aLanguage);
420 }
421 return 0;
422 }
423 catch (std::exception& e)
424 {
425 std::cerr << "pocheck: exception " << e.what() << std::endl;
426 return 1;
427 }
428}
429
430/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Interface to use po entries in localization.
Definition: po.hxx:34
OString const & getMsgId() const
Definition: po.cxx:392
OString const & getMsgCtxt() const
Definition: po.cxx:384
OString getGroupId() const
Definition: po.cxx:336
OString const & getMsgStr() const
Definition: po.cxx:399
bool isFuzzy() const
Definition: po.cxx:377
Interface to work with header of po/pot files.
Definition: po.hxx:81
Interface to read po entry from files as input streams.
Definition: po.hxx:126
bool eof() const
Definition: po.hxx:142
void open(const OString &rFileName)
Definition: po.cxx:591
bool isOpen() const
Definition: po.hxx:141
void close()
Definition: po.cxx:606
void readEntry(PoEntry &rPo)
Definition: po.cxx:612
Interface to write po entry to files as output streams.
Definition: po.hxx:101
void open(const OString &rFileName, OpenMode aMode=TRUNC)
Definition: po.cxx:498
void writeHeader(const PoHeader &rHeader)
Definition: po.cxx:521
void close()
Definition: po.cxx:515
void writeEntry(const PoEntry &rPo)
Definition: po.cxx:528
const css::uno::Reference< css::xml::crypto::XSecurityEnvironment > & env
int i
static void checkVerticalBar(const OString &aLanguage)
Definition: pocheck.cxx:294
static void checkMathSymbolNames(const OString &aLanguage)
Definition: pocheck.cxx:347
static void checkStyleNames(const OString &aLanguage)
Definition: pocheck.cxx:22
static void checkFunctionNames(const OString &aLanguage)
Definition: pocheck.cxx:140
int main()
Definition: pocheck.cxx:397