事情的经过是这样的。前段时间,看到Deno官网改版了: 但是当我习惯性点击『谷歌翻译』为中文简体时,发现页面不对劲,怎么是繁体字呢: 当时以为找到了『谷歌翻译』的Bug,后来看到其它网页的翻译都正常,就知道自己想多了。
下来问题来了,Deno官网是怎么做到的(相信它不是故意的)呢?
追凶
在网络里可以看到,点击了翻译后,主要是这个接口请求: 从参数上看,传递的是对的,zh-CN
是中文简体: 响应也没毛病,全是简体: 那么问题只能出在页面展示上了,我第一反应是字体引起的,是不是Deno用了什么特殊的字体呢?在网络里只看到这两位: 从预览上看也不是中文字体,没什么特殊的: 好在Deno的官网是开源的,到GitHub上找到项目地址github.com/denoland/de...,拉到本地。看package.json中的配置项,发现这是一个Node.js的项目,用的是docusaurus
框架: 好吧,意料之中,就像我的deno_nest
文档用Umi一样,都是图省事。然后使用.github/workflows/deploy.yml
配置了GitHub流水线,将打包后的产物(build目录)使用Deno Deploy平台部署:
yaml
name: Deploy
on:
push:
branches: main
pull_request:
branches: main
jobs:
deploy:
name: Deploy
runs-on: ubuntu-22.04-xl
permissions:
id-token: write # Needed for auth with Deno Deploy
contents: read # Needed to clone the repository
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: lts/*
- name: Build step
run: npm install && npm run build
- name: Upload to Deno Deploy
uses: denoland/deployctl@v1
with:
project: "deno-docs"
entrypoint: "server.ts"
root: "build"
扯远了。
我们接着探案。
将项目启动之后,在本地访问http://localhost:3000/
,再点谷歌翻译,效果是一样的: 我尝试着搜索了下woff文件,就两个文件里有: 但将这些字体内容都注释掉后,没有任何变化,说明这个猜测是错误的。那就有些懵圈了,还能有什么原因呢?
好在我能确定的一点是,之前Deno官网的翻译是绝对没有问题的,于是使用『二分法』,在Git记录中先找到23年8月的某个版本,运行了一下,看到翻译是OK的,再一步步缩减范围,最终找到了罪魁祸首,是12月5号的一个提交:
这个提交里,有我们刚才搜索到的index.js
和inter.css
文件。这又说明,与字体还是有关联的?
这是index.js
的部分代码,其实就是核心布局。
jsx
export default function Layout(props) {
const {
children,
noFooter,
wrapperClassName,
// Not really layout-related, but kept for convenience/retro-compatibility
title,
description,
} = props;
useKeyboardNavigation();
return (
<LayoutProvider>
<Head>
<link
rel="preload"
href="/fonts/inter/Inter-Regular.woff2"
as="font"
type="font/woff2"
crossOrigin="true"
/>
<link
rel="preload"
href="/fonts/inter/Inter-SemiBold.woff2"
as="font"
type="font/woff2"
crossOrigin="true"
/>
<script src="/trackjs.js"></script>
<script src="https://cdn.trackjs.com/releases/current/tracker.js"></script>
<link rel="stylesheet" href="/fonts/inter.css" />
</Head>
...
</LayoutProvider>
);
}
inter.css
的内容如下:
css
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/inter/Inter-Regular.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("/fonts/inter/Inter-Italic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/fonts/inter/Inter-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("/fonts/inter/Inter-SemiBoldItalic.woff2") format("woff2");
}
:root {
font-feature-settings: "cv05" on, "cv08" on, "cv10" on, "ss01" on, "ss08" on;
}
之前测试时,仅注释了index.js
中两个字体文件的link,和CSS文件中这几个font-face
。这次把漏网之鱼:root
也注释掉,发现居然正常了。
探底
这一行代码有什么魔力呢? 我写了一个最简单的页面进行测试,经过排查,发现与font-feature-settings
配置的cv10
有关。
html
<!DOCTYPE html>
<html>
<head>
<style>
:root {
font-feature-settings: "cv10" on;
}
</style>
</head>
<body>
<h2>我是中国人</h2>
</body>
</html>
可这玩意儿是什么呢?遇事不决问GPT:
Q:font-feature-settings的配置项"cv10 on"代表什么意思?它为什么将网页中本身的中文简体显示为繁体?
A:"cv10 on" 是 OpenType 字体特性设置的一种形式,它表示启用了 Contextual Variant Feature(上下文变体特性)的第10个变体。这通常用于处理字形的上下文相关变体,例如在特定语境中显示不同的字形形状。
关于为什么在网页中会将中文简体显示为繁体,这可能是因为网页作者使用了特定的字体特性设置,例如"cv10 on",以实现一些特定的设计效果。如果您在浏览网页时遇到这种情况,可以尝试联系网页作者或者检查网页的样式表和字体设置,看是否有类似的特性被应用。
另外,浏览器也可能会根据系统和用户首选项来进行字体替代,所以检查浏览器设置也是一个解决问题的途径。
好吧,这段回答中,可能有用的是,我们知道了它与字体特性有关。 MDN也这么说:
font-feature-settings
属性用于控制 OpenType 字体中的高级印刷功能。
在Google上以这几个关键字搜索时,找到一篇文章,是讲移动端开发的。
什么是 FontFeature? 简单来说就是影响字体形状的一个属性 ,在前端的对应领域里应该是 font-feature-settings,它有别于 FontFamily ,是用于指定字体内字的形状参数。
在iOS的段落里找到一部分: 我冒出一个想法,这个问题搞不好可能只有iOS上才有。于是修改了下HTML的代码(放在了CDN上,有兴趣的朋友可以点击验证下):
html
<!DOCTYPE html>
<html>
<head>
<title>font feature settings</title>
<style>
h2 {
font-feature-settings: "cv10" on;
}
</style>
<script>
window.onload = function () {
var app = document.getElementById('app');
var computedStyle = getComputedStyle(app);
var fontFamily = computedStyle.getPropertyValue("font-family");
document.getElementById('text').innerHTML = "当前文本使用的字体为:" + fontFamily;
}
</script>
</head>
<body>
<h2 id="app">我是中国人</h2>
<span id="text">文档</span>
</body>
</html>
在Chrome和Edge上显示,果然用的是『苹方』字体:
又在我的安卓机上访问,看到的是简体:
回过头,试了下Safari浏览器,居然也是正常的:
这么说来,就是『苹方』字体的锅了!
我再修改用例,给h2添加一个『简宋』字体:
css
h2 {
font-family: "简宋";
font-feature-settings: "cv10" on;
}
这次在Chrome里也正常了:
你可能会疑惑,为什么选择了一个『简宋』呢?其实我一开始用的是『宋体』,但是一个德性。
后来在字体集里一看,才意识到是不是这些成双成对的都不行?
而这几位单身狗都没问题:
现象已经很明显了:在Chrome里,当配置
font-feature-settings: "cv10" on
后,会优先匹配当前字体的繁体集。
为什么要强调是Chrome呢(Edge与它穿一条裤子,就不提了)?因为在Safari里不受这个影响:
我猜测是这个特性没有被实现有关。有兴趣的同学看看这篇字体特性的介绍,我是没兴趣研究它了。
总结
我们从Deno官网使用谷歌翻译为中文简体,但页面却展示为中文繁体开始查起,通过『二分法』,最终确定了罪魁祸首为font-feature-settings: "cv10" on
这个字体特性,它会影响字体形状,但在Chrome下表现为会优先匹配当前字体的繁体字集。
至于为什么会有这样的效果,我猜测是字体变形到某种程度引起的。将cv10
调整为临近的cv09
就没有问题。嗯,这也是我提交合并请求的思路,已经合并了。