hyperf 十四 国际化
一 安装
composer require hyperf/translation:v2.2.33
二 配置
1、设置语言文件
文件结构:
/storage/languages/en/messages.php
/storage/languages/zh_CH/messages.php
// storage/languages/en/messages.php
return ['welcome' => 'Welcome to our application :test :test2','test' => '{2} TEST1|[3,10] TEST2|[20,*] TEST3',
];// storage/languages/zh_CH/messages.php
return ['welcome' => '欢迎-使用 :test :test2',
];
2、设置配置文件
创建文件 /config/autoload/translation.php。
#/config/autoload/translation.php
return [// 默认语言'locale' => 'zh_CN',// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本'fallback_locale' => 'en',// 语言文件存放的文件夹'path' => BASE_PATH . '/storage/languages',
];
三 使用
1、临时设置语言
// storage/languages/zh_CH/messages.php
return ['welcome' => '欢迎-使用',
];#/config/autoload/translation.php
return [// 默认语言'locale' => 'en',// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本'fallback_locale' => 'en',// 语言文件存放的文件夹'path' => BASE_PATH . '/storage/languages',
];//App\Controller\TestController
use Hyperf\Di\Annotation\Inject;
use Hyperf\Contract\TranslatorInterface;class TestController
{/*** @Inject* @var TranslatorInterface*/private $translator;public function index(){$value = $this->translator->trans('messages.welcome', [], 'zh_CN');var_dump($value);}
}# 输出
string(26) "欢迎-使用"
2、全局函数
// storage/languages/zh_CH/messages.php
return ['welcome' => '欢迎-使用','test1' => '测试',
];// config/autoload/translation.php
return [// 默认语言'locale' => 'zn_CH',// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本'fallback_locale' => 'en',// 语言文件存放的文件夹'path' => BASE_PATH . '/storage/languages',
];// App\Controller\TestController
class TestController
{/*** @Inject* @var TranslatorInterface*/private $translator;public function test2(){echo __('message.welcome') . "\n"; //欢迎-使用echo trans('message.welcome') . "\n";//欢迎-使用}
}
4、自定义占位符
// storage/languages/en/messages.php
return ['welcome' => 'Welcome to our application :test :test2',
];// config/autoload/translation.php
return [// 默认语言'locale' => 'en',// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本'fallback_locale' => 'en',// 语言文件存放的文件夹'path' => BASE_PATH . '/storage/languages',
];// App\Controller\TestController
class TestController
{/*** @Inject* @var TranslatorInterface*/private $translator;public function test2(){echo __('message.welcome',['test'=>'qqq','test2':'aaa']) . "\n"; //Welcome to our application qqq aaaecho trans('message.welcome',['test'=>'qqq','test2':'aaa']) . "\n";//Welcome to our application qqq aaa}
}
5、复数处理
// storage/languages/en/messages.php
return ['test' => '{2} TEST1|[3,10] TEST2|[20,*] TEST3',
];// config/autoload/translation.php
return [// 默认语言'locale' => 'en',// 回退语言,当默认语言的语言文本没有提供时,就会使用回退语言的对应语言文本'fallback_locale' => 'en',// 语言文件存放的文件夹'path' => BASE_PATH . '/storage/languages',
];// App\Controller\TestController
class TestController
{/*** @Inject* @var TranslatorInterface*/private $translator;public function test2(){echo $this->translator->transChoice('message.test',0)."\n";echo trans_choice('message.test',0) . "\n"; echo trans_choice('message.test',2) . "\n"; echo trans_choice('message.test',4) . "\n";echo trans_choice('message.test',22) . "\n"; }
}//输出TEST1TEST1
TEST1
TEST2
TEST3
四 详解
1、调用
多语言的调用从注入开始,即Hyperf\Translation\Translator::__construct(TranslatorLoaderInterface $loader, string $locale)方法。根据配置文件TranslatorLoaderInterface 对应Hyperf\Translation\FileLoaderFactory类。读取配置文件/storage/languages/translation.path,并返回Hyperf\Translation\FileLoader类。即注入到Translator的构造的TranslatorLoaderInterface 实体类是FileLoader。
若无/storage/languages/translation.path配置文件,可使用默认配置vendor\hyperf\translation\publish\translation.php生成。
php bin/hyperf.php vendor:publish hyperf/translation
使用的语言标志比如en、zn_ch,在上下文中读取和设置。
Translator内调用顺序:
Translator::trans()->Translator::get()->Translator::getLine()->Translator::load()->FileLoader::load()
根据FileLoader::load()调用FileLoader::loadJsonPaths(),可以将不同语言的不同文件统一放到json文件中,使用FileLoader::addJsonPath()设置对应文件。会便利对应文件加载内容,就是对应语言的全部内容。
根据FileLoader::load()调用FileLoader::loadPath(),加载对应文件。比如翻译a.b,a是对应语言的组名,b对应是键名,文件是/storage/languages/对应语言/a.php。
根据FileLoader::load()调用FileLoader::loadNamespaced(),用命名空间加载。这里所谓命名空间就是,对比默认路径,设置一个键名对应非默认路径。也是调用loadPath()实现,不过传入非默认路径,用命名空间获取路径值,用FileLoader::addNamespace()设置命名空间和路径值。
Translator::trans()->Translator::get()->Translator::getLine()->Translator::makeReplacements()->sTranslator::ortReplacements()
根据Translator::ortReplacements(),查询字符串中":占位符"或":占位符全大写"或":占位符首字母大写"。
Translator::transChoice()->Translator::choice()->Translator::makeReplacements()->Translator::getSelector()->MessageSelector::choose()
Translator::choice()也调用Translator::get()但是中心加载了本地语言的标识。Translator::getSelector()将替换值作为数字,MessageSelector::choose()解析字符串、替换字符换中对应数字条件字符,并根据不同语言处理数字,返回最终结果。
全局函数在vendor\hyperf\translation\src\Function.php中,在其composer.json中自动加载。
2、源码
#Hyperf\Translation\Translator
class Translator implements TranslatorInterface
{public function __construct(TranslatorLoaderInterface $loader, string $locale){$this->loader = $loader;$this->locale = $locale;}public function trans(string $key, array $replace = [], ?string $locale = null){return $this->get($key, $replace, $locale);}public function transChoice(string $key, $number, array $replace = [], ?string $locale = null): string{return $this->choice($key, $number, $replace, $locale);}public function get(string $key, array $replace = [], ?string $locale = null, bool $fallback = true){[$namespace, $group, $item] = $this->parseKey($key);// Here we will get the locale that should be used for the language line. If one// was not passed, we will use the default locales which was given to us when// the translator was instantiated. Then, we can load the lines and return.$locales = $fallback ? $this->localeArray($locale): [$locale ?: $this->locale()];foreach ($locales as $locale) {if (!is_null($line = $this->getLine($namespace,$group,$locale,$item,$replace))) {break;}}// If the line doesn't exist, we will return back the key which was requested as// that will be quick to spot in the UI if language keys are wrong or missing// from the application's language files. Otherwise we can return the line.return $line ?? $key;}public function choice(string $key, $number, array $replace = [], ?string $locale = null): string{$line = $this->get($key,$replace,$locale = $this->localeForChoice($locale));// If the given "number" is actually an array or countable we will simply count the// number of elements in an instance. This allows developers to pass an array of// items without having to count it on their end first which gives bad syntax.if (is_array($number) || $number instanceof Countable) {$number = count($number);}$replace['count'] = $number;return $this->makeReplacements($this->getSelector()->choose($line, $number, $locale),$replace);}protected function localeForChoice(?string $locale): string{return $locale ?: $this->locale() ?: $this->fallback;}protected function getLine(string $namespace, string $group, string $locale, $item, array $replace){$this->load($namespace, $group, $locale);if (!is_null($item)) {$line = Arr::get($this->loaded[$namespace][$group][$locale], $item);} else {// do for hyperf Arr::get$line = $this->loaded[$namespace][$group][$locale];}if (is_string($line)) {return $this->makeReplacements($line, $replace);}if (is_array($line) && count($line) > 0) {foreach ($line as $key => $value) {$line[$key] = $this->makeReplacements($value, $replace);}return $line;}return null;}}#Hyperf\Translation\FileLoaderFactory
class FileLoaderFactory
{public function __invoke(ContainerInterface $container){$config = $container->get(ConfigInterface::class);$files = $container->get(Filesystem::class);$path = $config->get('translation.path', BASE_PATH . '/storage/languages');return make(FileLoader::class, compact('files', 'path'));}
}#Hyperf\Translation\FileLoader
class FileLoader implements TranslatorLoaderInterface
{public function __construct(Filesystem $files, string $path){$this->path = $path;$this->files = $files;}public function load(string $locale, string $group, ?string $namespace = null): array{if ($group === '*' && $namespace === '*') {return $this->loadJsonPaths($locale);}if (is_null($namespace) || $namespace === '*') {return $this->loadPath($this->path, $locale, $group);}return $this->loadNamespaced($locale, $group, $namespace);}public function addNamespace(string $namespace, string $hint){$this->hints[$namespace] = $hint;}public function addJsonPath(string $path){$this->jsonPaths[] = $path;}protected function loadNamespaced(string $locale, string $group, string $namespace): array{if (isset($this->hints[$namespace])) {$lines = $this->loadPath($this->hints[$namespace], $locale, $group);return $this->loadNamespaceOverrides($lines, $locale, $group, $namespace);}return [];}protected function loadPath(string $path, string $locale, string $group): array{if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) {return $this->files->getRequire($full);}return [];}protected function loadJsonPaths(string $locale): iterable{return collect(array_merge($this->jsonPaths, [$this->path]))->reduce(function ($output, $path) use ($locale) {if ($this->files->exists($full = "{$path}/{$locale}.json")) {$decoded = json_decode($this->files->get($full), true);if (is_null($decoded) || json_last_error() !== JSON_ERROR_NONE) {throw new RuntimeException("Translation file [{$full}] contains an invalid JSON structure.");}$output = array_merge($output, $decoded);}return $output;}, []);}
}#Hyperf\Translation\MessageSelector
class MessageSelector
{
public function choose(string $line, $number, string $locale){$segments = explode('|', $line);if (($value = $this->extract($segments, $number)) !== null) {return trim($value);}$segments = $this->stripConditions($segments);$pluralIndex = $this->getPluralIndex($locale, $number);if (count($segments) === 1 || ! isset($segments[$pluralIndex])) {return $segments[0];}return $segments[$pluralIndex];}private function extract(array $segments, $number){foreach ($segments as $part) {if (! is_null($line = $this->extractFromString($part, $number))) {return $line;}}}
private function stripConditions(array $segments): array{return collect($segments)->map(function ($part) {return preg_replace('/^[\{\[]([^\[\]\{\}]*)[\}\]]/', '', $part);})->all();}
private function stripConditions(array $segments): array{return collect($segments)->map(function ($part) {return preg_replace('/^[\{\[]([^\[\]\{\}]*)[\}\]]/', '', $part);})->all();}
public function getPluralIndex(string $locale, int $number): int{switch ($locale) {……case 'en':……return ($number == 1) ? 0 : 1;}……}
}
#vendor\hyperf\translation\src\Function.php
if (! function_exists('__')) {function __(string $key, array $replace = [], ?string $locale = null){$translator = ApplicationContext::getContainer()->get(TranslatorInterface::class);return $translator->trans($key, $replace, $locale);}
}if (! function_exists('trans')) {function trans(string $key, array $replace = [], ?string $locale = null){return __($key, $replace, $locale);}
}if (! function_exists('trans_choice')) {function trans_choice(string $key, $number, array $replace = [], ?string $locale = null): string{$translator = ApplicationContext::getContainer()->get(TranslatorInterface::class);return $translator->transChoice($key, $number, $replace, $locale);}
}
相关文章:
hyperf 十四 国际化
一 安装 composer require hyperf/translation:v2.2.33 二 配置 1、设置语言文件 文件结构: /storage/languages/en/messages.php /storage/languages/zh_CH/messages.php // storage/languages/en/messages.php return [welcome > Welcome to our applicat…...

C语言_初识C语言指针
文章目录 前言一、指针 ... 一个内存单元多大比较合适?二、地址或者编号如何产生?三、指针变量的大小 前言 内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。 所以为了有效的使用内存,就把内存划分成一个个…...

EMQX启用双向SSL/TLS安全连接以及java连接
作为基于现代密码学公钥算法的安全协议,TLS/SSL 能在计算机通讯网络上保证传输安全,EMQX 内置对 TLS/SSL 的支持,包括支持单/双向认证、X.509 证书、负载均衡 SSL 等多种安全认证。你可以为 EMQX 支持的所有协议启用 SSL/TLS,也可…...
4399面试总结C/C++游戏开发
主要流程 首先询问了C/C知识点 然后询问操作系统,计算机组成,数据结构,计算机网络哪两门熟悉 涉及的相关问题 多态的概念 tcp,udp? tcp,udp区别 tcp可靠,udp不可靠 tcp这个链接的过程? 一个TCP连接必须要经过三次“…...
hashlib 模块学习
hashlib 是 Python 标准库中用于散列和摘要算法的模块。散列算法将输入数据转换为固定长度的散列值(也称为摘要),并且对于相同的输入始终生成相同的散列值。这对于存储密码、数字签名、数据完整性验证等领域非常有用。以下是对 hashlib 模块的…...

大模型开发05:PDF 翻译工具开发实战
大模型开发实战05:PDF 翻译工具开发实战 PDF-Translator 机器翻译是最广泛和基础的 NLP 任务 PDF-Translator PDF 翻译器是一个使用 AI 大模型技术将英文 PDF 书籍翻译成中文的工具。这个工具使用了大型语言模型 (LLMs),如 ChatGLM 和 OpenAI 的 GPT-3 以及 GPT-3.5 Turbo 来…...

LeetCode 43题:字符串相乘
题目 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2", num2 "3&…...

基于java Swing 和 mysql实现的飞机订票系统(源码+数据库+ppt+ER图+流程图+架构说明+论文+运行视频指导)
一、项目简介 本项目是一套基于java Swing 和 mysql实现的飞机订票系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含:项目源码、项目文档、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过…...

Jmeter性能综合实战 —— 签到及批量签到
提取性能测试的三个方面:核心、高频、基础功能 签 到 请 求 步 骤 1、准备工作: 签到线程组n HTTP请求默认值n HTTP cookie 管理器n 首页访问请求n 登录请求n 查看结果树n 调试取样器l HTTP代理服务器 (1)创建线程组 …...

燃气管网监测系统,提升城市燃气安全防控能力
燃气是我们日常生活中不可或缺的能源,但其具有易燃易爆特性,燃气安全使用、泄漏监测尤为重要。当前全国燃气安全事故仍呈现多发频发态势,从公共安全的视角来看,燃气已成为城市安全的重大隐忧!因此,建立一个…...

【SQL】1731. 每位经理的下属员工数量 ( 新思想:确定左表,依次添加后续字段)
leetcode题目链接 注意点 确定左表(即,确定result表中的主键),依次添加后续字段。注意:主键可能是一个字段,也可能是多个字段COUNT(DISTINCT()),一般为了防止重复,使用COUNT计数时,…...
AMD Radeon RX 7000/6000系列显卡安装ROCm 调用CUDA
A卡用户画图炼丹、跑大模型甚至是跑机器学习、深度学习的有福了~ 注意:ROCm目前仅限在Linux系统下可用,Windows暂不支持 1. 安装ROCm RX6000系列及以下显卡使用ROCm 5.4.2稳定版本命令 【支持包括桌面级AMD Radeon RX6950XT、RX6900XT、RX6800XT、RX6…...

钉钉小程序引用阿里巴巴图标
2.打开的界面如图,先建一个iconfont.acss文件,全选浏览器打开的样式代码,复制粘贴进新建的iconfont.acss文件中 3.使用...

深入了解Nginx:高性能的开源Web服务器与反向代理
一、Nginx是什么 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,也可以作为负载均衡器和HTTP缓存服务器使用。它采用事件驱动、异步非阻塞的处理方式,能够处理大量并发连接和高流量负载ÿ…...
vue3 自定义显示内容
vue3 自定义显示内容 vue3 自定义显示内容示例uni-app封装自定义内容组件 vue3 自定义显示内容 在 Vue 3 中,你可以通过插槽(Slot)来自定义组件的显示内容。 插槽允许你将额外的内容插入到组件的特定位置,从而实现更灵活的组件…...
视频行为分析——视频图像转换与ffmpeg相关操作
工具类说明 1. 图像视频转换 1.1 视频输出gif from moviepy.editor import VideoFileClip # 设置输入视频文件路径和输出GIF文件路径 input_video video.avi output_gif output.gif # 读取视频文件 video VideoFileClip(input_video) # 将视频保存为GIF文件 video.write_…...
Bean 生命周期
Bean 生命周期 一、Bean 实例化的基本流程 Spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefifinition对象,所有的BeanDefifinition存储到一个名为beanDefifinitionMap的Map集合中去,Spring框架在对该Map进行遍历࿰…...

JavaScript原型链污染
前言 在浏览某个论坛的时候,第一次看到了JavaScript原型链污染漏洞。当时非常的好奇,当时我一直以为js作为一种前端语言,就算存在漏洞也是针对前端,不会危害到后端,因此我以为这种漏洞危害应该不大。可当我看到他的漏…...
【Java】设计模式之单例模式与工厂模式
1、设计模式概念及分类 简单来说设计模式是被广大程序员们总结并认可的编码套路,其中最常用的莫过于单例模式与工厂模式,而单例模式也有更加细的分类,一起来学习一下这些模式的用法和特点吧。 2、单例模式 一个类只能被实例化出来一个对象…...

web自动化框架:selenium学习使用操作大全(Python版)
目录 一、浏览器驱动下载二、selenium-python安装(打开网站、操作元素)三、网页解析(HTML、xpath)四、selenium基本操作1、元素定位八种方法2、元素动态定位3、iframe切换4、填充表单_填充文本框5、填充表单_单选按钮6、填充表单_…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...