从音译器转换中排除特定字符

Exclude specific characters from Transliterator conversion

我正在尝试使用 PHP 进行音译,但我需要的是转换所有非拉丁字符,但保留意大利重音字符 (àèìòù)。

PHP音译器缺少文档和在线示例。 我读过 ICU docs 并且我知道有一条规则强制音译器将一个字符转换为我们指定的另一个字符 (à > b)。

代码(使用create函数)

$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
$transliterator = Transliterator::create("Any-Latin; Latin-ASCII");
echo $transliterator->transliterate($str);

将所有非拉丁字符转换为拉丁字符(带有所有重音字符)并给出结果

ASAaeiou Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

和代码(使用createFromRules函数)

$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
$transliterator = Transliterator::createFromRules("á>b");
echo $transliterator->transliterate($str);

强制正确地将 à 转换为 b,但是,很明显,没有前面代码进行的转换 Any-Latin; Latin-ASCII,给出结果

AŠAbèìòù Chén Hǎi ybo München Faißt Финиш 国内 - 镜像

所以我的目标是合并 Any-Latin; Latin-ASCII 转换和 à > à 规则(以及其他意大利重音元音),以便告诉 Transliterator 将所有非拉丁字符转换为拉丁字符,但是将意大利重音元音转换为它们自身,结果如下:

ASAàèìòù Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

有没有办法将 à>à 规则放入 create 函数的参数或在 createFromRules 函数的参数中添加 Any-Latin; Latin-ASCII 指令?

您所要做的就是删除 Latin-ASCII 规则。

$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
$transliterator = Transliterator::create("Any-Latin; Any-NFC");
echo $transliterator->transliterate($str);

输出:

AŠAàèìòù Chén Hǎi yáo München Faißt Finiš guó nèi - jìng xiàng

您可能还想借此机会对字符串应用规范化规则,以将重音字符组合或分解为一致的形式,具体取决于您打算如何处理它们。

$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
$none = Transliterator::create("Any-Latin");
$nfc = Transliterator::create("Any-Latin; Any-NFC");
$nfd = Transliterator::create("Any-Latin; Any-NFD");
var_dump(
    $none->transliterate($str),
    $nfc->transliterate($str),
    $nfd->transliterate($str)
);

输出:

string(78) "AŠAàèìòù Chén Hǎi yáo München Faißt Finiš guó nèi - jìng xiàng"
string(78) "AŠAàèìòù Chén Hǎi yáo München Faißt Finiš guó nèi - jìng xiàng"
string(93) "AŠAàèìòù Chén Hǎi yáo München Faißt Finiš guó nèi - jìng xiàng"

NFC 是“合成的”,因为所有具有单代码点表示的重音字符都是这样表示的。 NFD 被“分解”,所有重音字符都被拆分成它们的基本代码点和一个重音组合标记。在这两种情况下,单个基本字符上的多个组合标记将以一致的方式排列。

一些文件系统需要某种形式,例如:Mac 需要 NFD,而有些文件系统会简单地接受任何东西,例如:ext,创建具有混合组成的“重复”文件,这些文件很难处理。

您可以使用 preg_replace_callback 过滤除意大利重音字符以外的所有字符并对其应用音译。

给定输入和输出示例:

$transliterator = Transliterator::create("Any-Latin; Latin-ASCII");
$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
echo $transliterator->transliterate($str), "\n";
ASAaeiou Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

仅在与您指定要保留的字符范围(意大利重音字符 [àèìòù])不匹配的句段上应用音译时,它应该提供结果。

一种选择是使用 preg_replace_callback

需要回调才能应用音译:

$transliterate = static function (array $match) use ($transliterator) {
    return $transliterator->transliterate($match[0]);
};

并且它需要有一个模式来匹配除要保留的字符之外的所有内容。它需要正确定义并与 Unicode 兼容:

([^\xE0\xE8\xEC\xF2\xF9]+)ui


(...)                : delimiters: the regular expression is inside
u                    : modifier: u - Unicode mode (UTF-8 encoding in
                       PHP, PCRE_UTF8)
i                    : modifier: i - letters in the pattern match
                       both upper and lower case letters
                       (PCRE_CASELESS)

[^...]               : character class: not matching any of the
                       characters (`^`); negated character class
\xE0\xE8\xEC\xF2\xF9 : the italian accented characters àèìòù written
                       in a stable notation (you can easily copy and
                       paste it for example)

最后但同样重要的是,要操作的主题必须与要保留的字符兼容。由于在 Unicode 中可以有多种方式来写入相同的字符,因此输入 规范化 与 PCRE 模式兼容

echo preg_replace_callback(
    '([^\xE0\xE8\xEC\xF2\xF9]+)ui', 
    $transliterate, 
    Normalizer::normalize($str, Normalizer::NFC)
), "\n";

输出:

ASAàèìòù Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

Example across PHP versions.


附录:

  • \xE0\xE1\xE8\xE9\xEC\xED\xF2\xF3\xF9\xFA 意大利重音字符的小写列表(可与 i-modifier 一起使用)
  • \xC0\xC1\xC8\xC9\xCC\xCD\xD2\xD3\xD9\xDA\xE0\xE1\xE8\xE9\xEC\xED\xF2\xF3\xF9\xFA 意大利重音字符的小写和大写列表(可以在没有 i 修饰符的情况下使用)
  • PCRE 语法字符(摘录):
       \xhh       character with hex code hh
       \x{hhh..}  character with hex code hhh..
    
  • Link 到完整的 PCRE 语法:https://www.pcre.org/original/doc/html/pcresyntax.html

我在尝试抵御不需要的音译时使用的一种方法 - 它有点难看,但工作起来相当简单。将不想音译的字符替换成标签,音译后再替换:

<?php

$str = "AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像";
$str = str_replace(['à', 'è', 'ì', 'ò', 'ù'], ['@@a@@', '@@e@@', '@@i@@', '@@o@@', '@@u@@'], $str);
$transliterator = Transliterator::create("Any-Latin; Latin-ASCII");
$out = $transliterator->transliterate($str);
$out = str_replace(['@@a@@', '@@e@@', '@@i@@', '@@o@@', '@@u@@'], ['à', 'è', 'ì', 'ò', 'ù'], $out);
echo $out;

结果是:

ASAàèìòù Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

[编辑] 更简单:使用过滤器仅将更改应用于所选字符:

$str = 'AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像';

$rules = <<<'RULES'
:: [^ÀàÈèÌìÒòÙù];
:: Any-Latin ;
:: Latin-ASCII ;
RULES;

$tls = Transliterator::createFromRules($rules);

echo $tls->transliterate($str), PHP_EOL;
// ASAàèìòù Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

:: [^ÀàÈèÌìÒòÙù] 是排除所选重音字母的过滤器。


[旧答案](也有效)
在从 AnyLatin 的音译 之前,您可以使用规范化来保护要保留的重音字符 ]:

$str = 'AŠAàèìòù Chén Hǎi yáo München Faißt Финиш 国内 - 镜像';

$rules = <<<'RULES'
:: NFC ;
à > a ̀  ;
è > e ̀  ;
ì > i ̀  ;
ò > o ̀  ;
ù > u ̀  ;
:: Any-Latin   ;
:: [^ ̀ ]-ASCII ;
:: NFC ;
RULES;

$tls = Transliterator::createFromRules($rules);

echo $tls->transliterate($str), PHP_EOL;
// ASAàèìòù Chen Hai yao Munchen Faisst Finis guo nei - jing xiang

这样,您想要用重音符保护的重音字符是唯一的分解形式(使用组合字符)。那些来自 Any-Latin 音译的是组合形式(它们只使用一个代码点)。 然后,您可以使用一个排除组合重音符的集合,而不是 Latin-ASCII 中的 Latin