diff --git a/src/main/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPI.java b/src/main/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPI.java
index 4b5754ba5a6182b5460910237ab32c5b148d85c7..188180a813a3535a6446fb221c503645a55512bc 100644
--- a/src/main/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPI.java
+++ b/src/main/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPI.java
@@ -19,8 +19,17 @@
  */
 package org.olat.core.commons.services.help.spi;
 
+import java.util.Date;
 import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.apache.poi.util.IOUtils;
 import org.olat.admin.user.tools.UserTool;
 import org.olat.core.commons.services.help.HelpLinkSPI;
 import org.olat.core.gui.UserRequest;
@@ -29,6 +38,10 @@ import org.olat.core.gui.components.link.ExternalLink;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.helpers.Settings;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.httpclient.HttpClientFactory;
 import org.springframework.stereotype.Service;
 
 /**
@@ -37,12 +50,21 @@ import org.springframework.stereotype.Service;
  * https://confluence.openolat.org/display/OO100DE/OpenOLAT+10+Benutzerhandbuch
  * 
  * Initial date: 07.01.2015<br>
+ * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
 @Service("ooConfluenceLinkHelp")
 public class ConfluenceLinkSPI implements HelpLinkSPI {
-	
+	private static final OLog logger = Tracing.createLoggerFor(ConfluenceLinkSPI.class);
+
+	private static final Map<String, String> spaces = new ConcurrentHashMap<String, String>();
+	private static final Map<String, String> translatedPages = new ConcurrentHashMap<String, String>();
+	private static final Map<String, Date> translatTrials = new ConcurrentHashMap<String, Date>();
+
+	public static final Locale EN_Locale = new Locale("en");
+	public static final Locale DE_Locale = new Locale("de");
+
 	@Override
 	public UserTool getHelpUserTool(WindowControl wControl) {
 		return new ConfluenceUserTool();
@@ -52,21 +74,43 @@ public class ConfluenceLinkSPI implements HelpLinkSPI {
 	public String getURL(Locale locale, String page) {
 		StringBuilder sb = new StringBuilder(64);
 		sb.append("https://confluence.openolat.org/display");
-		String version = Settings.getVersion();
-		sb.append(generateSpace(version, locale));
+		String space = spaces.get(locale.toString());
+		if (space == null) {
+			// Generate space only once per language, version does not change at
+			// runtime
+			String version = Settings.getVersion();
+			space = generateSpace(version, locale);
+			spaces.putIfAbsent(locale.toString(), space);
+		}
+		sb.append(space);
 		if (page != null) {
 			int anchorPos = page.indexOf("#");
 			if (anchorPos != -1) {
-				// page with anchor
-				String realPage = page.substring(0,anchorPos);
-				String anchor = page.substring(anchorPos+1);
-				// confluence has some super-fancy way to addressing pages with anchors 
-				sb.append(realPage.replace(" ", "%20"));			
-				sb.append("#").append(realPage.replace(" ", "")).append("-").append(anchor);
-				
+				// Page with anchor: real page name + anchor
+				String realPage = page.substring(0, anchorPos);
+				String anchor = page.substring(anchorPos + 1);
+
+				// Special case for non-en spaces: CustomWare Redirection Plugin
+				// can not redirect pages with anchors. We need to fix it here
+				// by fetching the page and lookup the redirect path. Ugly, but
+				// we see no other option here.
+				if (!locale.getLanguage().equals(EN_Locale.getLanguage())) {
+					String redirectedPage = getPageFromAlias(getURL(locale, realPage));
+					if (redirectedPage != null) {
+						realPage = redirectedPage;
+					}
+					// else realPage part stays the same - anchor won't work but
+					// at least the right page is loading
+				}
+
+				// Confluence has some super-fancy way to addressing pages with
+				// anchors
+				sb.append(realPage.replaceAll(" ", "%20"));
+				sb.append("#").append(realPage.replaceAll(" ", "")).append("-").append(anchor);
+
 			} else {
-				// page without anchor
-				sb.append(page.replace(" ", "%20"));			
+				// Page without anchor
+				sb.append(page.replaceAll(" ", "%20"));
 			}
 		}
 		return sb.toString();
@@ -84,17 +128,17 @@ public class ConfluenceLinkSPI implements HelpLinkSPI {
 	protected String generateSpace(String version, Locale locale) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("/OO");
-		
+
 		int firstPointIndex = version.indexOf('.');
-		if(firstPointIndex > 0) {
+		if (firstPointIndex > 0) {
 			sb.append(version.substring(0, firstPointIndex));
 			int secondPointIndex = version.indexOf('.', firstPointIndex + 1);
-			if(secondPointIndex > firstPointIndex) {
-				sb.append(version.substring(firstPointIndex+1, secondPointIndex));
-			} else if(firstPointIndex + 1 < version.length()) {
+			if (secondPointIndex > firstPointIndex) {
+				sb.append(version.substring(firstPointIndex + 1, secondPointIndex));
+			} else if (firstPointIndex + 1 < version.length()) {
 				String subVersion = version.substring(firstPointIndex + 1);
 				char[] subVersionArr = subVersion.toCharArray();
-				for(int i=0; i<subVersionArr.length && Character.isDigit(subVersionArr[i]); i++) {
+				for (int i = 0; i < subVersionArr.length && Character.isDigit(subVersionArr[i]); i++) {
 					sb.append(subVersionArr[i]);
 				}
 			} else {
@@ -102,22 +146,22 @@ public class ConfluenceLinkSPI implements HelpLinkSPI {
 			}
 		} else {
 			char[] versionArr = version.toCharArray();
-			for(int i=0; i<versionArr.length && Character.isDigit(versionArr[i]); i++) {
+			for (int i = 0; i < versionArr.length && Character.isDigit(versionArr[i]); i++) {
 				sb.append(versionArr[i]);
 			}
-			//add minor version
+			// add minor version
 			sb.append("0");
 		}
-		
-		if(locale.getLanguage().equals(new Locale("de").getLanguage())) {
+
+		if (locale.getLanguage().equals(DE_Locale.getLanguage())) {
 			sb.append("DE/");
 		} else {
 			sb.append("EN/");
 		}
-		
+
 		return sb.toString();
 	}
-	
+
 	public class ConfluenceUserTool implements UserTool {
 
 		@Override
@@ -139,7 +183,8 @@ public class ConfluenceLinkSPI implements HelpLinkSPI {
 	}
 
 	@Override
-	public Component getHelpPageLink(UserRequest ureq, String title, String tooltip, String iconCSS, String elementCSS, String page) {
+	public Component getHelpPageLink(UserRequest ureq, String title, String tooltip, String iconCSS, String elementCSS,
+			String page) {
 		ExternalLink helpLink = new ExternalLink("topnav.help." + page);
 		helpLink.setName(title);
 		helpLink.setTooltip(tooltip);
@@ -149,5 +194,74 @@ public class ConfluenceLinkSPI implements HelpLinkSPI {
 		helpLink.setUrl(getURL(ureq.getLocale(), page));
 		return helpLink;
 	}
-	
+
+	/**
+	 * Fetch the redirected page name for the given URL. Note that this is
+	 * executed asynchronously, meaning that the first time this method is
+	 * executed for a certain URL it will return null. As soon as the code could
+	 * get the redirection from the confluence server it will return the
+	 * redirected page name instead.
+	 * 
+	 * @param aliasUrl
+	 * @return The translated page name or NULL if not found
+	 */
+	private String getPageFromAlias(String aliasUrl) {
+		if (StringHelper.containsNonWhitespace(aliasUrl)) {
+			String translatedPage = translatedPages.get(aliasUrl);
+			if (translatedPage != null) {
+				return translatedPage;
+			}
+			// Not in cache. Start a background thread to fetch the translated
+			// page from the confluence. Since this can take several seconds, we
+			// exit here with null. Next time the page is loaded the translated
+			// page will be in the cache. 
+			
+			// Do this only once per 30 mins per page. Confluence might be down
+			// or another user already trigger the fetch.
+			Date lastTrial = translatTrials.get(aliasUrl);
+			Date now = new Date();
+			if (lastTrial == null || lastTrial.getTime() < (now.getTime() - (1800 * 1000))) {
+				translatTrials.put(aliasUrl, now);				
+				new Thread() {
+					public void run() {
+						CloseableHttpClient httpClient = HttpClientFactory.getHttpClientInstance(false);
+						try {
+							HttpGet httpMethod = new HttpGet(aliasUrl);
+							httpMethod.setHeader("User-Agent", Settings.getFullVersionInfo());
+							HttpResponse response = httpClient.execute(httpMethod);
+							int httpStatusCode = response.getStatusLine().getStatusCode();
+							// Looking at the HTTP status code tells us whether a
+							// user with the given MSN name exists.
+							if (httpStatusCode == HttpStatus.SC_OK) {
+								String body = EntityUtils.toString(response.getEntity());
+								// Page contains a javascript redirect call, extract
+								// redirect location
+								int locationPos = body.indexOf("location.replace('");
+								if (locationPos == -1) {
+									return;
+								}
+								int endPos = body.indexOf("'", locationPos + 18);
+								if (endPos == -1) {
+									return;
+								}
+								// Remove the path to extract the page name
+								String path = body.substring(locationPos + 18, endPos);
+								String translatedPage = path.substring(path.lastIndexOf("/") + 1);
+								translatedPage = translatedPage.replaceAll("\\+", " ");
+								// We're done. Put to cache for next retrieval
+								translatedPages.putIfAbsent(aliasUrl, translatedPage);
+							}
+						} catch (Exception e) {
+							logger.warn("Error while getting help page from EN alias", e);
+						} finally {
+							IOUtils.closeQuietly(httpClient);
+						}
+					}
+				}.start();
+			}
+			return null;
+
+		}
+		return null;
+	}
 }
diff --git a/src/main/java/org/olat/core/helpers/Settings.java b/src/main/java/org/olat/core/helpers/Settings.java
index 55eef9d59b6e460a970037a1cff65badb498cb99..e674c3a62dafff8c8c0893a7a54832b9f066af8f 100644
--- a/src/main/java/org/olat/core/helpers/Settings.java
+++ b/src/main/java/org/olat/core/helpers/Settings.java
@@ -102,7 +102,7 @@ public class Settings implements Initializable, Destroyable, GenericEventListene
 	// fxdiff: only set build id from build date if none is provided in olat.local.properties!
 	private static void setBuildIdFromBuildDate() {
 		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
-		buildIdentifier = formatter.format(buildDate);
+		buildIdentifier = formatter.format(getBuildDate());
 	}
 	
 	// fxdiff: only set build date 
diff --git a/src/test/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPITest.java b/src/test/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPITest.java
index 71ab0687a7f67f11c5c03a37cb98f64025540fa0..e68772670e17335bf330d0af7c0f1e1ce0077efc 100644
--- a/src/test/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPITest.java
+++ b/src/test/java/org/olat/core/commons/services/help/spi/ConfluenceLinkSPITest.java
@@ -23,6 +23,7 @@ import java.util.Locale;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.olat.core.helpers.SettingsTest;
 
 /**
  * 
@@ -52,4 +53,53 @@ public class ConfluenceLinkSPITest {
 		Assert.assertNotNull(url4);
 		Assert.assertTrue(url4.startsWith("/OO110EN/"));
 	}
+	
+	@Test
+	public void getUrl() {
+		// init settings to set version, required by ConfluenceLinkSPI
+		SettingsTest.createHttpDefaultPortSettings();
+		
+		ConfluenceLinkSPI linkSPI = new ConfluenceLinkSPI();
+		//Data%20Management#DataManagement-qb_import
+		// Standard Case in English
+		String url1 = linkSPI.getURL(Locale.ENGLISH, "Data Management");
+		Assert.assertNotNull(url1);
+		Assert.assertTrue(url1.endsWith("Data%20Management"));
+		
+		// Special handing for anchors in confluence
+		String url2 = linkSPI.getURL(Locale.ENGLISH, "Data Management#qb_import");
+		Assert.assertNotNull(url2);
+		Assert.assertTrue(url2.endsWith("Data%20Management#DataManagement-qb_import"));
+	}
+	
+	@Test
+	public void getTranslatedUrl() {
+		// init settings to set version, required by ConfluenceLinkSPI
+		SettingsTest.createHttpDefaultPortSettings();
+		
+		ConfluenceLinkSPI linkSPI = new ConfluenceLinkSPI();
+		// Standard Case in German - same as in english
+		String url1 = linkSPI.getURL(Locale.GERMAN, "Data Management");
+		Assert.assertNotNull(url1);
+		Assert.assertTrue(url1.endsWith("Data%20Management"));
+		
+		// Special handing for anchors in confluence
+		// Here some magic is needed since the CustomWare Redirection Plugin
+		// plugin we use in Confluence can not redirec links with anchors. The
+		// anchor is deleted.
+		// We have to translate this here
+		// First time it won't return the translated link as it does the translation asynchronously in a separate thread to not block the UI
+		String url2 = linkSPI.getURL(Locale.GERMAN, "Data Management#qb_import");
+		Assert.assertNotNull(url2);
+		Assert.assertTrue(url2.endsWith("Data%20Management#DataManagement-qb_import"));
+		// Wait 5secs and try it again, should be translated now
+		try {
+			Thread.sleep(5000);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		url2 = linkSPI.getURL(Locale.GERMAN, "Data Management#qb_import");
+		Assert.assertNotNull(url2);
+		Assert.assertTrue(url2.endsWith("Handhabung%20der%20Daten#HandhabungderDaten-qb_import"));
+	}
 }
diff --git a/src/test/java/org/olat/core/helpers/SettingsTest.java b/src/test/java/org/olat/core/helpers/SettingsTest.java
index 847ff686e12ec71bcfc14064b4444f462ea24867..f75469f297b12e2bc015b2ed0babbd761dc40876 100644
--- a/src/test/java/org/olat/core/helpers/SettingsTest.java
+++ b/src/test/java/org/olat/core/helpers/SettingsTest.java
@@ -140,27 +140,27 @@ public class SettingsTest {
 		assertEquals("other :port appended.",expectedValue, serverUriScheme);
 	}
 	
-	private Settings createHttpDefaultPortSettings(){
+	public static Settings createHttpDefaultPortSettings(){
 		Settings settings = createCommonSettingsForPortTests(0, SettingsTest.httpDefaultPort);
 		return settings;
 	}
 	
-	private Settings createHttpOtherPortSettings(){
+	public static Settings createHttpOtherPortSettings(){
 		Settings settings = createCommonSettingsForPortTests(0, SettingsTest.httpOtherPort);
 		return settings;
 	}
 	
-	private Settings createHttpsDefaultPortSettings(){
+	public static Settings createHttpsDefaultPortSettings(){
 		Settings settings = createCommonSettingsForPortTests(SettingsTest.httpsDefaultPort, 0);
 		return settings;
 	}
 	
-	private Settings createHttpsOtherPortSettings(){
+	public static Settings createHttpsOtherPortSettings(){
 		Settings settings = createCommonSettingsForPortTests(SettingsTest.httpsOtherPort, 0);
 		return settings;
 	}
 	
-	private Settings createCommonSettingsForPortTests(int securePort, int insecurePort){
+	public static Settings createCommonSettingsForPortTests(int securePort, int insecurePort){
 		Settings settings = new Settings();
 		PersistedProperties persistedPropertiesHttp = new PersistedProperties(new DummyListener());
 		Properties defaultPropertiesHttp = new Properties();
@@ -170,6 +170,12 @@ public class SettingsTest {
 		settings.setServerSecurePort(securePort);
 		settings.setServerInsecurePort(insecurePort);
 		settings.setServerDomainName(SettingsTest.serverFqnd);//${server.domainname}
+		if (settings.getVersion() == null) {
+			// used by ConfluenceLinkSPITest
+			settings.setVersion("10.4");			
+		}
+		// used by ConfluenceLinkSPITest
+		settings.setApplicationName("OpenOLAT-jUnit-runner");
 		WebappHelper.setServletContextPath(SettingsTest.contextPath);
 		return settings;
 	}