plugin.properties mecanismo no RCP eclipse
Pergunta
Meu projeto inclui vários plugins e cada plugin inclui o arquivo plugin.properties com perto de 20 traduções. O arquivo MANIFEST.MF define o nome dos arquivos de propriedades onde as cordas de plugins externos são armazenados.
Bundle-Localization: plugin
O nome do plugin i definir como
%plugin.name
Eclipse irá procurar o "% plugin.name" no arquivo plugin.properties em tempo de execução.
Qual classe ler a entrada MANIFEST.MF Bundle-Localization e em que ponto é a string com a iniciar '%' sufixo é pesquisado no arquivo "plugin.properties"?
Eu quero encontrar e corrigir estes classe dessa forma, que eu possa primeiro olhar para alguns outros diretórios / arquivos para o identificador "% plugin.name". Com estes novo mecanismo posso adicionar fragmentos para meu produto e linhas simples sobrescrever em um arquivo "plugin.properties" sem alterar o plugin originais. Com estes mecanismo i poderia criar um processo de compilação de vários clientes apenas adicionando diferentes fragmentos. Os fragmentos incluindo os nomes de clientes e corda especial que deseja alterar.
Eu quero fazê-lo dessa maneira, porque o mecanismo fragmento só adicionar arquivos para o plugin originais. Quando o "plugin.properties" arquivo é existente no plugin, os "plugin.properties" fragmentos de arquivos são ignorados.
UPDATE 1:
O método
class ManifestLocalization{
...
protected ResourceBundle getResourceBundle(String localeString) {
}
...
}
retorna o ResourceBundle do arquivo de propriedades para a string dada localidade. Quando agoras alguém como eu pode agora olhar primeiro para o fragmento para obter o caminho de recursos por favor poste.
UPDATE 2:
O método em ManifestLocalization classe
private URL findInResolved(String filePath, AbstractBundle bundleHost) {
URL result = findInBundle(filePath, bundleHost);
if (result != null)
return result;
return findInFragments(filePath, bundleHost);
}
Pesquisas para o arquivo de propriedades e cache-lo. As traduções podem que começa a partir do arquivo em cache. O problema é que o arquivo completo é armazenado em cache e não traduções individuais.
Uma solução seria a primeira ler o arquivo de fragmento, de ler o arquivo de pacote. Quando ambos os arquivos são existente fundi-los em um arquivo e gravar o arquivo novas propriedades para o disco. O URL dos novos retornos propriedades do arquivo, para que o novo arquivo propetries pode cache.
Solução
Embora eu tenho o errado informações ... Eu tinha exatamente o mesmo problema. O plug-in não é ativado duas vezes e eu não posso chegar aos fragmentos chave Bundle-Localization.
Eu quero que todos os meus traduções de idiomas nos plugin.properties (eu sei que isso é desaprovado, mas é muito mais fácil gerenciar um único arquivo).
I (meia) resolveu o problema usando
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())));
}
e simplesmente chamar meus fragmento de nome de arquivo de localização 'fragment.properties'.
Isto não é particularmente elegante, mas funciona.
A propósito, para obter arquivos do fragmento é necessário o getResource, parece que os arquivos de fragmento estão no classpath, ou só são procurados quando usando getResource.
Se alguém tem uma abordagem melhor, por favor me corrijam.
Todo o melhor,
Mark.
Outras dicas
/**
* 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);
}
}
Alterar o nome de seus plugin.properties fragmento para outra coisa, por exemplo. fragment.properties
no seu fragmento de manifestar a mudança do Bundle-Localization: plugin de para Bundle-Localization: fragmento
Seu plugin será ativado duas vezes, a primeira vez usando os plugin.properties, o segundo usando os fragment.properties.
Plugin é tratado pelo runtime OSGi Equinox. No entanto gostaria fortemente desencorajar a tentar corrigir quaisquer arquivos de lá para criar um comportamento específico. A forma sugerida de Mark parece ser uma abordagem muito mais sensato para o seu problema.
É uma maneira de anexar um ouvinte pacote, e ouvir para instalações de pacotes (e talvez também olhar para pacotes já instalados) e para cada pacote gerar / fornecer - e instalar - um fragmento com os arquivos de propriedade queria. Se isso for feito antes do aplicativo é iniciado, isso deve ter efeito.