summaryrefslogtreecommitdiffstats
path: root/gettext-tools
diff options
context:
space:
mode:
authorMiguel Ángel Arruga Vivas <rosen644835@gmail.com>2013-06-13 16:28:42 +0900
committerDaiki Ueno <ueno@gnu.org>2013-06-13 17:45:34 +0900
commit2c3f428a748eaa9a8cc2cc2f457ed8633e438d20 (patch)
tree05dcc5247ef1240168f802e0847006a8687bc7a8 /gettext-tools
parentf9d637f8dd3d435c2cd53c97ee1ddd336ed050a4 (diff)
downloadexternal_gettext-2c3f428a748eaa9a8cc2cc2f457ed8633e438d20.zip
external_gettext-2c3f428a748eaa9a8cc2cc2f457ed8633e438d20.tar.gz
external_gettext-2c3f428a748eaa9a8cc2cc2f457ed8633e438d20.tar.bz2
Support GtkBuilder file format in the Glade scanner.
Diffstat (limited to 'gettext-tools')
-rw-r--r--gettext-tools/src/ChangeLog23
-rw-r--r--gettext-tools/src/x-glade.c282
-rw-r--r--gettext-tools/src/x-glade.h1
3 files changed, 247 insertions, 59 deletions
diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog
index 9387115..1cb64a3 100644
--- a/gettext-tools/src/ChangeLog
+++ b/gettext-tools/src/ChangeLog
@@ -1,3 +1,26 @@
+2013-06-13 Miguel Angel Arruga Vivas <rosen644835@gmail.com>
+ Daiki Ueno <ueno@gnu.org>
+
+ Support for GtkBuilder file format in the Glade scanner.
+ * x-glade.h (EXTENSIONS_GLADE): Recognize .ui.
+ * x-glade.c (element_parser): New struct.
+ (start_element_glade1): New function split from start_element_handler.
+ (end_element_glade1): New function split from end_element_handler.
+ (start_element_glade2): New function split from start_element_handler.
+ (end_element_glade2): New function split from end_element_handler.
+ (start_element_gtkbuilder): New function.
+ (end_element_gtkbuilder): New function.
+ (element_parser_glade1): New variable.
+ (element_parser_glade2): New variable.
+ (element_parser_gtkbuilder): New variable.
+ (start_element_handler): Delegate the actual parsing logic to
+ specific element_parser.
+ (end_element_handler): Likewise.
+ Thanks to Miguel Ángel Arruga Vivas for the initial implementation
+ and the discussion in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-03/msg00074.html>
+ footnote 2.
+
2013-06-10 Daiki Ueno <ueno@gnu.org>
* Makefile.am: Use $(MKDIR_P) instead of $(mkdir_p).
diff --git a/gettext-tools/src/x-glade.c b/gettext-tools/src/x-glade.c
index 85ed21c..2c06621 100644
--- a/gettext-tools/src/x-glade.c
+++ b/gettext-tools/src/x-glade.c
@@ -52,8 +52,17 @@
#define _(s) gettext(s)
-/* glade is an XML based format. Some example files are contained in
- libglade-0.16. */
+/* Glade is an XML based format with three variants. The syntax for
+ each format is defined as follows.
+
+ - Glade 1
+ Some example files are contained in libglade-0.16.
+
+ - Glade 2
+ See http://library.gnome.org/devel/libglade/unstable/libglade-dtd.html
+
+ - GtkBuilder
+ See https://developer.gnome.org/gtk3/stable/GtkBuilder.html#BUILDER-UI */
/* ====================== Keyword set customization. ====================== */
@@ -61,6 +70,7 @@
/* If true extract all strings. */
static bool extract_all = false;
+/* Keywords are only used by Glade 1 support. */
static hash_table keywords;
static bool default_keywords = true;
@@ -385,8 +395,9 @@ static XML_Parser parser;
struct element_state
{
bool extract_string;
- bool extract_context;
- char *extracted_comment;
+ bool extract_context; /* used by Glade 2 */
+ char *extracted_comment; /* used by Glade 2 or GtkBuilder */
+ char *extracted_context; /* used by GtkBuilder */
int lineno;
char *buffer;
size_t bufmax;
@@ -412,35 +423,55 @@ ensure_stack_size (size_t size)
static size_t stack_depth;
-/* Callback called when <element> is seen. */
+/* Parser logic for each Glade compatible file format. */
+struct element_parser
+{
+ void (*start_element) (struct element_state *p, const char *name,
+ const char **attributes);
+ void (*end_element) (struct element_state *p, const char *name);
+};
+static struct element_parser *element_parser;
+
static void
-start_element_handler (void *userData, const char *name,
- const char **attributes)
+start_element_glade1 (struct element_state *p, const char *name,
+ const char **attributes)
{
- struct element_state *p;
void *hash_result;
- /* Increase stack depth. */
- stack_depth++;
- ensure_stack_size (stack_depth + 1);
-
- /* Don't extract a string for the containing element. */
- stack[stack_depth - 1].extract_string = false;
-
- p = &stack[stack_depth];
- p->extract_string = extract_all;
- p->extract_context = false;
- p->extracted_comment = NULL;
/* In Glade 1, a few specific elements are translatable. */
if (!p->extract_string)
p->extract_string =
(hash_find_entry (&keywords, name, strlen (name), &hash_result) == 0);
+}
+
+static void
+end_element_glade1 (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->buffer != NULL)
+ {
+ remember_a_message (mlp, NULL, p->buffer,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+ p->buffer = NULL;
+ }
+}
+
+static void
+start_element_glade2 (struct element_state *p, const char *name,
+ const char **attributes)
+{
/* In Glade 2, all <property> and <atkproperty> elements are translatable
that have the attribute translatable="yes".
See <http://library.gnome.org/devel/libglade/unstable/libglade-dtd.html>.
The translator comment is found in the attribute comments="...".
See <http://live.gnome.org/TranslationProject/DevGuidelines/Use comments>.
- */
+ If the element has the attribute context="yes", the content of
+ the element in the form "msgctxt|msgid". */
if (!p->extract_string
&& (strcmp (name, "property") == 0 || strcmp (name, "atkproperty") == 0))
{
@@ -465,6 +496,9 @@ start_element_handler (void *userData, const char *name,
? xstrdup (extracted_comment)
: NULL);
}
+
+ /* In Glade 2, the attribute description="..." of <atkaction>
+ element is also translatable. */
if (!p->extract_string
&& strcmp (name, "atkaction") == 0)
{
@@ -489,6 +523,170 @@ start_element_handler (void *userData, const char *name,
attp += 2;
}
}
+}
+
+static void
+end_element_glade2 (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+ char *msgid = NULL;
+ char *msgctxt = NULL;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->extract_context)
+ {
+ char *separator = strchr (p->buffer, '|');
+
+ if (separator == NULL)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ pos.file_name,
+ pos.line_number,
+ _("\
+Missing context for the string extracted from '%s' element"),
+ name);
+ error_with_progname = true;
+ }
+ else
+ {
+ *separator = '\0';
+ msgid = xstrdup (separator + 1);
+ msgctxt = xstrdup (p->buffer);
+ }
+ }
+ else
+ {
+ msgid = p->buffer;
+ p->buffer = NULL;
+ }
+
+ if (msgid != NULL)
+ remember_a_message (mlp, msgctxt, msgid,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+}
+
+static void
+start_element_gtkbuilder (struct element_state *p, const char *name,
+ const char **attributes)
+{
+ /* In GtkBuilder (used by Glade 3), all elements are translatable
+ that have the attribute translatable="yes".
+ See <https://developer.gnome.org/gtk3/stable/GtkBuilder.html#BUILDER-UI>.
+ The translator comment is found in the attribute comments="..."
+ and context is found in the attribute context="...".
+ */
+ if (!p->extract_string)
+ {
+ bool has_translatable = false;
+ const char *extracted_comment = NULL;
+ const char *extracted_context = NULL;
+ const char **attp = attributes;
+ while (*attp != NULL)
+ {
+ if (strcmp (attp[0], "translatable") == 0)
+ has_translatable = (strcmp (attp[1], "yes") == 0);
+ else if (strcmp (attp[0], "comments") == 0)
+ extracted_comment = attp[1];
+ else if (strcmp (attp[0], "context") == 0)
+ extracted_context = attp[1];
+ attp += 2;
+ }
+ p->extract_string = has_translatable;
+ p->extracted_comment =
+ (has_translatable && extracted_comment != NULL
+ ? xstrdup (extracted_comment)
+ : NULL);
+ p->extracted_context =
+ (has_translatable && extracted_context != NULL
+ ? xstrdup (extracted_context)
+ : NULL);
+ }
+}
+
+static void
+end_element_gtkbuilder (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->buffer != NULL)
+ {
+ remember_a_message (mlp, p->extracted_context, p->buffer,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+ p->buffer = NULL;
+ p->extracted_context = NULL;
+ }
+}
+
+static struct element_parser element_parser_glade1 =
+{
+ start_element_glade1,
+ end_element_glade1
+};
+
+static struct element_parser element_parser_glade2 =
+{
+ start_element_glade2,
+ end_element_glade2
+};
+
+static struct element_parser element_parser_gtkbuilder =
+{
+ start_element_gtkbuilder,
+ end_element_gtkbuilder
+};
+
+/* Callback called when <element> is seen. */
+static void
+start_element_handler (void *userData, const char *name,
+ const char **attributes)
+{
+ struct element_state *p;
+
+ if (!stack_depth)
+ {
+ if (strcmp (name, "GTK-Interface") == 0)
+ element_parser = &element_parser_glade1;
+ else if (strcmp (name, "glade-interface") == 0)
+ element_parser = &element_parser_glade2;
+ else if (strcmp (name, "interface") == 0)
+ element_parser = &element_parser_gtkbuilder;
+ else
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ logical_file_name,
+ XML_GetCurrentLineNumber (parser),
+ _("\
+The root element <%s> is not allowed in a valid Glade file"),
+ name);
+ error_with_progname = true;
+ }
+ }
+
+ /* Increase stack depth. */
+ stack_depth++;
+ ensure_stack_size (stack_depth + 1);
+
+ /* Don't extract a string for the containing element. */
+ stack[stack_depth - 1].extract_string = false;
+
+ p = &stack[stack_depth];
+ p->extract_string = extract_all;
+ p->extract_context = false;
+ p->extracted_comment = NULL;
+ p->extracted_context = NULL;
+
+ if (element_parser != NULL)
+ element_parser->start_element (p, name, attributes);
+
p->lineno = XML_GetCurrentLineNumber (parser);
p->buffer = NULL;
p->bufmax = 0;
@@ -509,55 +707,20 @@ end_element_handler (void *userData, const char *name)
/* Don't extract the empty string. */
if (p->buflen > 0)
{
- lex_pos_ty pos;
- char *msgid = NULL;
- char *msgctxt = NULL;
-
if (p->buflen == p->bufmax)
p->buffer = (char *) xrealloc (p->buffer, p->buflen + 1);
p->buffer[p->buflen] = '\0';
- pos.file_name = logical_file_name;
- pos.line_number = p->lineno;
-
- if (p->extract_context)
- {
- char *separator = strchr (p->buffer, '|');
-
- if (separator == NULL)
- {
- error_with_progname = false;
- error_at_line (0, 0,
- pos.file_name,
- pos.line_number,
- _("\
-Missing context for the string extracted from '%s' element"),
- name);
- error_with_progname = true;
- }
- else
- {
- *separator = '\0';
- msgid = xstrdup (separator + 1);
- msgctxt = xstrdup (p->buffer);
- }
- }
- else
- {
- msgid = p->buffer;
- p->buffer = NULL;
- }
-
- if (msgid != NULL)
- remember_a_message (mlp, msgctxt, msgid,
- null_context, &pos,
- p->extracted_comment, savable_comment);
+ if (element_parser != NULL)
+ element_parser->end_element (p, name);
}
}
/* Free memory for this stack level. */
if (p->extracted_comment != NULL)
free (p->extracted_comment);
+ if (p->extracted_context != NULL)
+ free (p->extracted_context);
if (p->buffer != NULL)
free (p->buffer);
@@ -641,6 +804,7 @@ do_extract_glade (FILE *fp,
XML_SetCommentHandler (parser, comment_handler);
stack_depth = 0;
+ element_parser = NULL;
while (!feof (fp))
{
diff --git a/gettext-tools/src/x-glade.h b/gettext-tools/src/x-glade.h
index 0af3385..e3cc253 100644
--- a/gettext-tools/src/x-glade.h
+++ b/gettext-tools/src/x-glade.h
@@ -30,6 +30,7 @@ extern "C" {
#define EXTENSIONS_GLADE \
{ "glade", "glade" }, \
{ "glade2", "glade" }, \
+ { "ui", "glade" }, \
#define SCANNERS_GLADE \
{ "glade", extract_glade, NULL, NULL, NULL }, \