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("&amp;", fp);
86 			break;
87 		case '<':
88 			fputs("&lt;", fp);
89 			break;
90 		case '>':
91 			fputs("&gt;", fp);
92 			break;
93 		case '"':
94 			fputs("&quot;", 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