将部分本地化字符串设为粗体 swift

Making part of localised string bold swift

我有一个字符串,让我们说“我的名字是 %@,我在 class %@ 学习”,现在我想将我将要插入的占位符文本加粗,以便结果看起来像像这样:“我的名字是 Harsh,我在 class 10 学习”,我会在标签上显示它

我已经尝试使用 NSAttributedString,但由于字符串将被本地化,我无法使用属性字符串的范围参数将其设为粗体。

我可以使用 NSRegularExpression 提供一个简单的解决方案,无需任何复杂/可怕的正则表达式。我相信还有比这更多的最佳解决方案。

这些步骤与上面发布的意思非常接近

  1. 将要注入的字符串 (Harsh, 13) 等存储在数组中
  2. 有一个包含占位符的本地化字符串
  3. 使用 REGEX 查找占位符的位置并将这些位置存储在 locations array
  4. 通过用字符串数组中的值替换占位符来更新本地化字符串
  5. 从更新的本地化字符串
  6. 创建NSMutableAttributedString
  7. 遍历字符串以注入数组并更新 locations array
  8. 定义的 NSMutableAttributedString 区域

这是我使用的代码和一些注释来解释:

// This is not needed, just part of my UI
// Only the inject part is relevant to you
@objc
private func didTapSubmitButton()
{
    if let inputText = textField.text
    {
        let input = inputText.components(separatedBy: ",")
        let text = "My name is %@ and I am %@ years old"
        inject(input, into: text)
    }
}

// The actual function
private func inject(_ strings: [String],
                    into text: String)
{
    let placeholderString = "%@"
    
    // Store all the positions of the %@ in the string
    var placeholderIndexes: [Int] = []
    
    // Locate the %@ in the original text
    do
    {
        let regex = try NSRegularExpression(pattern: placeholderString,
                                            options: .caseInsensitive)
        
        // Loop through all the %@ found and store their locations
        for match in regex.matches(in: text,
                                   options: NSRegularExpression.MatchingOptions(),
                                   range: NSRange(location: 0,
                                                  length: text.count))
            as [NSTextCheckingResult]
        {
            // Append your placeholder array with the location
            placeholderIndexes.append(match.range.location)
        }
    }
    catch
    {
        // handle errors
        print("error")
    }
    
    // Expand your string by inserting the parameters
    let updatedText = String(format: text, arguments: strings)
    
    // Configure an NSMutableAttributedString with the updated text
    let attributedText = NSMutableAttributedString(string: updatedText)
    
        // Keep track of an offset
    // Initially when you store the locations of the %@ in the text
    // My name is %@ and my age is %@ years old, the location is 11 and 27
    // But when you add Harsh, the next location should be increased by
    // the difference in length between the placeholder and the previous
    // string to get the right location of the second parameter
    var offset = 0
    
    // Loop through the strings you want to insert
    for (index, parameter) in strings.enumerated()
    {
        // Get the corresponding location of where it was inserted
        // Plus the offset as discussed above
        let locationOfString = placeholderIndexes[index] + offset
        
        // Get the length of the string
        let stringLength = parameter.count
        
        // Create a range
        let range = NSRange(location: locationOfString,
                            length: stringLength)
        
        // Set the bold font
        let boldFont
            = UIFont.boldSystemFont(ofSize: displayLabel.font.pointSize)
        
        // Set the attributes for the given range
        attributedText.addAttribute(NSAttributedString.Key.font,
                                    value: boldFont,
                                    range: range)
        
        // Update the offset as discussed above
        offset = stringLength - placeholderString.count
    }
    
    // Do what you want with the string
    displayLabel.attributedText = attributedText
}

最终结果:

本地化字符串的粗体部分参数化字符串粗体SwiftNSAttributedStringiOS

这应该足够灵活,可以处理字符串中存在的任意数量的占位符,您不需要跟踪不同的占位符。

let descriptionString = String(format: "localised_key".localized(), Harsh, 10)
let description = NSMutableAttributedString(string: descriptionString, attributes: [NSAttributedString.Key.font: UIFont(name: "NotoSans-Regular", size: 15.7)!, NSAttributedString.Key.foregroundColor: UIColor(rgb: 0x000b38), NSAttributedString.Key.kern: 0.5])
let rangeName = descriptionString.range(of: "Harsh")
let rangeClass = descriptionString.range(of: "10")
let nsrangeName = NSRange(rangeName!, in: descriptionString)
let nsrangeClass = NSRange(rangeClass!, in: descriptionString)
description.addAttributes([NSAttributedString.Key.font: UIFont(name: "NotoSans-Bold", size: 15.7)!, NSAttributedString.Key.foregroundColor: UIColor(rgb: 0x000b38), NSAttributedString.Key.kern: 0.5], range: nsrangeName)
description.addAttributes([NSAttributedString.Key.font: UIFont(name: "NotoSans-Bold", size: 15.7)!, NSAttributedString.Key.foregroundColor: UIColor(rgb: 0x000b38), NSAttributedString.Key.kern: 0.5], range: nsrangeClass)

如需更多参考,请使用 this

let withFormat = "my name is %@ and i study in class %@"

有多种方法可以做到这一点,但在我看来,最简单的方法之一是使用标签:

在占位符周围使用标签(以及其他部分,如果需要的话):

let withFormat = "my name is <b>%@</b> and i study in class <b>%@</b>"
let withFormat = "my name is [b]%@[/b] and i study in class [b]%@[/b]"
let withFormat = "my name is **%@** and i study in class **%@**"

标签可以是 HTML、Markdown、BBCode 或任何您喜欢的自定义,然后替换占位符值:

let localized = String(format: withFormat, value1, value2)

现在,根据你想怎么做,或者你使用了哪个标签,你可以使用 HTML 中 NSAttributedString 的初始化、Markdown 等,或者简单地使用 NSAttributedString(string: localized), 自己寻找标签并应用所需的渲染效果。

这是一个小例子:

let tv = UITextView(frame: CGRect(x: 0, y: 0, width: 300, height: 130))
tv.backgroundColor = .orange

let attributedString = NSMutableAttributedString()

let htmled = String(format: "my name is <b>%@</b> and i study in class <b>%@</b>", arguments: ["Alice", "Wonderlands"])
let markdowned = String(format: "my name is **%@** and i study in class **%@**", arguments: ["Alice", "Wonderlands"])
let bbcoded = String(format: "my name is [b]%@[/b] and i study in class [b]%@[/b]", arguments: ["Alice", "Wonderlands"])

let separator = NSAttributedString(string: "\n\n")
let html = try! NSAttributedString(data: Data(htmled.utf8), options: [.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil)
attributedString.append(html)
attributedString.append(separator)

let markdown = try! NSAttributedString(markdown: markdowned, baseURL: nil) //iO15+
attributedString.append(markdown)
attributedString.append(separator)

let bbcode = NSMutableAttributedString(string: bbcoded)
let regex = try! NSRegularExpression(pattern: "\[b\](.*?)\[\/b\]", options: [])
let matches = regex.matches(in: bbcode.string, options: [], range: NSRange(location: 0, length: bbcode.length))
let boldEffect: [NSAttributedString.Key: Any] = [.font: UIFont.boldSystemFont(ofSize: 12)]
//We use reversed() because if you replace the first one, you'll remove [b] and [/b], meaning that the other ranges will be affected, so the trick is to start from the end
matches.reversed().forEach { aMatch in
    let valueRange = aMatch.range(at: 1) //We use the regex group
    let replacement = NSAttributedString(string: bbcode.attributedSubstring(from: valueRange).string, attributes: boldEffect)
    bbcode.replaceCharacters(in: aMatch.range, with: replacement)
}
attributedString.append(bbcode)

tv.attributedText = attributedString

输出: