2010-04-07

从 CoSign 看开源软件本地化(1)

开源软件本地化,一般来说都是 a piece of cake。但是 CoSign (密歇根大学开发的单点登录系统)的本地化却是让我们大费周折,很是经历一些往复。 CoSign 是用 C 语言开发的,其 web 服务也是通过 C 语言开发的 CGI 来提供,而Web 页面是通过页面模板加载经过处理后形成的。CoSign 本身并没有提供多语言支持,但是模板可以用户自定义,就是说可以用中文来写 web 页面模板。 在我们最早的版本中,采用的是:多模板(根据语种自适应选择),以及 Apache 的内容协商实现的多语种支持。 在最近我们通过 Gettext 在底层实现了本地化,又通过精简模板,静态页面模板化等方法让单点登录页面定制更简单。 我们在 CoSign 本地化中所经历的,在我们众多开源产品的本地化中是最复杂和独特的,因此撰文共享。

模板自适应选择

正如之前所说,CoSign 的 web 服务是由一些用 c 语言写的 CGI 来提供,这些 CGI 会读取页面模板文件,经过处理后,动态产生 web 页面。 页面模板位于 /opt/cosign/lib/templates-local 目录下。缺省包含如下模板文件:
  • login.html 登录页面模板
  • login_error.html 登录过程中出现错误时,使用的模板。和 login.html 相比只是多了一处显示错误的区域( id 为 "error" 的 DIV)。
  • reauth.html 双因子认证,或者由于IP地址变更等语言需要的二次登录页面
  • error.html 显示系统错误的页面模板
  • expired_error.html 口令过期错误的页面模板
  • verify-logout.html 退出登录前,提示用户确认的页面模板
这些模板文件就是包含宏的的 HTML 文件。其中出现的 $ 字符加一个字母,作为特定的宏,由 CGI 在运行时扩展。例如 $t 一般扩展为页面标, $e 扩展为错误信息。 虽然可以直接对这些模板文件本地化但是这么做的结果是由一个说英文的 CoSign,变成了一个只会说中文的 CoSign,不是真正的本地化。这样实现的登录平台,将阻止母语为英语的用户使用相关服务。 因此在 2008 年我们开始做 CoSign 的定制开发时,就将多模板选择作为本地化的突破:
  • 模板的加载和替换,是由文件 cgi/subfile.c 的 subfile() 函数实现的
  • 模板文件的加载缺省都是在一个目录下完成的: /opt/cosign/lib/templates-local。
  • 多模板支持只需要在 subfile() 函数的文件加载时,根据语种选择不同的模板目录。例如中文选择 /opt/cosign/lib/templates-local/zh 不就可以了么?
  • 而且这么做对代码的侵入最小。
很快,我们就实现了这个功能
  • 增加一个叫做 lang.c 文件,提供 get_accept_language() 函数,返回浏览器支持的语种列表
  • 在 subfile() 函数中依次对语言列表中的语言进行尝试,如果发现存在相应语言的模板文件,就读取相应文件,提供给客户本地化语言的页面展示
很好,页面本地化了,但是慢着,页面仍然有英文出现:
  • 当由于口令错误等原因认证失败时,页面显示的错误信息仍然是英文!
  • 这些英文出错信息是认证因子(factor)返回的出错信息,直接替换掉模板中的 $e 宏。
  • 认证因子是脱离 Web 服务器执行的,不可能在认证因子本身中实现本地化!
实际上这个问题,用模板自适应是无法解决的,我们将在后面对 CoSign 核心用 gettext 本地化后才真正解决。但是当时这个问题并非急需解决的问题。而是还有一个更加讨厌的问题,即模板定制:
  • 每个语言都需要定制一系列的模板,如前所述,包括: login.html, login_error.html, 等等
  • 模板之间的冗余度非常之高,经常出现一个小小的改动,要打十来个文件,一一重复的去修改。这时因为我们同时支持两套风格的页面模板(经典式和Google式),以及两个不同语种(英文和中文)
这个问题最终通过模板嵌套完美的解决了。请听下回分解。
blog comments powered by Disqus