ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现

2026/10/8 14:24:18 拓冰建站 浏览量
Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现 后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载Hyperf 的翻译Translation组件为应用提供了一套友好且完备的国际化I18n能力让你可以轻松地让项目支持多种语言。本文以官方文档 docs/en/translation.md 为骨架结合仓库中hyperf/translation组件的真实源码完整讲解组件的安装、语言文件组织、三种 locale 配置方式、字符串翻译、占位符替换与复数处理并深入剖析Translator、MessageSelector、FileLoader等核心类的底层实现让你既能直接上手使用也能理解其内部原理。组件概览一个可独立复用的翻译库Hyperf 的国际化组件hyperf/translation是从illuminate/translation演化而来的独立翻译组件见 src/translation/composer.json 中的描述。它是一个独立组件不依赖 Hyperf 框架主体可以单独引入到其他项目或框架中使用其依赖仅限于hyperf/collection、hyperf/context、hyperf/contract、hyperf/macroable、hyperf/stringable、hyperf/support等轻量级工具包与psr/container并不需要整个框架运行时。在 Hyperf 应用中该组件通过 ConfigProvider 完成自动装配TranslatorLoaderInterface绑定到FileLoaderFactory负责从语言文件加载翻译内容TranslatorInterface绑定到TranslatorFactory负责创建翻译器实例并注入默认 locale 与 fallback locale同时把默认配置文件发布到config/autoload/translation.php。安装使用 Composer 安装即可composer require hyperf/translation安装完成后组件会通过ConfigProvider自动注册依赖与配置发布项。如需生成配置文件执行以下命令Hyperf 框架标准发布方式php bin/hyperf.php vendor:publish hyperf/translation语言文件的组织方式Hyperf 的语言文件默认存放在storage/languages目录下你也可以在配置中修改该目录。每种语言对应一个子目录目录名即语言标识例如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()方法它会拼接{path}/{locale}/{group}.php路径若文件存在则通过getRequire()返回数组内容语言文件名如messages即翻译键中的组group。组件内部如何定位语言目录FileLoaderFactory.php 从配置中心读取translation.path默认值为BASE_PATH . /storage/languages$path $config-get(translation.path, BASE_PATH . /storage/languages); return make(FileLoader::class, compact(files, path));配置 locale国际化的相关配置集中在config/autoload/translation.php文件中该文件由组件发布原始模板见 publish/translation.php你可以按需修改?php // config/autoload/translation.php return [ // 默认语言 locale zh_CN, // 回退语言当默认语言中缺少对应翻译文本时会使用回退语言的对应文本 fallback_locale en, // 语言文件存放目录 path BASE_PATH . /storage/languages, ];配置项说明配置项默认值说明localezh_CN默认语言应用启动后翻译器使用的初始语言fallback_localeen回退语言当指定语言找不到翻译键时按此语言兜底pathBASE_PATH . /storage/languages语言文件所在目录的绝对路径工厂如何消费这些配置TranslatorFactory.php 的创建逻辑印证了上述配置的作用$locale $config-get(translation.locale, zh_CN); $fallbackLocale $config-get(translation.fallback_locale, en); $loader $container-get(TranslatorLoaderInterface::class); $translator make(Translator::class, compact(loader, locale)); $translator-setFallback((string) $fallbackLocale);配置临时 locale按请求 / 协程生效除了全局默认 locale你还可以在运行期为当前请求动态设置临时 locale。由于 Hyperf 运行在 Swoole 协程环境中Translator::setLocale()会把语言存入Context协程上下文因此临时 locale 只在当前请求或当前协程的生命周期内有效不会污染其他协程?php use Hyperf\Di\Annotation\Inject; use Hyperf\Contract\TranslatorInterface; class FooController { #[Inject] private TranslatorInterface $translator; public function index() { // 仅对当前请求或协程生命周期内有效 $this-translator-setLocale(zh_CN); } }其底层实现在 Translator.php 中public function getLocaleContextKey(): string { return sprintf(%s::%s, TranslatorInterface::class, locale); } public function getLocale(): string { $locale Context::get($this-getLocaleContextKey()); return (string) ($locale ?? $this-locale); } public function setLocale(string $locale) { Context::set($this-getLocaleContextKey(), $locale); }注意getLocale()优先返回协程上下文中的 locale只有未设置时才回落到构造时传入的默认 locale。对应的契约定义见 TranslatorInterface.php。翻译字符串方式一注入 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方法的完整签名见 TranslatorInterface.phppublic function trans(string $key, array $replace [], ?string $locale null): array|string;三个参数分别表示翻译键、占位符替换数组可选、临时指定语言可选。方式二使用全局函数__()或trans()组件通过 Functions.php 注册了三个全局函数。函数第一个参数采用key直接用翻译文本作为键或file.key文件加键的形式echo __(messages.welcome); echo trans(messages.welcome);其中__()与trans()完全等价都从容器中取出TranslatorInterface并调用其trans方法function __(string $key, array $replace [], ?string $locale null) { $translator ApplicationContext::getContainer()-get(TranslatorInterface::class); return $translator-trans($key, $replace, $locale); } function trans(string $key, array $replace [], ?string $locale null) { return __($key, $replace, $locale); }键的解析规则Translator::get()首先调用parseKey()把翻译键拆分为[namespace, group, item]三元组不含::的普通键如messages.welcome按.分割第一段是组名group其余段拼成条目名item含::的键视为命名空间键namespace key::前的部分是命名空间解析结果会被缓存到parsed数组避免同一键的重复解析开销。若某个 locale 下找不到翻译get()会按localeArray()返回的语言序列当前语言 fallback 语言依次查找最终仍找不到则直接返回原键名方便在 UI 中快速定位缺失的语言键——这是宁可返回 key 也不抛异常的设计具体逻辑在 Translator.php 的get()方法中。在翻译字符串中定义占位符你可以在语言字符串中定义占位符所有占位符都以:作为前缀。例如以用户名作为占位符?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.php 的makeReplacements()实现它对同一个键同时替换三种形态$line str_replace( [: . $key, : . Str::upper($key), : . Str::ucfirst($key)], [$value, Str::upper($value), Str::ucfirst($value)], $line );替换数组的排序细节makeReplacements()会先调用sortReplacements()对替换数组按键名长度降序排序再执行替换protected function sortReplacements(array $replace): array { return (new Collection($replace))-sortBy(fn ($value, $key) mb_strlen((string) $key) * -1)-all(); }这样可以避免短键名先被替换而截胡长键名的问题例如同时存在:name与:name_en时name_en会优先被替换保证结果的正确性。处理复数Pluralization不同语言的复数规则各不相同。中文通常不需要关注单复数但在翻译其他语言如英语、俄语、阿拉伯语时必须处理名词的复数形式。组件使用竖线字符|来区分字符串的单复数形式apples There is one apple|There are many apples,你也可以指定数字区间来构造更复杂的复数规则apples {0} There are none|[1,19] There are some|[20,*] There are many,区间语法说明语法含义{0}精确匹配数字 0[1,19]闭区间匹配 1 到 19含两端[20,*]从 20 到无穷大[*,5]从负无穷到 5组件同样支持*作区间下界使用 trans_choice 取复数文本定义好复数规则后可通过全局函数trans_choice根据给定的数量获取对应字符串。在下面的示例中由于数字大于1将返回翻译字符串的复数形式echo trans_choice(messages.apples, 10); // 输出There are many apples除了全局函数trans_choice()也可以使用Hyperf\Contract\TranslatorInterface的transChoice方法$this-translator-transChoice(messages.apples, 10);底层实现MessageSelector复数选择的核心逻辑位于 MessageSelector.php 的choose()方法处理流程分为两步区间条件优先匹配先调用extract()/extractFromString()用正则preg_match(/^[\{\[](https://link.gitcode.com/i/a2eb137607123d8aa67a39b8c892f326)[\}\]](.*)/s, ...)解析{0}、[1,19]这类带条件的片段若命中区间则直接返回对应文本语言复数索引兜底若无区间条件则剥掉条件前缀后调用getPluralIndex($locale, $number)根据语言获取复数索引。getPluralIndex()内置了覆盖数十种语言含zh_CN、en、fr、ru、ar等及各自地区变体的复数规则表例如中文恒返回索引0英语按number 1返回0否则1俄语则按%10与%100的组合规则返回三态索引。Translator::choice()在调用选择器之前还会注入一个特殊占位符$replace[count] $number;这意味着你可以在复数文本中使用:count占位符输出实际数量例如apples {0} There are none|[1,19] There are :count apples|[20,*] There are many。此外choice()的$number参数也支持传入数组或可计数的对象组件会自动取其元素数量作为判断依据。进阶能力JSON 翻译与命名空间覆盖除 PHP 语言文件外从 FileLoader.php 的源码可以看出组件还支持两类进阶用法文档中虽未展开但已内置于实现中JSON 翻译文件FileLoader::loadJsonPaths()会尝试加载{path}/{locale}.json文件例如storage/languages/zh_CN.json解析失败时会抛出RuntimeException。配合Translator::getFromJson()方法键可直接使用翻译文本本身例如__(Welcome to our application)会先在 JSON 文件中查找找不到再回落到普通语言文件最终返回makeReplacements()处理后的文本。FileLoader还支持通过addJsonPath()注册额外的 JSON 翻译目录。命名空间与 vendor 覆盖当翻译键包含::如package::messages.welcome时FileLoader::loadNamespaced()会先从命名空间提示路径addNamespace()注册的 hint加载翻译再尝试从{path}/vendor/{namespace}/{locale}/{group}.php读取覆盖文件并用array_replace_recursive()递归合并实现组件自带翻译 应用本地覆盖的分层机制。备选加载器ArrayLoader除了从文件加载组件还提供了内存加载器 ArrayLoader.php通过addMessages(string $locale, string $group, array $messages, ?string $namespace null)把翻译直接注入内存适合单元测试或动态注册翻译的场景。它与FileLoader共同实现TranslatorLoaderInterface可以在容器中替换绑定。组件装配与依赖注入关系一览综合 ConfigProvider.php 的注册内容整个组件的依赖关系如下接口 / 配置实现 / 来源说明Hyperf\Contract\TranslatorInterfaceTranslatorFactory创建的Translator翻译器主入口读取translation.locale与translation.fallback_localeHyperf\Contract\TranslatorLoaderInterfaceFileLoaderFactory创建的FileLoader语言加载器读取translation.pathconfig/autoload/translation.phppublish/translation.php发布组件配置文件全局函数__()/trans()/trans_choice()Functions.php通过ApplicationContext::getContainer()获取翻译器Translator还使用了Macroabletrait见 Translator.php 第 26 行允许你在运行时为其扩展自定义方法。测试验证组件在 tests 目录下提供了三组测试可用于验证本文描述的行为TranslatorTest.php验证工厂默认 localezh_CN、has()方法、占位符替换、复数选择与并发场景下的 locale 隔离等MessageSelectorTest.php验证区间条件、复数索引及各类语言规则FileLoaderTest.php验证文件加载、命名空间与 JSON 路径处理。你可以在仓库根目录运行对应测试来印证实现行为composer test src/translation 2/dev/null || vendor/bin/phpunit --configuration phpunit.xml.dist src/translation/tests总结Hyperf 的翻译组件以语言文件 翻译器 选择器 加载器四层结构提供了一套从基础翻译到复杂复数的完整国际化方案。日常使用中只需记住四件事语言文件放storage/languages并按locale/组.php组织配置文件设好locale、fallback_locale与path翻译时用__()、trans()或注入的TranslatorInterface复数场景用trans_choice()配合|与区间语法。需要更精细控制时还可借助协程上下文实现按请求切换语言、使用 JSON 翻译文件或通过命名空间机制实现组件翻译的本地覆盖。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Hyperf 国际化多语言组件完整实战指南语言文件、占位符与复数规则Hyperf 国际化多语言组件完整实战指南语言文件、占位符与复数规则 导读 Hyperf 提供了开箱即用的国际化i18n支持让您的应用可以轻松面向多后端微服务Hyperf I18n 多语言国际化translation 组件完整实战指南Hyperf I18n 多语言国际化translation 组件完整实战指南 Hyperf 的国际化I18n能力由独立的 hyperf/translati后端Web框架微服务RPC框架异步编程Docker快速部署Wan2.1-Fun-1.3B-InP从镜像拉取到视频输出全程实录Docker快速部署Wan2.1 Fun 1.3B InP从镜像拉取到视频输出全程实录 想要快速体验最新的AI视频生成技术吗 今天我将为大家详细介绍如何上一篇5分钟给Windows 11 24H2 LTSC装回Microsoft StoreLTSC-Add-MicrosoftStore快速上手教程下一篇工具调用、多轮强化学习与Deep ResearchHands-On Modern RL Agentic RL完整实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考