如何迭代所有字符串,所有模块和所有语言?

时间:2017-03-15 10:02:52

标签: android android-resources

背景

我知道可以使用反射来迭代给定R java文件的所有字符串(基于here):

    for (Field field : R.string.class.getDeclaredFields()) {
        if (Modifier.isStatic(field.getModifiers()) && !Modifier.isPrivate(field.getModifiers()) && field.getType().equals(int.class)) {
            try {
                int stringResId = field.getInt(null);
                Log.d("AppLog", field.getName() + ":" + getResources().getString(stringResId));
            } catch (IllegalArgumentException | IllegalAccessException e) {
                Log.e("AppLog", e.toString());
            }
        }
    }

这将显示应用程序中使用的所有字符串,包括所有模块(它们已合并),使用当前区域设置。

问题

是否可以迭代特定模块的所有字符串(或除特定模块之外的所有字符串),并且还可以使用所有支持的语言迭代它们?

2 个答案:

答案 0 :(得分:1)

  

是否可以迭代特定模块的所有字符串(或除特定模块之外的所有字符串

没有。资源没有“模块”的概念。

正如您已经注意到,这些都合并为一个R。因此,您唯一的机会是使键具有唯一性(即前缀),然后跳过在步行期间没有前缀的所有键,或者您可以创建单独的元素来保存给定模块中的所有字符串键并稍后对其进行处理,这应该是比迭代更快,维护仍然很痛苦。或者你可以将两者结合起来,并在启动时根据前缀构建这样的过滤数组,如果你需要多次访问它,可以在以后处理它。

修改

  

例如,迭代英语和德语字符串?

首先,请注意,对于大多数条目,您将拥有相同的键(显而易见,但很容易忽略)。要迭代这两者,您需要获得正确的资源上下文:

Resources rsrc = getResources();
Configuration config = new Configuration(rsrc.getConfiguration());
config.locale = Locale.US;   //
Resources localeResources = new Resources(rsrc.getAssets(), rsrc.getDisplayMetrics(), config);

然后像往常一样阅读。然后在config中重复设置Locale.GERMAN

编辑2

  

还以所有支持的语言迭代它们

理论上,AssetManager功能getLocales()

  

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

但是当你意识到“包含数据”并不是你的意思时,事情会变得棘手。包括即提供希腊语翻译的外部库也足以让希腊语也返回。

因此,最简单的方法可能只是将支持的语言索引作为代码的一部分,直接在代码中或者作为资源中的数组,并且只要您想知道应用支持哪种语言,就可以依赖它。这应该 易于维护,你甚至可以创建一个小的gradle任务来让你这么做 在构建时生成。

答案 1 :(得分:1)

以下是将我的答案here与迭代语言环境的代码相结合的示例:

void foo() {
  getStringsForResource(R.strings.class, context);
  getStringsForResource(com.example.package1.R.strings.class, context);
  getStringsForResource(com.example.package2.R.strings.class, context);
  getStringsForResource(com.example.package3.R.strings.class, context);
}


void getStringsForResource(Class<?> R_strings, Context initialContext) {
  for (String locale : initialContext.getAssets().getLocales()) {
    Configuration config = new Configuration();
    config.setToDefaults();
    config.setLocale(Locale.forLanguageTag(locale));
    Context localeSpecificContext = initialContext.createConfigurationContext(config);

    getStringsForContext(R_strings, localeSpecificContext);
  }
}

void getStringsForContext(Class<?> R_strings, Context context) {
  for (Field field : R_strings.getDeclaredFields())
  {
    if (Modifier.isStatic(field.getModifiers()) && !Modifier.isPrivate(field.getModifiers()) && field.getType().equals(int.class))
    {
      try
      {
        int id = field.getInt(null);
        String resourceName = field.getName();
        String s = context.getString(id);
        // Do something with the string here
      } catch (IllegalArgumentException e)
      {
        // ignore
      } catch (IllegalAccessException e)
      {
        // ignore
      }
    }
  }

}

此代码根据资源中存在的区域设置获取区域设置列表(如果您在API 21之下,它将变得比它值得解析更麻烦,请参阅https://developer.android.com/reference/android/content/res/AssetManager.html#getLocales(),以及使用Locale.getAvailableLocales()可能会对问题的评论更好;但要小心你对此的期望,就好像你的资源中不存在某种语言一样,你会得到Android所确定的& #34;最接近的匹配&#34;,对于很多人来说可能是en-US,然后使用反射来获取字符串ID以获取所有字符串。资源类列表是手动完成的;使用类路径扫描也可以自动执行此部分,但在Android中这样做是脆弱的;如果你可以硬编码要扫描的资源类列表,那就更好了。