多语言:__() / _e() / .po / .mo

作者: admin 分类: wordpress主题制作教程 发布时间: 2026-11-06 18:54

我自己的主题,第一行注释就写错了

这次排查我自己站的时候,把 style.css 的前十几行打印出来看,发现三个问题:

[BOM]/*
Theme Name: 草根站长笔记
Theme URI: http://www.caogenbj.com
Author: "草根站长笔记"
Author URI: http:/www.caogenbj.com
Description: 草根站长笔记主要是一波自己运营用的主题...
Version: 1.2
*/

三个问题:

  1. 开头有一个看不见的 BOM(字节 EF BB BF)。它是 UTF-8 文件的”签名”,但 PHP 和浏览器不认它,会当成内容输出;
  2. Author URI 少了一个斜杠——http:/ 不是 http://,这个链接点不开;
  3. 没有 Text Domain。

前两个是格式问题,第三个直接关系到这一篇的主题:没有 Text Domain,这个主题永远无法被翻译。

Text Domain 是什么

它是一串”小名”,用来标记”这句话属于哪个主题/插件”。同一台 WordPress 上跑着主题、插件、核心三部分,每部分都有自己的文字,翻译文件要能对上号,就靠这个小名区分。

规则很硬:style.css 里写的 Text Domain,必须和代码里 __() 用的那个字符串一模一样。 不一致,翻译就加载不上,页面上永远是原始英文(或原始中文)。

/*
Theme Name:  Caogenbj
Text Domain: caogenbj
*/

习惯上就用主题目录名,全小写,不加空格。

四个函数,按用途分

WordPress 有四组翻译函数,名字很像,用途不同:

函数 作用 示例
__() 翻译,返回字符串 $s = __( 'Read more', 'caogenbj' );
_e() 翻译,直接输出 _e( 'Read more', 'caogenbj' );
esc_html__() 翻译 + 转义,返回 echo esc_html__( 'Read more', 'caogenbj' );
_n() 单复数 见下

__() 和 _e() 的区别只有一条:一个返回,一个输出。

用在 echo 里面就必须用 __(),因为 _e() 已经输出了,你再 echo 一次,页面上就会多一个空白(它的返回值是 null)。

// 错:什么都没输出,还多一个空格
echo _e( 'Read more', 'caogenbj' );

// 对
echo __( 'Read more', 'caogenbj' );

// 对(顺手转义,更安全)
echo esc_html__( 'Read more', 'caogenbj' );

这个错我在别人的主题里见过不止一次,现象是”文字出来了,但后面总多一个空格”,很难查。

单复数用 _n():

printf(
    _n( '%s comment', '%s comments', $count, 'caogenbj' ),
    number_format_i18n( $count )
);

中文没有单复数变化,所以这个函数对中文主题意义不大——但如果你的主题要给别人用,就得写。

一个更细的函数:_x()

同一个英文词,在不同语境下中文翻译不一样。比如 Post,作为名词是”文章”,作为动词是”发布”。

_x( 'Post', 'noun', 'caogenbj' );
_x( 'Post', 'verb', 'caogenbj' );

第二个参数是”上下文”,给翻译者看的提示,不参与输出。主题里用得不多,但写公开主题时该用。

翻译文件是怎么来的

流程是这样的:

  1. 代码里所有文字都用 __() 包起来;
  2. 用工具把这些字符串扫出来,生成一个 .pot 模板文件;
  3. 翻译者拿 .pot 翻成 .po(可读文本);
  4. 编译成 .mo(二进制,WordPress 实际读这个);
  5. 放进 wp-content/languages/themes/ 或者主题的 languages/ 目录。

工具叫 Poedit,图形界面,选中 .pot 就能翻,保存时自动生成 .mo。

然后在 functions.php 里告诉 WordPress 去哪找:

add_action( 'after_setup_theme', function () {
    load_theme_textdomain( 'caogenbj', get_template_directory() . '/languages' );
} );

路径要用 get_template_directory()(父主题)。如果你在用子主题并且翻译文件放在子主题里,就换成 get_stylesheet_directory()。

中文主题,做这个有意义吗

说实话:如果这个主题只有你自己用,而且界面本来就是中文,那没什么意义。

但我还是建议你做,原因不是”翻译”,而是另外两件事:

一、把所有文案收拢到一处。

我原来的主题里,”阅读全文”这四个字散落在 index.php、category.php、search.php、tag.php 四个文件里。哪天想改成”查看详情”,得改四处,漏一处就不一致。

包成 __() 之后,虽然还是四处调用,但至少它们是”同一个字符串”——将来真要做统一管理,只需处理一处。

二、避免踩到”文本域缺失”的坑。

有些插件(比如多语言插件、某些缓存插件)会检查主题的 Text Domain。缺失时可能报警告,甚至影响功能。补一行注释就能避免,成本极低。

顺便说 BOM,它比你想的更有害

BOM 是文件开头那三个字节。它对 HTML 页面影响不大(浏览器会忽略),但对两类东西是致命的:

  • XML:<?xml 必须是文件第一个字节,前面多三个字节就不合法。站点地图就是这么坏的;
  • PHP 的 header():任何输出(包括 BOM)都会导致”headers already sent”报错。

所以:所有 PHP 文件、所有 CSS 文件,都不要带 BOM。 用编辑器的”以 UTF-8 无 BOM 保存”。

怎么检查?Linux 下一条命令:

head -c 3 style.css | xxd

如果输出是 efbb bf,就是有 BOM。

小结

一句话记住:Text Domain 是三处必须一致的字符串(style.css、函数调用、load_theme_textdomain);__() 返回、_e() 输出,别在 echo 里用 _e()。

下一篇换个范式:区块主题(FSE),WordPress 这几年的最大变化。

本系列导航

相关文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Powered by 草根站长笔记 © 2015-2026 草根站长笔记 Inc.版权所有,禁止转载