commit
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
github: [overtrue]
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: composer
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "21:00"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: phpunit/phpunit
|
||||
versions:
|
||||
- ">= 8.a, < 9"
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Test
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
phpunit:
|
||||
name: PHP-${{ matrix.php_version }}-${{ matrix.perfer }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php_version:
|
||||
- 8.0
|
||||
- 8.1
|
||||
perfer:
|
||||
- stable
|
||||
- lowest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Dependencies
|
||||
run: composer update --prefer-dist --no-interaction --no-suggest --prefer-${{ matrix.perfer }}
|
||||
- name: Run PHPUnit
|
||||
run: ./vendor/bin/phpunit
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 安正超
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+279
@@ -0,0 +1,279 @@
|
||||
# Pinyin
|
||||
|
||||
[](https://github.com/overtrue/pinyin/actions/workflows/test.yml)
|
||||
[](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin)
|
||||
|
||||
:cn: 基于 [mozillazg/pinyin-data](https://github.com/mozillazg/pinyin-data) 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案。
|
||||
|
||||
[喜欢我的项目?点击这里支持我](https://github.com/sponsors/overtrue)
|
||||
|
||||
## 安装
|
||||
|
||||
使用 Composer 安装:
|
||||
|
||||
```bash
|
||||
composer require overtrue/pinyin:^5.0
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
### 拼音风格
|
||||
|
||||
除了获取首字母的方法外,所有方法都支持第二个参数,用于指定拼音的格式,可选值为:
|
||||
|
||||
- `symbol` (默认)声调符号,例如 `pīn yīn`
|
||||
- `none` 不输出拼音,例如 `pin yin`
|
||||
- `number` 末尾数字模式的拼音,例如 `pin1 yin1`
|
||||
|
||||
### 返回值
|
||||
|
||||
除了 `permalink` 返回字符串外,其它方法都返回集合类型 [`Overtrue\Pinyin\Collection`](https://github.com/overtrue/pinyin/blob/master/src/Collection.php):
|
||||
|
||||
```php
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
|
||||
$pinyin = Pinyin::sentence('你好,世界');
|
||||
```
|
||||
|
||||
你可以通过以下方式访问集合内容:
|
||||
|
||||
```php
|
||||
echo $pinyin; // nǐ hǎo shì jiè
|
||||
|
||||
// 直接将对象转成字符串
|
||||
$string = (string) $pinyin; // nǐ hǎo shì jiè
|
||||
|
||||
$pinyin->toArray(); // ['nǐ', 'hǎo', 'shì', 'jiè']
|
||||
|
||||
// 直接使用索引访问
|
||||
$pinyin[0]; // 'nǐ'
|
||||
|
||||
// 使用函数遍历
|
||||
$pinyin->map('ucfirst'); // ['Nǐ', 'Hǎo', 'Shì', 'Jiè']
|
||||
|
||||
// 拼接为字符串
|
||||
$pinyin->join(' '); // 'nǐ hǎo shì jiè'
|
||||
$pinyin->join('-'); // 'nǐ-hǎo-shì-jiè'
|
||||
|
||||
// 转成 json
|
||||
$pinyin->toJson(); // '["nǐ","hǎo","shì","jiè"]'
|
||||
json_encode($pinyin); // '["nǐ","hǎo","shì","jiè"]'
|
||||
```
|
||||
|
||||
### 文字段落转拼音
|
||||
|
||||
```php
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
|
||||
echo Pinyin::sentence('带着希望去旅行,比到达终点更美好');
|
||||
// dài zhe xī wàng qù lǚ xíng , bǐ dào dá zhōng diǎn gèng měi hǎo
|
||||
|
||||
// 去除声调
|
||||
echo Pinyin::sentence('带着希望去旅行,比到达终点更美好', 'none');
|
||||
// dai zhe xi wang qu lv xing , bi dao da zhong dian geng mei hao
|
||||
|
||||
// 保留所有非汉字字符
|
||||
echo Pinyin::fullSentence('ル是片假名,π是希腊字母', 'none');
|
||||
// ル shi pian jia ming ,π shi xi la zi mu
|
||||
```
|
||||
|
||||
### 生成用于链接的拼音字符串
|
||||
|
||||
通常用于文章链接等,可以使用 `permalink` 方法获取拼音字符串:
|
||||
|
||||
```php
|
||||
echo Pinyin::permalink('带着希望去旅行'); // dai-zhe-xi-wang-qu-lyu-xing
|
||||
echo Pinyin::permalink('带着希望去旅行', '.'); // dai.zhe.xi.wang.qu.lyu.xing
|
||||
```
|
||||
|
||||
### 获取首字符字符串
|
||||
|
||||
通常用于创建搜索用的索引,可以使用 `abbr` 方法转换:
|
||||
|
||||
```php
|
||||
Pinyin::abbr('带着希望去旅行'); // ['d', 'z', 'x', 'w', 'q', 'l', 'x']
|
||||
echo Pinyin::abbr('带着希望去旅行')->join('-'); // d-z-x-w-q-l-x
|
||||
echo Pinyin::abbr('你好2018!')->join(''); // nh2018
|
||||
echo Pinyin::abbr('Happy New Year! 2018!')->join(''); // HNY2018
|
||||
|
||||
// 保留原字符串的英文单词
|
||||
echo Pinyin::abbr('CGV电影院', false, true)->join(''); // CGVdyy
|
||||
```
|
||||
|
||||
**姓名首字母**
|
||||
|
||||
将首字作为姓氏转换,其余作为普通词语转换:
|
||||
|
||||
```php
|
||||
Pinyin::nameAbbr('欧阳'); // ['o', 'y']
|
||||
echo Pinyin::nameAbbr('单单单')->join('-'); // s-d-d
|
||||
```
|
||||
|
||||
### 姓名转换
|
||||
|
||||
姓名的姓的读音有些与普通字不一样,比如 ‘单’ 常见的音为 `dan`,而作为姓的时候读 `shan`。
|
||||
|
||||
```php
|
||||
Pinyin::name('单某某'); // ['shàn', 'mǒu', 'mǒu']
|
||||
Pinyin::name('单某某', 'none'); // ['shan', 'mou', 'mou']
|
||||
Pinyin::name('单某某', 'none')->join('-'); // shan-mou-mou
|
||||
```
|
||||
|
||||
### 护照姓名转换
|
||||
|
||||
根据国家规定 [关于中国护照旅行证上姓名拼音 ü(吕、律、闾、绿、女等)统一拼写为 YU 的提醒](http://sg.china-embassy.gov.cn/lsfw/zghz1/hzzxdt/201501/t20150122_2022198.htm) 的规则,将 `ü` 转换为 `yu`:
|
||||
|
||||
```php
|
||||
Pinyin::passportName('吕小布'); // ['lyu', 'xiao', 'bu']
|
||||
Pinyin::passportName('女小花'); // ['nyu', 'xiao', 'hua']
|
||||
Pinyin::passportName('律师'); // ['lyu', 'shi']
|
||||
```
|
||||
|
||||
### 多音字
|
||||
|
||||
多音字的返回值为关联数组的集合,默认返回去重后的所有读音:
|
||||
|
||||
```php
|
||||
$pinyin = Pinyin::polyphones('重庆');
|
||||
|
||||
$pinyin['重']; // ["zhòng", "chóng", "tóng"]
|
||||
$pinyin['庆']; // ["qìng"]
|
||||
|
||||
$pinyin->toArray();
|
||||
// [
|
||||
// "重": ["zhòng", "chóng", "tóng"],
|
||||
// "庆": ["qìng"]
|
||||
// ]
|
||||
```
|
||||
|
||||
如果不想要去重,可以数组形式返回:
|
||||
|
||||
```php
|
||||
$pinyin = Pinyin::polyphones('重庆重庆', Converter::TONE_STYLE_SYMBOL, true);
|
||||
|
||||
// or
|
||||
$pinyin = Pinyin::polyphonesAsArray('重庆重庆', Converter::TONE_STYLE_SYMBOL);
|
||||
|
||||
$pinyin->toArray();
|
||||
// [
|
||||
// ["重" => ["zhòng", "chóng", "tóng"]],
|
||||
// ["庆" => ["qìng"]],
|
||||
// ["重" => ["zhòng", "chóng", "tóng"]],
|
||||
// ["庆" => ["qìng"]]
|
||||
// ]
|
||||
```
|
||||
|
||||
### 单字转拼音
|
||||
|
||||
和多音字类似,单字的返回值为字符串,多音字将根据该字字频调整得到常用音:
|
||||
|
||||
```php
|
||||
$pinyin = Pinyin::chars('重庆');
|
||||
|
||||
echo $pinyin['重']; // "zhòng"
|
||||
echo $pinyin['庆']; // "qìng"
|
||||
|
||||
$pinyin->toArray();
|
||||
// [
|
||||
// "重": "zhòng",
|
||||
// "庆": "qìng"
|
||||
// ]
|
||||
```
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> 当单字处理时由于多音字来自词频表中取得常用音,所以在词语环境下可能出现不正确的情况,建议使用多音字处理。
|
||||
|
||||
更多使用请参考 [测试用例](https://github.com/overtrue/pinyin/blob/master/tests/PinyinTest.php)。
|
||||
|
||||
## v/yu/ü 的问题
|
||||
|
||||
根据国家语言文字工作委员会的规定,`lv`、`lyu`、`lǚ` 都是正确的,但是 `lv` 是最常用的,所以默认使用 `lv`,如果你需要使用其他的,可以在初始化时传入:
|
||||
|
||||
```php
|
||||
echo Pinyin::sentence('旅行');
|
||||
// lǚ xíng
|
||||
|
||||
echo Pinyin::sentence('旅行', 'none');
|
||||
// lv xing
|
||||
|
||||
echo Pinyin::yuToYu()->sentence('旅行', 'none');
|
||||
// lyu xing
|
||||
|
||||
echo Pinyin::yuToU()->sentence('旅行', 'none');
|
||||
// lu xing
|
||||
|
||||
echo Pinyin::yuToV()->sentence('旅行', 'none');
|
||||
// lv xing
|
||||
```
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> 仅在拼音风格为非 `none` 模式下有效。
|
||||
|
||||
## 命令行工具
|
||||
|
||||
你可以使用命令行来实现拼音的转换:
|
||||
|
||||
```bash
|
||||
php ./bin/pinyin 带着希望去旅行 --method=sentence --tone-style=symbol
|
||||
# dài zhe xī wàng qù lǚ xíng
|
||||
```
|
||||
|
||||
更多使用方法,可以查看帮助文档:
|
||||
|
||||
```bash
|
||||
php ./bin/pinyin --help
|
||||
|
||||
# Usage:
|
||||
# ./pinyin [chinese] [method] [options]
|
||||
# Options:
|
||||
# -j, --json 输出 JSON 格式.
|
||||
# -c, --compact 不格式化输出 JSON.
|
||||
# -m, --method=[method] 转换方式,可选:sentence/sentenceFull/permalink/abbr/nameAbbr/name/passportName/phrase/polyphones/chars.
|
||||
# --no-tone 不使用音调.
|
||||
# --tone-style=[style] 音调风格,可选值:symbol/none/number, default: none.
|
||||
# -h, --help 显示帮助.
|
||||
```
|
||||
|
||||
## 在 Laravel 中使用
|
||||
|
||||
独立的包在这里:[overtrue/laravel-pinyin](https://github.com/overtrue/laravel-pinyin)
|
||||
|
||||
## 中文简繁转换
|
||||
|
||||
如何你有这个需求,也可以了解我的另一个包:[overtrue/php-opencc](https://github.com/overtrue/php-opencc)
|
||||
|
||||
## Contribution
|
||||
|
||||
欢迎提意见及完善补充词库:
|
||||
|
||||
- 单字拼音错误请添加到:[sources/pathes/chars.txt](https://github.com/overtrue/pinyin/blob/master/sources/pathes/chars.txt);
|
||||
- 词语错误或补齐,请添加到:[sources/pathes/words.txt](https://github.com/overtrue/pinyin/blob/master/sources/pathes/words.txt);
|
||||
|
||||
## 参考
|
||||
|
||||
- [mozillazg/pinyin-data](https://github.com/mozillazg/pinyin-data)
|
||||
- [详细参考资料](https://github.com/overtrue/pinyin-resources)
|
||||
|
||||
## :heart: Sponsor me
|
||||
|
||||
[](https://github.com/sponsors/overtrue)
|
||||
|
||||
如果你喜欢我的项目并想支持它,[点击这里 :heart:](https://github.com/sponsors/overtrue)
|
||||
|
||||
## Project supported by JetBrains
|
||||
|
||||
Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.
|
||||
|
||||
[](https://www.jetbrains.com/?from=https://github.com/overtrue)
|
||||
|
||||
## PHP 扩展包开发
|
||||
|
||||
> 想知道如何从零开始构建 PHP 扩展包?
|
||||
>
|
||||
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
require __DIR__ . '/utils.php';
|
||||
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
|
||||
$methods = ['sentence','fullSentence','name','passportName','phrase','permalink','polyphones','chars','abbr','nameAbbr'];
|
||||
$method = 'sentence';
|
||||
$inputOptions = [];
|
||||
$methodAsString = implode('/', $methods);
|
||||
|
||||
$help = <<<"HELP"
|
||||
Usage:
|
||||
./pinyin [chinese] [method] [options]
|
||||
Options:
|
||||
-j, --json 输出 JSON 格式.
|
||||
-c, --compact 不格式化输出 JSON.
|
||||
-m, --method=[method] 转换方式,可选:{$methodAsString}.
|
||||
--no-tone 不使用音调.
|
||||
--tone-style=[style] 音调风格,可选值:symbol/none/number, default: none.
|
||||
-h, --help 显示帮助.
|
||||
HELP;
|
||||
|
||||
function has_option($option, $alias = null): bool
|
||||
{
|
||||
global $inputOptions;
|
||||
|
||||
if ($alias) {
|
||||
return array_key_exists($option, $inputOptions) || array_key_exists($alias, $inputOptions);
|
||||
}
|
||||
|
||||
return array_key_exists($option, $inputOptions);
|
||||
}
|
||||
|
||||
function get_option($option, $default = null, $alias = null): ?string
|
||||
{
|
||||
global $inputOptions;
|
||||
|
||||
if ($alias) {
|
||||
return $inputOptions[$option] ?? $inputOptions[$alias] ?? $default;
|
||||
}
|
||||
|
||||
return $inputOptions[$option] ?? $default;
|
||||
}
|
||||
|
||||
$inputOptions = parse_options($argv);
|
||||
$input = $inputOptions[0] ?? null;
|
||||
|
||||
if (empty($input) || has_option('help', 'h')) {
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$method = get_option('method', $method, 'm');
|
||||
$toneStyle = has_option('no-tone') ? 'none' : get_option('tone-style', 'none');
|
||||
|
||||
if (! in_array($method, $methods)) {
|
||||
echo "Method '{$method}' is not supported.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$result = Pinyin::$method($input, $method === 'permalink' ? '-' : $toneStyle);
|
||||
|
||||
$toJson = has_option('json', 'j') || in_array($method, ['polyphones']);
|
||||
|
||||
if ($toJson) {
|
||||
$options = JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT;
|
||||
|
||||
if (has_option('compact', 'c')) {
|
||||
$options = 0;
|
||||
}
|
||||
|
||||
$result = json_encode($result, $options);
|
||||
}
|
||||
|
||||
echo $result, "\n";
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "overtrue/pinyin",
|
||||
"description": "Chinese to pinyin translator.",
|
||||
"keywords": [
|
||||
"chinese",
|
||||
"pinyin",
|
||||
"cn2pinyin"
|
||||
],
|
||||
"homepage": "https://github.com/overtrue/pinyin",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "overtrue",
|
||||
"homepage": "http://github.com/overtrue",
|
||||
"email": "anzhengchao@gmail.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0|^11.2",
|
||||
"brainmaestro/composer-git-hooks": "^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"nunomaduro/termwind": "^1.0|^2.0",
|
||||
"laravel/pint": "^1.10"
|
||||
},
|
||||
"bin": [
|
||||
"bin/pinyin"
|
||||
],
|
||||
"extra": {
|
||||
"hooks": {
|
||||
"pre-commit": [
|
||||
"composer pint",
|
||||
"composer test"
|
||||
],
|
||||
"pre-push": [
|
||||
"composer pint",
|
||||
"composer test"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-update-cmd": [
|
||||
"cghooks update"
|
||||
],
|
||||
"post-merge": "composer install",
|
||||
"post-install-cmd": [
|
||||
"cghooks remove",
|
||||
"cghooks add --ignore-lock"
|
||||
],
|
||||
"cghooks": "vendor/bin/cghooks",
|
||||
"pint": "vendor/bin/pint ./src ./tests",
|
||||
"fix-style": "vendor/bin/pint ./src ./tests",
|
||||
"test": "vendor/bin/phpunit --colors=always",
|
||||
"build": "php ./bin/build",
|
||||
"benchmark": "php ./benchmark/run.php"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"test": "Run all tests.",
|
||||
"fix-style": "Run style checks and fix violations."
|
||||
}
|
||||
}
|
||||
+177507
File diff suppressed because it is too large
Load Diff
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
return array (
|
||||
'万俟' => ' mò qí ',
|
||||
'尉迟' => ' yù chí ',
|
||||
'单于' => ' chán yú ',
|
||||
'重' => ' chóng ',
|
||||
'秘' => ' bì ',
|
||||
'冼' => ' xiǎn ',
|
||||
'华' => ' huà ',
|
||||
'过' => ' guō ',
|
||||
'纪' => ' jǐ ',
|
||||
'燕' => ' yān ',
|
||||
'种' => ' chóng ',
|
||||
'繁' => ' pó ',
|
||||
'幺' => ' yāo ',
|
||||
'覃' => ' qín ',
|
||||
'冯' => ' féng ',
|
||||
'石' => ' shí ',
|
||||
'缪' => ' miào ',
|
||||
'瞿' => ' qú ',
|
||||
'曾' => ' zēng ',
|
||||
'解' => ' xiè ',
|
||||
'折' => ' shè ',
|
||||
'那' => ' nā ',
|
||||
'佴' => ' nài ',
|
||||
'难' => ' nàn ',
|
||||
'粘' => ' niàn ',
|
||||
'藏' => ' zàng ',
|
||||
'扎' => ' zā ',
|
||||
'翟' => ' zhái ',
|
||||
'都' => ' dū ',
|
||||
'六' => ' lù ',
|
||||
'薄' => ' bó ',
|
||||
'贾' => ' jiǎ ',
|
||||
'的' => ' dē ',
|
||||
'哈' => ' hǎ ',
|
||||
'居' => ' jū ',
|
||||
'盖' => ' gě ',
|
||||
'查' => ' zhā ',
|
||||
'盛' => ' shèng ',
|
||||
'塔' => ' tǎ ',
|
||||
'和' => ' hé ',
|
||||
'柏' => ' bǎi ',
|
||||
'朴' => ' piáo ',
|
||||
'蓝' => ' lán ',
|
||||
'牟' => ' móu ',
|
||||
'殷' => ' yīn ',
|
||||
'陆' => ' lù ',
|
||||
'乜' => ' niè ',
|
||||
'乐' => ' yuè ',
|
||||
'阚' => ' kàn ',
|
||||
'叶' => ' yè ',
|
||||
'强' => ' qiáng ',
|
||||
'不' => ' fǒu ',
|
||||
'丁' => ' dīng ',
|
||||
'阿' => ' ā ',
|
||||
'汤' => ' tāng ',
|
||||
'万' => ' wàn ',
|
||||
'车' => ' chē ',
|
||||
'称' => ' chēng ',
|
||||
'沈' => ' shěn ',
|
||||
'区' => ' ōu ',
|
||||
'仇' => ' qiú ',
|
||||
'宿' => ' sù ',
|
||||
'南' => ' nán ',
|
||||
'单' => ' shàn ',
|
||||
'卜' => ' bǔ ',
|
||||
'鸟' => ' niǎo ',
|
||||
'思' => ' sī ',
|
||||
'殳' => ' shū ',
|
||||
'寻' => ' xún ',
|
||||
'於' => ' yú ',
|
||||
'烟' => ' yān ',
|
||||
'余' => ' yú ',
|
||||
'浅' => ' qiǎn ',
|
||||
'艾' => ' ài ',
|
||||
'浣' => ' wǎn ',
|
||||
'无' => ' wú ',
|
||||
'信' => ' xìn ',
|
||||
'许' => ' xǔ ',
|
||||
'齐' => ' qí ',
|
||||
'俞' => ' yú ',
|
||||
'若' => ' ruò ',
|
||||
'贠' => ' yùn ',
|
||||
'貟' => ' yùn ',
|
||||
'么' => ' yāo ',
|
||||
);
|
||||
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+8003
File diff suppressed because it is too large
Load Diff
+1503
File diff suppressed because it is too large
Load Diff
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
parameters:
|
||||
level: 9
|
||||
paths:
|
||||
- src
|
||||
inferPrivatePropertyTypeFromConstructor: true
|
||||
checkMissingIterableValueType: false
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use ArrayAccess;
|
||||
use JsonSerializable;
|
||||
use Stringable;
|
||||
|
||||
class Collection implements ArrayAccess, JsonSerializable, Stringable
|
||||
{
|
||||
public function __construct(protected $items = []) {}
|
||||
|
||||
public function join(string $separator = ' '): string
|
||||
{
|
||||
return implode($separator, \array_map(function ($item) {
|
||||
return \is_array($item) ? '['.\implode(', ', $item).']' : $item;
|
||||
}, $this->items));
|
||||
}
|
||||
|
||||
public function map(callable $callback): Collection
|
||||
{
|
||||
return new static(array_map($callback, $this->all()));
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->all();
|
||||
}
|
||||
|
||||
public function toJson(int $options = 0): string
|
||||
{
|
||||
return json_encode($this->all(), $options);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->join();
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->items[$offset] ?? null;
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
class Converter
|
||||
{
|
||||
private const SEGMENTS_COUNT = 10;
|
||||
|
||||
private const WORDS_PATH = __DIR__.'/../data/words-%s.php';
|
||||
|
||||
private const CHARS_PATH = __DIR__.'/../data/chars.php';
|
||||
|
||||
private const SURNAMES_PATH = __DIR__.'/../data/surnames.php';
|
||||
|
||||
public const TONE_STYLE_SYMBOL = 'symbol';
|
||||
|
||||
public const TONE_STYLE_NUMBER = 'number';
|
||||
|
||||
public const TONE_STYLE_NONE = 'none';
|
||||
|
||||
protected bool $heteronym = false;
|
||||
|
||||
protected bool $heteronymAsList = false;
|
||||
|
||||
protected bool $asSurname = false;
|
||||
|
||||
protected bool $noWords = false;
|
||||
|
||||
protected bool $cleanup = true;
|
||||
|
||||
protected string $yuTo = 'v';
|
||||
|
||||
protected string $toneStyle = self::TONE_STYLE_SYMBOL;
|
||||
|
||||
protected array $regexps = [
|
||||
'separator' => '\p{Z}',
|
||||
'mark' => '\p{M}',
|
||||
'tab' => "\t",
|
||||
];
|
||||
|
||||
public const REGEXPS = [
|
||||
'number' => '0-9',
|
||||
'alphabet' => 'a-zA-Z',
|
||||
// 中文不带符号
|
||||
'hans' => '\x{3007}\x{2E80}-\x{2FFF}\x{3100}-\x{312F}\x{31A0}-\x{31EF}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}',
|
||||
// 符号: !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`
|
||||
'punctuation' => '\p{P}',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->regexps = \array_merge($this->regexps, self::REGEXPS);
|
||||
}
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
return new static;
|
||||
}
|
||||
|
||||
public function heteronym(bool $asList = false): static
|
||||
{
|
||||
$this->heteronym = true;
|
||||
$this->heteronymAsList = $asList;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `heteronym` instead.
|
||||
* This method will be removed in the next major version.
|
||||
*/
|
||||
public function polyphonic(bool $asList = false): static
|
||||
{
|
||||
return $this->heteronym($asList);
|
||||
}
|
||||
|
||||
public function surname(): static
|
||||
{
|
||||
$this->asSurname = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noWords(): static
|
||||
{
|
||||
$this->noWords = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noCleanup(): static
|
||||
{
|
||||
$this->cleanup = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onlyHans(): static
|
||||
{
|
||||
// 中文汉字不含符号
|
||||
$this->regexps['hans'] = self::REGEXPS['hans'];
|
||||
|
||||
return $this->noAlpha()->noNumber()->noPunctuation();
|
||||
}
|
||||
|
||||
public function noAlpha(): static
|
||||
{
|
||||
unset($this->regexps['alphabet']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noNumber(): static
|
||||
{
|
||||
unset($this->regexps['number']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noPunctuation(): static
|
||||
{
|
||||
unset($this->regexps['punctuation']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withToneStyle(string $toneStyle): static
|
||||
{
|
||||
$this->toneStyle = $toneStyle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noTone(): static
|
||||
{
|
||||
$this->toneStyle = self::TONE_STYLE_NONE;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useNumberTone(): static
|
||||
{
|
||||
$this->toneStyle = self::TONE_STYLE_NUMBER;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yuToYu(): static
|
||||
{
|
||||
$this->yuTo = 'yu';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yuToV(): static
|
||||
{
|
||||
$this->yuTo = 'v';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yuToU(): static
|
||||
{
|
||||
$this->yuTo = 'u';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function when(bool $condition, callable $callback): static
|
||||
{
|
||||
if ($condition) {
|
||||
$callback($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function convert(string $string, ?callable $beforeSplit = null): Collection
|
||||
{
|
||||
// 把原有的数字和汉字分离,避免拼音转换时被误作声调
|
||||
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
|
||||
return "\t".$matches[0];
|
||||
}, $string);
|
||||
|
||||
// 过滤掉不保留的字符
|
||||
if ($this->cleanup) {
|
||||
$string = \preg_replace(\sprintf('~[^%s]~u', \implode($this->regexps)), '', $string);
|
||||
}
|
||||
|
||||
// 多音字
|
||||
if ($this->heteronym) {
|
||||
return $this->convertAsChars($string, true);
|
||||
}
|
||||
|
||||
if ($this->noWords) {
|
||||
return $this->convertAsChars($string);
|
||||
}
|
||||
|
||||
// 替换姓氏
|
||||
if ($this->asSurname) {
|
||||
$string = $this->convertSurname($string);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < self::SEGMENTS_COUNT; $i++) {
|
||||
$string = strtr($string, require sprintf(self::WORDS_PATH, $i));
|
||||
}
|
||||
|
||||
return $this->split($beforeSplit ? $beforeSplit($string) : $string);
|
||||
}
|
||||
|
||||
public function convertAsChars(string $string, bool $polyphonic = false): Collection
|
||||
{
|
||||
$map = require self::CHARS_PATH;
|
||||
|
||||
// split string as chinese chars
|
||||
$chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$items = [];
|
||||
foreach ($chars as $char) {
|
||||
if (isset($map[$char])) {
|
||||
if ($polyphonic) {
|
||||
$pinyin = \array_map(fn ($pinyin) => $this->formatTone($pinyin, $this->toneStyle), $map[$char]);
|
||||
if ($this->heteronymAsList) {
|
||||
$items[] = [$char => $pinyin];
|
||||
} else {
|
||||
$items[$char] = $pinyin;
|
||||
}
|
||||
|
||||
} else {
|
||||
$items[$char] = $this->formatTone($map[$char][0], $this->toneStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection($items);
|
||||
}
|
||||
|
||||
protected function convertSurname(string $name): string
|
||||
{
|
||||
static $surnames = null;
|
||||
$surnames ??= require self::SURNAMES_PATH;
|
||||
|
||||
foreach ($surnames as $surname => $pinyin) {
|
||||
if (\str_starts_with($name, $surname)) {
|
||||
return $pinyin.\mb_substr($name, \mb_strlen($surname));
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
protected function split(string $item): Collection
|
||||
{
|
||||
$items = \array_values(array_filter(preg_split('/\s+/i', $item)));
|
||||
|
||||
foreach ($items as $index => $item) {
|
||||
$items[$index] = $this->formatTone($item, $this->toneStyle);
|
||||
}
|
||||
|
||||
return new Collection($items);
|
||||
}
|
||||
|
||||
protected function formatTone(string $pinyin, string $style): string
|
||||
{
|
||||
if ($style === self::TONE_STYLE_SYMBOL) {
|
||||
return $pinyin;
|
||||
}
|
||||
|
||||
$replacements = [
|
||||
// mb_chr(593) => 'ɑ' 轻声中除了 `ɑ` 和 `ü` 以外,其它和字母一样
|
||||
'ɑ' => ['a', 5], 'ü' => ['v', 5],
|
||||
'üē' => ['ue', 1], 'üé' => ['ue', 2], 'üě' => ['ue', 3], 'üè' => ['ue', 4],
|
||||
'ā' => ['a', 1], 'ē' => ['e', 1], 'ī' => ['i', 1], 'ō' => ['o', 1], 'ū' => ['u', 1], 'ǖ' => ['v', 1],
|
||||
'á' => ['a', 2], 'é' => ['e', 2], 'í' => ['i', 2], 'ó' => ['o', 2], 'ú' => ['u', 2], 'ǘ' => ['v', 2],
|
||||
'ǎ' => ['a', 3], 'ě' => ['e', 3], 'ǐ' => ['i', 3], 'ǒ' => ['o', 3], 'ǔ' => ['u', 3], 'ǚ' => ['v', 3],
|
||||
'à' => ['a', 4], 'è' => ['e', 4], 'ì' => ['i', 4], 'ò' => ['o', 4], 'ù' => ['u', 4], 'ǜ' => ['v', 4],
|
||||
];
|
||||
|
||||
foreach ($replacements as $unicode => $replacement) {
|
||||
if (\str_contains($pinyin, $unicode)) {
|
||||
$umlaut = $replacement[0];
|
||||
|
||||
// https://zh.wikipedia.org/wiki/%C3%9C
|
||||
if ($this->yuTo !== 'v' && $umlaut === 'v') {
|
||||
$umlaut = $this->yuTo;
|
||||
}
|
||||
|
||||
$pinyin = \str_replace($unicode, $umlaut, $pinyin);
|
||||
|
||||
if ($this->toneStyle === self::TONE_STYLE_NUMBER) {
|
||||
$pinyin .= $replacement[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pinyin;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @method static Converter surname()
|
||||
* @method static Converter noWords()
|
||||
* @method static Converter onlyHans()
|
||||
* @method static Converter noAlpha()
|
||||
* @method static Converter noNumber()
|
||||
* @method static Converter noCleanup()
|
||||
* @method static Converter noPunctuation()
|
||||
* @method static Converter noTone()
|
||||
* @method static Converter useNumberTone()
|
||||
* @method static Converter yuToV()
|
||||
* @method static Converter yuToU()
|
||||
* @method static Converter polyphonic(bool $asList = false)
|
||||
* @method static Converter withToneStyle(string $toneStyle = 'symbol')
|
||||
* @method static Collection convert(string $string, callable $beforeSplit = null)
|
||||
*/
|
||||
class Pinyin
|
||||
{
|
||||
public static function name(string $name, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::converter()->surname()->withToneStyle($toneStyle)->convert($name);
|
||||
}
|
||||
|
||||
public static function passportName(string $name, string $toneStyle = Converter::TONE_STYLE_NONE): Collection
|
||||
{
|
||||
return self::converter()->surname()->yuToYu()->withToneStyle($toneStyle)->convert($name);
|
||||
}
|
||||
|
||||
public static function phrase(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::converter()->noPunctuation()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function sentence(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::converter()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function fullSentence(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::converter()->noCleanup()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function heteronym(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL, bool $asList = false): Collection
|
||||
{
|
||||
return self::converter()->heteronym($asList)->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function heteronymAsList(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::heteronym($string, $toneStyle, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `heteronym` instead.
|
||||
* This method will be removed in the next major version.
|
||||
*/
|
||||
public static function polyphones(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL, bool $asList = false): Collection
|
||||
{
|
||||
return self::heteronym($string, $toneStyle, $asList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `heteronymAsList` instead.
|
||||
* This method will be removed in the next major version.
|
||||
*/
|
||||
public static function polyphonesAsArray(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::heteronym($string, $toneStyle, true);
|
||||
}
|
||||
|
||||
public static function chars(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::converter()->onlyHans()->noWords()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function permalink(string $string, string $delimiter = '-'): string
|
||||
{
|
||||
if (! in_array($delimiter, ['_', '-', '.', ''], true)) {
|
||||
throw new InvalidArgumentException("Delimiter must be one of: '_', '-', '', '.'.");
|
||||
}
|
||||
|
||||
return self::converter()->noPunctuation()->noTone()->convert($string)->join($delimiter);
|
||||
}
|
||||
|
||||
public static function nameAbbr(string $string): Collection
|
||||
{
|
||||
return self::abbr($string, true);
|
||||
}
|
||||
|
||||
public static function abbr(string $string, bool $asName = false, bool $preserveEnglishWords = false): Collection
|
||||
{
|
||||
return self::converter()->noTone()
|
||||
->noPunctuation()
|
||||
->when($asName, fn ($c) => $c->surname())
|
||||
->convert($string)
|
||||
->map(function ($pinyin) use ($string, $preserveEnglishWords) {
|
||||
// 如果内容在原字符串中,则直接返回
|
||||
if ($preserveEnglishWords && str_contains($string, $pinyin)) {
|
||||
return $pinyin;
|
||||
}
|
||||
|
||||
// 常用于电影名称入库索引处理,例如:《晚娘2012》-> WN2012
|
||||
return \is_numeric($pinyin) || preg_match('/\d{2,}/', $pinyin) ? $pinyin : \mb_substr($pinyin, 0, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public static function converter(): Converter
|
||||
{
|
||||
return Converter::make();
|
||||
}
|
||||
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
$converter = self::converter();
|
||||
|
||||
if (\method_exists($converter, $name)) {
|
||||
return $converter->$name(...$arguments);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Method {$name} does not exist.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user