Using locale settings to detect wheter to use imperial units
Asked Answered
C

6

35

I'm working on an app that wants to display lengths either in centimeters (cm) or in inches("). Is there a way to select the right unit from the locale? In any event I'm also going to put in an option so that the user can to override the locale setting.

USA, Liberia, and Burma should use imperial units and the rest of the world normal units. One way is to put in this logic in my own classes, but I would prefer using any built in logic if available. Any pointers?

Concepcionconcept answered 4/2, 2011 at 13:1 Comment(4)
"USA, Liberia, and Burma should use imperial units" Well, that's debatable.Rata
@Rata yeah :) so true. But while the world is like this...Concepcionconcept
FYI, Imperial units are not the same as United States units. For instance, an Imperial Gallon is 16% larger than a US Gallon, and an Imperial Pound is 21% larger than a US Pound.Cellulose
from API Level 28, the Android framework provides an official API for this. Read my response below.Moorwort
M
15

LocaleData.getMeasurementSystem is available from API level 28 and beyond. It returns the information you are looking for.

Moorwort answered 16/4, 2020 at 4:43 Comment(1)
That's awesome! 😊Concepcionconcept
C
54

In the end I went for the following solution.

public class UnitLocale {
    public static UnitLocale Imperial = new UnitLocale();
    public static UnitLocale Metric = new UnitLocale();

    public static UnitLocale getDefault() {
            return getFrom(Locale.getDefault());
    }
    public static UnitLocale getFrom(Locale locale) {
        String countryCode = locale.getCountry();
        if ("US".equals(countryCode)) return Imperial; // USA
        if ("LR".equals(countryCode)) return Imperial; // Liberia
        if ("MM".equals(countryCode)) return Imperial; // Myanmar
        return Metric;
    }
}

Use it like this for example.

if (UnitLocale.getDefault() == UnitLocale.Imperial) convertToimperial();

If convert methods are also need they can preferably be added to subclasses of UnitLocale. I only needed to detect wheter to use imperial units and send it to the server.

Using ints over java objects have extremely slim performance gains and makes the code harder to read. Comparing two references in java is comparable in speed to comparing two ints. Also using objects allow us to add methods to the UnitLocale class or subclasses such as, convertToMetric, etc.

You could also use an enum instead if you prefer that.

Concepcionconcept answered 22/10, 2011 at 16:9 Comment(6)
Nice solution@. I just changed it to use enum :) so I can use it in an utility class I already have for unit conversion.Churchwarden
@MarcinOrlowski thanks for upvote, but whats the point with the int?Concepcionconcept
Nice Answer! What is the basis for only those 3 countries to be in Imperial?Breckenridge
thanks! Hard to tell really, but in my view it mostly historical and political reasons for keeping the imperial system.Concepcionconcept
FYI, Imperial units are not the same as United States units. For instance, an Imperial Gallon is 16% larger than a US Gallon, and an Imperial Pound is 21% larger than a US Pound.Cellulose
from API Level 28, the Android framework provides an official API for this. Read my response below.Moorwort
M
15

LocaleData.getMeasurementSystem is available from API level 28 and beyond. It returns the information you are looking for.

Moorwort answered 16/4, 2020 at 4:43 Comment(1)
That's awesome! 😊Concepcionconcept
A
9

A more or less complete way to do this is this way.

Kotlin:

private fun Locale.toUnitSystem() =
    when (country.toUpperCase()) {
        // https://en.wikipedia.org/wiki/United_States_customary_units
        // https://en.wikipedia.org/wiki/Imperial_units
        "US" -> UnitSystem.IMPERIAL_US
        // UK, Myanmar, Liberia, 
        "GB", "MM", "LR" -> UnitSystem.IMPERIAL
        else -> UnitSystem.METRIC
    }

Note that there is a difference between UK and US imperial systems, see the wiki articles for more details.

Aculeate answered 6/11, 2018 at 15:9 Comment(2)
Where does the UnitSystem type come from?Earthquake
Also for rough comparison US customary vs. imperial see: en.wikipedia.org/wiki/…Exudate
B
9

Building on the other nice solutions here, you can also implement this as a Kotlin extension function to the Locale object:

fun Locale.isMetric(): Boolean {
    return when (country.toUpperCase(this)) {
        "US", "LR", "MM" -> false
        else -> true
    }
}

This way, all you need to do is call:

val metric = Locale.getDefault().isMetric()
Bigford answered 10/4, 2019 at 16:22 Comment(2)
Well, the United Kingdom is missing here.Virginity
At least according to iOS SDK, United Kingdom is using the metric system. I suppose it's the same with Android. Same result with google search.Dispirit
R
5
  1. Programmatically

Making a small improvement in the solution from @vidstige

I would use getCountry().toUpperCase() to be safe and change the checks to a switch for a cleaner code. Something like this:

public static UnitLocale getFrom(Locale locale) {
    String countryCode = locale.getCountry().toUpperCase();
    switch (countryCode) {
        case "US":
        case "LR":
        case "MM":
            return Imperial;
        default:
            return Metric;
    }
}
  1. From Resources

Another solution could be creating resources folders for each country like: [values_US] [values_LR] [values_MM] with a boolean resource changed to true. Then read that boolean resource from the code.

Rosariarosario answered 22/11, 2016 at 1:19 Comment(4)
When is the toUpperCase() needed? It is unused code and should be removed. How is a switch case cleaner? It's just more lines of code and messier to reda.Concepcionconcept
Hi, We are know discussing different coding styles. I believe a switch is actually more efficient than several, if else statement.Rosariarosario
About the toUpperCase(), I know it looks redundant, but Android devices manufacturers can modify the source code and you never know if the country code will be always upper case. This is a matter of taste.Rosariarosario
Yes, and it's very important to be able to motivate your coding styles imho. If you do toUpperCase() where do you stop? Manufaturers can also add prefixes and suffixes. Perhaps you should also trim whitespace?Concepcionconcept
S
2

Just give the user the option to choose a preferred unit in a settings menu. If it is a traveling user you don't want the app to be geographically aware, IMO.

Stapleton answered 4/2, 2011 at 13:8 Comment(6)
Good point. Having this as an option is always better than having it selected for me.Gamophyllous
Good point. I will put in a setting anyway. However the locale is dependent of the user and not the location. That is, a user from USA would always want to see inches regardless of where she is traveling. Therefor a nice default-selection would be convenient.Concepcionconcept
ok I see.. Maybe you could use the ACTION_GET_LANGUAGE_DETAILS from RecognizerIntent and from there draw conclusions on what unit the user would prefer from what language they have set as default... This however will not work for users having English as default language and and still prefer the metric system... Good luck.Stapleton
Travelling has nothing to do with the locale. An American can choose a LR or MM locale without travelling.Breckenridge
In my case, I do have a settings to set the units, but I want to use this auto-detection to set the default values.Shooin
@Corbella's way is the right way to approach this- Then most users can just pick up and use the app without changing anything, but the ability is there if they need it.Gallagher

© 2022 - 2024 — McMap. All rights reserved.