2010-04-09 75 views
14

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

我想出了解决办法去是这样的:

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中吐出的)查找相同的资源包,但未指定自定义控件。如果您编写一个简单的单元测试,然后调用该单元测试,而这个测试看起来是不可重复的,但是当应用程序真实运行时会发生这种情况。不知何故,ResourceBundle内的缓存弹出良好的值,并用破碎的替换它。我还没有弄清楚为什么Sun的jar文件是在没有调试信息的情况下编译的,所以调试它是一件苦差事。

我的问题:

  1. 是否有在全球设置默认的ResourceBundle.Control,我可能不知道的一些方法?这将解决一切相当优雅。

  2. 有没有其他的方式来优雅地处理这种事情,也许没有篡改ResourceBundle类呢?

+0

这是使用元编程可以轻松处理的东西,如果Java有它的话。 – 2010-04-09 10:38:28

回答

6

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

走错了路:

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} 

uploaded the code to GitHub所以你或其他人可以下载它。另外,我为任何使用Stripes Framework(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软件包以获取更多详细信息。

+0

您说得对,这与我们的解决方案非常相似,它具有相同的缺点,即使用ResourceBundle.getBundle()生成的代码将不会收到解决方法。主要区别在于我们使用现有的ResourceBundle高速缓存,而在你的情况下,你需要单独保存高速缓存,大概是为了解决Sun的高速缓存偶尔会破坏高速缓存的数据包。 – Trejkaz 2010-04-12 04:02:29

+0

因为我刚碰巧遇到同样的问题: 我基本上想要重新使用所有ResourceBundle的功能,但添加了密钥扩展。所以我从PropertyResourceBundle派生并相应地重写handleGetObject()(可以在Matt Brocks的资料中看到)。 然后我从ResourceBundle.Control派生,重载newBundle()以返回我的自定义PropertyResourceBundle(仅修改此方法的原始源)并将其传递给ResourceBundle.getBundle()。 这使得重新使用所有ResourceBundle的功能+添加密钥扩展成为可能。 – quaylar 2012-09-05 16:02:55

+0

@马特布洛克,它似乎链接被打破。你能分享页面吗? – 2015-03-23 08:37:16

0

如果字符串重复在你知道某些字符串会反复,但只能在同一项目,使得共享资源包是不是设计的噩梦感本地化的,那么你可以考虑打破串下来分成多个键值部分。重复那些不重复的部分并重复使用重复的部分。例如,假设你有你需要显示以下两个字符串:“红色覆盖的罗宾是一个小的雀形目鸟类,原产于澳大利亚”

  1. “红皑皑罗宾在发现在整个大陆的大部分地区都有烘干机。“

资源包可能如下:

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).

有一件事你必须要小心,虽然是所需零件的语法规则类似主题谓词顺序等可能在所有语言中都不相同。所以你在分割句子时需要小心。

+5

事实上,对于不同的上下文(例如在菜单或状态栏中),主题的性别以及多个(1,2,27等)也是一个坏主意,因为这些也取决于文化。 – devstuff 2010-04-09 07:04:18

+0

这实际上是我们采用从ResourceBundle一侧进行所有插入的方法的确切原因。使用我们目前的方法,可以通过将{{robin.name}}放入其下面的值中的适当位置来完成该示例。如果有人遇到一种不起作用的语言,他们有权完全删除变量。但是......我们的解决方案当然很难正确工作。 – Trejkaz 2010-04-09 23:18:08