【问题标题】:plugin.properties mechanism in eclipse RCPeclipse RCP 中的 plugin.properties 机制
【发布时间】:2009-03-23 12:52:47
【问题描述】:

我的项目包含多个插件,每个插件都包含带有近 20 个翻译的 plugin.properties 文件。 MANIFEST.MF 文件定义了存储外部插件字符串的属性文件的名称。

Bundle-Localization: plugin

我定义的插件名称

%plugin.name

Eclipse 将在运行时在 plugin.properties 文件中搜索“%plugin.name”。

哪个类读出了 MANIFEST.MF Bundle-Localization 条目,在什么时候在“plugin.properties”文件中搜索了以 '%' 后缀开头的字符串?

我想以这种方式查找和修补这些类,我可以首先查看其他目录/文件中的“%plugin.name”标识符。通过这些新机制,我可以在我的产品中添加片段并覆盖“plugin.properties”文件中的单行,而无需更改原始插件。 通过这些机制,我可以通过添加不同的片段来为多个客户创建一个构建过程。包括客户姓名和他们想要更改的特殊字符串的片段。

我想这样做,因为片段机制只是将文件添加到原始插件中。当插件中存在“plugin.properties”文件时,忽略片段“plugin.properties”文件。

更新 1:

方法

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

返回给定语言环境字符串的属性文件的 ResourceBundle。 当有人现在知道我现在如何首先查看片段以获取资源路径时,请发布它。

更新 2:

ManifestLocalization 类中的方法

    private URL findInResolved(String filePath, AbstractBundle bundleHost) {

        URL result = findInBundle(filePath, bundleHost);
        if (result != null)
            return result;
        return findInFragments(filePath, bundleHost);
    }

搜索属性文件并将其缓存。翻译可以从缓存文件中获取。问题是,完整的文件被缓存而不是单个翻译。

一种解决方案是首先读取片段文件,而不是读取捆绑文件。当两个文件都存在时,将它们合并到一个文件中并将新的属性文件写入磁盘。返回新属性文件的 URL,以便缓存新的属性文件。

【问题讨论】:

    标签: java plugins rcp fragment


    【解决方案1】:

    虽然我得到的信息有误……但我遇到了完全相同的问题。该插件未激活两次,我无法访问 Fragments Bundle-Localization 键。

    我希望在 plugin.properties 中提供我所有的语言翻译(我知道这是不受欢迎的,但管理单个文件要容易得多)。

    我(一半)通过使用解决了这个问题

    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())));
    }
    

    只需将我的片段本地化文件名称为“fragment.properties”。

    这不是特别优雅,但很有效。

    顺便说一句,要从片段中获取文件,您需要getResource,似乎片段文件在类路径上,或者仅在使用getResource时才被搜索。

    如果有人有更好的方法,请纠正我。

    一切顺利,

    标记。

    【讨论】:

    • 在哪里可以找到填充方法?还是我必须写?
    • 您能否在答案中添加更多信息。谢谢!
    【解决方案2】:
    /**
     * 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);
        }
    }
    

    【讨论】:

    • getLocalizedEntry bundle.getResource 和 bundle.getEntry 是必需的,因为类路径中的文件(片段中的文件)需要 getResource,而插件根目录(插件本地化)中的文件需要 getEntry。同样,如果有人有更好的答案,请告诉我们,因为我也很感兴趣。
    • 我不清楚如何使用您提供的课程。例如,它是否可以用于以不同于在 bundle.properties 文件中查看的方式来翻译 plugin.xml 文件中声明的透视名称?
    【解决方案3】:

    将片段 plugin.properties 的名称更改为其他名称,例如。片段属性

    在您的片段清单中更改 捆绑本地化:插件 到 捆绑本地化:片段

    您的插件将被激活两次,第一次使用 plugin.properties,第二次使用 fragment.properties。

    【讨论】:

    • 但我不能将相同的键用于不同的值。仅找到并返回插件密钥。当在插件中找到搜索到的键时,片段永远不会打开
    【解决方案4】:

    插件激活由 OSGi 运行时 Equinox 处理。但是,我强烈反对尝试修补那里的任何文件以创建特定行为。 Mark 建议的方法似乎是解决您的问题的更明智的方法。

    【讨论】:

    • 你的意思是马克米勒建议的方式还是马克? :)
    【解决方案5】:

    一种方法是附加一个捆绑侦听器,并侦听捆绑的安装(可能还查看已安装的捆绑),并为每个捆绑生成/提供 - 并安装 - 带有所需属性文件的片段。如果这是在应用程序启动之前完成的,这应该会生效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-30
      • 2011-04-29
      • 2013-01-18
      • 1970-01-01
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多