Last week I’ve decided to translate my QVTo transformation to Portuguese. I’m preparing an experiment that uses this transformation, and my subjects are Brazilian students. So it wouldn’t be appropriate to have English texts in the obtained transformation. It would be a silly independent variable to consider…

So, how can I make my transformation outputs a Portuguese text? A simple solution would be to copy my existent code and just translate it. Easy… But an horrible solution. If I ever change my transformation code, due to this solution I would have to change in 2 places instead of just one. It’s the common “copy-paste in maintenance” problem.

So, how can I create an elegant localization solution? Using the Dictionary type defined in the QVTo Standard Library (defined in the QVT standard), I decided to use the following solution:

  • Create 2 dictionaries: one for English text and one for Portuguese text.
  • Create a “configuration property” so the user can select the language without touching the transformation code.
  • Create a query that returns the message asked, considering the selected dictionary.
  • Create a library for this localization query, separating it to my original code.

Initially I wanted to create the 2 dictionaries as properties, but it was not possible due to this bug (I think), already corrected for the next version of QVTo. Therefore, the solution is not so elegant for now, but ok. For the query, it would be interesting to have parameters in the localization text, for instance: “Something happened in {0}”, where the “{0}” would match the first String passed as parameter, “{1}” match the second String etc (considering the Eclipse standard for parameter in localization texts). Because QVTo doesn’t accept variadict queries, I had to use an OrderedSet to pass the parameters (although I found my code needs at most 2 parameters). To simplify my code, I will overload my localization query to have a query with no parameters and another with the OrderedSet. That’s it. Here is the code I’ve created:

library Localization;
configuration property locale : String;

query MESSAGE(message:String) : String {
	return MESSAGE(message, OrderedSet{});
}

query MESSAGE(message:String, parameters:OrderedSet(String))
		: String {
	// There is no way to have a Dict as a property!
	var EN_US_MESSAGES := Dict {
		"COMPLETED_SUCCESSFULLY"="--nTransformation
			completed successfully.",
		"COMPLETED_WITH_ERROR"="--nTransformation
			ended with errors."
		// etc.
	};

	var PT_BR_MESSAGES: Dict(String, String) = Dict {
		"COMPLETED_SUCCESSFULLY"="--nTransformação
			completada com sucesso",
		"COMPLETED_WITH_ERROR"="--nTransformation
			completada com erros. "
		// etc.
	};

	var fullMessage : String =
		if ("EN_US".equalsIgnoreCase(locale)) then
			EN_US_MESSAGES->get(message)
		else if ("PT_BR".equalsIgnoreCase(locale)) then
			PT_BR_MESSAGES->get(message)
		else
			EN_US_MESSAGES->get(message)
		endif endif;

	if (not (fullMessage.oclIsInvalid() or
			fullMessage.oclIsUndefined())) then {
		var i : Integer = 0;
		while(i < parameters->size()) {
			fullMessage := fullMessage.replace("{" +
				 i.repr() + "}",
				parameters->at(i + 1));
			i := i + 1; // i++
		};
	} endif;
	return fullMessage;
}

An interesting detail: OrderedSets in OCL (and QVT) starts at 1, and not at 0 as normally expected. And here is an example of its use:

log(MESSAGE("COMPLETED_SUCCESSFULLY"));

Don’t forget to do the import – for instance: “import br.com.levysiqueira.Localization;”.

A future improvement in this code is to raise an error when there is no message. For now I use the default “<null>” message.

That’s it. If someone has a suggestion about how to improve my code, I will be happy to listen.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *