1 /*
2 * Copyright (C) 2007 Ignacio Casal Quinteiro <nacho.resa@gmail.com>
3 * 2008 Igalia
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors:
19 * Ignacio Casal Quinteiro <nacho.resa@gmail.com>
20 * Pablo Sanxiao <psanxiao@gmail.com>
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "gtr-msg.h"
28
29 #include <glib.h>
30 #include <glib-object.h>
31 #include <glib/gi18n.h>
32 #include <gtk/gtk.h>
33 #include <gettext-po.h>
34
35 typedef struct
36 {
37 po_message_iterator_t iterator;
38
39 po_message_t message;
40
41 GtrMsgStatus status;
42
43 gint po_position;
44 } GtrMsgPrivate;
45
46
47 G_DEFINE_TYPE_WITH_PRIVATE (GtrMsg, gtr_msg, G_TYPE_OBJECT)
48
49 static gchar *message_error = NULL;
50
51 static void
gtr_msg_recalc_status(GtrMsg * msg)52 gtr_msg_recalc_status (GtrMsg *msg)
53 {
54 /* Set the status */
55 if (gtr_msg_is_fuzzy (msg))
56 gtr_msg_set_status (msg, GTR_MSG_STATUS_FUZZY);
57 else if (gtr_msg_is_translated (msg))
58 gtr_msg_set_status (msg, GTR_MSG_STATUS_TRANSLATED);
59 else
60 gtr_msg_set_status (msg, GTR_MSG_STATUS_UNTRANSLATED);
61 }
62
63 static void
gtr_msg_init(GtrMsg * msg)64 gtr_msg_init (GtrMsg * msg)
65 {
66 }
67
68 static void
gtr_msg_finalize(GObject * object)69 gtr_msg_finalize (GObject * object)
70 {
71 G_OBJECT_CLASS (gtr_msg_parent_class)->finalize (object);
72 }
73
74 static void
gtr_msg_class_init(GtrMsgClass * klass)75 gtr_msg_class_init (GtrMsgClass * klass)
76 {
77 GObjectClass *object_class = G_OBJECT_CLASS (klass);
78
79 object_class->finalize = gtr_msg_finalize;
80 }
81
82 /***************************** Public funcs ***********************************/
83
84 /**
85 * gtr_msg_new:
86 * @iter: the po_message_iterator_t to set into the @msg
87 * @message: the
88 *
89 * Creates a new #GtrMsg.
90 *
91 * Return value: (transfer full): a new #GtrMsg object
92 **/
93 GtrMsg *
_gtr_msg_new(po_message_iterator_t iter,po_message_t message)94 _gtr_msg_new (po_message_iterator_t iter, po_message_t message)
95 {
96 GtrMsg *msg;
97
98 g_return_val_if_fail (iter != NULL || message != NULL, NULL);
99
100 msg = g_object_new (GTR_TYPE_MSG, NULL);
101
102 _gtr_msg_set_iterator (msg, iter);
103 _gtr_msg_set_message (msg, message);
104
105 gtr_msg_recalc_status (msg);
106
107 return msg;
108 }
109
110 /**
111 * gtr_msg_get_iterator:
112 * @msg: a #GtrMsg
113 *
114 * Return value: the message iterator in gettext format
115 **/
116 po_message_iterator_t
_gtr_msg_get_iterator(GtrMsg * msg)117 _gtr_msg_get_iterator (GtrMsg * msg)
118 {
119 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
120 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
121
122 return priv->iterator;
123 }
124
125 /**
126 * gtr_msg_set_iterator:
127 * @msg: a #GtrMsg
128 * @iter: the po_message_iterator_t to set into the @msg
129 *
130 * Sets the iterator into the #GtrMsg class.
131 **/
132 void
_gtr_msg_set_iterator(GtrMsg * msg,po_message_iterator_t iter)133 _gtr_msg_set_iterator (GtrMsg * msg, po_message_iterator_t iter)
134 {
135 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
136 g_return_if_fail (GTR_IS_MSG (msg));
137
138 priv->iterator = iter;
139 }
140
141 /**
142 * gtr_msg_get_message:
143 * @msg: a #GtrMsg
144 *
145 * Return value: the message in gettext format
146 **/
147 po_message_t
_gtr_msg_get_message(GtrMsg * msg)148 _gtr_msg_get_message (GtrMsg * msg)
149 {
150 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
151 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
152
153 return priv->message;
154 }
155
156 /**
157 * gtr_msg_set_message:
158 * @msg: a #GtrMsg
159 * @message: the po_message_t to set into the @msg
160 *
161 * Sets the message into the #GtrMsg class.
162 **/
163 void
_gtr_msg_set_message(GtrMsg * msg,po_message_t message)164 _gtr_msg_set_message (GtrMsg * msg, po_message_t message)
165 {
166 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
167 g_return_if_fail (GTR_IS_MSG (msg));
168 g_return_if_fail (message != NULL);
169
170 priv->message = message;
171 }
172
173 /**
174 * gtr_msg_is_translated:
175 * @msg: a #GtrMsg
176 *
177 * Gets whether or not the message is translated. See that a fuzzy message
178 * is also counted as translated so it must be checked first that the message
179 * is fuzzy.
180 *
181 * Return value: %TRUE if the message is translated
182 **/
183 gboolean
gtr_msg_is_translated(GtrMsg * msg)184 gtr_msg_is_translated (GtrMsg *msg)
185 {
186 g_return_val_if_fail (GTR_IS_MSG (msg), FALSE);
187
188 if (gtr_msg_get_msgid_plural (msg) == NULL)
189 return gtr_msg_get_msgstr (msg)[0] != '\0';
190 else
191 {
192 gint i;
193
194 for (i = 0;; i++)
195 {
196 const gchar *msgstr_i = gtr_msg_get_msgstr_plural (msg, i);
197 if (msgstr_i == NULL)
198 break;
199 if (msgstr_i[0] == '\0')
200 return FALSE;
201 }
202
203 return TRUE;
204 }
205 }
206
207 /**
208 * gtr_msg_is_fuzzy:
209 * @msg: a #GtrMsg
210 *
211 * Return value: TRUE if the message is fuzzy
212 **/
213 gboolean
gtr_msg_is_fuzzy(GtrMsg * msg)214 gtr_msg_is_fuzzy (GtrMsg * msg)
215 {
216 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
217 g_return_val_if_fail (GTR_IS_MSG (msg), FALSE);
218
219 return po_message_is_fuzzy (priv->message);
220 }
221
222
223 /**
224 * gtr_msg_set_fuzzy:
225 * @msg: a #GtrMsg
226 * @fuzzy: the fuzzy value to set to the message
227 *
228 * Change the fuzzy mark of a message.
229 **/
230 void
gtr_msg_set_fuzzy(GtrMsg * msg,gboolean fuzzy)231 gtr_msg_set_fuzzy (GtrMsg * msg, gboolean fuzzy)
232 {
233 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
234 g_return_if_fail (GTR_IS_MSG (msg));
235
236 po_message_set_fuzzy (priv->message, fuzzy);
237 }
238
239 /**
240 * gtr_msg_set_status:
241 * @msg: a #GtrMsg
242 * @status: a #GtrMsgStatus
243 *
244 * Sets the status for a message.
245 */
246 void
gtr_msg_set_status(GtrMsg * msg,GtrMsgStatus status)247 gtr_msg_set_status (GtrMsg * msg, GtrMsgStatus status)
248 {
249 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
250 g_return_if_fail (GTR_IS_MSG (msg));
251
252 priv->status = status;
253 }
254
255 /**
256 * gtr_msg_get_status:
257 * @msg: a #GtrMsg
258 *
259 * Return value: the message's status.
260 */
261 GtrMsgStatus
gtr_msg_get_status(GtrMsg * msg)262 gtr_msg_get_status (GtrMsg * msg)
263 {
264 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
265 g_return_val_if_fail (GTR_IS_MSG (msg), 0);
266
267 return priv->status;
268 }
269
270 /**
271 * gtr_msg_get_msgid:
272 * @msg: a #GtrMsg
273 *
274 * Return value: the msgid (untranslated English string) of a message.
275 **/
276 const gchar *
gtr_msg_get_msgid(GtrMsg * msg)277 gtr_msg_get_msgid (GtrMsg * msg)
278 {
279 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
280 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
281
282 return po_message_msgid (priv->message);
283 }
284
285
286 /**
287 * gtr_msg_get_msgid_plural:
288 * @msg: a #GtrMsg
289 *
290 * Return value: (transfer none): the msgid_plural (untranslated English plural
291 * string) of a message, or NULL for a message without plural.
292 **/
293 const gchar *
gtr_msg_get_msgid_plural(GtrMsg * msg)294 gtr_msg_get_msgid_plural (GtrMsg * msg)
295 {
296 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
297 return po_message_msgid_plural (priv->message);
298 }
299
300
301 /**
302 * gtr_msg_get_msgstr:
303 * @msg: a #GtrMsg
304 *
305 * Return value: (transfer none): the msgstr (translation) of a message.
306 * Return the empty string for an untranslated message.
307 **/
308 const gchar *
gtr_msg_get_msgstr(GtrMsg * msg)309 gtr_msg_get_msgstr (GtrMsg * msg)
310 {
311 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
312 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
313
314 return po_message_msgstr (priv->message);
315 }
316
317
318 /**
319 * gtr_msg_set_msgstr:
320 * @msg: a #GtrMsg
321 * @msgstr: the string to set in the @msg
322 *
323 * Change the msgstr (translation) of a message.
324 * Use an empty string to denote an untranslated message.
325 **/
326 void
gtr_msg_set_msgstr(GtrMsg * msg,const gchar * msgstr)327 gtr_msg_set_msgstr (GtrMsg * msg, const gchar * msgstr)
328 {
329 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
330 g_return_if_fail (GTR_IS_MSG (msg));
331 g_return_if_fail (msgstr != NULL);
332
333 po_message_set_msgstr (priv->message, msgstr);
334 }
335
336
337 /**
338 * gtr_msg_get_msgstr_plural:
339 * @msg: a #GtrMsg
340 * @index: the index of the plural array
341 *
342 * Return value: (transfer none): the msgstr[index] for a message with plural
343 * handling, or NULL when the index is out of range or for a
344 * message without plural.
345 **/
346 const gchar *
gtr_msg_get_msgstr_plural(GtrMsg * msg,gint index)347 gtr_msg_get_msgstr_plural (GtrMsg * msg, gint index)
348 {
349 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
350 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
351
352 return po_message_msgstr_plural (priv->message, index);
353 }
354
355 /**
356 * gtr_msg_set_msgstr_plural:
357 * @msg: a #GtrMsg
358 * @index: the index where to set the msgstr
359 * @msgstr: the message to set in the msg
360 *
361 * Change the msgstr[index] for a message with plural handling.
362 * Use a NULL value at the end to reduce the number of plural forms.
363 **/
364 void
gtr_msg_set_msgstr_plural(GtrMsg * msg,gint index,const gchar * msgstr)365 gtr_msg_set_msgstr_plural (GtrMsg * msg, gint index, const gchar * msgstr)
366 {
367 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
368 g_return_if_fail (GTR_IS_MSG (msg));
369 g_return_if_fail (msgstr != NULL);
370
371 po_message_set_msgstr_plural (priv->message, index, msgstr);
372 }
373
374
375 /**
376 * gtr_msg_get_comment:
377 * @msg: a #GtrMsg
378 *
379 * Return value: (transfer none) the comments for a message.
380 **/
381 const gchar *
gtr_msg_get_comment(GtrMsg * msg)382 gtr_msg_get_comment (GtrMsg * msg)
383 {
384 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
385 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
386
387 return po_message_comments (priv->message);
388 }
389
390 /**
391 * gtr_msg_set_comment:
392 * @msg: a #GtrMsg
393 * @comment: the comment to set for a message
394 *
395 * Change the comments for a message.
396 * comments should be a multiline string,
397 * ending in a newline, or empty.
398 **/
399 void
gtr_msg_set_comment(GtrMsg * msg,const gchar * comment)400 gtr_msg_set_comment (GtrMsg * msg, const gchar * comment)
401 {
402 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
403 g_return_if_fail (GTR_IS_MSG (msg));
404 g_return_if_fail (comment != NULL);
405
406 po_message_set_comments (priv->message, comment);
407 }
408
409 /**
410 * gtr_msg_get_po_position:
411 * @msg: a #GtrMsg
412 *
413 * Return value: the position of the message.
414 *
415 * Gets the position of this message in the PO file in relation to the other
416 * messages.
417 **/
418 gint
gtr_msg_get_po_position(GtrMsg * msg)419 gtr_msg_get_po_position (GtrMsg * msg)
420 {
421 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
422 g_return_val_if_fail (GTR_IS_MSG (msg), 0);
423
424 return priv->po_position;
425 }
426
427 /**
428 * gtr_msg_set_po_position:
429 * @msg: a #GtrMsg
430 * @po_position: the numerical position of the message.
431 *
432 * Sets the numerical position of this message in relation to other messages.
433 **/
434 void
gtr_msg_set_po_position(GtrMsg * msg,gint po_position)435 gtr_msg_set_po_position (GtrMsg * msg, gint po_position)
436 {
437 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
438 g_return_if_fail (GTR_IS_MSG (msg));
439
440 priv->po_position = po_position;
441 }
442
443 /**
444 * gtr_msg_get_extracted_comments:
445 * @msg: a #GtrMsg
446 *
447 * Return value: (transfer none): the extracted comments for a message.
448 **/
449 const gchar *
gtr_msg_get_extracted_comments(GtrMsg * msg)450 gtr_msg_get_extracted_comments (GtrMsg * msg)
451 {
452 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
453 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
454
455 return po_message_extracted_comments (priv->message);
456 }
457
458 /**
459 * gtr_msg_get_filename:
460 * @msg: a #GtrMsg
461 * @i: the i-th file for a message.
462 *
463 * Return value: (transfer none): the i-th filename for a message, or NULL if
464 * @i is out of range.
465 */
466 const gchar *
gtr_msg_get_filename(GtrMsg * msg,gint i)467 gtr_msg_get_filename (GtrMsg * msg, gint i)
468 {
469 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
470 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
471
472 po_filepos_t filepos;
473
474 filepos = po_message_filepos (priv->message, i);
475
476 if (filepos == NULL)
477 return NULL;
478
479 return po_filepos_file (filepos);
480 }
481
482 /**
483 * gtr_msg_get_file_line:
484 * @msg: a #GtrMsg
485 * @i: the i-th file for a message.
486 *
487 * Return value: (transfer none): the i-th file line for a message, or NULL if
488 * @i is out of range.
489 */
490 gint *
gtr_msg_get_file_line(GtrMsg * msg,gint i)491 gtr_msg_get_file_line (GtrMsg * msg, gint i)
492 {
493 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
494 g_return_val_if_fail (GTR_IS_MSG (msg), (gint *) 0);
495
496 po_filepos_t filepos;
497
498 filepos = po_message_filepos (priv->message, i);
499
500 if (filepos == NULL)
501 return NULL;
502
503 return (gint *) po_filepos_start_line (filepos);
504 }
505
506 /**
507 * gtr_msg_get_msgctxt:
508 * @msg: a #GtrMsg
509 *
510 * Return value: (transfer none): the context of a message, or NULL for a
511 * message not restricted to a context.
512 */
513 const gchar *
gtr_msg_get_msgctxt(GtrMsg * msg)514 gtr_msg_get_msgctxt (GtrMsg * msg)
515 {
516 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
517 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
518
519 return po_message_msgctxt (priv->message);
520 }
521
522 /**
523 * gtr_msg_get_format:
524 * @msg: a #GtrMsg
525 *
526 * Return the pretty name associated with a format type.
527 * For example, for "csharp-format", return "C#".
528 * Return NULL if the are no format type in the message.
529 *
530 * Return value: (transfer none): the pretty name associated with a format type
531 * or NULL if the message hasn't any format type.
532 */
533 const gchar *
gtr_msg_get_format(GtrMsg * msg)534 gtr_msg_get_format (GtrMsg * msg)
535 {
536 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
537 const gchar *const *format_list;
538 gint i;
539
540 g_return_val_if_fail (GTR_IS_MSG (msg), NULL);
541
542 format_list = po_format_list ();
543
544 for (i = 0; format_list[i] != NULL; i++)
545 {
546 if (po_message_is_format (priv->message, format_list[i]))
547 return po_format_pretty_name (format_list[i]);
548 }
549
550 return NULL;
551 }
552
553 /*
554 * Functions to manage the gettext errors
555 */
556 static void
on_gettext_po_xerror(gint severity,po_message_t message,const gchar * filename,size_t lineno,size_t column,gint multiline_p,const gchar * message_text)557 on_gettext_po_xerror (gint severity,
558 po_message_t message,
559 const gchar * filename, size_t lineno, size_t column,
560 gint multiline_p, const gchar * message_text)
561 {
562 if (message_text)
563 message_error = g_strdup (message_text);
564 else
565 message_error = NULL;
566 }
567
568 static void
on_gettext_po_xerror2(gint severity,po_message_t message1,const gchar * filename1,size_t lineno1,size_t column1,gint multiline_p1,const gchar * message_text1,po_message_t message2,const gchar * filename2,size_t lineno2,size_t column2,gint multiline_p2,const gchar * message_text2)569 on_gettext_po_xerror2 (gint severity,
570 po_message_t message1,
571 const gchar * filename1, size_t lineno1,
572 size_t column1, gint multiline_p1,
573 const gchar * message_text1, po_message_t message2,
574 const gchar * filename2, size_t lineno2,
575 size_t column2, gint multiline_p2,
576 const gchar * message_text2)
577 {
578 g_warning ("Error: %s.\n %s", message_text1, message_text2);
579 }
580
581 /**
582 * gtr_msg_check:
583 * @msg: a #GtrMsg
584 *
585 * Test whether the message translation is a valid format string if the message
586 * is marked as being a format string.
587 *
588 * Return value: (transfer full): the message error or NULL if there is not any
589 * error. Must be freed with g_free.
590 **/
591 gchar *
gtr_msg_check(GtrMsg * msg)592 gtr_msg_check (GtrMsg * msg)
593 {
594 gchar *error = NULL;
595 GtrMsgPrivate *priv = gtr_msg_get_instance_private (msg);
596 struct po_xerror_handler handler;
597
598 g_return_val_if_fail (msg != NULL, NULL);
599
600 /* We are not freeing the message_error so at start should be NULL
601 * always for us
602 */
603 message_error = NULL;
604
605 handler.xerror = &on_gettext_po_xerror;
606 handler.xerror2 = &on_gettext_po_xerror2;
607
608 po_message_check_all (priv->message, priv->iterator, &handler);
609
610 if (gtr_msg_is_fuzzy (msg) || !gtr_msg_is_translated (msg))
611 {
612 if (message_error)
613 g_free (message_error);
614 message_error = NULL;
615 }
616
617 if (message_error)
618 {
619 error = g_strdup (message_error);
620 g_free (message_error);
621 }
622 message_error = NULL;
623
624 return error;
625 }
626