Search code examples
iosxcodelocalizationnsattributedstringnslocalizedstring

Translate attributed string


I have a UILabel with an attributed string. Here is a printscreen of it:

enter image description here

Now, I have to translate this attributed string to english and italian. I am searching for a way to do this. Can I build this attributed string in code part by part? I have only found a solution where the whole string is set and then the attributes are set by range. But when I translate the string, I don't know the range anymore, because the words are longer or smaller.


Solution

  • Another option is to create localized .rtf files from which to create NSAttributedStrings:

    NSAttributedString *attributedStr = [[NSAttributedString alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType} documentAttributes:nil error:nil];
    

    To use dynamic formatting (e.g. set colors, fonts specific to some app settings), I'm using some html-like formatting, with 1-char tags for which I afterwards apply the format from inside the app.

    // NSMutableAttributedString category method
    /**
     * Updates the attributes of xml elements (where the xml tags are formed of 1 single char) with the passed attributes from param `tagsAttributes`
     * Current version doesn't support recursive tags (tags in tags)
     * All tags of form '<char>' or '</char>' will be used as formatting (the resulting string should not be expected to have any tags of this form)
     * @param tagsAttributes - list of attribute dictionaries, where the key is the tag name */
    -(void)formatCharXMLTagsUsingAttributes:(NSDictionary *)tagsAttributes {
        int strippedLength = 0;
    
        NSString *str = [[self string] copy];
        NSScanner *scanner = [NSScanner scannerWithString:str];
        while (![scanner isAtEnd]) {
            NSString *tag = nil;
            do {
                [scanner scanUpToString:@"<" intoString:nil];
                [scanner scanString:@"<" intoString:nil];
                if (scanner.scanLocation + 2 < [str length] && [str characterAtIndex:scanner.scanLocation + 1] == '>') {
                    [scanner scanUpToString:@">" intoString:&tag];
                    [scanner scanString:@">" intoString:nil];
                }
            } while (!tag && ![scanner isAtEnd]);
    
            if ([scanner isAtEnd]) {
                break;
            }
    
            NSString *endTag = [NSString stringWithFormat:@"</%@>", tag];
            NSString *tmpString;
            [scanner scanUpToString:endTag intoString:&tmpString];
            [scanner scanString:endTag intoString:nil];
            NSRange range;
            strippedLength += 7; // start tag + end tag length
            range.location = scanner.scanLocation - [tmpString length] - strippedLength;
            range.length = [tmpString length] + 7;
            [self replaceCharactersInRange:range withString:tmpString];
            range.length -= 7;
            [self addAttributes:tagsAttributes[tag] range:range];
        }
    }
    

    The method could afterwards be used like this:

    NSDictionary* highlightAttributes = @{NSForegroundColorAttributeName: [UIColor blueColor],
                                     NSFontAttributeName: [UIFont boldSystemFontOfSize:16]};
    NSDictionary *xmlTagsAttributes = @{@"b": highlightAttributes};
    [attrStr formatCharXMLTagsUsingAttributes:xmlTagsAttributes];
    

    Where attrStr may be @"Press <b>Next</b> button to ...".