From b1ef79eb0d93bbf5533ad2f0d612b9fba91faff4 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 13 Jan 2012 17:06:06 +0100 Subject: [PATCH] Support Localized resources --- gio/gioenums.h | 8 ++++++- gio/glib-compile-resources.c | 25 ++++++++++++++++++++-- gio/gresource.c | 33 +++++++++++++++++++++++++++++ gio/tests/resources.c | 41 ++++++++++++++++++++++++++++++++++-- gio/tests/test.gresource.xml | 3 +++ 5 files changed, 105 insertions(+), 5 deletions(-) diff --git a/gio/gioenums.h b/gio/gioenums.h index ecb6b97cc..17ddb1077 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -649,23 +649,29 @@ typedef enum { * GResourceFlags: * @G_RESOURCE_FLAGS_NONE: No flags set. * @G_RESOURCE_FLAGS_COMPRESSED: The file is compressed. + * @G_RESOURCE_FLAGS_LOCALIZED: The file has alternate versions based on locale. * * GResourceFlags give information about a particular file inside a resource * bundle. **/ typedef enum { G_RESOURCE_FLAGS_NONE = 0, - G_RESOURCE_FLAGS_COMPRESSED = (1<<0) + G_RESOURCE_FLAGS_COMPRESSED = (1<<0), + G_RESOURCE_FLAGS_LOCALIZED = (1<<1) } GResourceFlags; /** * GResourceLookupFlags: * @G_RESOURCE_LOOKUP_FLAGS_NONE: No flags set. + * @G_RESOURCE_LOOKUP_FLAGS_NO_ALTERNATIVE: If there is an alternative + * (for instance a locale-specific version of the file) return the original + * version, not the alternative. * * GResourceLookupFlags determine how resource path lookups are handled. **/ typedef enum { G_RESOURCE_LOOKUP_FLAGS_NONE = 0, + G_RESOURCE_LOOKUP_FLAGS_NO_ALTERNATIVE = (1<<0), } GResourceLookupFlags; /** diff --git a/gio/glib-compile-resources.c b/gio/glib-compile-resources.c index f4a787e16..de5e95d19 100644 --- a/gio/glib-compile-resources.c +++ b/gio/glib-compile-resources.c @@ -54,6 +54,7 @@ typedef struct /* per gresource */ char *prefix; + char *lang; /* per file */ char *alias; @@ -106,7 +107,8 @@ start_element (GMarkupParseContext *context, if (strcmp (element_name, "gresource") == 0) { COLLECT (OPTIONAL | STRDUP, - "prefix", &state->prefix); + "prefix", &state->prefix, + OPTIONAL | STRDUP, "lang", &state->lang); return; } } @@ -172,12 +174,14 @@ end_element (GMarkupParseContext *context, { g_free (state->prefix); state->prefix = NULL; + g_free (state->lang); + state->lang = NULL; } else if (strcmp (element_name, "file") == 0) { gchar *file, *real_file; - gchar *key; + gchar *key, *old_key; FileData *data; file = state->string->str; @@ -190,6 +194,23 @@ end_element (GMarkupParseContext *context, else key = g_build_path ("/", "/", key, NULL); + if (state->lang) + { + data = g_hash_table_lookup (state->table, key); + if (data == NULL) + { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + _("File %s is in lang=%s, but not without lang"), + key, state->lang); + return; + } + data->flags |= G_RESOURCE_FLAGS_LOCALIZED; + + old_key = key; + key = g_build_path ("/", "/", state->lang, key, NULL); + g_free (old_key); + } + if (g_hash_table_lookup (state->table, key) != NULL) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, diff --git a/gio/gresource.c b/gio/gresource.c index aa4cae507..d659df0cc 100644 --- a/gio/gresource.c +++ b/gio/gresource.c @@ -58,6 +58,9 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref) * simple (no need to check for things like I/O errors or locate the files in the filesystem). It * also makes it easier to create relocatable applications. * + * Resource files can have locale specific alternatives, so that the current locale causes resource + * lookups to automatically pick up an alternate file. + * * Resource files can also be marked as compresses. Such files will be included in the resource bundle * in a compressed form, but will be automatically uncompressed when the resource is used. This * is very useful e.g. for larger text files that are parsed once (or rarely) and then thrown away. @@ -75,6 +78,9 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref) * dialog.ui * menumarkup.xml * + * + * menumarkup_SV.xml + * * * ]]> * @@ -83,8 +89,12 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref) * /org/gtk/Example/data/splashscreen.png * /org/gtk/Example/dialog.ui * /org/gtk/Example/menumarkup.xml + * /sv/org/gtk/Example/menumarkup.xml * ]]> * + * Where the contents at "/org/gtk/Example/menumarkup.xml" automatically redirects + * to "/sv/org/gtk/Example/menumarkup.xml" when run in e.g. a sv_SE locale. + * * Note that all resources in the process share the same namespace, so use java-style * path prefixes (like in the above example) to avoid conflicts. * @@ -277,6 +287,29 @@ static gboolean do_lookup (GResource *resource, &_flags, &array); + if (!(lookup_flags & G_RESOURCE_LOOKUP_FLAGS_NO_ALTERNATIVE) && + (_flags & G_RESOURCE_FLAGS_LOCALIZED)) + { + const gchar * const *langs = g_get_language_names (); + int i; + + for (i = 0; langs[i] != NULL; i++) + { + char *prefixed_path = g_strconcat ("/", langs[i], path, NULL); + res = do_lookup (resource, prefixed_path, + lookup_flags | G_RESOURCE_LOOKUP_FLAGS_NO_ALTERNATIVE, + size, flags, data, data_size, NULL); + g_free (prefixed_path); + if (res) + { + /* Mark the target as localized too if we followed the "link" */ + if (flags) + *flags |= G_RESOURCE_FLAGS_LOCALIZED; + break; + } + } + } + if (!res) { if (size) diff --git a/gio/tests/resources.c b/gio/tests/resources.c index 3da0e0a30..ed6a24964 100644 --- a/gio/tests/resources.c +++ b/gio/tests/resources.c @@ -48,7 +48,7 @@ test_resource (GResource *resource) g_assert (found); g_assert_no_error (error); g_assert (size == 6); - g_assert (flags == (G_RESOURCE_FLAGS_COMPRESSED)); + g_assert (flags == (G_RESOURCE_FLAGS_LOCALIZED | G_RESOURCE_FLAGS_COMPRESSED)); found = g_resource_get_info (resource, "/a_prefix/test2.txt", @@ -68,6 +68,15 @@ test_resource (GResource *resource) g_assert (size == 6); g_assert (flags == 0); + found = g_resource_get_info (resource, + "/sv/test1.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, + &size, &flags, &error); + g_assert (found); + g_assert_no_error (error); + g_assert (size == 6); + g_assert (flags == 0); + data = g_resource_lookup_data (resource, "/test1.txt", G_RESOURCE_LOOKUP_FLAGS_NONE, @@ -116,6 +125,16 @@ test_resource (GResource *resource) g_assert_cmpstr (g_bytes_get_data (data, NULL), ==, "test2\n"); g_bytes_unref (data); + data = g_resource_lookup_data (resource, + "/sv/test1.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, + &error); + g_assert (data != NULL); + g_assert_no_error (error); + g_assert (size == 6); + g_assert_cmpstr (g_bytes_get_data (data, NULL), ==, "test3\n"); + g_bytes_unref (data); + children = g_resource_enumerate_children (resource, "/not/here", G_RESOURCE_LOOKUP_FLAGS_NONE, @@ -209,7 +228,7 @@ test_resource_registred (void) g_assert (found); g_assert_no_error (error); g_assert (size == 6); - g_assert (flags == (G_RESOURCE_FLAGS_COMPRESSED)); + g_assert (flags == (G_RESOURCE_FLAGS_LOCALIZED | G_RESOURCE_FLAGS_COMPRESSED)); found = g_resources_get_info ("/a_prefix/test2.txt", G_RESOURCE_LOOKUP_FLAGS_NONE, @@ -227,6 +246,14 @@ test_resource_registred (void) g_assert (size == 6); g_assert (flags == 0); + found = g_resources_get_info ("/sv/test1.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, + &size, &flags, &error); + g_assert (found); + g_assert_no_error (error); + g_assert (size == 6); + g_assert (flags == 0); + data = g_resources_lookup_data ("/test1.txt", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); @@ -272,6 +299,16 @@ test_resource_registred (void) g_assert_cmpstr (g_bytes_get_data (data, NULL), ==, "test2\n"); g_bytes_unref (data); + data = g_resources_lookup_data ("/sv/test1.txt", + G_RESOURCE_LOOKUP_FLAGS_NONE, + &error); + g_assert (data != NULL); + g_assert_no_error (error); + size = g_bytes_get_size (data); + g_assert (size == 6); + g_assert_cmpstr (g_bytes_get_data (data, NULL), ==, "test3\n"); + g_bytes_unref (data); + children = g_resources_enumerate_children ("/not/here", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); diff --git a/gio/tests/test.gresource.xml b/gio/tests/test.gresource.xml index d66d08aca..c9885e582 100644 --- a/gio/tests/test.gresource.xml +++ b/gio/tests/test.gresource.xml @@ -7,4 +7,7 @@ test2.txt test2.txt + + test3.txt +