根本原因是paginate()只解析page和list_rows,不自动处理lang参数;必须手动通过query透传lang、where过滤语言、缓存key含语言、render前用Lang::get()翻译文案,并显式指定page参数以避免重复解析。

ThinkPHP 多语言分页链接掉回默认语言,根本原因不是语言没设好,而是 paginate() 根本不认 lang 参数——它只解析 page 和 list_rows,其他全靠你手动塞。
为什么分页一翻页就变中文(或变英文)?
因为 render() 生成的上一页/下一页链接里没带 lang=zh-cn 这类参数。即使 Lang::setLocale('ja-jp') 已生效,链接本身不含语言标识,浏览器点过去就是裸 ?page=2,框架只能 fallback 到 app.default_lang。
常见错误写法:User::where('status', 1)->paginate(15) —— 没传 query,lang 彻底丢失input('lang') 直接写死在 where 里,但没透传到分页链接中
- 语言来源可能是 URL、cookie、session 或 header,但
paginate()不自动读取其中任何一个 -
appends(['lang' => 'en-us'])看似可行,但若用户本来是通过?lang=fr-fr&keyword=abc进来的,appends会覆盖掉keyword,搜索条件反而丢了 - 漏掉
false第二个参数(即关闭自动解析 URL)会导致page被重复提取,比如?page=2&lang=de可能被当成第 4 页
必须显式传入 query 参数并过滤 page
正确做法是:先统一取当前语言值,再把它和其他有效查询参数一起塞进 query 数组,同时确保 page 键由 input('page/d', 1) 显式控制。
立即学习“PHP免费学习笔记(深入)”;
示例代码:
$lang = input('lang', config('app.default_lang'));
$users = User::where('lang', $lang)
->paginate(15, false, [
'page' => input('page/d', 1),
'query' => array_merge(
array_diff_key(input(), ['page' => '']),
['lang' => $lang]
),
]);
-
array_diff_key(input(), ['page' => ''])是为了安全剔除原始page,避免和上面显式的'page' => ...冲突 - 如果语言来自 session,就用
session('lang', config('app.default_lang'))替代input('lang', ...),再同样塞进query -
query里的键名必须和你实际需要的 URL 参数名一致,比如用locale就写['locale' => $lang],不能硬套lang
render() 前必须用 lang::get() 翻译文案
分页 HTML 中的“上一页”“共 12 页”等文字不会自动走多语言翻译。如果你模板里直接写 <a href="...">Previous</a>,切换语言后这些字永远是英文。
ThinkPHP 的 render() 默认模板用的是内置字符串,不经过 Lang::get()。所以要么重写模板,要么在调用前临时替换文案:
$users->setConfig([
'prev_page' => lang::get('Previous'),
'next_page' => lang::get('Next'),
'first_page' => lang::get('First'),
'last_page' => lang::get('Last'),
'total_rows' => lang::get('Total %d rows'),
]);
echo $users->render();
- 所有文案必须提前定义在对应语言的
lang/zh-cn.php或lang/en-us.php里,否则返回空字符串 - 像
"Page %d of %d"这种带占位符的,要用sprintf(lang::get('Page %d of %d'), $users->currentPage(), $users->lastPage()),不能拼字符串 - 别在视图层用
__('Previous')这类函数——render()输出的是已渲染完的 HTML 字符串,不是模板变量
缓存 key 和数据库 WHERE 必须含语言维度
如果用了 Redis 缓存分页结果,或者内容表是多语言共存(如 articles 表有 lang 字段),漏掉语言隔离会导致数据错乱。
错误示例:SELECT * FROM articles LIMIT 10 OFFSET 10 —— 没 WHERE lang = ?,第 2 页可能混着中英文文章Cache::get('articles:page:2') —— key 没带语言,日语用户和英语用户共享同一份缓存
- SQL 查询必须加
where('lang', $lang),且该条件要在paginate()调用前完成 - 缓存 key 示例:
articles:'.$lang.':page:'.$page,确保不同语言互不干扰 - 如果用了
total(),它依赖 COUNT 查询结果;若该 COUNT 没加lang条件,总数就不准,进而影响页码总数显示
最易被忽略的一点:分页对象一旦调用过 toArray() 或 json(),就再也无法调用 render() —— 因为分页上下文(包括 query 配置、文案配置、总条数元数据)全丢了。要渲染,就得在转数组前完成 render() 调用。



















