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、填充表单_…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
EEG-fNIRS联合成像在跨频率耦合研究中的创新应用
摘要 神经影像技术对医学科学产生了深远的影响,推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下,基于神经血管耦合现象的多模态神经影像方法,通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里,本研…...
免费批量Markdown转Word工具
免费批量Markdown转Word工具 一款简单易用的批量Markdown文档转换工具,支持将多个Markdown文件一键转换为Word文档。完全免费,无需安装,解压即用! 官方网站 访问官方展示页面了解更多信息:http://mutou888.com/pro…...
