ThinkPHP 的 lang() 函数仅支持竖线分隔的二元复数语法如 'item|items',仅当参数为整数 1 时取左,其余值(含 0、-1、1.0)均取右,不支持多段式、范围判断、嵌套占位符或 ICU/ gettext 格式。

lang() 函数的复数语法只支持 item|items 这一种写法
ThinkPHP 不兼容 gettext 的 ngettext 或 ICU MessageFormat,也不识别 {count, plural, one {...} other {...}} 这类结构。它只认竖线分隔的简单二元复数:'user|users'。
调用时传入数字即可自动选边:
-
lang('user|users', 1)→ 返回左边'user' -
lang('user|users', 0)→ 返回右边'users' -
lang('user|users', 5)→ 返回右边'users'
注意:不是按“是否等于 1”判断,而是“是否为 1”——只有整数 1 才取前半部分,其余所有值(包括 0、-1、1.0)都取后半部分。没有三段式(如 zero/one/other)或范围判断能力。
语言包里写复数项必须和调用完全一致
复数键名不是独立配置项,而是直接作为 key 写在语言包数组里。比如你在 lang/zh-cn/common.php 中写了:
立即学习“PHP免费学习笔记(深入)”;
return [
'post|posts' => '文章|文章',
'comment|comments' => '评论|评论',
];
那么必须用 lang('post|posts', $count) 调用才生效;写成 lang('post', $count) 或 lang('posts', $count) 都会原样返回字符串,不会触发复数逻辑。
常见错误:
- 语言包中写
'post' => '文章',却在代码里传lang('post|posts', 2)→ 不匹配,返回'post|posts' - 语言包中写
'post|posts' => '文章'(只写了一个值)→ 右边缺失,lang('post|posts', 2)返回空字符串 - 键名含空格或特殊字符,如
'post | posts'→ 匹配失败
复数不支持嵌套占位符,也不能和 {key} 混用
lang() 的复数机制和占位符是两套独立解析逻辑,不能交叉使用。以下写法全部无效:
-
'{name} post|{name} posts'→ 占位符在复数结构内,不解析 -
'{name} post|{name} posts'配合lang('...', ['name' => 'Tom'], 2)→ 参数顺序错乱,抛出警告或静默失败 -
'post|posts' => '{count} 篇文章|{count} 篇文章'→{count}不会被替换,因为复数分支里不走占位符渲染
正确做法是把数量放到占位符里,复数逻辑交给前端或业务层判断:
lang('you_have_x_posts', ['count' => $n])
对应语言包:'you_have_x_posts' => '你有 {count} 篇文章'(中文无需复数区分),英文则可写:'you_have_x_posts' => 'You have {count} post|You have {count} posts' ——但注意:这里 {count} 仍不会被替换,只是当作文本字面量显示。
复数失效时优先检查 Lang::range() 输出
当 lang('item|items', 2) 返回原字符串而非翻译内容,大概率是语言包根本没加载,或 key 名拼错。别急着改逻辑,先确认运行时实际加载了哪些键:
- 在中间件或控制器开头加一行:
dump(\think\Lang::range()); - 看输出数组里是否有
'item|items'这个完整 key - 如果没有,说明语言包路径错(比如用了
zh_CN)、文件名错(不是common.php)、或Lang::setLang()调用太晚(已在 lang() 之后执行)
特别注意:Linux 下 lang/zh-cn/common.php 和 lang/ZH-CN/common.php 是两个不同路径,后者完全不会被扫描。



















