__('welcome') 返回空或原样字符串,是因为 laravel 未找到对应翻译:需检查 resources/lang/{locale}/messages.php 是否存在且路径与 config/app.php 中 locale 值一致、文件以 return [] 开头、键名完全匹配;若用 json 格式,则必须传原文字符串而非键名。

直接用 __() 就能工作,但翻不出来、报错或占位符不替换,基本是语言文件路径不对、键名写错、locale 没生效,或者用了 JSON 文件却按 PHP 数组方式引用。
为什么 __('welcome') 返回空或原样字符串
这是最常见误判点:不是函数失效,而是 Laravel 找不到对应翻译。关键检查三处:
-
resources/lang/{locale}/messages.php是否存在,且{locale}和config/app.php中的'locale'值一致(比如设了'zh_CN',就得有resources/lang/zh_CN/messages.php) - 文件必须以
return [...]开头,不能漏掉return或写成echo输出 - 键名要完全匹配,
__('welcome')查的是messages.php顶层键'welcome',不是子数组或嵌套结构 - 如果用的是 JSON 方式(如
resources/lang/zh_CN.json),就不能用__('welcome'),得写成__('Welcome to our site!')—— 此时键就是原文字符串本身
__() 和 trans() 有什么区别
没区别,trans() 就是 __() 的别名,底层调同一个 Translator 实例。选哪个纯看团队习惯:
- 在 Blade 模板里更常见
{{ __('key') }},视觉上轻量 - 在控制器或服务类里,有人倾向
trans('key'),语义更直白 - 两者都支持参数数组:
__('Hello :name', ['name' => $user->name])和trans('Hello :name', ['name' => $user->name])行为完全一致 - 都不做自动 HTML 转义,输出含用户输入的内容时,需自行用
{{ }}(自动转义)而非{!! !!}
带复数的翻译怎么写才不出错
别硬套单复数逻辑,Laravel 的 @choice 和 trans_choice() 依赖语言文件里的「管道分隔复数规则」,不是简单 if-else:
- 在
resources/lang/en/messages.php中定义时,必须用竖线|分隔不同数量区间:'apples' => '{0} No apples|{1} One apple|[2,*] :count apples' - 调用时传入的是**数字本身**,不是布尔值:
@choice('messages.apples', $count, ['count' => $count]),其中第一个参数是键,第二个是数量,第三个才是占位符替换数组 -
trans_choice()在 PHP 代码中用法相同,但注意:Laravel 9+ 已标记为 legacy,新项目建议统一用__()配合 ICU MessageFormat(需启用message_format驱动) - 中文几乎不用复数规则(没有语法单复数),所以
{0}、{1}、[2,*]这套对中文意义不大,强行套用反而容易漏分支
语言文件该用 PHP 还是 JSON 格式
取决于项目规模和协作方式,不是技术高低问题:
- 小到中型项目、翻译项少于 500 条、团队熟悉 PHP:用
resources/lang/en/messages.php。加载快、IDE 支持好、可写注释、能动态计算(虽然不推荐) - 大型项目、多语言并行、有专职翻译人员、需要导出/导入工具链:用
resources/lang/en.json。JSON 更易被 i18n 工具识别,也避免 PHP 语法错误导致整个语言包崩掉 - 混用可以,但别在一个语言下既放
messages.php又放messages.json—— Laravel 会优先读 PHP,JSON 被忽略 - 无论哪种,键名都不能含点号以外的特殊字符;PHP 文件里键名区分大小写,JSON 文件里也是
最容易被忽略的是 fallback 机制:当 zh_CN/messages.php 里缺某个键,Laravel 不会自动去 en/messages.php 找,除非你在 config/app.php 明确设置了 'fallback_locale' => 'en'。没配这个,缺键就直接返回原始字符串或空,而不是降级取英文。











