If you want to make your application available to a large number of people, you won't be able to internataionalize your application. Flex comes with great support for this making this usually pretty complex task really easy.

Instead of defining text constants you simply define these in property files and reference the key values from your application. Depending on the selected language Flex will make sure the correct I18N value is used.

Example without I18N:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
            xmlns:s="library://ns.adobe.com/flex/spark" 
            xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
   <s:Label text="Hello World." />
</s:Application>

The same code using Flex' I18N features:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
            xmlns:s="library://ns.adobe.com/flex/spark" 
            xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
 
	<fx:Metadata>
	    [ResourceBundle("myresources")]
	</fx:Metadata>

    <s:Label text="{resourceManager.getString('myresources','greeting')}" />
</s:Application>

Or programmatically:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
            xmlns:s="library://ns.adobe.com/flex/spark" 
            xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
			initialize="onInitialize(event)">
 
	<fx:Metadata>
	    [ResourceBundle("myresources")]
	</fx:Metadata>

	<fx:Script>
    	<![
			import mx.events.FlexEvent;

			private function onInitialize(event:FlexEvent):void {
				label.text = resourceManager.getString("myresources", "greeting");
			}
		]]>
    </fx:Script>

    <s:Label id="label" />
</s:Application>

The values Flex displays for a given locale, are provided in the project structure in property-files named "myresources.properties" (Has to match the name used in the "ResourceBundle")

If for example you wanted to support English and German, the directory structure would be the follwing:

src
	main
		locales
			en_EN
				myresources.properties
			de_DE
				myresources.properties

The content of both files would be obviously simple (wink)

greeting=Hello World.

Or for German:

greeting=Hallo Welt.

Configuring I18N with Flexmojos

In general there are two ways how the I18N data can be bundled. 

  • Compiles Locales, where the locale data for each supported language is compiled in to the binary
  • Runtime Locales, where the locale data is loaded at runtime with each supported language providing a dedicated language bundle

Both options have their pro and cons

  • Compiled Locales 
    • Pro: 
      • don't require a second call to the server and can be faster
      • eventually better suited for mobile applications in which when switching a language the mobile connection could be a problem
      • code for changing locales is really easy
    • Con: 
      • make the binary bigger
      • are not extensible, so if you decide to add a language to have to re-compile everything
  • Runtime 
    • Pro:
      • highly extensible, you can add new languages any time
      • smaller binaries
    • Con:
      • require a call to the server whenever you switch the language
      • might be slower to ba available as they require another roundtrip to the server
      • the code to handle language changes is a lot more complex

Configuring Compiled Locales

Compiled locales are by far the easiest way to provide locales support:

<plugin>
    <groupId>net.flexmojos.oss</groupId>
    <artifactId>flexmojos-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
        <localesCompiled>
            <locale>en_US</locale>
            <locale>de_DE</locale>
        </localesCompiled>
    </configuration>
</plugin>

Simply by listing the locales you want to have compiled in within a "localesCompiled" tag, takes care of everything.

The following output will be created in the target directory:

target
	classes
	surefire-reports
	fonts.ser
	swf-1.0.0-SNAPSHOT.swf
	swf-1.0.0-SNAPSHOT-configs.xml
	swf-1.0.0-SNAPSHOT-link-report.xml
	swf-1.0.0-SNAPSHOT-size-report.xml

The "copy-flex-resources" goal will create the following output in the war directory:

war-1.0.0-SNAPSHOT
	history
	META-INF
	WEB-INF
	expressInstall.swf
	index.html
	swf-1.0.0-SNAPSHOT.swf
	swfobject.js

See an example project of this at: https://github.com/apache/flex-utilities/tree/develop/maven-flex-plugin/examples/i18n/compiled-locales

Configuring Runtime Locales

The configuration of runtime locales is actually equally simple, it's just a different tag:

<plugin>
    <groupId>net.flexmojos.oss</groupId>
    <artifactId>flexmojos-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
        <localesRuntime>
            <locale>en_US</locale>
            <locale>de_DE</locale>
        </localesRuntime>
    </configuration>
</plugin>

The main difference is the output and how you have to make it available. While with compiled locales a single SWC or SWF will be output, with runtime locales more files are created:

target
	classes
	surefire-reports
	fonts.ser
	swf-1.0.0-SNAPSHOT.rb.swc
	swf-1.0.0-SNAPSHOT.swf
	swf-1.0.0-SNAPSHOT-configs.xml
	swf-1.0.0-SNAPSHOT-de_DE.swf
	swf-1.0.0-SNAPSHOT-de_DE-configs.xml
	swf-1.0.0-SNAPSHOT-de_DE-link-report.xml
	swf-1.0.0-SNAPSHOT-de_DE-size-report.xml
	swf-1.0.0-SNAPSHOT-en_US.swf
	swf-1.0.0-SNAPSHOT-en_US-configs.xml
	swf-1.0.0-SNAPSHOT-en_US-link-report.xml
	swf-1.0.0-SNAPSHOT-en_US-size-report.xml
	swf-1.0.0-SNAPSHOT-link-report.xml
	swf-1.0.0-SNAPSHOT-rb.properties
	swf-1.0.0-SNAPSHOT-size-report.xml

The "copy-flex-resources" goal will create the following output in the war directory:

war-1.0.0-SNAPSHOT
	history
	locales
		swf-1.0.0-SNAPSHOT-de_DE.swf
		swf-1.0.0-SNAPSHOT-en_US.swf
	META-INF
	WEB-INF
	expressInstall.swf
	index.html
	swf-1.0.0-SNAPSHOT.swf
	swfobject.js

Notice the "locales" directory which contains the locale SWFs for each of the runtime locales defined in the SWF project.

See an example project of this at: https://github.com/apache/flex-utilities/tree/develop/maven-flex-plugin/examples/i18n/runtime-locales

Locale Chains

Sometimes when adding a new languages you don't want to add constants for everything. In the example below for example we want to provide resources for US and British English as well as German and Austrian German. Now in general this would cause compile errors, if resource bundles are missing for any of the listed. Unfortunately we don't have resource bundles in en_GB and de_AT for any of the framework libraries. In order to compile we would have to write resource bundles for all of the missing languages.
To avoid having to do this, we can provide a chain of resources. In the case below wherever a resource for en_GB is missing, the en_US version is simply used and wherever an de_AT bundle is missing the de_DE version is used.
 
<plugin>
    <groupId>net.flexmojos.oss</groupId>
    <artifactId>flexmojos-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
        <localesCompiled>
            <locale>en_US</locale>
            <locale>en_GB,en_US</locale>
            <locale>de_DE</locale>
			<locale>de_AT,de_DE</locale>
        </localesCompiled>
    </configuration>
</plugin>

Please note this is not the same mechanism as providing a localization chain with the "resourceManager" inside the application, it just prevents compilation-failures for if we are missing resource bundles for any language.

In order to fully demonstrate the difference between locale-chains in a pom.xml and a locale chain in the application, have a look at the example in: https://github.com/apache/flex-utilities/tree/develop/maven-flex-plugin/examples/i18n/locale-chains

  • No labels