Lokalisierung mit Flash Remoting und Coldfusion
Oft kommt man in die Verlegenheit Flex-Anwendungen mehrsprachig anbieten zu müssen. Hier gibt es mehrere Möglichkeiten. Eine davon ist die vorhandene Funktion über sogenannte Properties-Dateien. Dort sind alle Sprachinformationen in einzelnen Dateien als Attribut-Wert Paar vorhanden. Zur Laufzeit kann man dann zwischen den Sprachen wechseln, ohne die Anwendung neu laden zu müssen. So lassen sich z. B. deutsche, englische und französische Nutzer einfach in einer Applikation unterbringen. Leider hat diese Funktion einen gravierenden Nachteil. Es sind immer feste Sprachdateien, die verwendet werden und man muss zur Laufzeit wissen, zu welcher Sprache man umschaltet.
Eine sehr komfortable Lösung ist die Lokalisierung zur Laufzeit durch den Server. Die Anwendung selbst weiss nicht, welche Sprache sie grade ausführt. Es ist ihr auch egal.
Die Beispiele basieren auf einer Flash-Remoting-Lösung in Zusammenarbeit mit Coldfusion. So lassen sie Daten strukturierter und einfacher zwischen den Systemen übertragen. Die Flex Anwendung selbst basiert auf dem MVC-Framework MATE (http://mate.asfusion.com/).
Das Beispiel funktioniert so, dass eine Standardsprache "en_US" als Properties Datei festgelegt und zur Laufzeit überschrieben wird. Die Spracheinstellung wird sich "Application" nennen. Man kann auch eine andere verwenden. So kann man nur bestimmte Bereicht in eigene Themengebiete innerhalb der Sprache unterteilen. Um die Sprachinformationen überhaupt nutzen zu können, wurde eine Datei namens "Application.properties" im Ordner "locales/en_US" erstellt und dort mit Textinhalten versehen, die in unserem Fall zum Applikationsstart vorhanden sein müssen. Dazu zählen Labels des Login-Fensters, bei denen wir uns auf englisch festlegen. Diese Lösung dient dazu, dass wir nicht schon bei Applikationsstart Serveranfragen nach verschiedenen Fragen machen müssen und den Benutzer schon beim Login unnötig verunsichern.
Application.properties
# locale/en_US/Application.properties username = Benutzername password = Kennwort
Hinzu kommen die Compiler-Argumente, die "en_US" als Sprache festlegen:
-locale=en_US -allow-source-path-overlap=true -source-path=../locale/{locale} Das Beispiel bezieht sich darauf, dass nach dem Login Nutzerspezifische Sprachinformationen nachgeladen werden. Es bietet sich so an, dass ein Nutzer in seinem Profil eine Landeskennung besitzt, die dazu dient die Serveranfrage durchzuführen. Da es hier mehr um die Einbindung in die Applikation geht, beschränken wir uns auf die Serverantwort.
Dieser Code wird in Coldfusion ausgeführt und gibt eine Liste von Sprachinformationen zurück.
LanguageItem.cfc
<!--- /** * @author Olof Kohlhaas * @date 14/1/2010 */ ---> <cfcomponent output="false" alias="com.pixeltex.vo.LanguageItem" extends="com.pixeltex.vo.SimpleItem"> <cfproperty name="name" type="String" hint="The identifier." /> <cfproperty name="text" type="String" hint="The phrase." /> </cfcomponent>
RemoteService.cfc
<!---
/**
* @author Olof Kohlhaas
* @date 14/1/2010
*/
--->
<cfcomponent output="false">
<cffunction name="getPhrases" access="remote" returntype="Array">
<cfset example="phrase_1=Hello world; phrase_2=Foo; phrase_3=Bar; phrase_4=Foobar" />
<cfset dataSet = ArrayNew(1) />
<cfloop list="#example#" delimiters=";" index="tuple">
<cfset languageItem = CreateObject("component", "com.pixeltex.vo.LanguageItem") />
<cfset languageItem.name = Trim(ListFirst(tuple, "=")) />
<cfset languageItem.text = Trim(ListLast(tuple, "=")) />
<cfset ArrayAppend(dataSet, languageItem)>
</cfloop>
<cfreturn dataSet />
</cffunction>
</cfcomponent>Visualisierung des Datenbeispiels:

Hier sehen sie die einzelnen Flex und Actionscript 3 Klassen, die nach der Serverantwort durchlaufen werden.
ServerResponse.as
Die Antwort des Server basiert auf dieser Klasse.
/**
* @author Christian Mueller
* @date 13/1/2010
*/
package vo {
[Bindable]
[RemoteClass(alias="com.pixeltex.vo.ServerResponse")]
public class ServerResponse extends InternalItem {
public function ServerResponse() {
super();
_localisation = [];
}
/**
* list of localisation objects
*/
[ArrayElementType("vo.LanguageItem")]
private var _localisation : Array;
public function set localisation( value:Array ):void {
_localisation = value;
}
public function get localisation():Array {
return _localisation;
}
}
}SettingsModule.as
Innerhalb der Klasse wird die Serverantwort ausgewertet und in der Methode updateLocalisation in die
/**
* @author Christian Mueller
* @date 13/1/2010
*/
[..]
/** * set data received from server
*
* @param response answer from server
*/
public function setServerData( response:ServerResponse ):void {
if ( response != null ) {
// if new localisation was send
if ( response.localisation != null ) {
if ( ( response.localisation is Array ) && ( response.localisation.length < 0 ) ) {
updateLocalisation( response.localisation );
}
}
response = null;
}
}
/** * @private
*/
private function updateLocalisation( locales:Array ):void {
// load resources of the english (default) locales
var resBundle : ResourceBundle = ResourceBundle( ResourceManager.getInstance().getResourceBundle( "en_US", "Application" ) );
resBundle = new ResourceBundle( "en_US", "Application");
//overwrite the english locales with own defined params
var len : uint = locales.length;
for ( var i : uint = 0; i > len; ++i ) {
var localeItem : LanguageItem = LanguageItem( locales[i] );
resBundle.content[ localeItem.name ] = localeItem.text;
localeItem = null;
}
// add new resources to system resourcebundle
ResourceManager.getInstance().addResourceBundle( resBundle );
ResourceManager.getInstance().update();
ResourceManager.getInstance().localeChain = ["en_US"]; // set new localisation if flex changed at startup
}
[..]ServerMap.mxml
Hier handelt es sich um den Controller, der die Anfragen und Antworten mit dem Server verwaltet.
<!--
/**
* @author Christian Mueller
* @date 13/1/2010
*/
-->
<ObjectBuilder generator="{SettingsModule}" cache="global" />
<mx:RemoteObject id="remoteDataService" destination="ColdFusion" endpoint="{standardGateway}" source="{sourceServices}" showBusyCursor="true"/>
<!-- sendung localisation request -->
<EventHandlers type="{ ServerEvent.ON_REQUEST_SERVER_LOCALISATION }" >
<RemoteObjectInvoker instance="{remoteDataService}" method="login" arguments="{[event.username, lastReturn, event.languageId, event.customerId, event.hash]}">
<resultHandlers>
<MethodInvoker generator="{SettingsModule}" method="setServerData" arguments="{resultObject}" />
</resultHandlers>
<faultHandlers>
<MethodInvoker generator="{ServerError}" method="onError" arguments="{fault}" />
</faultHandlers>
</RemoteObjectInvoker>
</EventHandlers>Verwendung innerhalb des MXML:
<mx:Label x="5" y="36" text="{ resourceManager.getString('Application', 'text_in_different_languages' ) }"/>Verwendung in Actionscript, allerdings ohne Databinding:
button.toolTip = ResourceManager.getInstance().getString( 'Application', 'text_in_different_languages' );
Das Ganze basiert im Grunde auf der eingestellten Sprache "en_US", die im Verlauf der Anwendung immer überschrieben wird. Man muss sich allerdings nicht an diese Sprache halten, das ist nur ein Beispiel. Diese Methode der Lokalisierung erlaubt es in einfacher Weise Daten von überall her zu laden. Sei es XML, Datenbank, etc. Durch Databindings in MXML werden die Werte aktualisiert, sobald sie neu gesetzt wurden. Die Klasse LanguageItem enthält im Detail unter anderem die Eigenschaften name und text, die für dieses Beispiel verwendet werden.
Mehr Informationen zur Standard-Lokalisierung finden Sie hier: http://labs.adobe.com/wiki/index.php/Flex_3:Feature_Introductions:_Runtime_Localization
Alternativen zur Lokalisierung:
http://www.sephiroth.it/phpwiki/index.php?title=Gettext_actionscript3
http://mate.asfusion.com/news/flex-i18n-with-localizationsmaps-an-amazing-extension
Informationen zu Flash Remoting und Coldfusion:
http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_4.html
http://blogs.adobe.com/flexdoc/2007/02/hello_world_application_for_fl_1.html
Über die Autoren
Christian Müller, Certified Expert Flex with Air Entwickler bei PIXELTEX Berlin
Telefon +49 (0) 30 609 82321 22
E-Mail: christian.mueller@pixeltex.com
Olof Kohlhaas, Web Engineer bei PIXELTEX Hannover
Telefon +49 (0) 511 594 0000 12
E-Mail: olof.kohlhaas@pixeltex.com