The plugin.properties mechanism in eclipse RCP

My project includes several plugins, and each plugin includes a plugin.properties file with almost 20 translations. The MANIFEST.MF file defines the name of the property files in which the strings of the external plug-in are stored.

Bundle-Localization: plugin 

I define the name of the plugin as

 %plugin.name 

Eclipse will look for "% plugin.name" in the plugin.properties file at runtime.

Which class reads the MANIFEST.MF Bundle-Localization record and at what point is the line with the initial suffix "%" in the file "plugin.properties" found?

I want to find and fix this class so that I can first examine some other directories / files for the identifier "% plugin.name". With this new mechanism, I can add fragments to my product and overwrite individual lines in the "plugin.properties" file without changing the original plugin. With this mechanism, I could create a build process for multiple clients by simply adding different fragments. Fragments, including customer names and a special line, that they want to change.

I want to do this because the fragment mechanism only adds files to the source plugin. When the "plugin.properties" file exists in the plugin, the files of the "plugin.properties" fragment are ignored.

UPDATE 1:

Method

 class ManifestLocalization{ ... protected ResourceBundle getResourceBundle(String localeString) { } ... } 

returns the ResourceBundle of the properties file for the given locale string. When someone tells me how I can first examine the fragment to get the path to the resource, send it.

UPDATE 2:

Method in class ManifestLocalization

  private URL findInResolved(String filePath, AbstractBundle bundleHost) { URL result = findInBundle(filePath, bundleHost); if (result != null) return result; return findInFragments(filePath, bundleHost); } 

Search for a properties file and cache it. Translation can be obtained from the cached file. The problem is that the full file is cached, not single translations.

The solution is to read the fragment file first than to read the package file. When both files exist, merge them into one file and write the new properties file to disk. The URL of the new properties file is returned so that the new proposition file can be cached.

+7
java plugins rcp fragment
source share
5 answers

Although I got the information wrong ... I had the same problem. The plugin is not activated twice, and I cannot get to the Bundle-Localization fragment.

I want all my language translations in plugin.properties (I know this is disapproving, but managing a single file is a lot easier).

I (half) solved the problem using

 public void populate(Bundle bundle) { String localisation = (String) bundle.getHeaders().get("Bundle-Localization"); Locale locale = Locale.getDefault(); populate(bundle.getEntry(getFileName(localisation))); populate(bundle.getEntry(getFileName(localisation, locale.getLanguage()))); populate(bundle.getEntry(getFileName(localisation, locale.getLanguage(), locale.getCountry()))); populate(bundle.getResource(getFileName("fragment"))); populate(bundle.getResource(getFileName("fragment", locale.getLanguage()))); populate(bundle.getResource(getFileName("fragment", locale.getLanguage(), locale.getCountry()))); } 

and just call the fragment localization file name "frag.properties".

It is not particularly elegant, but it works.

By the way, getResource is needed to get files from a fragment, it seems that the fragment files are in the class path or are executed only when using getResource.

If anyone has a better approach, please correct me.

All the best

Mark.

+3
source share
 /** * The Hacked NLS (National Language Support) system. * <p> * Singleton. * * @author mima */ public final class HackedNLS { private static final HackedNLS instance = new HackedNLS(); private final Map<String, String> translations; private final Set<String> knownMissing; /** * Create the NLS singleton. */ private HackedNLS() { translations = new HashMap<String, String>(); knownMissing = new HashSet<String>(); } /** * Populates the NLS key/value pairs for the current locale. * <p> * Plugin localization files may have any name as long as it is declared in the Manifest under * the Bundle-Localization key. * <p> * Fragments <b>MUST</b> define their localization using the base name 'fragment'. * This is due to the fact that I have no access to the Bundle-Localization key for the * fragment. * This may change. * * @param bundle The bundle to use for population. */ public void populate(Bundle bundle) { String baseName = (String) bundle.getHeaders().get("Bundle-Localization"); populate(getLocalizedEntry(baseName, bundle)); populate(getLocalizedEntry("fragment", bundle)); } private URL getLocalizedEntry(String baseName, Bundle bundle) { Locale locale = Locale.getDefault(); URL entry = bundle.getEntry(getFileName(baseName, locale.getLanguage(), locale.getCountry())); if (entry == null) { entry = bundle.getResource(getFileName(baseName, locale.getLanguage(), locale.getCountry())); } if (entry == null) { entry = bundle.getEntry(getFileName(baseName, locale.getLanguage())); } if (entry == null) { entry = bundle.getResource(getFileName(baseName, locale.getLanguage())); } if (entry == null) { entry = bundle.getEntry(getFileName(baseName)); } if (entry == null) { entry = bundle.getResource(getFileName(baseName)); } return entry; } private String getFileName(String baseName, String...arguments) { String name = baseName; for (int index = 0; index < arguments.length; index++) { name += "_" + arguments[index]; } return name + ".properties"; } private void populate(URL resourceUrl) { if (resourceUrl != null) { Properties props = new Properties(); InputStream stream = null; try { stream = resourceUrl.openStream(); props.load(stream); } catch (IOException e) { warn("Could not open the resource file " + resourceUrl, e); } finally { try { stream.close(); } catch (IOException e) { warn("Could not close stream for resource file " + resourceUrl, e); } } for (Object key : props.keySet()) { translations.put((String) key, (String) props.get(key)); } } } /** * @param key The key to translate. * @param arguments Array of arguments to format into the translated text. May be empty. * @return The formatted translated string. */ public String getTranslated(String key, Object...arguments) { String translation = translations.get(key); if (translation != null) { if (arguments != null) { translation = MessageFormat.format(translation, arguments); } } else { translation = "!! " + key; if (!knownMissing.contains(key)) { warn("Could not find any translation text for " + key, null); knownMissing.add(key); } } return translation; } private void warn(String string, Throwable cause) { Status status; if (cause == null) { status = new Status( IStatus.ERROR, MiddlewareActivator.PLUGIN_ID, string); } else { status = new Status( IStatus.ERROR, MiddlewareActivator.PLUGIN_ID, string, cause); } MiddlewareActivator.getDefault().getLog().log(status); } /** * @return The NLS instance. */ public static HackedNLS getInstance() { return instance; } /** * @param key The key to translate. * @param arguments Array of arguments to format into the translated text. May be empty. * @return The formatted translated string. */ public static String getText(String key, Object...arguments) { return getInstance().getTranslated(key, arguments); } } 
+1
source share

Change the name of your plugin.properties snippet to something else, for example. fragment.properties

in your fragment manifest, change Package Localization: plugin in bundle Localization: fragment

Your plugin will be activated twice, the first time with plugin.properties, the second with the .properties snippet.

0
source share

Activation of the plugin is handled by the Equinox OSGi environment. However, I would strongly discourage trying to fix any files there to create specific behavior. The proposed path from Mark seems to be a more reasonable approach to your problem.

0
source share

One way is to connect the package listener and listen to the package settings (and, possibly, also look at already installed packages), and for each package generate / provide - and install - a fragment with the files of the required properties. If this is done before the application starts, this should have an effect.

0
source share

All Articles