LibreOffice Module l10ntools (master) 1
lngmerge.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
22#include <o3tl/string_view.hxx>
23
24#include <cstddef>
25#include <iostream>
26#include <memory>
27#include <string>
28
29#include <common.hxx>
30#include <po.hxx>
31#include <lngmerge.hxx>
32#include <utility>
33
34namespace {
35
36bool lcl_isNextGroup(OString &sGroup_out, std::string_view sLineTrim)
37{
38 if (o3tl::starts_with(sLineTrim, "[") && o3tl::ends_with(sLineTrim, "]"))
39 {
40 sLineTrim = o3tl::getToken(sLineTrim, 1, '[');
41 sLineTrim = o3tl::getToken(sLineTrim, 0, ']');
42 sGroup_out = OString(o3tl::trim(sLineTrim));
43 return true;
44 }
45 return false;
46}
47
48void lcl_RemoveUTF8ByteOrderMarker( OString &rString )
49{
50 if( rString.getLength() >= 3 && rString[0] == '\xEF' &&
51 rString[1] == '\xBB' && rString[2] == '\xBF' )
52 {
53 rString = rString.copy(3);
54 }
55}
56
57}
58
59
60
61LngParser::LngParser(OString sLngFile)
62 : sSource(std::move( sLngFile ))
63{
64 std::ifstream aStream(sSource.getStr());
65 if (!aStream.is_open())
66 return;
67
68 bool bFirstLine = true;
69 std::string s;
70 std::getline(aStream, s);
71 while (!aStream.eof())
72 {
73 OString sLine(s.data(), s.length());
74
75 if( bFirstLine )
76 {
77 // Always remove UTF8 BOM from the first line
78 lcl_RemoveUTF8ByteOrderMarker( sLine );
79 bFirstLine = false;
80 }
81
82 mvLines.push_back( sLine );
83 std::getline(aStream, s);
84 }
85 mvLines.push_back( OString() );
86}
87
89{
90}
91
92void LngParser::CreatePO( const OString &rPOFile )
93{
94 PoOfstream aPOStream( rPOFile, PoOfstream::APP );
95 if (!aPOStream.isOpen()) {
96 std::cerr << "Ulfex error: Can't open po file:" << rPOFile << "\n";
97 }
98
99 size_t nPos = 0;
100 bool bStart = true;
101 OString sGroup, sLine;
103 OString sID;
104
105 while( nPos < mvLines.size() ) {
106 sLine = mvLines[ nPos++ ];
107 while( nPos < mvLines.size() && !isNextGroup( sGroup , sLine ) ) {
108 ReadLine( sLine , Text );
109 sID = sGroup;
110 sLine = mvLines[ nPos++ ];
111 }
112 if( bStart ) {
113 bStart = false;
114 sID = sGroup;
115 }
116 else {
117 WritePO( aPOStream , Text , sSource , sID );
118 }
119 Text.erase("x-comment");
120 }
121 aPOStream.close();
122}
123
125 OStringHashMap &rText_inout, const OString &rActFileName,
126 const OString &rID)
127{
129 "Ulfex", aPOStream, rActFileName, "LngText",
130 rID, OString(), rText_inout.count("x-comment") ? rText_inout["x-comment"] : OString(), rText_inout["en-US"]);
131}
132
133bool LngParser::isNextGroup(OString &sGroup_out, std::string_view sLine_in)
134{
135 return lcl_isNextGroup(sGroup_out, o3tl::trim(sLine_in));
136}
137
138void LngParser::ReadLine(std::string_view rLine_in,
139 OStringHashMap &rText_inout)
140{
141 if (!o3tl::starts_with(rLine_in, " *") && !o3tl::starts_with(rLine_in, "/*"))
142 {
143 OString sLang(o3tl::trim(o3tl::getToken(rLine_in, 0, '=')));
144 if (!sLang.isEmpty()) {
145 OString sText(o3tl::getToken(rLine_in,1, '"'));
146 rText_inout[sLang] = sText;
147 }
148 }
149}
150
152 const OString &rPOFile,
153 const OString &rDestinationFile,
154 std::string_view rLanguage )
155{
156 std::ofstream aDestination(
157 rDestinationFile.getStr(), std::ios_base::out | std::ios_base::trunc);
158
159 MergeDataFile aMergeDataFile( rPOFile, sSource, false, true );
160 if( o3tl::equalsIgnoreAsciiCase(rLanguage, "ALL") )
161 aLanguages = aMergeDataFile.GetLanguages();
162
163 size_t nPos = 0;
164 bool bGroup = false;
165 OString sGroup;
166
167 // seek to next group
168 while ( nPos < mvLines.size() && !bGroup )
169 bGroup = lcl_isNextGroup(sGroup, o3tl::trim(mvLines[nPos++]));
170
171 while ( nPos < mvLines.size()) {
173 OString sID( sGroup );
174 std::size_t nLastLangPos = 0;
175
176 ResData aResData( sID, sSource );
177 aResData.sResTyp = "LngText";
178 MergeEntrys *pEntrys = aMergeDataFile.GetMergeEntrys( &aResData );
179 // read languages
180 bGroup = false;
181
182 OString sLanguagesDone;
183
184 while ( nPos < mvLines.size() && !bGroup )
185 {
186 const OString sLine{ mvLines[nPos].trim() };
187 if ( lcl_isNextGroup(sGroup, sLine) )
188 {
189 bGroup = true;
190 nPos ++;
191 sLanguagesDone = "";
192 }
193 else
194 {
195 sal_Int32 n = 0;
196 OString sLang(sLine.getToken(0, '=', n));
197 if (n == -1 || static_cast<bool>(sLine.match("/*")))
198 {
199 ++nPos;
200 }
201 else
202 {
203 sLang = sLang.trim();
204
205 OString sSearch{ ";" + sLang + ";" };
206
207 if ( sLanguagesDone.indexOf( sSearch ) != -1 ) {
208 mvLines.erase( mvLines.begin() + nPos );
209 }
210 if( pEntrys )
211 {
212 if( !sLang.isEmpty() )
213 {
214 OString sNewText;
215 pEntrys->GetText( sNewText, sLang, true );
216 if( sLang == "qtz" )
217 continue;
218
219 if ( !sNewText.isEmpty()) {
220 mvLines[ nPos ] = sLang
221 + " = \""
222 // escape quotes, unescape double escaped quotes fdo#56648
223 + sNewText.replaceAll("\"","\\\"").replaceAll("\\\\\"","\\\"")
224 + "\"";
225 Text[ sLang ] = sNewText;
226 }
227 }
228 nLastLangPos = nPos;
229 nPos ++;
230 sLanguagesDone += sSearch;
231 }
232 else {
233 nLastLangPos = nPos;
234 nPos ++;
235 sLanguagesDone += sSearch;
236 }
237 }
238 }
239 }
240 OString sCur;
241 if ( nLastLangPos )
242 {
243 for(size_t n = 0; n < aLanguages.size(); ++n)
244 {
245 sCur = aLanguages[ n ];
246 if( !sCur.equalsIgnoreAsciiCase("en-US") && Text[sCur].isEmpty() && pEntrys )
247 {
248
249 OString sNewText;
250 pEntrys->GetText( sNewText, sCur, true );
251 if( sCur == "qtz" )
252 continue;
253 if ( !sNewText.isEmpty() && sCur != "x-comment")
254 {
255 const OString sLine { sCur
256 + " = \""
257 // escape quotes, unescape double escaped quotes fdo#56648
258 + sNewText.replaceAll("\"","\\\"").replaceAll("\\\\\"","\\\"")
259 + "\"" };
260
261 nLastLangPos++;
262 nPos++;
263
264 if ( nLastLangPos < mvLines.size() ) {
265 mvLines.insert( mvLines.begin() + nLastLangPos, sLine );
266 } else {
267 mvLines.push_back( sLine );
268 }
269 }
270 }
271 }
272 }
273 }
274
275 for ( size_t i = 0; i < mvLines.size(); ++i )
276 aDestination << mvLines[i] << '\n';
277
278 aDestination.close();
279}
280
281/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::unordered_map< OString, OString > OStringHashMap
Definition: cfgmerge.hxx:33
void Merge(const OString &rPOFile, const OString &rDestinationFile, std::string_view rLanguage)
Definition: lngmerge.cxx:151
static void ReadLine(std::string_view rLine_in, OStringHashMap &rText_inout)
Definition: lngmerge.cxx:138
OString sSource
Definition: lngmerge.hxx:44
LngParser(OString sLngFile)
Definition: lngmerge.cxx:61
~LngParser()
Definition: lngmerge.cxx:88
std::vector< OString > aLanguages
Definition: lngmerge.hxx:45
std::vector< OString > mvLines
Definition: lngmerge.hxx:43
static void WritePO(PoOfstream &aPOStream, OStringHashMap &rText_inout, const OString &rActFileName, const OString &rID)
Definition: lngmerge.cxx:124
static bool isNextGroup(OString &sGroup_out, std::string_view sLine_in)
Definition: lngmerge.cxx:133
void CreatePO(const OString &rPOFile)
Definition: lngmerge.cxx:92
Purpose: holds information of data to merge, read from PO file.
Definition: export.hxx:114
MergeEntrys * GetMergeEntrys(ResData *pResData)
Definition: merge.cxx:274
std::vector< OString > GetLanguages() const
Definition: merge.cxx:242
Purpose: holds information of data to merge.
Definition: export.hxx:77
bool GetText(OString &rReturn, const OString &nLangIndex, bool bDel=false)
Definition: merge.cxx:87
Interface to write po entry to files as output streams.
Definition: po.hxx:101
bool isOpen() const
Definition: po.hxx:116
void close()
Definition: po.cxx:515
@ APP
Definition: po.hxx:109
Purpose: holds mandatory data to export a single res.
Definition: export.hxx:55
OString sResTyp
Definition: export.hxx:60
sal_Int64 n
sal_uInt16 nPos
void writePoEntry(const OString &rExecutable, PoOfstream &rPoStream, const OString &rSourceFile, std::string_view rResType, const OString &rGroupId, const OString &rLocalId, const OString &rHelpText, const OString &rText, const PoEntry::TYPE eType)
Write out a PoEntry with attention to exceptions.
Definition: common.cxx:110
int i
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
constexpr bool ends_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)