1 /* AbiWord
2 * Copyright (C) 1998 AbiSource, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "ap_Features.h"
25
26 #include "ut_assert.h"
27 #include "ut_debugmsg.h"
28 #include "ut_string.h"
29 #include "ut_growbuf.h"
30 #include "ut_bytebuf.h"
31 #include "ut_wctomb.h"
32
33 #include "xap_App.h"
34 #include "xap_EncodingManager.h"
35
36 #include "ap_Strings.h"
37
38 //////////////////////////////////////////////////////////////////
39 // a sub-class to wrap the compiled-in (english) strings
40 // (there will only be one instance of this sub-class)
41 //////////////////////////////////////////////////////////////////
42
AP_BuiltinStringSet(XAP_App * pApp,const gchar * szLanguageName)43 AP_BuiltinStringSet::AP_BuiltinStringSet(XAP_App * pApp, const gchar * szLanguageName)
44 : XAP_BuiltinStringSet(pApp,szLanguageName)
45 {
46 #define dcl(id,s) s,
47
48 static const gchar * s_a[] =
49 {
50 dcl(__FIRST__,0) // bogus entry for zero
51 #include "ap_String_Id.h"
52 dcl(__LAST__,0) // bogus entry for end
53 };
54
55 m_arrayAP = s_a;
56
57 #undef dcl
58 }
59
~AP_BuiltinStringSet(void)60 AP_BuiltinStringSet::~AP_BuiltinStringSet(void)
61 {
62 }
63
getValue(XAP_String_Id id) const64 const gchar * AP_BuiltinStringSet::getValue(XAP_String_Id id) const
65 {
66 // if it's in our range, we fetch it.
67 // otherwise, we hand it down to the base class.
68
69 if ( (id > AP_STRING_ID__FIRST__) && (id < AP_STRING_ID__LAST__) )
70 return m_arrayAP[id-AP_STRING_ID__FIRST__];
71
72 return XAP_BuiltinStringSet::getValue(id);
73 }
74
75 #ifdef DEBUG
s_dumpXMLpair(FILE * fp,const gchar * szID,const gchar * sz)76 static void s_dumpXMLpair(FILE * fp, const gchar *szID, const gchar *sz)
77 {
78 fprintf(fp,"%s=\"",szID);
79
80 for (; *sz; ++sz)
81 {
82 switch (*sz)
83 {
84 case '&':
85 fputs("&", fp);
86 break;
87 case '<':
88 fputs("<", fp);
89 break;
90 case '>':
91 fputs(">", fp);
92 break;
93 case '"':
94 fputs(""", fp);
95 break;
96 case 9:
97 case 10:
98 case 13:
99 fprintf(fp, "&#%d;", *sz);
100 break;
101 default:
102 putc(*sz, fp);
103 break;
104 }
105 }
106
107 fprintf(fp,"\"\n");
108 }
109
dumpBuiltinSet(const char * szFilename) const110 bool AP_BuiltinStringSet::dumpBuiltinSet(const char * szFilename) const
111 {
112 // Dump a full set of english strings. The resulting file
113 // can then be translated and later loaded as a DiskStringSet
114 // for the other language.
115
116 bool bResult = false; // assume failure
117 FILE * fp = NULL;
118 fp = fopen(szFilename, "w");
119 if (!fp)
120 {
121 UT_DEBUGMSG(("Could not open String file [%s].\n",szFilename));
122 return false;
123 }
124
125 UT_DEBUGMSG(("Dumping English strings into String file [%s].\n",szFilename));
126
127 // most translators need to explicitly set an encoding, so provide a sample
128
129 fprintf(fp,"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
130 fprintf(fp,"\n");
131
132 // write a comment block as a prolog.
133 // NOTE: this is human readable information only.
134
135 fprintf(fp,"<!-- ============================================================== -->\n");
136 fprintf(fp,"<!-- This file contains AbiWord Strings. AbiWord is an Open Source -->\n");
137 fprintf(fp,"<!-- word processor developed by AbiSource, Inc. Information about -->\n");
138 fprintf(fp,"<!-- this application can be found at http://www.abisource.com -->\n");
139 fprintf(fp,"<!-- This file contains the string translations for one language. -->\n");
140 fprintf(fp,"<!-- This file is covered by the GNU Public License (GPL). -->\n");
141 fprintf(fp,"<!-- ============================================================== -->\n");
142 fprintf(fp,"\n");
143 fprintf(fp,"<!-- _Language_ translations provided by _name_ <_email_> -->\n");
144 fprintf(fp,"\n");
145
146 // end of prolog.
147 // now we begin the actual document.
148
149 //////////////////////////////////////////////////////////////////
150 // declare a static table of all the ids and strings
151 //////////////////////////////////////////////////////////////////
152
153 #define dcl(id,s) { #id,s },
154
155 static struct { const gchar * szId; const gchar * szString; } s_mapXAP[] =
156 {
157 #include "xap_String_Id.h"
158 };
159
160 static struct { const gchar * szId; const gchar * szString; } s_mapAP[] =
161 {
162 #include "ap_String_Id.h"
163 };
164
165 #undef dcl
166
167 fprintf(fp,"\n<AbiStrings app=\"%s\" ver=\"%s\" language=\"%s\">\n",
168 m_pApp->getApplicationName(), "1.0", getLanguageName());
169 {
170 UT_uint32 k;
171
172 fprintf(fp,"\n<Strings\tclass=\"XAP\"\n");
173 for (k=0; k<G_N_ELEMENTS(s_mapXAP); k++)
174 s_dumpXMLpair(fp,s_mapXAP[k].szId,s_mapXAP[k].szString);
175 fprintf(fp,"/>\n");
176
177 fprintf(fp,"\n<Strings\tclass=\"AP\"\n");
178 for (k=0; k<G_N_ELEMENTS(s_mapAP); k++)
179 s_dumpXMLpair(fp,s_mapAP[k].szId,s_mapAP[k].szString);
180 fprintf(fp,"/>\n");
181 }
182
183 fprintf(fp,"\n</AbiStrings>\n");
184
185 if (fp)
186 fclose(fp);
187 return bResult;
188 }
189
190 #endif // DEBUG
191
192 //////////////////////////////////////////////////////////////////
193 // a sub-class to deal with disk-based string sets (translations)
194 // (a unique one of these will be instantiated for each language
195 // that we load -- or rather one for each time the user switches
196 // languages and we load another one from disk)
197 //////////////////////////////////////////////////////////////////
198
AP_DiskStringSet(XAP_App * pApp)199 AP_DiskStringSet::AP_DiskStringSet(XAP_App * pApp)
200 : XAP_DiskStringSet(pApp),
201 m_vecStringsAP(AP_STRING_ID__LAST__ - AP_STRING_ID__FIRST__ + 1, 4, true)
202 {
203 setValue(AP_STRING_ID__FIRST__,0); // bogus zero element
204 }
205
~AP_DiskStringSet(void)206 AP_DiskStringSet::~AP_DiskStringSet(void)
207 {
208 UT_sint32 kLimit = m_vecStringsAP.getItemCount();
209 UT_sint32 k;
210
211 for (k=kLimit-1; k>=0; k--)
212 {
213 gchar * sz = (gchar *)m_vecStringsAP.getNthItem(k);
214 if (sz)
215 g_free(sz);
216 }
217 }
218
setValue(XAP_String_Id id,const gchar * szString)219 bool AP_DiskStringSet::setValue(XAP_String_Id id, const gchar * szString)
220 {
221 if (id < AP_STRING_ID__FIRST__)
222 return XAP_DiskStringSet::setValue(id,szString);
223
224 bool bFoundMultiByte = false;
225 gchar * szDup = NULL;
226 if (szString && *szString)
227 {
228 UT_GrowBuf gb;
229 UT_decodeUTF8string(szString,strlen(szString),&gb);
230
231 // TODO The strings that we use (for dialogs and etc) are currently
232 // TODO limited to single-byte encodings by the code below.
233
234 int kLimit=gb.getLength();
235 UT_UCS4Char * p= (UT_UCS4Char*) gb.getPointer(0);
236 UT_ByteBuf str;
237
238 // now we run this string through fribidi
239 if(XAP_App::getApp()->theOSHasBidiSupport() == XAP_App::BIDI_SUPPORT_NONE)
240 {
241 if (p && *p)
242 {
243 UT_UCS4Char *fbdStr2 = 0;
244 fbdStr2 = new UT_UCS4Char [kLimit + 1];
245 UT_return_val_if_fail (fbdStr2, false);
246
247 UT_sint32 i;
248
249 UT_BidiCharType iDomDir = UT_bidiGetCharType(p[0]);
250 UT_bidiReorderString(p,kLimit, iDomDir, fbdStr2);
251
252 for(i = 0; i < kLimit; i++)
253 {
254 p[i] = fbdStr2[i];
255 }
256
257 UT_ASSERT_HARMLESS(p[i] == 0);
258 delete[] fbdStr2;
259 }
260 }
261
262 /* We setup the real encoding for the strings we are storing*/
263 setEncoding(XAP_App::getApp()->getDefaultEncoding());
264
265 UT_Wctomb wctomb_conv(XAP_App::getApp()->getDefaultEncoding());
266 char letter_buf[20];
267 int length;
268 for (int k=0; k<kLimit; k++)
269 {
270 if (wctomb_conv.wctomb(letter_buf,length,p[k])) {
271 str.append((UT_Byte*)letter_buf,length);
272 };
273 }
274 length = str.getLength();
275 szDup = (gchar *)g_try_malloc(length+1);
276 if (!szDup)
277 return false;
278 memcpy(szDup,str.getPointer(0),length);
279 szDup[length]='\0';
280 }
281
282 gchar * pOldValue = NULL;
283 bool bResult = (m_vecStringsAP.setNthItem(id-AP_STRING_ID__FIRST__,szDup,&pOldValue) == 0);
284 UT_ASSERT_HARMLESS(pOldValue == NULL); // duplicate string for this id
285
286 if (bFoundMultiByte)
287 {
288 UT_DEBUGMSG(("WARNING: DiskStringSet: Found Multi-Byte char in String [%s][id %d] (we mapped it to [%s])\n",szString,id,szDup));
289 }
290
291 return bResult;
292 }
293
getValue(XAP_String_Id id) const294 const gchar * AP_DiskStringSet::getValue(XAP_String_Id id) const
295 {
296 // dispatch to XAP code if not in our range
297
298 if (id < AP_STRING_ID__FIRST__)
299 return XAP_DiskStringSet::getValue(id);
300
301 // if it is in our range, look it up in our table
302
303 UT_uint32 kLimit = m_vecStringsAP.getItemCount();
304
305 if (id-AP_STRING_ID__FIRST__ < kLimit)
306 {
307 const gchar * szValue = (const gchar *) m_vecStringsAP.getNthItem(id-AP_STRING_ID__FIRST__);
308 if (szValue)
309 return szValue;
310 }
311
312 // if no entry in our table for this string, fallback to the builtin value (if provided).
313
314 if (m_pFallbackStringSet)
315 return m_pFallbackStringSet->getValue(id);
316
317 return NULL;
318 }
319
320 //////////////////////////////////////////////////////////////////
321 // build a static table to map id by names into numbers
322 //////////////////////////////////////////////////////////////////
323
324 #define dcl(id,s) { #id, AP_STRING_ID_##id },
325
326 static struct { const gchar * szName; XAP_String_Id id; } s_map[] =
327 {
328 #include "ap_String_Id.h"
329 };
330
331 #undef dcl
332
333 //////////////////////////////////////////////////////////////////
334
setValue(const gchar * szId,const gchar * szString)335 bool AP_DiskStringSet::setValue(const gchar * szId, const gchar * szString)
336 {
337 if (!szId || !*szId || !szString || !*szString)
338 return true;
339
340 UT_uint32 kLimit = G_N_ELEMENTS(s_map);
341 UT_uint32 k;
342
343 // we use predefined IDs to access the preferences, so there is no need to do
344 // case-insensitive comparison (and it is costing us lot of time).
345 for (k=0; k<kLimit; k++)
346 if (strcmp(s_map[k].szName,szId) == 0)
347 return setValue(s_map[k].id,szString);
348
349 // the name (szId) is not in our table, see if the base class knows about it.
350
351 return XAP_DiskStringSet::setValue(szId,szString);
352 }
353
loadStringsFromDisk(const char * szFilename)354 bool AP_DiskStringSet::loadStringsFromDisk(const char * szFilename)
355 {
356 if (!XAP_DiskStringSet::loadStringsFromDisk(szFilename))
357 return false;
358
359 return true;
360 }
361