*/
package org.sonar.core.i18n;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
this.pluginRepository = pluginRepository;
}
- I18nManager(Map<String, ClassLoader> bundleToClassloaders) {
+ @VisibleForTesting
+ I18nManager(Map<String, ClassLoader> bundleToClassloaders, ClassLoader languagePackClassLoader) {
this.bundleToClassloaders = bundleToClassloaders;
+ this.languagePackClassLoader = languagePackClassLoader;
}
public void start() {
public String message(Locale locale, String key, String defaultValue, Object... parameters) {
String bundleKey = propertyToBundles.get(key);
- ResourceBundle resourceBundle = getBundle(bundleKey, locale);
+ ResourceBundle resourceBundle = null;
+ if (bundleKey != null) {
+ resourceBundle = getBundle(bundleKey, locale);
+ }
return message(resourceBundle, key, defaultValue, parameters);
}
return fileCache.get(locale);
}
- ClassLoader classloader = getClassLoaderForProperty(relatedProperty);
+ ClassLoader classloader = getClassLoaderForProperty(relatedProperty, locale);
String result = null;
if (classloader != null) {
String bundleBase = propertyToBundles.get(relatedProperty);
}
ResourceBundle getBundle(String bundleKey, Locale locale) {
+ ResourceBundle bundle = null;
try {
+ // First, we check if the bundle exists in the language pack classloader
+ bundle = ResourceBundle.getBundle(bundleKey, locale, languagePackClassLoader);
+ } catch (MissingResourceException e1) {
+ // well, maybe the plugin has specified its own bundles, let's see
ClassLoader classloader = bundleToClassloaders.get(bundleKey);
if (classloader != null) {
- return ResourceBundle.getBundle(bundleKey, locale, classloader);
+ try {
+ bundle = ResourceBundle.getBundle(bundleKey, locale, classloader);
+ } catch (MissingResourceException e2) {
+ // Well, here, there's nothing much we can do...
+ }
}
- } catch (MissingResourceException e) {
- // ignore
}
- return null;
+ return bundle;
}
- ClassLoader getClassLoaderForProperty(String propertyKey) {
+ ClassLoader getClassLoaderForProperty(String propertyKey, Locale locale) {
String bundleKey = propertyToBundles.get(propertyKey);
- return (bundleKey != null ? bundleToClassloaders.get(bundleKey) : null);
+ if (bundleKey == null) {
+ return null;
+ }
+
+ try {
+ // First, we check if the bundle exists in the language pack classloader
+ ResourceBundle.getBundle(bundleKey, locale, languagePackClassLoader);
+ return languagePackClassLoader;
+ } catch (MissingResourceException e) {
+ // the plugin has specified its own bundles
+ return bundleToClassloaders.get(bundleKey);
+ }
}
String message(ResourceBundle resourceBundle, String key, String defaultValue, Object... parameters) {
import java.util.Locale;
import java.util.Map;
+import static org.fest.assertions.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
private I18nManager manager;
private ClassLoader coreClassLoader;
private ClassLoader sqaleClassLoader;
+ private ClassLoader forgeClassLoader;
/**
* See http://jira.codehaus.org/browse/SONAR-2927
@Before
public void init() {
- coreClassLoader = newCoreClassLoader();
- sqaleClassLoader = newSqaleClassLoader();
Map<String, ClassLoader> bundleToClassLoaders = Maps.newHashMap();
+ // following represents the English language pack + a core plugin : they use the same classloader
+ coreClassLoader = newCoreClassLoader();
bundleToClassLoaders.put(BUNDLE_PACKAGE + "core", coreClassLoader);
bundleToClassLoaders.put(BUNDLE_PACKAGE + "checkstyle", coreClassLoader);
+ // following represents a commercial plugin that must embed all its bundles, whatever the language
+ sqaleClassLoader = newSqaleClassLoader();
bundleToClassLoaders.put(BUNDLE_PACKAGE + "sqale", sqaleClassLoader);
- manager = new I18nManager(bundleToClassLoaders);
+ // following represents a forge plugin that embeds only the english bundle, and lets the language
+ // packs embed all the bundles for the other languages
+ forgeClassLoader = newForgeClassLoader();
+ bundleToClassLoaders.put(BUNDLE_PACKAGE + "forge", forgeClassLoader);
+
+ manager = new I18nManager(bundleToClassLoaders, coreClassLoader);
manager.start();
}
bundleToClassLoaders.put(BUNDLE_PACKAGE + "core", getClass().getClassLoader());
bundleToClassLoaders.put(BUNDLE_PACKAGE + "checkstyle", getClass().getClassLoader());
bundleToClassLoaders.put(BUNDLE_PACKAGE + "sqale", getClass().getClassLoader());
- I18nManager i18n = new I18nManager(bundleToClassLoaders);
+ I18nManager i18n = new I18nManager(bundleToClassLoaders, coreClassLoader);
i18n.start();
assertThat(i18n.extractBundleFromKey("by"), Is.is(BUNDLE_PACKAGE + "core"));
@Test
public void shouldGetClassLoaderByProperty() {
- assertThat(manager.getClassLoaderForProperty("foo.unknown"), nullValue());
- assertThat(manager.getClassLoaderForProperty("by"), Is.is(coreClassLoader));
- assertThat(manager.getClassLoaderForProperty("sqale.page"), Is.is(sqaleClassLoader));
+ assertThat(manager.getClassLoaderForProperty("foo.unknown", Locale.ENGLISH), nullValue());
+ assertThat(manager.getClassLoaderForProperty("by", Locale.ENGLISH), Is.is(coreClassLoader));
+ // The following plugin defines its own bundles, whatever the language
+ assertThat(manager.getClassLoaderForProperty("sqale.page", Locale.ENGLISH), Is.is(sqaleClassLoader));
+ assertThat(manager.getClassLoaderForProperty("sqale.page", Locale.FRENCH), Is.is(sqaleClassLoader));
+ // The following plugin defines only the English bundle, and lets the language packs handle the translations
+ assertThat(manager.getClassLoaderForProperty("forge_plugin.page", Locale.ENGLISH), Is.is(forgeClassLoader));
+ assertThat(manager.getClassLoaderForProperty("forge_plugin.page", Locale.FRENCH), Is.is(coreClassLoader));
}
@Test
public void shouldFindEnglishFile() {
- String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */, false);
+ String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /*
+ * any property in the same
+ * bundle
+ */, false);
assertThat(html, Is.is("This is the architecture rule"));
}
public void shouldNotKeepInCache() {
assertThat(manager.getFileContentCache().size(), Is.is(0));
boolean keepInCache = false;
- String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */, keepInCache);
+ String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /*
+ * any property in the same
+ * bundle
+ */, keepInCache);
assertThat(html, not(nullValue()));
assertThat(manager.getFileContentCache().size(), Is.is(0));
public void shouldKeepInCache() {
assertThat(manager.getFileContentCache().size(), Is.is(0));
boolean keepInCache = true;
- String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */, keepInCache);
+ String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /*
+ * any property in the same
+ * bundle
+ */, keepInCache);
assertThat(html, not(nullValue()));
Map<String, Map<Locale, String>> cache = manager.getFileContentCache();
assertThat(cache.get("ArchitectureRule.html").get(Locale.ENGLISH), Is.is("This is the architecture rule"));
}
+ // see SONAR-3596
+ @Test
+ public void shouldLookInCoreClassloaderForPluginsThatDontEmbedAllLanguages() {
+ assertThat(manager.message(Locale.ENGLISH, "forge_plugin.page", null)).isEqualTo("This is my plugin");
+ assertThat(manager.message(Locale.FRENCH, "forge_plugin.page", null)).isEqualTo("C'est mon plugin");
+ }
+
+ private URLClassLoader newForgeClassLoader() {
+ return newClassLoader("/org/sonar/core/i18n/forgePlugin/");
+ }
private URLClassLoader newSqaleClassLoader() {
return newClassLoader("/org/sonar/core/i18n/sqalePlugin/");