加入混合的从右到左和从左到右的语言字符串时的奇怪行为
Weird behaviour on joining mixed right-to-left and left-to-right language strings
输入:
临时文本数组:
▿ 3个元素
- 0 : "זה מבחן"
- 1 : "7 x 5 abc"
- 2 : "other text"
当做一个简单的 tempText = tempTextArray.joined(" | ")
时,结果并没有将所有元素放在我期望的位置...结果:
Printing description of tempText:
"זזה מבחן | 7 x 5 abc | other text"
这是我第一次将从右到左和从左到右的文本结合起来,有没有人处理过类似的情况?
我的应用正在接收来自后端的翻译,所以我不知道哪些元素被翻译成(在本例中)希伯来语,而我将以我的默认语言(英语)接收哪些元素
这是Unicode BIDI (Bidirectional Text) algorithm造成的。首先,我将解释如何修复它,因为它相当简单,然后我将解释发生了什么,以防您需要更多信息。
您需要在每个要将文本方向重置为 LTR 的位置添加 LTR(从左到右标记)字符。在您的情况下,它位于字符串的开头和每个 |
块的开头:
let ltr = "\u{200e}"
let tempText = ltr + tempTextArray.joined(separator: "\(ltr) | ")
// => זה מבחן | 7 x 5 abc | other text
如果你打算使用希伯来语,你绝对想阅读 Cal Henderson 对算法的精彩解释:Understanding Bidirectional (BIDI) Text in Unicode。
现在解释发生了什么。您正在打印一个字符串,其第一个字符是“זה מבחן”中的 ז
,最后一个字符是“文本”中的最后一个 t
。它不是用 |
分隔的三个字符串,它只是一个长字符串。当您显示该字符串时,BIDI 算法必须决定所有字符的位置。
第一个字符 (ז) 是一个 RTL 字符,因此它确定这是一个嵌入了一些 LTR 文本的 RTL 字符串。这与你想要的相反。您希望这是一个嵌入了一些 RTL 文本的 LTR 字符串。所以你需要从一个LTR字符开始,比如Left-To-Right Mark。
BIDI 算法的工作是告诉系统下一个字符应该朝哪个方向移动。 זה 中的每个字符都是 RTL,所以这很简单,继续向左走。但是 זה 和 מבחן 之间的 space 呢? Space 在方向上是中性的,最后一个字符是 RTL,所以 space 向左。但随后我们来到了 מבחן 和 | 之间的 space。 Space 是中立的 |是中性的,因此 BIDI 算法会将 space 和 |再次向左。你想要 space 和 |是LTR,所以你需要在那里添加另一个LTR字符。
7 也是中性的,但是 x
是 LTR 的拉丁文小写字母 X(不是中性的 MULTIPLICATION X)。
最终结果是 BIDI 算法确定这是一个以 7 | זה מבחן
开头的 RTL 字符串,然后(向左)跟随嵌入的 LTR 字符串 x 5 abc | other text
。 (换句话说,这是一个希伯来字符串,其中恰好有一些英语,而不是一个英文字符串,恰好有一些希伯来语。)
我希望你上面的问题中实际显示的不是你所看到的(因为 BIDI 算法是如何在 Stack Overflow 上应用的)。我希望它实际上看起来像这样:
如果你从右到左阅读这篇文章,现在应该更能理解正在发生的事情。
输入:
临时文本数组: ▿ 3个元素
- 0 : "זה מבחן"
- 1 : "7 x 5 abc"
- 2 : "other text"
当做一个简单的 tempText = tempTextArray.joined(" | ")
时,结果并没有将所有元素放在我期望的位置...结果:
Printing description of tempText:
"זזה מבחן | 7 x 5 abc | other text"
这是我第一次将从右到左和从左到右的文本结合起来,有没有人处理过类似的情况?
我的应用正在接收来自后端的翻译,所以我不知道哪些元素被翻译成(在本例中)希伯来语,而我将以我的默认语言(英语)接收哪些元素
这是Unicode BIDI (Bidirectional Text) algorithm造成的。首先,我将解释如何修复它,因为它相当简单,然后我将解释发生了什么,以防您需要更多信息。
您需要在每个要将文本方向重置为 LTR 的位置添加 LTR(从左到右标记)字符。在您的情况下,它位于字符串的开头和每个 |
块的开头:
let ltr = "\u{200e}"
let tempText = ltr + tempTextArray.joined(separator: "\(ltr) | ")
// => זה מבחן | 7 x 5 abc | other text
如果你打算使用希伯来语,你绝对想阅读 Cal Henderson 对算法的精彩解释:Understanding Bidirectional (BIDI) Text in Unicode。
现在解释发生了什么。您正在打印一个字符串,其第一个字符是“זה מבחן”中的 ז
,最后一个字符是“文本”中的最后一个 t
。它不是用 |
分隔的三个字符串,它只是一个长字符串。当您显示该字符串时,BIDI 算法必须决定所有字符的位置。
第一个字符 (ז) 是一个 RTL 字符,因此它确定这是一个嵌入了一些 LTR 文本的 RTL 字符串。这与你想要的相反。您希望这是一个嵌入了一些 RTL 文本的 LTR 字符串。所以你需要从一个LTR字符开始,比如Left-To-Right Mark。
BIDI 算法的工作是告诉系统下一个字符应该朝哪个方向移动。 זה 中的每个字符都是 RTL,所以这很简单,继续向左走。但是 זה 和 מבחן 之间的 space 呢? Space 在方向上是中性的,最后一个字符是 RTL,所以 space 向左。但随后我们来到了 מבחן 和 | 之间的 space。 Space 是中立的 |是中性的,因此 BIDI 算法会将 space 和 |再次向左。你想要 space 和 |是LTR,所以你需要在那里添加另一个LTR字符。
7 也是中性的,但是 x
是 LTR 的拉丁文小写字母 X(不是中性的 MULTIPLICATION X)。
最终结果是 BIDI 算法确定这是一个以 7 | זה מבחן
开头的 RTL 字符串,然后(向左)跟随嵌入的 LTR 字符串 x 5 abc | other text
。 (换句话说,这是一个希伯来字符串,其中恰好有一些英语,而不是一个英文字符串,恰好有一些希伯来语。)
我希望你上面的问题中实际显示的不是你所看到的(因为 BIDI 算法是如何在 Stack Overflow 上应用的)。我希望它实际上看起来像这样:
如果你从右到左阅读这篇文章,现在应该更能理解正在发生的事情。