libglade patch: Disambiguating translation msgids



Dear all,

the attached patch implements disambiguating msgids for libglade according to 
the recently introduced g_strip_context() function from glib/gstrfuncs.h. 
This implements the proposal from the gettext manual about how to 
disambiguate short msgids. (detailed rationale [1])

The solution adds a new optional attribute to the top-level element of the 
glade XML file and, hence, to its DTD glade-2-0.dtd. If this new attribute 
use_translation_disambiguation is set to 'yes', the new disambiguating msgid 
behaviour is activated. Otherwise the old behaviour for msgids will be kept, 
so that this solution will be fully backwards compatible.

The maintainer of libglade, James Henstridge  <james daa com au>, seems to be 
busy by other means -- he didn't react to this proposal during the last 
weeks. Actually I have a cvs.gnome.org account as well. Therefore if nobody 
objects to this patch during the next days, I would simply go ahead and 
commit it to libglade.

Best regards,

Christian Stimming


[1] Rationale for "disambiguating msgids":
A long known issue in the internationalization and translation of GUI
application is the problem with ambiguous message string. If GUIs contain
very short strings (e.g. a button label "File") in different places with
different meanings (example [2]), then all these labels are
forced to have one common translation. This frequently leads to totally wrong
translations, and translation teams all over the place are longing for a
solution for this problem.

The gettext manual knows about this as well
http://www.gnu.org/manual/gettext/html_node/gettext_151.html#SEC151 but it
defers the solution into the application domain. It proposes to introduce a
new "qualified_gettext()" function and "disambiguating comments" into the
msgid, so that the msgid then reads "noun|File" instead of just "File". Every
occurrence of the call to gettext() (or _() ) in the program code should then
be replaced by a call to the newly introduced qualified_gettext() (or Q_() )
function or however it's called. Fortunately, just recently exactly these 
extra functions have been introduced into glib: On November 5th, Matthias 
Clausen introduced glib/gi18n.h into glib's CVS, and they define such a Q_() 
function (using g_strip_context() from gstrfuncs.h).

Such a change has to be introduced into libglade because in order to make use 
of this extra functionality, the code place where gettext() is called has to 
be changed in order to call the qualified_gettext() or Q_(). It is important 
that this is done in libglade (as opposed to the application's code), because 
the message strings in question usually only show up inside the glade XML 
file. In the project I'm involved (www.gnucash.org) we don't define the 
button labels inside (our) C code (where we could switch from _()
to Q_() on our own) but only inside the Glade XML files. Therefore we can
only make use of the qualified gettext functions if libglade uses these, and
we cannot make this transition in our application code alone.

[2] Example for ambiguous msgids, from an earlier posting to 
translation-i18n lists sourceforge net: Think of any english word that can 
both be a noun and a verb (e.g. "a file" and "to file"). Think of the fact 
that almost always in at least some languages the translation of the verb is 
[very] different from the translation of the noun (e.g. in German the noun is 
"Datei" and the verb is "ablegen"). Now think of a GUI button that is 
labelled with this word. Now think of a case where this button has the 
meaning of the verb, and another case where this button has the meaning of 
the noun (e.g. "File" meaning "to file something somewhere" as opposed to 
"File" meaning "do something with a file"). There you are -- the msgid in 
both cases is identical, but the msgstr should be different. Therefore we 
need a disambiguating addition in the msgids. In the example this can be as 
simple as "noun|File" and "verb|File", but you could also use the real 
meaning: "to file something somewhere|File" and so on.
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/libglade/ChangeLog,v
retrieving revision 1.299
diff -u -r1.299 ChangeLog
--- ChangeLog	14 Nov 2003 13:11:13 -0000	1.299
+++ ChangeLog	23 Dec 2003 08:58:23 -0000
@@ -1,3 +1,16 @@
+2003-12-20  Christian Stimming  <stimming tuhh de>
+
+	* glade-2.0.dtd: Add attribute use_translation_disambiguation to
+	top-level element glade-interface which can optionally enable the
+	usage of disambiguated msgids in translatable strings.
+
+	* glade/glade-parser.c: Add proposed code for using disambiguated
+	msgids in gettext() by utilizing the newly introduced
+	g_strip_context function from glib-2.3.1. This code will only be
+	enabled when the optional attribute use_translation_disambiguation
+	was set to 'yes'. This has been discussed on
+	http://article.gmane.org/gmane.comp.gnome.apps.gnucash.devel/10812
+
 2003-11-14  Mark McLoughlin  <mark skynet ie>
 
 	* configure.in: Version 2.3.1.
Index: glade-2.0.dtd
===================================================================
RCS file: /cvs/gnome/libglade/glade-2.0.dtd,v
retrieving revision 1.11
diff -u -r1.11 glade-2.0.dtd
--- glade-2.0.dtd	21 Feb 2002 14:24:17 -0000	1.11
+++ glade-2.0.dtd	23 Dec 2003 08:58:23 -0000
@@ -2,7 +2,8 @@
 
 <!ELEMENT glade-interface (requires*, widget*) >
 <!ATTLIST glade-interface
-  xmlns CDATA #FIXED 'http://glade.gnome.org/glade-2.0.dtd' >
+  xmlns CDATA #FIXED 'http://glade.gnome.org/glade-2.0.dtd' 
+  use_translation_disambiguation (yes|no) 'no' >
 
 <!ELEMENT requires EMPTY >
 <!ATTLIST requires
Index: glade/glade-parser.c
===================================================================
RCS file: /cvs/gnome/libglade/glade/glade-parser.c,v
retrieving revision 1.27
diff -u -r1.27 glade-parser.c
--- glade/glade-parser.c	12 May 2002 11:26:14 -0000	1.27
+++ glade/glade-parser.c	23 Dec 2003 08:58:24 -0000
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <glib/gstrfuncs.h>
 
 #ifdef ENABLE_NLS
 #  include <libintl.h>
@@ -111,6 +112,7 @@
     enum {PROP_NONE, PROP_WIDGET, PROP_ATK, PROP_CHILD } prop_type;
     gchar *prop_name;
     gboolean translate_prop;
+    gboolean translation_disambiguation;
     GArray *props;
 
     GArray *signals;
@@ -476,6 +478,7 @@
     state->prop_type = PROP_NONE;
     state->prop_name = NULL;
     state->translate_prop = FALSE;
+    state->translation_disambiguation = FALSE;
     state->props = NULL;
 
     state->signals = NULL;
@@ -506,17 +509,18 @@
     case PARSER_START:
 	if (!strcmp(name, "glade-interface")) {
 	    state->state = PARSER_GLADE_INTERFACE;
-#if 0
-	    /* check for correct XML namespace */
 	    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
-		if (!strcmp(attrs[i], "xmlns") &&
+		/* check whether to use translation disambiguation prefix */
+		if (!strcmp(attrs[i], "use_translation_disambiguation")) {
+		    state->translation_disambiguation = !strcmp(attrs[i+1], "yes");
+	        /* check for correct XML namespace */
+		} else if (!strcmp(attrs[i], "xmlns") &&
 		    !strcmp(attrs[i+1], "...")) {
 		    g_warning("bad XML namespace `%s'.", attrs[i+1]);
 		} else
 		    g_warning("unknown attribute `%s' for <glade-interface>",
 			      attrs[i]);
 	    }
-#endif
 	} else {
 	    g_warning("Expected <glade-interface>.  Got <%s>.", name);
 	    state->prev_state = state->state;
@@ -900,7 +904,14 @@
 	    state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
 	prop.name = state->prop_name;
 	if (state->translate_prop && state->content->str[0] != '\0') {
-	    prop.value = alloc_string(state->interface,
+	    if (state->translation_disambiguation)
+		prop.value = 
+		    alloc_string(state->interface,
+				 g_strip_context(state->content->str,
+						 dgettext(state->domain, 
+							  state->content->str)));
+	    else
+		prop.value = alloc_string(state->interface,
 			dgettext(state->domain, state->content->str));
 	} else {
 	    prop.value = alloc_string(state->interface, state->content->str);
@@ -922,7 +933,14 @@
 	    state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
 	prop.name = state->prop_name;
 	if (state->translate_prop && state->content->str[0] != '\0') {
-	    prop.value = alloc_string(state->interface,
+	    if (state->translation_disambiguation) 
+		prop.value = 
+		    alloc_string(state->interface,
+				 g_strip_context(state->content->str,
+						 dgettext(state->domain, 
+							  state->content->str)));
+	    else
+		prop.value = alloc_string(state->interface,
 			dgettext(state->domain, state->content->str));
 	} else {
 	    prop.value = alloc_string(state->interface, state->content->str);
@@ -984,7 +1002,14 @@
 	    state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
 	prop.name = state->prop_name;
 	if (state->translate_prop && state->content->str[0] != '\0') {
-	    prop.value = alloc_string(state->interface,
+	    if (state->translation_disambiguation) 
+		prop.value = 
+		    alloc_string(state->interface,
+				 g_strip_context(state->content->str,
+						 dgettext(state->domain, 
+							  state->content->str)));
+	    else
+		prop.value = alloc_string(state->interface,
 			dgettext(state->domain, state->content->str));
 	} else {
 	    prop.value = alloc_string(state->interface, state->content->str);
@@ -1402,7 +1427,7 @@
 main(int argc, char **argv) {
     gtk_init(&argc, &argv);
     if (argc > 1) {
-	GladeInterface *interface = glade_parser_parse_file(argv[1]);
+	GladeInterface *interface = glade_parser_parse_file(argv[1], "");
 	g_message("output: %p", interface);
 	if (interface) {
 	    glade_interface_dump(interface, "/dev/stdout");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]