Make apps for a global audience: our approach to empowering NSAttributedString
When working on an app that is released worldwide, one of the common topics is how to stylise the text based on the user language.
At the WWDC 2018, Apple Engineers gave an awesome talk about Creating Apps for a Global Audience during which they provided countless interesting suggestions about managing different languages in apps.
So, what’s the problem?
When we want to provide different styles to our text-based controls, we usually use the NSAttributedString. By doing so, we can easily provide different fonts or different text colours to parts of our text. You can find tons of articles on the internet about this.
Instead, what we want to achieve is the dynamic application of different styles to the UI, based on the language itself (to be precise, to the current text Unicode script), like:
As you can see from these images, the descriptions include Arabic, Japanese and Latin characters. When we want to apply different character-based fonts, we have two options:
- Buy a font which includes both (or several) languages and so different sets of possible characters (glyphs);
- Use Apple’s way to specify a font fallback through Core Text (cascadeList), which is not fully documented. If a glyph is not in your app font, there is always a way to show a glyph from another font;
However, both solutions have the same problem - they don’t work if the selected font includes different character sets — and neither solution is powerful enough: what Apple doesn’t provide is an easy way to apply all the styles we need (not only different fonts) on the UIKit controls and based on the characters populating the UI.
To understand the approach we implemented, we need to focus on how Unicode.Scalar works.
Q: How many languages are covered by Unicode?
A: It’s hard to say, because Unicode encodes scripts for languages, rather than languages per se. Many scripts (especially the Latin script) are used to write a large number of languages. The easiest answer is that Unicode covers all of the languages that can be written in the following scripts: Latin, Greek, Cyrillic, Armenian, Hebrew, Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar, Georgian, Hangul, Ethiopic, Cherokee, Canadian Aboriginal Syllabics, Khmer, Mongolian, Han (Japanese, Chinese, Korean ideographs), Hiragana, Katakana, and Yi.
So what the Unicode standard defines is the unique set of characters that we can use to write each language in the world.
Our approach to the text styling is to focus on the unicode scalars instead of the language itself. For instance, it would be nice to have a way to say:
I want to apply font size 10 and blue to Greek characters and font size 15 and green colour to Latin characters
We have a solution for that and that has been implemented in our CascadeKit library.
Let’s say we have the following text:
That’s a mix of Latin, Greek and Russian characters and let’s say we want to apply specific styles only to Greek and Russian scripts:
- Blue background colour, font Helvetica Neue, size 15 to Greek characters;
- Red background colour, white text colour, font Courier, size 18 to Russian characters;
Well this is easily achievable with the CascadeKit NSMutableAttributedString extension:
What this new extension does is:
- Request to apply specific styles only to Greek and Russian scripts (there are two scripts for both Greek and Russian);
- The addAttributes callback “emits” N identified ranges of characters that are populated with Greek or Russian characters;
- Inside the callback we apply our styles;
And that’s it. Regardless of my style in picking colours, the result is
Cool right ? :)
Please check out CascadeKit repo here and let us know what you think.
Any feedback is greatly appreciated!🙏🏼
kudos to Daniele Bogo for the massive contribution to this library🙏🏼