[PATCH v3] http: Add Accept-Language header if possible

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add an Accept-Language header which indicates the user's preferred
languages defined by $LANGUAGE, $LC_ALL, $LC_MESSAGES and $LANG.

Examples:
  LANGUAGE= -> ""
  LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
  LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
  LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"

This gives git servers a chance to display remote error messages in
the user's preferred language.

Signed-off-by: Yi EungJun <eungjun.yi@xxxxxxxxxxxxx>
---
 http.c                     | 125 +++++++++++++++++++++++++++++++++++++++++++++
 remote-curl.c              |   2 +
 t/t5550-http-fetch-dumb.sh |  21 ++++++++
 3 files changed, 148 insertions(+)

diff --git a/http.c b/http.c
index 3a28b21..a20f3e2 100644
--- a/http.c
+++ b/http.c
@@ -983,6 +983,129 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,
 		strbuf_addstr(charset, "ISO-8859-1");
 }
 
+/*
+ * Guess the user's preferred languages from the value in LANGUAGE environment
+ * variable and LC_MESSAGES locale category.
+ *
+ * The result can be a colon-separated list like "ko:ja:en".
+ */
+static const char* get_preferred_languages() {
+    const char* retval;
+
+	retval = getenv("LANGUAGE");
+	if (retval != NULL && retval[0] != '\0')
+		return retval;
+
+	retval = setlocale(LC_MESSAGES, NULL);
+	if (retval != NULL && retval[0] != '\0'
+		&& strcmp(retval, "C") != 0
+		&& strcmp(retval, "POSIX") != 0)
+		return retval;
+
+	return NULL;
+}
+
+/*
+ * Add an Accept-Language header which indicates user's preferred languages.
+ *
+ * Examples:
+ *   LANGUAGE= -> ""
+ *   LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
+ *   LANGUAGE=ko_KR.UTF-8:sr@latin -> "Accept-Language: ko-KR, sr; q=0.9, *; q=0.1"
+ *   LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
+ *   LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
+ *   LANGUAGE= LANG=C -> ""
+ */
+static struct curl_slist* add_accept_language(struct curl_slist *headers)
+{
+	const char *p1, *p2, *p3;
+	struct strbuf buf = STRBUF_INIT;
+	float q = 1.0;
+	float q_precision = 0.1;
+	int num_langs = 1;
+	char* q_format = "; q=%.1f";
+
+	p1 = get_preferred_languages();
+
+	/* Don't add Accept-Language header if no language is preferred. */
+	if (p1 == NULL || p1[0] == '\0') {
+		strbuf_release(&buf);
+		return headers;
+	}
+
+	/* Count number of preferred languages to decide precision of q-factor */
+	for (p3 = p1; *p3 != '\0'; p3++) {
+		if (*p3 == ':') {
+			num_langs++;
+		}
+	}
+
+	/* Decide the precision for q-factor on number of preferred languages. */
+	if (num_langs + 1 > 100) { /* +1 is for '*' */
+		q_precision = 0.001;
+		q_format = "; q=%.3f";
+	} else if (num_langs + 1 > 10) { /* +1 is for '*' */
+		q_precision = 0.01;
+		q_format = "; q=%.2f";
+	}
+
+	strbuf_addstr(&buf, "Accept-Language: ");
+
+	for (p2 = p1; q > q_precision; p2++) {
+		if ((*p2 == ':' || *p2 == '\0') && p1 != p2) {
+			if (q < 1.0) {
+				strbuf_addstr(&buf, ", ");
+			}
+
+			for (p3 = p1; p3 < p2; p3++) {
+				/* Replace '_' with '-'. */
+				if (*p3 == '_') {
+					strbuf_add(&buf, p1, p3 - p1);
+					strbuf_addstr(&buf, "-");
+					p1 = p3 + 1;
+				}
+
+				/* Chop off anything after '.' or '@'. */
+				if ((*p3 == '.' || *p3 == '@')) {
+					break;
+				}
+			}
+
+			if (p3 > p1) {
+				strbuf_add(&buf, p1, p3 - p1);
+			}
+
+			/* Put the q factor if only it is less than 1.0. */
+			if (q < 1.0) {
+				strbuf_addf(&buf, q_format, q);
+			}
+
+			q -= q_precision;
+			p1 = p2 + 1;
+
+			if (*p2 == '\0') {
+				break;
+			}
+		}
+	}
+
+	/* Don't add Accept-Language header if no language is preferred. */
+	if (q >= 1.0) {
+		strbuf_release(&buf);
+		return headers;
+	}
+
+	/* Add '*' with minimum q-factor greater than 0.0. */
+	strbuf_addstr(&buf, ", *");
+	strbuf_addf(&buf, q_format, q_precision);
+
+	headers = curl_slist_append(headers, buf.buf);
+
+	strbuf_release(&buf);
+
+	return headers;
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF	0
 #define HTTP_REQUEST_FILE	1
@@ -1020,6 +1143,8 @@ static int http_request(const char *url,
 					 fwrite_buffer);
 	}
 
+	headers = add_accept_language(headers);
+
 	strbuf_addstr(&buf, "Pragma:");
 	if (options && options->no_cache)
 		strbuf_addstr(&buf, " no-cache");
diff --git a/remote-curl.c b/remote-curl.c
index 4493b38..07f2a5d 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -946,6 +946,8 @@ int main(int argc, const char **argv)
 	struct strbuf buf = STRBUF_INIT;
 	int nongit;
 
+	git_setup_gettext();
+
 	git_extract_argv0_path(argv[0]);
 	setup_git_directory_gently(&nongit);
 	if (argc < 2) {
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index ac71418..2cc41d2 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -196,5 +196,26 @@ test_expect_success 'reencoding is robust to whitespace oddities' '
 	grep "this is the error message" stderr
 '
 
+test_expect_success 'git client sends Accept-Language based on LANGUAGE, LC_ALL, LC_MESSAGES and LANG' '
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE=ko_KR.UTF-8 LC_ALL=de_DE.UTF-8 LC_MESSAGES=ja_JP.UTF-8 LANG=en_US.UTF-8 git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	grep "^Accept-Language: ko-KR, \\*; q=0.1" stderr &&
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE= LC_ALL=de_DE.UTF-8 LC_MESSAGES=ja_JP.UTF-8 LANG=en_US.UTF-8 git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	grep "^Accept-Language: de-DE, \\*; q=0.1" stderr &&
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE= LC_ALL= LC_MESSAGES=ja_JP.UTF-8 LANG=en_US.UTF-8 git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	grep "^Accept-Language: ja-JP, \\*; q=0.1" stderr &&
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE= LC_ALL= LC_MESSAGES= LANG=en_US.UTF-8 git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	grep "^Accept-Language: en-US, \\*; q=0.1" stderr
+'
+
+test_expect_success 'git client sends Accept-Language with many preferred languages' '
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE=ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	grep "^Accept-Language: ko-KR, en-US; q=0.99, fr-CA; q=0.98, de; q=0.97, sr; q=0.96, ja; q=0.95, zh; q=0.94, sv; q=0.93, pt; q=0.92, nb; q=0.91, \\*; q=0.01" stderr
+'
+
+test_expect_success 'git client does not send Accept-Language' '
+	test_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE= git clone "$HTTPD_URL/accept/language" 2>stderr &&
+	! grep "^Accept-Language:" stderr
+'
+
 stop_httpd
 test_done
-- 
2.0.1.473.g4b69a73.dirty

The only difference from the patch v2 is \-escape in the tests. Thanks to Eric Sunshine.
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]