简体   繁体   English

eclipse RCP 中的 plugin.properties 机制

[英]plugin.properties mechanism in eclipse RCP

My project includes multiple plugins and every plugin includes the plugin.properties file with near to 20 translations.我的项目包含多个插件,每个插件都包含带有近 20 个翻译的 plugin.properties 文件。 The MANIFEST.MF file defines the name of the properties files where the external plugin strings are stored. 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?哪个 class 读出了 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.我想以这种方式查找并修补这些 class,我可以先查看其他一些目录/文件中的“%plugin.name”标识符。 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.通过这些新机制,我可以在我的产品中添加片段并覆盖“plugin.properties”文件中的单行,而无需更改原始插件。 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.包括客户姓名和他们想要更改的特殊字符串的片段。

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.返回给定语言环境字符串的属性文件的 ResourceBundle。 When somebody nows how i can now first look into the fragment to get the resource path please post it.当有人知道我现在如何首先查看片段以获取资源路径时,请发布它。

UPDATE 2:更新 2:

The method in class ManifestLocalization class 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返回,这样新的属性文件就可以缓存了。

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.该插件未激活两次,我无法访问 Fragments 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.标记。

/**
 * 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);
    }
}

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.如果这是在应用程序启动之前完成的,这应该会生效。

Change the name of your fragment plugin.properties to something else eg.将片段 plugin.properties 的名称更改为其他名称,例如。 fragment.properties片段属性

in your fragment manifest change the Bundle-Localization: plugin to Bundle-Localization: fragment在您的片段清单中将 Bundle-Localization: 插件更改为 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。

Plugin activation is handled by the OSGi runtime Equinox.插件激活由 OSGi 运行时 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.马克建议的方法似乎是解决您的问题的更明智的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM