有没有办法在 UITextView/UILabel 上使用与“设置”中不同的文本语言进行正确的断字?
Is there a way to make correct hyphenation on UITextView/UILabel with text language different than in Settings?
我的问题是我的应用程序中有不同语言的文本块,在某些语言(嗨德语)中,单词可能太长,我正在使用 hyphenationFactor 来进行适当的布局...但是正如文档:"This property detects the user-selected language by examining the first item in preferredLanguages." 在我的例子中,设置中可能有英语,屏幕上有德语文本,因此连字符的语法规则是错误的。
有没有办法改变这个 属性 的语言或者可能有一些破解方法?提前致谢!
2022 年更新:
在 iOS15 之前,Apple 严格遵循软连字符,而在 iOS15 中,他们现在只将这些视为连字机会。
所以基本上现在我们应该使用 languageIdentifier 属性,减少我们自己的自定义连字规则的机会(比如删除丑陋的、一侧短的、连字中断),但在语法上是正确的。
旧答案:
最终,Frank Rausch 的扩展提供了真正的帮助
https://gist.github.com/frankrausch/0d2c91fad5e417a84aaa43bfb9c9aec8
添加了一些内容来检测字符串的主要语言并限制应按字符数连字的单词。
import Foundation
import NaturalLanguage
extension String {
func detectedLanguage(for string: String) -> String? {
let recognizer = NLLanguageRecognizer()
recognizer.processString(string)
guard let languageCode = recognizer.dominantLanguage?.rawValue else { return nil }
let detectedLanguage = Locale.current.localizedString(forIdentifier: languageCode)
return detectedLanguage
}
func autoHyphenated() -> String {
return self.hyphenated(languageCode: detectedLanguage(for: self) ?? "")
}
func hyphenated(languageCode: String) -> String {
let locale = Locale(identifier: languageCode)
return self.hyphenated(locale: locale)
}
func hyphenated(locale: Locale, wordMinimumLenght: Int = 13) -> String {
guard CFStringIsHyphenationAvailableForLocale(locale as CFLocale) else { return self }
var s = self
var words = s.components(separatedBy: " ")
for index in 0..<words.count {
if words[index].count > wordMinimumLenght && !words[index].contains("-") {
let fullRange = CFRangeMake(0, words[index].utf16.count)
var hyphenationLocations = [CFIndex]()
for (i, _) in words[index].utf16.enumerated() {
let location: CFIndex = CFStringGetHyphenationLocationBeforeIndex(words[index] as CFString, i, fullRange, 0, locale as CFLocale, nil)
if hyphenationLocations.last != location {
hyphenationLocations.append(location)
}
}
for l in hyphenationLocations.reversed() {
guard l > 0 else { continue }
let strIndex = String.Index(utf16Offset: l, in: words[index])
words[index].insert("\u{00AD}", at: strIndex)
}
}
}
s = words.joined(separator: " ")
return s
}
}
我的问题是我的应用程序中有不同语言的文本块,在某些语言(嗨德语)中,单词可能太长,我正在使用 hyphenationFactor 来进行适当的布局...但是正如文档:"This property detects the user-selected language by examining the first item in preferredLanguages." 在我的例子中,设置中可能有英语,屏幕上有德语文本,因此连字符的语法规则是错误的。
有没有办法改变这个 属性 的语言或者可能有一些破解方法?提前致谢!
2022 年更新:
在 iOS15 之前,Apple 严格遵循软连字符,而在 iOS15 中,他们现在只将这些视为连字机会。
所以基本上现在我们应该使用 languageIdentifier 属性,减少我们自己的自定义连字规则的机会(比如删除丑陋的、一侧短的、连字中断),但在语法上是正确的。
旧答案:
最终,Frank Rausch 的扩展提供了真正的帮助 https://gist.github.com/frankrausch/0d2c91fad5e417a84aaa43bfb9c9aec8
添加了一些内容来检测字符串的主要语言并限制应按字符数连字的单词。
import Foundation
import NaturalLanguage
extension String {
func detectedLanguage(for string: String) -> String? {
let recognizer = NLLanguageRecognizer()
recognizer.processString(string)
guard let languageCode = recognizer.dominantLanguage?.rawValue else { return nil }
let detectedLanguage = Locale.current.localizedString(forIdentifier: languageCode)
return detectedLanguage
}
func autoHyphenated() -> String {
return self.hyphenated(languageCode: detectedLanguage(for: self) ?? "")
}
func hyphenated(languageCode: String) -> String {
let locale = Locale(identifier: languageCode)
return self.hyphenated(locale: locale)
}
func hyphenated(locale: Locale, wordMinimumLenght: Int = 13) -> String {
guard CFStringIsHyphenationAvailableForLocale(locale as CFLocale) else { return self }
var s = self
var words = s.components(separatedBy: " ")
for index in 0..<words.count {
if words[index].count > wordMinimumLenght && !words[index].contains("-") {
let fullRange = CFRangeMake(0, words[index].utf16.count)
var hyphenationLocations = [CFIndex]()
for (i, _) in words[index].utf16.enumerated() {
let location: CFIndex = CFStringGetHyphenationLocationBeforeIndex(words[index] as CFString, i, fullRange, 0, locale as CFLocale, nil)
if hyphenationLocations.last != location {
hyphenationLocations.append(location)
}
}
for l in hyphenationLocations.reversed() {
guard l > 0 else { continue }
let strIndex = String.Index(utf16Offset: l, in: words[index])
words[index].insert("\u{00AD}", at: strIndex)
}
}
}
s = words.joined(separator: " ")
return s
}
}