Better i18n support in GtkBuilder
Let descriptions of accessible actions be translated, by specifying the description as content of the <action> element, and allowing "translatable", "context" and "comment" as attributes. (#518642)
This commit is contained in:
@ -49,7 +49,7 @@ internal child "accessible" of a <structname>GtkWidget</structname>.
|
|||||||
</object>
|
</object>
|
||||||
<object class="GtkButton" id="button1">
|
<object class="GtkButton" id="button1">
|
||||||
<accessibility>
|
<accessibility>
|
||||||
<action action_name="click" description="Click the button."/>
|
<action action_name="click" translatable="yes">Click the button.</action>
|
||||||
<relation target="label1" type="labelled-by"/>
|
<relation target="label1" type="labelled-by"/>
|
||||||
</accessibility>
|
</accessibility>
|
||||||
<child internal-child="accessible">
|
<child internal-child="accessible">
|
||||||
|
|||||||
147
gtk/gtkwidget.c
147
gtk/gtkwidget.c
@ -8174,13 +8174,13 @@ gboolean
|
|||||||
_gtk_widget_is_pointer_widget (GtkWidget *widget)
|
_gtk_widget_is_pointer_widget (GtkWidget *widget)
|
||||||
{
|
{
|
||||||
if (GTK_WIDGET_HAS_POINTER (widget))
|
if (GTK_WIDGET_HAS_POINTER (widget))
|
||||||
{
|
{
|
||||||
GdkWindow *win;
|
GdkWindow *win;
|
||||||
GtkWidget *wid;
|
GtkWidget *wid;
|
||||||
|
|
||||||
win = _gtk_widget_get_pointer_window (widget);
|
win = _gtk_widget_get_pointer_window (widget);
|
||||||
if (win)
|
if (win)
|
||||||
{
|
{
|
||||||
gdk_window_get_user_data (win, &wid);
|
gdk_window_get_user_data (win, &wid);
|
||||||
if (wid == widget)
|
if (wid == widget)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -9466,12 +9466,16 @@ gtk_widget_buildable_set_buildable_property (GtkBuildable *buildable,
|
|||||||
g_object_set_property (G_OBJECT (buildable), name, value);
|
g_object_set_property (G_OBJECT (buildable), name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
gchar *action_name;
|
gchar *action_name;
|
||||||
gchar *description;
|
GString *description;
|
||||||
|
gchar *context;
|
||||||
|
gboolean translatable;
|
||||||
} AtkActionData;
|
} AtkActionData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
gchar *target;
|
gchar *target;
|
||||||
gchar *type;
|
gchar *type;
|
||||||
} AtkRelationData;
|
} AtkRelationData;
|
||||||
@ -9480,7 +9484,8 @@ static void
|
|||||||
free_action (AtkActionData *data, gpointer user_data)
|
free_action (AtkActionData *data, gpointer user_data)
|
||||||
{
|
{
|
||||||
g_free (data->action_name);
|
g_free (data->action_name);
|
||||||
g_free (data->description);
|
g_string_free (data->description, TRUE);
|
||||||
|
g_free (data->context);
|
||||||
g_slice_free (AtkActionData, data);
|
g_slice_free (AtkActionData, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9497,7 +9502,7 @@ gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
|
|||||||
GtkBuilder *builder)
|
GtkBuilder *builder)
|
||||||
{
|
{
|
||||||
GSList *atk_relations;
|
GSList *atk_relations;
|
||||||
|
|
||||||
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_default))
|
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_default))
|
||||||
gtk_widget_grab_default (GTK_WIDGET (buildable));
|
gtk_widget_grab_default (GTK_WIDGET (buildable));
|
||||||
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_focus))
|
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_focus))
|
||||||
@ -9513,14 +9518,14 @@ gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
|
|||||||
GObject *target;
|
GObject *target;
|
||||||
AtkRelationType relation_type;
|
AtkRelationType relation_type;
|
||||||
AtkObject *target_accessible;
|
AtkObject *target_accessible;
|
||||||
|
|
||||||
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
|
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
|
||||||
relation_set = atk_object_ref_relation_set (accessible);
|
relation_set = atk_object_ref_relation_set (accessible);
|
||||||
|
|
||||||
for (l = atk_relations; l; l = l->next)
|
for (l = atk_relations; l; l = l->next)
|
||||||
{
|
{
|
||||||
AtkRelationData *relation = (AtkRelationData*)l->data;
|
AtkRelationData *relation = (AtkRelationData*)l->data;
|
||||||
|
|
||||||
target = gtk_builder_get_object (builder, relation->target);
|
target = gtk_builder_get_object (builder, relation->target);
|
||||||
if (!target)
|
if (!target)
|
||||||
{
|
{
|
||||||
@ -9530,7 +9535,7 @@ gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
|
|||||||
}
|
}
|
||||||
target_accessible = gtk_widget_get_accessible (GTK_WIDGET (target));
|
target_accessible = gtk_widget_get_accessible (GTK_WIDGET (target));
|
||||||
g_assert (target_accessible != NULL);
|
g_assert (target_accessible != NULL);
|
||||||
|
|
||||||
relation_type = atk_relation_type_for_name (relation->type);
|
relation_type = atk_relation_type_for_name (relation->type);
|
||||||
if (relation_type == ATK_RELATION_NULL)
|
if (relation_type == ATK_RELATION_NULL)
|
||||||
{
|
{
|
||||||
@ -9548,21 +9553,21 @@ gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
|
|||||||
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
|
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
GSList *actions;
|
GSList *actions;
|
||||||
GSList *relations;
|
GSList *relations;
|
||||||
} AccessibilitySubParserData;
|
} AccessibilitySubParserData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
accessibility_start_element (GMarkupParseContext *context,
|
accessibility_start_element (GMarkupParseContext *context,
|
||||||
const gchar *element_name,
|
const gchar *element_name,
|
||||||
const gchar **names,
|
const gchar **names,
|
||||||
const gchar **values,
|
const gchar **values,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
AccessibilitySubParserData *data = (AccessibilitySubParserData*)user_data;
|
AccessibilitySubParserData *data = (AccessibilitySubParserData*)user_data;
|
||||||
guint i;
|
guint i;
|
||||||
@ -9573,7 +9578,7 @@ accessibility_start_element (GMarkupParseContext *context,
|
|||||||
gchar *target = NULL;
|
gchar *target = NULL;
|
||||||
gchar *type = NULL;
|
gchar *type = NULL;
|
||||||
AtkRelationData *relation;
|
AtkRelationData *relation;
|
||||||
|
|
||||||
for (i = 0; names[i]; i++)
|
for (i = 0; names[i]; i++)
|
||||||
{
|
{
|
||||||
if (strcmp (names[i], "target") == 0)
|
if (strcmp (names[i], "target") == 0)
|
||||||
@ -9617,21 +9622,34 @@ accessibility_start_element (GMarkupParseContext *context,
|
|||||||
relation = g_slice_new (AtkRelationData);
|
relation = g_slice_new (AtkRelationData);
|
||||||
relation->target = target;
|
relation->target = target;
|
||||||
relation->type = type;
|
relation->type = type;
|
||||||
|
|
||||||
data->relations = g_slist_prepend (data->relations, relation);
|
data->relations = g_slist_prepend (data->relations, relation);
|
||||||
}
|
}
|
||||||
else if (strcmp (element_name, "action") == 0)
|
else if (strcmp (element_name, "action") == 0)
|
||||||
{
|
{
|
||||||
gchar *action_name = NULL;
|
gchar *action_name = NULL;
|
||||||
gchar *description = NULL;
|
gchar *description = NULL;
|
||||||
|
gchar *context = NULL;
|
||||||
|
gboolean translatable = FALSE;
|
||||||
AtkActionData *action;
|
AtkActionData *action;
|
||||||
|
|
||||||
for (i = 0; names[i]; i++)
|
for (i = 0; names[i]; i++)
|
||||||
{
|
{
|
||||||
if (strcmp (names[i], "action_name") == 0)
|
if (strcmp (names[i], "action_name") == 0)
|
||||||
action_name = g_strdup (values[i]);
|
action_name = values[i];
|
||||||
else if (strcmp (names[i], "description") == 0)
|
else if (strcmp (names[i], "description") == 0)
|
||||||
description = g_strdup (values[i]);
|
description = values[i];
|
||||||
|
else if (strcmp (names[i], "translatable") == 0)
|
||||||
|
{
|
||||||
|
if (!_gtk_builder_boolean_from_string (values[i], &translatable, error))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (strcmp (names[i], "comments") == 0)
|
||||||
|
{
|
||||||
|
/* do nothing, comments are for translators */
|
||||||
|
}
|
||||||
|
else if (strcmp (names[i], "context") == 0)
|
||||||
|
context = values[i];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g_markup_parse_context_get_position (context,
|
g_markup_parse_context_get_position (context,
|
||||||
@ -9643,13 +9661,11 @@ accessibility_start_element (GMarkupParseContext *context,
|
|||||||
"%s:%d:%d '%s' is not a valid attribute of <%s>",
|
"%s:%d:%d '%s' is not a valid attribute of <%s>",
|
||||||
"<input>",
|
"<input>",
|
||||||
line_number, char_number, names[i], "action");
|
line_number, char_number, names[i], "action");
|
||||||
g_free (action_name);
|
|
||||||
g_free (description);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!action_name || !description)
|
if (!action_name)
|
||||||
{
|
{
|
||||||
g_markup_parse_context_get_position (context,
|
g_markup_parse_context_get_position (context,
|
||||||
&line_number,
|
&line_number,
|
||||||
@ -9660,16 +9676,16 @@ accessibility_start_element (GMarkupParseContext *context,
|
|||||||
"%s:%d:%d <%s> requires attribute \"%s\"",
|
"%s:%d:%d <%s> requires attribute \"%s\"",
|
||||||
"<input>",
|
"<input>",
|
||||||
line_number, char_number, "action",
|
line_number, char_number, "action",
|
||||||
description ? "action_name" : "description");
|
"action_name");
|
||||||
g_free (action_name);
|
|
||||||
g_free (description);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action = g_slice_new (AtkActionData);
|
action = g_slice_new (AtkActionData);
|
||||||
action->action_name = action_name;
|
action->action_name = g_strdup (action_name);
|
||||||
action->description = description;
|
action->description = g_string_new (description);
|
||||||
|
action->context = g_strdup (context);
|
||||||
|
action->translatable = translatable;
|
||||||
|
|
||||||
data->actions = g_slist_prepend (data->actions, action);
|
data->actions = g_slist_prepend (data->actions, action);
|
||||||
}
|
}
|
||||||
else if (strcmp (element_name, "accessibility") == 0)
|
else if (strcmp (element_name, "accessibility") == 0)
|
||||||
@ -9678,12 +9694,32 @@ accessibility_start_element (GMarkupParseContext *context,
|
|||||||
g_warning ("Unsupported tag for GtkWidget: %s\n", element_name);
|
g_warning ("Unsupported tag for GtkWidget: %s\n", element_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
accessibility_text (GMarkupParseContext *context,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
AccessibilitySubParserData *data = (AccessibilitySubParserData*)user_data;
|
||||||
|
|
||||||
|
if (strcmp (g_markup_parse_context_get_element (context), "action") == 0)
|
||||||
|
{
|
||||||
|
AtkActionData *action = data->actions->data;
|
||||||
|
|
||||||
|
g_string_append_len (action->description, text, text_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const GMarkupParser accessibility_parser =
|
static const GMarkupParser accessibility_parser =
|
||||||
{
|
{
|
||||||
accessibility_start_element,
|
accessibility_start_element,
|
||||||
|
NULL,
|
||||||
|
accessibility_text,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
GObject *object;
|
GObject *object;
|
||||||
guint key;
|
guint key;
|
||||||
guint modifiers;
|
guint modifiers;
|
||||||
@ -9691,12 +9727,12 @@ typedef struct {
|
|||||||
} AccelGroupParserData;
|
} AccelGroupParserData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
accel_group_start_element (GMarkupParseContext *context,
|
accel_group_start_element (GMarkupParseContext *context,
|
||||||
const gchar *element_name,
|
const gchar *element_name,
|
||||||
const gchar **names,
|
const gchar **names,
|
||||||
const gchar **values,
|
const gchar **values,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
gint i;
|
gint i;
|
||||||
guint key = 0;
|
guint key = 0;
|
||||||
@ -9817,26 +9853,36 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
|
|||||||
AtkAction *action;
|
AtkAction *action;
|
||||||
gint i, n_actions;
|
gint i, n_actions;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
|
|
||||||
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
|
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
|
||||||
|
|
||||||
action = ATK_ACTION (accessible);
|
action = ATK_ACTION (accessible);
|
||||||
n_actions = atk_action_get_n_actions (action);
|
n_actions = atk_action_get_n_actions (action);
|
||||||
|
|
||||||
for (l = a11y_data->actions; l; l = l->next)
|
for (l = a11y_data->actions; l; l = l->next)
|
||||||
{
|
{
|
||||||
AtkActionData *action_data = (AtkActionData*)l->data;
|
AtkActionData *action_data = (AtkActionData*)l->data;
|
||||||
|
|
||||||
for (i = 0; i < n_actions; i++)
|
for (i = 0; i < n_actions; i++)
|
||||||
if (strcmp (atk_action_get_name (action, i),
|
if (strcmp (atk_action_get_name (action, i),
|
||||||
action_data->action_name) == 0)
|
action_data->action_name) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (i < n_actions)
|
if (i < n_actions)
|
||||||
atk_action_set_description (action, i,
|
{
|
||||||
action_data->description);
|
gchar *description;
|
||||||
|
|
||||||
|
if (action_data->translatable && action_data->description->len)
|
||||||
|
description = _gtk_builder_parser_translate (gtk_builder_get_translation_domain (builder),
|
||||||
|
action_data->context,
|
||||||
|
action_data->description->str);
|
||||||
|
else
|
||||||
|
description = action_data->description->str;
|
||||||
|
|
||||||
|
atk_action_set_description (action, i, description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_slist_foreach (a11y_data->actions, (GFunc)free_action, NULL);
|
g_slist_foreach (a11y_data->actions, (GFunc)free_action, NULL);
|
||||||
g_slist_free (a11y_data->actions);
|
g_slist_free (a11y_data->actions);
|
||||||
}
|
}
|
||||||
@ -9844,9 +9890,8 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
|
|||||||
if (a11y_data->relations)
|
if (a11y_data->relations)
|
||||||
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
|
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
|
||||||
a11y_data->relations);
|
a11y_data->relations);
|
||||||
|
|
||||||
g_slice_free (AccessibilitySubParserData, a11y_data);
|
g_slice_free (AccessibilitySubParserData, a11y_data);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,10 +44,18 @@ builder_new_from_string (const gchar *buffer,
|
|||||||
const gchar *domain)
|
const gchar *domain)
|
||||||
{
|
{
|
||||||
GtkBuilder *builder;
|
GtkBuilder *builder;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
builder = gtk_builder_new ();
|
builder = gtk_builder_new ();
|
||||||
if (domain)
|
if (domain)
|
||||||
gtk_builder_set_translation_domain (builder, domain);
|
gtk_builder_set_translation_domain (builder, domain);
|
||||||
gtk_builder_add_from_string (builder, buffer, length, NULL);
|
gtk_builder_add_from_string (builder, buffer, length, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_print ("ERROR: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1580,6 +1588,7 @@ test_widget (void)
|
|||||||
" <object class=\"GtkButton\" id=\"button1\">"
|
" <object class=\"GtkButton\" id=\"button1\">"
|
||||||
" <accessibility>"
|
" <accessibility>"
|
||||||
" <action action_name=\"click\" description=\"Sliff\"/>"
|
" <action action_name=\"click\" description=\"Sliff\"/>"
|
||||||
|
" <action action_name=\"clack\" translatable=\"yes\">Sniff</action>"
|
||||||
" </accessibility>"
|
" </accessibility>"
|
||||||
" </object>"
|
" </object>"
|
||||||
" </child>"
|
" </child>"
|
||||||
|
|||||||
Reference in New Issue
Block a user