• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdesdk API Reference
  • KDE Home
  • Contact Us
 

lokalize

  • sources
  • kde-4.14
  • kdesdk
  • lokalize
  • src
  • catalog
  • gettext
gettextexport.cpp
Go to the documentation of this file.
1 /* ****************************************************************************
2  This file is part of Lokalize
3  This file contains parts of KBabel code
4 
5  Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de>
6  2001-2002 by Stanislav Visnovsky <visnovsky@kde.org>
7  Copyright (C) 2005,2006 by Nicolas GOUTTE <goutte@kde.org>
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 
23  In addition, as a special exception, the copyright holders give
24  permission to link the code of this program with any edition of
25  the Qt library by Trolltech AS, Norway (or with modified versions
26  of Qt that use the same license as Qt), and distribute linked
27  combinations including the two. You must obey the GNU General
28  Public License in all respects for all of the code used other than
29  Qt. If you modify this file, you may extend this exception to
30  your version of the file, but you are not obligated to do so. If
31  you do not wish to do so, delete this exception statement from
32  your version.
33 
34 **************************************************************************** */
35 
36 #include "gettextexport.h"
37 
38 //#include <resources.h>
39 #include "gettextstorage.h"
40 #include "catalogitem.h"
41 
42 #include <QFile>
43 #include <QTextCodec>
44 #include <QList>
45 #include <QTextStream>
46 #include <QEventLoop>
47 #include <QStringBuilder>
48 
49 #include <ksavefile.h>
50 #include <kapplication.h>
51 #include <klocale.h>
52 #include <kdebug.h>
53 
54 
55 using namespace GettextCatalog;
56 
57 GettextExportPlugin::GettextExportPlugin(short wrapWidth, short trailingNewLines)
58  : m_wrapWidth(wrapWidth)
59  , m_trailingNewLines(trailingNewLines)
60 {
61 }
62 
63 ConversionStatus GettextExportPlugin::save(QIODevice* device,
64  const GettextStorage* catalog,
65  QTextCodec* codec)
66 {
67  QTextStream stream(device);
68  stream.setCodec(codec);
69 
70  //if ( m_wrapWidth == -1 ) m_wrapWidth=80;
71 
72  // only save header if it is not empty
73  const QString& headerComment( catalog->m_header.comment() );
74  // ### why is this useful to have a header with an empty msgstr?
75  if ( !headerComment.isEmpty() || !catalog->m_header.msgstrPlural().isEmpty() )
76  {
77  // write header
78  writeComment( stream, headerComment );
79 
80  const QString& headerMsgid (catalog->m_header.msgid());
81 
82  // Gettext PO files should have an empty msgid as header
83  if ( !headerMsgid.isEmpty() )
84  {
85  // ### perhaps it is grave enough for a user message
86  kWarning() << "Non-empty msgid for the header, assuming empty msgid!" << endl << headerMsgid << "---";
87  }
88 
89  // ### FIXME: if it is the header, then the msgid should be empty! (Even if KBabel has made something out of a non-header first entry!)
90  stream << "msgid \"\"\n";
91 
92  writeKeyword( stream, "msgstr", catalog->m_header.msgstr(), false );
93  }
94 
95 
96  const QVector<CatalogItem>& catalogEntries=catalog->m_entries;
97  int limit=catalog->numberOfEntries();
98  QStringList list;
99  for (int counter = 0; counter < limit; counter++)
100  {
101  stream << '\n';
102 
103  const CatalogItem& catalogItem = catalogEntries.at(counter);
104  // write entry
105  writeComment( stream, catalogItem.comment() );
106 
107  const QString& msgctxt = catalogItem.msgctxt();
108  if (! msgctxt.isEmpty() || catalogItem.keepEmptyMsgCtxt())
109  writeKeyword( stream, "msgctxt", msgctxt );
110 
111  writeKeyword( stream, "msgid", catalogItem.msgid(), true, catalogItem.prependEmptyForMsgid() );
112  if ( catalogItem.isPlural() )
113  writeKeyword( stream, "msgid_plural", catalogItem.msgid(1), true, catalogItem.prependEmptyForMsgid() );
114 
115  if (!catalogItem.isPlural())
116  writeKeyword( stream, "msgstr", catalogItem.msgstr(), true, catalogItem.prependEmptyForMsgstr() );
117  else
118  {
119  kDebug() << "Saving gettext plural form";
120  //TODO check len of the actual stringlist??
121  const int forms = catalog->numberOfPluralForms();
122  for ( int i = 0; i < forms; ++i )
123  {
124  QString keyword = "msgstr[" % QString::number( i ) % ']';
125  writeKeyword( stream, keyword, catalogItem.msgstr(i), true, catalogItem.prependEmptyForMsgstr() );
126  }
127  }
128  }
129 
130 #if 0
131 //legacy
132  if ( _saveSettings.saveObsolete )
133 #endif
134  {
135  QList<QString>::const_iterator oit;
136  const QStringList& _obsolete=catalog->m_catalogExtraData;
137  oit=_obsolete.constBegin();
138  if (oit!=_obsolete.constEnd())
139  {
140  stream << "\n" << (*oit);
141  while((++oit)!=_obsolete.constEnd())
142  stream << "\n\n" << (*oit);
143  }
144  }
145 
146  int i=m_trailingNewLines+1;
147  while (--i>=0)
148  stream << '\n';
149 
150  return OK;
151 }
152 
153 void GettextExportPlugin::writeComment( QTextStream& stream, const QString& comment ) const
154 {
155  if( !comment.isEmpty() )
156  {
157  // We must check that each comment line really starts with a #, to avoid syntax errors
158  int pos = 0;
159  for(;;)
160  {
161  const int newpos = comment.indexOf( '\n', pos, Qt::CaseInsensitive );
162  if ( newpos == pos )
163  {
164  ++pos;
165  stream << '\n';
166  continue;
167  }
168  const QString& span ((newpos==-1 ) ? comment.mid(pos) : comment.mid(pos, newpos-pos) );
169 
170  const int len = span.length();
171  QString spaces; // Stored leading spaces
172  for ( int i = 0 ; i < len ; ++i )
173  {
174  const QChar& ch = span[ i ];
175  if ( ch == '#' )
176  {
177  stream << spaces << span.mid( i );
178  break;
179  }
180  else if ( ch == ' ' || ch == '\t' )
181  {
182  // We have a leading white space character, so store it temporary
183  spaces += ch;
184  }
185  else
186  {
187  // Not leading white space and not a # character. so consider that the # character was missing at first position.
188  stream << "# " << spaces << span.mid( i );
189  break;
190  }
191  }
192  stream << '\n';
193 
194  if ( newpos == -1 )
195  break;
196  else
197  pos = newpos + 1;
198  }
199  }
200 }
201 
202 void GettextExportPlugin::writeKeyword( QTextStream& stream, const QString& keyword, QString text, bool containsHtml, bool startedWithEmptyLine ) const
203 {
204  if ( text.isEmpty() )
205  {
206  // Whatever the wrapping mode, an empty line is an empty line
207  stream << keyword << " \"\"\n";
208  return;
209  }
210 
211  //TODO remove this for KDE 4.4
212  //NOTE not?
213  int pos=0;
214  while ((pos=text.indexOf("\\\"",pos))!=-1)
215  {
216  if (pos==0 || text.at(pos-1)!='\\')
217  text.replace(pos,2,'"');
218  else
219  pos++;
220  }
221  text.replace('"',"\\\"");
222 #if 0
223  if ( m_wrapWidth == -1 )
224  {
225  // Traditional KBabel wrapping
226  QStringList list = text.split( '\n', QString::SkipEmptyParts );
227 
228  if ( text.startsWith( '\n' ) )
229  list.prepend( QString() );
230 
231  if(list.isEmpty())
232  list.append( QString() );
233 
234  if( list.count() > 1 )
235  list.prepend( QString() );
236 
237  stream << keyword << ' ';
238 
239  QStringList::const_iterator it;
240  for( it = list.constBegin(); it != list.constEnd(); ++it )
241  stream << '\"' << (*it) << "\"\n";
242  return;
243  }
244 #endif
245 
246  if ( m_wrapWidth == 0 ) // Unknown special wrapping, so assume "no wrap" instead
247  {
248  // No wrapping (like Gettext's --no.wrap or -w0 )
249  // we need to remove the \n characters, as they are extra characters
250  QString realText( text );
251  realText.remove( '\n' );
252  stream << keyword << " \"" << realText << "\"\n";
253  return;
254  }
255  else if ( m_wrapWidth < 0 )
256  {
257  // No change in wrapping
258  QStringList list = text.split( '\n');
259  if (list.count()>1 || startedWithEmptyLine /* || keyword.length()+3+text.length()>=80*/)
260  list.prepend(QString());
261 
262  stream << keyword << " ";
263  QStringList::const_iterator it;
264  for( it = list.constBegin(); it != list.constEnd(); ++it )
265  stream << "\"" << (*it) << "\"\n";
266 
267  return;
268  }
269 
270  // lazy wrapping
271  QStringList list = text.split( '\n', QString::SkipEmptyParts );
272 
273  if ( text.startsWith( '\n' ) )
274  list.prepend( QString() );
275 
276  if(list.isEmpty())
277  list.append( QString() );
278 
279  //static QRegExp breakStopReForHtml("[ >.%/:,]", Qt::CaseSensitive, QRegExp::Wildcard);
280  //static QRegExp breakStopReForText("[ .%/:,]", Qt::CaseSensitive, QRegExp::Wildcard);
281  static QRegExp breakStopReForHtml("[ >%]", Qt::CaseSensitive, QRegExp::Wildcard);
282  static QRegExp breakStopReForText("[ &%]", Qt::CaseSensitive, QRegExp::Wildcard);
283  QRegExp breakStopRe=containsHtml?breakStopReForHtml:breakStopReForText;
284 
285  int max=m_wrapWidth-2;
286  bool prependedEmptyLine=false;
287  QStringList::iterator itm;
288  for( itm = list.begin(); itm != list.end(); ++itm )
289  {
290  if (list.count()==1 && keyword.length()+1+itm->length()>=max)
291  {
292  prependedEmptyLine=true;
293  itm=list.insert(itm,QString());
294  }
295 
296  if (itm->length()>max)
297  {
298  int pos = itm->lastIndexOf(breakStopRe,max-1);
299  if (pos>(max/2))
300  {
301  int pos2 = itm->indexOf('<',pos);
302  if (pos2>0&&pos2<max-1)
303  pos=itm->indexOf('<',pos);
304  ++pos;
305  }
306  else
307  {
308  if (itm->at(max-1)=='\\')
309  {
310  do {--max;}
311  while (max>=2 && itm->at(max-1)=='\\');
312  }
313  pos=max;
314  }
315  //itm=list.insert(itm,itm->left(pos));
316  QString t=*itm;
317  itm=list.insert(itm,t);
318  ++itm;
319  if (itm != list.end())
320  {
321  (*itm)=itm->remove(0,pos);
322  --itm;
323  if (itm != list.end())
324  itm->truncate(pos);
325  }
326  }
327  }
328 
329  if( !prependedEmptyLine && list.count() > 1 )
330  list.prepend( QString() );
331 
332  stream << keyword << " ";
333 
334  QStringList::const_iterator it;
335  for( it = list.constBegin(); it != list.constEnd(); ++it )
336  stream << "\"" << (*it) << "\"\n";
337 }
QIODevice
QTextStream::setCodec
void setCodec(QTextCodec *codec)
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
GettextCatalog::GettextExportPlugin::m_wrapWidth
short m_wrapWidth
Width of the wrap.
Definition: gettextexport.h:87
GettextCatalog::CatalogItem::msgid
const QString & msgid(const int form=0) const
Definition: catalogitem.cpp:73
QChar
QString::split
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
GettextCatalog::CatalogItem
This class represents an entry in a catalog.
Definition: catalogitem.h:55
catalogitem.h
GettextCatalog::GettextExportPlugin::m_trailingNewLines
short m_trailingNewLines
Definition: gettextexport.h:88
GettextCatalog::CatalogItem::prependEmptyForMsgid
bool prependEmptyForMsgid(const int form=0) const
Definition: catalogitem.cpp:86
GettextCatalog::GettextExportPlugin::GettextExportPlugin
GettextExportPlugin(short wrapWidth=-1, short trailingNewLines=1)
Definition: gettextexport.cpp:57
QList::const_iterator
QTextStream
GettextCatalog::CatalogItem::keepEmptyMsgCtxt
bool keepEmptyMsgCtxt() const
Definition: catalogitem.cpp:129
QRegExp
QString::number
QString number(int n, int base)
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
GettextCatalog::CatalogItem::prependEmptyForMsgstr
bool prependEmptyForMsgstr(const int form=0) const
Definition: catalogitem.cpp:91
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
GettextCatalog::ConversionStatus
ConversionStatus
Result of the conversion.
Definition: catalogfileplugin.h:53
QString
QTextCodec
QList::iterator
QStringList
QList::end
iterator end()
GettextCatalog::CatalogItem::comment
QString comment() const
Definition: catalogitem.cpp:62
GettextCatalog::CatalogItem::msgstrPlural
const QVector< QString > & msgstrPlural() const
Definition: catalogitem.cpp:96
QString::replace
QString & replace(int position, int n, QChar after)
QVector::at
const T & at(int i) const
QString::mid
QString mid(int position, int n) const
QVector
GettextCatalog::CatalogItem::msgstr
const QString & msgstr(const int form=0) const
Definition: catalogitem.cpp:78
QList::insert
void insert(int i, const T &value)
QVector::isEmpty
bool isEmpty() const
QString::at
const QChar at(int position) const
CatalogStorage::numberOfEntries
int numberOfEntries() const
Definition: catalogstorage.h:56
gettextexport.h
QString::length
int length() const
GettextCatalog::CatalogItem::msgctxt
const QString & msgctxt(const bool noNewlines=false) const
Definition: catalogitem.cpp:67
gettextstorage.h
GettextCatalog::GettextExportPlugin::save
ConversionStatus save(QIODevice *device, const GettextStorage *catalog, QTextCodec *codec)
Definition: gettextexport.cpp:63
QList::prepend
void prepend(const T &value)
GettextCatalog::CatalogItem::isPlural
bool isPlural() const
Definition: catalogitem.cpp:202
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
GettextCatalog::GettextStorage
Implementation of storage for Gettext PO.
Definition: gettextstorage.h:39
CatalogStorage::numberOfPluralForms
int numberOfPluralForms() const
Definition: catalogstorage.h:57
QList::begin
iterator begin()
GettextCatalog::OK
Definition: catalogfileplugin.h:54
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:40:07 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

lokalize

Skip menu "lokalize"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdesdk API Reference

Skip menu "kdesdk API Reference"
  • kapptemplate
  • kcachegrind
  • kompare
  • lokalize
  • umbrello
  •   umbrello

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal