How to add locale(with country) to font resources

2019-04-23 11:41发布

问题:

Google published the official font resources handling since support library v26 released: https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html

However, looks like I cannot add country qualifier for the font resources folder.

I have an app which has 3 languages: English, Traditional Chinese and Simplified Chinese.

And I tried to name my font folder under res this way:
font
font-zh
font-zh_CN
(I have also tried font-zh-CN)

(Note that zh is the language, while CN is the country. With some more context for your information - In China we all use "Chinese", but there are Simplified Chinese and Traditional Chinese; Moreover, Hong Kong and Taiwan both use Traditional Chinese but the way we use them are a bit different. Most importantly, typefaces have separate files for Traditional Chinese and Simplified Chinese, due to the fact that they are quite different in their shapes.)

But I cannot build the project. Android Studio keeps telling me that font-zh_CN (or font-zh-CN) is an invalid resource directory name.

This does not happen for my values-zh_CN folder (nor values-zh-CN).

Question:

How do you use different fonts for the same language but different country, using the new official method since API 26?

回答1:

The pattern for resource folder names is defined in Table 2 here: https://developer.android.com/guide/topics/resources/providing-resources.html#Alternative%20Resources

Specifically, try:

font-zh-rCN



回答2:

As @SeanBarbeau pointed out, the correct pattern should be {language}-r{country}.

I definitely have tried this 4 months ago before I asked this question, but at that time, I believe there are other factors which make it fail.

And therefore I would like to add some more info here, in the hope of helping more people who are in trouble.

How do I change language (and corresponding font) in-app

Short answer is by wrapping the context in onAttachBaseContext of a BaseActivity.
Specifically I followed the guide here.

But in order for everything to work, you need to make sure 2 things:

(1) Your font resources are located in a correctly qualified folder;

Which is answered already in this thread, and;

(2) You wrapped the context in a correct locale

I believe this was what I missed 4 months ago.

At that time I didn't know much about the format of a locale.
I think I kept using something like new Locale("zh-CN") or new Locale("zh_CN") or new Locale("zh-rCN"). All of these are wrong!

The best way to specify a locale is to use the built-in constants, e.g. Locale.SIMPLIFIED_CHINESE.

If you look into this constant, it is calling the method createConstant("zh", "CN").
While createConstant() is a private method, I think it should be the same as new Locale("zh", "CN"); yet I still suggest using the constants.

So, at the end, your ContextWrapper should look like this:

public static Context wrap(Context context, Locale locale) {
    Configuration config = context.getResources().getConfiguration();
    if (locale == null) locale = Locale.ENGLISH; //Your default locale
    Locale.setDefault(locale);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        config.setLocale(locale);
    } else {
        config.locale = locale;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return context.createConfigurationContext(config);
    } else {
        context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
        return context;
    }
}

The point here is to directly pass a Locale object to the wrapper, which is different from most examples out there which used a String.
While the conversion of preference (probably just a String) to Locale object is up to your implementation.