在Java中放置i18n键字符串的位置

在Java中进行国际化时,为每条消息分配一个字符串键。 什么是最佳实践,在哪里放置这些字符串键。 目标是允许简单的重构(例如,密钥名称更改),清晰可读的代码,关注点的分离,但即使从代码的不同部分调用,仍然没有重复的密钥/消息。

//bad way, strings directly in code messages.getString("hello_key"); 

 // better way, use String constants public static final String HELLO_KEY = "hello_key"; ... messages.getString(HELLO_KEY); 

 // other (better?) way, put all keys in one huge central class public class AllMessageKeys { public static final String HELLO_KEY = "hello_key"; ... } public class Foo { ... messages.getString(AllMessageKeys.HELLO_KEY); } 

 // other (better?) way, put all keys in neighbor class public class FooMessageKeys { public static final String HELLO_KEY = "hello_key"; } public class Foo { ... messages.getString(FooMessageKeys.HELLO_KEY); } 

还有其他建议吗? 哪个最好? 我在Eclipse IDE上,如果这使得重构部分更清晰。

澄清:在上面的示例中,“messages”的类型为ResourceBundle。

我总是使用这样的东西一个我的键列出的界面。 Interace的名称主要是DESC = Issue / short descrition / topic和键值。 通过这种方式,您可以制作一些不错的界面和一些通用界面,例如OK或Abort键。

 // other (better?) way, put all keys in neighbor class public interface DESCMessage { public static final String HELLO_KEY = "hello_key"; } public class Foo { ... messages.getString(DESCMessage.HELLO_KEY); } 

基本上,似乎我们都同意需要某种常数。 谈到常数,我更喜欢Enums。 Java Enumfunction非常强大,并且未得到充分利用:

 String title = Messages.getString(RunDialogMessages.TITLE); 

好的,但我必须做些什么才能让它看起来像这样? 简单的界面,枚举和对标准消息访问例程的轻微修改。 让我们从界面开始:

 public interface MessageKeyProvider { String getKey(); } 

枚举:

 public enum RunDialogMessages implements MessageKeyProvider { TITLE("RunDialog.Title"), PROMPT("RunDialog.Prompt.Label"), RUN("RunDialog.Run.Button"), CANCEL("RunDialog.Cancel.Button"); private RunDialogMessages(String key) { this.key = key; } private String key; @Override public String getKey() { return key; } } 

并修改了getString()方法:

 public static String getString(MessageKeyProvider provider) { String key = provider.getKey(); try { return RESOURCE_BUNDLE.getString(key); } catch (MissingResourceException e) { return '!' + key + '!'; } } 

为了完成图片,让我们看一下RunDialog.properties(我会尽快说明一下):

 RunDialog.Title=Run RunDialog.Prompt.Label=Enter the name of the program to run: RunDialog.Run.Button=Run RunDialog.Cancel.Button=Cancel 

显然,您可以使用Enum从属性文件中读取(通过嵌入ResourceBundle),但它可能违反单一责任原则(以及不要重复自己,因为访问代码需要重复)。

回到属性文件,我有一种感觉(我可能在这里错了),你的目标之一是避免重复翻译。 这就是为什么我在上面的例子中放两个运行。 你看,这个词会根据上下文以不同的方式翻译(实际上很常见)。 在这个例子中,如果我要将其翻译为波兰语,它将如下所示:

 RunDialog.Title=Uruchamianie RunDialog.Prompt.Label=Wpisz nazwę programu do uruchomienia: RunDialog.Run.Button=Uruchom RunDialog.Cancel.Button=Anuluj 

这是一个有着共轭概念的奇怪语言的不幸问题……

我也认为第一个是最糟糕的选择。 在大多数情况下(键仅由一个类使用)我更喜欢第二个使用String常量的解决方案。

如果密钥是从多个类引用的,则邻居类是更好的方法(使用像@moohkooh所提到的接口)。

一个中心类的解决方案创建了一个依赖磁铁,在我看来这是一个糟糕的设计。 具有每个包的常量的邻居接口将是更好的。

如果您不希望接口保存常量,则可以使用丰富的枚举:

 public enum DESCMessage { HELLO("hello_key"), OTHER("other_key"); private final String key; private DESCMessage(String key) { this.key = key; } public String key() { return key; } } 

这可以用作:

 messages.getString(DESCMessage.HELLO.key()); 

字符串常量是要走的路。 在您定义它们的地方,它实际上取决于您的代码结构和密钥的使用。 例如:

  • 如果你只使用一个类中的键,最好将它们放在那里。
  • 如果在代码中重复使用相同的键,最好将它们放在辅助类中(全局或每个包类,具体取决于密钥用法和密钥数)

从重构的角度来看,将常量从一个类移动到另一个类比重命名它们或更改它们的值要复杂得多(需要更多更改)。

更改其值时,无法自动更改已定义的资源。

这里解释了这样做的好方法之一: 使用NLS的新方法的简短文章,与ResourceBundle方法相比具有一些优势。

IMHO ResourceBundle有助于使用Locale特定的属性文件。 为了使用ResourceBundle,应根据以下约定命名属性文件: –

 BaseName_langCode.properties 

要么

 BaseName_langCode_countryCode.properties 

您可以使用属性文件。

恕我直言定义这些键及其相关字符串的最佳位置是NLS文件。您必须将它们保存在ResourceBundle文件中