当前位置: 首页 > news >正文

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)创建线程组 &#xf…...

燃气管网监测系统,提升城市燃气安全防控能力

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

【SQL】1731. 每位经理的下属员工数量 ( 新思想:确定左表,依次添加后续字段)

leetcode题目链接 注意点 确定左表(即,确定result表中的主键),依次添加后续字段。注意:主键可能是一个字段,也可能是多个字段COUNT(DISTINCT()),一般为了防止重复,使用COUNT计数时&#xff0c…...

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缓存服务器使用。它采用事件驱动、异步非阻塞的处理方式,能够处理大量并发连接和高流量负载&#xff…...

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进行遍历&#xff0…...

JavaScript原型链污染

前言 在浏览某个论坛的时候,第一次看到了JavaScript原型链污染漏洞。当时非常的好奇,当时我一直以为js作为一种前端语言,就算存在漏洞也是针对前端,不会危害到后端,因此我以为这种漏洞危害应该不大。可当我看到他的漏…...

【Java】设计模式之单例模式与工厂模式

1、设计模式概念及分类 简单来说设计模式是被广大程序员们总结并认可的编码套路,其中最常用的莫过于单例模式与工厂模式,而单例模式也有更加细的分类,一起来学习一下这些模式的用法和特点吧。 2、单例模式 一个类只能被实例化出来一个对象…...

web自动化框架:selenium学习使用操作大全(Python版)

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

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...

门静脉高压——表现

一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...