📘 第六章 HTTP首部
📄 一句话概括

Content-Language 说明正文是哪种语言,不是「客户端想要哪种」。 它是描述,不是协商结果本身。

📝 本节和前面几节的关系

上一节讲正文被怎么压,这一节讲正文用哪种语言写。它和 6.4.4 的 Accept-Language 正好一问一答。

一、它说的是「实际是什么」

Accept-Language: en 是客户端的愿望;Content-Language: en 是服务端交出的答案。 两个方向,别搞反。 Demo 里五组请求:要 en 给 en(32 字节),要 ja 给 ja(54 字节),要 zh 给 zh(27 字节); 要 fr 服务端没有,退到默认的中文;干脆不发,也退到中文。 五组的 Content-Language 都如实反映了正文的实际语言——包括那两组「客户端要的不是这个」的情况。 这就是「描述」的含义:说真话,而不是说对方爱听的。

二、为什么中文字节数最多还是最少

有意思的是那三个字节数:en 32、ja 54、zh 27。 中文句子短、日文假名多、英文单词长,加上 UTF-8 里汉字占三字节、假名也占三字节,结果就是这样。 这提醒一件事:Content-Language 的长度和「内容多少」无关。 它只说语言,不描述体量——体量是 Content-Length 的事。 另外别拿字节数反推语言。 判断语言要靠这个首部,靠字节猜是猜不准的(UTF-8 里 ASCII 和汉字的比例随时会变)。

三、它可以写多个

Content-Language: zh-CN, en 表示正文里两种语言都有。产品说明书、双语公告就是这样。 值用的是语言标签(BCP 47):zh、zh-CN、en-US、ja。主标签必填,子标签可选,用连字符分隔。 写错标签不会报错,但会让缓存和搜索引擎判错。zh_CN(下划线)是错的,必须是 zh-CN。

四、动手 Demo:五组 Accept-Language,看 Content-Language 怎么答

服务端准备中文、英文、日文三个版本,按客户端点的语言挑一个, 并在响应里如实写出正文实际用的是哪种语言。

docs = {u'zh': ZH_BYTES, u'en': EN_BYTES, u'ja': JA_BYTES}

def fn(raw, st):
    want = hd.get(u'accept-language', u'')
    pick = u'zh'                       # 默认语言
    for tag in (u'en', u'ja', u'zh'):
        if tag in want:
            pick = tag
            break
    return X.reply(u'HTTP/1.1 200 OK',
                   [(u'Content-Language', pick)], docs[pick])

实跑输出:

--------------------------------------------------------------------
Content-Language:正文是哪国话
--------------------------------------------------------------------
  Accept-Language      Content-Language     字节   正文开头
  en                   en                   32     This p
  ja                   ja                   54     このページは
  zh                   zh                   27     这一页是中文
  fr                   zh                   27     这一页是中文
  (不发)             zh                   27     这一页是中文

  最后两行是重点:要法语(服务端没有)和干脆不发,都退到默认的中文版本。
  所以 Content-Language 的作用不是「告诉客户端该用哪种语言」,
  而是「告诉客户端这份正文究竟是哪种语言」——它是描述,不是协商结果本身。

  这点和 Accept-Language 正好一对:一个问,一个答。
  另外 Content-Language 允许写多个,比如 `zh-CN, en`,表示正文里两种语言都有。

前三条要什么给什么,Content-Language 与请求对得上:en 32 字节、ja 54 字节、zh 27 字节。 第四条要法语,服务端没有,退到中文,但 Content-Language 老实写 zh。 第五条干脆不发 Accept-Language,结果一样。 这两行是整个 Demo 的重点:客户端要什么,和正文是什么,是两件事。 服务端不撒谎,客户端才能正确决定要不要自己翻译。 三个字节数的差也不是「内容多少」,只是句子长短加编码宽度的结果。

30 秒实验

  1. 把 Accept-Language 写成 fr, zh;q=0.1,看服务端会不会挑到中文。
  2. 给响应加 Content-Language: zh-CN,看字节数变化。
  3. 想一想:如果正文里中英文混排,这个首部该怎么写?

常见误区

  1. 「Content-Language 表示客户端想要的语言」——那是 Accept-Language。
  2. 「没有对应语言就该报错」——通常退到默认版本。
  3. 「zh_CN 和 zh-CN 一样」——语言标签必须用连字符。
自测三问(点开对答案) 1. `Content-Language` 和 `Accept-Language` 的关系是什么? —— 一个问、一个答;前者描述实际语言,后者表达偏好。 2. 客户端要法语但服务端只有中文,响应该怎么写? —— 给中文版本,`Content-Language: zh` 如实写,不撒谎。 3. 语言标签能不能用下划线? —— 不能,必须用连字符,比如 `zh-CN`。

语言是「内容层面的描述」。接下来这个字段是实体首部里最硬的一个:Content-Length。

相关笔记