国际化
国际化
什么是国际化?
国际化是开发插件的过程,以便可以轻松地将其翻译成其他语言。国际化常缩写为i18n
(因为字母i和n之间有18个字母)。
为什么国际化很重要?
WordPress 在世界各地使用,在英语不是主要语言的国家/地区。WordPress 插件中的字符串需要以特殊方式编码,以便可以轻松翻译成其他语言。作为开发人员,您可能无法为所有用户提供本地化(即需要对文本和其他内容进行更改,例如特定于给定区域设置(位置)的数字格式);但是,翻译人员可以成功本地化主题,而无需修改源代码本身。
进一步阅读“如何国际化你的插件”。
资源
- 视频:i18n:为世界准备您的 WordPress 主题
- 视频:国际化:适用于全世界的插件和主题 幻灯片
- 视频:日本大业:主题和国际化指南
- 视频:迷失翻译 — i18n 和 WordPress
- 国际化和本地化您的 WordPress 主题
- 国际化:你可能做错了
- 更多国际化乐趣
- 使用wp_localize_script,太棒了
- 理解_n_noop()
- 语言包 101 – 准备工作
- 翻译 WordPress 插件和主题:不要太聪明
- 如何加载主题和插件翻译
- 多语言手册:插件/主题作者指南
本土化
什么是本地化?
本地化描述了翻译国际化插件的后续过程。Localization 通常缩写为l10n
(因为 l 和 n 之间有 10 个字母。)
本地化文件
(便携式对象模板)文件
该文件包含插件中的原始字符串(英文)。
(便携式对象)文件
每个翻译人员都会获取该POT
文件并将各个msgstr
部分翻译成自己的语言。结果是一个PO
与 a 格式相同的文件POT
,但带有翻译和一些特定的标头。每种语言都有一个PO
文件。
(机器对象)文件
从每个翻译的PO
文件中MO
构建一个文件。这些是 gettext 函数实际使用的机器可读的二进制文件(它们不关心.POT
或.PO
文件),并且是文件的“编译”版本PO
。转换是使用msgfmt
命令行工具完成的。一般来说,一个应用程序可能会相应地使用多个大型逻辑可翻译模块和不同的MO
文件。文本域是每个模块的句柄,每个模块都有不同的MO
文件。
生成POT
文件
该POT
文件是您需要交给翻译人员的文件,以便他们可以完成工作。和文件可以轻松地互换重命名以更改文件类型,而不会出现任何问题POT
。 PO
有几种方法可以POT
为您的插件生成文件:
WP-CLI
安装WP-CLIwp i18n make-pot
并根据文档使用命令。
诗歌
翻译时也可以在本地使用Poedit。这是适用于所有主要操作系统的开源工具。免费的Poedit默认版本支持使用Gettext功能手动扫描所有源代码。它的专业版还具有一键扫描 WordPress 插件的功能。生成PO
文件后,您可以将文件重命名为POT
. 如果MO
生成了,那么您可以删除该文件,因为不需要它。如果您没有专业版,您可以轻松获得空白 POT并将其用作POT file
. 将空白放入POT
语言文件夹后,您可以在 Poedit 中单击“更新”以POT
使用您的字符串更新文件。
繁重任务
您甚至可以使用一些繁重的任务来创建 POT。 grunt-wp-i18n 和 grunt-pot。设置 grunt 的步骤超出了本文档的范围,但请注意这是可能的。以下是一个示例 Grunt.js 和 package.json,您可以将其放置在插件的根目录中。
翻译PO
文件
将翻译后的文件另存为 my-plugin-{locale}.mo
. 区域设置是语言代码和/或国家/地区代码。例如,德语的区域设置是 de_DE
。从上面的代码示例中,文本域是“my-plugin”,因此德语 MO 和 PO 文件应命名为 my-plugin-de_DE.mo
和 my-plugin-de_DE.po
。有关语言和国家/地区代码的更多信息,请参阅 以您的语言安装 WordPress。
翻译文件的方法有多种PO
。
手动
您可以使用文本编辑器输入翻译。在文本编辑器中,它看起来像这样。
#: plugin-name.php:123
msgid "Page Title"
msgstr ""
您在引号之间输入翻译。对于德语翻译,它看起来像这样。
#: plugin-name.php:123
msgid "Page Title"
msgstr "Seitentitel"
诗歌
翻译时也可以使用Poedit。免费的Poedit默认版本支持使用Gettext功能手动扫描所有源代码。它的专业版还具有一键扫描 WordPress 插件和主题的功能。
在线服务
第三种选择是使用在线翻译服务。一般的想法是,您上传POT
文件,然后您可以授予用户或翻译人员翻译您的插件的权限。这使您可以跟踪更改,始终拥有最新的翻译并减少两次翻译。
以下是一些可用于在线翻译 PO 文件的工具:
生成MO文件
命令行
msgfmt
用于创建 MO 文件。msgfmt
是 Gettext 包的一部分。否则可以使用命令行。典型的 msgfmt
命令如下所示:
Unix操作系统
msgfmt -o filename.mo filename.po
Windows 操作系统
msgfmt -o filename.mo filename.po
如果您有很多 PO
文件需要一次转换,您可以批量运行它。例如,使用 bash
命令:
Unix操作系统
# Find PO files, process each with msgfmt and rename the result to MO
for file in `find . -name "*.po"` ; do msgfmt -o ${file/.po/.mo} $file ; done
Windows 操作系统
对于 Windows,您需要先安装 Cygwin。
创建一个名为的文件potomo.sh
并将以下内容放入其中:
#! /bin/sh
# Find PO files, process each with msgfmt and rename the result to MO
for file in `/usr/bin/find . -name '*.po'` ; do /usr/bin/msgfmt -o ${file/.po/.mo} $file ; done
您可以在命令行中运行此命令。
cd C:/path/to/language/folder/my-plugin/languages & C:/cygwin/bin/bash -c /cygdrive/c/path/to/script/directory/potomo.sh
诗歌
msgfmt
还集成在Poedit中 ,允许您使用它来生成 MO 文件。首选项中有一个设置,您可以在其中启用或禁用它。

咕噜任务
grunt-po2mo可以转换所有文件。
良好翻译的秘诀
不要逐字翻译,要有机翻译
作为双语或多语者,您无疑知道您所说的语言具有不同的结构、节奏、语气和语调变化。翻译后的消息不需要采用与英语消息相同的结构:采用所呈现的想法,并提出一条消息,以自然的方式为目标语言表达相同的内容。这就是创建同等消息和等效消息之间的区别:不要复制,而是替换。即使消息中有更多的结构性项目,如果您认为这对目标受众来说更合乎逻辑或更适合,您也可以创造性地进行调整和更改。
尝试保持相同程度的正式(或非正式)
每条消息都有不同程度的正式或非正式。您必须自己(或与您的团队)一起弄清楚目标语言中每条消息的正式或非正式程度,但 WordPress 消息(特别是信息性消息)往往有礼貌的非正式语气。英语的语气。尝试在您的文化背景下用目标语言完成同等的任务。
不要使用俚语或特定于受众的术语
博客中会出现一定数量的术语,但不要使用只有“圈内”人群才会使用的口语。如果不熟悉的博主要以您的语言安装 WordPress,他们会知道该术语的含义吗?pingback、trackback 和 feed 之类的词是该规则的例外;它们是通常难以翻译的术语,许多译者选择保留英语。
阅读其他软件的您语言的本地化版本
如果您遇到困难或需要指导,请尝试阅读其他流行软件工具的翻译,以了解常用术语、如何处理正式性等。当然,WordPress 有自己的语气和感觉,所以请保留这一点当您阅读其他本地化版本时,请记住这一点,但请随意挖掘 UI 术语等,以保持与您语言中的其他软件的一致性。
使用本地化
将本地化文件放在语言文件夹中,可以放在插件languages
文件夹中,也可以从 WordPress 3.7 开始放在插件languages
文件夹中,通常位于wp-content
. 完整路径将是wp-content/languages/plugins/my-plugin-fr_FR.mo
.
您可以在“常规设置”中更改语言。如果您没有看到此选项,或者未列出您要切换到的语言,请手动执行此操作:
- 定义
WPLANG
内部wp-config.php
为您选择的语言。例如,如果您想使用法语,则需要:define ('WPLANG', 'fr_FR');
- 转到
wp-admin/options-general.php
或“设置”->“常规” - 在“站点语言”下拉列表中选择您的语言
- 去
wp-admin/update-core.php
- 如果可用,请点击“更新翻译”
- 下载核心翻译文件(如果可用)
资源
- 为您的主题或插件创建 .pot 文件
- 如何国际化 WordPress 插件
- 翻译你的主题
- 空白 WordPress 罐子
- 改进的 i18n WordPress 工具
- 如何快速更新翻译
- GitHub/Transifex 之间的工作流程
- 要点:完成本地化 Grunt 任务
- WordPress.tv标签:i18n、国际化和翻译
如何国际化你的插件
为了使字符串在应用程序中可翻译,您必须将原始字符串包装在对一组特殊函数之一的调用中。这些函数统称为“gettext”。
Gettext简介
WordPress 使用i18n 的gettext库和工具,但不是直接使用:有一组专门为了启用字符串翻译而创建的特殊函数。下面列出了这些功能。这些是您应该在插件中使用的函数。
文本域
使用文本域来表示属于您的插件的所有文本。文本域是一个唯一标识符,以确保 WordPress 可以区分所有加载的翻译。这提高了可移植性,并且可以更好地与现有的 WordPress 工具配合使用。
文本域必须与slug
插件的文本域匹配。如果您的插件是一个名为的单个文件my-plugin.php
或它包含在一个名为的文件夹中,则my-plugin
域名必须是my-plugin
. 如果您的插件托管在 wordpress.org 上,则它必须是插件 URL 的 slug 部分 ( wordpress.org/plugins/
)。
文本域名必须使用破折号而不是下划线、小写且不含空格。
文本域还需要添加到插件标头中。即使插件被禁用,WordPress也会使用它来国际化您的插件元数据。文本域应与加载文本域时使用的文本域相同。
标头示例:
/*
* Plugin Name: My Plugin
* Author: Plugin Author
* Text Domain: my-plugin
*/
笔记:再次将“my-plugin”更改为插件的名称。
笔记:从 WordPress 4.6 开始,Text Domain
标头是可选的,因为它必须与插件 slug 相同。包含它没有什么坏处,但不是必需的。
域路径
域路径定义插件翻译的位置。这有一些用途,特别是即使插件被禁用,WordPress 也知道在哪里可以找到翻译。默认为您的插件所在的文件夹。
例如,如果翻译位于languages
插件内调用的文件夹中,则域路径/languages
必须用第一个斜杠编写:
标头示例:
/*
* Plugin Name: My Plugin
* Author: Plugin Author
* Text Domain: my-plugin
* Domain Path: /languages
*/
笔记:Domain Path
如果插件位于官方 WordPress 插件目录中,则可以省略标头 。
基本字符串
对于基本字符串(即没有占位符或复数的字符串),请使用__()
. 它返回其参数的翻译:
__( 'Blog Options', 'my-plugin' );
警告:不要对 gettext 函数的文本域部分使用变量名或常量。例如: 不要将此作为快捷方式:
__( '翻译我。' , $text_domain );
要回显检索到的翻译,请使用_e()
. 所以不要写:
echo __( 'WordPress is the best!', 'my-plugin' );
您可以使用:
_e( 'WordPress is the best!', 'my-plugin' );
变量
如果你有一个像下面这样的字符串怎么办:
echo 'Your city is $city.'
在这种情况下,$city
是一个变量,不应该是翻译的一部分。解决方案是使用变量的占位符以及printf
函数族。尤其有帮助的是printf
和sprintf
。正确的解决方案如下所示:
printf(
/* translators: %s: Name of a city */
__( 'Your city is %s.', 'my-plugin' ),
$city
);
请注意,这里用于翻译的字符串只是 template "Your city is %s."
,它在源代码和运行时都是相同的。
另请注意,翻译人员会收到提示,以便他们了解占位符的上下文。
如果字符串中有多个占位符,建议您使用参数交换。在这种情况下,(')
字符串周围的单引号是强制性的,因为双引号(")
会告诉 php 将 the 解释$s
为s
变量,这不是我们想要的。
printf(
/* translators: 1: Name of a city 2: ZIP code */
__( 'Your city is %1$s, and your zip code is %2$s.', 'my-plugin' ),
$city,
$zipcode
);
这里,邮政编码显示在城市名称后面。在某些语言中,以相反的顺序显示邮政编码和城市会更合适。在上面的示例中使用%s前缀可以实现这种情况。因此可以写出一个翻译:
printf(
/* translators: 1: Name of a city 2: ZIP code */
__( 'Your zip code is %2$s, and your city is %1$s.', 'my-plugin' ),
$city,
$zipcode
);
重要的!下面的代码是不正确的:
// This is incorrect do not use.
_e( "Your city is $city.", 'my-plugin' );
用于翻译的字符串是从源中提取的,因此翻译者将得到这个短语来翻译:"Your city is $city."
。
然而,在应用程序中,_e
将使用类似的参数进行调用"Your city is London."
,并且gettext
不会找到该参数的合适翻译,并将返回其参数:"Your city is London."
。不幸的是,它的翻译不正确。
复数
基本复数
如果您的字符串随着项目数量的变化而变化,您将需要一种方法来在翻译中反映这一点。例如,在英语中,有"One comment"
和"Two comments"
。在其他语言中,您可以有多种复数形式。要在 WordPress 中处理此问题,请使用该_n()
函数。
printf(
_n(
'%s comment',
'%s comments',
get_comments_number(),
'my-plugin'
),
number_format_i18n( get_comments_number() )
);
_n()
接受 4 个参数:
- 单数 – 字符串的单数形式(请注意,在某些语言中,它可以用于除 1 之外的数字,因此
'%s item'
应该使用 代替'One item'
) - plural——字符串的复数形式
- count – 对象的数量,这将决定是否应返回单数或复数形式(有些语言具有远远超过 2 种形式)
- 文本域 – 插件文本域
函数的返回值是正确的翻译形式,对应于给定的计数。
请注意,某些语言对其他数字使用单数形式(例如 21、31 等,很像英语中的“21st”、“31st”)。如果您想对单数进行特殊处理,请特别检查:
if ( 1 === $count ) {
printf( esc_html__( 'Last thing!', 'my-text-domain' ), $count );
} else {
printf( esc_html( _n( '%d thing.', '%d things.', $count, 'my-text-domain' ) ), $count );
}
另请注意,该$count
参数通常会使用两次。首先$count
传递给_n()
以确定要使用哪个翻译字符串,然后$count
传递给printf()
将数字替换为翻译字符串。
复数稍后完成
首先使用_n_noop()
或设置复数字符串_nx_noop()
。
$comments_plural = _n_noop(
'%s comment.',
'%s comments.'
);
然后,在代码的稍后位置,您可以使用它translate_nooped_plural()
来加载字符串。
printf(
translate_nooped_plural(
$comments_plural,
get_comments_number(),
'my-plugin'
),
number_format_i18n( get_comments_number() )
);
通过上下文消除歧义
有时,一个术语会在多种上下文中使用,尽管它在英语中是同一个单词,但在其他语言中必须有不同的翻译。例如,该词Post
既可以用作动词"Click here to post your comment"
,也可以用作名词"Edit this post"
。在这种情况下,应该使用_x()
or函数。_ex()
它类似于__()
and _e()
,但它有一个附加参数 - 上下文:
_x( 'Post', 'noun', 'my-plugin' );
_x( 'Post', 'verb', 'my-plugin' );
在这两种情况下使用此方法,我们将获得原始版本的注释字符串,但译者将看到两个翻译注释字符串,每个字符串都在不同的上下文中。
请注意,与 类似__()
,_x()
也有一个echo
版本:_ex()
。前面的例子可以写成:
_ex( 'Post', 'noun', 'my-plugin' );
_ex( 'Post', 'verb', 'my-plugin' );
使用您认为可以增强可读性和易于编码的任何一种。
描述
这样翻译人员就知道如何翻译字符串,就像__( 'g:i:s a' )
您可以在源代码中添加澄清注释一样。它必须以单词开头translators:
,并且是 gettext 调用之前的最后一个 PHP 注释。这是一个例子:
/* translators: draft saved date format, see http://php.net/date */
$saved_date_format = __( 'g:i:s a' );
它还用于解释字符串中的占位符,例如_n_noop( 'Version %1$s addressed %2$s bug.','Version %1$s addressed %2$s bugs.' )
.
/* translators: 1: WordPress version number, 2: plural number of bugs. */
_n_noop( 'Version %1$s addressed %2$s bug.','Version %1$sstrong> addressed %2$s bugs.' );
换行符
Gettext 不喜欢r
可翻译字符串中的 (ASCII 代码:13),因此请避免使用它并改用它n
。
空字符串
空字符串保留供内部 Gettext 使用,您不得尝试国际化空字符串。它也没有任何意义,因为翻译者看不到任何上下文。
如果您有一个有效的用例来国际化空字符串,请添加上下文以帮助翻译人员并与 Gettext 系统保持一致。
转义字符串
最好转义所有字符串,这样翻译器就无法运行恶意代码。有一些转义函数与国际化函数集成在一起。
本地化功能
基本功能
翻译和转义功能
需要翻译并在 html 标记的属性中使用的字符串必须进行转义。
日期和数字函数
编写字符串的最佳实践
以下是编写字符串的最佳实践
- 使用得体的英语风格——尽量减少俚语和缩写。
- 使用整个句子——大多数语言的词序与英语不同。
- 在段落中拆分 - 合并相关句子,但不要在一个字符串中包含整页文本。
- 不要在可翻译短语中留下前导或尾随空格。
- 假设翻译后字符串的长度可以加倍
- 避免不寻常的标记和不寻常的控制字符 - 不要包含文本周围的标签
- 不要将不必要的 HTML 标记放入翻译后的字符串中
- 不要留下 URL 进行翻译,除非它们有其他语言的版本。
- 将变量作为占位符添加到字符串中,就像在某些语言中占位符会更改位置一样。
printf(
__( 'Search results for: %s', 'my-plugin' ),
get_search_query()
);
- 使用格式字符串而不是字符串连接——翻译短语而不是单词——
printf( __( 'Your city is %1$s, and your zip code is %2$s.', 'my-plugin' ), $city, $zipcode );
总是比:__( 'Your city is ', 'my-plugin' ) . $city . __( ', and your zip code is ', 'my-plugin' ) . $zipcode;
- 尝试使用相同的单词和相同的符号,这样就不需要翻译多个字符串,例如
__( 'Posts:', 'my-plugin' );
和__( 'Posts', 'my-plugin' );
将文本域添加到字符串
您必须将 Text 域作为参数添加到每个__()
,_e()
和__n()
gettext 调用,否则您的翻译将无法工作。
例子:
__( 'Post' )
应该成为__( 'Post', 'my-theme' )
_e( 'Post' )
应该成为_e( 'Post', 'my-theme' )
_n( '%s post', '%s posts', $count )
应该成为_n( '%s post', '%s posts', $count, 'my-theme' )
如果您的插件中的某些字符串也在 WordPress 核心中使用(例如“设置”),您仍然应该向它们添加您自己的文本域,否则如果核心字符串发生更改(这种情况发生),它们将变为未翻译。
如果在编写代码时不连续添加文本域,则手动添加文本域可能会成为一种负担,这就是为什么您可以自动执行此操作:
- 将脚本下载
add-textdomain.php
到您要添加文本域的文件所在的文件夹 - 在命令行中移动到文件所在的目录
- 运行以下命令创建一个添加了文本域的新文件:
php add-textdomain.php my-plugin my-plugin.php > new-my-plugin.php
如果您希望将其放在add-textdomain.php
不同的文件夹中,则只需在命令中定义位置即可。
php /path/to/add-textdomain.php my-plugin my-plugin.php > new-my-plugin.php
如果您不想输出新文件,请使用此命令:
php add-textdomain.php -i my-plugin my-plugin.php
如果要更改目录中的多个文件,还可以将目录传递给脚本:
php add-textdomain.php -i my-plugin my-plugin-directory
完成后,文本域将被添加到文件中所有 gettext 调用的末尾。如果存在现有文本域,则不会被替换。
加载文本域
可以使用 加载翻译load_plugin_textdomain
,例如:
add_action( 'init', 'wpdocs_load_textdomain' );
function wpdocs_load_textdomain() {
load_plugin_textdomain( 'wpdocs_textdomain', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
WordPress.org 上的插件
笔记:由于 WordPress 4.6 翻译现在以translate.wordpress.org为优先级,因此通过translate.wordpress.org翻译的插件不再需要load_plugin_textdomain()
。如果您不想添加load_plugin_textdomain()
对插件的调用,则必须将Requires at least:
readme.txt 中的字段设置为 4.6 或更高。
如果您仍然想加载自己的翻译而不是来自 translate 的翻译,则必须使用名为 的钩子过滤器load_textdomain_mofile
。插件目录中的 .mo 文件
示例,并将此代码插入到主插件文件中:/languages/
function my_plugin_load_my_own_textdomain( $mofile, $domain ) {
if ( 'my-domain' === $domain && false !== strpos( $mofile, WP_LANG_DIR . '/plugins/' ) ) {
$locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
$mofile = WP_PLUGIN_DIR . '/' . dirname( plugin_basename( __FILE__ ) ) . '/languages/' . $domain . '-' . $locale . '.mo';
}
return $mofile;
}
add_filter( 'load_textdomain_mofile', 'my_plugin_load_my_own_textdomain', 10, 2 );
处理 JavaScript 文件
查看通用 API 手册的国际化 javascript部分,了解如何正确加载翻译文件。还有古腾堡插件文档页面。
语言包
如果您对语言包以及如何导入translate.wordpress.org感兴趣,请阅读有关翻译的元手册页面。
另请参阅多语言手册中的插件/主题作者指南以翻译您的项目。
国际化安全
在谈论国际化时,安全性常常被忽视,但有一些重要的事情需要牢记。
检查垃圾邮件和其他恶意字符串
当翻译人员向您提交本地化版本时,请务必检查以确保他们的翻译中不包含垃圾邮件或其他恶意词语。您可以使用谷歌翻译 将其翻译翻译回您的母语,以便您可以轻松比较原始字符串和翻译字符串。
转义国际化字符串
您不能相信翻译人员只会在其本地化中添加良性文本;如果他们愿意,他们可以添加恶意 JavaScript 或其他代码。为了防止这种情况发生,像对待任何其他不受信任的输入一样对待国际化字符串非常重要。
如果您要输出字符串,则应该对它们进行转义。
不安全:
_e( 'The REST API content endpoints were added in WordPress 4.7.', 'your-text-domain' );
安全的:
esc_html_e( 'The REST API content endpoints were added in WordPress 4.7.', 'your-text-domain' );
或者,有些人选择依赖翻译验证机制,而不是在代码中添加转义。验证机制的一个示例是WordPress Polyglots 团队用于translate.wordpress.org 的编辑者角色。这确保了不受信任的贡献者提交的任何翻译在被接受之前都已经过受信任的编辑的验证。
使用 URL 占位符
不要在国际化字符串中包含 URL,因为恶意翻译者可能会将它们更改为指向不同的 URL。相反,请使用printf()或 sprintf()的占位符。
不安全:
_e(
'Please register for a WordPress.org account.',
'your-text-domain'
);
安全的:
printf(
esc_html__( 'Please %1$s register for a WordPress.org account %2$s.', 'your-text-domain' ),
'',
''
);
编译您自己的 .mo 二进制文件
通常翻译人员会将编译后的 .mo 文件与纯文本 .po 文件一起发送,但您应该丢弃他们的 .mo 文件并编译自己的 .mo 文件,因为您无法知道它是否是从相应的 .po 文件编译的,或不同的一个。如果它是针对不同的版本进行编译的,那么它可能会在您不知情的情况下包含垃圾邮件和其他恶意字符串。
使用 PoEdit 生成二进制文件将覆盖 .po 文件中的标头,因此最好从命令行编译它:
msgfmt -cv -o /path/to/output.mo /path/to/input.po