[info]en_dmitriid


Tigers, and lions, and bears, oh my!


Previous Entry Add to Memories Tell a Friend Next Entry
Coldfusion и XML
happy
[info]dmitriid wrote in [info]en_dmitriid
It is a major pain in the nether regions of the body to work with a language which provides reasonable tools for working with XML and provides no sane tools to modify XML. I'm talking about ColdFusion.

Suppose we have the following piece of XML:
<xml>
    <root>
        <elem>
            <value>Text</value>
        </elem>
    </root>


If we read this piece into a variable called xmlDoc, we have immedate access to the value "Text":

txt = xmlDoc.root.elem.value.xmlText;


Now txt contains, as you may have guessed it, a string, "Text".

Moreover, if we have quite a handful of such elements, we may use an extremely convenient function XMLSearch which accepts (an unspecified subset of) XPath and returns an array of found elements.

A real-world example now. Here's a stripped-down localization file from the project I'm working on::

<?xml version="1.0" encoding="UTF-8"?>
<strings>
	<nspace NAME="global">
		<string CRC="73FB418E8CCD929E219338A555AA7EA4">
			<original>You need to login first</original>
			<localized/>
		</string>
		<string CRC="99DEA78007133396A7B8ED70578AC6AE">
			<original>Login</original>
			<localized/>
		</string>
	</nspace>
	<controller NAME="admin">
		<action NAME="cities">
			<string CRC="FD8459135F9464065B708800B0BDF6D8">
				<original>Add a new city</original>
				<localized/>
			</string>
			<string CRC="F67FDD86A499050E0585BCA9EA023188">
				<original>Add city</original>
				<localized/>
			</string>
			<string CRC="4505DE1F3D02176AA6F1403778C5ADD1">
				<original>Region:</original>
				<localized/>
			</string>
		</action>
	</controller>
</strings>


Searching across this monster of a file is trivial:
searchString = "/strings/controller[@NAME=""admin""]/action[@NAME=""cities""]/string[@CRC=""CRC""]";
elems = XmlSearch(xmlDoc, searchString);

/* If there's only one elems, we return it (simplified example): */

return elems[1].localized.xmlString;
where CRC is calculated using obscure Vodoo rituals :)

Now, the question is: what if we want to change the value oа this element? The procedure required to do that evokes an unbearable desire to nuke Macromedia offices :)

Scanning thorugh the docs reveals that the only standard procedure to change the desired value of a tag involves the use of ArrayAppend function on the array of "value" from the array of "elem" from the array of "root"...

That is... You can find an element, but you cannot manipulate it directly. You have to take the root element. Then you have to take an array of first-level elements anв find the desired one. In that element you once again take an array (of second-level elements) and find the desired one. In that element you take an array of third-level elements and find the desired one. And finally, you take an array of fourth-level elements and find the desired one. If such an element does not exist, you append it to the array of fourth-level elements. Save.

Anyway, here's what it looks like in the end:
// Add a new string
ArrayAppend(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].XmlChildren, XMLElemNew(xmlDoc, "string"));

// Find its position
stringIndex = ArrayLen(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].XmlChildren);

// Change its attribute
xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].xmlAttributes.crc = Hash(t);

// Add "original" to it
ArrayAppend(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].xmlChildren, XMLElemNew(xmlDoc, "original"));

// Find original's position
orgIndex = ArrayLen(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].xmlChildren);

// Add text
xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].original.xmlText = t;

// Add "localized"
ArrayAppend(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].xmlChildren, XMLElemNew(xmlDoc, "localized"));

// Obtain its index
locIndex = ArrayLen(xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].xmlChildren);

// Add text
xmlDoc.strings.xmlChildren[controllersIndex].action[actionsIndex].string[stringIndex].localized.xmlText = '';


:)))

I am only grateful that I had to write this function only once. And it took me the better part of the day...

(Leave a comment)
смотри, блин, а в русской версии написал, что весь день убил!! дааа, трудно успешно вешать лапшу на разных языках, это тебе не rsdn с утра до вечера ворошить... кстати функции они на то и даны, чтобы не писать несколько раз, а коли не вкурсе, то жаль мне тебя, одно слово - webprogrammer


Ты сматри. Обе версии отслеживает, гад :))

ну дык, мало того, еще и со словариком на коленках. нам ... за вредность молоко давать должны!!

Coldfusion project globalization/localization

[info]bogdan_s

2007-01-26 04:16 am (UTC)

From your post I assume you are using XML to store language resources for your Coldfusion projects? How do you use it from code? Do you use any resource managers/editors to manage those resources?

I'm not really familiar with Coldfusion, but I have a project that I need localize. Code is already written (not keeping localization in mind). I was wondering if there are any ready to use tools available.

Re: Coldfusion project globalization/localization

[info]dmitriid

2007-01-26 09:05 am (UTC)

I've decided to youse a gettext-style approach.

I created a function, called T(text[, namespace]) which accepts two arguments - a text to be translated and, optionally, a namespace from which the string should be retrieved.

URLs in my site looks like this:

http://mysite/controller/action

So, if I invoke, say, http://mysite/search/collection, then I invoke an action called "collection" in the controller called "search". This information is used in my T() function as follows.

Function is invoked like this from within /search/collection:
T("Test text")


The XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
    <controller NAME="search">
        <action NAME="collection">
            <string CRC="90D9F5EE7FC9B4655AAFA7484BAC81F8">
                <original>Test text</original>
                <localized/>
            </string>
        </action>
    </controller>


Function T() computes the string's hash. Then it searches for this hash within the specified controller/action. If it finds such a string it returns it's localized value, if it exists. If no localized value exists, it returns the original string. If no controller/action combination exists, it creates the necessary XML, saves it and returns the original text.

Function is invoked like this from within /search/collection:
T(text="Test text", namespace="all_site_texts")


The XML file additionally has this:
	<nspace NAME="all_site_texts">
		<string CRC="73FB418E8CCD929E219338A555AA7EA4">
			<original>Test text</original>
			<localized/>
		</string>
	


The function looks for the string in the specified namespace and creates all the necessary XML if it doesn't find one.

To manage the resultant XML file we've written an onine sort of browser of strings. It simply presents all the strings from actions/namespaces and lets you change the values. Then it simply overwrites the existing XML file with new values.

To do all that we now use http://www.torchbox.com/resources/xml/toolkit.cfm

Hopefully that helps :)

(Leave a comment)

Home