LibreOffice Module l10ntools (master) 1
merge.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 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <algorithm>
24#include <cstdlib>
25#include <fstream>
26#include <iostream>
27#include <string>
28#include <utility>
29#include <vector>
30
31#include <export.hxx>
32#include <po.hxx>
33
34namespace
35{
36 OString lcl_NormalizeFilename(std::string_view rFilename)
37 {
38 size_t idx1 = rFilename.rfind( '\\' );
39 size_t idx2 = rFilename.rfind( '/' );
40 if (idx1 == std::string_view::npos && idx2 == std::string_view::npos)
41 return OString(rFilename);
42 if (idx1 == std::string_view::npos)
43 idx1 = 0;
44 if (idx2 == std::string_view::npos)
45 idx2 = 0;
46 return OString(rFilename.substr(std::max(idx1, idx2)+1));
47 };
48
49 bool lcl_ReadPoChecked(
50 PoEntry& o_rPoEntry, PoIfstream& rPoFile,
51 const OString& rFileName)
52 {
53 try
54 {
55 rPoFile.readEntry( o_rPoEntry );
56 }
57 catch (const PoIfstream::Exception&)
58 {
59 SAL_WARN("l10ntools", rFileName << " contains invalid entry");
60 return false;
61 }
62 return true;
63 }
64}
65
66
67
68
69ResData::ResData( OString _sGId )
70 :
71 sGId(std::move( _sGId ))
72{
73 sGId = sGId.replaceAll("\r", OString());
74}
75
76ResData::ResData( OString _sGId, OString _sFilename)
77 :
78 sGId(std::move( _sGId )),
79 sFilename(std::move( _sFilename ))
80{
81 sGId = sGId.replaceAll("\r", OString());
82}
83
84
85
86
87bool MergeEntrys::GetText( OString &rReturn,
88 const OString &nLangIndex, bool bDel )
89{
90 bool bReturn = true;
91 rReturn = sText[ nLangIndex ];
92 if ( bDel )
93 sText[ nLangIndex ] = "";
94 bReturn = bTextFirst[ nLangIndex ];
95 bTextFirst[ nLangIndex ] = false;
96 return bReturn;
97}
98
99namespace
100{
101 OString GetDoubleBars()
102 {
103 //DOUBLE VERTICAL LINE instead of || because the translations make their
104 //way into action_names under gtk3 where || is illegal
105 return u8"\u2016";
106 }
107}
108
109OString MergeEntrys::GetQTZText(const ResData& rResData, std::string_view rOrigText)
110{
111 const OString sFilename = rResData.sFilename.copy(rResData.sFilename.lastIndexOf('/')+1);
112 const OString sKey =
113 PoEntry::genKeyId(sFilename + rResData.sGId + rResData.sId + rResData.sResTyp + rOrigText);
114 return sKey + GetDoubleBars() + rOrigText;
115}
116
117
118
120 const OString &rFileName, std::string_view rFile,
121 bool bCaseSensitive, bool bWithQtz )
122{
123 auto const env = getenv("ENABLE_RELEASE_BUILD");
124 OString sEnableReleaseBuild(env == nullptr ? "" : env);
125
126 std::ifstream aInputStream( rFileName.getStr() );
127 if ( !aInputStream.is_open() )
128 {
129 SAL_WARN("l10ntools", "Can't open po path container file for " << rFileName);
130 return;
131 }
132 std::string sPoFile;
133 aInputStream >> sPoFile;
134 bool bFirstLang = true;
135 while( !aInputStream.eof() )
136 {
137 bool bSkipCurrentPOFile = false;
138 const OString sFileName( lcl_NormalizeFilename(rFile) );
139 const bool bReadAll = sFileName.isEmpty();
140 // coverity[tainted_data] - this is a build time tool
141 const OString sPoFileName(sPoFile.data(), static_cast<sal_Int32>(sPoFile.length()));
142 PoIfstream aPoInput;
143 aPoInput.open( sPoFileName );
144 if ( !aPoInput.isOpen() )
145 {
146 SAL_WARN("l10ntools", "Can't open file: " << sPoFileName);
147 return;
148 }
149
150 OString sLang;
151 //Get language id from path
152 {
153 static constexpr OStringLiteral sTransSource("translations/source/");
154 const sal_Int32 nStart =
155 sPoFileName.indexOf(sTransSource)+sTransSource.getLength();
156 const sal_Int32 nCount =
157 sPoFileName.indexOf('/',nStart) - nStart;
158 sLang = sPoFileName.copy(nStart,nCount);
159 }
160 aLanguageSet.insert( sLang );
161 PoEntry aNextPo;
162 do
163 {
164 if( !lcl_ReadPoChecked(aNextPo, aPoInput, sPoFileName) )
165 {
166 bSkipCurrentPOFile = true;
167 break;
168 }
169 } while( !aPoInput.eof() && aNextPo.getSourceFile() != sFileName && !bReadAll );
170 while( !aPoInput.eof() && (aNextPo.getSourceFile() == sFileName || bReadAll ) && !bSkipCurrentPOFile )
171 {
172 PoEntry aActPo( aNextPo );
173
174 bool bInSameComp = false;
175 OString sText;
176 OString sQHText;
177 OString sTitle;
178 OString sExText;
179 OString sExQHText;
180 OString sExTitle;
181 do
182 {
183 if( bInSameComp )
184 aActPo = aNextPo;
185 OString sTemp = aActPo.getMsgStr();
186 if( aActPo.isFuzzy() || sTemp.isEmpty() )
187 sTemp = aActPo.getMsgId();
188 switch( aActPo.getType() )
189 {
190 case PoEntry::TTEXT:
191 sText = sTemp;
192 sExText = aActPo.getMsgId();
193 break;
195 sQHText = sTemp;
196 sExQHText = aActPo.getMsgId();
197 break;
198 case PoEntry::TTITLE:
199 sTitle = sTemp;
200 sExTitle = aActPo.getMsgId();
201 break;
202 }
203 if( !lcl_ReadPoChecked(aNextPo, aPoInput, sPoFileName) )
204 {
205 bSkipCurrentPOFile = true;
206 break;
207 }
208 if (aPoInput.eof())
209 break;
210 bInSameComp = PoEntry::IsInSameComp(aActPo, aNextPo);
211 } while( bInSameComp );
212
214 aActPo.getResourceType(), aActPo.getGroupId(),
215 aActPo.getLocalId(), sLang, sText,
216 sQHText, sTitle, aActPo.getSourceFile(),
217 bFirstLang, bCaseSensitive );
218
219 if( bFirstLang && bWithQtz &&
220 sEnableReleaseBuild != "TRUE" )
221 {
222 aLanguageSet.insert("qtz");
224 aActPo.getResourceType(), aActPo.getGroupId(),
225 aActPo.getLocalId(), "qtz",
226 sExText, sExQHText,
227 sExTitle, aActPo.getSourceFile(),
228 false, bCaseSensitive );
229 }
230 }
231 aPoInput.close();
232 aInputStream >> sPoFile;
233 bFirstLang = false;
234 }
235 aInputStream.close();
236}
237
239{
240}
241
242std::vector<OString> MergeDataFile::GetLanguages() const
243{
244 return std::vector<OString>(aLanguageSet.begin(),aLanguageSet.end());
245}
246
247MergeEntrys *MergeDataFile::GetMergeData( ResData *pResData , bool bCaseSensitive )
248{
249 OString sOldG = pResData->sGId;
250 OString sOldL = pResData->sId;
251 OString sGID = pResData->sGId;
252 OString sLID;
253 if (sGID.isEmpty())
254 sGID = pResData->sId;
255 else
256 sLID = pResData->sId;
257 pResData->sGId = sGID;
258 pResData->sId = sLID;
259
260 OString sKey = CreateKey( pResData->sResTyp , pResData->sGId , pResData->sId , pResData->sFilename , bCaseSensitive );
261
262 auto mit = aMap.find( sKey );
263 if(mit != aMap.end())
264 {
265 pResData->sGId = sOldG;
266 pResData->sId = sOldL;
267 return mit->second.get();
268 }
269 pResData->sGId = sOldG;
270 pResData->sId = sOldL;
271 return nullptr;
272}
273
275{
276 // search for requested MergeEntrys
277 return GetMergeData( pResData );
278}
279
281{
282 // search for requested MergeEntrys
283 return GetMergeData( pResData , true );
284}
285
287 std::string_view rTYP, std::string_view rGID,
288 std::string_view rLID, const OString &nLANG,
289 const OString &rTEXT, const OString &rQHTEXT,
290 const OString &rTITLE, std::string_view rInFilename,
291 bool bFirstLang, bool bCaseSensitive )
292{
293 MergeEntrys *pMergeEntrys = nullptr;
294
295 // search for MergeData
296 OString sKey = CreateKey(rTYP , rGID , rLID , rInFilename , bCaseSensitive);
297
298 if( !bFirstLang )
299 {
300 auto mit = aMap.find( sKey );
301 if(mit != aMap.end())
302 pMergeEntrys = mit->second.get();
303
304 }
305
306 if( !pMergeEntrys )
307 {
308 pMergeEntrys = new MergeEntrys;
309 if (!aMap.emplace( sKey, std::unique_ptr<MergeEntrys>(pMergeEntrys) ).second)
310 {
311 std::cerr << "Duplicate entry " << sKey << "\n";
312 std::exit(EXIT_FAILURE);
313 }
314 }
315
316
317 // insert the cur string
318 if( nLANG =="qtz" )
319 {
320 const OString sTemp = OString::Concat(rInFilename) + rGID + rLID + rTYP;
321 pMergeEntrys->InsertEntry(
322 nLANG,
323 rTEXT.isEmpty()? rTEXT : PoEntry::genKeyId(sTemp + rTEXT) + GetDoubleBars() + rTEXT,
324 rQHTEXT.isEmpty()? rQHTEXT : PoEntry::genKeyId(sTemp + rQHTEXT) + GetDoubleBars() + rQHTEXT,
325 rTITLE.isEmpty()? rTITLE : PoEntry::genKeyId(sTemp + rTITLE) + GetDoubleBars() + rTITLE );
326 }
327 else
328 {
329 pMergeEntrys->InsertEntry( nLANG , rTEXT, rQHTEXT, rTITLE );
330 }
331}
332
333OString MergeDataFile::CreateKey(std::string_view rTYP, std::string_view rGID,
334 std::string_view rLID, std::string_view rFilename, bool bCaseSensitive)
335{
336 static const char sStroke[] = "-";
337 OString sKey = OString::Concat(rTYP) + sStroke + rGID + sStroke + rLID + sStroke +
338 lcl_NormalizeFilename(rFilename);
339 if(bCaseSensitive)
340 return sKey; // officecfg case sensitive identifier
341 return sKey.toAsciiUpperCase();
342}
343
344/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
MergeDataFile(const OString &rFileName, std::string_view rFile, bool bCaseSensitive, bool bWithQtz=true)
Definition: merge.cxx:119
~MergeDataFile()
Definition: merge.cxx:238
static OString CreateKey(std::string_view rTYP, std::string_view rGID, std::string_view rLID, std::string_view rFilename, bool bCaseSensitive)
Definition: merge.cxx:333
std::unordered_map< OString, std::unique_ptr< MergeEntrys > > aMap
Definition: export.hxx:116
std::set< OString > aLanguageSet
Definition: export.hxx:117
void InsertEntry(std::string_view rTYP, std::string_view rGID, std::string_view rLID, const OString &nLang, const OString &rTEXT, const OString &rQHTEXT, const OString &rTITLE, std::string_view sFilename, bool bFirstLang, bool bCaseSensitive)
Definition: merge.cxx:286
MergeEntrys * GetMergeData(ResData *pResData, bool bCaseSensitive=false)
Definition: merge.cxx:247
MergeEntrys * GetMergeEntrys(ResData *pResData)
Definition: merge.cxx:274
MergeEntrys * GetMergeEntrysCaseSensitive(ResData *pResData)
Definition: merge.cxx:280
std::vector< OString > GetLanguages() const
Definition: merge.cxx:242
Purpose: holds information of data to merge.
Definition: export.hxx:77
OStringBoolHashMap bTextFirst
Definition: export.hxx:81
OStringHashMap sText
Definition: export.hxx:80
void InsertEntry(const OString &rId, const OString &rText, const OString &rQuickHelpText, const OString &rTitle)
Definition: export.hxx:89
static OString GetQTZText(const ResData &rResData, std::string_view rOrigText)
Generate QTZ string with ResData For executable which works one language and without PO files.
Definition: merge.cxx:109
bool GetText(OString &rReturn, const OString &nLangIndex, bool bDel=false)
Definition: merge.cxx:87
Interface to use po entries in localization.
Definition: po.hxx:34
OString const & getMsgId() const
Definition: po.cxx:392
OString getResourceType() const
Get the type of component from which entry is extracted.
Definition: po.cxx:352
OString getGroupId() const
Definition: po.cxx:336
@ TQUICKHELPTEXT
Definition: po.hxx:45
@ TTITLE
Definition: po.hxx:45
@ TTEXT
Definition: po.hxx:45
OString const & getSourceFile() const
Get name of file from which entry is extracted.
Definition: po.cxx:330
OString const & getMsgStr() const
Definition: po.cxx:399
bool isFuzzy() const
Definition: po.cxx:377
static OString genKeyId(const OString &rGenerator)
Definition: po.cxx:415
static bool IsInSameComp(const PoEntry &rPo1, const PoEntry &rPo2)
Check whether po-s belong to the same localization component.
Definition: po.cxx:406
OString getLocalId() const
Definition: po.cxx:342
TYPE getType() const
Get the type of entry.
Definition: po.cxx:362
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
Purpose: holds mandatory data to export a single res.
Definition: export.hxx:55
OString sGId
Definition: export.hxx:62
OString sFilename
Definition: export.hxx:63
OString sId
Definition: export.hxx:61
ResData(OString rGId)
Definition: merge.cxx:69
OString sResTyp
Definition: export.hxx:60
int nCount
#define SAL_WARN(area, stream)
const css::uno::Reference< css::xml::crypto::XSecurityEnvironment > & env