最佳实践多语言网站

新手上路,请多包涵

几个月来我一直在为这个问题苦苦挣扎,但我之前没有遇到过需要探索所有可能选择的情况。现在,我觉得是时候了解这些可能性并创建我自己的个人偏好,以便在我即将进行的项目中使用。

让我先勾勒一下我正在寻找的情况

我即将升级/重新开发我已经使用了很长一段时间的内容管理系统。但是,我觉得多语言是对这个系统的一个很大的改进。在我没有使用任何框架之前,我将在即将到来的项目中使用 Laraval4。 Laravel 似乎是一种更简洁的 PHP 编码方式的最佳选择。 Sidenote: Laraval4 should be no factor in your answer 。我正在寻找独立于平台/框架的通用翻译方式。

应该翻译什么

由于我正在寻找的系统需要尽可能用户友好,因此管理翻译的方法应该在 CMS 内部。应该不需要启动 FTP 连接来修改翻译文件或任何 html/php 解析的模板。

此外,我正在寻找翻译多个数据库表的最简单方法,也许不需要制作额外的表。

我自己想出了什么

正如我自己一直在搜索、阅读和尝试的那样。我有几个选择。但我仍然不觉得我已经达到了我真正寻求的最佳实践方法。现在,这是我想出的,但这种方法也有副作用。

  1. PHP Parsed Templates : 模板系统应该被 PHP 解析。这样我就可以将翻译后的参数插入到 HTML 中,而无需打开模板并修改它们。除此之外,PHP 解析的模板使我能够为完整的网站拥有 1 个模板,而不是为每种语言(我以前有过)拥有一个子文件夹。达到这个目标的方法可以是 Smarty、TemplatePower、Laravel’s Blade 或任何其他模板解析器。正如我所说,这应该独立于书面解决方案。
  2. 数据库驱动:也许我不需要再提这个了。但解决方案应该是数据库驱动的。 CMS 的目标是面向对象和 MVC,所以我需要考虑字符串的逻辑数据结构。由于我的模板是结构化的:templates/Controller/View.php 也许这种结构最有意义: Controller.View.parameter 。数据库表将包含这些字段,并带有 value 字段。在模板中,我们可以使用某种排序方法,例如 echo __('Controller.View.welcome', array('name', 'Joshua')) 并且参数包含 Welcome, :name 。因此结果是 Welcome, Joshua 。这似乎是一个很好的方法,因为 :name 等参数很容易被编辑器理解。
  3. 低数据库负载:当然,如果在旅途中加载这些字符串,上述系统会导致数据库负载负载。因此,我需要一个缓存系统,一旦语言文件在管理环境中被编辑/保存,就会重新呈现它们。因为文件是生成的,所以还需要一个好的文件系统布局。我想我们可以使用 languages/en_EN/Controller/View.php 或 .ini,无论哪种最适合您。也许.ini 最终会被更快地解析。这个犯规应该包含 format parameter=value; 中的数据。我想这是最好的方法,因为渲染的每个视图都可以包含它自己的语言文件(如果存在)。然后应该将语言参数加载到特定视图而不是全局范围内,以防止参数相互覆盖。
  4. 数据库表翻译:这其实是我最担心的事情。我正在寻找一种方法来创建新闻/页面/等的翻译。尽快。每个模块有两个表(例如 NewsNews_translations )是一种选择,但感觉要获得一个好的系统需要做很多工作。我想出的一件事是基于我写的一个 data versioning 系统:有一个数据库表名 Translations ,这个表有一个独特的组合 language , tablenameprimarykey 。例如:en_En / News / 1(指的是英文版的 ID=1 的 News 条目)。但是这种方法有两个巨大的缺点:首先,由于数据库中有大量数据,该表往往会变得很长,其次,使用这种设置来搜索表将是一项艰巨的工作。例如,搜索该项目的 SEO slug 将是一个全文搜索,这非常愚蠢。但另一方面:这是在每个表格中非常快速地创建可翻译内容的快速方法,但我不相信这个专业人士超过了骗局。
  5. 前端工作:前端也需要一些思考。当然,我们会将可用的语言存储在数据库中,并(停用)我们需要的语言。这样,脚本可以生成一个下拉菜单来选择一种语言,并且后端可以自动决定使用 CMS 可以进行哪些翻译。然后,在获取语言文件以供查看或获取网站上内容项的正确翻译时,将使用所选语言(例如 en_EN)。

所以,他们在那里。到目前为止我的想法。它们甚至还不包括日期等的本地化选项,但由于我的服务器支持 PHP5.3.2+,因此最好的选择是使用 intl 扩展,如下所述:http: //devzone.zend.com/1500/internationalization-in -php-53/ - 但这将在以后的任何开发体育场中使用。目前的主要问题是如何对网站内容进行最佳翻译。

除了我在这里解释的一切,我还有一件事我还没有决定,这看起来是一个简单的问题,但实际上它让我很头疼:

网址翻译?我们应该这样做还是不这样做?以什么方式?

所以..如果我有这个网址: http://www.domain.com/about-us 英语是我的默认语言。当我选择荷兰语作为我的语言时,这个 URL 是否应该翻译成 http://www.domain.com/over-ons ?或者我们应该走简单的路,简单地更改在 /about 处可见的页面内容。最后一件事似乎不是一个有效的选项,因为这会生成同一个 URL 的多个版本,这种索引内容将以正确的方式失败。

另一种选择是使用 http://www.domain.com/nl/about-us 代替。这至少会为每个内容生成一个唯一的 URL。此外,使用另一种语言会更容易,例如 http://www.domain.com/en/about-us 并且所提供的 URL 对于 Google 和人类访问者来说更容易理解。使用此选项,我们如何处理默认语言?默认语言是否应该删除默认选择的语言?因此,将 http://www.domain.com/en/about-us 重定向到 http://www.domain.com/about-us …在我看来,这是最好的解决方案,因为当 CMS 仅设置为一种语言时,无需在网址。

第三个选项是两个选项的组合:对主要语言使用“language-identification-less”-URL ( http://www.domain.com/about-us )。并使用带有翻译的 SEO slug 的 URL 用于子语言: http://www.domain.com/nl/over-ons & http://www.domain.com/de/uber-uns

我希望我的问题能引起您的注意,他们肯定会解决我的问题!它确实帮助我在这里解决问题。让我有机会回顾我以前使用过的方法以及我为即将推出的 CMS 的想法。

我要感谢你花时间阅读这堆文字!

// Edit #1 :

我忘了提:__() 函数是翻译给定字符串的别名。在这个方法中,显然应该有某种后备方法,当还没有可用的翻译时加载默认文本。如果缺少翻译,则应将其插入或重新生成翻译文件。

原文由 Joshua - Pendo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 519
2 个回答

题目的前提

多语言网站具有三个不同的方面:

  • 界面翻译
  • 内容
  • 网址路由

虽然它们都以不同的方式相互连接,但从 CMS 的角度来看,它们使用不同的 UI 元素进行管理并以不同的方式存储。您似乎对前两个的实施和理解充满信心。问题是关于后者—— “URL 翻译?我们应该这样做还是不这样做?以什么方式?”

URL 可以由什么组成?

一件非常重要的事情是,不要对 IDN 有好感。而是倾向于 音译(也:转录和罗马化)。虽然乍一看 IDN 似乎是国际 URL 的可行选择,但它实际上并不像宣传的那样工作,原因有两个:

  • 一些浏览器会将非 ASCII 字符,如 'ч''ž' 转换为 '%D1%87''%C5%BE'
  • 如果用户有自定义主题,主题的字体很可能没有这些字母的符号

几年前,我实际上在一个基于 Yii 的项目中尝试了 IDN 方法(可怕的框架,恕我直言)。在抓取该解决方案之前,我遇到了上述两个问题。另外,我怀疑它可能是一个攻击媒介。

可用选项…正如我所见。

基本上你有两个选择,可以抽象为:

  • http://site.tld/[:query] : [:query] 决定语言和内容选择

  • http://site.tld/[:language]/[:query] :其中 [:language] URL 的一部分定义了语言的选择,而 [:query] 仅用于识别内容

查询是 Α 和 Ω ..

假设您选择 http://site.tld/[:query]

在这种情况下,您有一个主要的语言来源: [:query] 段的内容;和两个额外的来源:

  • 该特定浏览器的值 $_COOKIE['lang']
  • HTTP Accept-Language (1) , (2)标头中的语言列表

首先,您需要将查询与定义的路由模式之一匹配(如果您选择 Laravel, 请阅读此处)。成功匹配模式后,您需要找到语言。

您必须遍历模式的所有部分。找到所有这些句段的潜在翻译,并确定使用了哪种语言。两个额外的来源(cookie 和标头)将用于解决路由冲突,当(不是“如果”)它们出现时。

举个例子: http://site.tld/blog/novinka

那是 "блог, новинка" 的音译,在英语中大约是 "blog", "latest" 的意思。

正如您已经注意到的那样,俄语中的“блог”将被音译为“博客”。这意味着对于 [:query] 的第一部分,您(在 _最好的情况下_)最终会得到 ['en', 'ru'] 可能的语言列表。然后你采取下一段 - “novinka”。在可能性列表中可能只有一种语言: ['ru']

当列表中有一项时,您已成功找到该语言。

但是,如果您最终得到 2 个(例如:俄语和乌克兰语)或更多可能性 .. 或 0 个可能性,视情况而定。您必须使用 cookie 和/或标头来找到正确的选项。

如果一切都失败了,您选择网站的默认语言。

语言作为参数

另一种方法是使用 URL,它可以定义为 http://site.tld/[:language]/[:query] 。在这种情况下,翻译查询时,您无需猜测语言,因为此时您已经知道要使用哪种语言。

还有第二个语言来源:cookie 值。但是这里没有必要弄乱 Accept-Language 标头,因为在“冷启动”(当用户第一次使用自定义查询打开网站时)的情况下,您不会处理未知数量的可能语言。

相反,您有 3 个简单的优先选项:

  1. 如果设置了 [:language] 段,使用它
  2. 如果设置了 $_COOKIE['lang'] ,使用它
  3. 使用默认语言

当您拥有该语言时,您只需尝试翻译查询,如果翻译失败,则使用该特定段的“默认值”(基于路由结果)。

这不是第三种选择吗?

是的,从技术上讲,您可以结合使用这两种方法,但这会使过程复杂化,并且仅适用于想要手动将 http://site.tld/en/news 的 URL 更改为 http://site.tld/de/news 并希望新闻页面更改为德语的人.

但即使是这种情况,也可以使用 cookie 值(其中包含有关先前选择的语言的信息)来缓解这种情况,从而以更少的魔力和希望来实现。

使用哪种方法?

正如您可能已经猜到的那样,我会推荐 http://site.tld/[:language]/[:query] 作为更明智的选择。

同样在真实的情况下,您将在 URL 中有第三个主要部分:“标题”。如在线商店中的产品名称或新闻网站中的文章标题。

示例: http://site.tld/en/news/article/121415/EU-as-global-reserve-currency

在这种情况下 '/news/article/121415' 将是查询,而 'EU-as-global-reserve-currency' 是标题。纯粹出于 SEO 目的。

可以在 Laravel 中完成吗?

有点,但不是默认的。

我对它不是很熟悉,但是据我所见,Laravel 使用了简单的基于模式的路由机制。要实现多语言 URL,您可能必须 扩展核心类,因为多语言路由需要访问不同形式的存储(数据库、缓存和/或配置文件)。

是路由的。现在怎么办?

结果,您最终会得到两条有价值的信息:当前语言和已翻译的查询段。然后这些值可用于分派给将产生结果的类。

基本上,以下 URL: http://site.tld/ru/blog/novinka (或没有 '/ru' 的版本)变成了类似的东西

$parameters = [
   'language' => 'ru',
   'classname' => 'blog',
   'method' => 'latest',
];

您仅用于调度:

 $instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );

..或它的一些变体,取决于特定的实现。

原文由 tereško 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您托管静态内容,那么 Google 的 Firebase 托管 支持 i18n 托管规则,这些规则返回国家、语言或语言 + 国家/地区特定内容,包括 index.html404.htmlmanifest.json 文件(用于渐进式网络应用程序)。

他们的主机将根据用户 IP 地址选择国家,并根据浏览器的 Accept-Language 标头选择语言,然后应用 优先规则 返回每个请求的文件内容。

  1. 语言代码 + 国家代码(例如,来自 fr_ca/ 的内容)

  2. 仅国家代码(例如,来自 ALL_ca/ 的内容)

  3. 仅语言代码(例如,来自 fr/ 或 es_ALL/ 的内容)

  4. 位于“i18n 内容”目录之外的“默认”内容,例如公共目录的根目录。

规则 1 和 3 按请求的 Accept-Language 标头中每种语言的质量值的顺序应用。

例子

public/
    index.html  // Default homepage
    manifest.json  // Default manifest.json
    404.html  // Default custom 404 page

    localized-files/
        ALL_ca/
            index.html
        es_ALL/
            index.html
            404.html
            manifest.json   << Spanish
        fr/
            index.html
            404.html
            manifest.json   << French
        fr_ca/
            index.html
            manifest.json

 // firebase.json

"hosting": {

  "public": "public",

  "ignore": [
    "firebase.json",
    "**/.*",
    "**/node_modules/**"
  ],

  "i18n": {
    "root": "/localized-files"  // <<< "i18n content" folder
  }

  ...
}

完整的细节…

原文由 Tony O‘Hagan 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题