简体   繁体   English

如何避免Java ResourceBundle字符串中的重复?

[英]How do I avoid repetition in Java ResourceBundle strings?

We had a lot of strings which contained the same sub-string, from sentences about checking the log or how to contact support, to branding-like strings containing the company or product name. 我们有很多字符串包含相同的子字符串,从关于检查日志或如何联系支持的句子到包含公司或产品名称的类似品牌的字符串。 The repetition was causing a few issues for ourselves (primarily typos or copy/paste errors) but it also causes issues in that it increases the amount of text our translator has to translate. 重复导致了我们自己的一些问题(主要是拼写错误或复制/粘贴错误),但它也会导致问题,因为它增加了翻译者必须翻译的文本数量。

The solution I came up with went something like this: 我提出的解决方案是这样的:

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 delegates to the real resource bundle but performs conversion of {{this.kind.of.thing}} to look up the key in the resources. ExpandingResourceBundle委托给真正的资源包,但执行{{this.kind.of.thing}}的转换以查找资源中的密钥。

Every time you want to get one of these, you have to go: 每当你想要获得其中一个,你必须去:

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

And this works fine -- for a while. 这工作正常 - 一段时间。

What eventually happens is that some new code (in our case autogenerated code which was spat out of Matisse) looks up the same resource bundle without specifying the custom control. 最终发生的是一些新代码(在我们的例子中是自动生成的代码,它是由Matisse吐出的)在没有指定自定义控件的情况下查找相同的资源包。 This appears to be non-reproducible if you write a simple unit test which calls it with and then without, but it occurs when the application is run for real. 如果你编写一个简单的单元测试来调用它然后没有,这似乎是不可重现的,但它是在应用程序运行真实时发生的。 Somehow the cache inside ResourceBundle ejects the good value and replaces it with the broken one. 不知何故, ResourceBundle的缓存弹出了良好的值,并用破坏的值替换它。 I am yet to figure out why and Sun's jar files were compiled without debug info so debugging it is a chore. 我还没弄清楚为什么Sun的jar文件是在没有调试信息的情况下编译的,所以调试它是一件苦差事。

My questions: 我的问题:

  1. Is there some way of globally setting the default ResourceBundle.Control that I might not be aware of? 有没有办法全局设置我可能不知道的默认ResourceBundle.Control? That would solve everything rather elegantly. 这样可以相当优雅地解决所有问题。

  2. Is there some other way of handling this kind of thing elegantly, perhaps without tampering with the ResourceBundle classes at all? 有没有其他方式优雅地处理这种事情,也许没有篡改ResourceBundle类?

I think this is a fundamental flaw in the way ResourceBundles are designed to function: keys that reference other keys automatically violate the DRY (don't repeat yourself) principle. 我认为这是ResourceBundles设计运行方式的一个根本缺陷:引用其他键的键会自动违反DRY(不要重复自己)原则。 The way I got around this was similar to your method: create a ReflectiveResourceBundle class that allows you to specify Resource keys in the messages using EL notation. 我解决这个问题的方法类似于你的方法:创建一个ReflectiveResourceBundle类,它允许你使用EL表示法在消息中指定资源键。

THE WRONG WAY: 错误的方法:

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

THE RIGHT WAY: 正确的方式:

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

I've uploaded the code to GitHub so you or anyone else can download it. 我已将代码上传到GitHub,以便您或其他任何人可以下载它。 Additionally, I've added some sample code for anyone using the Stripes Framework ( http://www.stripesframework.org/ ) to get you quickly up-and-running. 此外,我为使用Stripes Framework( http://www.stripesframework.org/ )的任何人添加了一些示例代码,以帮助您快速启动并运行。

The trick to getting this to work with standard JSTL fmt taglibs was to set up an interceptor that replaced the HttpServletRequest's resource with our own. 让它与标准JSTL fmt taglib一起工作的技巧是设置一个拦截器,用我们自己的资源替换HttpServletRequest的资源。 The code looks something like this: 代码看起来像这样:

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

Take a look at the stripes.interceptor package in the link above for more details. 有关更多详细信息,请查看上面链接中的stripes.interceptor包。

If the string repetitions are localized in the sense that you know a certain string will be repeated but only within the same project such that sharing resource bundles is not a design nightmare, then you might consider breaking the strings down into multiple key-value parts. 如果字符串重复是本地化的,你知道某个字符串将被重复,但只在同一个项目中,共享资源包不是设计的噩梦,那么你可以考虑将字符串分解为多个键值部分。 Separate the parts that repeat from those that do not and reuse the repeated parts. 将重复的部分与不重复的部分分开,并重复使用重复的部分。 For example, lets say you have the following two strings you need to display: 例如,假设您需要显示以下两个字符串:

  1. "The Red-capped Robin is a small passerine bird native to Australia. " “红顶罗宾是一种原产于澳大利亚的小雀形目鸟。”
  2. "The Red-capped Robin is found in dryer regions across much of the continent." “红顶罗宾在大陆大部分地区的干燥地区都有发现。”

The resource bundle could be as follows: 资源包可以如下:

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.

and then combine the required parts where needed bundle.getString("robin.name")+bundle.getString(robin.native). 然后在需要的地方组合所需的部分bundle.getString("robin.name")+bundle.getString(robin.native).

One thing you need to be careful about though is that the grammar rules like subject predicate order etc. might not be the same in all languages. 但是,您需要注意的一件事是,所有语言中的主语谓词顺序等语法规则可能并不相同。 So you would need to be a little careful when splitting sentences. 因此,在分割句子时你需要小心一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM