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>
19 #include <boost/crc.hpp>
24 /** Container of po entry
26 Provide all file operations related to LibreOffice specific
27 po entry and store it's attributes.
33 OString m_sExtractCom
;
44 virtual ~GenPoEntry();
45 // Default copy constructor and copy operator work well
47 virtual OString
getExtractCom() const { return m_sExtractCom
; }
48 virtual OString
getReference() const { return m_sReference
; }
49 virtual OString
getMsgCtxt() const { return m_sMsgCtxt
; }
50 virtual OString
getMsgId() const { return m_sMsgId
; }
51 virtual OString
getMsgStr() const { return m_sMsgStr
; }
52 virtual bool isFuzzy() const { return m_bFuzzy
; }
53 virtual bool isNull() const { return m_bNull
; }
55 virtual void setExtractCom(const OString
& rExtractCom
)
57 m_sExtractCom
= rExtractCom
;
59 virtual void setReference(const OString
& rReference
)
61 m_sReference
= rReference
;
63 virtual void setMsgCtxt(const OString
& rMsgCtxt
)
65 m_sMsgCtxt
= rMsgCtxt
;
67 virtual void setMsgId(const OString
& rMsgId
)
71 virtual void setMsgStr(const OString
& rMsgStr
)
75 virtual void setFuzzy(const bool bFuzzy
)
80 virtual void writeToFile(std::ofstream
& rOFStream
) const;
81 virtual void readFromFile(std::ifstream
& rIFStream
);
86 // Convert a normal string to msg/po output string
87 static OString
lcl_GenMsgString(const OString
& rString
)
89 if ( rString
.isEmpty() )
94 helper::escapeAll(rString
,"\n""\t""\r""\\""\"","\\n""\\t""\\r""\\\\""\\\"") +
97 while((nIndex
=sResult
.indexOf("\\n",nIndex
))!=-1)
99 if( sResult
.copy(nIndex
-1,3)!="\\\\n" &&
100 nIndex
!=sResult
.getLength()-3)
102 sResult
= sResult
.replaceAt(nIndex
,2,"\\n\"\n\"");
107 if ( sResult
.indexOf('\n') != -1 )
108 return "\"\"\n" + sResult
;
113 // Convert msg string to normal form
114 static OString
lcl_GenNormString(const OString
& rString
)
118 rString
.copy(1,rString
.getLength()-2),
119 "\\n""\\t""\\r""\\\\""\\\"",
120 "\n""\t""\r""\\""\"");
124 GenPoEntry::GenPoEntry()
125 : m_sExtractCom( OString() )
126 , m_sReference( OString() )
127 , m_sMsgCtxt( OString() )
128 , m_sMsgId( OString() )
129 , m_sMsgStr( OString() )
135 GenPoEntry::~GenPoEntry()
139 void GenPoEntry::writeToFile(std::ofstream
& rOFStream
) const
141 if ( rOFStream
.tellp() != std::ofstream::pos_type( 0 ))
142 rOFStream
<< std::endl
;
143 if ( !m_sExtractCom
.isEmpty() )
146 << m_sExtractCom
.replaceAll("\n","\n#. ").getStr() << std::endl
;
147 if ( !m_sReference
.isEmpty() )
148 rOFStream
<< "#: " << m_sReference
.getStr() << std::endl
;
150 rOFStream
<< "#, fuzzy" << std::endl
;
151 if ( !m_sMsgCtxt
.isEmpty() )
152 rOFStream
<< "msgctxt "
153 << lcl_GenMsgString(m_sReference
+"\n"+m_sMsgCtxt
).getStr()
155 rOFStream
<< "msgid "
156 << lcl_GenMsgString(m_sMsgId
).getStr() << std::endl
;
157 rOFStream
<< "msgstr "
158 << lcl_GenMsgString(m_sMsgStr
).getStr() << std::endl
;
161 void GenPoEntry::readFromFile(std::ifstream
& rIFStream
)
163 *this = GenPoEntry();
164 OString
* pLastMsg
= 0;
166 getline(rIFStream
,sTemp
);
167 if( rIFStream
.eof() || sTemp
.empty() )
172 while(!rIFStream
.eof())
174 OString sLine
= OString(sTemp
.data(),sTemp
.length());
175 if (sLine
.startsWith("#. "))
177 if( !m_sExtractCom
.isEmpty() )
179 m_sExtractCom
+= "\n";
181 m_sExtractCom
+= sLine
.copy(3);
183 else if (sLine
.startsWith("#: "))
185 m_sReference
= sLine
.copy(3);
187 else if (sLine
.startsWith("#, fuzzy"))
191 else if (sLine
.startsWith("msgctxt "))
193 m_sMsgCtxt
= lcl_GenNormString(sLine
.copy(8));
194 pLastMsg
= &m_sMsgCtxt
;
196 else if (sLine
.startsWith("msgid "))
198 m_sMsgId
= lcl_GenNormString(sLine
.copy(6));
199 pLastMsg
= &m_sMsgId
;
201 else if (sLine
.startsWith("msgstr "))
203 m_sMsgStr
= lcl_GenNormString(sLine
.copy(7));
204 pLastMsg
= &m_sMsgStr
;
206 else if (sLine
.startsWith("\"") && pLastMsg
)
208 if (pLastMsg
!= &m_sMsgCtxt
|| sLine
!= "\"" + m_sReference
+ "\\n\"")
210 *pLastMsg
+= lcl_GenNormString(sLine
);
215 getline(rIFStream
,sTemp
);
225 , m_bIsInitialized( false )
230 const OString
& rSourceFile
, const OString
& rResType
, const OString
& rGroupId
,
231 const OString
& rLocalId
, const OString
& rHelpText
,
232 const OString
& rText
, const TYPE eType
)
234 , m_bIsInitialized( false )
236 if( rSourceFile
.isEmpty() )
238 else if ( rResType
.isEmpty() )
240 else if ( rGroupId
.isEmpty() )
242 else if ( rText
.isEmpty() )
244 else if ( rHelpText
.getLength() == 5 )
247 m_pGenPo
= new GenPoEntry();
248 m_pGenPo
->setReference(rSourceFile
.copy(rSourceFile
.lastIndexOf("/")+1));
252 (rLocalId
.isEmpty() ? OString( "" ) : rLocalId
+ "\n") +
256 sMsgCtxt
+= ".text"; break;
258 sMsgCtxt
+= ".quickhelptext"; break;
260 sMsgCtxt
+= ".title"; break;
261 // Default case is unneeded because the type of eType has only three element
263 m_pGenPo
->setMsgCtxt(sMsgCtxt
);
264 m_pGenPo
->setMsgId(rText
);
265 m_pGenPo
->setExtractCom(
266 ( !rHelpText
.isEmpty() ? rHelpText
+ "\n" : OString( "" )) +
267 genKeyId( m_pGenPo
->getReference() + rGroupId
+ rLocalId
+ rResType
+ rText
) );
268 m_bIsInitialized
= true;
276 PoEntry::PoEntry( const PoEntry
& rPo
)
277 : m_pGenPo( rPo
.m_pGenPo
? new GenPoEntry( *(rPo
.m_pGenPo
) ) : 0 )
278 , m_bIsInitialized( rPo
.m_bIsInitialized
)
282 PoEntry
& PoEntry::operator=(const PoEntry
& rPo
)
292 *m_pGenPo
= *(rPo
.m_pGenPo
);
296 m_pGenPo
= new GenPoEntry( *(rPo
.m_pGenPo
) );
304 m_bIsInitialized
= rPo
.m_bIsInitialized
;
308 OString
PoEntry::getSourceFile() const
310 assert( m_bIsInitialized
);
311 return m_pGenPo
->getReference();
314 OString
PoEntry::getGroupId() const
316 assert( m_bIsInitialized
);
317 return m_pGenPo
->getMsgCtxt().getToken(0,'\n');
320 OString
PoEntry::getLocalId() const
322 assert( m_bIsInitialized
);
323 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
324 if (sMsgCtxt
.indexOf('\n')==sMsgCtxt
.lastIndexOf('\n'))
327 return sMsgCtxt
.getToken(1,'\n');
330 OString
PoEntry::getResourceType() const
332 assert( m_bIsInitialized
);
333 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
334 if (sMsgCtxt
.indexOf('\n')==sMsgCtxt
.lastIndexOf('\n'))
335 return sMsgCtxt
.getToken(1,'\n').getToken(0,'.');
337 return sMsgCtxt
.getToken(2,'\n').getToken(0,'.');
340 PoEntry::TYPE
PoEntry::getType() const
342 assert( m_bIsInitialized
);
343 const OString sMsgCtxt
= m_pGenPo
->getMsgCtxt();
344 const OString sType
= sMsgCtxt
.copy( sMsgCtxt
.lastIndexOf('.') + 1 );
346 (sType
== "text" || sType
== "quickhelptext" || sType
== "title") );
347 if ( sType
== "text" )
349 else if ( sType
== "quickhelptext" )
350 return TQUICKHELPTEXT
;
355 bool PoEntry::isFuzzy() const
357 assert( m_bIsInitialized
);
358 return m_pGenPo
->isFuzzy();
361 // Get translation string in merge format
362 OString
PoEntry::getMsgId() const
364 assert( m_bIsInitialized
);
365 return m_pGenPo
->getMsgId();
368 // Get translated string in merge format
369 OString
PoEntry::getMsgStr() const
371 assert( m_bIsInitialized
);
372 return m_pGenPo
->getMsgStr();
376 bool PoEntry::IsInSameComp(const PoEntry
& rPo1
,const PoEntry
& rPo2
)
378 assert( rPo1
.m_bIsInitialized
&& rPo2
.m_bIsInitialized
);
379 return ( rPo1
.getSourceFile() == rPo2
.getSourceFile() &&
380 rPo1
.getGroupId() == rPo2
.getGroupId() &&
381 rPo1
.getLocalId() == rPo2
.getLocalId() &&
382 rPo1
.getResourceType() == rPo2
.getResourceType() );
385 OString
PoEntry::genKeyId(const OString
& rGenerator
)
387 boost::crc_32_type aCRC32
;
388 aCRC32
.process_bytes(rGenerator
.getStr(), rGenerator
.getLength());
389 sal_uInt32 nCRC
= aCRC32
.checksum();
390 // Use simple ASCII characters, exclude I, l, 1 and O, 0 to avoid confusing IDs
391 static const OString sSymbols
=
392 "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
394 for( short nKeyInd
= 0; nKeyInd
< 5; ++nKeyInd
)
396 sKeyId
[nKeyInd
] = sSymbols
[(nCRC
& 63) % sSymbols
.getLength()];
400 return OString(sKeyId
);
409 // Get actual time in "YEAR-MO-DA HO:MI+ZONE" form
410 static OString
lcl_GetTime()
412 time_t aNow
= time(NULL
);
413 struct tm
* pNow
= localtime(&aNow
);
415 strftime( pBuff
, sizeof pBuff
, "%Y-%m-%d %H:%M%z", pNow
);
420 PoHeader::PoHeader( const OString
& rExtSrc
)
421 : m_pGenPo( new GenPoEntry() )
422 , m_bIsInitialized( false )
424 m_pGenPo
->setExtractCom("extracted from " + rExtSrc
);
426 OString("Project-Id-Version: PACKAGE VERSION\n"
427 "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
428 "product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
429 "POT-Creation-Date: ") + lcl_GetTime() +
430 OString("\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
431 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
432 "Language-Team: LANGUAGE <LL@li.org>\n"
433 "MIME-Version: 1.0\n"
434 "Content-Type: text/plain; charset=UTF-8\n"
435 "Content-Transfer-Encoding: 8bit\n"
436 "X-Generator: LibreOffice\n"
437 "X-Accelerator-Marker: ~\n"));
438 m_bIsInitialized
= true;
441 PoHeader::~PoHeader()
450 PoOfstream::PoOfstream()
452 , m_bIsAfterHeader( false )
456 PoOfstream::PoOfstream(const OString
& rFileName
, OpenMode aMode
)
458 , m_bIsAfterHeader( false )
460 open( rFileName
, aMode
);
463 PoOfstream::~PoOfstream()
471 void PoOfstream::open(const OString
& rFileName
, OpenMode aMode
)
476 m_aOutPut
.open( rFileName
.getStr(),
477 std::ios_base::out
| std::ios_base::trunc
);
478 m_bIsAfterHeader
= false;
480 else if( aMode
== APP
)
482 m_aOutPut
.open( rFileName
.getStr(),
483 std::ios_base::out
| std::ios_base::app
);
484 m_bIsAfterHeader
= m_aOutPut
.tellp() != std::ofstream::pos_type( 0 );
488 void PoOfstream::close()
494 void PoOfstream::writeHeader(const PoHeader
& rPoHeader
)
496 assert( isOpen() && !m_bIsAfterHeader
&& rPoHeader
.m_bIsInitialized
);
497 rPoHeader
.m_pGenPo
->writeToFile( m_aOutPut
);
498 m_bIsAfterHeader
= true;
501 void PoOfstream::writeEntry( const PoEntry
& rPoEntry
)
503 assert( isOpen() && m_bIsAfterHeader
&& rPoEntry
.m_bIsInitialized
);
504 rPoEntry
.m_pGenPo
->writeToFile( m_aOutPut
);
514 // Check the validity of read entry
515 static bool lcl_CheckInputEntry(const GenPoEntry
& rEntry
)
517 const OString sMsgCtxt
= rEntry
.getMsgCtxt();
518 const sal_Int32 nFirstEndLine
= sMsgCtxt
.indexOf('\n');
519 const sal_Int32 nLastEndLine
= sMsgCtxt
.lastIndexOf('\n');
520 const sal_Int32 nLastDot
= sMsgCtxt
.lastIndexOf('.');
521 const OString sType
= sMsgCtxt
.copy( nLastDot
+ 1 );
522 return !rEntry
.getReference().isEmpty() &&
524 (nLastEndLine
== nFirstEndLine
|| nLastEndLine
== sMsgCtxt
.indexOf('\n',nFirstEndLine
+1)) &&
525 nLastDot
- nLastEndLine
> 1 &&
526 (sType
== "text" || sType
== "quickhelptext" || sType
== "title")&&
527 !rEntry
.getMsgId().isEmpty();
532 PoIfstream::PoIfstream()
538 PoIfstream::PoIfstream(const OString
& rFileName
)
545 PoIfstream::~PoIfstream()
553 void PoIfstream::open( const OString
& rFileName
)
556 m_aInPut
.open( rFileName
.getStr(), std::ios_base::in
);
560 std::getline(m_aInPut
,sTemp
);
561 while( !sTemp
.empty() && !m_aInPut
.eof() )
563 std::getline(m_aInPut
,sTemp
);
568 void PoIfstream::close()
574 void PoIfstream::readEntry( PoEntry
& rPoEntry
)
576 assert( isOpen() && !eof() );
578 aGenPo
.readFromFile( m_aInPut
);
579 if( aGenPo
.isNull() )
582 rPoEntry
= PoEntry();
586 if( lcl_CheckInputEntry(aGenPo
) )
588 if( rPoEntry
.m_pGenPo
)
590 *(rPoEntry
.m_pGenPo
) = aGenPo
;
594 rPoEntry
.m_pGenPo
= new GenPoEntry( aGenPo
);
596 rPoEntry
.m_bIsInitialized
= true;
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */