views:

39

answers:

3

I'm currently looking at ways to externalise all text in my flash applications into external xml files (for translation purposes). Basically, all copy in these applications has to be in two languages (English and Welsh).

At the moment, I'm just looking at a structure whereby the name of each TextField instance is the same as a field in the XML, which then has two fields (<english> & <welsh>) inside with the relevant text. The XML will be loaded in and then looped through applying the text to the textfield of the same name in the current language. There will obviously need to be some error-handling with regards making sure the names match up but this can only be done at run-time.

This seems a bit, I dunno.. lose? Is there are way of creating a better structure or system that'll be a bit less reliant on accurately naming various things so they match?

+1  A: 

Edit/

If by "binding" you mean a two way communication , I'm not sure it's possible for the server side to notify the client side without a request...

You could create a simple class that at the minimum contains the TextField you need to populate and a language property . When creating a new instance of this class you'll just need to specify the language for this instance, so when loading the text you can check the value of the language property and load your text accordingly.


public class MyTextField extends Sprite
{
    private var _textfield:TextField;
    private var _language:String;

    private var _text:String;
    private var _xmlURL:String;

    private var loader:URLoader = new URLLoader();

    public function MyTextField(fmt:Object)
    {
       configureListeners();

       _textfield = new TextField();
       _textfield.defaultTextFormat = fmt.format;
       //etc...

       addChild( _textfield );
    }

    public function set xmlURL(value:String):void
    {
        loader.load(value);
     }

    public function set language(value:String):void
    {
        _language = value;
     }

    public function get language():String
    {
       return _language;
    }

    //add here onCompleteHandler , configureListeners and parseXML method
}
PatrickS
I have considered doing this sort of thing but extending TextField instead of wrapping it. Not really what I was asking though, I was more concerned about binding the TextFields to the XML.
dr_tchock
It's a case of composition vs inheritance, both approaches are valid. As for this class, it's just a model you can expand on and add any methods you may need. For instance you could have a xmlURL property , that would bind your class to a specific xml without worrying about name matching.
PatrickS
Ah yeah sorry.. wrong terminology (binding). I do like the xmlURL prop, good idea.
dr_tchock
+1  A: 

you can also use the Text Layout Framework with E4X for a bit more flexibility. the following code sets up a TextLayout object using format and container attributes from an XML file and content from a plain text file, but it shows how to incorporate E4X with TLF so you should be able to easily tailor it to your needs.

caller:

[SWF(width="1000", height="600", frameRate="60", backgroundColor="#330000")]

var layout:XMLTextLayout = new XMLTextLayout("XMLTextLayout.XML");
addChild(layout);

XMLTextLayout class:

package
{
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.formats.TextLayoutFormat;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.edit.SelectionManager;
import flashx.textLayout.edit.SelectionFormat;
import flashx.textLayout.edit.EditingMode;
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.conversion.ConversionType;

public class XMLTextLayout extends Sprite
    {
    //Class Variables
    private var xmlData:XML;

    //Constructor
    public function XMLTextLayout(XMLFileURL:String)
        {
        var xmlLoader:URLLoader = new URLLoader();
        xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
        xmlLoader.addEventListener(Event.COMPLETE, xmlDataHandler);
        xmlLoader.load(new URLRequest(XMLFileURL));
        }

    private function xmlDataHandler(evt:Event):void
        {
        evt.target.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
        evt.target.removeEventListener(Event.COMPLETE, xmlDataHandler);
        xmlData = new XML(evt.target.data);

        var textLoader:URLLoader = new URLLoader();
        textLoader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
        textLoader.addEventListener(Event.COMPLETE, initiateLayout);
        textLoader.load(new URLRequest(xmlData.@textFileURL));
        }

    private function errorHandler(evt:IOErrorEvent):void
        {
        throw(evt.text);
        }

    //Layout Text
    private function initiateLayout(evt:Event):void
        {
        evt.target.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
        evt.target.removeEventListener(Event.COMPLETE, initiateLayout);

        var textFormat:TextLayoutFormat = new TextLayoutFormat();

        for each (var formatProperty:XML in xmlData.format.*)
                {
                if  (textFormat.hasOwnProperty(formatProperty.name()))
                    textFormat[formatProperty.name()] = formatProperty.toString();
                }

        var configuration:Configuration = new Configuration();
        configuration.textFlowInitialFormat = textFormat;
        configuration.focusedSelectionFormat = new SelectionFormat(xmlData.format.@highlightColor);

        var textFlow:TextFlow = TextConverter.importToFlow(evt.target.data, xmlData.@conversionType, configuration);

        for each    (var element:XML in xmlData..container)
                {
                var sprite:Sprite = new Sprite();
                sprite.graphics.drawRect(0, 0, element.width, element.height);
                sprite.x = element.x;
                sprite.y = element.y;
                addChild(sprite);

                textFlow.flowComposer.addController(new ContainerController(sprite, sprite.width, sprite.height));
                }

        textFlow.flowComposer.updateAllControllers();
        textFlow.interactionManager = new SelectionManager();
        textFlow.interactionManager.editingMode == EditingMode.READ_SELECT;
        }
    }
}

XML file:

<?XML version="1.0" encoding="UTF-8"?>

<textLayout textFileURL="Lorem Ipsum.txt" conversionType="plainTextFormat">

    <format highlightColor="0xFF0000">
        <color>0xAA0000</color>
        <fontSize>10.5</fontSize>
        <fontWeight>bold</fontWeight>
        <textAlign>left</textAlign>
        <verticalAlign>middle</verticalAlign>
        <columnCount>4</columnCount>
        <paddingLeft>40</paddingLeft>
        <paddingRight>40</paddingRight>
        <paddingTop>40</paddingTop>
        <paddingBottom>40</paddingBottom>
    </format>

    <container>
        <x>0</x>
        <y>0</y>
        <width>500</width>
        <height>600</height>
    </container>

    <container>
        <x>500</x>
        <y>70</y>
        <width>500</width>
        <height>480</height>
    </container>

</textLayout>

<!--
    Notes:
    1.  Format element names must match the public property names of the TextLayoutFormat class.
    2.  ConversionType and format element values must match the string value of the property's public constant.
-->
TheDarkInI1978
+1  A: 

Are you using FlexBuilder (or FlashBuilder)? If so you might also consider a technique that is generally used for localization, namely resource bundles. For more information, see, for example, here:

http://dispatchevent.org/roger/introduction-to-flex-resource-bundles

Farther down in the comments on that page are described some alternatives for an AS3-only project.

-- David

David
That's incredibly useful - looks like the way to go thanks!
dr_tchock