Java 具有编译时检查的l18n框架

Java 具有编译时检查的l18n框架,java,internationalization,translation,Java,Internationalization,Translation,我目前正在开发一个更大的Java桌面应用程序,现在我想添加翻译。l18n系统让我烦恼的是,它不提供任何类型的编译时检查 在java系统中,您有一个类似于HashMap的东西,其中每个本地化字符串都有一个“键”,然后翻译的字符串就是“值”。这看起来像这样(取自下图): 如果您有一个简单/小型的应用程序,那么这很好用。但是,在一个有数千个翻译字符串的大型应用程序中,可能会出现“Key”输入错误,从而得到一个空字符串或错误字符串 如果运气好的话,应用程序会抛出一个RuntimeException,告诉

我目前正在开发一个更大的Java桌面应用程序,现在我想添加翻译。l18n系统让我烦恼的是,它不提供任何类型的编译时检查

在java系统中,您有一个类似于HashMap的东西,其中每个本地化字符串都有一个“键”,然后翻译的字符串就是“值”。这看起来像这样(取自下图):

如果您有一个简单/小型的应用程序,那么这很好用。但是,在一个有数千个翻译字符串的大型应用程序中,可能会出现“Key”输入错误,从而得到一个空字符串或错误字符串

如果运气好的话,应用程序会抛出一个
RuntimeException
,告诉您这一点,但即使如此,您也可能没有达到这一点,因为在特定情况下可能不会显示的对话框中使用了错误的“键”(假设这是一个错误对话框)

为了防止这种情况发生,最好使用一个系统,该系统提供对所用“键”的编译时检查。例如,在Android中,您可以指定XML文件中的资源,然后将其索引并映射到类(包括要使用的“键”)。通过这种方式,您可以(从中)得到如下信息:

如果您在该“键”中有一个输入错误,您将在编译时得到一个错误(您的IDE中也有“自动完成”)

现在,要使类似的工作正常,您需要一个小工具/脚本来完成索引部分并生成资源类(
R.java
)。在Android中,Eclipse插件(或您的IDE)可以为您实现这一点


我现在的问题是:是否已经有一个系统可以用于Java中的普通桌面应用程序?还是我说的话大错特错了?

我从来都不在乎这样一个工具,它可以索引您的资源。嗯,这是一个很不寻常的要求。其中一种方法-不要在代码中使用字符串文字,而是将它们全部移动到一个类中,使用静态final修饰符声明它们。使用此常量作为资源映射的键。编写测试非常容易,使用反射将检查单个类中声明的所有资源是否都存在于资源文件中。编写代码分析器要容易得多。对于这个问题,有一个相当简单的解决方案。首先,不要使用魔术字符串作为代码,定义常量并引用它们。所以你改变了

messages.getString("greetings");

并具有相应的类

public class I18 {

  public static final String GREETINGS_CODE = "greetings";

}

接下来编写一个测试用例,其中在每个语言资源文件中查找
I18
类中的每个代码。如果任何资源文件缺少代码,则测试失败。这不是编译时间,但是如果您有任何问题,您的项目将无法通过测试。

我见过一些相当大的企业应用程序使用标准的资源包,相信我,这不是问题

成功因素很少:

  • 资源组织
  • 资源助手
  • 资源工厂
  • 密钥命名策略
  • 公元1年。如果您决定使用单个资源包,您的问题将特别明显。不要走这条路。没有办法维护大的单个文件。您将面临密钥复制、密钥命名等问题。此外,对于大型应用程序,通常很少有人同时处理同一组文件。在资源束的情况下,这意味着大量的合并和相互阻碍。这是常规编程团队的问题,但请相信我的本地化工具,它甚至更糟糕 您需要的是多个小型资源文件。根据应用程序的规模,可能是每个模块一个文件,甚至每个对话框一个文件。如何分割文件取决于实际应用程序,但我建议资源文件的长度不要超过两个文本屏幕。
    对于大量的文件,问题在于如何组织它们-将它们放入有效的目录。我的建议是将资源文件从源代码中分离出来(将它们放在名为-例如-L10n的单独目录中)。在资源文件根目录下,最好有语言文件夹(这样很容易将每个目标语言的翻译分开),尽管这需要处理ResourceBundle.Control类。然后在语言根目录下为每个模块创建子目录。在模块根目录下,您可以有单独的子目录(具有非常大的应用程序),也可以直接放置资源包

    公元2年。无法直接使用ResourceBundle类。如果文件中没有这样的密钥,或者没有您要查找的文件,就会引发一个问题。相反,您可能希望看到(在用户界面上)出现了错误,例如感叹号(!the.key.does.not.exist!)之间的键的名称-这是Eclipse的“外部化字符串”在默认情况下所做的操作。
    您可以决定创建资源帮助器,它可以是ResourceBundle上的包装器,也可以从中派生。选择权归你(尽管对于像上面这样严肃的资源组织,合理的选择是派生,因为你需要创建自己的ResourceBundle.Control类)。除了简单的翻译处理外,您还可以向资源助手添加其他功能,如处理消息格式、复数形式等。
    目前,它不会处理错误资源密钥名称的问题。您将需要实际的UI测试-打字错误将是可见的。如果使用错误的键(特别是对于使用复制粘贴设计模式的人),我认为没有任何技术能够解决这个问题。机器不会思考。无论如何,您可以通过放置(例如)具有键常量或嵌套枚举的嵌套类来处理键的问题。这种解决方案的问题
    messages.getString("greetings");
    
    messages.getString(I18.GREETINGS_CODE);
    
    public class I18 {
    
      public static final String GREETINGS_CODE = "greetings";
    
    }
    
    ResourceHelper helper = ResourceFactory(
                              ResourceFactory.Bundles.ERRORS, Locale locale);
    
    public interface Bundle{
      @En("Hello, World!")
      @De("Hallo Welt!")
      String greeting();
    }
    
    // ... somewhere in your code
    public class MyClass{
      private static final Bundle bundle = C10N.get(Bundle.class);
    
      public void myMethod(){
        System.out.println(bundle.greeting());
      }
    }