如何国际化插件

为了使您的应用程序中的字符串可以翻译,您必须将原始字符串打包到一组特殊功能的调用中。

Gettext简介

WordPress使用i18n的gettext库和工具。 如果你在网上看,你会看到_()函数,它引用了本地PHP gettext兼容的翻译功能。 使用WordPress,您应该使用__()WordPress定义的PHP函数。 如果您想获得更广泛和更深入的gettext视图,我们建议您阅读gettext在线手册。

文字域

使用文本域来表示属于该插件的所有文本很重要。 文本域是唯一的标识符,可以确保WordPress能够区分所有加载的翻译。 这增加了可移植性,并通过已经存在的WordPress工具更好地发挥作用。 文本域必须与插件的插槽相匹配。 如果您的插件是一个名为my-plugin.php的文件,或者它包含在一个名为my-plugin的文件夹中,则域名应为my-plugin。 如果您的插件托管在wordpress.org上,那么它必须是插件URL(wordpress.org/plugins/)中的一小部分。

文本域名必须使用破折号而不是下划线。

字符串示例:

__( 'String (text to be internationalized)', 'text-domain' );

将“文本域”更改为插件的插件。

警告:不要在gettext函数的文本域部分使用变量名。 不要做这个快捷方式:__('Translate me。',$ text_domain);

文本域也需要添加到插件头。 即使插件被禁用,WordPress也使用它来国际化你的插件元数据。 文本域应与加载文本域时使用的文本域相同。

标题示例:

/*
 * Plugin Name: My Plugin
 * Author: Plugin Author
 * Text Domain: my-plugin
 */

域路径

使用域路径,以便当插件被禁用时,WordPress知道在哪里找到翻译。 仅当翻译位于单独的语言文件夹中时才有用。 例如,如果.mo文件位于languages文件夹中,则Domain Path将是“/languages”,必须用第一个斜杠写入。 默认为插件的languages文件夹:

/*
 * Plugin Name: My Plugin
 * Author: Plugin Author
 * Text Domain: my-plugin
 * Domain Path: /languages
 */

基本字符串

最常用的是__()。 它只是返回其参数的翻译:

__( 'Blog Options', 'my-plugin' );

另一个简单的是_e(),它输出其参数的翻译。 而不是写:

echo __( 'WordPress is the best!', 'my-plugin' );

你可以使用较短的:

_e( 'WordPress is the best!', 'my-plugin' );

变量

如果您在字符串中使用变量,如下面的示例,您将使用占位符。

echo 'Your city is $city.'

解决方案是使用printf系列函数。 特别有用的是printf和sprintf。 以下是正确的解决方案:

printf(
    /* translators: %s: Name of a city */
    __( 'Your city is %s.', 'my-plugin' ),
    $city
);

请注意,这里的翻译字符串只是模板“你的城市是%s”,这在源代码和运行时都是一样的。

如果字符串中有多个占位符,建议您使用参数交换。 在这种情况下,单引号(')是强制性的:双引号(“)将告诉PHP将$ 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' );

翻译的字符串是从源中提取出来的,所以翻译人员会得到这个短语:“你的城市是$ city”。

但是在应用程序中,将会被称为“你的城市是伦敦”。 gettext将不会找到适合的翻译,并返回其论点:“你的城市是伦敦”。 不幸的是,它没有正确翻译。

多个

##基本多元化

如果在项目数量更改时有变化的字符串。 在英语中你有“一个评论”和“两个意见”。 在其他语言中,您可以有多个复数形式。 要在WordPress中处理这个问题,可以使用_n()函数。

printf(
    _n(
        '%s comment',
        '%s comments',
        get_comments_number(),
        'my-plugin'
    ),
    number_format_i18n( get_comments_number() )
);

_n() 接受4个参数:

  • singular – 字符串的单数形式(注意,它可以用于某些语言中的一个以外的数字,因此应使用“%s项”而不是“一个项”)
  • plural – 字符串的复数形式
  • count – 对象的数量,这将决定是否应该返回单数或复数形式(有多种语言,有两种以上的形式)
  • text domain – 插件文本域

功能的返回值是正确的翻译形式,对应于给定的计数。

稍后完成

您首先使用 _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可以用作动词“点击这里发表你的评论”,作为名词“编辑这篇文章”。 在这种情况下,应该使用 _x()_ex() 函数。 它类似于 __()_e() ,但它有一个附加的参数 - 上下文:

_x( 'Post', 'noun', 'my-plugin' );
_x( 'Post', 'verb', 'my-plugin' );

在这两种情况下使用此方法,我们将获得原始版本的字符串注释,但翻译器将看到两个用于翻译的注释字符串,每个字符串在不同的上下文中。

请注意,与__()类似,_x()具有回调版本:_ex()。 前面的例子可以写成:

_ex( 'Post', 'noun', 'my-plugin' );
_ex( 'Post', 'verb', 'my-plugin' );

使用您觉得增强易读性和易于编码的任何一个。

说明

所以翻译人员知道如何翻译一个字符串__('g:i:s a'),你可以在源代码中添加一个澄清的注释。 它必须从翻译人员开始:在gettext调用之前成为最后一个PHP注释。 这是一个例子:

/* translators: draft saved date format, see http://php.net/date */
$saved_date_format = __( 'g:i:s a' );

它还用于解释字符串中的占位符,如_n_noop(版本%1 $ s 已解决%2 $ s错误,版本%1 $ s “解决了%2 $ s的错误”)。

/* translators: 1: WordPress version number, 2: plural number of bugs. */
_n_noop( '<strong>Version %1$s</strong> addressed %2$s bug.',
         '<strong>Version %1$s</strong> addressed %2$s bugs.' );

换行字符

Gettext不喜欢 \ r (ASCII码:13)在可翻译的字符串,所以请避免它,并使用 \ n 代替。

空字符串

空字符串保留用于内部Gettext使用,您不得尝试将空字符串国际化。 它也没有任何意义,因为翻译者不会看到任何上下文。

如果您有一个有效的用例来使一个空字符串国际化,请添加上下文以帮助翻译人员,并与Gettext系统保持一致。

处理JavaScript文件

使用 wp_localize_script() 将已翻译的字符串或其他服务器端数据添加到先前排入的脚本。

转义字符串

逃避所有的字符串是好的,这样翻译者就不能运行恶意代码。 有几个与国际化功能相结合的逃生功能。

本地化功能

基本功能

  • __()
  • _e()
  • _x()
  • _ex()
  • _n()
  • _nx()
  • _n_noop()
  • _nx_noop()
  • translate_nooped_plural()

翻译和退出功能

必须转义需要翻译并在HTML标签的属性中使用的字符串。

  • esc_html__()
  • esc_html_e()
  • esc_html_x()
  • esc_attr__()
  • esc_attr_e()
  • esc_attr_x()

日期和数字功能

  • number_format_i18n()
  • date_i18n()

写字符串的最佳做法

以下是编写字符串的最佳做法

  • 使用体面的英式风格 - 尽量减少俚语和缩写。
  • 使用整个句子 - 在大多数语言中,单词顺序与英语不同。
  • 拆分段落 - 合并相关句子,但不要在一个字符串中包含整个文本页面。
  • 不要将前导或尾随空格留在可翻译的短语中。
  • 假设翻译时字符串的长度可以翻倍
  • 避免不正常的标记和不寻常的控制字符 - 不要包含文本周围的标签
  • 不要将不必要的HTML标记放入已翻译的字符串中
  • 不要留下翻译的网址,除非他们可以使用其他语言的版本。
  • 将变量作为占位符添加到字符串中,如在某些语言中,占位符更改位置。
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');

将文本域添加到字符串

您必须将您的文本域作为参数添加到每个 __()_e()__n() gettext调用中,否则您的翻译将无法正常工作。

例子:

__( 'Post' )

应该成为

__( 'Post', 'my-theme' )
_e( 'Post' )

应该成为

_e( 'Post', 'my-theme' )
_n( 'One post', '%s posts', $count )

应该成为

_n( 'One post', '%s posts', $count, 'my-theme' )

如果您的插件中也有用于WordPress核心的字符串(例如“设置”),那么您还应该添加自己的文本域,否则如果核心字符串发生变化(如果发生),那么它们将变为非翻译。

手动添加文本域可能是一个负担,如果不是在编写代码时连续完成,这就是为什么你可以自动执行:

如果您的插件位于WordPress.org插件目录中,请转到您的“管理”页面,然后滚动到“添加域到Gettext呼叫”。上传要添加文本域的文件。点击“获取域文件”。[

除此以外:

将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调用的末尾。 如果存在现有的文本域,则不会被替换。

加载文本域

注意:WordPress 4.6出来之后,翻译现在以translate.wordpress.org为优先,因此通过translate.wordpress.org翻译的插件不再需要load_plugin_textdomain()了。 如果您不想在您的插件中添加一个load_plugin_textdomain()调用,则必须将readme.txt中的至少要求至少为:。

您需要使用插件的翻译加载MO文件。 您可以通过调用函数 load_plugin_textdomain() 来加载它们。 此调用从插件的基本目录载入 {text-domain} - {locale} .mo 。 语言环境是常规设置下的站点语言设置的语言代码和/或国家/地区代码。 有关语言和国家/地区代码的更多信息,请参阅您的语言中的WordPress。

从上面的代码示例,文本域是my-plugin,因此德语MO和PO文件应该命名为my-plugin-de_DE.mo和my-plugin-de_DE.po。

function my_plugin_load_plugin_textdomain() {
    load_plugin_textdomain( 'my-plugin', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' );
}
add_action( 'plugins_loaded', 'my_plugin_load_plugin_textdomain' );

语言包

如果您对语言包感兴趣,以及如何进行translate.wordpress.org的导入,请阅读关于翻译的Meta手册页面。

下一节:在谈论国际化时,安全往往被忽视,但有一些重要的事情要记住。