Возможно ли во время выполнения знать, какие языки ресурсов встроены в мое приложение?
Присутствие этих папок:
values-en
values-de
values-fr
...
Возможно ли во время выполнения знать, какие языки ресурсов встроены в мое приложение?
Присутствие этих папок:
values-en
values-de
values-fr
...
AssetManager.getLocales() на самом деле способ сделать это. Однако из публичного API каждый созданный AssetManager также имеет ресурсы фреймворка, включенные в его путь поиска... поэтому, когда вы вызываете AssetManager.getLocales(), вы также увидите любые локали, которые являются частью ресурсов фреймворка. Невозможно обойти это с помощью SDK, извините.
Это сложно, потому что даже если у вас есть папка с именем values-de
, это не значит, что у вас есть какие-то ресурсы. Если у вас string.xml
в values-de
, это не значит, что у вас есть строковое значение.
значения:
<resources>
<string name="app_name">LocTest</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
</resources>
значения-де:
<resources>
<string name="hello_world">Hallo Welt!</string>
</resources>
Вы можете проверить, отличается ли ресурс для определенной локали по умолчанию:
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]);
}
}
У него есть одна ошибка - вы можете проверить одно значение, имеет ли он перевод.
Грязный код выше напечатает что-то вроде:
LOCALE (5667): 51: ru_NZ LOCALE (5667): 52: uk_UA LOCALE (5667): 53: nl_BE LOCALE (5667): 54: de_DE РАЗНОЕ ЛОКАЛЬНОЕ (5667): 54: Hallo Welt! Привет мир! de_DE LOCALE (5667): 55: ka_GE LOCALE (5667): 56: sv_SE LOCALE (5667): 57: bg_BG LOCALE (5667): 58: de_CH РАЗНОЕ LOCALE (5667): 58: Hallo Welt! Привет мир! de_CH LOCALE (5667): 59: fr_CH LOCALE (5667): 60: fi_FI
Вдохновленный решением Мендхака, я создал нечто более чистым:
defaultConfig {
....
def locales = ["en", "it", "pl", "fr", "es", "de", "ru"]
buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{\""+locales.join("\",\"")+"\"}"
resConfigs locales
}
Затем в Java используйте:
BuildConfig.TRANSLATION_ARRAY
Адвокаты этого метода:
Для тех, кто использует Gradle, я сделал это так, что ниже он пересекает все strings.xml
, захватывает имена каталогов и вычисляет локали оттуда. Он добавляет String[]
в BuildConfig
, к которому вы можете получить доступ: 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
Итак, после выполнения вышеуказанной задачи (в prebuild) BuildConfig.TRANSLATION_ARRAY
имеет свой список локалей.
Я не эксперт Gradle/Groovy, поэтому это определенно может быть немного опрятным.
Рассуждение - я столкнулся с множеством проблем, реализующих решение pawelzieba, у меня не было надежных строк для сравнения, поскольку переводы были краудсорсированы. Самый простой способ - это посмотреть на доступные папки "ценности-бла".
Вдохновленный ответами выше, я создал простой способ получить все языки приложений на основе предоставленных переводов:
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;
}
где в качестве id
arg следует использовать R.string.some_message
, который предоставляется во всех переводах и содержит четко различимый текст, например "Do you really want to delete the object?"
Может быть, это поможет кому-то...
эти res-lang фактически зависят от Locales, поэтому вам нужно получить локаль с устройства, и вы можете получить, какой язык будет отображаться в локали.
Locale myPhoneLocale = Locale.getDefault();
Затем вы можете вызвать getDisplayLanguage(), чтобы узнать, какой язык отображается.
Ссылка: Locale
Вы об этом говорите?
String language = Locale.getDefault().getDisplayLanguage();
Попробуйте позвонить AssetManager.getLocales()
:
Получить локали, для которых этот менеджер активов содержит данные.
Или вы можете попробовать, если вы можете получить список, используя list()
.
Возвращает массив String всех активов по заданному пути.
Вы имеете в виду:
String[] locales = getAssets().getLocales();
Это позволит вам получить язык, который имеет ваше устройство.
Вдохновленный кодом @Injecteer Я сделал следующее:
для списка языков, поддерживаемых приложением, необходимо передать язык по умолчанию, так как невозможно обнаружить
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;
}
Результатом является список map<key,value>
языков, поддерживаемых приложением, первое, что нужно, если вы хотите использовать для заполнения listPreference
LocaleList
или LocaleListCompat
- один из способов получения языков, поддерживаемых вашим приложением.
LocaleList был представлен в API 24.
При использовании LocaleListCompat следует учитывать, что для API <24 будет использоваться только первый языковой тег.