【问题标题】:Making part of localised string bold swift使本地化字符串的一部分变为粗体 swift
【发布时间】:2022-02-22 17:35:11
【问题描述】:

我有一个字符串,假设“我的名字是 %@,我在 %@ 班学习”现在我想将要插入的占位符文本加粗,这样结果看起来像这样:“我的名字是苛刻,我在10班学习”,我会将其显示在标签上

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

【问题讨论】:

  • 你能分享你的代码和你的本地化字符串吗?
  • 你试过降价吗?就像在 %@ 前后添加双 * 一样?
  • @HunterLion 我正在尝试,但它不适合我,
  • 使用标记来分隔粗体部分会更容易,无论是 Markdown、HTML 还是自定义:[b]/[/b]、<b>/</b>,一旦你替换占位符值,搜索这些标签并在需要时呈现它们(粗体、斜体等),或者使用已经内置的 HTML 解析、Markdown 等。

标签: ios swift nsattributedstring nslocalizedstring


【解决方案1】:
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、Markdown 等的 NSAttributedString 的 init,或者简单地使用 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

输出:

【讨论】:

  • 您能否提供您全部需求的样本,哪些解决方案可能无法解决?
  • 你能澄清一下吗?因为我不介意%2$@ 或任何其他位置,因为标签位于带有格式的字符串上,并且渲染是在读取标签之后完成的,而不是位置......您可以通过替换@987654330 来测试我的代码@ 与 %2$@ 和 %1$@,并将它们反转,它应该仍然可以工作。
  • 很好,我可以看到如何使用反向。
  • html 不会随着设备中字体大小的增加而缩放,如何为其添加不同的属性?如可缩放字体等
  • 如果我明白了,你需要UILabel 中的adjustsFontSizeToFitWidth 和minimumScaleFactor,对吧?我认为NSAttributedString 不太喜欢他们。还是您的初始大小有问题?
【解决方案2】:
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

【讨论】:

    【解决方案3】:

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

    步骤和上面贴的意思很接近

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

    这是我用一些cmets来解释的代码:

    // 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
    }
    

    最终结果:

    本地化字符串的粗体部分参数化字符串粗体 Swift NSAttributedString iOS

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

    【讨论】:

    • 应该有一个简单的解决方案:将带有占位符的本地化字符串渲染成NSAttributedString,将加粗效果添加到%@,枚举加粗属性,并替换找到的文本值。这将避免跟踪索引。另外,如果你想摆脱offset的计算,一个小技巧就是倒着做,用reversed()
    • @Larme - 我愿意改进/优化/简化我的解决方案。但是,我没有完全理解您的建议。 add the bold effects to %@ - 如果没有 REGEX 来找出它们在哪里,我该怎么做? enumerate the bold attributes, and replace the text values found - 如何使用 developer.apple.com/documentation/foundation/nsattributedstring/… ? a little trick is to do it backwards, with reversed() - 哪一部分?
    • 我贴了一个解决方案,我用reversed()来说明。您仍然需要正则表达式部分。
    • @meaning-matters - 你能给出一个不起作用的场景输入示例吗?
    • @ShawnFrank "my name is %2$@ and I study in class %1$@" 例如,说值中的第一个元素应该在%1$@,第二个元素是@ 987654338@。想象一下“blue car”,在英语中,形容词总是在名词之前,但并非所有语言都如此......
    猜你喜欢
    • 1970-01-01
    • 2021-06-26
    • 1970-01-01
    • 2015-10-17
    • 2016-07-28
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    • 2013-04-27
    相关资源
    最近更新 更多