Skip to content

Instantly share code, notes, and snippets.

@tschulte
Last active August 29, 2015 13:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschulte/9003397 to your computer and use it in GitHub Desktop.
Save tschulte/9003397 to your computer and use it in GitHub Desktop.
Use custom ResourceBundle.Control in Eclipse TranslationService and MessageFactoryService
package custom.translation;
import java.util.Dictionary;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import kam.text.Utf8ResourceTextControl;
import org.eclipse.e4.core.internal.services.ResourceBundleHelper;
import org.eclipse.osgi.service.localization.BundleLocalization;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.wiring.BundleWiring;
public class CustomBundleLocalization implements BundleLocalization
{
@Override
public ResourceBundle getLocalization(Bundle bundle, String localeString)
{
Dictionary<String, String> headers = bundle.getHeaders();
String bundleName;
if (headers == null || (bundleName = headers.get(Constants.BUNDLE_LOCALIZATION)) == null)
{
return null;
}
// TODO: Use Control that talks to the DB instead
Control control = new Utf8ResourceTextControl();
ClassLoader classLoader = bundle.adapt(BundleWiring.class).getClassLoader();
Locale locale = ResourceBundleHelper.toLocale(localeString);
return ResourceBundle.getBundle(bundleName, locale, classLoader, control);
}
}
package custom.translation;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ResourceBundle;
import javax.inject.Inject;
import org.eclipse.e4.core.internal.services.BundleTranslationProvider;
import org.eclipse.e4.core.internal.services.ServicesActivator;
import org.eclipse.osgi.service.localization.BundleLocalization;
import org.osgi.framework.Bundle;
import org.osgi.service.log.LogService;
import org.osgi.service.packageadmin.PackageAdmin;
public class CustomTranslationService extends BundleTranslationProvider
{
/*
* BEGIN: copied from org.eclipse.e4.core.internal.services.BundleTranslationProvider
*/
/**
* The schema identifier used for Eclipse platform references
*/
final private static String PLATFORM_SCHEMA = "platform"; //$NON-NLS-1$
final private static String PLUGIN_SEGMENT = "/plugin/"; //$NON-NLS-1$
final private static String FRAGMENT_SEGMENT = "/fragment/"; //$NON-NLS-1$
/*
* END: copied from org.eclipse.e4.core.internal.services.BundleTranslationProvider
*/
@Inject
private BundleLocalization localization;
/*
* Partly copied from org.eclipse.e4.core.internal.services.BundleTranslationProvider
*/
@Override
public String translate(String key, String contributorURI)
{
Bundle bundle = getBundle(contributorURI);
if (bundle == null)
return key;
ResourceBundle resourceBundle = localization.getLocalization(bundle, locale);
if (resourceBundle == null)
return key;
return getResourceString(key, resourceBundle);
}
/*
* Copied from org.eclipse.e4.core.internal.services.BundleTranslationProvider
*/
private Bundle getBundle(String contributorURI)
{
if (contributorURI == null)
return null;
URI uri;
try
{
uri = new URI(contributorURI);
}
catch (URISyntaxException e)
{
LogService logService = ServicesActivator.getDefault().getLogService();
if (logService != null)
logService.log(LogService.LOG_ERROR, "Invalid contributor URI: " + contributorURI); //$NON-NLS-1$
return null;
}
if (!PLATFORM_SCHEMA.equals(uri.getScheme()))
return null; // not implemented
String bundleName = uri.getPath();
if (bundleName.startsWith(PLUGIN_SEGMENT))
bundleName = bundleName.substring(PLUGIN_SEGMENT.length());
else if (bundleName.startsWith(FRAGMENT_SEGMENT))
bundleName = bundleName.substring(FRAGMENT_SEGMENT.length());
PackageAdmin packageAdmin = ServicesActivator.getDefault().getPackageAdmin();
Bundle[] bundles = packageAdmin.getBundles(bundleName, null);
if (bundles == null)
return null;
// Return the first bundle that is not installed or uninstalled
for (int i = 0; i < bundles.length; i++)
{
if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0)
{
return bundles[i];
}
}
return null;
}
}
package custom.translation;
import javax.annotation.PostConstruct;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.services.translation.TranslationService;
import org.eclipse.osgi.service.localization.BundleLocalization;
public class CustomTranslationServiceAddon
{
@PostConstruct
public void init(IEclipseContext context)
{
context.set(BundleLocalization.class,
ContextInjectionFactory.make(CalipseBundleLocalization.class, context));
context.set(TranslationService.class,
ContextInjectionFactory.make(CalipseTranslationService.class, context));
}
}
package custom.translation;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Logger;
/**
* This is a special Control for the ResourceBundle, that allows UTF-8 ResourceBundle files. The Idea comes from
* http://stackoverflow.com/questions/4659929/how-to-use-utf-8-in-resource-properties-with-resourcebundle. This Control
* will have no problems with ResourceBundle-files that are ASCII. Files that where converted with the native2ascii-tool
* are no problem. Files in ISO-8859-1 with chars above 127 bit will be auto-detected and the default-java
* implementation will be used.
*
* @author Schulte
*/
public class Utf8ResourceTextControl extends ResourceBundle.Control
{
private static final Logger LOGGER = Logger.getLogger(Utf8ResourceTextControl.class.getName());
private static final String UNACCEPTABLE_UTF8_CHAR = "\ufffd";
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
if (format.equals("java.properties"))
{
/*
* this is a copy of the base implementation just adding UTF-8 loading (and leaving out the
* AccessController.doPrivileged-stuff)
*/
String bundleName = toBundleName(baseName, locale);
final String resourceName = toResourceName(bundleName, "properties");
InputStream stream = getInputStream(loader, reload, resourceName);
if (stream != null)
{
try
{
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
boolean bomConsumed = consumeBom(reader);
if (bomConsumed)
{
LOGGER.warning("skipped UTF-8 BOM in " + resourceName);
}
PropertyResourceBundle bundle = new PropertyResourceBundle(reader);
if (!bomConsumed && containsNonUTF8Chars(bundle))
{
// no UTF-8 file use default implementation
LOGGER.warning(resourceName + " is no UTF-8 file, trying ISO-8859-1 instead");
return super.newBundle(baseName, locale, format, loader, reload);
}
return bundle;
}
finally
{
stream.close();
}
}
}
return super.newBundle(baseName, locale, format, loader, reload);
}
private boolean containsNonUTF8Chars(ResourceBundle bundle)
{
for (String key : bundle.keySet())
{
if (key.contains(UNACCEPTABLE_UTF8_CHAR) || bundle.getString(key).contains(UNACCEPTABLE_UTF8_CHAR))
return true;
}
return false;
}
/**
* If the first char of the BufferedReader is the BOM, this char is consumed.
*
* @return true, if the bom has been consumed
*/
private boolean consumeBom(BufferedReader reader) throws IOException
{
char utf8bom = '\ufeff';
reader.mark(1);
if (reader.read() == utf8bom)
{
return true;
}
else
{
reader.reset();
return false;
}
}
protected InputStream getInputStream(ClassLoader loader, boolean reload, final String resourceName)
throws IOException
{
if (reload)
{
URL url = loader.getResource(resourceName);
if (url != null)
{
URLConnection connection = url.openConnection();
if (connection != null)
{
// Disable caches to get fresh data for
// reloading.
connection.setUseCaches(false);
return connection.getInputStream();
}
}
}
else
{
return loader.getResourceAsStream(resourceName);
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment