Home > Software engineering >  Hyperlink is not getting highlighted when translating from English to French/German in swift
Hyperlink is not getting highlighted when translating from English to French/German in swift

Time:11-08

I have a UILabel with hyperlink text. Here is the code:

 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()
        }

And this is the attributedString code:

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

This is the code for setAttributedTextWithLinks():

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()
    }

I have the Language change in the app. The problem is this translation works with hyperlink using the above code for Spanish and Chinese but its not working for French and German languages. Is there something I should do specifically to these two languages?

CodePudding user response:

The issue is on:

for linkText in linksText {
    let linkRange = nsstringForAttributedText.range(of: linkText)
    mutableAttributedText.addAttributes(linkAttributesVar, range: linkRange)
}

linkRange value when wrong is {9223372036854775807, 0}, which is NSNotFound (for the first one). You should check it.

In your French translation, you have "... modalités et conditions ...", and you search "Modalités et conditions", with an uppercase at start, so it's normal that it isn't found...

You are using range(of:options:) and omitting the options parameter, using the default value. Instead use .caseInsensitive for options. parameter.

Now, this way of doing has a flow, if you have multiple links with the same word, range(of:options:) will return only the first occurence. You need to loop.

I find this work quite complex. If you have the hand on the translation, I'd go instead with:

"UseThisFeature" = "To continue using this feature, please accept the <link>%@</link>"
"TermAndConditions" = "Terms & Conditions"

It'd be simpler to replace afterwards and apply the effects. Where <link> & </link> can be any "marker/tag" you want.

Finally, I'd recommend to use UITextView for handling tags. It's much easier. UILabel aren't made for that. See WWDC 2018: TextKit Best practices, slide 2. It's easy to male a UITextView looks like a UILabel for the final user.

  • Related