{"thread":{"id":"37110","subject":"[PATCH v2] http: Add Accept-Language header if possible","startedAt":"2014-07-11T09:46:29Z","lastAt":"2014-07-11T09:46:29Z","messageCount":1,"participants":["Yi EungJun"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"245772","messageId":"1405071989-7682-1-git-send-email-eungjun.yi@navercorp.com","threadId":"37110","inReplyTo":null,"subject":"[PATCH v2] http: Add Accept-Language header if possible","fromName":"Yi EungJun","fromEmail":"semtlenori@gmail.com","sentAt":"2014-07-11T09:46:29Z","receivedAt":"2014-07-11T09:46:29Z","isPatch":true,"sender":{"key":"semtlenori@gmail.com","avatar":"https://gravatar.com/avatar/8363435d2badb3450df0dd7c4ec2113f6e1d62c44dd3cf7dfe83c5a389b8a9bf?d=mp&s=160"},"body":"From: Yi EungJun <eungjun.yi@navercorp.com>\n\nAdd an Accept-Language header which indicates the user's preferred\nlanguages defined by $LANGUAGE, $LC_ALL, $LC_MESSAGES and $LANG.\n\nExamples:\n  LANGUAGE= -> \"\"\n  LANGUAGE=ko:en -> \"Accept-Language: ko, en; q=0.9, *; q=0.1\"\n  LANGUAGE=ko LANG=en_US.UTF-8 -> \"Accept-Language: ko, *; q=0.1\"\n  LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n\nThis gives git servers a chance to display remote error messages in\nthe user's preferred language.\n\nSigned-off-by: Yi EungJun <eungjun.yi@navercorp.com>\n---\n http.c                     | 125 +++++++++++++++++++++++++++++++++++++++++++++\n remote-curl.c              |   2 +\n t/t5550-http-fetch-dumb.sh |  21 ++++++++\n 3 files changed, 148 insertions(+)\n\ndiff --git a/http.c b/http.c\nindex 3a28b21..a20f3e2 100644\n--- a/http.c\n+++ b/http.c\n@@ -983,6 +983,129 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,\n \t\tstrbuf_addstr(charset, \"ISO-8859-1\");\n }\n \n+/*\n+ * Guess the user's preferred languages from the value in LANGUAGE environment\n+ * variable and LC_MESSAGES locale category.\n+ *\n+ * The result can be a colon-separated list like \"ko:ja:en\".\n+ */\n+static const char* get_preferred_languages() {\n+    const char* retval;\n+\n+\tretval = getenv(\"LANGUAGE\");\n+\tif (retval != NULL && retval[0] != '\\0')\n+\t\treturn retval;\n+\n+\tretval = setlocale(LC_MESSAGES, NULL);\n+\tif (retval != NULL && retval[0] != '\\0'\n+\t\t&& strcmp(retval, \"C\") != 0\n+\t\t&& strcmp(retval, \"POSIX\") != 0)\n+\t\treturn retval;\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ * Add an Accept-Language header which indicates user's preferred languages.\n+ *\n+ * Examples:\n+ *   LANGUAGE= -> \"\"\n+ *   LANGUAGE=ko:en -> \"Accept-Language: ko, en; q=0.9, *; q=0.1\"\n+ *   LANGUAGE=ko_KR.UTF-8:sr@latin -> \"Accept-Language: ko-KR, sr; q=0.9, *; q=0.1\"\n+ *   LANGUAGE=ko LANG=en_US.UTF-8 -> \"Accept-Language: ko, *; q=0.1\"\n+ *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n+ *   LANGUAGE= LANG=C -> \"\"\n+ */\n+static struct curl_slist* add_accept_language(struct curl_slist *headers)\n+{\n+\tconst char *p1, *p2, *p3;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tfloat q = 1.0;\n+\tfloat q_precision = 0.1;\n+\tint num_langs = 1;\n+\tchar* q_format = \"; q=%.1f\";\n+\n+\tp1 = get_preferred_languages();\n+\n+\t/* Don't add Accept-Language header if no language is preferred. */\n+\tif (p1 == NULL || p1[0] == '\\0') {\n+\t\tstrbuf_release(&buf);\n+\t\treturn headers;\n+\t}\n+\n+\t/* Count number of preferred languages to decide precision of q-factor */\n+\tfor (p3 = p1; *p3 != '\\0'; p3++) {\n+\t\tif (*p3 == ':') {\n+\t\t\tnum_langs++;\n+\t\t}\n+\t}\n+\n+\t/* Decide the precision for q-factor on number of preferred languages. */\n+\tif (num_langs + 1 > 100) { /* +1 is for '*' */\n+\t\tq_precision = 0.001;\n+\t\tq_format = \"; q=%.3f\";\n+\t} else if (num_langs + 1 > 10) { /* +1 is for '*' */\n+\t\tq_precision = 0.01;\n+\t\tq_format = \"; q=%.2f\";\n+\t}\n+\n+\tstrbuf_addstr(&buf, \"Accept-Language: \");\n+\n+\tfor (p2 = p1; q > q_precision; p2++) {\n+\t\tif ((*p2 == ':' || *p2 == '\\0') && p1 != p2) {\n+\t\t\tif (q < 1.0) {\n+\t\t\t\tstrbuf_addstr(&buf, \", \");\n+\t\t\t}\n+\n+\t\t\tfor (p3 = p1; p3 < p2; p3++) {\n+\t\t\t\t/* Replace '_' with '-'. */\n+\t\t\t\tif (*p3 == '_') {\n+\t\t\t\t\tstrbuf_add(&buf, p1, p3 - p1);\n+\t\t\t\t\tstrbuf_addstr(&buf, \"-\");\n+\t\t\t\t\tp1 = p3 + 1;\n+\t\t\t\t}\n+\n+\t\t\t\t/* Chop off anything after '.' or '@'. */\n+\t\t\t\tif ((*p3 == '.' || *p3 == '@')) {\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (p3 > p1) {\n+\t\t\t\tstrbuf_add(&buf, p1, p3 - p1);\n+\t\t\t}\n+\n+\t\t\t/* Put the q factor if only it is less than 1.0. */\n+\t\t\tif (q < 1.0) {\n+\t\t\t\tstrbuf_addf(&buf, q_format, q);\n+\t\t\t}\n+\n+\t\t\tq -= q_precision;\n+\t\t\tp1 = p2 + 1;\n+\n+\t\t\tif (*p2 == '\\0') {\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Don't add Accept-Language header if no language is preferred. */\n+\tif (q >= 1.0) {\n+\t\tstrbuf_release(&buf);\n+\t\treturn headers;\n+\t}\n+\n+\t/* Add '*' with minimum q-factor greater than 0.0. */\n+\tstrbuf_addstr(&buf, \", *\");\n+\tstrbuf_addf(&buf, q_format, q_precision);\n+\n+\theaders = curl_slist_append(headers, buf.buf);\n+\n+\tstrbuf_release(&buf);\n+\n+\treturn headers;\n+}\n+\n /* http_request() targets */\n #define HTTP_REQUEST_STRBUF\t0\n #define HTTP_REQUEST_FILE\t1\n@@ -1020,6 +1143,8 @@ static int http_request(const char *url,\n \t\t\t\t\t fwrite_buffer);\n \t}\n \n+\theaders = add_accept_language(headers);\n+\n \tstrbuf_addstr(&buf, \"Pragma:\");\n \tif (options && options->no_cache)\n \t\tstrbuf_addstr(&buf, \" no-cache\");\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 4493b38..07f2a5d 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -946,6 +946,8 @@ int main(int argc, const char **argv)\n \tstruct strbuf buf = STRBUF_INIT;\n \tint nongit;\n \n+\tgit_setup_gettext();\n+\n \tgit_extract_argv0_path(argv[0]);\n \tsetup_git_directory_gently(&nongit);\n \tif (argc < 2) {\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex ac71418..687baf9 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -196,5 +196,26 @@ test_expect_success 'reencoding is robust to whitespace oddities' '\n \tgrep \"this is the error message\" stderr\n '\n \n+test_expect_success 'git client sends Accept-Language based on LANGUAGE, LC_ALL, LC_MESSAGES and LANG' '\n+\ttest_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 &&\n+\tgrep \"^Accept-Language: ko-KR, \\*; q=0.1\" stderr &&\n+\ttest_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 &&\n+\tgrep \"^Accept-Language: de-DE, \\*; q=0.1\" stderr &&\n+\ttest_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 &&\n+\tgrep \"^Accept-Language: ja-JP, \\*; q=0.1\" stderr &&\n+\ttest_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 &&\n+\tgrep \"^Accept-Language: en-US, \\*; q=0.1\" stderr\n+'\n+\n+test_expect_success 'git client sends Accept-Language with many preferred languages' '\n+\ttest_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 &&\n+\tgrep \"^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\n+'\n+\n+test_expect_success 'git client does not send Accept-Language' '\n+\ttest_must_fail env GIT_CURL_VERBOSE=1 LANGUAGE= git clone \"$HTTPD_URL/accept/language\" 2>stderr &&\n+\t! grep \"^Accept-Language:\" stderr\n+'\n+\n stop_httpd\n test_done\n-- \n2.0.1.473.g4b69a73.dirty\n"}]}