Managing Zawgyi/Unicode in your Android App
If you ever have developed app targeted only for Burmese users, you will be no stranger...
If you ever have developed app targeted only for Burmese users, you will be no stranger to the struggle of managing your UI and database to handle both Zawgyi and Unicode font accurately. As we’re making a shift towards Unicode standards, we definitely cannot ignore the older devices, when there are workarounds for it. Hence, I took the liberty of listing down some of the methods I have uncovered to handle both fonts in your app.
Detecting the font of the user’s device
The first step is figuring out how to find out which font the user has installed on their device. Only by knowing that, we can do further operations such as rendering the correct version of the font on the device’s screen.
The Freedom of Choice
The most obvious solution is to just give the user a choice to choose between the two fonts. This can be done either on the first startup of your app, or through a settings screen. Remember to store the chosen value in some form of persistent storage, as you will require to use this value for all sorts of things later. Here’s a sample code of SplashActivity to get the idea of showing a Dialog.
This method is simple and reasonable as it gives the user a flexible choice on what font they want to use. Even though it’s convenient and flexible, The downside to this approach is that the user have to select a font on the first setup. In some cases, we would want as less user’s action as possible, and onboard them fast into our app, or maybe we don’t want to show a splash screen every time the app loads, which the Google design guideline is against.
Auto-detect font
Another solution is to just automatically detect the font, on your Application onCreate method. The perk is that there’s a handy library called MDetect for this scenario. In my experience, it works on most of the devices, including those pesky Samsung devices that try to support both Zawgyi and Unicode font through some weird configurations.
Rendering Burmese texts
Now that we know what font the device possess, we can move on to rendering it on the screen. First, know that the texts can be of two types : static and variable. Static strings are those that you store in your strings.xml, or one that you keep as static constant value in some classes. Variable strings would be those that either comes from the backend API or your persistent storage, and are stored as a string field in a POJO class, whose contents changes depending on the object.
String resources
If you’re going to use string resources for static strings, you will be required to create a custom locale string for Zawgyi as there’s no locale standard for it, and you will require a splash screen which will do the task of overriding your app’s locale configuration on each launch.(You will also have to implement this function in your Settings screen if you have one). You will then create two sets of string resources, the first one being with your custom locale string to store Zawgyi strings, and the latter one, for Unicode string with already defined standard, named “mm”. The downside to this approach is that your app will have larger APK size since there’s an extra set of values for each line of string, and that you will require a splash screen for configuring your local on each app’s launch.
Of course, if you don’t want these downsides, you can store only one of the set of strings, and write a utility getString method, whose job is to provide a conversion process and provide a bridge between the two fonts. This comes at the cost of your CPU for a particular set of devices, i.e, on devices with Zawgyi font if you store the set of Unicode strings, or devices with Unicode font if you store the set of Zawgyi strings.
It’s a trade-off between two approaches, one have larger app size, and one have more runtime consumption. Personally, I prefer to store the strings as xml instead of doing conversion tasks, as the string resources, in my case, usually doesn’t take a lot of storage, and having a larger size is better than costing resources on runtime.
Dynamic Strings
Just like static string resources, we can choose between storing both sets of strings in our persistence storage, and storing only one set of font and do the conversion. I’d like to point out here that if we choose to store only one set of strings, we should always go with Unicode as it has better data integrity compared to Zawgyi. Either way, do not perform the conditional check of what string you should get or the conversion process on your UI thread, especially if you choose to go with conversion approach, as it can have significant impact on your rendering time. Instead, do this task on your background thread, and let the UI handle only string. My approach is to setup Mapper classes, and I would map from data POJO to UI POJO, using RxJava’s map function before emitting the result onto the UI.
Likewise, the backend can return both sets of strings or you can specify a font parameter, and return only one set of string. If you’re using Retrofit with Okhttp, then you can use an okhttp interceptor to pass the font parameter every time you call an endpoint. Next, you will need to decide whether you will store only one set of strings in your persistence storage, and do the conversion from one to another on configuration changes(or wipe the whole database), or you would do the conversion and store both sets of string in your database, even if the backend sends only one set of string.
To sum up,
- Decide whether you will give user choice, or automatically detect
- Decide whether you will store only one set of string and do the conversion every time (make sure it’s not on UI thread) or store both sets of string and parse with a conditional check
- Perform tasks such as searching, on only Unicode strings if possible
- Be sure to update Locale Settings if you have two sets of string resources.
I hope this clears the concerns of handling both fonts. Every approach has its yin and yang, resource consumption, larger app size, boilerplate codes… etc. Go with whatever works for you and your team. If I miss any other approaches, feel free to share in the comments.