[VRaptor3] Interceptar e modificar o Bundle de mensagens i18n

10 respostas
lscosta

Buenas!

Tenho um sistema multi-idiomas e multi-usuário.

Mesmo os usuários de um mesmo idioma precisam ver termos diferentes, mais adequados ao seu negócio. Isso significa ter mais de uma tradução para uma mesma chave do arquivo de messages_xx.properties.

Gostaria de interceptar bundle e substituir o valor de alguma chave. Tem uma forma simples de fazer isso?

10 Respostas

lscosta

quero dizer, via interceptador, modificar o bundle de mensagens…

Lucas_Cavalcanti

cria uma classe que implementa Localization ou que estende JSTLLocalization… ela é responsável por prover o ResourceBundle…

anote a classe q vc criou com @Component.

vc pode se basear na implementação default http://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/core/JstlLocalization.java

lscosta

Aí Lucas!

Cara, não rolou… não sei se entendi bem…

Para teste, implementei Localization copiando todo o código da JSTLLocalization, e alterei apenas a úlima linha:

de:
return “???” + key + “???”;

para:
return “###” + key + “###”;

Após isso, no meu jsp, coloquei a tag:
<fmt:message key=“WTF”></fmt:message>

A mensagem exibida na tela continua sendo ???WTF???

A classe que criei foi colocada no construtor de um interceptor. Dali mesmo, para teste, chamei o getMessage(“WTF”,"") e obtive o mesmo resultado.

O que faltou?

Lucas_Cavalcanti

vc anotou a classe com @Component?

lscosta

Sim!

Segue o MessagesBundle (mesmo JstlLocalization com a ultima linha editada)

@Component
@RequestScoped
public class MessagesBundle implements Localization {
	
	private static final Logger logger = LoggerFactory.getLogger(MessagesBundle.class);
	
	private static final String DEFAULT_BUNDLE_NAME = "messages";
	
	private final RequestInfo request;
	
	private ResourceBundle bundle;

	public MessagesBundle(RequestInfo request) {
        this.request = request;
		
	}
	
    public ResourceBundle getBundle() {
        if (this.bundle == null) {
            Locale locale = getLocale();
            Object bundle = get(Config.FMT_LOCALIZATION_CONTEXT);
            if (bundle instanceof String || bundle == null) {
				String baseName = (String) bundle;
	            if (baseName == null) {
	                baseName = DEFAULT_BUNDLE_NAME;
	            }
	            try {
					this.bundle = new SafeResourceBundle(ResourceBundle.getBundle(baseName, locale));
				} catch (MissingResourceException e) {
					logger.debug("couldn't find message bundle, creating an empty one");
					this.bundle = new SafeResourceBundle(createEmptyBundle());
				}
            } else if (bundle instanceof LocalizationContext) {
            	LocalizationContext context = (LocalizationContext) bundle;
            	this.bundle = context.getResourceBundle();
            } else {
            	logger.warn("Can't handle bundle: " + bundle + ". Please report this bug. Using an empty bundle");
            	this.bundle = new SafeResourceBundle(createEmptyBundle());
            }
        }
        return this.bundle;
    }

	private ResourceBundle createEmptyBundle() {
		try {
			return new PropertyResourceBundle(new ByteArrayInputStream(new byte[0]));
		} catch (IOException e) {
			logger.warn("It shouldn't happen. Please report this bug", e);
			return null;
		}
	}

    public Locale getLocale() {
        return localeFor(Config.FMT_LOCALE);
    }

    public Locale getFallbackLocale() {
        return localeFor(Config.FMT_FALLBACK_LOCALE);
    }

    private Locale localeFor(String key) {
        Object localeValue = get(key);
        if (localeValue instanceof String) {
            return stringToLocale((String) localeValue);
        }
        if (localeValue != null) {
            return (Locale) localeValue;
        }
        return request.getRequest().getLocale();
    }

    /**
     * Extracted from XStream project, copyright Joe Walnes
     */
    private Locale stringToLocale(String str) {
        int[] underscorePositions = underscorePositions(str);
        if (underscorePositions[0] == -1) {
            return new Locale(str);
        }
        String language = str.substring(0, underscorePositions[0]);
        if (underscorePositions[1] == -1) {
            return new Locale(language, str.substring(underscorePositions[0] + 1));
        }
        return new Locale(language, str.substring(underscorePositions[0] + 1, underscorePositions[1]), str
                .substring(underscorePositions[1] + 1));
    }

    private int[] underscorePositions(String str) {
        int[] result = new int[2];
        for (int i = 0; i < result.length; i++) {
            int last = i == 0 ? 0 : result[i - 1];
            result[i] = str.indexOf('_', last + 1);
        }
        return result;
    }

    private Object get(String key) {
        Object value = Config.get(request.getRequest(), key);
        if (value != null) {
            return value;
        }
        value = Config.get(request.getRequest().getSession(), key);
        if (value != null) {
            return value;
        }
        value = Config.get(request.getServletContext(), key);
        if (value != null) {
            return value;
        }
        return request.getServletContext().getInitParameter(key);
    }

    public String getMessage(String key, String... parameters) {
        try {
            String content = getBundle().getString(key);
            return MessageFormat.format(content, parameters);
        } catch (MissingResourceException e) {
            return "###" + key + "???";
        }
    }

	
}

E no interceptor:

@Intercepts
public class I18nInterceptor implements Interceptor {

	private MessagesBundle messagesBundle;

	public I18nInterceptor(MessagesBundle messagesBundle) {
		this.messagesBundle = messagesBundle;
	}

	public boolean accepts(ResourceMethod arg0) {
		return true;
	}

	public void intercept(InterceptorStack stack, ResourceMethod method, Object resource) throws InterceptionException {
		
		System.out.println(messagesBundle.getMessage("cururuperneta", ""));

		stack.next(method, resource);
	}
}

resultado:

01:15:01,591 DEBUG [InstantiatedInterceptorHandler] Invoking interceptor I18nInterceptor
???cururuperneta???
01:15:01,623 DEBUG [InstantiatedInterceptorHandler] Invoking interceptor InstantiateInterceptor
lscosta

Minha necessidade é complementar esse bundle e/ou editá-lo, de modo que não haja impacto nos jsp’s. Esse código foi só para teste.

Lucas_Cavalcanti

tenta debugar e ver se tá passando por essa classe… não faz sentido não passar por ela, pois vc tah referenciando ela exatamente no construtor do interceptor…

talvez outro cara esteja colocando esses ‘???’ na mensagem (um EmptyResourceBundle ou NullResourceBundle se eu não me engano)

lscosta

Lucas, a tag <fmt:message /> dos jsp’s não está nem aí para a classe que criei… continua carregando a configuração padrão…

Tentei isso no meu CustomProvider, mas tbm não resolveu…

registry.register(Localization.class, MessagesBundle.class);

lscosta

Quanto aos “???”, aparentemente é esta classe que faz isso:

/**
 * A Resource bundle that doesn't throw exception when there is no resource of given key.
 * It only returns ??? + key + ??? when the key doesn't exist.
 *
 * @author Lucas Cavalcanti
 * @since 3.0.2
 */
public class SafeResourceBundle extends ResourceBundle {

	private final ResourceBundle delegate;

	public SafeResourceBundle(ResourceBundle delegate) {
		this.delegate = delegate;
	}
	@Override
	public Enumeration<String> getKeys() {
		return delegate.getKeys();
	}

	@Override
	protected Object handleGetObject(String key) {
		try {
			return delegate.getString(key);
		} catch (MissingResourceException e) {
			return "???" + key + "???";
		}
	}

}
Lucas_Cavalcanti

lscosta:
Lucas, a tag <fmt:message /> dos jsp’s não está nem aí para a classe que criei… continua carregando a configuração padrão…

Tentei isso no meu CustomProvider, mas tbm não resolveu…

registry.register(Localization.class, MessagesBundle.class);

essa classe não vai afetar o fmt:message mesmo… =X
o fmt usa um atributo da sessão pra decidir qual é o bundle… talvez se vc mudar esse atributo vá funcionar… é o atributo com a chave
Config.FMT_LOCALIZATION_CONTEXT (a constante)…

talvez fazer:

session.setAttribute(Config.FMT_LOCALIZATION_CONTEXT, meuBundleLegal);

funcione… ou colocar na session ou colocar no request… ele procura primeiro no request

Criado 25 de fevereiro de 2010
Ultima resposta 26 de fev. de 2010
Respostas 10
Participantes 2