~ubuntu-branches/ubuntu/intrepid/kdesdk/intrepid-updates

« back to all changes in this revision

Viewing changes to kbabel/filters/gettext/gettextexport.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2008-05-28 10:11:43 UTC
  • mto: This revision was merged to the branch mainline in revision 37.
  • Revision ID: james.westby@ubuntu.com-20080528101143-gzc3styjz1b70zxu
Tags: upstream-4.0.80
ImportĀ upstreamĀ versionĀ 4.0.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* ****************************************************************************
2
 
  This file is part of KBabel
3
 
 
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>
7
 
 
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.
12
 
 
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.
17
 
 
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.
21
 
 
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
31
 
  your version.
32
 
 
33
 
**************************************************************************** */
34
 
 
35
 
#include "gettextexport.h"
36
 
 
37
 
#include <resources.h>
38
 
#include "catalog.h"
39
 
#include "catalogitem.h"
40
 
#include "catalogsettings.h"
41
 
#include "kbprojectsettings.h"
42
 
 
43
 
#include <qfile.h>
44
 
#include <qtextcodec.h>
45
 
 
46
 
#include <kapplication.h>
47
 
#include <kdebug.h>
48
 
#include <kgenericfactory.h>
49
 
 
50
 
K_EXPORT_COMPONENT_FACTORY( kbabel_gettextexport, KGenericFactory<GettextExportPlugin> ( "kbabelgettextexportfilter" ) )
51
 
 
52
 
using namespace KBabel;
53
 
 
54
 
GettextExportPlugin::GettextExportPlugin(QObject* parent, const char* name, const QStringList &) :
55
 
    CatalogExportPlugin(parent,name), m_wrapWidth( -1 )
56
 
{
57
 
}
58
 
 
59
 
ConversionStatus GettextExportPlugin::save(const QString& localFile , const QString& mimetype, const Catalog* catalog)
60
 
{
61
 
   // check, whether we know how to handle the extra data
62
 
   if( catalog->importPluginID() != "GNU gettext") 
63
 
      return UNSUPPORTED_TYPE;
64
 
 
65
 
   // we support on the application/x-gettext MIME type
66
 
   if( mimetype != "application/x-gettext") 
67
 
      return UNSUPPORTED_TYPE;
68
 
 
69
 
   QFile file(localFile);
70
 
 
71
 
   if(file.open(IO_WriteOnly))
72
 
   {
73
 
      int progressRatio = QMAX(100/ QMAX(catalog->numberOfEntries(),1), 1);
74
 
      emit signalResetProgressBar(i18n("saving file"),100);
75
 
 
76
 
      QTextStream stream(&file);
77
 
      
78
 
      SaveSettings _saveSettings = catalog->saveSettings();
79
 
      
80
 
      if(_saveSettings.useOldEncoding && catalog->fileCodec())
81
 
      {
82
 
            stream.setCodec(catalog->fileCodec());
83
 
      }
84
 
      else
85
 
      {
86
 
         switch(_saveSettings.encoding)
87
 
         {
88
 
            case ProjectSettingsBase::UTF8:
89
 
               stream.setCodec(QTextCodec::codecForName("utf-8"));
90
 
               break;
91
 
            case ProjectSettingsBase::UTF16:
92
 
               stream.setCodec(QTextCodec::codecForName("utf-16"));
93
 
               break;
94
 
            default:
95
 
               stream.setCodec(QTextCodec::codecForLocale());
96
 
               break;
97
 
         }
98
 
      }
99
 
 
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() )
104
 
      {
105
 
            // write header
106
 
            writeComment( stream, headerComment );
107
 
    
108
 
            const QString headerMsgid = catalog->header().msgid().first();
109
 
    
110
 
            // Gettext PO files should have an empty msgid as header
111
 
            if ( !headerMsgid.isEmpty() )
112
 
            {
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;
115
 
            }
116
 
          
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";
119
 
            
120
 
            writeKeyword( stream, "msgstr", catalog->header().msgstr().first() );
121
 
 
122
 
            stream << "\n";
123
 
      }
124
 
 
125
 
      QStringList list;
126
 
      for( uint counter = 0; counter < catalog->numberOfEntries() ; counter++ )
127
 
      {
128
 
          if(counter%10==0) {
129
 
             emit signalProgress(counter/progressRatio);
130
 
          }
131
 
          
132
 
            // write entry
133
 
            writeComment( stream, catalog->comment(counter) );
134
 
    
135
 
            const QString msgctxt = catalog->msgctxt(counter);
136
 
            if (! msgctxt.isEmpty() )
137
 
            {
138
 
                writeKeyword( stream, "msgctxt", msgctxt );
139
 
            }
140
 
 
141
 
            writeKeyword( stream, "msgid", catalog->msgid( counter ).first() );
142
 
            if( catalog->pluralForm( counter ) == Gettext )
143
 
            {
144
 
                writeKeyword( stream, "msgid_plural", catalog->msgid( counter ).last() );
145
 
            }
146
 
          
147
 
            if( catalog->pluralForm(counter) != Gettext)
148
 
            {
149
 
                writeKeyword( stream, "msgstr", catalog->msgstr( counter ).first() );
150
 
            }
151
 
            else
152
 
            {
153
 
                kdDebug(KBABEL) << "Saving gettext plural form" << endl;
154
 
                const int forms = catalog->msgstr( counter ).count();
155
 
                for ( int i = 0; i < forms; ++i )
156
 
                {
157
 
                    QString keyword ( "msgstr[" );
158
 
                    keyword += QString::number( i );
159
 
                    keyword += ']';
160
 
 
161
 
                    writeKeyword( stream, keyword, *( catalog->msgstr( counter ).at( i ) ) );
162
 
                }
163
 
            }
164
 
            
165
 
            stream << "\n";
166
 
 
167
 
         kapp->processEvents(10);
168
 
         if( isStopped() )
169
 
         {
170
 
            return STOPPED;
171
 
         }
172
 
      }
173
 
 
174
 
      if( _saveSettings.saveObsolete )
175
 
      {
176
 
          QValueList<QString>::ConstIterator oit;
177
 
          
178
 
          QStringList _obsolete = catalog->catalogExtraData();
179
 
 
180
 
          for( oit = _obsolete.begin(); oit != _obsolete.end(); ++oit )
181
 
          {
182
 
              stream << (*oit) << "\n\n";
183
 
 
184
 
              kapp->processEvents(10);
185
 
              if( isStopped() )
186
 
              {
187
 
                  return STOPPED;
188
 
              }
189
 
          }
190
 
      }
191
 
      
192
 
      emit signalProgress(100);
193
 
      file.close();
194
 
 
195
 
      emit signalClearProgressBar();
196
 
   }
197
 
   else
198
 
   {
199
 
      //emit signalError(i18n("Wasn't able to open file %1").arg(filename.ascii()));
200
 
      return OS_ERROR;
201
 
   }
202
 
 
203
 
   return OK;
204
 
}
205
 
 
206
 
void GettextExportPlugin::writeComment( QTextStream& stream, const QString& comment ) const
207
 
{
208
 
    if( !comment.isEmpty() )
209
 
    {
210
 
        // We must check that each comment line really starts with a #, to avoid syntax errors
211
 
        int pos = 0;
212
 
        for(;;)
213
 
        {
214
 
            const int newpos = comment.find( '\n', pos, false );
215
 
            if ( newpos == pos )
216
 
            {
217
 
                ++pos;
218
 
                stream << "\n";
219
 
                continue;
220
 
            }
221
 
            const QString span ( ( newpos == -1 ) ? comment.mid( pos ) : comment.mid( pos, newpos-pos ) );
222
 
 
223
 
            const int len = span.length();
224
 
            QString spaces; // Stored leading spaces
225
 
            for ( int i = 0 ; i < len ; ++i )
226
 
            {
227
 
                const QChar& ch = span[ i ];
228
 
                if ( ch == '#' )
229
 
                {
230
 
                    stream << spaces << span.mid( i );
231
 
                    break;
232
 
                }
233
 
                else if ( ch == ' ' || ch == '\t' )
234
 
                {
235
 
                    // We have a leading white space character, so store it temporary
236
 
                    spaces += ch;
237
 
                }
238
 
                else
239
 
                {
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 );
242
 
                    break;
243
 
                }
244
 
            }
245
 
            stream << "\n";
246
 
 
247
 
            if ( newpos == -1 )
248
 
                break;
249
 
            else
250
 
                pos = newpos + 1;
251
 
        }
252
 
    }
253
 
}
254
 
 
255
 
void GettextExportPlugin::writeKeyword( QTextStream& stream, const QString& keyword, const QString& text ) const
256
 
{
257
 
    if ( text.isEmpty() )
258
 
    {
259
 
        // Whatever the wrapping mode, an empty line is an empty line
260
 
        stream << keyword << " \"\"\n";
261
 
        return; 
262
 
    }
263
 
    else if ( m_wrapWidth == -1 )
264
 
    {
265
 
        // Traditional KBabel wrapping
266
 
        QStringList list = QStringList::split( '\n', text );
267
 
 
268
 
        if ( text.startsWith( "\n" ) )
269
 
            list.prepend( QString() );
270
 
 
271
 
        if(list.isEmpty())
272
 
            list.append( QString() );
273
 
    
274
 
        if( list.count() > 1 )
275
 
            list.prepend( QString() );
276
 
 
277
 
        stream << keyword << " ";
278
 
    
279
 
        QStringList::const_iterator it;
280
 
        for( it = list.constBegin(); it != list.constEnd(); ++it )
281
 
        {
282
 
            stream << "\"" << (*it) << "\"\n";
283
 
        }
284
 
        return;
285
 
    }
286
 
    else if ( ( !m_wrapWidth )
287
 
        || ( m_wrapWidth < 0 ) // Unknown special wrapping, so assume "no wrap" instead
288
 
        )
289
 
    {
290
 
        // No wrapping (like Gettext's --no.wrap or -w0 )
291
 
        
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";
296
 
        return;
297
 
    }
298
 
 
299
 
    // ### TODO: test!
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
302
 
    
303
 
    // we need to remove the \n characters, as they are extra characters
304
 
    QString realText( text );
305
 
    realText.remove( '\n' );
306
 
 
307
 
    bool needFirstEmptyLine = false;
308
 
    if ( realText.find( "\\n" ) != -1 )
309
 
    {
310
 
        // We have more than one (logical) line, so write the extra empty line
311
 
        needFirstEmptyLine = true;
312
 
    }
313
 
    else
314
 
    {
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;
317
 
        if ( rest > 0 )
318
 
        {
319
 
            needFirstEmptyLine = true;
320
 
        }
321
 
    }
322
 
    int availableWidth = m_wrapWidth;
323
 
    if ( needFirstEmptyLine )
324
 
    {
325
 
        stream << keyword << " \"\"\n";
326
 
    }
327
 
    else
328
 
    {
329
 
        stream << keyword << " ";
330
 
        availableWidth -= keyword.length();
331
 
        availableWidth--; // The space after the keyword
332
 
    }
333
 
    
334
 
    const int spanLength = realText.length();
335
 
    for ( int pos = 0; pos < spanLength; )
336
 
    {
337
 
        availableWidth -= 2; // Count the quote characters
338
 
        if ( availableWidth < 2 )
339
 
        {
340
 
            // Be sure that at least two useful characters are written, even if the wrap width is too small
341
 
            availableWidth = 2;
342
 
        }
343
 
        const int newlinePos = realText.find ( "\\n", pos );
344
 
        if ( ( newlinePos >= 0 ) && ( newlinePos - pos + 2 < availableWidth ) )
345
 
        {
346
 
            // The newline is near than the maximum available numbers of characters
347
 
            availableWidth = newlinePos - pos + 2;
348
 
        }
349
 
        stream << '\"' << realText.mid( pos, availableWidth ) << "\"\n";
350
 
        pos += availableWidth;
351
 
    }
352
 
}