From b5b32495f9301cb184f4e995d9dc21e8a9b51c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20St=C3=B6sel?= Date: Thu, 7 Jan 2010 00:49:41 +0100 Subject: [PATCH] Implement 'Preferred languages' preference, ACCEPT_LANGUAGE header --- midori/main.c | 31 +++++++++++++ midori/midori-preferences.c | 5 +++ midori/midori-websettings.c | 24 +++++++++++ midori/sokoke.c | 86 +++++++++++++++++++++++++++++++++++++ midori/sokoke.h | 3 ++ 5 files changed, 149 insertions(+) diff --git a/midori/main.c b/midori/main.c index 2c4c614f..fe0789c2 100644 --- a/midori/main.c +++ b/midori/main.c @@ -1116,6 +1116,34 @@ soup_session_settings_notify_ident_string_cb (MidoriWebSettings* settings, } #endif +static void +midori_soup_session_settings_accept_language_cb (SoupSession* session, + SoupMessage* msg, + MidoriWebSettings* settings) +{ + gchar* languages = katze_object_get_string (settings, "preferred-languages"); + gchar* accpt; + + /* Empty, use the system locales */ + if (!(languages && *languages)) + accpt = sokoke_accept_languages (g_get_language_names ()); + /* No =, no ., looks like a list of language names */ + else if (!(strchr (languages, '=') && strchr (languages, '.'))) + { + gchar ** lang_names = g_strsplit_set (languages, ",; ", -1); + accpt = sokoke_accept_languages ((const gchar* const *)lang_names); + g_strfreev (lang_names); + } + /* Presumably a well formatted list including priorities */ + else + accpt = languages; + + if (accpt != languages) + g_free (languages); + soup_message_headers_append (msg->request_headers, "ACCEPT_LANGUAGE", accpt); + g_free (accpt); +} + static void midori_soup_session_debug (SoupSession* session) { @@ -1150,6 +1178,9 @@ midori_soup_session_prepare (SoupSession* session, G_CALLBACK (soup_session_settings_notify_ident_string_cb), session); #endif + g_signal_connect (session, "request-queued", + G_CALLBACK (midori_soup_session_settings_accept_language_cb), settings); + config_file = build_config_filename ("logins"); feature = g_object_new (KATZE_TYPE_HTTP_AUTH, "filename", config_file, NULL); g_free (config_file); diff --git a/midori/midori-preferences.c b/midori/midori-preferences.c index 75d7a67e..82c5241a 100644 --- a/midori/midori-preferences.c +++ b/midori/midori-preferences.c @@ -495,6 +495,11 @@ midori_preferences_set_settings (MidoriPreferences* preferences, INDENTED_ADD (label); button = katze_property_proxy (settings, "identify-as", "custom-ident-string"); SPANNED_ADD (button); + label = katze_property_label (settings, "preferred-languages"); + INDENTED_ADD (label); + entry = katze_property_proxy (settings, "preferred-languages", NULL); + SPANNED_ADD (entry); + /* Page "Privacy" */ PAGE_NEW (GTK_STOCK_INDEX, _("Privacy")); diff --git a/midori/midori-websettings.c b/midori/midori-websettings.c index 9c04704b..33e1b552 100644 --- a/midori/midori-websettings.c +++ b/midori/midori-websettings.c @@ -86,6 +86,7 @@ struct _MidoriWebSettings gboolean remember_last_downloaded_files; gchar* http_proxy; + gchar* http_accept_language; gboolean auto_detect_proxy; MidoriIdentity identify_as; gchar* ident_string; @@ -170,6 +171,7 @@ enum PROP_AUTO_DETECT_PROXY, PROP_IDENTIFY_AS, PROP_IDENT_STRING, + PROP_PREFERRED_LANGUAGES, PROP_CLEAR_PRIVATE_DATA }; @@ -1048,6 +1050,22 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * MidoriWebSettings:preferred-languages: + * + * A comma separated list of languages preferred for rendering multilingual webpages. + * + * Since: 0.2.3 + */ + g_object_class_install_property (gobject_class, + PROP_PREFERRED_LANGUAGES, + g_param_spec_string ( + "preferred-languages", + _("Preferred languages"), + _("A comma separated list of languages preferred for rendering multilingual webpages, for example \"de\", \"ru,nl\" or \"en-us;q=1.0, fr-fr;q=0.667\""), + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * MidoriWebSettings:clear-private-data: * @@ -1462,6 +1480,9 @@ midori_web_settings_set_property (GObject* object, #endif } break; + case PROP_PREFERRED_LANGUAGES: + katze_assign (web_settings->http_accept_language, g_value_dup_string (value)); + break; case PROP_CLEAR_PRIVATE_DATA: web_settings->clear_private_data = g_value_get_int (value); break; @@ -1677,6 +1698,9 @@ midori_web_settings_get_property (GObject* object, } g_value_set_string (value, web_settings->ident_string); break; + case PROP_PREFERRED_LANGUAGES: + g_value_set_string (value, web_settings->http_accept_language); + break; case PROP_CLEAR_PRIVATE_DATA: g_value_set_int (value, web_settings->clear_private_data); break; diff --git a/midori/sokoke.c b/midori/sokoke.c index bd74132f..c917d7e2 100644 --- a/midori/sokoke.c +++ b/midori/sokoke.c @@ -1633,3 +1633,89 @@ sokoke_prefetch_uri (const char* uri) soup_uri_free (s_uri); return TRUE; } + +/* Provide a new way for SoupSession to assume an 'Accept-Language' + string automatically from the return value of g_get_language_names(), + properly formatted according to RFC2616. + Copyright (C) 2009 Mario Sanchez Prada + Copyright (C) 2009 Dan Winship + Mostly copied from libSoup 2.29, coding style adjusted */ + +/* Converts a language in POSIX format and to be RFC2616 compliant */ +/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */ +static gchar * +sokoke_posix_lang_to_rfc2616 (const gchar *language) +{ + if (!strchr (language, '.') && !strchr (language, '@') && language[0] != 'C') + /* change to lowercase and '_' to '-' */ + return g_strdelimit (g_ascii_strdown (language, -1), "_", '-'); + + return NULL; +} + +/* Adds a quality value to a string (any value between 0 and 1). */ +static gchar * +sokoke_add_quality_value (const gchar *str, + float qvalue) +{ + if ((qvalue >= 0.0) && (qvalue <= 1.0)) + { + int qv_int = (qvalue * 1000 + 0.5); + return g_strdup_printf ("%s;q=%d.%d", + str, (int) (qv_int / 1000), qv_int % 1000); + } + + return g_strdup (str); +} + +/* Returns a RFC2616 compliant languages list from system locales */ +gchar * +sokoke_accept_languages (const gchar* const * lang_names) +{ + GArray *langs_garray = NULL; + char *cur_lang = NULL; + char *prev_lang = NULL; + char **langs_array; + char *langs_str; + float delta; + int i, n_lang_names; + + /* Calculate delta for setting the quality values */ + n_lang_names = g_strv_length ((gchar **)lang_names); + delta = 0.999 / (n_lang_names - 1); + + /* Build the array of languages */ + langs_garray = g_array_new (TRUE, FALSE, sizeof (char*)); + for (i = 0; lang_names[i] != NULL; i++) + { + cur_lang = sokoke_posix_lang_to_rfc2616 (lang_names[i]); + + /* Apart from getting a valid RFC2616 compliant + language, also get rid of extra variants */ + if (cur_lang && (!prev_lang || + (!strcmp (prev_lang, cur_lang) || !strstr (prev_lang, cur_lang)))) + { + + gchar *qv_lang = NULL; + + /* Save reference for further comparison */ + prev_lang = cur_lang; + + /* Add the quality value and append it */ + qv_lang = sokoke_add_quality_value (cur_lang, 1 - i * delta); + g_array_append_val (langs_garray, qv_lang); + } + } + + /* Fallback: add "en" if list is empty */ + if (langs_garray->len == 0) + { + gchar* fallback = g_strdup ("en"); + g_array_append_val (langs_garray, fallback); + } + + langs_array = (char **) g_array_free (langs_garray, FALSE); + langs_str = g_strjoinv (", ", langs_array); + + return langs_str; +} diff --git a/midori/sokoke.h b/midori/sokoke.h index 621ca999..1960650e 100644 --- a/midori/sokoke.h +++ b/midori/sokoke.h @@ -204,4 +204,7 @@ sokoke_file_chooser_dialog_new (const gchar* title, gboolean sokoke_prefetch_uri (const char* uri); +gchar * +sokoke_accept_languages (const gchar* const * lang_names); + #endif /* !__SOKOKE_H__ */