java eclipse RCP中的plugin.properties机制
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/673265/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
plugin.properties mechanism in eclipse RCP
提问by Markus Lausberg
My project includes multiple plugins and every plugin includes the plugin.properties file with near to 20 translations. The MANIFEST.MF file defines the name of the properties files where the external plugin strings are stored.
我的项目包含多个插件,每个插件都包含带有近 20 个翻译的 plugin.properties 文件。MANIFEST.MF 文件定义了存储外部插件字符串的属性文件的名称。
Bundle-Localization: plugin
The name of the plugin i define like
我定义的插件名称
%plugin.name
Eclipse will search the "%plugin.name" in the plugin.properties file at runtime.
Eclipse 将在运行时搜索 plugin.properties 文件中的“%plugin.name”。
Which class read out the MANIFEST.MF Bundle-Localization entry and at which point is the string with the starting '%' suffix is searched in the "plugin.properties" file?
哪个类读出 MANIFEST.MF Bundle-Localization 条目,此时在“plugin.properties”文件中搜索带有起始 '%' 后缀的字符串?
I want to find and patch these class in that way, that i can first look into some other directories/files for the "%plugin.name" identifier. With these new mechanism i can add fragments to my product and overwrite single lines in a "plugin.properties" file without changing the original plugin. With these mechanism i could create a build process for multiple customers just by adding different fragments. The fragments including the customer names and special string they want to change.
我想以这种方式找到并修补这些类,我可以首先查看其他一些目录/文件以获取“%plugin.name”标识符。使用这些新机制,我可以向我的产品添加片段并覆盖“plugin.properties”文件中的单行,而无需更改原始插件。通过这些机制,我可以通过添加不同的片段为多个客户创建一个构建过程。包括客户姓名和他们想要更改的特殊字符串的片段。
I want to do it that way, because the fragment mechanism only add files to the original plugin. When the "plugin.properties" file is existing in the plugin, the fragment "plugin.properties" files are ignored.
我想这样做,因为片段机制只向原始插件添加文件。当插件中存在“plugin.properties”文件时,片段“plugin.properties”文件将被忽略。
UPDATE 1:
更新 1:
The method
方法
class ManifestLocalization{
...
protected ResourceBundle getResourceBundle(String localeString) {
}
...
}
returns the ResourceBundle of the properties file for the given locale string. When somebody nows how i can now first look into the fragment to get the resource path please post it.
返回给定语言环境字符串的属性文件的 ResourceBundle。当有人知道我现在如何首先查看片段以获取资源路径时,请发布它。
UPDATE 2:
更新 2:
The method in class ManifestLocalization
ManifestLocalization 类中的方法
private URL findInResolved(String filePath, AbstractBundle bundleHost) {
URL result = findInBundle(filePath, bundleHost);
if (result != null)
return result;
return findInFragments(filePath, bundleHost);
}
Searchs for the properties file and cache it. The translations can than get from the cached file. The problem is, that the complete file is cached and not single translations.
搜索属性文件并缓存它。翻译可以从缓存文件中获取。问题是,完整的文件被缓存,而不是单个翻译。
A solution would be to first read the fragment file, than read the bundle file. When both files are existing merge them into one file and write the new properties file to the disk. The URL of the new properties file returns, so that the new propetries file can cached.
一种解决方案是先读取片段文件,然后再读取捆绑文件。当两个文件都存在时,将它们合并为一个文件并将新的属性文件写入磁盘。返回新属性文件的 URL,以便可以缓存新属性文件。
采纳答案by Mark Miller
Although I got the information wrong ... I had exactly the same problem. The plugin is not activated twice and I cannot get to the fragments Bundle-Localization key.
虽然我弄错了信息......我也遇到了完全相同的问题。该插件未激活两次,我无法获得片段 Bundle-Localization 密钥。
I want all my language translations in the plugin.properties (I know this is frowned upon but it is much easier to manage a single file).
我希望在 plugin.properties 中进行所有语言翻译(我知道这令人不悦,但管理单个文件要容易得多)。
I (half)solved the problem by 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 simply call my fragment localisation file name 'fragment.properties'.
并简单地调用我的片段本地化文件名“fragment.properties”。
This is not particularly elegant, but it works.
这不是特别优雅,但它有效。
By the way, to get files from the fragment you need the getResource, it seems that fragment files are on the classpath, or are only searched when using getResource.
顺便说一句,要从片段中获取文件,您需要 getResource,片段文件似乎在类路径上,或者仅在使用 getResource 时搜索。
If someone has a better approach, please correct me.
如果有人有更好的方法,请纠正我。
All the best,
一切顺利,
Mark.
标记。
回答by Mark Miller
/**
* 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);
}
}
回答by Peteter
One way is to attach a bundle listener, and listen for installations of bundles (and perhaps also look at already installed bundles) and for each bundle generate/provide - and install - a fragment with the wanted property files. If this is done before the application starts up, this should have effect.
一种方法是附加一个包侦听器,并侦听包的安装(也许还可以查看已安装的包),并为每个包生成/提供 - 并安装 - 一个带有所需属性文件的片段。如果这是在应用程序启动之前完成的,这应该有效。
回答by Peteter
Change the name of your fragment plugin.properties to something else eg. fragment.properties
将您的片段 plugin.properties 的名称更改为其他名称,例如。片段属性
in your fragment manifest change the Bundle-Localization: plugin to Bundle-Localization: fragment
在您的片段清单中,将 Bundle-Localization: plugin 更改为 Bundle-Localization: fragment
Your plugin will be activated twice, the first time using the plugin.properties, the second using the fragment.properties.
您的插件将被激活两次,第一次使用 plugin.properties,第二次使用 fragment.properties。
回答by lothar
Plugin activation is handled by the OSGi runtime Equinox. However I would strongly discourage trying to patch any files there to create specific behavior. The suggested way from Mark seems a much more sane approach to your problem.
插件激活由 OSGi 运行时 Equinox 处理。但是,我强烈不鼓励尝试修补那里的任何文件以创建特定行为。Mark 建议的方法似乎是解决您问题的更明智的方法。

