Reputation: 2571
Is it possible, at runtime, to know which resources languages are embedded in my app?
i.e the presence of this folders:
values-en
values-de
values-fr
...
Upvotes: 31
Views: 11161
Reputation: 1029
Kotlin solution : (id is the resource id of a string that is used in all locales)
fun getAppLanguages(id: Int): Set<String> {
val dm = resources.displayMetrics
val conf: Configuration = resources.configuration
val originalLocale = conf.locales.get(0)
conf.setLocale(Locale.ENGLISH)
val reference = resources.getString(id)
val result: MutableSet<String> = HashSet()
result.add(Locale.ENGLISH.language)
for (loc in resources.assets.locales) {
if (loc.isEmpty()) continue
val locale: Locale = Locale.forLanguageTag(loc)
conf.setLocale(locale)
if (reference != Resources(resources.assets, dm, conf).getString(id)) {
result.add(locale.getLanguage())
}
}
conf.setLocale(originalLocale)
return result
}
Upvotes: 1
Reputation: 29
LocaleList
or LocaleListCompat
are one of the ways you could get the languages supported by your application.
LocaleList was introduced in API 24.
The thing to consider when using LocaleListCompat is that for API < 24 only the first language tag will be used.
Upvotes: 2
Reputation: 1255
Inspired by Mendhak's solution I created something a bit cleaner:
defaultConfig {
....
def locales = ["en", "it", "pl", "fr", "es", "de", "ru"]
buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{\""+locales.join("\",\"")+"\"}"
resConfigs locales
}
Then in Java use:
BuildConfig.TRANSLATION_ARRAY
Advatages of this method:
Upvotes: 10
Reputation: 2873
Inspired with the code by@Injecteer I have done the following:
for the list of languages that the app supports , it is necessary to pass the default language , since it is not possible to detect
public static Map<String,String> getAppLanguages(Context context, String appDefaultLang) {
Map<String, String> listAppLocales = new LinkedHashMap<>();
listAppLocales.put("default","Auto");
DisplayMetrics metrics = new DisplayMetrics();
Resources res = context.getResources();
Configuration conf = res.getConfiguration();
String[] listLocates = res.getAssets().getLocales();
for (String locate : listLocates) {
conf.locale = new Locale(locate);
Resources res1 = new Resources(context.getAssets(), metrics, conf);
String s1 = res1.getString(R.string.title_itinerary);
String value = ucfirst(conf.locale.getDisplayName());
conf.locale = new Locale("");
Resources res2 = new Resources(context.getAssets(), metrics, conf);
String s2 = res2.getString(R.string.title_itinerary);
if (!s1.equals(s2)) {
listAppLocales.put(locate, value);
} else if (locate.equals(appDefaultLang)) {
listAppLocales.put(locate, value);
}
}
return listAppLocales;
}
The result is a list map<key,value>
of languages supported by the application, the first thing is for if you want to use to populate a listPreference
Upvotes: 0
Reputation: 8785
For anyone using Gradle, I did it like so below it traverses all strings.xml
, grabs the directory names and figures out the locales from there. It adds a String[]
to BuildConfig
which you can access as BuildConfig.TRANSLATION_ARRAY
task buildTranslationArray << {
def foundLocales = new StringBuilder()
foundLocales.append("new String[]{")
fileTree("src/main/res").visit { FileVisitDetails details ->
if(details.file.path.endsWith("strings.xml")){
def languageCode = details.file.parent.tokenize('/').last().replaceAll('values-','').replaceAll('-r','-')
languageCode = (languageCode == "values") ? "en" : languageCode;
foundLocales.append("\"").append(languageCode).append("\"").append(",")
}
}
foundLocales.append("}")
//Don't forget to remove the trailing comma
def foundLocalesString = foundLocales.toString().replaceAll(',}','}')
android.defaultConfig.buildConfigField "String[]", "TRANSLATION_ARRAY", foundLocalesString
}
preBuild.dependsOn buildTranslationArray
So after the above task occurs (on prebuild) the BuildConfig.TRANSLATION_ARRAY
has your list of locales.
I'm not a Gradle/Groovy expert so this could definitely be a bit neater.
Reasoning - I ran into too many issues implementing pawelzieba's solution, I had no reliable strings to 'compare' as the translations were crowdsourced. The easiest way then was to actually look at the values-blah folders available.
Upvotes: 10
Reputation: 16082
It's complicated because even if you have a folder named values-de
it doesn't mean you have any resources there. If you have string.xml
in values-de
it doesn't mean you have string value there.
values:
<resources>
<string name="app_name">LocTest</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
</resources>
values-de:
<resources>
<string name="hello_world">Hallo Welt!</string>
</resources>
You can test if a resource for a specific locale is different than the default:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources r = getResources();
Configuration c = r.getConfiguration();
String[] loc = r.getAssets().getLocales();
for (int i = 0; i < loc.length; i++) {
Log.d("LOCALE", i + ": " + loc[i]);
c.locale = new Locale(loc[i]);
Resources res = new Resources(getAssets(), metrics, c);
String s1 = res.getString(R.string.hello_world);
c.locale = new Locale("");
Resources res2 = new Resources(getAssets(), metrics, c);
String s2 = res2.getString(R.string.hello_world);
if(!s1.equals(s2)){
Log.d("DIFFERENT LOCALE", i + ": "+ s1+" "+s2 +" "+ loc[i]);
}
}
It has one fault - you can check one value whether it has translation.
The dirty code above will print something like:
LOCALE(5667): 51: en_NZ LOCALE(5667): 52: uk_UA LOCALE(5667): 53: nl_BE LOCALE(5667): 54: de_DE DIFFERENT LOCALE(5667): 54: Hallo Welt! Hello world! de_DE LOCALE(5667): 55: ka_GE LOCALE(5667): 56: sv_SE LOCALE(5667): 57: bg_BG LOCALE(5667): 58: de_CH DIFFERENT LOCALE(5667): 58: Hallo Welt! Hello world! de_CH LOCALE(5667): 59: fr_CH LOCALE(5667): 60: fi_FI
Upvotes: 16
Reputation: 20699
Inspired by the answers above I created a simple method to get all app's languages based on provided translations:
public static Set<String> getAppLanguages( Context ctx, int id ) {
DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
Configuration conf = ctx.getResources().getConfiguration();
Locale originalLocale = conf.locale;
conf.locale = Locale.ENGLISH;
final String reference = new Resources( ctx.getAssets(), dm, conf ).getString( id );
Set<String> result = new HashSet<>();
result.add( Locale.ENGLISH.getLanguage() );
for( String loc : ctx.getAssets().getLocales() ){
if( loc.isEmpty() ) continue;
Locale l = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH ? new Locale( loc.substring( 0, 2 ) ) : Locale.forLanguageTag( loc );
conf.locale = l;
if( !reference.equals( new Resources( ctx.getAssets(), dm, conf ).getString( id ) ) ) result.add( l.getLanguage() );
}
conf.locale = originalLocale;
return result;
}
where as id
arg should be used a R.string.some_message
which is provided in all translations and contains clearly distinguishable text, like "Do you really want to delete the object?"
Maybe it would help someone...
Upvotes: 4
Reputation: 27549
these res-lang actually depends on Locales, so you need to get locale from device and you can get which language is getting shown from locale..
Locale myPhoneLocale = Locale.getDefault();
You can then call getDisplayLanguage() to know which language is getting shown.
Reference : Locale
Upvotes: 1
Reputation: 91331
AssetManager.getLocales() is actually the way to do this. However, from the public API every AssetManager you create also has the framework resources included in its search path... so when you call AssetManager.getLocales() you will also see any locales that are part of the framework resources. There is no way to get around this with the SDK, sorry.
Upvotes: 10
Reputation: 29
Do you mean:
String[] locales = getAssets().getLocales();
This would let you get the language that your device have.
Upvotes: -3
Reputation: 109237
Are you talking about this?
String language = Locale.getDefault().getDisplayLanguage();
Upvotes: 0