1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <rtl/ustring.hxx>
20 #pragma warning (push, 1)
21 #pragma warning (disable: 4245)
23 #include <boost/crc.hpp>
32 /** Container of po entry
34 Provide all file operations related to LibreOffice specific
35 po entry and store it's attributes.
41 OString m_sExtractCom
;
52 virtual ~GenPoEntry();
53 // Default copy constructor and copy operator work well
55 OString
getReference() const { return m_sReference
; }
56 OString
getMsgCtxt() const { return m_sMsgCtxt
; }
57 OString
getMsgId() const { return m_sMsgId
; }
58 OString
getMsgStr() const { return m_sMsgStr
; }
59 bool isFuzzy() const { return m_bFuzzy
; }
60 bool isNull() const { return m_bNull
; }
62 void setExtractCom(const OString
& rExtractCom
)
64 m_sExtractCom
= rExtractCom
;
66 void setReference(const OString
& rReference
)
68 m_sReference
= rReference
;
70 void setMsgCtxt(const OString
& rMsgCtxt
)
72 m_sMsgCtxt
= rMsgCtxt
;
74 void setMsgId(const OString
& rMsgId
)
78 void setMsgStr(const OString
& rMsgStr
)
83 void writeToFile(std::ofstream
& rOFStream
) const;
84 void readFromFile(std::ifstream
& rIFStream
);
89 // Convert a normal string to msg/po output string
90 static OString
lcl_GenMsgString(const OString
& rString
)
92 if ( rString
.isEmpty() )
97 helper::escapeAll(rString
,"\n""\t""\r""\\""\"","\\n""\\t""\\r""\\\\""\\\"") +
100 while((nIndex
=sResult
.indexOf("\\n",nIndex
))!=-1)
102 if( sResult
.copy(nIndex
-1,3)!="\\\\n" &&
103 nIndex
!=sResult
.getLength()-3)
105 sResult
= sResult
.replaceAt(nIndex
,2,"\\n\"\n\"");
110 if ( sResult
.indexOf('\n') != -1 )
111 return "\"\"\n" + sResult
;
116 // Convert msg string to normal form
117 static OString
lcl_GenNormString(const OString
& rString
)
121 rString
.copy(1,rString
.getLength()-2),
122 "\\n""\\t""\\r""\\\\""\\\"",
123 "\n""\t""\r""\\""\"");
127 GenPoEntry::GenPoEntry()
128 : m_sExtractCom( OString() )
129 , m_sReference( OString() )
130 , m_sMsgCtxt( OString() )
131 , m_sMsgId( OString() )
132 , m_sMsgStr( OString() )
138 GenPoEntry::~GenPoEntry()
142 void GenPoEntry::writeToFile(std::ofstream
& rOFStream
) const
144 if ( rOFStream
.tellp() != std::ofstream::pos_type( 0 ))
145 rOFStream
<< std::endl
;
146 if ( !m_sExtractCom
.isEmpty() )
149 << m_sExtractCom
.replaceAll("\n","\n#. ").getStr() << std::endl
;
150 if ( !m_sReference
.isEmpty() )
151 rOFStream
<< "#: " << m_sReference
.getStr() << std::endl
;
153 rOFStream
<< "#, fuzzy" << std::endl
;
154 if ( !m_sMsgCtxt
.isEmpty() )
155 rOFStream
<< "msgctxt "
156 << lcl_GenMsgString(m_sReference
+"\n"+m_sMsgCtxt
).getStr()
158 rOFStream
<< "msgid "
159 << lcl_GenMsgString(m_sMsgId
).getStr() << std::endl
;
160 rOFStream
<< "msgstr "
161 << lcl_GenMsgString(m_sMsgStr
).getStr() << std::endl
;
164 void GenPoEntry::readFromFile(std::ifstream
& rIFStream
)
166 *this = GenPoEntry();
167 OString
* pLastMsg
= 0;
169 getline(rIFStream
,sTemp
);
170 if( rIFStream
.eof() || sTemp
.empty() )
175 while(!rIFStream
.eof())
177 OString sLine
= OString(sTemp
.data(),sTemp
.length());
178 if (sLine
.startsWith("#. "))
180 if( !m_sExtractCom
.isEmpty() )
182 m_sExtractCom
+= "\n";
184 m_sExtractCom
+= sLine
.copy(3);
186 else if (sLine
.startsWith("#: "))
188 m_sReference
= sLine
.copy(3);
190 else if (sLine
.startsWith("#, fuzzy"))
194 else if (sLine
.startsWith("msgctxt "))
196 m_sMsgCtxt
= lcl_GenNormString(sLine
.copy(8));
197 pLastMsg
= &m_sMsgCtxt
;
199 else if (sLine
.startsWith("msgid "))
201 m_sMsgId
= lcl_GenNormString(sLine
.copy(6));
202 pLastMsg
= &m_sMsgId
;
204 else if (sLine
.startsWith("msgstr "))
206 m_sMsgStr
= lcl_GenNormString(sLine
.copy(7));
207 pLastMsg
= &m_sMsgStr
;
209 else if (sLine
.startsWith("\"") && pLastMsg
)
211 if (pLastMsg
!= &m_sMsgCtxt
|| sLine
!= "\"" + m_sReference
+ "\\n\"")
213 *pLastMsg
+= lcl_GenNormString(sLine
);
218 getline(rIFStream
,sTemp
);
228 , m_bIsInitialized( false )
233 const OString
& rSourceFile
, const OString
& rResType
, const OString
& rGroupId
,
234 const OString
& rLocalId
, const OString
& rHelpText
,
235 const OString
& rText
, const TYPE eType
)
237 , m_bIsInitialized( false )
239 if( rSourceFile
.isEmpty() )
241 else if ( rResType
.isEmpty() )
243 else if ( rGroupId
.isEmpty() )
245 else if ( rText
.isEmpty() )
247 else if ( rHelpText
.getLength() == 5 )
250 m_pGenPo
= new GenPoEntry();
251 m_pGenPo
->setReference(rSourceFile
.copy(rSourceFile
.lastIndexOf('/')+1));
255 (rLocalId
.isEmpty() ? OString( "" ) : rLocalId
+ "\n") +
259 sMsgCtxt
+= ".text"; break;
261 sMsgCtxt
+= ".quickhelptext"; break;
263 sMsgCtxt
+= ".title"; break;
264 // Default case is unneeded because the type of eType has only three element
266 m_pGenPo
->setMsgCtxt(sMsgCtxt
);
267 m_pGenPo
->setMsgId(rText
);
268 m_pGenPo
->setExtractCom(
269 ( !rHelpText
.isEmpty() ? rHelpText
+ "\n" : OString( "" )) +
270 genKeyId( m_pGenPo
->getReference() + rGroupId
+ rLocalId
+ rResType
+ rText
) );
271 m_bIsInitialized
= true;
279 PoEntry::PoEntry( const PoEntry
& rPo
)
280 : m_pGenPo( rPo
.m_pGenPo
? new GenPoEntry( *(rPo
.m_pGenPo
) ) : 0 )
281 , m_bIsInitialized( rPo
.m_bIsInitialized
)
285 PoEntry
& PoEntry::operator=(const PoEntry
& rPo
)
295 *m_pGenPo
= *(rPo
.m_pGenPo
);
299 m_pGenPo
= new GenPoEntry( *(rPo
.m_pGenPo
) );
307 m_bIsInitialized
= rPo
.m_bIsInitialized
;
311 OString
PoEntry::getSourceFile() const
313 assert( m_bIsInitialized
);
314 return m_pGenPo
->getReference();
317 OString
PoEntry::getGroupId() const
319 assert( m_bIsInitialized
);
320 return m_pGenPo
->getMsgCtxt().getToken(0,'\n');
323 OString
PoEntry::getLocalId() const
325 assert( m_bIsInitialized
);
326 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
327 if (sMsgCtxt
.indexOf('\n')==sMsgCtxt
.lastIndexOf('\n'))
330 return sMsgCtxt
.getToken(1,'\n');
333 OString
PoEntry::getResourceType() const
335 assert( m_bIsInitialized
);
336 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
337 if (sMsgCtxt
.indexOf('\n')==sMsgCtxt
.lastIndexOf('\n'))
338 return sMsgCtxt
.getToken(1,'\n').getToken(0,'.');
340 return sMsgCtxt
.getToken(2,'\n').getToken(0,'.');
343 PoEntry::TYPE
PoEntry::getType() const
345 assert( m_bIsInitialized
);
346 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
347 const OString sType
= sMsgCtxt
.copy( sMsgCtxt
.lastIndexOf('.') + 1 );
349 (sType
== "text" || sType
== "quickhelptext" || sType
== "title") );
350 if ( sType
== "text" )
352 else if ( sType
== "quickhelptext" )
353 return TQUICKHELPTEXT
;
358 bool PoEntry::isFuzzy() const
360 assert( m_bIsInitialized
);
361 return m_pGenPo
->isFuzzy();
364 // Get translation string in merge format
365 OString
PoEntry::getMsgId() const
367 assert( m_bIsInitialized
);
368 return m_pGenPo
->getMsgId();
371 // Get translated string in merge format
372 OString
PoEntry::getMsgStr() const
374 assert( m_bIsInitialized
);
375 return m_pGenPo
->getMsgStr();
379 bool PoEntry::IsInSameComp(const PoEntry
& rPo1
,const PoEntry
& rPo2
)
381 assert( rPo1
.m_bIsInitialized
&& rPo2
.m_bIsInitialized
);
382 return ( rPo1
.getSourceFile() == rPo2
.getSourceFile() &&
383 rPo1
.getGroupId() == rPo2
.getGroupId() &&
384 rPo1
.getLocalId() == rPo2
.getLocalId() &&
385 rPo1
.getResourceType() == rPo2
.getResourceType() );
388 OString
PoEntry::genKeyId(const OString
& rGenerator
)
390 boost::crc_32_type aCRC32
;
391 aCRC32
.process_bytes(rGenerator
.getStr(), rGenerator
.getLength());
392 sal_uInt32 nCRC
= aCRC32
.checksum();
393 // Use simple ASCII characters, exclude I, l, 1 and O, 0 to avoid confusing IDs
394 static const char sSymbols
[] =
395 "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
397 for( short nKeyInd
= 0; nKeyInd
< 5; ++nKeyInd
)
399 sKeyId
[nKeyInd
] = sSymbols
[(nCRC
& 63) % strlen(sSymbols
)];
403 return OString(sKeyId
);
412 // Get actual time in "YEAR-MO-DA HO:MI+ZONE" form
413 static OString
lcl_GetTime()
415 time_t aNow
= time(NULL
);
416 struct tm
* pNow
= localtime(&aNow
);
418 strftime( pBuff
, sizeof pBuff
, "%Y-%m-%d %H:%M%z", pNow
);
423 PoHeader::PoHeader( const OString
& rExtSrc
)
424 : m_pGenPo( new GenPoEntry() )
425 , m_bIsInitialized( false )
427 m_pGenPo
->setExtractCom("extracted from " + rExtSrc
);
429 OString("Project-Id-Version: PACKAGE VERSION\n"
430 "Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?"
431 "product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
432 "POT-Creation-Date: ") + lcl_GetTime() +
433 OString("\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
434 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
435 "Language-Team: LANGUAGE <LL@li.org>\n"
436 "MIME-Version: 1.0\n"
437 "Content-Type: text/plain; charset=UTF-8\n"
438 "Content-Transfer-Encoding: 8bit\n"
439 "X-Generator: LibreOffice\n"
440 "X-Accelerator-Marker: ~\n"));
441 m_bIsInitialized
= true;
444 PoHeader::~PoHeader()
453 PoOfstream::PoOfstream()
455 , m_bIsAfterHeader( false )
459 PoOfstream::PoOfstream(const OString
& rFileName
, OpenMode aMode
)
461 , m_bIsAfterHeader( false )
463 open( rFileName
, aMode
);
466 PoOfstream::~PoOfstream()
474 void PoOfstream::open(const OString
& rFileName
, OpenMode aMode
)
479 m_aOutPut
.open( rFileName
.getStr(),
480 std::ios_base::out
| std::ios_base::trunc
);
481 m_bIsAfterHeader
= false;
483 else if( aMode
== APP
)
485 m_aOutPut
.open( rFileName
.getStr(),
486 std::ios_base::out
| std::ios_base::app
);
487 m_bIsAfterHeader
= m_aOutPut
.tellp() != std::ofstream::pos_type( 0 );
491 void PoOfstream::close()
497 void PoOfstream::writeHeader(const PoHeader
& rPoHeader
)
499 assert( isOpen() && !m_bIsAfterHeader
&& rPoHeader
.m_bIsInitialized
);
500 rPoHeader
.m_pGenPo
->writeToFile( m_aOutPut
);
501 m_bIsAfterHeader
= true;
504 void PoOfstream::writeEntry( const PoEntry
& rPoEntry
)
506 assert( isOpen() && m_bIsAfterHeader
&& rPoEntry
.m_bIsInitialized
);
507 rPoEntry
.m_pGenPo
->writeToFile( m_aOutPut
);
517 // Check the validity of read entry
518 static bool lcl_CheckInputEntry(const GenPoEntry
& rEntry
)
520 const OString sMsgCtxt
= rEntry
.getMsgCtxt();
521 const sal_Int32 nFirstEndLine
= sMsgCtxt
.indexOf('\n');
522 const sal_Int32 nLastEndLine
= sMsgCtxt
.lastIndexOf('\n');
523 const sal_Int32 nLastDot
= sMsgCtxt
.lastIndexOf('.');
524 const OString sType
= sMsgCtxt
.copy( nLastDot
+ 1 );
525 return !rEntry
.getReference().isEmpty() &&
527 (nLastEndLine
== nFirstEndLine
|| nLastEndLine
== sMsgCtxt
.indexOf('\n',nFirstEndLine
+1)) &&
528 nLastDot
- nLastEndLine
> 1 &&
529 (sType
== "text" || sType
== "quickhelptext" || sType
== "title")&&
530 !rEntry
.getMsgId().isEmpty();
535 PoIfstream::PoIfstream()
541 PoIfstream::PoIfstream(const OString
& rFileName
)
548 PoIfstream::~PoIfstream()
556 void PoIfstream::open( const OString
& rFileName
)
559 m_aInPut
.open( rFileName
.getStr(), std::ios_base::in
);
563 std::getline(m_aInPut
,sTemp
);
564 while( !sTemp
.empty() && !m_aInPut
.eof() )
566 std::getline(m_aInPut
,sTemp
);
571 void PoIfstream::close()
577 void PoIfstream::readEntry( PoEntry
& rPoEntry
)
579 assert( isOpen() && !eof() );
581 aGenPo
.readFromFile( m_aInPut
);
582 if( aGenPo
.isNull() )
585 rPoEntry
= PoEntry();
589 if( lcl_CheckInputEntry(aGenPo
) )
591 if( rPoEntry
.m_pGenPo
)
593 *(rPoEntry
.m_pGenPo
) = aGenPo
;
597 rPoEntry
.m_pGenPo
= new GenPoEntry( aGenPo
);
599 rPoEntry
.m_bIsInitialized
= true;
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */