1
/* ****************************************************************************
2
This file is part of KBabel
4
Copyright (C) 1999-2000 by Matthias Kiefer <matthias.kiefer@gmx.de>
5
2001-2002 by Stanislav Visnovsky <visnovsky@kde.org>
6
Copyright (C) 2005,2006 by Nicolas GOUTTE <goutte@kde.org>
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
In addition, as a special exception, the copyright holders give
23
permission to link the code of this program with any edition of
24
the Qt library by Trolltech AS, Norway (or with modified versions
25
of Qt that use the same license as Qt), and distribute linked
26
combinations including the two. You must obey the GNU General
27
Public License in all respects for all of the code used other than
28
Qt. If you modify this file, you may extend this exception to
29
your version of the file, but you are not obligated to do so. If
30
you do not wish to do so, delete this exception statement from
33
**************************************************************************** */
35
#include "gettextexport.h"
37
#include <resources.h>
39
#include "catalogitem.h"
40
#include "catalogsettings.h"
41
#include "kbprojectsettings.h"
44
#include <qtextcodec.h>
46
#include <kapplication.h>
48
#include <kgenericfactory.h>
50
K_EXPORT_COMPONENT_FACTORY( kbabel_gettextexport, KGenericFactory<GettextExportPlugin> ( "kbabelgettextexportfilter" ) )
52
using namespace KBabel;
54
GettextExportPlugin::GettextExportPlugin(QObject* parent, const char* name, const QStringList &) :
55
CatalogExportPlugin(parent,name), m_wrapWidth( -1 )
59
ConversionStatus GettextExportPlugin::save(const QString& localFile , const QString& mimetype, const Catalog* catalog)
61
// check, whether we know how to handle the extra data
62
if( catalog->importPluginID() != "GNU gettext")
63
return UNSUPPORTED_TYPE;
65
// we support on the application/x-gettext MIME type
66
if( mimetype != "application/x-gettext")
67
return UNSUPPORTED_TYPE;
69
QFile file(localFile);
71
if(file.open(IO_WriteOnly))
73
int progressRatio = QMAX(100/ QMAX(catalog->numberOfEntries(),1), 1);
74
emit signalResetProgressBar(i18n("saving file"),100);
76
QTextStream stream(&file);
78
SaveSettings _saveSettings = catalog->saveSettings();
80
if(_saveSettings.useOldEncoding && catalog->fileCodec())
82
stream.setCodec(catalog->fileCodec());
86
switch(_saveSettings.encoding)
88
case ProjectSettingsBase::UTF8:
89
stream.setCodec(QTextCodec::codecForName("utf-8"));
91
case ProjectSettingsBase::UTF16:
92
stream.setCodec(QTextCodec::codecForName("utf-16"));
95
stream.setCodec(QTextCodec::codecForLocale());
100
// only save header if it is not empty
101
const QString headerComment( catalog->header().comment() );
102
// ### TODO: why is this useful to have a header with an empty msgstr?
103
if( !headerComment.isEmpty() || !catalog->header().msgstr().isEmpty() )
106
writeComment( stream, headerComment );
108
const QString headerMsgid = catalog->header().msgid().first();
110
// Gettext PO files should have an empty msgid as header
111
if ( !headerMsgid.isEmpty() )
113
// ### TODO: perhaps it is grave enough for a user message
114
kdWarning() << "Non-empty msgid for the header, assuming empty msgid!" << endl << headerMsgid << "---" << endl;
117
// ### 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!)
118
stream << "msgid \"\"\n";
120
writeKeyword( stream, "msgstr", catalog->header().msgstr().first() );
126
for( uint counter = 0; counter < catalog->numberOfEntries() ; counter++ )
129
emit signalProgress(counter/progressRatio);
133
writeComment( stream, catalog->comment(counter) );
135
const QString msgctxt = catalog->msgctxt(counter);
136
if (! msgctxt.isEmpty() )
138
writeKeyword( stream, "msgctxt", msgctxt );
141
writeKeyword( stream, "msgid", catalog->msgid( counter ).first() );
142
if( catalog->pluralForm( counter ) == Gettext )
144
writeKeyword( stream, "msgid_plural", catalog->msgid( counter ).last() );
147
if( catalog->pluralForm(counter) != Gettext)
149
writeKeyword( stream, "msgstr", catalog->msgstr( counter ).first() );
153
kdDebug(KBABEL) << "Saving gettext plural form" << endl;
154
const int forms = catalog->msgstr( counter ).count();
155
for ( int i = 0; i < forms; ++i )
157
QString keyword ( "msgstr[" );
158
keyword += QString::number( i );
161
writeKeyword( stream, keyword, *( catalog->msgstr( counter ).at( i ) ) );
167
kapp->processEvents(10);
174
if( _saveSettings.saveObsolete )
176
QValueList<QString>::ConstIterator oit;
178
QStringList _obsolete = catalog->catalogExtraData();
180
for( oit = _obsolete.begin(); oit != _obsolete.end(); ++oit )
182
stream << (*oit) << "\n\n";
184
kapp->processEvents(10);
192
emit signalProgress(100);
195
emit signalClearProgressBar();
199
//emit signalError(i18n("Wasn't able to open file %1").arg(filename.ascii()));
206
void GettextExportPlugin::writeComment( QTextStream& stream, const QString& comment ) const
208
if( !comment.isEmpty() )
210
// We must check that each comment line really starts with a #, to avoid syntax errors
214
const int newpos = comment.find( '\n', pos, false );
221
const QString span ( ( newpos == -1 ) ? comment.mid( pos ) : comment.mid( pos, newpos-pos ) );
223
const int len = span.length();
224
QString spaces; // Stored leading spaces
225
for ( int i = 0 ; i < len ; ++i )
227
const QChar& ch = span[ i ];
230
stream << spaces << span.mid( i );
233
else if ( ch == ' ' || ch == '\t' )
235
// We have a leading white space character, so store it temporary
240
// Not leading white space and not a # character. so consider that the # character was missing at first position.
241
stream << "# " << spaces << span.mid( i );
255
void GettextExportPlugin::writeKeyword( QTextStream& stream, const QString& keyword, const QString& text ) const
257
if ( text.isEmpty() )
259
// Whatever the wrapping mode, an empty line is an empty line
260
stream << keyword << " \"\"\n";
263
else if ( m_wrapWidth == -1 )
265
// Traditional KBabel wrapping
266
QStringList list = QStringList::split( '\n', text );
268
if ( text.startsWith( "\n" ) )
269
list.prepend( QString() );
272
list.append( QString() );
274
if( list.count() > 1 )
275
list.prepend( QString() );
277
stream << keyword << " ";
279
QStringList::const_iterator it;
280
for( it = list.constBegin(); it != list.constEnd(); ++it )
282
stream << "\"" << (*it) << "\"\n";
286
else if ( ( !m_wrapWidth )
287
|| ( m_wrapWidth < 0 ) // Unknown special wrapping, so assume "no wrap" instead
290
// No wrapping (like Gettext's --no.wrap or -w0 )
292
// we need to remove the \n characters, as they are extra characters
293
QString realText( text );
294
realText.remove( '\n' );
295
stream << keyword << " \"" << realText << "\"\n";
300
// Normal wrapping like Gettext's -w parameter with a value bigger than 0
301
// From here on, we assume that we have an non-empty text and a positive non-null m_wrapWidth
303
// we need to remove the \n characters, as they are extra characters
304
QString realText( text );
305
realText.remove( '\n' );
307
bool needFirstEmptyLine = false;
308
if ( realText.find( "\\n" ) != -1 )
310
// We have more than one (logical) line, so write the extra empty line
311
needFirstEmptyLine = true;
315
// We must see if the text would fit in one line, including the keyword, a space and two quote characters.
316
const int rest = text.length() + keyword.length() + 3 - m_wrapWidth;
319
needFirstEmptyLine = true;
322
int availableWidth = m_wrapWidth;
323
if ( needFirstEmptyLine )
325
stream << keyword << " \"\"\n";
329
stream << keyword << " ";
330
availableWidth -= keyword.length();
331
availableWidth--; // The space after the keyword
334
const int spanLength = realText.length();
335
for ( int pos = 0; pos < spanLength; )
337
availableWidth -= 2; // Count the quote characters
338
if ( availableWidth < 2 )
340
// Be sure that at least two useful characters are written, even if the wrap width is too small
343
const int newlinePos = realText.find ( "\\n", pos );
344
if ( ( newlinePos >= 0 ) && ( newlinePos - pos + 2 < availableWidth ) )
346
// The newline is near than the maximum available numbers of characters
347
availableWidth = newlinePos - pos + 2;
349
stream << '\"' << realText.mid( pos, availableWidth ) << "\"\n";
350
pos += availableWidth;