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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/59083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用awvs进行web安全扫描

1、安装 docker pull secfa/docker-awvs docker run -it -d -name awvs -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs2、账号密码 # https://ip:13443/ # 用户名:adminadmin.com # 密码:Admin1233、使用 ps:需要征得甲方的同意

2023年Java核心技术第十篇(篇篇万字精讲)

目录 十九 . 一个线程两次调用start()方法会出现什么情况?线程的生命周期和状态转移。 19.1 典型回答 19.1.1 线程生命周期: 19.1.2 计时等待详细解释: 19.2 深入扩展考察 19.2.1 线程是什么? 19.2.2 Green…

【juc】读写锁ReentrantReadWriteLock

目录 一、说明二、读读不互斥2.1 代码示例2.2 截图示例 三、读写互斥3.1 代码示例3.2 截图示例 四、写写互斥4.1 代码示例4.2 截图示例 五、注意事项5.2.1 代码示例5.2.2 截图示例 一、说明 1.当读操作远远高于写操作时,使用读写锁让读读可以并发,来提高…

网络编程day1——进程间通信-socket套接字

基本特征:socket是一种接口技术,被抽象了一种文件操作,可以让同一计算机中的不同进程之间通信,也可以让不同计算机中的进程之间通信(网络通信) 本地进程间通信编程模型: 进程A …

IDEA遇到 git pull 冲突的几种解决方法

1 忽略本地修改,强制拉取远程到本地 主要是项目中的文档目录,看的时候可能多了些标注,现在远程文档更新,本地的版本已无用,可以强拉 git fetch --all git reset --hard origin/dev git pull关于commit和pull的先后顺…

亚马逊鲲鹏系统是怎么操作测评的

亚马逊鲲鹏系统可以注册亚马逊买家号、养号、下单留评等,是一款功能比较齐全的测评软件,具体操作如下: 首先我们需要先准备好买家账号,账号可以直接去购买已经注册好了的账号,也可以准备好账号所需要的一些邮箱、ip、…

无门槛访问ChatGPT升级版-数据指北AI

大家好,我是脚丫先生 (o^^o) 给小伙伴们介绍ChatGPT升级版不需要任何门槛,不需要单独搞账号,只要邮箱登录的方式,即可访问平台,以用户体验为首要,让所有人都能无门槛的使用目前市面上最强大的AI智能聊天&a…

Rn实现省市区三级联动

省市区三级联动选择是个很频繁的需求,但是查看了市面上很多插件不是太老不维护就是不满足需求,就试着实现一个 这个功能无任何依赖插件 功能略简单,但能实现需求 核心代码也尽力控制在了60行左右 pca-code.json树型数据来源 Administrative-d…

液体神经网络LLN:通过动态信息流彻底改变人工智能

巴乌米克泰吉 一、说明 在在人工智能领域,神经网络已被证明是解决复杂问题的非常强大的工具。多年来,研究人员不断寻求创新方法来提高其性能并扩展其能力。其中一种方法是液体神经网络(LNN)的概念,这是一个利用动态计算…

07:STM32----ADC模数转化器

目录 1:简历 2:逐次逼近型ADC 3:ADC基本结构 4:输入通道 5:规则组的4种转换模式 1:单次转化,非扫描模式 2:连续转化,非扫描模式 3:单次转化,扫描模式 4:单次转化,扫描模式 6:触发控制 7:数据对齐 8:转化时间 9:校准 10:ADC的硬件电路 A: AD单通道 1:连接图 2:函…

Git小白入门——了解分布式版本管理和安装

Git是什么? Git是目前世界上最先进的分布式版本控制系统(没有之一) 什么是版本控制系统? 程序员开发过程中,对于每次开发对各种文件的修改、增加、删除,达到预期阶段的一个快照就叫做一个版本。 如果有一…

【OpenCV入门】第一部分——图像处理基础

本文结构 图像处理的基本操作读取图像imread() 显示图像imshow()waitKey()destroyAllWindows() 保存图像imwrite() 复制图像copy() 获取图像属性 像素确定像素的位置获取像素的BGR值修改像素的BGR值 色彩空间GRAY色彩空间cvtColor()——从BGR色彩空间转换到GRAY色彩空间 HSV色彩…

Lua学习(一)

lua基础学习 LUA 语言1. 什么是lua?1.1 准备工作 2. 基本语法2.1 注释2.2 标识符2.3 关键字2.4 全局变量 3. 数据类型4. 变量4.1 赋值语句 5. 循环5.1 while循环5.2 for循环5.3泛型for循环5.4 repeat until 循环5.5 break 语句 6. 流程控制6.1 if语句6.2 if else 语…

GNU make系列之介绍Makefile(0)

一.欢迎来到我的酒馆 在本章节介绍Makefile。 目录 一.欢迎来到我的酒馆二.GNU make 预览三.一个简单的Makefile四.make程序如何处理Makefile文件五.在Makefile中使用变量 二.GNU make 预览 2.1 GNU make工具会自动决定哪些程序需要被重新编译,并且执行相应的命令来…

leetcode做题笔记96. 不同的二叉搜索树

给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 思路一&#xff1a;动态规划 int numTrees(int n){int f[n1];memset(f,0,sizeof(f));f[0] f[1] 1;for(int i 2;i<n;i)…

reactantd(12)动态表单的默认值问题

最近遇到一个需求是有一个表单可以输入各种信息&#xff0c;然后还需要有一个编辑功能&#xff0c;点击编辑的时候需要把当前数据填入到表单里面。在网上查了很多种方法&#xff0c;然后我的思路是使用initialValues搭配setState()使用。默认值都为空&#xff0c;然后点击单条数…

数学建模:ARMA时间序列预测

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 ARMA预测 时间序列是按时间顺序的一组数字序列 时间序列的特点&#xff1a; 现实的、真实的一组数据&#xff0c;时间序列背后是某一现象的变化规律&#xff0c;时间序列预测就是学习之前的规律来预测后面…

从零开始学习 Java:简单易懂的入门指南之查找算法及排序算法(二十)

查找算法及排序算法 常见的七种查找算法&#xff1a;1. 基本查找2. 二分查找3. 插值查找4. 斐波那契查找5. 分块查找6. 哈希查找7. 树表查找 四种排序算法&#xff1a;1. 冒泡排序1.1 算法步骤1.2 动图演示1.3 代码示例 2. 选择排序2.1 算法步骤2.2 动图演示 3. 插入排序3.1 算…

knife4j 整合 springboot

官方文档&#xff1a;https://doc.xiaominfo.com/knife4j 版本兼容说明&#xff1a;https://doc.xiaominfo.com/docs/quick-start/start-knife4j-version 升级说明&#xff1a;https://doc.xiaominfo.com/docs/upgrading/upgrading-to-v4版本兼容惯关系&#xff1a; springboot…

MySQL一行记录是如何存储的?

目录 MySQL的数据存放在哪个文件&#xff1f; 表空间文件的结构是怎么样的&#xff1f; 1、行&#xff08;row&#xff09; 2、页&#xff08;page&#xff09; 3、区&#xff08;extent&#xff09; 4、段&#xff08;segment&#xff09; InnoDB 行格式有哪些&#xf…