lonr

Hello, world!
avatar

Profile

过期茴香豆三粒

远古时期有三则的关于 HTML 的讨论:

都 HTML 有点关系(而且一样的看起来简单,实际很复杂,最终不了了之,纯浪费时间),这篇整理下自己之前写的见解。

简中 HTML lang 属性到底应该选择什么?

TL;DR:为了兼容性 zhzh-CN 没毛病,zh-Hans 更精确一点,这也是 W3C i18n 简中主页的选择。

规范、tag 注册表、和查找工具

语言标签的规范是 RFC 5646: Tags for Identifying Languages (also known as BCP 47)

规范比较长,想了解规范可以阅读 W3C i18n 写的 Language tags in HTML and XML

规范的一部分是一个注册表——the IANA Language Sub-tag Registry.,其中包含了 sub tag 的定义。如果想知道如何从中选择 lang tag,可以看 W3C i18n 写的 Choosing a Language Tag。此外有一个 Subtag search tool 工具可以查找、验证 tag。

我们从 Registry 中挑选一些简中相关的 sub tag:

%%
Type: language
Subtag: zh
Description: Chinese
Added: 2005-10-16
Scope: macrolanguage
%%
Type: language
Subtag: cmn
Description: Mandarin Chinese
Added: 2009-07-29
Macrolanguage: zh
%%
Type: extlang
Subtag: cmn
Description: Mandarin Chinese
Added: 2009-07-29
Preferred-Value: cmn
Prefix: zh
Macrolanguage: zh
%%
Type: script
Subtag: Hans
Description: Han (Simplified variant)
Added: 2005-10-16
%%
Type: region
Subtag: CN
Description: China
Added: 2005-10-16
%%
Type: redundant
Tag: zh-Hans
Description: simplified Chinese
Added: 2003-05-30
%%
Type: redundant
Tag: zh-Hans-CN
Description: PRC Mainland Chinese in simplified script
Added: 2005-04-13
%%
Type: redundant
Tag: zh-cmn-Hans
Description: Mandarin Chinese (Simplified)
Added: 2005-07-15
Deprecated: 2009-07-29
Preferred-Value: cmn-Hans

在规范中 tag 由一堆 sub tag 组成:language-extlang-script-region-variant-extension-privateuse。例如:

  • zh-Hans 对应 language-script,表示“简体书写的中文”
  • zh-cmn-Hans-CN 对应 language-extlang-script-region

此外可以看到 cmn 既可以做 language 又可以做 extlang,例如:

  • cmn-hanscmnlanguage(The primary language subtag)
  • zh-cmn-hanszhlanguage(The primary language subtag),cmn 这次是 extlang(The extended language subtag)

从 Registry 中我们还可以观察到 zh-Hans 等组合 Typeredundantzh-cmn-Hans 还被标记为 Deprecated,后文会做解释。

另见:

为什么说知乎次高赞回答是错的

‘网页头部的声明应该是用 lang="zh" 还是 lang="zh-cn"?’次高赞回答给出的结论:

  • 用 “zh-CN” 准没错,万无一失。
    • 回答者以为这样做破坏了规范(大概被最高赞误导了),但其实 zh-CN 一直是合法的
    • 我赞同的是:为了兼容性这是最好的选择,因为规范中的精确的 tag 没有被浏览器完整实现。(但常见的 zh-Hans 在主流浏览器中可用)
  • 如果严格按照规范,必须用 cmn-Hans-CN;他的理由是 zh-Hans, zh-Hans-CN, zh-cmn 等都已被废弃

他从 Registry 中 zh-Hans, zh-Hans-CN, zh-cmn 等 tags 的 Typeredundant 得出了被废弃的结论,实际上 Deprecated 才是真正的“废弃”,Type: redundantPreferred-ValueType: grandfathered 和“废弃”没有必然联系。

Q:什么是废弃?

3.1.6. Deprecated Field

The field 'Deprecated' contains the date the record was deprecated
and MAY be added, changed, or removed from any record via the
maintenance process described in Section 3.3 or via the registration
process described in Section 3.5. Usually, the addition of a
'Deprecated' field is due to the action of one of the standards
bodies, such as ISO 3166, withdrawing a code. Although valid in
language tags, subtags and tags with a 'Deprecated' field are
deprecated, and validating processors SHOULD NOT generate these
subtags.
Note that a record that contains a 'Deprecated' field and
no corresponding 'Preferred-Value' field has no replacement mapping.

A:里面提到:加了 Deprecated 的 tag 表示“被废弃”。举例:

%%
Type: redundant
Tag: zh-cmn-Hans
Description: Mandarin Chinese (Simplified)
Added: 2005-07-15
Deprecated: 2009-07-29
Preferred-Value: cmn-Hans

Deprecated 表示 zh-cmn-Hans 是被废弃的。

结合其他部分可以得出的信息:

  • Deprecated 其后的日期不重要,因为可以估计,重要的是有没有这个字段。合理的推测是:“所有被废弃的 tags 都有 Deprecated 属性”
  • 被废弃不一定有 Preferred-Value 属性
  • 即使目前“被废弃”,后续规范也可以修改

Q:Type: redundantType: grandfathered 表示“废弃”吗?

A:不。见:

2.2.8. Grandfathered and Redundant Registrations

Prior to RFC 4646, whole language tags were registered according to
the rules in RFC 1766 and/or RFC 3066. All of these registered tags
remain valid as language tags.
Many of these registered tags were made redundant by the advent of
either RFC 4646 or this document. A redundant tag is a grandfathered
registration whose individual subtags appear with the same semantic
meaning in the registry. For example, the tag "zh-Hant" (Traditional
Chinese) can now be composed from the subtags 'zh' (Chinese) and
'Hant' (Han script traditional variant). These redundant tags are
maintained in the registry as records of type 'redundant', mostly as
a matter of historical curiosity.

如果我理解的没错:以 zh-Hant 为例,加 redundant 是说之前的注册表(a grandfathered registration)有这个 tag,直接解释为 Traditional Chinese,而在 RFC 4646 或当前规范中可以解释为由 sub tags zh (Chinese) and Hant (Han script traditional variant) 组合而成。而且规范说了 redundant 主要用来“考古的”。

规范的另一处

  1. The redundant and grandfathered entries together form the complete list of tags registered under [RFC3066]. The redundant tags are those previously registered tags that can now be formed using the subtags defined in the registry. The grandfathered entries include those that can never be legal because they are 'irregular' (that is, they do not match the 'langtag' production in Figure 1), are limited by rule (subtags such as 'nyn' and 'min' look like the extlang production, but cannot be registered as extended language subtags), or their subtags are inappropriate for registration. All of the grandfathered tags are listed in either the 'regular' or the 'irregular' productions in the ABNF. Under [RFC4646] it was possible for grandfathered tags to become redundant. However, all of the tags for which this was possible became redundant before this document was produced. So the set of redundant and grandfathered tags is now permanent and immutable: new entries of either type MUST NOT be added and existing entries MUST NOT be removed. The decision-making process about which tags were initially grandfathered and which were made redundant is described in [RFC4645]. Many of the grandfathered tags are deprecated -- indeed, they were deprecated even before [RFC4646]. For example, the tag "art-lojban" was deprecated in favor of the primary language subtag 'jbo'. These tags could have been made 'redundant' by registering some of their subtags as 'variants'. The 'variant- like' subtags in the grandfathered registrations SHALL NOT be registered in the future, even with a similar or identical meaning.

从其中

The redundant tags are those previously registered tags that can now be formed using the subtags defined in the registry. The grandfathered entries include those that can never be legal because they are 'irregular'

可以确信 redundant 就是现在的规范重新定义了相同的 tagsgrandfathered 表示不合法的,例如 zh-min

Many of the grandfathered tags are deprecated

推测即使 grandfathered 也不一定是 deprecated(真正的“废弃”)。可以找到一个例子:

%%
Type: grandfathered
Tag: i-default
Description: Default Language
Added: 1998-03-10

为什么说知乎最高赞回答是错的

‘网页头部的声明应该是用 lang="zh" 还是 lang="zh-cn"?’这个讨论中最高赞给出的最重要的两个结论: 1. 规范中 zhzh-CN 被废弃 2. 回答推荐使用 zh-cmn-Hans,但貌似是有问题的。

TL;DR:1. zhzh-CN 符合规范,并没有被废弃 2. 回答推荐的 zh-cmn-Hans 倒是被废弃的

zh 是否被弃用?

在 Registry 中,zh 并没有被标记 deprecated(或 redundantgrandfathered)。相反,在规范中:

The language subtag 'zh' can still be used without an extended language subtag to label a resource as some unspecified variety of Chinese, while the primary language subtag ('gan', 'yue', 'cmn') is preferred to using the extended language form ("zh-gan", "zh-yue", "zh-cmn"). --- BCP 47

在规范中,zhMacrolanguage,作为中文的统称。按照这样的逻辑,举两个例子:

  • zh 包含所有中文方言,假如有未分类的中文方言(比如?),也就是说没有自己 sub tag,这种情况只能使用 zh
  • 有不需要强调具体方言的场景:我发布一篇中文博客,并不需要强调我的内部言语(Inner Speech)使用的是普通话,或者设定读者的 UA 使用普通话朗读

规范中也只是说说 cmn 优于 zh-cmn,“不推荐”都没提吧?结论是自然是没有被弃用。

zh-CN 是否被弃用?

在规范和 Registry 均没有搜索到 zh-CN 相关内容,但毕竟只是在 zh 后加了 region,何罪有之?

推荐 zh-cmn-Hans

回答者应该是综合考虑了规范里的新标签和 zh 带来的兼容性,殊不知在 Registry 中:

%%
Type: redundant
Tag: zh-cmn-Hans
Description: Mandarin Chinese (Simplified)
Added: 2005-07-15
Deprecated: 2009-07-29
Preferred-Value: cmn-Hans

zh-cmn-Hans 才是真正的“被废弃”的 tag。不清楚具体为什么废弃,个人猜想逻辑大概是:

  • 如果为了兼容,可以选择 zhzh-CNzh-Hans
  • 如果为了符合规范而且精准,使用 cmn-Hans。如果应用能识别 cmn,那么 zh 显然是多余的

zh-cmn-Hans 不可以使用?(我是很实用主义的,觉得问题不大,只是比较尴尬)

为什么答案是 zh-Hans

There are many Chinese dialects, often mutually unintelligible, but these dialects are all written using either Simplified or Traditional Chinese script. People typically want to label Chinese text as either Simplified or Traditional, but until recently there was no way to do so. People had to bend something like zh-CN (meaning Chinese as spoken in China) to mean Simplified Chinese, even in Singapore, and zh-TW (meaning Chinese as spoken in Taiwan) for Traditional Chinese. (Other people, however, use zh-HK for Traditional Chinese.) The availability of zh-Hans and zh-Hant for Chinese written in Simplified and Traditional scripts should improve consistency and accuracy, and is already becoming widely used, although of course you may need to continue to use the old language tags in some cases for consistency.
-- W3C i18n - Language tags in HTML and XML

  • 在没有 zh-HansHans 是 The script subtag)之前,简中网页需要使用 zh-CN(中国地区使用的中文)来表示简体中文
  • 现在可以使用 zh-Hans 表示“中文(简体)”
    • 相对的:cmn-Hans 从语义上应该对应“普通话(简体)”。一点观点,不一定对

兼容性

维基百科使用 zh-Hans-CNlzhyue-Hant 分别表示“大陆简体”、“文言”、“粤语”。zh-Hans-CN 应该没有什么大问题。

维基百科曾经在首页使用了 mul

%%
Type: language
Subtag: mul
Description: Multiple languages
Added: 2005-10-16
Scope: special

Linux 平台中,Fontconfig 的设计是浏览器(或其他应用)可以传入 lang 参数请求的,但是 Fontconfig 默认、Fedora vendored、第三方字体附带的配置文件中都没有处理 zh-Hans。显然从这个功能来说,浏览器实现自己的逻辑是合理的。

<match>
  <test name="lang">
    <string>zh-cn</string>
  </test>
  <test name="family">
    <string>monospace</string>
  </test>
  <edit name="family" mode="prepend">
    <string>Noto Sans Mono CJK SC</string>
  </edit>
</match>

如何优雅地定义 font-family?

知乎问题“如何优雅地定义 font-family?”最高赞的总结挺不错。

对于默认字体,我的个人博客选择使用 font-family: system-ui;,让用户来决定使用什么字体,甚至对使用 serif 还是 sans-serif 也没有偏好,因为字体不是我设计的一部分(我的博客没有设计,全是模仿)。(本来我还设置一个 sans-serif 的,直到“发现”还有人喜欢使用 serif 字体)

类似的,好多网站现在优先使用系统 UI 的字体,比如 GitHub Primer 的默认格式是 -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",在苹果设备下应该会使用系统默认字体。

字体族 system-uiui-sans-serifui-serifui-monorepoui-rounded

一个自然的问题是:ui-sans-serifsans-serif 有什么区别?我理解的是:ui-sans-serif 对应系统 UI 的无衬线字体;sans-serif 对应的是用户浏览器字体选项中设置的无衬线字体。类似的:system-ui 对应系统 UI 的默认字体;网页没有设置字体对应用户浏览器字体选项中设置的默认字体。

因为字体族(包括 sans-serifserifsystem-ui)都可以在 Fontconfig 配置中定义(浏览器有没有自己的逻辑我就不清楚了), 在 Fontconfig 默认配置中有定义,所以之前我在 Fontconfig 的用户配置里添加了一些 ui-* 字体族。

注意到可以使用字体选择控件选择 sans-serifserifsystem-ui,但是无法选择自定义的 ui-* 不知道为何。

看到 MDN 的中文页面没有 ui-*,所以忍不住去混了一个 PR。发现文档系统 mdn/yari 使用了 remark,按理说可以做一些格式化、linting 或者脚本批量修改的工作

发现除了 system-ui,其他的 ui-* 字体族只有 Safari 支持

知乎引号之争

知乎应如何尊重用户对弯引号的使用习惯?公共编辑区域应该使用哪种引号?,争论的原因:

  • 对于一些符号(逗号,句号……),中文有区别于英文的字符 ();:,。?—,但对于引号 ‘’“”,这四个字符和英文是共用的
    • 省略号 ……(U+2026)其实也是中英文共用的,英文用户反而不容易打出来
    • 另外有 〝〞(U+301D、U+301E)这两种引号,
  • 设置 font-family: Arial, 'Noto Sans CJK SC'; 后,对于一个字符,会先尝试使用 Arial 字体渲染,如果 Arial 字体中不支持这个字符,则使用 Noto Sans CJK SC 字体渲染
  • 英文字体(比如 Arial)中一般没有中文的逗号和句号(,。),所以这两个字符由 Noto Sans CJK SC 渲染
  • 但对于引号 “”‘’,会使用 Arial 字体中的引号(为英文设计的)而不是更符合中文排版的 Noto Sans CJK SC 的引号
    • Noto Sans CJK SC 中也有英文的字体,为什么不直接使用中文字体渲染英文字母呢?这确实是个问题,但比想象的复杂
    • 发现有人做了一个引号之神,fork 了 vinta/pangu.js

其实 Fontconfig 是可以配置对于指定字符使用指定字体的,但 Chrome 并不理会这些配置

另见: