You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中英译法/德时UILabel超链接未高亮问题排查

多语言环境下UILabel超链接高亮异常问题

问题描述

我实现了一个带超链接的UILabel,相关代码如下。APP支持多语言切换,西班牙语和中文环境下翻译后的超链接可正常工作,但法语和德语环境下无法正常高亮,请问这两种语言需要特殊处理吗?

调用代码

let termsText = NSLocalizedString("To continue using this feature, please accept the Terms & Conditions", bundle: Bundle.terms, comment: "Continue")
let linkText = NSLocalizedString("Terms & Conditions", bundle: Bundle.terms, comment: "Terms")
termsLabel.setAttributedTextWithLinks(stringForAttributedText: termsText,
                                      linksText: [linkText], boldFont: false,
                                      underlined: false,
                                      fontSize: 14,
                                      textColor: UIColor(named: "Charcoal",
                                                         in: Bundle(for: ViewController.self),
                                                         compatibleWith: nil) ?? .black,
                                      linkColor: UIColor(named: "Blue",
                                                         in: Bundle(for: ViewController.self),
                                                         compatibleWith: nil) ?? .blue,
                                      paragraphStyle: nil) { [weak self] _ in
    self?.navigateToTermsAndConditionsVC()
}

富文本生成代码

func setAttributedTextWithLinks(stringForAttributedText: String, linksText: [String], boldFont: Bool = false, underlined: Bool = false, fontSize: CGFloat = 14.0, textColor: UIColor = UIColor(named: "Carbon", in: Bundle(for: UILabelWithLink.self), compatibleWith: nil) ?? .black, linkColor: UIColor = UIColor(named: "Blue", in: Bundle(for: AlertViewController.self), compatibleWith: nil) ?? .blue, paragraphStyle: NSParagraphStyle? = nil, linkTapped: @escaping (UInt) -> Void) {
    font = UIFont.systemFont(ofSize: fontSize, weight: boldFont ? .semibold : .regular)
    let textAttributes = [NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.font: font] as [NSAttributedString.Key: Any]
    var linkAttributes = [NSAttributedString.Key.foregroundColor: linkColor, NSAttributedString.Key.font: font] as [NSAttributedString.Key: Any]
    if underlined {
        linkAttributes[NSAttributedString.Key.underlineStyle] = 1
    }
    setAttributedTextWithLinks(stringForAttributedText: stringForAttributedText, textAttributes: textAttributes, isTextBold: boldFont, linksText: linksText, linkAttributes: linkAttributes, isLinkBold: boldFont, paragraphStyle: paragraphStyle, linkTapped: linkTapped)
}

核心实现代码

func setAttributedTextWithLinks(stringForAttributedText: String, textAttributes: [NSAttributedString.Key: Any], isTextBold: Bool, linksText: [String], linkAttributes: [NSAttributedString.Key: Any], isLinkBold: Bool, paragraphStyle: NSParagraphStyle?, linkTapped: @escaping (UInt) -> Void) {
    self.linkTapped = linkTapped
    cachedLinksText = linksText
    let nsstringForAttributedText = stringForAttributedText as NSString

    var textAttributesVar = textAttributes
    var linkAttributesVar = linkAttributes
    updateAttributesForAsianCharacters(attributes: &textAttributesVar, isBold: isTextBold)
    updateAttributesForAsianCharacters(attributes: &linkAttributesVar, isBold: isLinkBold)

    let mutableAttributedText = NSMutableAttributedString(string: stringForAttributedText)
    let nonLinkComponents = stringForAttributedText.components(separatedBy: linksText)
    for regularText in nonLinkComponents {
        let range = nsstringForAttributedText.range(of: regularText)
        mutableAttributedText.addAttributes(textAttributesVar, range: range)
    }

    for linkText in linksText {
        let linkRange = nsstringForAttributedText.range(of: linkText)
        mutableAttributedText.addAttributes(linkAttributesVar, range: linkRange)
    }
    if paragraphStyle != nil {
        mutableAttributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle!, range: nsstringForAttributedText.range(of: stringForAttributedText))
    }

    attributedText = mutableAttributedText
    configureLinkTextForTouch()
    configureLayoutManagerTextStorageTextContainer()
}

问题原因

核心问题出在字符串匹配逻辑上:

  • 法语、德语存在特殊字符(如德语变音字母ä/ö/ü、法语重音字符é/è/ê),默认的range(of:)匹配会严格区分这些字符变体
  • 法语中常用非断空格(U+00A0)替代普通空格,导致链接文本和主文本的空格不匹配
  • 部分翻译可能存在大小写、标点的细微差异,导致range(of:)无法定位到正确的链接范围,最终没应用链接样式

解决方案

方案1:使用本地化键拼接富文本(推荐)

避免直接匹配翻译后的文本,通过String(format:)用本地化键拼接完整文本,确保链接文本的位置绝对准确:

// 替换原调用代码中的文本获取逻辑
let baseTextKey = "continue_feature_accept_terms"
let linkTextKey = "terms_conditions"

// 本地化文件中对应键值示例:
// continue_feature_accept_terms = "To continue using this feature, please accept the %@";
// terms_conditions = "Terms & Conditions";

let linkText = NSLocalizedString(linkTextKey, bundle: Bundle.terms, comment: "Terms")
let fullTextFormat = NSLocalizedString(baseTextKey, bundle: Bundle.terms, comment: "Continue")
let fullText = String(format: fullTextFormat, linkText)

// 直接获取链接范围
let nsFullText = fullText as NSString
let linkRange = nsFullText.range(of: linkText)

// 构建富文本(可集成到原setAttributedTextWithLinks方法中)
let mutableAttributedText = NSMutableAttributedString(string: fullText)
// 先设置全局普通文本样式
mutableAttributedText.addAttributes(textAttributesVar, range: NSRange(location: 0, length: nsFullText.length))
// 覆盖链接文本样式
if linkRange.location != NSNotFound {
    mutableAttributedText.addAttributes(linkAttributesVar, range: linkRange)
}

方案2:优化字符串匹配规则

如果要保留原有匹配逻辑,修改range(of:)的匹配选项,支持变音字符和大小写不敏感,同时处理非断空格:

// 替换原核心代码中的linkRange获取逻辑
for linkText in linksText {
    // 标准化空格:替换非断空格为普通空格
    let normalizedFullText = stringForAttributedText.replacingOccurrences(of: "\u{00A0}", with: " ")
    let normalizedLinkText = linkText.replacingOccurrences(of: "\u{00A0}", with: " ")
    let nsNormalizedFullText = normalizedFullText as NSString
    
    // 使用不区分大小写和变音符号的匹配选项
    let linkRange = nsNormalizedFullText.range(of: normalizedLinkText, options: [.caseInsensitive, .diacriticInsensitive])
    
    if linkRange.location != NSNotFound {
        mutableAttributedText.addAttributes(linkAttributesVar, range: linkRange)
    }
}

额外检查项

  • 核对法语、德语本地化文件中,链接文本的翻译和主文本中的对应部分完全一致(包括空格、标点、变音符号)
  • 检查updateAttributesForAsianCharacters函数是否会修改欧洲语言的字体属性,导致链接样式被覆盖

内容的提问来源于stack exchange,提问作者niagara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:30:50