加载本地化资源

Load localized resources

在我正在进行的项目中,我想根据当前语言环境加载不同的资源。使用字符串,这非常简单:只需通过添加一些文件创建资源包,例如 Localization.propertiesLocalization_de.properties,然后通过 ResourceBundle.getBundle("domain.project.Localization").

加载它

但是如何加载不是简单字符串的本地化资源?

具体来说,我想根据区域设置加载图像和 HTML 文件。为该资源获取正确的 URL 就足够了。例如,我想要一个 Welcome.htmlWelcome_de.html 文件,并在当前语言环境为德语时加载后者。

简单地使用 getClass().getResource("Welcome.html") 是行不通的,它总是准确地返回那个文件并且不进行语言环境处理。除了根据当前语言环境手动修改文件名之外,我还没有找到解决这个问题的便捷方法。

我想出的一个解决方案是向我的 Localization.properties 文件添加一个密钥,其中包含正确的本地化 HTML 文件的名称。但这感觉很老套而且是错误的。

请注意,这不是 Android 应用程序,只是一个标准的 Java 8 桌面应用程序。

拥有保存本地化文件名称的资源确实是一个有效的解决方案,但正如您所提到的那样很麻烦。如果您需要在您的应用程序中显示 HTML 页面,为什么不嵌入小型 Web 服务器并让它服务器 JSP 页面?这样,整个 HTML 将保持不变,页面本身将被本地化。

另一种选择是使用本地模板引擎(例如 FreeMarker)并在提供服务之前动态生成 HTMLs。

ListResourceBundle呢?

定义您的捆绑包:

public class MyBundle_fr_FR extends ListResourceBundle
{
    public Object[][] getContents()
    {
        return new Object[][] 
        {
            { "welcome_file", getClass().getResource("Welcome_fr.html") },
            { "another_file", getClass().getResource("foo_fr.html") }
        };
    }
}

public class MyBundle_de_DE extends ListResourceBundle
{
    public Object[][] getContents()
    {
        return new Object[][] 
        {
            { "welcome_file", getClass().getResource("Welcome_de.html") },
            { "another_file", getClass().getResource("foo_de.html") }
        };
    }
}

然后像这样使用它:

ResourceBundle bundle = ResourceBundle.getBundle("MyBundle", Locale.GERMANY);
URL url = (URL) bundle.getObject("welcome_file");

你做这件事的方式对我来说有点奇怪。我开发了一些国际应用程序,但从未使用过这种方法。您的方法的维护成本非常高。想象一下,您想要更改 welcome.html 文件的一部分,您需要更改所有其他欢迎文件,这是一项巨大的工作。 java 中此问题的常见解决方案是使用 JSTL fmt taglib 并获得一些包含键值对的 属性 文件的帮助,这些键值对的键是固定的,值根据您所在的区域设置而变化在。例如,您在 Localization.properties 文件中有如下键值对:

page.welcome.welcome=welcome

并且您有另一个名称为 Localization_de.properties 的文件,该文件具有相同的键和不同的值

page.welcome.welcome=willkommen

现在,在您的 welcome.jsp 中,您可以使用这样的东西 <fmt:message key="page.welcome.welcome"/> 来在您的页面中呈现正确的值。对于图像,您可以获得相同的方法。只需将图像名称作为键导入每个 属性 文件,并将其正确命名为它们的值,然后在 jsp 中使用该键。 对于 i18n,使用 eclipse 插件(如 Resource Bundel Editor)让你的生活更轻松。

在我使用的其他语言框架(如 django)中,我看到了与上述相同的方法。

(这是对我的评论的补充,以便有更详细的解释)

您可以根据语言将资源放在不同的文件夹中。

文件树可能如下所示:

  • 路径资源

    • DE

      • 图片
        • Image1.png
        • Image2.png
      • 本地化
        • MainPage // 更多网站信息....
        • 印象
    • EN

      • ...

并像 "file-localization-path-builder-class" 一样使用(声称有史以来最好的名字)

public static Locale currentLocale = Locale.GERMAN;

public static String baseRessourcePath = "Path to your basic folder for ressources";

public static String getLocalizedPathToFile(String ressourcePath)
{
    return Paths.get(currentLocale.getLanguage(), ressourcePath).toString();
}

无论您需要在何处加载资源,只需使用

调用您的加载程序
getLocalizedPath(PathToFile)

其中 PathToFile 类似于 /Images/Image1.png 或其他内容。

这也应该很容易用于客户端应用程序。

希望这对您有所帮助:)

实际上很容易获得与 ResourceBundle.getBundle 相同的行为。我 looked at the code and found that the most important part for handling of the locales and filenames is ResourceBundle.Control.

所以这里有一个简单的方法 returns 将 URL 本地化资源(遵循与资源包相同的文件名方案)而无需缓存并仅支持当前语言环境:

/** Load localized resource for current locale.
 * 
 * @param baseName Basename of the resource. May include a path.
 * @param suffix File extension of the resource.
 * @return URL for the localized resource or null if none was found.
 */
public static URL getLocalizedResource(String baseName, String suffix) {
    Locale locale = Locale.getDefault();
    ResourceBundle.Control control = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
    List<Locale> candidateLocales = control.getCandidateLocales(baseName, locale);

    for (Locale specificLocale : candidateLocales) {
        String bundleName = control.toBundleName(baseName, specificLocale);
        String resourceName = control.toResourceName(bundleName, suffix);

        // Replace "Utils" with the name of your class!
        URL url = Utils.class.getResource(resourceName);
        if (url != null) {
            return url;
        }
    }

    return null;
}

如果有人想扩展它以支持通用语言环境作为参数:ResourceBundle.getBundleImpl 实现中的重要部分是:

for (Locale targetLocale = locale;
     targetLocale != null;
     targetLocale = control.getFallbackLocale(baseName, targetLocale)) {
         List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale);
         ...
}

getCandidateLocales 的最后一个条目是空语言环境 (Locale.ROOT)。当区域设置时应该忽略它,因为默认资源将在第一个 for 迭代中找到,然后再测试后备区域设置。