bump product version to 4.1.6.2
[LibreOffice.git] / l10ntools / source / localize.cxx
blob8b93ec9d36dd1dc52ac3f33d1af459616c33249f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
22 #include <cstddef>
23 #include <cstdlib>
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 #include <algorithm>
29 #include "osl/file.h"
30 #include "osl/file.hxx"
31 #include "osl/thread.h"
32 #include "rtl/string.h"
33 #include "rtl/string.hxx"
34 #include "rtl/textcvt.h"
35 #include "rtl/strbuf.hxx"
36 #include "rtl/ustring.h"
37 #include "rtl/ustring.hxx"
38 #include "sal/macros.h"
39 #include "sal/main.h"
40 #include "sal/types.h"
42 #include "po.hxx"
44 using namespace std;
46 namespace {
48 struct AsciiString {
49 char const * string;
50 sal_Int32 length;
53 bool matchList(
54 const OUString& rUrl, const AsciiString* pList, size_t nLength)
56 for (size_t i = 0; i != nLength; ++i) {
57 if (rUrl.endsWithAsciiL(pList[i].string, pList[i].length)) {
58 return true;
61 return false;
64 bool passesNegativeList(const OUString& rUrl) {
65 static const AsciiString list[] = {
66 { RTL_CONSTASCII_STRINGPARAM("/dictionaries.xcu") },
67 { RTL_CONSTASCII_STRINGPARAM(
68 "/dictionaries/da_DK/help/da/help.tree") },
69 { RTL_CONSTASCII_STRINGPARAM(
70 "/dictionaries/da_DK/help/da/"
71 "org.openoffice.da.hunspell.dictionaries/page1.xhp") },
72 { RTL_CONSTASCII_STRINGPARAM(
73 "/dictionaries/da_DK/help/da/"
74 "org.openoffice.da.hunspell.dictionaries/page2.xhp") },
75 { RTL_CONSTASCII_STRINGPARAM(
76 "/dictionaries/hu_HU/help/hu/help.tree") },
77 { RTL_CONSTASCII_STRINGPARAM(
78 "/dictionaries/hu_HU/help/hu/"
79 "org.openoffice.hu.hunspell.dictionaries/page1.xhp") },
80 { RTL_CONSTASCII_STRINGPARAM(
81 "/officecfg/registry/data/org/openoffice/Office/"
82 "Accelerators.xcu") },
83 { RTL_CONSTASCII_STRINGPARAM(
84 "/officecfg/registry/data/org/openoffice/Office/SFX.xcu") }
86 return !matchList(rUrl, list, SAL_N_ELEMENTS(list));
89 bool passesPositiveList(const OUString& rUrl) {
90 static const AsciiString list[] = {
91 { RTL_CONSTASCII_STRINGPARAM(
92 "/chart2/source/controller/dialogs/res_DataLabel_tmpl.hrc") },
93 { RTL_CONSTASCII_STRINGPARAM(
94 "/chart2/source/controller/dialogs/res_ErrorBar_tmpl.hrc") },
95 { RTL_CONSTASCII_STRINGPARAM(
96 "/chart2/source/controller/dialogs/res_LegendPosition_tmpl.hrc") },
97 { RTL_CONSTASCII_STRINGPARAM(
98 "/chart2/source/controller/dialogs/"
99 "res_SecondaryAxisCheckBoxes_tmpl.hrc") },
100 { RTL_CONSTASCII_STRINGPARAM(
101 "/chart2/source/controller/dialogs/res_Statistic_tmpl.hrc") },
102 { RTL_CONSTASCII_STRINGPARAM(
103 "/chart2/source/controller/dialogs/res_Titlesx_tmpl.hrc") },
104 { RTL_CONSTASCII_STRINGPARAM(
105 "/chart2/source/controller/dialogs/res_Trendline_tmpl.hrc") },
106 { RTL_CONSTASCII_STRINGPARAM(
107 "/chart2/source/controller/menu/MenuItems_tmpl.hrc") },
108 { RTL_CONSTASCII_STRINGPARAM(
109 "/dbaccess/source/ui/dlg/AutoControls_tmpl.hrc") },
110 { RTL_CONSTASCII_STRINGPARAM(
111 "/dbaccess/source/ui/inc/toolbox_tmpl.hrc") },
112 { RTL_CONSTASCII_STRINGPARAM("/description.xml") },
113 { RTL_CONSTASCII_STRINGPARAM("/android/sdremote/res/values/strings.xml") },
114 { RTL_CONSTASCII_STRINGPARAM("/offmgr/inc/offmenu_tmpl.hrc") },
115 { RTL_CONSTASCII_STRINGPARAM(
116 "/offmgr/source/offapp/intro/intro_tmpl.hrc") },
117 { RTL_CONSTASCII_STRINGPARAM("/svx/inc/globlmn_tmpl.hrc") },
118 { RTL_CONSTASCII_STRINGPARAM("/svx/source/intro/intro_tmpl.hrc") },
119 { RTL_CONSTASCII_STRINGPARAM(
120 "/svx/source/unodialogs/textconversiondlgs/"
121 "chinese_direction_tmpl.hrc") },
122 { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/swacc_tmpl.hrc") },
123 { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/swmn_tmpl.hrc") },
124 { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/toolbox_tmpl.hrc") }
126 return matchList(rUrl, list, SAL_N_ELEMENTS(list));
129 void handleCommand(
130 const OString& rInPath, const OString& rOutPath,
131 const OString& rExecutable)
133 OStringBuffer buf(OString(getenv("SOLARVER")));
134 buf.append('/');
135 buf.append(OString(getenv("INPATH_FOR_BUILD")));
136 buf.append("/bin/");
137 buf.append(rExecutable);
138 buf.append(" -i ");
139 buf.append(rInPath);
140 buf.append(" -o ");
141 buf.append(rOutPath);
143 const OString cmd = buf.makeStringAndClear();
144 if (system(cmd.getStr()) != 0)
146 cerr << "Error: Failed to execute " << cmd.getStr() << '\n';
147 throw false; //TODO
151 void InitPoFile(
152 const OString& rProject, const OString& rInPath,
153 const OString& rPotDir, const OString& rOutPath )
155 //Create directory for po file
157 OUString outDir =
158 OStringToOUString(
159 rPotDir.copy(0,rPotDir.lastIndexOf('/')), RTL_TEXTENCODING_UTF8);
160 OUString outDirUrl;
161 if (osl::FileBase::getFileURLFromSystemPath(outDir, outDirUrl)
162 != osl::FileBase::E_None)
164 cerr << "Error: Cannot convert pathname to URL in " << __FILE__ << ", in line " << __LINE__ << "\n"
165 << " outDir: " << OUStringToOString(outDir, RTL_TEXTENCODING_ASCII_US).getStr() << "\n";
166 throw false; //TODO
168 osl::Directory::createPath(outDirUrl);
171 //Add header to the po file
172 PoOfstream aPoOutPut;
173 aPoOutPut.open(rOutPath.getStr());
174 if (!aPoOutPut.isOpen())
176 cerr
177 << "Error: Cannot open po file "
178 << rOutPath.getStr() << "\n";
179 throw false; //TODO
182 const sal_Int32 nProjectInd = rInPath.indexOf(rProject);
183 const OString relativPath =
184 rInPath.copy(nProjectInd, rInPath.lastIndexOf('/')- nProjectInd);
186 PoHeader aTmp(relativPath);
187 aPoOutPut.writeHeader(aTmp);
188 aPoOutPut.close();
191 bool handleFile(
192 const OString& rProject, const OUString& rUrl,
193 const OString& rPotDir, bool bInitPoFile )
195 struct Command {
196 char const * extension;
197 sal_Int32 extensionLength;
198 OString executable;
199 bool positive;
201 static Command const commands[] = {
202 { RTL_CONSTASCII_STRINGPARAM(".src"), "transex3", false },
203 { RTL_CONSTASCII_STRINGPARAM(".hrc"), "transex3", true },
204 { RTL_CONSTASCII_STRINGPARAM(".ulf"), "ulfex", false },
205 { RTL_CONSTASCII_STRINGPARAM(".xcu"), "cfgex", false },
206 { RTL_CONSTASCII_STRINGPARAM(".xrm"), "xrmex", false },
207 { RTL_CONSTASCII_STRINGPARAM("description.xml"), "xrmex", true },
208 { RTL_CONSTASCII_STRINGPARAM("strings.xml"), "stringex", true },
209 { RTL_CONSTASCII_STRINGPARAM(".xhp"), "helpex", false },
210 { RTL_CONSTASCII_STRINGPARAM(".properties"), "propex", false },
211 { RTL_CONSTASCII_STRINGPARAM(".ui"), "uiex", false },
212 { RTL_CONSTASCII_STRINGPARAM(".tree"), "treex", false } };
213 for (size_t i = 0; i != SAL_N_ELEMENTS(commands); ++i)
215 if (rUrl.endsWithAsciiL(
216 commands[i].extension, commands[i].extensionLength) &&
217 (commands[i].executable != "propex" || rUrl.indexOf("en_US") != -1))
219 if (commands[i].positive ? passesPositiveList(rUrl) : passesNegativeList(rUrl))
221 //Get input file path
222 OString sInPath;
224 OUString sInPathTmp;
225 if (osl::FileBase::getSystemPathFromFileURL(rUrl, sInPathTmp) !=
226 osl::FileBase::E_None)
228 cerr << "osl::FileBase::getSystemPathFromFileURL(" << rUrl << ") failed\n";
229 throw false; //TODO
231 sInPath = OUStringToOString( sInPathTmp, RTL_TEXTENCODING_UTF8 );
233 OString sOutPath = rPotDir.concat(".pot");
235 if ( bInitPoFile )
237 InitPoFile(rProject, sInPath, rPotDir, sOutPath);
239 handleCommand(sInPath, sOutPath, commands[i].executable);
240 return true;
242 break;
245 return false;
248 void handleFilesOfDir(
249 std::vector<OUString>& aFiles, const OString& rProject,
250 const OString& rPotDir )
252 ///Handle files in lexical order
253 std::sort(aFiles.begin(), aFiles.end());
255 typedef std::vector<OUString>::const_iterator citer_t;
257 bool bFirstLocFile = true; ///< First file in directory which needs localization
259 for( citer_t aIt = aFiles.begin(); aIt != aFiles.end(); ++aIt )
261 if (handleFile( rProject, *aIt, rPotDir, bFirstLocFile))
263 bFirstLocFile = false;
267 if( !bFirstLocFile )
269 //Delete pot file if it contain only the header
270 OString sPotFile = rPotDir.concat(".pot");
271 PoIfstream aPOStream( sPotFile );
272 PoEntry aPO;
273 aPOStream.readEntry( aPO );
274 bool bDel = aPOStream.eof();
275 aPOStream.close();
276 if( bDel )
278 if ( system(OString("rm " + sPotFile).getStr()) != 0 )
280 cerr
281 << "Error: Cannot remove entryless pot file: "
282 << sPotFile.getStr() << "\n";
283 throw false; //TODO
289 bool includeProject(const OString& rProject) {
290 static OString projects[] = {
291 "accessibility",
292 "android",
293 "avmedia",
294 "basctl",
295 "basic",
296 "chart2",
297 "connectivity",
298 "cui",
299 "dbaccess",
300 "desktop",
301 "dictionaries",
302 "editeng",
303 "extensions",
304 "extras",
305 "filter",
306 "forms",
307 "formula",
308 "fpicker",
309 "framework",
310 "helpcontent2",
311 "instsetoo_native",
312 "librelogo",
313 "mysqlc",
314 "nlpsolver",
315 "officecfg",
316 "padmin",
317 "readlicense_oo",
318 "reportbuilder",
319 "reportdesign",
320 "sc",
321 "scaddins",
322 "sccomp",
323 "scp2",
324 "sd",
325 "sdext",
326 "setup_native",
327 "sfx2",
328 "shell",
329 "starmath",
330 "svl",
331 "svtools",
332 "svx",
333 "sw",
334 "swext",
335 "sysui",
336 "tubes",
337 "uui",
338 "vcl",
339 "wizards",
340 "xmlsecurity" };
341 for (size_t i = 0; i != SAL_N_ELEMENTS(projects); ++i) {
342 if (rProject == projects[i]) {
343 return true;
346 return false;
349 /// Handle one directory in the hierarchy.
351 /// Ignores symlinks and instead explicitly descends into clone/* or src/*,
352 /// as the Cygwin symlinks are not supported by osl::Directory on Windows.
354 /// @param rUrl the absolute file URL of this directory
356 /// @param nLevel 0 if this is either the root directory that contains the
357 /// projects or one of the clone/* or src/* directories that contain the
358 /// additional projects; -1 if this is the clone directory; 1 if this
359 /// is a project directory; 2 if this is a directory inside a project
361 /// @param rProject the name of the project (empty and ignored if nLevel <= 0)
362 /// @param rPotDir the path of pot directory
363 void handleDirectory(
364 const OUString& rUrl, int nLevel,
365 const OString& rProject, const OString& rPotDir)
367 osl::Directory dir(rUrl);
368 if (dir.open() != osl::FileBase::E_None) {
369 cerr
370 << "Error: Cannot open directory: " << rUrl << '\n';
371 throw false; //TODO
373 std::vector<OUString> aFileNames;
374 for (;;) {
375 osl::DirectoryItem item;
376 osl::FileBase::RC e = dir.getNextItem(item);
377 if (e == osl::FileBase::E_NOENT) {
378 break;
380 if (e != osl::FileBase::E_None) {
381 cerr << "Error: Cannot read directory\n";
382 throw false; //TODO
384 osl::FileStatus stat(
385 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName
386 | osl_FileStatus_Mask_FileURL);
387 if (item.getFileStatus(stat) != osl::FileBase::E_None) {
388 cerr << "Error: Cannot get file status\n";
389 throw false; //TODO
391 const OString sDirName =
392 OUStringToOString(stat.getFileName(),RTL_TEXTENCODING_UTF8);
393 switch (nLevel) {
394 case -1: // the clone or src directory
395 if (stat.getFileType() == osl::FileStatus::Directory) {
396 handleDirectory(
397 stat.getFileURL(), 0, OString(), rPotDir);
399 break;
400 case 0: // a root directory
401 if (stat.getFileType() == osl::FileStatus::Directory) {
402 if (includeProject(sDirName)) {
403 handleDirectory(
404 stat.getFileURL(), 1, sDirName, rPotDir.concat("/").concat(sDirName));
405 } else if ( sDirName == "clone" ||
406 sDirName == "src" )
408 handleDirectory( stat.getFileURL(), -1, OString(), rPotDir);
411 break;
412 default:
413 if (stat.getFileType() == osl::FileStatus::Directory)
415 handleDirectory(
416 stat.getFileURL(), 2, rProject, rPotDir.concat("/").concat(sDirName));
418 else
420 aFileNames.push_back(stat.getFileURL());
422 break;
426 if( !aFileNames.empty() )
428 handleFilesOfDir( aFileNames, rProject, rPotDir );
431 if (dir.close() != osl::FileBase::E_None) {
432 cerr << "Error: Cannot close directory\n";
433 throw false; //TODO
436 //Remove empty pot directory
437 OUString sPoPath =
438 OStringToOUString(
439 rPotDir.copy(0,rPotDir.lastIndexOf('/')), RTL_TEXTENCODING_UTF8);
440 OUString sPoUrl;
441 if (osl::FileBase::getFileURLFromSystemPath(sPoPath, sPoUrl)
442 != osl::FileBase::E_None)
444 cerr << "Error: Cannot convert pathname to URL in " << __FILE__ << ", in line " << __LINE__ << "\n"
445 << OUStringToOString(sPoPath, RTL_TEXTENCODING_UTF8).getStr() << "\n";
446 throw false; //TODO
448 osl::Directory::remove(sPoUrl);
451 void handleProjects(char * sSourceRoot, char const * sDestRoot)
453 OUString root16;
454 if (!rtl_convertStringToUString(
455 &root16.pData, sSourceRoot, rtl_str_getLength(sSourceRoot),
456 osl_getThreadTextEncoding(),
457 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
458 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
459 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
461 cerr << "Error: Cannot convert pathname to UTF-16\n";
462 throw false; //TODO
464 OUString rootUrl;
465 if (osl::FileBase::getFileURLFromSystemPath(root16, rootUrl)
466 != osl::FileBase::E_None)
468 cerr << "Error: Cannot convert pathname to URL in " << __FILE__ << ", in line " << __LINE__ << "\n"
469 << " root16: " << OUStringToOString(root16, RTL_TEXTENCODING_ASCII_US).getStr() << "\n";
470 throw false; //TODO
472 handleDirectory(rootUrl, 0, OString(), OString(sDestRoot));
476 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) {
477 if (argc != 3) {
478 cerr
479 << ("localize (c)2001 by Sun Microsystems\n\n"
480 "As part of the L10N framework, localize extracts en-US\n"
481 "strings for translation out of the toplevel modules defined\n"
482 "in projects array in l10ntools/source/localize.cxx.\n\n"
483 "Syntax: localize <source-root> <outfile>\n");
484 exit(EXIT_FAILURE);
486 try {
487 handleProjects(argv[1],argv[2]);
488 } catch (bool) { //TODO
489 return EXIT_FAILURE;
491 return EXIT_SUCCESS;
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */