## 什么是国际化? 国际化是开发您的主题的过程,因此可以很容易地将其翻译成其他语言。 国际化通常缩写为i18n(因为i和n之间有18个字母)。 ## 为什么国际化很重要? WordPress在世界各地使用,这是一个很好的主意,准备WordPress主题,以便他们可以很容易地翻译成其他语言。 作为开发人员,您可能没有一个轻松的时间为所有用户提供本地化,但是任何翻译人员都可以在不修改源代码的情况下成功本地化主题。 ## 如何使您的主题国际化? 要使您的应用程序中的字符串可以翻译,您必须将原始字符串打包到一组特殊功能的调用中。 ## Gettext简介 WordPress使用i18n的gettext库和工具。 请注意,如果您在线查看,您将看到`_()`函数,它引用了本机PHP gettext兼容的翻译函数,而是使用WordPress,您应该使用`_()`WordPress定义的PHP函数。 如果您想获得更广泛和更深入的gettext视图,我们建议您阅读gettext在线手册。 ## 文字域 您需要使用文本域来表示属于该主题的所有文本。 文本域是唯一的标识符,可以确保WordPress能够区分所有加载的翻译。 这增加了可移植性,并且已经存在的WordPress工具更好。 文本域必须匹配主题的块。 如果您的主题名称My Theme在style.css中定义,或者它包含在名为my-theme的文件夹中,则域名应该是my-theme。 文本域名必须使用破折号而不是下划线并且小写。 字符串示例: ``` __( 'text', 'text-domain' ); ``` ``` __( 'String or text to be internationalized', 'text-domain' ); ``` 将“文本域”更改为主题。 文本域也需要添加到style.css标题中。 即使主题被禁用,WordPress也会使用它来国际化您的主题元数据。 文本域应与加载文本域时使用的文本域相同。 标题示例: ``` /* * Theme Name: My Theme * Author: Theme Author * Text Domain: my-theme */ ``` ## 域路径 域路径被使用,所以当主题被禁用时,WordPress知道在哪里找到翻译。 仅当翻译位于语言文件夹中被命名为不同于语言的语言文件夹时才有用。 例如,如果.mo文件位于languages文件夹中,则Domain Path将是/languages,必须用第一个斜杠写入。 默认为主题中的languages文件夹。 标题示例: ``` /* * Theme Name: My Theme * Author: Theme Author * Text Domain: my-theme * Domain Path: /languages */ ``` ## 基本字符串 最常用的函数是`__()`。 它返回其参数的翻译: ``` __( 'Blog Options', 'my-theme' ); ``` 另一个简单的是`_e()`,它输出其参数的翻译。 而不是写: ``` echo __( 'WordPress is the best!', 'my-theme' ); ``` 你可以使用较短的: ``` _e( 'WordPress is the best!', 'my-theme' ); ``` ## 变量 如果您使用字符串中的变量,类似于下面的示例,您需要使用占位符。 ``` echo 'Your city is $city.' ``` 使用printf系列函数。 特别有用的是printf和sprintf。 例如: ``` printf( __( 'Your city is %s.', 'my-theme' ), $city ); ``` >[warning] 请注意,用于翻译的字符串是“您的城市是%s”的模板,源和运行时都是相同的。 如果字符串中有多个占位符,建议您使用参数交换。 在这种情况下,单引号(')是必需的:双引号(“)告诉PHP将$s解释为s变量,这不是我们想要的。 ``` printf( __( 'Your city is %1$s, and your zip code is %2$s.', 'my-theme' ), $city, $zipcode ); ``` 这里的邮政编码显示在城市名称之后。 在某些语言中,以相反的顺序显示邮政编码和城市是比较合适的。 使用%s前缀,如上例所示,允许这样做。 翻译可以写成: ``` printf( __( 'Your zip code is %2$s, and your city is %1$s.', 'my-theme' ), $city, $zipcode ); ``` 以下示例告诉您什么不做 >[warning] 警告:这是不正确的。 ``` // 这不正确不要使用。 _e( 'Your city is $city.', 'my-theme' ); ``` 用于翻译的字符串从源中提取而不执行与之关联的PHP。 例如:变量$ city可能是温哥华,所以当模板运行时,你的字符串将会显示为“你的城市是温哥华”,但是gettext不会提前知道PHP变量里面是什么。 由于当您的字符串被翻译时变量的值是未知的,所以需要翻译器知道变量$ country的每个案例。 这不是理想的,最好删除动态内容,并允许翻译者专注于静态内容。 ## 基本多元化 如果您的项目数量更改时有一个字符串更改。 在英语中你有“一个评论”和“两个意见”。 在其他语言中,您可以有多个复数形式。 要在WordPress中处理这个问题,可以使用`_n()`函数。 ``` printf( _n( 'One comment', '%s comments', get_comments_number(), 'my-theme' ), number_format_i18n( get_comments_number() ) ); ``` `_n()` 接受4个参数: - singular – 字符串的单数形式 - plural – 字符串的复数形式 - count – 对象的数量,这将决定是否应该返回单数或复数形式(有多种语言,有两种以上的形式) - text domain – 主题的文本域 功能的返回值是正确的翻译形式,对应于给定的计数。 ## 稍后进行多元化 您首先使用`_n_noop()`或`_nx_noop()`设置多个字符串。 ``` $comments_plural = _n_noop( 'One comment.', '%s comments.' ); ``` 在稍后的代码中,您可以使用`translate_nooped_plural`来加载字符串。 ``` printf( translate_nooped_plural( $comments_plural, get_comments_number(), 'my-theme' ), number_format_i18n( get_comments_number() ) ); ``` ## 背景消歧 有时一个术语在多个上下文中使用,并且必须以其他语言单独翻译,即使用英文每个语境使用相同的单词。 例如,Post可以用作动词“点击这里发表你的评论”,作为名词“编辑这篇文章”。 在这种情况下,应该使用`_x()`或`_ex`函数。 它类似于`__()`和`_e()`,但它有一个附加参数 - 上下文: ``` _x( 'Post', 'noun', 'my-theme' ); _x( 'post', 'verb', 'my-theme' ); ``` 在这两种情况下使用这种方法,我们得到原始版本的字符串注释。 然而,翻译者将看到两个用于翻译的注释字符串,每个字符串在不同的上下文中。 以德文版WordPress为例:Post isBeiträge。 德语中相应的动词形式是beitragen。 >[danger] 请注意,类似于`__()`, `_x()`有一个回声版本:`_ex()`。 前面的例子可以写成: ``` _ex( 'Post', 'noun', 'my-theme' ); _ex( 'post', 'verb', 'my-theme' ); ``` 使用你觉得增强易读性和易于编码的。 ## 描述 您可以在源代码中添加一个澄清的注释,所以翻译人员知道如何翻译一个字符串`__('g:i:s a')`。 它必须以翻译者的语言开头:并且是gettext调用之前的最后一个PHP注释。 这是一个例子: ``` /* translators: draft saved date format, see http://php.net/date */ $saved_date_format = __( 'g:i:s a' ); ``` ## 换行字符 Gettext不喜欢`\r`(ASCII码:13)在可翻译的字符串,所以避免它,并使用`\n`代替。 ## 空字符串 空字符串保留用于内部Gettext使用,您不得尝试将空字符串国际化。 它也没有任何意义,因为翻译者不会有上下文。 如果您有一个有效的用例来使一个空字符串国际化,请添加上下文以帮助翻译人员,并与Gettext系统保持一致。 ## 处理JavaScript文件 使用`wp_localize_script()`将已翻译的字符串或其他服务器端数据添加到先前排入的脚本。 ## 逃避字符串 逃避所有的字符串是很好的,防止翻译者运行恶意代码。 有几个与国际化功能相结合的逃生功能。 ``` <a title="<?php esc_attr_e( 'Skip to content', 'my-theme' ); ?>" class="screen-reader-text skip-link" href="#content" > <?php _e( 'Skip to content', 'my-theme' ); ?> </a> ``` ``` <label for="nav-menu"> <?php esc_html_e( 'Select Menu:', 'my-theme' ); ?> </label> ``` ## 基本功能 - __() - _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-theme' ), get_search_query() ); ``` 使用格式字符串而不是字符串连接 - 翻译短语而不是单词 - ``` printf( __( 'Your city is %1$s, and your zip code is %2$s.', 'my-theme' ), $city, $zipcode ); ``` 总是比好 ``` __( 'Your city is ', 'my-theme' ) . $city . __( ', and your zip code is ', 'my-theme' ) . $zipcode; ``` 尝试使用相同的单词和符号来防止翻译多个相似的字符串(例如,请勿执行以下操作) ``` __( 'Posts:', 'my-theme' ); and __( 'Posts', 'my-theme' ); ``` ## 将文本域添加到字符串 您必须将您的Text域作为参数添加到每个`__()`,`_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' ) ``` ## 加载文本域 您需要使用主题翻译加载MO文件。 您可以通过调用load_theme_textdomain()函数加载它们。 此调用从您的主题基础目录或{text-domain} - {locale} .mo从WordPress主题语言文件夹中加载{locale} .mo。 语言环境是您在文件wp-config.php中的常量WPLANG中定义的语言代码和/或国家/地区代码。 例如,德语的语言环境是de_DE。 所以代码需要wp-config.php将被定义('WPLANG','de_DE');. 有关语言和国家/地区代码的更多信息,请参阅您的语言中的WordPress。 小心 将MO文件命名为{locale} .mo(例如de_DE.po&de_DE.mo),如果将翻译添加到主题文件夹。 将您的MO文件命名为{text-domain} - {locale} .mo(例如my-theme-de_DE.po&my-theme-de_DE.mo),如果要将翻译添加到WordPress主题语言文件夹。 例: ``` function my_theme_load_theme_textdomain() { load_theme_textdomain( 'my-theme', get_template_directory() . '/languages' ); } add_action( 'after_setup_theme', 'my_theme_load_theme_textdomain' ); ``` 这个功能最好在主题'function.php中运行 >[danger] 注意:WordPress 4.6发布后,翻译现在以translate.wordpress.org为优先,因此通过translate.wordpress.org翻译的主题不再需要load_theme_textdomain()了。 但是,没有任何伤害离开这条线。 ## 资源 - Video: i18n: Preparing Your WordPress Theme for the World - Video: On Internationalization: Plugins and themes for the whole world Slides - Video: Big in Japan: A Guide for Themes and Internationalization - Video: Lost in Translation—i18n and WordPress - Internationalizing And Localizing Your WordPress Theme - Internationalization: You’re probably doing it wrong - More Internationalization Fun - Translating custom page template names - Use wp_localize_script, It Is Awesome - Understanding _n_noop() - Language Packs 101 – Prepwork - Translating WordPress Plugins and Themes: Don’t Get Clever - How to load theme and plugin translations - Loading WordPress language files the right way