aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2011-07-29 12:09:43 +0200
committerSimon Brandhof <simon.brandhof@gmail.com>2011-07-29 12:09:43 +0200
commit402f8958a403d94422f38d47cc39aba0f13e8aae (patch)
treee4bac52b9a2927d7e994d9961bacf6d880611db7 /sonar-core
parent15f5d562a3da55f3702500c1c855f3f9d7a52e2d (diff)
downloadsonarqube-402f8958a403d94422f38d47cc39aba0f13e8aae.tar.gz
sonarqube-402f8958a403d94422f38d47cc39aba0f13e8aae.zip
SONAR-75 Improve i18n API
- The extension point LanguagePack is not required anymore - No error logs when rule description is not available in all locales - Increase code coverage and decrease complexity
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java141
-rw-r--r--sonar-core/src/main/java/org/sonar/core/i18n/RuleI18nManager.java111
-rw-r--r--sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java138
-rw-r--r--sonar-core/src/test/java/org/sonar/core/i18n/RuleI18nManagerTest.java90
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle.properties1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle/ArchitectureRule.html1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/core.properties4
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr.properties1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr/ArchitectureRule.html1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/core_fr.properties2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale.properties1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale_fr.properties1
12 files changed, 460 insertions, 32 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java b/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java
index a99ba3397bf..45f31b36853 100644
--- a/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java
+++ b/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java
@@ -20,50 +20,145 @@
package org.sonar.core.i18n;
import com.google.common.collect.Maps;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.ServerExtension;
import org.sonar.api.i18n.I18n;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.platform.PluginRepository;
+import org.sonar.api.utils.SonarException;
+import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
+import java.util.*;
public class I18nManager implements I18n, ServerExtension {
+ private static Logger LOG = LoggerFactory.getLogger(I18nManager.class);
+
+ public static final String ENGLISH_PACK_PLUGIN_KEY = "i18nen";
+ public static final String BUNDLE_PACKAGE = "org.sonar.i18n.";
private PluginRepository pluginRepository;
- private Map<String, ClassLoader> bundleToClassloader;
+ private Map<String, ClassLoader> bundleToClassloaders;
+ private Map<String, String> propertyToBundles;
public I18nManager(PluginRepository pluginRepository) {
this.pluginRepository = pluginRepository;
}
- I18nManager(Map<String, ClassLoader> bundleToClassloader) {
- this.bundleToClassloader = bundleToClassloader;
+ I18nManager(Map<String, ClassLoader> bundleToClassloaders) {
+ this.bundleToClassloaders = bundleToClassloaders;
}
public void start() {
- ClassLoader coreClassLoader = pluginRepository.getPlugin("i18nen").getClass().getClassLoader();
+ initClassloaders();
+ initProperties();
+ }
+
+ private void initClassloaders() {
+ if (bundleToClassloaders == null) {
+ ClassLoader coreClassLoader = pluginRepository.getPlugin(ENGLISH_PACK_PLUGIN_KEY).getClass().getClassLoader();
+ bundleToClassloaders = Maps.newHashMap();
+ for (PluginMetadata metadata : pluginRepository.getMetadata()) {
+ if (!metadata.isCore() && !ENGLISH_PACK_PLUGIN_KEY.equals(metadata.getBasePlugin())) {
+ // plugin but not a language pack
+ // => plugins embedd only their own bundles with all locales
+ ClassLoader classLoader = pluginRepository.getPlugin(metadata.getKey()).getClass().getClassLoader();
+ bundleToClassloaders.put(BUNDLE_PACKAGE + metadata.getKey(), classLoader);
- bundleToClassloader = Maps.newHashMap();
- for (PluginMetadata metadata : pluginRepository.getMetadata()) {
- if (!metadata.isCore() && !"i18nen".equals(metadata.getBasePlugin())) {
- ClassLoader classLoader = pluginRepository.getPlugin(metadata.getKey()).getClass().getClassLoader();
- bundleToClassloader.put(metadata.getKey(), classLoader);
+ } else if (metadata.isCore()) {
+ // bundles of core plugins are defined into language packs. All language packs are supposed
+ // to share the same classloader (english pack classloader)
+ bundleToClassloaders.put(BUNDLE_PACKAGE + metadata.getKey(), coreClassLoader);
+ }
+ }
+ }
+ bundleToClassloaders = Collections.unmodifiableMap(bundleToClassloaders);
+ }
- } else if (metadata.isCore()) {
- bundleToClassloader.put(metadata.getKey(), coreClassLoader);
+ private void initProperties() {
+ propertyToBundles = Maps.newHashMap();
+ for (Map.Entry<String, ClassLoader> entry : bundleToClassloaders.entrySet()) {
+ try {
+ String bundleKey = entry.getKey();
+ ResourceBundle bundle = ResourceBundle.getBundle(bundleKey, Locale.ENGLISH, entry.getValue());
+ Enumeration<String> keys = bundle.getKeys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ propertyToBundles.put(key, bundleKey);
+ }
+ } catch (MissingResourceException e) {
+ // ignore
}
}
+ propertyToBundles = Collections.unmodifiableMap(propertyToBundles);
+ LOG.debug(String.format("Loaded %d properties from English bundles", propertyToBundles.size()));
}
public String message(Locale locale, String key, String defaultValue, Object... parameters) {
- String bundle = keyToBundle(key);
- ResourceBundle resourceBundle = ResourceBundle.getBundle(bundle, locale, bundleToClassloader.get(bundle));
- String value = resourceBundle.getString(key);
- if (value==null) {
+ String bundleKey = propertyToBundles.get(key);
+ ResourceBundle resourceBundle = getBundle(bundleKey, locale);
+ return message(resourceBundle, key, defaultValue, parameters);
+ }
+
+ /**
+ * Results are not kept in cache.
+ */
+ String messageFromFile(Locale locale, String filename, String relatedProperty) {
+ ClassLoader classloader = getClassLoaderForProperty(relatedProperty);
+ String result = null;
+ if (classloader != null) {
+ String bundleBase = propertyToBundles.get(relatedProperty);
+ String filePath = bundleBase.replace('.', '/');
+ if (locale != Locale.ENGLISH) {
+ filePath += "_" + locale.toString();
+ }
+ filePath += "/" + filename;
+ InputStream input = classloader.getResourceAsStream(filePath);
+ if (input != null) {
+ try {
+ result = IOUtils.toString(input, "UTF-8");
+ } catch (IOException e) {
+ throw new SonarException("Fail to load file: " + filePath, e);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+ }
+ return result;
+ }
+
+ ResourceBundle getBundle(String bundleKey, Locale locale) {
+ try {
+ ClassLoader classloader = bundleToClassloaders.get(bundleKey);
+ if (classloader != null) {
+ return ResourceBundle.getBundle(bundleKey, locale, classloader);
+ }
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ return null;
+ }
+
+
+ ClassLoader getClassLoaderForProperty(String propertyKey) {
+ String bundleKey = propertyToBundles.get(propertyKey);
+ return (bundleKey != null ? bundleToClassloaders.get(bundleKey) : null);
+ }
+
+ String message(ResourceBundle resourceBundle, String key, String defaultValue, Object... parameters) {
+ String value = null;
+ if (resourceBundle != null) {
+ try {
+ value = resourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ }
+ if (value == null) {
value = defaultValue;
}
if (value != null && parameters.length > 0) {
@@ -72,11 +167,11 @@ public class I18nManager implements I18n, ServerExtension {
return value;
}
- String keyToBundle(String key) {
- String pluginKey = StringUtils.substringBefore(key, ".");
- if (bundleToClassloader.containsKey(pluginKey)) {
- return pluginKey;
+ String extractBundleFromKey(String key) {
+ String bundleKey = BUNDLE_PACKAGE + StringUtils.substringBefore(key, ".");
+ if (bundleToClassloaders.containsKey(bundleKey)) {
+ return bundleKey;
}
- return "core";
+ return BUNDLE_PACKAGE + "core";
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/RuleI18nManager.java b/sonar-core/src/main/java/org/sonar/core/i18n/RuleI18nManager.java
new file mode 100644
index 00000000000..764871806cc
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/i18n/RuleI18nManager.java
@@ -0,0 +1,111 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.i18n;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ServerComponent;
+
+import java.util.Locale;
+
+public class RuleI18nManager implements ServerComponent {
+
+ private I18nManager i18nManager;
+
+ public RuleI18nManager(I18nManager i18nManager) {
+ this.i18nManager = i18nManager;
+ }
+
+ public String getName(String repositoryKey, String ruleKey, Locale locale) {
+ return message(repositoryKey, ruleKey, locale, ".name", ruleKey);
+ }
+
+ public String getDescription(String repositoryKey, String ruleKey, Locale locale) {
+ String relatedProperty = "rule." + repositoryKey + "." + ruleKey + ".name";
+
+ // TODO add cache
+ String description = i18nManager.messageFromFile(locale, ruleKey + ".html", relatedProperty);
+ if (description == null && !Locale.ENGLISH.equals(locale)) {
+ description = i18nManager.messageFromFile(Locale.ENGLISH, ruleKey + ".html", relatedProperty);
+ }
+ return StringUtils.defaultString(description, "");
+ }
+
+ public String getParamDescription(String repositoryKey, String ruleKey, String paramKey, Locale locale) {
+ return message(repositoryKey, ruleKey, locale, ".param." + paramKey, "");
+ }
+
+ private String message(String repositoryKey, String ruleKey, Locale locale, String suffix, String defaultValue) {
+ String propertyKey = new StringBuilder().append("rule.").append(repositoryKey).append(".").append(ruleKey).append(suffix).toString();
+ return i18nManager.message(locale, propertyKey, defaultValue);
+ }
+
+// static class RuleKey {
+// private String repositoryKey;
+// private String key;
+//
+// RuleKey(String repositoryKey, String key) {
+// this.repositoryKey = repositoryKey;
+// this.key = key;
+// }
+//
+// public String getRepositoryKey() {
+// return repositoryKey;
+// }
+//
+// public String getKey() {
+// return key;
+// }
+//
+// @Override
+// public boolean equals(Object o) {
+// if (this == o) return true;
+// if (o == null || getClass() != o.getClass()) return false;
+//
+// RuleKey ruleKey = (RuleKey) o;
+//
+// if (!key.equals(ruleKey.key)) return false;
+// if (!repositoryKey.equals(ruleKey.repositoryKey)) return false;
+//
+// return true;
+// }
+//
+// @Override
+// public int hashCode() {
+// int result = repositoryKey.hashCode();
+// result = 31 * result + key.hashCode();
+// return result;
+// }
+//
+// @Override
+// public String toString() {
+// return new StringBuilder().append(repositoryKey).append(":").append(key).toString();
+// }
+// }
+//
+// static class RuleMessages {
+// private String name;
+// private String description;
+//
+// RuleMessages(String name, String description) {
+// this.name = name;
+// this.description = description;
+// }
+// }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java
index af57b768738..b2ef460c993 100644
--- a/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java
@@ -20,25 +20,145 @@
package org.sonar.core.i18n;
import com.google.common.collect.Maps;
+import org.hamcrest.CoreMatchers;
import org.hamcrest.core.Is;
+import org.junit.Before;
import org.junit.Test;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
import java.util.Map;
+import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
+import static org.sonar.core.i18n.I18nManager.BUNDLE_PACKAGE;
public class I18nManagerTest {
+ private I18nManager manager;
+ private ClassLoader coreClassLoader;
+ private ClassLoader sqaleClassLoader;
+
+ @Before
+ public void init() {
+ coreClassLoader = newCoreClassLoader();
+ sqaleClassLoader = newSqaleClassLoader();
+ Map<String, ClassLoader> bundleToClassLoaders = Maps.newHashMap();
+ bundleToClassLoaders.put(BUNDLE_PACKAGE + "core", coreClassLoader);
+ bundleToClassLoaders.put(BUNDLE_PACKAGE + "checkstyle", coreClassLoader);
+ bundleToClassLoaders.put(BUNDLE_PACKAGE + "sqale", sqaleClassLoader);
+ manager = new I18nManager(bundleToClassLoaders);
+ manager.start();
+ }
+
@Test
- public void shouldExtractBundleKey() {
- Map<String,ClassLoader> bundleToClassLoaders = Maps.newHashMap();
- bundleToClassLoaders.put("core", getClass().getClassLoader());
- bundleToClassLoaders.put("checkstyle", getClass().getClassLoader());
- bundleToClassLoaders.put("sqale", getClass().getClassLoader());
+ public void shouldExtractPluginFromKey() {
+ Map<String, ClassLoader> bundleToClassLoaders = Maps.newHashMap();
+ 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);
+ i18n.start();
+
+ assertThat(i18n.extractBundleFromKey("by"), Is.is(BUNDLE_PACKAGE + "core"));
+ assertThat(i18n.extractBundleFromKey("violations_drilldown.page"), Is.is(BUNDLE_PACKAGE + "core"));
+ assertThat(i18n.extractBundleFromKey("checkstyle.rule1.name"), Is.is(BUNDLE_PACKAGE + "checkstyle"));
+ assertThat(i18n.extractBundleFromKey("sqale.console.page"), Is.is(BUNDLE_PACKAGE + "sqale"));
+ }
+
+ @Test
+ public void shouldFindKeysInEnglishLanguagePack() {
+ assertThat(manager.message(Locale.ENGLISH, "checkstyle.rule1.name", null), Is.is("Rule one"));
+ assertThat(manager.message(Locale.ENGLISH, "by", null), Is.is("By"));
+ assertThat(manager.message(Locale.ENGLISH, "sqale.page", null), Is.is("Sqale page title"));
+
+ assertThat(manager.message(Locale.FRENCH, "checkstyle.rule1.name", null), Is.is("Rule un"));
+ assertThat(manager.message(Locale.FRENCH, "by", null), Is.is("Par"));
+ assertThat(manager.message(Locale.FRENCH, "sqale.page", null), Is.is("Titre de la page Sqale"));
+ }
+
+ @Test
+ public void shouldUseDefaultLocale() {
+ assertThat(manager.message(Locale.CHINA, "checkstyle.rule1.name", null), Is.is("Rule one"));
+ assertThat(manager.message(Locale.CHINA, "by", null), Is.is("By"));
+ assertThat(manager.message(Locale.CHINA, "sqale.page", null), Is.is("Sqale page title"));
+ }
+
+ @Test
+ public void shouldUseLanguagePack() {
+ assertThat(manager.message(Locale.FRENCH, "checkstyle.rule1.name", null), Is.is("Rule un"));
+ assertThat(manager.message(Locale.FRENCH, "by", null), Is.is("Par"));
+ assertThat(manager.message(Locale.FRENCH, "sqale.page", null), Is.is("Titre de la page Sqale"));
+ }
+
+ @Test
+ public void shouldReturnDefaultValueIfMissingKey() {
+ assertThat(manager.message(Locale.ENGLISH, "foo.unknown", "default"), Is.is("default"));
+ assertThat(manager.message(Locale.FRENCH, "foo.unknown", "default"), Is.is("default"));
+ }
+
+ @Test
+ public void shouldAcceptEmptyLabels() {
+ assertThat(manager.message(Locale.ENGLISH, "empty", "default"), Is.is(""));
+ assertThat(manager.message(Locale.FRENCH, "empty", "default"), Is.is(""));
+ }
+
+ @Test
+ public void shouldFormatMessageWithParameters() {
+ assertThat(manager.message(Locale.ENGLISH, "with.parameters", null, "one", "two"), Is.is("First is one and second is two"));
+ }
+
+ @Test
+ public void shouldUseDefaultLocaleIfMissingValueInLocalizedBundle() {
+ assertThat(manager.message(Locale.FRENCH, "only.in.english", null), Is.is("Missing in French bundle"));
+ assertThat(manager.message(Locale.CHINA, "only.in.english", null), Is.is("Missing in French bundle"));
+ }
+
+ @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));
+ }
+
+ @Test
+ public void shouldFindEnglishFile() {
+ String html = manager.messageFromFile(Locale.ENGLISH, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */);
+ assertThat(html, Is.is("This is the architecture rule"));
+ }
+
+ @Test
+ public void shouldNotFindFile() {
+ String html = manager.messageFromFile(Locale.ENGLISH, "UnknownRule.html", "checkstyle.rule1.name" /* any property in the same bundle */);
+ assertThat(html, nullValue());
+ }
+
+ @Test
+ public void shouldFindFrenchFile() {
+ String html = manager.messageFromFile(Locale.FRENCH, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */);
+ assertThat(html, Is.is("Règle d'architecture"));
+ }
+
+ @Test
+ public void shouldNotFindMissingLocale() {
+ String html = manager.messageFromFile(Locale.CHINA, "ArchitectureRule.html", "checkstyle.rule1.name" /* any property in the same bundle */);
+ assertThat(html, nullValue());
+ }
+
+
+ private URLClassLoader newSqaleClassLoader() {
+ return newClassLoader("/org/sonar/core/i18n/sqalePlugin/");
+ }
+
+ private URLClassLoader newCoreClassLoader() {
+ return newClassLoader("/org/sonar/core/i18n/englishPack/", "/org/sonar/core/i18n/frenchPack/");
+ }
- assertThat(i18n.keyToBundle("by"), Is.is("core"));
- assertThat(i18n.keyToBundle("violations_drilldown.page"), Is.is("core"));
- assertThat(i18n.keyToBundle("checkstyle.rule1.name"), Is.is("checkstyle"));
- assertThat(i18n.keyToBundle("sqale.console.page"), Is.is("sqale"));
+ private URLClassLoader newClassLoader(String... resourcePaths) {
+ URL[] urls = new URL[resourcePaths.length];
+ for (int index = 0; index < resourcePaths.length; index++) {
+ urls[index] = getClass().getResource(resourcePaths[index]);
+ }
+ return new URLClassLoader(urls);
}
}
diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/RuleI18nManagerTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/RuleI18nManagerTest.java
new file mode 100644
index 00000000000..17608330075
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/i18n/RuleI18nManagerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.i18n;
+
+import org.hamcrest.core.Is;
+import org.junit.Test;
+
+import java.util.Locale;
+
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.isNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+public class RuleI18nManagerTest {
+ @Test
+ public void shouldGetName() {
+ I18nManager i18n = mock(I18nManager.class);
+ RuleI18nManager ruleI18n = new RuleI18nManager(i18n);
+
+ ruleI18n.getName("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH);
+
+ String propertyKey = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name";
+ verify(i18n).message(Locale.ENGLISH, propertyKey, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck" /* default value is the key */);
+ verifyNoMoreInteractions(i18n);
+ }
+
+ @Test
+ public void shouldGetParamDescription() {
+ I18nManager i18n = mock(I18nManager.class);
+ RuleI18nManager ruleI18n = new RuleI18nManager(i18n);
+
+ ruleI18n.getParamDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", "pattern", Locale.ENGLISH);
+
+ String propertyKey = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.param.pattern";
+ verify(i18n).message(Locale.ENGLISH, propertyKey, "" /* default value must be blank */);
+ verifyNoMoreInteractions(i18n);
+ }
+
+ @Test
+ public void shouldGetDescriptionFromFile() {
+ I18nManager i18n = mock(I18nManager.class);
+ RuleI18nManager ruleI18n = new RuleI18nManager(i18n);
+
+ ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH);
+
+ String propertyKeyForName = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name";
+ verify(i18n).messageFromFile(Locale.ENGLISH, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName);
+ verifyNoMoreInteractions(i18n);
+ }
+
+ @Test
+ public void shoudlReturnBlankIfMissingDescription() {
+ I18nManager i18n = mock(I18nManager.class);
+ RuleI18nManager ruleI18n = new RuleI18nManager(i18n);
+
+ assertThat(ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.ENGLISH), Is.is(""));
+ }
+
+ @Test
+ public void shouldUseEnglishIfMissingLocale() {
+ I18nManager i18n = mock(I18nManager.class);
+ RuleI18nManager ruleI18n = new RuleI18nManager(i18n);
+
+ ruleI18n.getDescription("checkstyle", "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck", Locale.FRENCH);
+
+ String propertyKeyForName = "rule.checkstyle.com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.name";
+ verify(i18n).messageFromFile(Locale.FRENCH, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName);
+ verify(i18n).messageFromFile(Locale.ENGLISH, "com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck.html", propertyKeyForName);
+ verifyNoMoreInteractions(i18n);
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle.properties
new file mode 100644
index 00000000000..10fa9295c44
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle.properties
@@ -0,0 +1 @@
+checkstyle.rule1.name=Rule one
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle/ArchitectureRule.html b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle/ArchitectureRule.html
new file mode 100644
index 00000000000..a7cad9049d7
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/checkstyle/ArchitectureRule.html
@@ -0,0 +1 @@
+This is the architecture rule \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/core.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/core.properties
new file mode 100644
index 00000000000..de205d651cb
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/englishPack/org/sonar/i18n/core.properties
@@ -0,0 +1,4 @@
+by=By
+empty=
+with.parameters=First is {0} and second is {1}
+only.in.english=Missing in French bundle \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr.properties
new file mode 100644
index 00000000000..b2fc8f9651f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr.properties
@@ -0,0 +1 @@
+checkstyle.rule1.name=Rule un \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr/ArchitectureRule.html b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr/ArchitectureRule.html
new file mode 100644
index 00000000000..9b12ae071ce
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/checkstyle_fr/ArchitectureRule.html
@@ -0,0 +1 @@
+Règle d'architecture \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/core_fr.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/core_fr.properties
new file mode 100644
index 00000000000..e9ced4039ae
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/frenchPack/org/sonar/i18n/core_fr.properties
@@ -0,0 +1,2 @@
+by=Par
+empty= \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale.properties
new file mode 100644
index 00000000000..a8ea9c0553e
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale.properties
@@ -0,0 +1 @@
+sqale.page=Sqale page title \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale_fr.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale_fr.properties
new file mode 100644
index 00000000000..471d015a11a
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/i18n/sqalePlugin/org/sonar/i18n/sqale_fr.properties
@@ -0,0 +1 @@
+sqale.page=Titre de la page Sqale \ No newline at end of file