android - para - Obtén los recursos de la aplicación en los idiomas.
duolingo app (10)
¿Es posible, en tiempo de ejecución, saber qué recursos están incorporados en mi aplicación?
Es decir, la presencia de estas carpetas:
values-en
values-de
values-fr
...
¿Estás hablando de esto?
String language = Locale.getDefault().getDisplayLanguage();
AssetManager.getLocales () es realmente la manera de hacer esto. Sin embargo, desde la API pública, cada AssetManager que crea también tiene los recursos del marco incluidos en su ruta de búsqueda ... así que cuando llame a AssetManager.getLocales () también verá las configuraciones regionales que forman parte de los recursos del marco. No hay forma de evitar esto con el SDK, lo siento.
Es complicado porque incluso si tiene una carpeta con el nombre de values-de
, no significa que tenga recursos allí. Si tiene string.xml
en values-de
, no significa que tenga valor de cadena allí.
valores:
<resources>
<string name="app_name">LocTest</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
</resources>
valores-de:
<resources>
<string name="hello_world">Hallo Welt!</string>
</resources>
Puede probar si un recurso para una configuración regional específica es diferente al predeterminado:
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]);
}
}
Tiene un fallo: puede verificar un valor si tiene traducción.
El código sucio de arriba imprimirá algo como:
LOCALE (5667): 51: es_NZ LOCALE (5667): 52: uk_UA LOCALE (5667): 53: nl_BE LOCALE (5667): 54: de_DE DIFERENTE LOCAL (5667): 54: ¡Hola, Welt! ¡Hola Mundo! de_DE LOCALE (5667): 55: ka_GE LOCALE (5667): 56: sv_SE LOCALE (5667): 57: bg_BG LOCALE (5667): 58: de_CH DIFERENTE LOCAL (5667): 58: ¡Hola, Welt! ¡Hola Mundo! de_CH LOCALE (5667): 59: fr_CH LOCALE (5667): 60: fi_FI
Inspirado con el código de @ Injecteer he hecho lo siguiente:
para la lista de idiomas que admite la aplicación, es necesario pasar el idioma predeterminado, ya que no es posible detectar
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;
}
El resultado es un map<key,value>
lista map<key,value>
de los idiomas admitidos por la aplicación, lo primero es para si desea utilizar para rellenar una listaPreferencia
Inspirado por la solución de Mendhak, creé algo un poco más limpio:
defaultConfig {
....
def locales = ["en", "it", "pl", "fr", "es", "de", "ru"]
buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{/""+locales.join("/",/"")+"/"}"
resConfigs locales
}
Luego, en el uso de Java:
BuildConfig.TRANSLATION_ARRAY
Avances de este método:
- Una versión más pequeña: resConfigs recortará los recursos de las bibliotecas que no necesitas (algunos tienen cientos)
- Rápido - No es necesario analizar configuraciones de recursos
Inspirado por las respuestas anteriores, creé un método sencillo para obtener todos los idiomas de la aplicación según las traducciones proporcionadas:
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;
}
donde como id
arg se debe usar un R.string.some_message
que se proporciona en todas las traducciones y contiene un texto claramente distinguible, como "Do you really want to delete the object?"
Tal vez ayudaría a alguien ...
Intente llamar a AssetManager.getLocales()
:
Obtenga las configuraciones regionales para las que este administrador de activos contiene datos.
O puede intentarlo si puede obtener una lista usando list()
.
Devuelve una matriz de cadenas de todos los activos en la ruta dada.
Para cualquier persona que use Gradle, lo hice así que debajo atraviesa todas las strings.xml
, toma los nombres de los directorios y determina los lugares desde allí. Agrega una String[]
a BuildConfig
que puede acceder como 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
Entonces, después de que ocurra la tarea anterior (en la etapa previa al desarrollo), BuildConfig.TRANSLATION_ARRAY
tiene su lista de BuildConfig.TRANSLATION_ARRAY
locales.
No soy un experto en Gradle / Groovy, así que definitivamente podría ser un poco más ordenado.
Razonamiento: Me encontré con demasiados problemas al implementar la solución de pawelzieba, no tenía cadenas confiables para ''comparar'', ya que las traducciones eran de fuentes múltiples. La forma más sencilla era mirar las carpetas de valores-bla disponibles.
Quieres decir:
String[] locales = getAssets().getLocales();
Esto le permitirá obtener el idioma que tiene su dispositivo.
estos res-lang realmente dependen de las configuraciones regionales, por lo que necesita obtener la configuración regional del dispositivo y puede ver qué idioma se muestra desde la configuración regional.
Locale myPhoneLocale = Locale.getDefault();
Luego puede llamar a getDisplayLanguage () para saber qué idioma se muestra.
Referencia: Locale