获取应用程序的资源语言

时间:2012-07-23 10:50:40

标签: android

是否有可能在运行时知道我的应用中嵌入了哪些资源语言?

即存在此文件夹:

values-en
values-de
values-fr
...

11 个答案:

答案 0 :(得分:13)

这很复杂,因为即使你有一个名为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>

值-DE:

<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:en_NZ LOCALE(5667):52:uk_UA LOCALE(5667):53:   nl_BE LOCALE(5667):54:de_DE DIFFERENT LOCALE(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

答案 1 :(得分:10)

AssetManager.getLocales()实际上就是这样做的方法。但是,从公共API开始,您创建的每个AssetManager都会在其搜索路径中包含框架资源...因此,当您调用AssetManager.getLocales()时,您还将看到属于框架资源的任何区域设置。抱歉,没有办法解决这个问题。

答案 2 :(得分:5)

受到Mendhak解决方案的启发,我创造了一些更清洁的东西:

    defaultConfig {
        ....

        def locales = ["en", "it", "pl", "fr", "es", "de", "ru"]

        buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{\""+locales.join("\",\"")+"\"}"
        resConfigs locales
    }

然后在Java中使用:

BuildConfig.TRANSLATION_ARRAY

推广此方法:

  • 更小的apk - resConfigs将从您不需要的图书馆(有些有数百个)中删除资源
  • 快速 - 无需解析资源配置

答案 3 :(得分:3)

对于任何使用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

因此,在上述任务发生后(预建),BuildConfig.TRANSLATION_ARRAY包含您的区域设置列表。

我不是Gradle / Groovy专家,所以这肯定会有点整洁。

推理 - 我遇到了太多实施pawelzieba解决方案的问题,我没有可靠的字符串来比较&#39;因为翻译是众包的。最简单的方法是实际查看可用的值 - 文件夹。

答案 4 :(得分:2)

受上述答案的启发,我创建了一种基于提供的翻译获取所有应用程序语言的简单方法:

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?"并包含明确可区分的文字,例如Imports System Imports System.Reflection Module M Sub Main() AddHandler C1.Bang, AddressOf SomeAction C1.Test() AddHandler C2.Boom, AddressOf SomeAction C2.Test() ' Check which GetRaiseMethod find something Dim bangRaiseAccessor As MethodInfo = GetType(C1).GetEvent("Bang").GetRaiseMethod(True) Dim boomRaiseAccessor As MethodInfo = GetType(C2).GetEvent("Boom").GetRaiseMethod(True) Console.WriteLine("M: GetRaiseMethod(True) for C1.Bang: {0}", If(bangRaiseAccessor, DirectCast("<none>", Object)) ) Console.WriteLine("M: GetRaiseMethod(True) for C2.Boom: {0}", If(boomRaiseAccessor, DirectCast("<none>", Object)) ) End Sub Private Sub SomeAction() Console.WriteLine("M: Inside 'SomeAction'.") End Sub End Module Class C1 ' auto-event; backing delegate type, backing field and accessor methods are compiler-generated Shared Event Bang() Private Shared Sub OnBang() Handles Me.Bang Console.WriteLine("C1: Inside 'OnBang'.") End Sub Shared Sub Test() Console.WriteLine("C1: Test firing event Bang.") RaiseEvent Bang() Console.WriteLine("C1: Test finished.") End Sub End Class Class C2 Shared Custom Event Boom As Action AddHandler(value As Action) Console.WriteLine("C2: Inside custom AddHandler accessor, 'value' contains '{0}'.", If(value = Nothing, "<none>", value.Method.ToString()) ) ' TODO: use parameter, for example aggregate "value" in field with delegate combination End AddHandler RemoveHandler(value As Action) Console.WriteLine("C2: Inside custom RemoveHandler accessor, 'value' contains '{0}'.", If(value = Nothing, "<none>", value.Method.ToString()) ) ' TODO: use parameter, for example subtract "value" from field with delegate removal End RemoveHandler RaiseEvent() Console.WriteLine("C2: Inside custom RaiseEvent accessor.") End RaiseEvent End Event Private Shared Sub OnBoom() Handles Me.Boom Console.WriteLine("C2: Inside 'OnBoom'.") End Sub Shared Sub Test() Console.WriteLine("C2: Test firing event Boom.") RaiseEvent Boom() Console.WriteLine("C2: Test finished.") End Sub End Class

也许这对某人有帮助......

答案 5 :(得分:1)

这些res-lang实际上依赖于Locales,因此您需要从设备获取区域设置,并且您可以从区域设置中获取显示的语言..

Locale myPhoneLocale = Locale.getDefault();

然后,您可以调用getDisplayLanguage()来了解要显示的语言。

参考:Locale

答案 6 :(得分:0)

你在说这个吗?

String language = Locale.getDefault().getDisplayLanguage();

答案 7 :(得分:0)

尝试拨打AssetManager.getLocales()

  

获取此资产管理器包含数据的区域设置。

或者,您可以尝试使用list()获取列表。

  

返回给定路径上所有资产的String数组。

答案 8 :(得分:0)

你的意思是:

String[] locales = getAssets().getLocales();  

这样可以让您获得设备所具有的语言。

答案 9 :(得分:0)

受@ 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,那么第一件事就是

答案 10 :(得分:0)

LocaleListLocaleListCompat是获得应用程序支持的语言的一种方法。

LocaleList在API 24中引入。

使用LocaleListCompat时要考虑的事情是对于API <24,仅会使用第一个语言标记。