Using TranslateX with EPiServer 6 R2

In a recent project, I had to install the TranslateX module into an EPiServer 6R2 site. TranslateX is an open source EPiServer module that allows you to export pages in XLIFF format for translation services. The problem is that the original module was created for EPiServer 4 and has been since updated to work with version 6.0, but I was unable to find any resources or help to get it installed in 6R2 or 7. After some trial and error, I came up with these steps.

  1. Download EPiServer CMS 6.0 from EPiServer World and install it. Don’t worry — even though it’s an older version, it shouldn’t cause any problems with your R2 installation.
  2. Download the install package for the 6.0 version of the module.
  3. Run EPiServer Deployment Center (located at C:\Program Files (x86)\EPiServer\Shared\Install\EPiServerInstall.exe by default).
  4. On the All Actions tab, run Installed Products > EPiServer CMS > Version 6.0.x > Modules > EPiServer.Research.TranslateX.Installer.6.
  5. The default options for the first step of the installer should be fine. On step 2, check the “Show All Sites” check box, and then select the site where you will install TranslateX. (Note: You will receive a compatibility warning when you check the “Show All Sites” check box, but it doesn’t appear to be a problem.)
  6. Finish the installation.

Now, you should have it successfully installed. However, there’s more to do before it’ll work. First, you will need to create a folder for TranslateX to save temporary files, and EPiServer will need write access to the folder. You can name the folder “translationtemp” or something similar.

Open your Web.config file and look in the AppSettings section for a key named “xliffworkpath”. You’ll need to update this value with the location of your “translationtemp” directory.

At this point, you can now create translation projects for your pages by navigating to a page in the CMS and clicking the “Translation” tab. But when you try to run the project (Admin Mode > Admin tab > Scheduled Jobs > Translation scheduler service > Start Manually), you’ll probably notice an error on the “History” tab and you will never receive an e-mail with the translation. That’s because you will need to tell EPiServer which languages are available for translation through an XML file.

In the root folder of your EPiServer installation, there should be a “lang” folder. Create an XML file called “translationlangs.xml” and place it in that folder. Here’s a sample of the content to include in the XML file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<languages>
	<language name="English" id="en">
		<translationlanguages>
		<en>en</en>
		<en-GB>en-gb</en-GB>
		<es>es</es>
		</translationlanguages>
	</language>

	<language name="British" id="en-GB">
		<translationlanguages>
			<en>en</en>
			<en-GB>en-gb</en-GB>
			<es>es</es>
		</translationlanguages>
	</language>

	<language name="Spanish" id="es">
		<translationlanguages>
			<en>en</en>
			<en-GB>en-gb</en-GB>
			<es>es</es>
		</translationlanguages>
	</language>
</languages>

Make sure to update the XML to fit your own language needs. As long as your mail server is working, you shouldn’t need anything else. If the “History” tab on the translation scheduler service says, “OK,” after starting the service, you’re good to go.

I haven’t tried installing TranslateX in EPiServer 7, but let me know if the steps above were any help.

Simple jQuery Autosubmit Plugin

One issue that I have with ASP.NET is how often it injects inline styles and scripts into your pages. One example is the “AutoPostBack” attribute that you can add to your DropDownList and RadioButtonList server controls. When this attribute is added, an inline “onchange” event handler will be added to your markup along with some other scripts. Ideally, this functionality should be handled in an external JS file, and it’s really not that hard to do.

Here’s a quick jQuery plugin that will accomplish that without having to rely on .NET’s “AutoPostBack” feature.

/// <summary>
/// The parent form of the provided element(s) will be submitted when its/their value changes.
/// </summary>
/// <param name="options.ignore">An ignore option is available (array) to prevent the form from submitting when certain values are chosen. By default, options with an empty string value are ignored.</param>
/// <param name="options.trigger">If trigger is omitted, the parent form will be submitted. Otherwise, a click event will be triggered on the provided jQuery object.</param>
(function(factory) {
	if (typeof define === 'function' && define.amd) {
		// Register as an anonymous module
		define(['jquery'], factory);
	} else {
		// Browser globals
		factory(jQuery);
	}
}(function ($) {
	$.fn.autosubmit = function (options) {
		var settings = $.extend({
			'ignore': ['']
		}, options);

		this.change(function () {
			var $this = $(this);

			if ($.inArray($this.val(), settings.ignore) === -1) {
				if (typeof settings.trigger !== 'undefined') {
					settings.trigger.click();
				} else {
					$this.closest('form').submit();
				}
			}
		})

		return this;
	}
}));

Using the plugin is very easy. Create your DropDownList as usual, but give it a CssClass attribute so you can reference it easily:

<asp:DropDownList ID="myDropDownList" CssClass="auto-drop-down" runat="server">…</asp:DropDownList>

Include jQuery and the plugin file in your page, then write some JS similar to the following:

$('.auto-drop-down').autosubmit();

The plugin also allows you to specify certain values that will not autosubmit when chosen. For example, you may have a drop-down with a “Select an option” choice, in which case, you wouldn’t want to submit the form unless a different choice was made. Here’s how to ignore certain values:

$('.auto-drop-down').autosubmit({'ignore': ['', 'n/a', 'empty']});

One more thing to point out — be sure to include a submit button even if the autosubmit feature makes it unnecessary. If someone isn’t using JavaScript, they should still be able to use the drop-down on your page. Another line of JS for hiding the submit button is trivial:

$('#my-button').hide();

Strongly Typed Ektron Smart Forms

Unfortunately, Ektron still doesn’t allow for strongly typed access to smart forms out of the box. However, that doesn’t mean you’re stuck using XSLT or parsing the XML. Some time ago, Bill Cava hosted a webinar titled Ektron Content Types that explained how to gain strongly typed access to smart forms by serializing the XML into a .NET class. It’s a great webinar, but a few things were left out, so I figured I’d include some instruction in a little more detail (mainly for my own reference).

Step 1: Getting the Smart Form XSD

Assuming you’ve already created your smart form, the next step is to get its XSD, or XML Schema Definition. To do this, click the XSD icon in the smart form designer toolbar. Now, copy the content to a plain text file, and save it as MySchema.xsd (obviously, replace MySchema with something relevant to your smart form — this will become the .NET class name also).

Step 2: Creating the .NET Class from Your XSD

Visual Studio comes with an application called xsd.exe, which you can use to automatically generate your .NET class from the XSD. If you don’t have or can’t find xsd.exe, there are online services that can do this as well (e.g. http://www.bware.biz/DotNet/Tools/CodeXS/WebClient/GenerateInput.aspx). Assuming you’re using xsd.exe, let’s create a .bat file and add the following to it (if you aren’t familiar with batch files, just open Notepad, type the code below and save as xsd.bat):

SET ROOT=C:\"Program Files (x86)"\"Microsoft SDKs"\Windows\"v8.0A"\bin\"NETFX 4.0 Tools"\
SET XSDPATH=C:\Users\username\Desktop\
SET XSDFILE=%1
SET NAMESPACE=%2

%root%xsd %XSDPATH%%XSDFILE% /classes /language:CS /namespace:%NAMESPACE% /outputdir:%XSDPATH%

Make sure to update XSDPATH to the path where you saved your XSD file from the last step (or move your file to the desktop). Also the ROOT path may be different for you depending on where your xsd.exe is located. Update it accordingly.

Now, open a command prompt (Windows key + R, type cmd, and press Enter), type the following, and press Enter:

cd C:\Users\username\Desktop

Note: If you saved your xsd.bat file somewhere other than the desktop, cd into that directory instead of the above.

Now type the following, making sure to replace “MySchema.xsd” with the name of your XSD file and “Some.Namespace” with your own custom namespace:

xsd.bat MySchema.xsd Some.Namespace

After the commands from the batch file are done, you should have a new .cs file in the same folder as your XSD file.

Step 3: Update the .NET Class

Although xsd.exe creates your .NET class for you, there are some changes you’ll need to make that weren’t mentioned in the Ektron webinar. First, you’ll want to change the following, which should appear near the top of the .cs file:

[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
    public partial class root {
        ...
    }

to this:

[System.Xml.Serialization.XmlRootAttribute(Namespace="", ElementName="root", IsNullable=false)]
    public partial class MySchema {
        ...
    }

Notice that we added ElementName="root", and we changed the class name from “root” to one better fitting our class. If our smart form was named “Book,” for example, we’d have chosen “Book” for the name of the class.

Now move the .cs file to your website’s AppCode folder, or wherever it fits best in the architecture of your website.

Step 4: Using the ContentTypeManager<T> and ContentType<T> Classes

Download Ektron’s source code for the ContentTypeManager and ContentType classes, and copy the ContentType.cs file to your AppCode directory (or wherever applicable). The webinar does a good job of explaining how to use ContentTypeManager and ContentTypes, so I won’t go into detail with their usage. Here’s some sample code to get you started:

ASPX

<asp:Repeater ID="MyRepeater" runat="server">
    <ItemTemplate>
        <p><%# Eval("SmartForm.Property") %></p>
    </ItemTemplate>
</asp:Repeater>

ASPX.CS

long folderId = 32; // Folder containing our smart form content
ContentTypeManager<MySchema> mgr = new ContentTypeManager<MySchema>();
Criteria<ContentProperty> criteria = new Criteria<ContentProperty>();
criteria.AddFilter(ContentProperty.FolderId, CriteriaFilterOperator.EqualTo, folderId);
List<ContentType<MySchema>> items = mgr.GetList(criteria);

MyRepeater.DataSource = items;
MyRepeater.DataBind();

Step 5: Updating the .NET Class for Rich Text and Other Complex Types

Another problem, not mentioned in the webinar, that you’re likely to run into is rich text fields and other smart form data types that don’t map to a standard .NET type. I’ll explain how to handle rich text, and figuring out the others should be self explanatory afterward.

Basically, what happens is that your .NET class will contain new types for anything that it doesn’t understand (i.e. anything other than String, DateTime, Int32, etc.). One of these types is “rich”, which it creates for rich text boxes. Search for the following in your MySchema.cs file:

public partial class rich {
    ...
}

What we want is for the “rich” type to return a string representation of the nodes it contains. To do this, we need to override its ToString() method. Add the following directives at the top…

using System.Text;
using System.Xml;

and add the following after the “rich” class’s field declarations:

public override string ToString()
{
    StringBuilder sb = new StringBuilder();

    foreach (XmlNode item in this.anyField)
    {
        sb.Append(item.OuterXml);
    }

    return sb.ToString();
}

Use the same approach for other Ektron smart form types that don’t match up to a .NET type.

At this point, you should have everything you need. Enjoy!