Workerman本身不提供国际化能力,多语言逻辑必须由业务层实现;推送前需确定用户语言并发送翻译后消息,因onConnect无法获取HTTP请求头,且语言是用户属性而非连接属性。

Workerman 本身不提供国际化(I18n)能力,它只负责消息的实时传输;多语言逻辑必须由业务层实现——也就是说,**推送前就要确定目标用户语言,再把对应翻译后的消息发出去**。不能指望 Workerman 自动翻译或根据请求头选语言。
为什么不能在 Workerman 连接层做语言自动识别
Workerman 的 onConnect 回调里拿不到 HTTP 请求头(如 Accept-Language),因为 WebSocket 握手虽基于 HTTP,但连接建立后即升为二进制帧通道,原始 header 不会持续透传。你看到的 $connection->getRemoteIp() 或 $connection->context 里没有语言信息。
- 常见错误:在
onConnect里试图用$_SERVER['HTTP_ACCEPT_LANGUAGE']—— 这是空的,PHP 超全局变量在 Workerman 进程中不可用 - 真实场景:用户登录后通过 WebSocket 发送一个带
locale: "zh_CN"的初始化消息,你得靠这个来绑定语言上下文 - 安全风险:若靠 IP 地理位置推断语言,会误判海外华人、跨国企业员工等群体
如何让每个连接关联正确语言并推送对应文案
核心是在连接生命周期内维护一个「连接 ID → 语言标识」映射,并在 onMessage 或业务触发推送时查表取翻译结果。推荐用 GatewayWorker 架构,因其自带 Gateway::bindUid() 和自定义 session 支持。
- 用户登录成功后,服务端调用
Gateway::bindUid($client_id, $user_id),同时把$user_id对应的语言存到 Redis,键如user:lang:$user_id - 推送前,先用
Gateway::getClientIdByUid($user_id)拿到所有在线 client_id,再批量查 Redis 获取各连接对应语言 - 翻译动作必须在推送前完成:比如
i18n('welcome', $lang)返回"欢迎"或"Welcome",再塞进$connection->send() - 避免在
onMessage中同步调用耗时翻译函数(如读文件、查 DB);提前加载好语言包到内存,用数组 key-value 查找
Webman + Workerman 混合项目中的语言分流陷阱
如果你用 Webman 做 HTTP 接口、Workerman 做 WebSocket 推送,**二者语言配置不能共用同一套加载逻辑**。Webman 的 i18n() 函数依赖其 App 实例和请求上下文,而 Workerman 进程无 Request 对象。
- 错误做法:在 Workerman 的
onMessage里直接调用 Webman 的i18n('hello')—— 会报Call to a member function getLocale() on null - 正确做法:把 Webman 的语言文件(如
resources/lang/zh_CN.php)导出为纯 PHP 数组,在 Workerman 启动时用require_once加载到全局变量,例如$LANGS['zh_CN']['hello'] = '你好' - 注意路径差异:Webman 默认从
resources/lang加载,Workerman 进程工作目录通常是start.php所在目录,需写绝对路径或用__DIR__定位 - 热更新难:语言包改了要重启 Workerman 进程,不像 Webman 可靠缓存机制自动刷新
真正容易被忽略的点是:**语言不是连接属性,而是用户属性;而一个用户可能多个设备在线(Web + App),每个连接语言还可能不同(比如用户在网页切了语言但 App 还没同步)**。所以不要绑定在 $client_id 上,而要以 $uid 为粒度查语言,并允许单次推送指定语言参数。

















