prinsfrank / transliteration
一个对原生 PHP 转换器进行类型包装的封装,提供类型化且有文档说明的参数,而不是任意字符串
Requires
- php: ^8.1 || ^8.2 || ^8.3
- ext-intl: *
- prinsfrank/standards: ^3.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.46
- phpstan/phpstan: ^1.10
- phpstan/phpstan-strict-rules: ^1.5
- phpunit/phpunit: ^10.4
README
转写
一个对原生 PHP 转换器进行类型包装的封装,提供类型化且有文档说明的参数,而不是任意字符串
设置
注意 确保您正在运行 PHP 8.1 或更高版本以使用此包
要立即开始,请在您的 composer 项目中运行以下命令;
composer require prinsfrank/transliteration
或仅用于开发;
composer require prinsfrank/transliteration --dev
为什么选择这个包?
转写是用来表示或拼写另一个字母表中的字符。但 PHP 中的转写功能远不止于此。遗憾的是,它的文档 很少,并且您需要查看 ICU 文档 以获取完整的特性列表。
但最糟糕的是:创建转换器的参数是字符串。此包提供了一个完整的严格类型包装来创建转换器。它还提供了一些现成的 ConversionSets,让您可以迅速开始使用!
用例
个人通讯
假设您有一个多语言平台(商店、SAAS 等),但客户支持人员只说其中几种。他们可以使用本地语言(语言)的平台,但不能翻译像名字或地址这样的东西。
所以,一位客户打电话过来,他们已经将他们的名字输入为 이준
。作为不说法语但只说英语的人来说,该如何发音?很简单:将其转写并在其名字旁边显示: 이준 (Ijun)
身份验证
另一个例子:根据当地法律和法规,您必须在您的平台上验证用户的身份(银行、雇主等)。他们已将其官方名字输入为 Ivan
,但他们的身份证明文件显示他们的名字是 Иван
。对于不识读西里尔文的人来说,这可能很难判断,但实际上它们是相同的。在这种情况下,根据身份证明文件的国籍显示多种脚本的名字可能是有意义的。
转写基础知识
transliteratorBuilder 是此包中的主要入口点。它提供了一种通用但严格类型的接口,用于 ext-intl 转换器。
简单转换
最简单的例子可能是用其他一些字母替换一个字母,例如在德语中
(new TransliteratorBuilder()) ->addConversion(new Conversion('ß', 'ss')) ->transliterate('Straße'); // Strasse
幕后,这最终被转换为一个字符串,用于构建基于规则的 ext-intl 转换器; 'ß>ss;'
在这个例子中,这个包的优势并不明显,所以让我们来看一个更具挑战性的例子。
脚本转换
假设我们有一堆任意脚本中的文本,并且我们想确保它们都可以表示为 ASCII。
(new TransliteratorBuilder()) ->applyConversionSet(new ToASCII()) ->transliterate('アマゾン'); // amazon
由于无法直接从任何脚本转换为 ASCII,因此幕后进行了以下转换: 'Any-Latin;Latin-ASCII;'
IPA 到英语近似
现在我们更进一步:在某个环节,我们的转换结果是一个 IPA 字符而不是英语字符。如果我们能在英语中近似它怎么办?这个包已经为您准备好了!
(new TransliteratorBuilder()) ->applyConversionSet(new ToASCII()) ->applyConversionSet(new IPAToEnglishApproximation()) ->transliterate('naɕi gʌba'); // naci guba
幕后为您创建以下规则集字符串:::Any-Latin;::Latin-ASCII;dʒ>g;kʰ>c;kʷ>qu;kᶣ>cu;ɫ>ll;ŋ>n;Ŋ>N;ɲ>n;Ɲ>N;pʰ>p;ʃ>sh;Ʃ>SH;tʰ>t;tʃ>ch;aː>a;Aː>A;ɛ>e;Ɛ>E;eː>a;Eː>A;ɪ>i;Ɪ>I;iː>i;Iː>I;ɔ>o;Ɔ>O;oː>aw;ʊ>u;Ʊ>U;ʌ>u;Ʌ>U;uː>u;yː>u;ae̯>igh;oe̯>oy;au̯>ow;ei̯>ay;ui̯>ui;
在此文档的其余部分中,让我们忽略生成的规则集字符串,因为此包的目标是提供一个抽象但类型化的接口,而不需要您了解其底层语法。
转换集
转换集是这个包中最大的构建块。可以通过为每个集合调用 applyConversionSet
方法来应用它们;
(new TransliteratorBuilder()) ->applyConversionSet(new ToASCII()) ->applyConversionSet(new IPAToEnglishApproximation())
或者通过传递转换集数组调用 applyConversionSets
方法;
(new TransliteratorBuilder()) ->applyConversionSets([ new ToASCII(), new IPAToEnglishApproximation(), ])
捆绑的转换集
此包包含许多转换集
创建自定义转换集
要创建自定义转换集,请简单地
- 将 ConversionSet 接口 添加到您的类中;
use PrinsFrank\Transliteration\ConversionSet; class CustomConversionSet implements ConversionSet { }
- 添加对 apply 方法的实现;
public function apply(TransliteratorBuilder $transliteratorBuilder): void { // Add your code here }
- 并将您的新自定义转换传递给 transliteratorBuilder;
(new TransliteratorBuilder()) ->applyConversionSet(new CustomConversionSet());
如果 ConversionSet 需要任何参数,您可以实现一个构造函数并在 apply 方法中使用这些参数。关于此的简单示例,请参阅 Replace ConversionSet。
SingleID
SingleID 是最基本的一种转换。它期望一个 BasicID 和一个可选的过滤器。BasicID 的参数是自我文档化的;将 ScriptName、ScriptAlias、Language 或 SpecialTag 传递给目标(和可选的源)以转换为(和从)脚本和语言或特殊标记;
public function apply(TransliteratorBuilder $transliteratorBuilder): void { $transliteratorBuilder->addSingleID( new SingleID( new BasicID(SpecialTag::ASCII, SpecialTag::Any), ) ); }
转换
要添加从 A 到 B 的自定义转换,可以直接添加;
public function apply(TransliteratorBuilder $transliteratorBuilder): void { $transliteratorBuilder->addConversion( new Conversion('A', 'B') ); }
这里有一些额外的参数可用:beforeContext
、afterContext
和 resultToRevisit
。这些目前在此处未记录,但在 ICU 规则文档中可以详细了解。
VariableDefinition
可以创建变量以供以后重用。例如,如果您想多次使用字符串 'bar',则可以创建一个命名变量
public function apply(TransliteratorBuilder $transliteratorBuilder): void { $transliteratorBuilder->addVariableDefinition( new VariableDefinition('foo', 'bar') ); }
请注意,变量名称不能为空,也不能包含任何特殊字符。如果您这样做,VariableDefinition 构造函数将抛出一个异常,警告您这样做。如果变量值包含任何特殊字符,则将自动转义。
(全局)过滤器
全局过滤器可以从 ConversionSet 的作用域中技术上修改,但不应这样做。如果 ConversionSet A 修改了全局过滤器,然后 ConversionSet B 修改了它,ConversionSet A 将不再正常工作。相反,请确保为每个 ID 添加 SingleID 级别的过滤器;
public function apply(TransliteratorBuilder $transliteratorBuilder): void { $transliteratorBuilder->addSingleID( new SingleID( new BasicID(SpecialTag::ASCII, SpecialTag::Any), (new Filter())->addRange(new Character('a'), new Character('z')) ) ); }
ConversionSet 嵌套
可以将转换集嵌套;
public function apply(TransliteratorBuilder $transliteratorBuilder): void { $transliteratorBuilder->applyConversionSet(new ToASCII()); }
为了防止递归,不能使用已在 ConversionSet 链中调用的 ConversionSet。例如:可以使用 A 在 B 中,B 在 C 中,但不能使用 A 在 B 中和 B 在 A 中。
技术:转换器组合
为了给此包的用户提供一个美好且可读的参数列表,根据规范实现了创建普通转换器的 formalIDSyntax 和 TransformRuleSyntax。它们之间的关系如下
当通过调用 applyConversionSet
、applyConversionSets
、addSingleID
、addConversion
或 AddVariableDefinition
构建 transliterator 时,内部数组 $conversions
将直接填充,或者通过转换集在 apply
方法中填充。
在随后创建转写器时——无论是通过调用 getTransliterator
还是 transliterate
——该包会检查可以使用的最简单的转写器类型。
由于无法确定流体表示法是否最终需要,因此不会直接构造 RuleLists
和 CompoundID
。例如:当添加单个ID,随后再次添加另一个单个ID时,我们应该将所有内容转换为复合ID。但在添加第一个单个ID时,我们无法得知这一点。这就是为什么 CompoundID 和 RuleList 是虚拟的内部类,在运行时作为包装器使用(因此标记为内部)。这一逻辑位于 TranliteratorBuilder 的 transliterate
、getTransliterator
和 containsRuleSyntax
方法中。