如何基于 Typecho 实现中英双语网站(上)

转载文章 建站运维

前言

我在前面谈 Google Adsense 的文章(如 一组数据让大家直观感受一下出海的重要性)中多次提到过,要想让网站中的 Google Adsense 有更高的收益,就一定要考虑出海,而出海就离不开多语言这个话题。理论上网站应该支持尽可能多的语言,但每增加一种语言,网站的复杂度和维护难度都会成倍增加,因此,对我们而言,性价比最高、最常用的无疑还是 中英双语网站。

比较遗憾的是,我查过一些资料,也读过 Typecho 的源码,并没有找到一种简单直接的多语言方案(直接部署并维护多个站点除外)。虽然 Typecho 本身就提供了多语言翻译功能,但需要管理员在后台切换,并且切换后会全局生效。也就是说, Typecho 并不能满足 内地显示中文,海外显示英文 这种看似简单的需求,其本质还是仅支持单一语言。换句话说,要想实现常规意义上的多语言网站,就必须修改 Typecho 源码,至少我暂时还没有找到仅扩展 插件 或 主题 就能同时支持多语言的方案。

1. 思路与方案

如果考虑修改源码的话,那可选方案就多了。但结合 Typecho 自身的特点,我这里主要参考了 微信公众号 的方案,如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第1张图片-iT日记

即通过在网址中增加 lang 参数来区分语言,该方案很多其它网站也在使用,又如阿里巴巴的 iconfont。
如何基于 Typecho 实现中英双语网站(上)-第2张图片-iT日记

当然,基于个人特定的需求,我的实现也略有不同。以下是我希望达到的效果:

  1. 自动切换语言:用户访问网站时,如果浏览器首选语言为中文(无论简体还是繁体),则显示简体中文,如果浏览器首选语言不是中文,则显示英文;

  2. 手动切换语言:用户访问网站时,可以通过下拉框、单选框等形式,手动切换希望显示的语言;

  3. 文章内容支持切换语言:网站中,除了公用词条外,文章的 标题、内容、分类、标签 等,也应该支持语言切换。

前两条虽然 Typecho 默认不支持,但基于 Typecho 自身提供的多语言翻译功能,略微修改源码依然可以实现,这个我会在下文详细介绍。而第三条, Typecho 无论如何都是不支持的,这就必须得另想它法了,这个比较麻烦,而且不同的人、不同的应用场景实现方式也可能不同,我会在下一篇文章中单独给出我的方案。

整个中英双语方案我都是在 一起学笛子 这个网站中落地的,如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第3张图片-iT日记

感兴趣的也可以关注跟踪一下实时效果。

2. 多语言翻译功能

无论是 自动切换语言 还是 手动切换语言,我们首先要解决的就是词条翻译问题,这个上面已经说过了, Typecho 默认就是支持的,我们现在唯一要做的就是了解它的用法。具体可以参考官方文档:https://docs.typecho.org/translate/start,其中还包含一个语言包开源项目:https://github.com/typecho/languages。实际上,也可以直接无视它们,只需下载一个 Poedit 就可以了。

2.1 创建语言包

下载并安装好 Poedit 后,就可以新建语言包文件了,如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第4张图片-iT日记

注意,这里是通过 文件->新建(N)... 创建语言包文件的,而不是利用 POT 模板(需要用到https://github.com/typecho/languages 中的 messages.pot)创建。然后选择一种语言,我这里用的是美国英语(en_US)。
如何基于 Typecho 实现中英双语网站(上)-第5张图片-iT日记

点击 确定,保存 到任意位置都可以,我为了方便管理,保存到了项目的 langs 目录(如果不存在,则需要手动创建)下,如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第6张图片-iT日记

事实上,en_US.po 本身只是一个普通的 文本文件 而已,用文本编辑器就可以打开并编辑词条,文本结构如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第7张图片-iT日记

也就是说,我们只需要不断往这个文件中增加 msgid 和 msgstr 键值对就可以了,直至把所有希望翻译的词条都添加完为止。当然,这里的词条指的是代码中用 _t()(翻译)和 _e()(翻译并输出)方法包起来的文本,其它文本即使翻译了也无法识别。

由于 Poedit 只能翻译已存在的词条,而不能新增词条(我没有找到),所以我的做法是通过文本编辑器(如VS Code)新增词条,然后通过 Poedit 翻译。当然,如果不希望在多个软件之间来回切换,不用 Poedit 也是可以的。只是使用 Poedit,界面会更加直观,也不用担心误操作,下图是翻译好的界面:
如何基于 Typecho 实现中英双语网站(上)-第8张图片-iT日记

2.2 编译语言包

经过上一节,其实多语言翻译问题就已经完成了,但可惜的是, Typecho 并不直接识别 po 文件,而是识别其编译后的 mo 文件。这时就需要用到 Poedit 的编译功能,操作很简单,直接 编译为MO... 就可以了,如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第9张图片-iT日记

然后把 en_US.mo 文件拷贝到 langs 目录下,就可以通过管理后台切换语言来测试效果了。
如何基于 Typecho 实现中英双语网站(上)-第10张图片-iT日记

由于我们最终需要的是 en_US.mo 而不是 en_US.po,所以不难发现,如果没有这里的编译过程,Poedit 都是可以不需要的。

3. 语言包初始化

虽然我们上面通过管理后台切换语言测试了翻译效果,但这显然不是我们想要的,它更适合单语言网站,如 中文站 或 英文站。
为了实现不同用户端显示不同语言(手动或自动)这一功能,我们得先看一下源码,看看后台统一设置时是怎么做的:

/** 语言包初始化 */ if ($options->lang && $options->lang != 'zh_CN') {     $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs';     I18n::setLang($dir . '/' . $options->lang . '.mo'); }

这段代码在 var\Widget\Init.php 文件中,大约位于 84 行左右的位置。大意是在每次页面初始化的时候,判断后台设置的语言是不是 简体中文,如果不是,则从 langs 目录下面读取对应的 .mo 文件来初始化语言包。
了解到这一点,接下来就简单了,只需要将判断条件换一下就可以了,具体实现如下:

/** 语言包初始化 */ // if ($options->lang && $options->lang != 'zh_CN') { //     $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs'; //     I18n::setLang($dir . '/' . $options->lang . '.mo'); // } $lang = $this->getLang(); if ($lang == 'en_US') {     $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs';     I18n::setLang($dir . '/en_US.mo'); }

这里的 getLang() 方法就是用来获取当前用户语言的,是我们自己实现的。考虑到后续在很多地方都需要用到,因此,我把这个方法实现在了组件基类 Widget 中,具体代码如下:

function getLang() {     if ($this->request->is('lang')) {         // 通过QUERY参数指定语言         if (preg_match('/^zh/i', $this->request->get('lang'))) {             $lang = 'zh_CN';         } else {             $lang = 'en_US';         }         return $lang;     }          // 没有手动指定语言,使用浏览器的首选语言     $acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];     if (!empty($acceptLang) && preg_match('/^zh/i', $acceptLang)) {         $lang = 'zh_CN';     } else {         $lang = 'en_US';     }     return $lang; }

这段代码的大意是,如果客户端的页面请求中带有 lang 参数,则代表用户手动指定了语言,如果用户指定的是中文(无论是简体还是繁体),则返回 简体中文 页面给用户,否则返回 英文 页面。如果客户端的页面请求中没有带有 lang 参数,则代表用户没有手动指定语言,此时使用浏览器的首选语言,判断逻辑和手动指定时是一样的。

完成这一步,就可以手动修改浏览器首选语言,或手动在网址后面加上 lang 参数来测试语言切换效果了。

4. 手动切换语言

每次访问网站都手动添加 lang 参数显然是不合适的,因此我们需要在页面上增加手动切换语言的下拉框,实现方式有很多,以下是简单的示例代码:

<ul class="dropdown-menu">    <li><a class="dropdown-item" href="?lang=en_US">English</a></li>    <li><a class="dropdown-item" href="?lang=zh_CN">简体中文</a></li> </ul>

这其实就是两个跳转到当前页面的超链接,只是在超链接的后面加上了一个 lang 参数。但这仅限于上面的两个超链接,网站中的其它超链接依然没有 lang 参数,因此我们还需要在每次切换语言时,将页面中的所有超链接都加上相应的 lang 参数,这就需要在组件基类 Widget 中再增加一个 urlWithLang($url) 方法,具体代码如下:

function urlWithLang($url) {     echo $url . (strpos($url, '?') === false ? '?' : '&') . 'lang=' . $this->getLang(); }

使用时,只需要将页面中所有生成 url 的地方都调用一下 urlWithLang 方法即可,如将 <?php $categories->permalink(); ?> 替换为 <?php $this->urlWithLang($categories->permalink); ?>,将 <?php $pages->permalink(); ?> 替换为 <?php $this->urlWithLang($pages->permalink); ?> 等。

这样就可以了吗?NO,还不够,还有两个地方比较特殊,好在都只需修改 var\Widget\Archive.php 中的少量代码即可,一个是由 <?php $this->pageNav('&laquo;', '&raquo;'); ?> 生成的分页组件,该功能是 Typecho 内部实现的,修改源码如下图所示:
如何基于 Typecho 实现中英双语网站(上)-第11张图片-iT日记

另一个是搜索页面,改法一样,即在 Router::url('search', ['keywords' => urlencode($filterKeywords)], $this->options->index) 的后面加上 . '?lang=' . $this->getLang() 即可。

页面元数据如 descriptionkeywords等也含有中文,同样在 var\Widget\Archive.php 文件中,可视情况修改一下。

好了,现在是真的可以了,当然,和 微信公众号 相比,这里没有实现记住上一次所选语言的功能,倒不是没考虑到或忘记了,而是我不太想过多依赖 Cookie,而且,该功能对我而言价值似乎也不大,所以就无视了,有需要的可以自行补上。

结语

看到这里,不难发现,Typecho 其实并不是很适合做多语言网站,单语言博客网站才是它的强项,也就是说,不修改源码很难实现这个哪怕看似比较简单的功能,但如果是二次开发的话,基于 Typecho 还是可以节省很多时间的。

至此,我的中英双语网站也算实现一半了,虽然一眼望去,还是满屏的中文,但这已经是目前所能达到的极限了。另一半主要是针对文章内容翻译的,虽然考虑过通过一些翻译库或 API 简单实现,但目测了一下,感觉不太靠谱,要想达到比较满意的效果,可能还是要面临更多源码的修改,这个等我完成以后再分享吧!

标签: Typecho 实现 中英双语 网站建设

发布评论 0条评论)

您需要 登录账户 后才能发表评论
  • Refresh code

还木有评论哦,快来抢沙发吧~