diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2015-06-11 19:11:42 +0100 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2015-08-21 16:22:29 +0900 |
commit | 55f8e0c9c59e1600faa3082ce619a6a959d8d0c2 (patch) | |
tree | 7e917adb2f958312e2f9c3df1130c22919cc5715 | |
parent | b9b0b41095655c747aabde68915aac061bcc65ae (diff) | |
download | gettext-55f8e0c9c59e1600faa3082ce619a6a959d8d0c2.tar.gz |
xgettext: Add support for AppData XML files
AppData files are used to describe an application for package
management, and are described here:
http://people.freedesktop.org/~hughsient/appdata/
They may also currently be translated using intltool or itstool:
http://people.freedesktop.org/~hughsient/appdata/#translation
-rw-r--r-- | gettext-tools/doc/gettext.texi | 18 | ||||
-rw-r--r-- | gettext-tools/src/ChangeLog | 12 | ||||
-rw-r--r-- | gettext-tools/src/FILES | 3 | ||||
-rw-r--r-- | gettext-tools/src/Makefile.am | 4 | ||||
-rw-r--r-- | gettext-tools/src/x-appdata.c | 373 | ||||
-rw-r--r-- | gettext-tools/src/x-appdata.h | 46 | ||||
-rw-r--r-- | gettext-tools/src/xgettext.c | 3 | ||||
-rw-r--r-- | gettext-tools/tests/ChangeLog | 6 | ||||
-rw-r--r-- | gettext-tools/tests/Makefile.am | 1 | ||||
-rwxr-xr-x | gettext-tools/tests/xgettext-appdata-1 | 119 |
10 files changed, 582 insertions, 3 deletions
diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 640d600a6..6dda6635c 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -469,6 +469,7 @@ Internationalizable Data * RST:: Resource String Table * Glade:: Glade - GNOME user interface description * GSettings:: GSettings - GNOME user configuration schema +* AppData:: AppData - freedesktop.org application description Concluding Remarks @@ -12153,6 +12154,7 @@ using GNU gettext. * RST:: Resource String Table * Glade:: Glade - GNOME user interface description * GSettings:: GSettings - GNOME user configuration schema +* AppData:: AppData - freedesktop.org application description @end menu @node POT, RST, List of Data Formats, List of Data Formats @@ -12198,7 +12200,7 @@ glade, libglade, glade2, libglade2, intltool @code{xgettext}, @code{libglade-xgettext}, @code{xml-i18n-extract}, @code{intltool-extract} @end table -@node GSettings, , Glade, List of Data Formats +@node GSettings, AppData, Glade, List of Data Formats @subsection GSettings - GNOME user configuration schema @table @asis @@ -12212,6 +12214,20 @@ glib2 @code{xgettext}, @code{intltool-extract} @end table +@node AppData, , GSettings, List of Data Formats +@subsection AppData - freedesktop.org application description + +@table @asis +@item RPMs +appdata-tools, appstream, libappstream-glib, libappstream-glib-builder + +@item File extension +@code{appdata.xml} + +@item Extractor +@code{xgettext}, @code{intltool-extract}, @code{itstool} +@end table + @c This is the template for new data formats. @ignore diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 052b61445..6ffa742a3 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,15 @@ +2015-06-11 Philip Withnall <philip.withnall@collabora.co.uk> + + xgettext: add support for AppData files + * x-appdata.h: New file. + * x-appdata.c: New file. + * xgettext.c: Include x-appdata.h. + (language_to_extractor): Add AppData rule. + (extension_to_language): Add AppData rule. + * Makefile.am (noinst_HEADERS): Add x-appdata.h. + (xgettext_SOURCES): Add x-appdata.c. + * FILES: Update. + 2015-08-21 Daiki Ueno <ueno@gnu.org> xgettext: Allow multiple --copyright-holder diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index 5fb474ffe..b647a84b9 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -357,6 +357,9 @@ msgl-check.c | x-desktop.h | x-desktop.c | String extractor from Desktop Entry file. +| x-appdata.h +| x-appdata.c +| String extractor for AppData files. | xgettext.c | Main source for the 'xgettext' program. | diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 7b1b9982c..61da5c4eb 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -55,7 +55,7 @@ po-time.h plural-table.h lang-table.h format.h filters.h \ xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \ x-scheme.h x-smalltalk.h x-java.h x-properties.h x-csharp.h x-awk.h x-ycp.h \ x-tcl.h x-perl.h x-php.h x-stringtable.h x-rst.h x-glade.h x-lua.h \ -x-javascript.h x-vala.h x-gsettings.h x-desktop.h libexpat-compat.h +x-javascript.h x-vala.h x-gsettings.h x-desktop.h x-appdata.h libexpat-compat.h EXTRA_DIST += FILES project-id ChangeLog.0 @@ -183,7 +183,7 @@ xgettext_SOURCES += \ x-c.c x-po.c x-sh.c x-python.c x-lisp.c x-elisp.c x-librep.c x-scheme.c \ x-smalltalk.c x-java.c x-csharp.c x-awk.c x-ycp.c x-tcl.c x-perl.c x-php.c \ x-rst.c x-glade.c x-lua.c x-javascript.c x-vala.c x-gsettings.c \ - x-desktop.c + x-desktop.c x-appdata.c if !WOE32DLL msgattrib_SOURCES = msgattrib.c else diff --git a/gettext-tools/src/x-appdata.c b/gettext-tools/src/x-appdata.c new file mode 100644 index 000000000..31020a01d --- /dev/null +++ b/gettext-tools/src/x-appdata.c @@ -0,0 +1,373 @@ +/* xgettext AppData schema file backend. + Copyright (C) 2002-2003, 2005-2009, 2013, 2015 Free Software + Foundation, Inc. + + Written by Philip Withnall <philip.withnall@collabora.co.uk>, 2015. + Based on GSettings extractor by Daiki Ueno <ueno@gnu.org>, 2013. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Specification. */ +#include "x-appdata.h" + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "message.h" +#include "xgettext.h" +#include "error.h" +#include "xerror.h" +#include "xvasprintf.h" +#include "basename.h" +#include "progname.h" +#include "xalloc.h" +#include "hash.h" +#include "po-charset.h" +#include "gettext.h" +#include "libexpat-compat.h" + +#define _(s) gettext(s) + + +/* AppData is an XML format for describing application information. + The syntax is described here: + http://people.freedesktop.org/~hughsient/appdata/ + Translation is described in the ITS file: + http://people.freedesktop.org/~hughsient/appdata/#translation */ + + +/* ====================== Keyword set customization. ====================== */ + +/* If true extract all strings. */ +static bool extract_all = false; + + +/* ============================= XML parsing. ============================= */ + +#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT + +/* Accumulator for the extracted messages. */ +static message_list_ty *mlp; + +/* Logical filename, used to label the extracted messages. */ +static char *logical_file_name; + +/* XML parser. */ +static XML_Parser parser; + +enum whitespace_type_ty +{ + none, + normalize, + strip +}; +typedef enum whitespace_type_ty whitespace_type_ty; + +struct element_state +{ + bool is_component; /* the <component> element */ + bool is_description; /* the <description> element */ + bool extract_string; + whitespace_type_ty whitespace; + int lineno; + char *buffer; + size_t bufmax; + size_t buflen; +}; +static struct element_state *stack; +static size_t stack_size; + +static char * +normalize_whitespace (const char *text, whitespace_type_ty whitespace) +{ + if (whitespace == none) + return xstrdup (text); + else + { + char *result, *p; + + /* Strip whitespaces at the beginning/end of the text. */ + result = xstrdup (text + strspn (text, " \t\n")); + for (p = result + strlen (result); + p > result && (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n'); + p--) + ; + if (p > result) + *++p = '\0'; + + /* Normalize whitespaces within the text. */ + if (whitespace == normalize) + { + char *end = result + strlen (result); + for (p = result; *p != '\0';) + { + size_t len = strspn (p, " \t\n"); + if (len > 0) + { + *p = ' '; + memmove (p + 1, p + len, end - (p + len)); + end -= len - 1; + *end = '\0'; + p++; + } + p += strcspn (p, " \t\n"); + } + } + return result; + } +} + +/* Ensures stack_size >= size. */ +static void +ensure_stack_size (size_t size) +{ + if (size > stack_size) + { + stack_size = 2 * stack_size; + if (stack_size < size) + stack_size = size; + stack = + (struct element_state *) + xrealloc (stack, stack_size * sizeof (struct element_state)); + } +} + +static size_t stack_depth; + +/* Callback called when <element> is seen. */ +static void +start_element_handler (void *userData, const char *name, + const char **attributes) +{ + struct element_state *p; + + /* 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->is_component = (strcmp (name, "component") == 0); + p->is_description = (strcmp (name, "description") == 0); + p->extract_string = extract_all; + + if (!p->extract_string) + { + bool has_translatable = false; + whitespace_type_ty whitespace = none; + if ((stack_depth == 2 + && stack[1].is_component + && (strcmp (name, "name") == 0 + || strcmp (name, "summary") == 0)) + || (stack_depth > 2 + && stack[1].is_component + && stack[2].is_description)) + { + has_translatable = true; + whitespace = normalize; + } + + p->extract_string = has_translatable; + p->whitespace = whitespace; + } + + p->lineno = XML_GetCurrentLineNumber (parser); + p->buffer = NULL; + p->bufmax = 0; + p->buflen = 0; + if (!p->extract_string) + savable_comment_reset (); +} + +/* Callback called when </element> is seen. */ +static void +end_element_handler (void *userData, const char *name) +{ + struct element_state *p = &stack[stack_depth]; + + /* Actually extract string. */ + if (p->extract_string) + { + /* Don't extract the empty string. */ + if (p->buflen > 0) + { + lex_pos_ty pos; + + 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->buffer != NULL) + { + remember_a_message (mlp, NULL, + normalize_whitespace (p->buffer, + p->whitespace), + null_context, &pos, + NULL, savable_comment); + } + } + } + + /* Free memory for this stack level. */ + if (p->buffer != NULL) + free (p->buffer); + + /* Decrease stack depth. */ + stack_depth--; + + savable_comment_reset (); +} + +/* Callback called when some text is seen. */ +static void +character_data_handler (void *userData, const char *s, int len) +{ + struct element_state *p = &stack[stack_depth]; + + /* Accumulate character data. */ + if (len > 0) + { + if (p->buflen + len > p->bufmax) + { + p->bufmax = 2 * p->bufmax; + if (p->bufmax < p->buflen + len) + p->bufmax = p->buflen + len; + p->buffer = (char *) xrealloc (p->buffer, p->bufmax); + } + memcpy (p->buffer + p->buflen, s, len); + p->buflen += len; + } +} + +/* Callback called when some comment text is seen. */ +static void +comment_handler (void *userData, const char *data) +{ + /* Split multiline comment into lines, and remove leading and trailing + whitespace. */ + char *copy = xstrdup (data); + char *p; + char *q; + + for (p = copy; (q = strchr (p, '\n')) != NULL; p = q + 1) + { + while (p[0] == ' ' || p[0] == '\t') + p++; + while (q > p && (q[-1] == ' ' || q[-1] == '\t')) + q--; + *q = '\0'; + savable_comment_add (p); + } + q = p + strlen (p); + while (p[0] == ' ' || p[0] == '\t') + p++; + while (q > p && (q[-1] == ' ' || q[-1] == '\t')) + q--; + *q = '\0'; + savable_comment_add (p); + free (copy); +} + + +static void +do_extract_appdata (FILE *fp, + const char *real_filename, const char *logical_filename, + msgdomain_list_ty *mdlp) +{ + mlp = mdlp->item[0]->messages; + + /* expat feeds us strings in UTF-8 encoding. */ + xgettext_current_source_encoding = po_charset_utf8; + + logical_file_name = xstrdup (logical_filename); + + parser = XML_ParserCreate (NULL); + if (parser == NULL) + error (EXIT_FAILURE, 0, _("memory exhausted")); + + XML_SetElementHandler (parser, start_element_handler, end_element_handler); + XML_SetCharacterDataHandler (parser, character_data_handler); + XML_SetCommentHandler (parser, comment_handler); + + stack_depth = 0; + + while (!feof (fp)) + { + char buf[4096]; + int count = fread (buf, 1, sizeof buf, fp); + + if (count == 0) + { + if (ferror (fp)) + error (EXIT_FAILURE, errno, _("\ +error while reading \"%s\""), real_filename); + /* EOF reached. */ + break; + } + + if (XML_Parse (parser, buf, count, 0) == 0) + error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename, + (unsigned long) XML_GetCurrentLineNumber (parser), + (unsigned long) XML_GetCurrentColumnNumber (parser) + 1, + XML_ErrorString (XML_GetErrorCode (parser))); + } + + if (XML_Parse (parser, NULL, 0, 1) == 0) + error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename, + (unsigned long) XML_GetCurrentLineNumber (parser), + (unsigned long) XML_GetCurrentColumnNumber (parser) + 1, + XML_ErrorString (XML_GetErrorCode (parser))); + + XML_ParserFree (parser); + + /* Close scanner. */ + logical_file_name = NULL; + parser = NULL; +} + +#endif + +void +extract_appdata (FILE *fp, + const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp) +{ +#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT + if (LIBEXPAT_AVAILABLE ()) + do_extract_appdata (fp, real_filename, logical_filename, mdlp); + else +#endif + { + multiline_error (xstrdup (""), + xasprintf (_("\ +Language \"appdata\" is not supported. %s relies on expat.\n\ +This version was built without expat.\n"), + basename (program_name))); + exit (EXIT_FAILURE); + } +} diff --git a/gettext-tools/src/x-appdata.h b/gettext-tools/src/x-appdata.h new file mode 100644 index 000000000..57de593b7 --- /dev/null +++ b/gettext-tools/src/x-appdata.h @@ -0,0 +1,46 @@ +/* xgettext AppData file backend. + Copyright (C) 2002-2003, 2006, 2013, 2015 Free Software Foundation, + Inc. + Written by Philip Withnall <philip.withnall@collabora.co.uk>, 2015. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + + +#include <stdio.h> + +#include "message.h" +#include "xgettext.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define EXTENSIONS_APPDATA \ + { "appdata.xml", "appdata" }, \ + +#define SCANNERS_APPDATA \ + { "appdata", extract_appdata, NULL, NULL, NULL, NULL }, \ + +/* Scan an AppData XML file and add its translatable strings to mdlp. */ +extern void extract_appdata (FILE *fp, const char *real_filename, + const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp); + + +#ifdef __cplusplus +} +#endif diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 8e303c74a..be2aa6cd2 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -89,6 +89,7 @@ #include "x-java.h" #include "x-properties.h" #include "x-csharp.h" +#include "x-appdata.h" #include "x-awk.h" #include "x-ycp.h" #include "x-tcl.h" @@ -3764,6 +3765,7 @@ language_to_extractor (const char *name) SCANNERS_VALA SCANNERS_GSETTINGS SCANNERS_DESKTOP + SCANNERS_APPDATA /* Here may follow more languages and their scanners: pike, etc... Make sure new scanners honor the --exclude-file option. */ }; @@ -3854,6 +3856,7 @@ extension_to_language (const char *extension) EXTENSIONS_VALA EXTENSIONS_GSETTINGS EXTENSIONS_DESKTOP + EXTENSIONS_APPDATA /* Here may follow more file extensions... */ }; diff --git a/gettext-tools/tests/ChangeLog b/gettext-tools/tests/ChangeLog index 1596b4c4a..e1a1bb00c 100644 --- a/gettext-tools/tests/ChangeLog +++ b/gettext-tools/tests/ChangeLog @@ -1,3 +1,9 @@ +2015-06-11 Philip Withnall <philip.withnall@collabora.co.uk> + + xgettext: add support for AppData files + * Makefile.am (TESTS): Add xgettext-appdata-1. + * xgettext-appdata-1: New file. + 2015-08-21 Daiki Ueno <ueno@gnu.org> * Makefile.am (check_PROGRAMS): Rename from noinst_PROGRAMS, to diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 0cbe205be..0da1787a5 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -74,6 +74,7 @@ TESTS = gettext-1 gettext-2 gettext-3 gettext-4 gettext-5 gettext-6 gettext-7 \ xgettext-2 xgettext-3 xgettext-4 xgettext-5 xgettext-6 \ xgettext-7 xgettext-8 xgettext-9 xgettext-10 xgettext-11 xgettext-12 \ xgettext-13 xgettext-14 \ + xgettext-appdata-1 \ xgettext-awk-1 xgettext-awk-2 \ xgettext-c-2 xgettext-c-3 xgettext-c-4 xgettext-c-5 \ xgettext-c-6 xgettext-c-7 xgettext-c-8 xgettext-c-9 xgettext-c-10 \ diff --git a/gettext-tools/tests/xgettext-appdata-1 b/gettext-tools/tests/xgettext-appdata-1 new file mode 100755 index 000000000..14543d04e --- /dev/null +++ b/gettext-tools/tests/xgettext-appdata-1 @@ -0,0 +1,119 @@ +#!/bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test of AppData support. + +cat <<EOF > xg-gs-1-empty.appdata.xml +<?xml version="1.0"?> +<component type="desktop"/> +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} -o xg-gs-1.pot xg-gs-1-empty.appdata.xml 2>/dev/null +test $? = 0 || { + echo "Skipping test: xgettext was built without AppData support" + exit 77 +} + +cat <<EOF > xg-gs-1.appdata.xml +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright 2013 First Lastname <your@email.com> --> +<component type="desktop"> + <id>gnome-power-statistics.desktop</id> + <metadata_license>CC0-1.0</metadata_license> + <project_license>GPL-2.0+ and GFDL-1.3</project_license> + <name>Power Statistics</name> + <summary>Observe power management</summary> + <description> + <p> + Power Statistics is a program used to view historical and current battery + information and will show programs running on your computer using power. + </p> + <p>Example list:</p> + <ul> + <li>First item</li> + <li>Second item</li> + </ul> + <p> + You probably only need to install this application if you are having problems + with your laptop battery, or are trying to work out what programs are using + significant amounts of power. + </p> + </description> + <screenshots> + <screenshot type="default"> + <image>http://www.hughsie.com/en_US/main.png</image> + <caption>The main window showing the application in action</caption> + </screenshot> + <screenshot> + <image>http://www.hughsie.com/en_US/preferences.png</image> + <caption>The preferences window where you can change the defaults</caption> + </screenshot> + </screenshots> + <url type="homepage">http://www.gnome.org/projects/en_US/gnome-power-manager</url> + <updatecontact>gnome-power-manager-list@gnome.org</updatecontact> + <project_group>GNOME</project_group> +</component> +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} --add-comments -o - xg-gs-1.appdata.xml | grep -v 'POT-Creation-Date' > xg-gs-1.pot || exit 1 + +cat <<EOF > xg-gs-1.ok +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: xg-gs-1.appdata.xml:7 +msgid "Power Statistics" +msgstr "" + +#: xg-gs-1.appdata.xml:8 +msgid "Observe power management" +msgstr "" + +#: xg-gs-1.appdata.xml:10 +msgid "" +"Power Statistics is a program used to view historical and current battery " +"information and will show programs running on your computer using power." +msgstr "" + +#: xg-gs-1.appdata.xml:14 +msgid "Example list:" +msgstr "" + +#: xg-gs-1.appdata.xml:16 +msgid "First item" +msgstr "" + +#: xg-gs-1.appdata.xml:17 +msgid "Second item" +msgstr "" + +#: xg-gs-1.appdata.xml:19 +msgid "" +"You probably only need to install this application if you are having " +"problems with your laptop battery, or are trying to work out what programs " +"are using significant amounts of power." +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-gs-1.ok xg-gs-1.pot +result=$? + +exit $result |