- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
本文以 Hyperf 官方国际化组件hyperf/translation为核心,系统讲解如何在 Hyperf 项目中搭建多语言支持:从安装、语言文件组织、语言环境配置,到翻译字符串、占位符替换与复数规则处理,并深入到源码层解析键解析、回退链与协程安全实现。读完本文,你将能独立为 Hyperf 应用接入完整的 i18n 能力,并理解其底层工作机制。
安装与组件定位
Hyperf 的国际化组件可通过 Composer 直接安装:
composer require hyperf/translation从 composer.json 可以看出,该组件是一个独立组件,仅依赖hyperf/collection、hyperf/context、hyperf/contract、hyperf/macroable、hyperf/stringable、hyperf/support与psr/container,要求 PHP >= 8.2。这意味着它不绑定框架的完整运行环境,可以独立复用于其它项目或框架中。
组件通过ConfigProvider完成与框架的对接(见 ConfigProvider.php):
- 将
TranslatorInterface绑定到TranslatorFactory,将TranslatorLoaderInterface绑定到FileLoaderFactory; - 注册配置发布项,把 publish/translation.php 发布到项目的
config/autoload/translation.php。
安装后可以通过以下命令发布配置(Hyperf 标准的配置发布机制):
php bin/hyperf.php vendor:publish hyperf/translation语言文件的组织与编写
Hyperf 的语言文件默认放在storage/languages目录下,也可以在config/autoload/translation.php内更改语言文件的文件夹。每种语言对应其中的一个子文件夹,例如en指英文语言文件,zh_CN指中文简体的语言文件,你可以按照实际需要创建新的语言文件夹和里面的语言文件。目录结构示例如下:
/storage /languages /en messages.php /zh_CN messages.php所有的语言文件都是返回一个数组,数组的键是字符串类型的:
<?php // storage/languages/en/messages.php return [ 'welcome' => 'Welcome to our application', ];语言文件的加载由 FileLoader 完成:它根据路径/语言文件夹/组名.php的约定查找文件(loadPath方法),并将文件 return 的数组作为该语言该组(group)的翻译行集合。这里的「组名」对应语言文件名,也就是翻译键的第一段,例如messages.php对应messages组。
除了 PHP 数组文件,FileLoader还支持JSON 语言文件(loadJsonPaths方法):每个语言目录下的{locale}.json文件会被加载为键值映射,并支持通过addJsonPath()注册额外的 JSON 路径。JSON 文件被解析时如果结构非法,会抛出RuntimeException("Translation file [...] contains an invalid JSON structure."),便于尽早发现问题。此外,组件还提供 ArrayLoader,一个纯内存的加载器,可通过addMessages()在运行时直接注入翻译内容,适合测试或动态语言包场景。
命名空间(Namespace)支持
从 Translator.php 的parseKey方法可以看到,翻译键支持两种形式:
- 普通键:
组.条目,如messages.welcome; - 命名空间键:
命名空间::组.条目,如module::messages.welcome。
命名空间通过addNamespace($namespace, $hint)注册,对应的语言文件存放在 hint 指定的目录下;同时FileLoader的loadNamespaceOverrides还支持在{path}/vendor/{namespace}/{locale}/{group}.php中放置覆盖文件,对组件提供的翻译进行局部覆盖——这在扩展包(Package)场景中非常实用。
配置语言环境
国际化组件的相关配置都在config/autoload/translation.php配置文件中设定,你可以按照实际需要修改它:
<?php // config/autoload/translation.php return [ // 默认语言 'locale' => 'zh_CN', // 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本 'fallback_locale' => 'en', // 语言文件存放的文件夹 'path' => BASE_PATH . '/storage/languages', ];这三个配置项的读取分别发生在两个工厂中:
- TranslatorFactory 通过
config->get('translation.locale', 'zh_CN')与config->get('translation.fallback_locale', 'en')读取默认语言与回退语言,构造Translator后调用setFallback()注入回退语言; - FileLoaderFactory 通过
config->get('translation.path', BASE_PATH . '/storage/languages')读取语言文件根目录,并注入FileLoader。
配置临时语言环境
除了全局默认语言,你还可以在运行时为当前请求或协程生命周期临时切换语言。此时应通过依赖注入获得TranslatorInterface,然后调用setLocale():
<?php use Hyperf\Di\Annotation\Inject; use Hyperf\Contract\TranslatorInterface; class FooController { #[Inject] private TranslatorInterface $translator; public function index() { // 只在当前请求或协程生命周期有效 $this->translator->setLocale('zh_CN'); } }值得说明的是setLocale()的「临时」语义在源码中有明确的实现支撑:Translator的setLocale()并非直接修改对象属性,而是调用Context::set()将语言写入Hyperf 协程上下文(见 Translator.php 的getLocale/setLocale/getLocaleContextKey)。由于协程上下文按协程隔离,切换语言不会污染其它协程,也不会产生并发下「串语言」的问题——这正是 Hyperf 作为协程框架对国际化能力的原生适配。
翻译字符串
通过 TranslatorInterface 翻译
可直接通过注入Hyperf\Contract\TranslatorInterface并调用实例的trans方法实现对字符串的翻译:
<?php use Hyperf\Di\Annotation\Inject; use Hyperf\Contract\TranslatorInterface; class FooController { #[Inject] private TranslatorInterface $translator; public function index() { return $this->translator->trans('messages.welcome', [], 'zh_CN'); } }trans()方法签名支持三个参数:翻译键、占位符替换数组、以及可选的locale(不传则使用当前语言环境)。其底层实现会依次查找当前语言与回退语言(见下文「回退链机制」),最终把翻译行中的占位符替换为真实值后返回。
通过全局函数翻译
您也可以通过全局函数__()或trans()来对字符串进行翻译。函数的第一个参数使用键(指使用翻译字符串作为键的键)或者是文件. 键的形式。
echo __('messages.welcome'); echo trans('messages.welcome');这两个全局函数定义在 Functions.php 中,内部通过ApplicationContext::getContainer()->get(TranslatorInterface::class)获取翻译器实例后转发给trans()。由于Functions.php已通过 composer 的files字段自动加载(见 composer.json),你在项目的任意位置都可以直接调用这两个函数,无需手动引入。
翻译不存在的键
当某个键在当前语言与回退语言中都找不到时,Translator::get()会原样返回传入的键名(见 Translator.php 中return $line ?? $key;)。这意味着界面上的翻译键本身就是最好的错误提示——你一眼就能看出哪个键缺失或拼写错误,这在大型多语言项目中非常利于排查。
翻译字符串中定义占位符
您也可以在语言字符串中定义占位符,所有的占位符使用:作为前缀。例如,把用户名作为占位符:
<?php // storage/languages/en/messages.php return [ 'welcome' => 'Welcome :name', ];替换占位符使用函数的第二个参数:
echo __('messages.welcome', ['name' => 'Hyperf']); // 输出:Welcome Hyperf如果占位符全部是大写字母,或者是首字母大写,那么翻译过来的字符串也会是相应的大写形式:
'welcome' => 'Welcome, :NAME', // Welcome, HYPERF 'goodbye' => 'Goodbye, :Name', // Goodbye, Hyperf这一行为由Translator::makeReplacements()实现(见 Translator.php):对于替换数组中的每个键,它会一次性把:key、:KEY(全大写)与:Key(首字母大写)三种形态替换为对应的大小写变体。此外,sortReplacements()会先按键长度降序排序,确保较长的占位符(如:first_name)不会先被较短的键(如:first)错误替换——这是 Laravel 系翻译实现中常见的边界细节。
处理复数
不同语言的复数规则是不同的,在中文中可能不太关注这一点,但在翻译其它语言时我们需要处理复数形式的用词。我们可以使用「管道」字符|,用来区分字符串的单数和复数形式:
'apples' => 'There is one apple|There are many apples',也可以指定数字范围,创建更加复杂的复数规则:
'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many',使用「管道」字符定义好复数规则后,就可以使用全局函数trans_choice来获得给定「数量」的字符串文本。在下面的例子中,因为数量大于 1,所以就会返回翻译字符串的复数形式:
echo trans_choice('messages.apples', 10);当然除了全局函数trans_choice(),您也可以使用Hyperf\Contract\TranslatorInterface的transChoice方法:
$this->translator->transChoice('messages.apples', 10);复数选择器底层原理
复数规则的实现集中在 MessageSelector.php 的choose()方法中,处理流程分为两步:
内联条件匹配(
extract/extractFromString):逐段解析{0}...、[1,19]...、[20,*]...这类条件前缀。支持区间语法[from,to]、[from,*](大于等于 from)、[*,to](小于等于 to),以及{n}(等于 n)精确匹配,并支持小数数量。这些规则在 MessageSelectorTest.php 中有大量测试用例覆盖,例如'{0} first|[1,9] second'在数量为 0、1、10 时分别命中不同的分支。语言复数规则(
getPluralIndex):如果没有命中内联条件,则按目标语言选择复数索引。该方法内置了数十种语言的复数规则,例如:- 中文(
zh_CN)、日语(ja)、韩语(ko)等语言无复数区分,恒返回 0; - 英语(
en)等语言按「数量是否为 1」返回 0 或 1; - 俄语(
ru)、乌克兰语(uk)等语言存在三种复数形式(单数、少数、多数); - 阿拉伯语(
ar)则多达六种复数形式。
- 中文(
Translator::choice()在调用选择器之前还会把数量写入$replace['count'],因此你可以在复数文本中通过:count占位符输出实际数量(见 Translator.php)。同时它支持传入Countable对象或数组作为数量,内部会自动count()统计元素个数,例如trans_choice('messages.apples', $orderItems)。
回退链机制:默认语言与回退语言如何协作
前面提到配置中有locale与fallback_locale两个语言,二者在源码中的协作关系很清晰。Translator::get()会调用localeArray()构建「查找语言数组」:
protected function localeArray(?string $locale): array { return array_filter([$locale ?: $this->locale(), $this->fallback]); }即:先查目标语言(或当前语言),查不到再查回退语言,逐个尝试,直到找到翻译行为止。例如默认语言为zh_CN、回退语言为en时,trans('messages.welcome')会先在zh_CN中查找,若zh_CN/messages.php中没有welcome键,则自动到en/messages.php中查找。这套回退机制保证了多语言项目在个别语言翻译不完整时,用户依然能看到可用的文案,而不是空白或异常。对应的查找行为在 TranslatorTest.php 中均有测试覆盖(包括has()与hasForLocale()对回退开关的区分)。
小结
Hyperf 的hyperf/translation组件以「语言文件 + 语言环境 + 翻译函数」为核心,提供了从基础键值翻译、占位符大小写替换、复数规则选择到命名空间扩展的完整国际化能力。其关键设计包括:
- 独立组件:不绑定 Hyperf 框架,可复用于其它项目(composer.json);
- 语言文件驱动:
storage/languages/{locale}/{group}.php约定目录结构,支持 PHP 数组与 JSON 两种格式,支持命名空间与 vendor 覆盖(FileLoader.php); - 协程安全:临时语言环境写入协程上下文,互不干扰(Translator.php);
- 默认语言 + 回退语言双保险,键缺失时原样返回键名便于排查;
- 占位符与复数:
:name/:NAME/:Name自动大小写,|管道与{n}/[from,to]/[from,*]区间支持复杂复数规则(MessageSelector.php)。
对于构建面向多语言用户的 Hyperf 应用(如中英文官网、国际化 API 提示信息、多语言邮件模板),按照本文的目录约定组织语言文件、合理配置locale与fallback_locale,再配合__()/trans()/trans_choice()三个全局函数,即可快速落地一套稳定、易维护的多语言方案。
- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
相关推荐
Hyperf 国际化(多语言)组件完整实战指南:语言文件、占位符与复数规则
Hyperf 国际化(多语言)组件完整实战指南:语言文件、占位符与复数规则 导读 Hyperf 提供了开箱即用的国际化(i18n)支持,让您的应用可以轻松面向多
后端微服务Hyperf 国际化(translation)组件实战指南:多语言文件、占位符与复数规则全解析
Hyperf 国际化(translation)组件实战指南:多语言文件、占位符与复数规则全解析 Hyperf 框架对国际化(i18n)的支持非常友好,通过 hy
后端Web框架微服务RPC框架异步编程Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现
Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现 Hyperf 的翻译(Translation)组件为应用提供了一
后端微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考