如何避免 Java 资源捆绑字符串中的重复?

2022-09-04 04:25:20

我们有很多包含相同子字符串的字符串,从有关检查日志或如何联系支持的句子,到包含公司或产品名称的类似品牌的字符串。重复给我们自己带来了一些问题(主要是拼写错误或复制/粘贴错误),但它也会导致问题,因为它增加了我们的翻译人员必须翻译的文本量。

我想出的解决方案是这样的:

public class ExpandingResourceBundleControl extends ResourceBundle.Control {
  public static final ResourceBundle.Control EXPANDING =
    new ExpandingResourceBundleControl();

  private ExpandingResourceBundleControl() { }

  @Override
  public ResourceBundle newBundle(String baseName, Locale locale, String format,
                                  ClassLoader loader, boolean reload)
    throws IllegalAccessException, InstantiationException, IOException {

      ResourceBundle inner = super.newBundle(baseName, locale, format, loader, reload);
      return inner == null ? null : new ExpandingResourceBundle(inner, loader);
  }
}

ExpandingResourceBundle委托到真正的资源包,但执行 {{this.kind.of.thing}} 的转换以查找资源中的键。

每次你想得到其中之一,你必须去:

ResourceBundle.getBundle("com/acme/app/Bundle", EXPANDING);

这很好 - 在一段时间内。

最终发生的事情是,一些新代码(在我们的例子中,是从Matisse吐出的自动生成的代码)查找相同的资源包,而无需指定自定义控件。如果您编写一个简单的单元测试,该单元测试使用和不使用调用它,这似乎是不可重现的,但是当应用程序实际运行时,就会发生这种情况。不知何故,内部的缓存会弹出良好的值,并将其替换为损坏的值。我还没有弄清楚为什么Sun的jar文件在没有调试信息的情况下编译,所以调试它是一件苦差事。ResourceBundle

我的问题:

  1. 有没有某种方法可以全局设置我可能不知道的默认 ResourceBundle.Control?这将相当优雅地解决所有问题。

  2. 有没有其他方法可以优雅地处理这种事情,也许根本不会篡改ResourceBundle类?


答案 1

我认为这是 ResourceBundles 设计功能的一个基本缺陷:引用其他键的键自动违反了 DRY(不要重复自己)原则。我解决这个问题的方式类似于你的方法:创建一个 ReflectiveResourceBundle 类,该类允许您使用 EL 表示法在消息中指定 Resource 键。

错误的方式:

my.name.first=Bob
my.name.last=Smith
my.name.full=Bob Smith

正确的方法:

my.name.first=Bob
my.name.last=Smith
my.name.full=${my.name.first} ${my.name.last}

我已将代码上传到 GitHub,以便您或其他任何人都可以下载它。此外,我还为使用 Stripes 框架 (http://www.stripesframework.org/) 的任何人添加了一些示例代码,以便快速启动和运行。

让它与标准的JSTL fmt taglibs一起使用的诀窍是设置一个拦截器,用我们自己的资源替换HttpServletRequest的资源。代码如下所示:

ResourceBundle bundle = MyStaticResourceHoldingTheBundle.getBundle();
Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale));

请查看上面链接中的 stripes.interceptor 包,了解更多详细信息。


答案 2

如果字符串重复被本地化为您知道某个字符串将重复,但仅在同一项目中,以便共享资源包不是设计噩梦,那么您可以考虑将字符串分解为多个键值部分。将重复的部分与不重复的部分分开,并重复使用重复的部分。例如,假设您有以下两个字符串需要显示:

  1. “红顶知更鸟是一种原产于澳大利亚的小型鹦鹉。
  2. “红顶知更鸟在非洲大陆大部分地区的干燥地区被发现。

资源包可能如下所示:

robin.name=The Red-capped Robin
robin.native=is a small passerine bird native to Australia.
robin.region=is found in dryer regions across much of the continent.

然后根据需要组合所需的部件bundle.getString("robin.name")+bundle.getString(robin.native).

不过,您需要注意的一件事是,诸如主语谓词顺序等语法规则在所有语言中可能都不相同。因此,在拆分句子时需要小心一点。