prinsfrank/transliteration

一个对原生 PHP 转换器进行类型包装的封装,提供类型化且有文档说明的参数,而不是任意字符串

v1.0.0 2024-02-09 20:58 UTC

This package is auto-updated.

Last update: 2024-09-18 19:18:36 UTC


README

转写

GitHub PHP Version Support codecov PHPStan Level

一个对原生 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(),
    ])

捆绑的转换集

此包包含许多转换集

创建自定义转换集

要创建自定义转换集,请简单地

  1. ConversionSet 接口 添加到您的类中;
use PrinsFrank\Transliteration\ConversionSet;

class CustomConversionSet implements ConversionSet
{
}
  1. 添加对 apply 方法的实现;
public function apply(TransliteratorBuilder $transliteratorBuilder): void
{
    // Add your code here
}
  1. 并将您的新自定义转换传递给 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')
    );
}

这里有一些额外的参数可用:beforeContextafterContextresultToRevisit。这些目前在此处未记录,但在 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 中。

技术:转换器组合

为了给此包的用户提供一个美好且可读的参数列表,根据规范实现了创建普通转换器的 formalIDSyntaxTransformRuleSyntax。它们之间的关系如下

当通过调用 applyConversionSetapplyConversionSetsaddSingleIDaddConversionAddVariableDefinition 构建 transliterator 时,内部数组 $conversions 将直接填充,或者通过转换集在 apply 方法中填充。

在随后创建转写器时——无论是通过调用 getTransliterator 还是 transliterate——该包会检查可以使用的最简单的转写器类型。

由于无法确定流体表示法是否最终需要,因此不会直接构造 RuleListsCompoundID。例如:当添加单个ID,随后再次添加另一个单个ID时,我们应该将所有内容转换为复合ID。但在添加第一个单个ID时,我们无法得知这一点。这就是为什么 CompoundIDRuleList 是虚拟的内部类,在运行时作为包装器使用(因此标记为内部)。这一逻辑位于 TranliteratorBuildertransliterategetTransliteratorcontainsRuleSyntax 方法中。