Supertext Home
Chief of the System Blog

Knockoutjs – A custom binding for grouped <select> elements

January 27th, 2014 by

While building our new site we converted a lot of server-side ASP.NET WebForms code to client-side MVVM scripting, and for these purposes we used Knockoutjs. However, one of the sticking points – something that we almost thought that we’d have to do without – was a <select> element which made use of <optgroup> child elements.

There’s link at the bottom of the page which will download a zipped folder containing the custom binding as well as an HTML file showing some simple implementation examples.

Just to be clear, here’s a plain <select> element:

2014-01-24_1548

And here’s a <select> which contains <optgroup> elements:

2014-01-24_1548

The markup for this is quite simple:


<select>
	<option>- Please select -</option>
	<optgroup label="Advertising">
		<option value="1">Advertisement</option>
		<option value="4">Brochure</option>
	</optgroup>
	<optgroup label="Business">
		<option value="45">Annual report / company report</option>
		<option value="8">Company profile</option>
	</optgroup>
</select>

So what we’re hoping to achieve here is to provide Knockout with a custom binding which will enumerate a collection of collections, rendering <optgroup> elements for items in the outer collection and <option> elements for items in the inner collection.

A simple example

If we simply want to bind an observableArray to a <select> element then the Knockout website already has clear examples of how to achieve this, so we won’t repeat the foundation stuff here.

And if we were prepared to forego having the default “Please select” <option> element then we could simply declare a foreach binding on the <select> to take care of the <optgroup> elements, and a further foreach binding on the template <optgroup> element to take care of the inner <option> elements.

In which case, something like this would suffice.


<select data-bind="foreach: TextTypeGroups">
	<optgroup data-bind="attr: { label: Label }, foreach: Options">
		<option data-bind="text: Text, value: Id"></option>
	</optgroup>
</select>

But because we do wish to have a default “Please select” <option> element we now have the difficulty of where to enumerate the <optgroup> elements.

One apparently simple method of achieving this is to use what Knockout calls virtual elements. Let’s look at an example:


<select>
	<option value="">- Please select -</option>
	<!-- ko foreach: TextTypeGroups -->
		<optgroup data-bind="attr: { label: Label }, foreach: Options">
			<option data-bind="text: Text, value: Id"></option>
		</optgroup>
	<!-- /ko -->
</select>

The above example assumes that you have an observableArray named TextTypeGroups and that each element in TextTypeGroups has an observableArray named Options.

However, the problem with the above example is that it fails for IE8 and IE9. These browsers object to the HTML comment tags ( <!– … –> ) inside the <select> element, so they strip out these tags. Consequently this means that Knockout is then looking for the inner properties, Text and Id, on the outer observableArray, TextTypeGroups, which then causes an exception in the JavaScript.

Because of our support for IE8, and to make our solution portable, the solution we came up with was a custom binding.

Planning the solution

Let’s think first of all about what we would need for such a binding: well, we would need to know the name of the outer collection, and in this collection we’d need to know the name of the property which holds the text which is displayed in the <optgroup> element as well as the name of the collection which holds the <option> elements.

We would also need to know, for this inner collection, which property holds the text and which property holds the value.

So, thus far we have:

For the outermost collection (i.e. the collection which contains the <optgroup> elements)

  • the collection (as a property, observable or otherwise)
  • the name of the property containing the text label for each <optgroup>
  • the name of the property containing the inner collection which contains the <option> elements. And for this inner collection,
    • the name of the property containing the text for each <option>
    • the name of the property containing the value for each <option>

A proposed view-model

At this stage, let’s step aside from the custom binding and look at the view-model(s) with which we might represent this data.


function vmMain() {
	this.TextTypeGroups = ko.observableArray();
}

function vmTextTypeGroup() {
	this.Options = ko.observableArray();
	this.Label = ko.observable();
}

function vmTextType() {
	this.Text = ko.observable();
	this.Value = ko.observable();
}

In actual fact, in our custom binding the elements can be rendered from standard JavaScript properties as well as observables. Therefore, the above example could also be declared as


function vmMain() {
	this.TextTypeGroups = [];
}

function vmTextTypeGroup() {
	this.Options = [];
	this.Label = null;
}

function vmTextType() {
	this.Text = null;
	this.Value = null;
}

Obviously this second example of a view-model will not update when any of the properties are mutated, but our custom binding can still render the <select> when ko.applyBindings() is called.

Depending upon your own use you can choose to make things slightly more efficient (i.e. no observables) if you have no need for the benefits that observables bring.

Implementing the proposed view-model

We can see in this example JavaScript that if we start off with an instance of vmMain, this contains the collection TextTypeGroups which contains the <optgroup> elements, each of which will be represented by an instance of vmTextTypeGroup.

Perhaps the following pseudo-markup will explain things:


<select> <!-- bound to vmMain -->
	<option>- Please select -</option> <!-- bound to the specified defaultOpt -->
	<!-- for each instance of vmTextTypeGroup in vmMain.TextTypeGroups -->
		<optgroup label="[vmTextTypeGroup.Label]">
			<!-- for each instance of vmTextType in TextTypeGroups.Options -->
			<option value="[vmTextType.Value]">
				[vmTextType.Text]
			</option>
			<!-- / for each... -->
		</optgroup>
	<!-- / for each... -->
</select>

So that’s how our idealised view-model will appear. There’s one other consideration that we may wish to make: default/placeholder text. For example, we may wish to have “Please select” appear as the first option in the <select> element. This is something else that we would need to specify to our custom binding.

When all these required properties are then specified in Knockout’s data-bind attribute we have something like


data-bind="groupedSelect: { groups: { 
					coll: TextTypeGroups, 
					label: 'Label', 
					options: {
						coll: 'Options',
						text: 'Text',
						val: 'Value'
					}
				}, 
			defaultOpt: { 
					text: '- Please select -', 
					val: '' 
				} 
			}"

Using conventions

That’s probably the longest binding you’ll ever have to specify for Knockout, but we can reduce the complexity if we’re prepared to follow some conventions. For example, if we were prepared to always use the name Options for the collection of <option> elements then we can build the custom binding in such a way that if we don’t specify this property then we’ll assume that it should look for an array or an observableArray named Options.

And similarly, Text and Value seem to be fairly logical names so let’s offer the same convention for those as well. And while we’re at it, let’s do the same with the Label property on the <optgroup>-level collection.

Of course, if we don’t want to use those property names then that’s fine, you can still use our custom binding. The only downside is that you’ll have to explicitly tell the binding what those properties have been named.

If, on the other hand, you can follow these conventions then the Knockout data-bind attribute may be reduced to


data-bind="groupedSelect: { groups: { 
					coll: TextTypeGroups 
				}, 
			defaultOpt: { 
					text: '- Please select -' 
				} 
			}"

Having got that explanation of what the required properties are and why they’re required, here’s the custom binding. We’ll explain a few points afterwards.

Also, rather than copy the code from this page we suggest that you download the accompanying demo files (at the bottom of this article).


ko.bindingHandlers.groupedSelect = {
    update: function(element, valueAccessor) {

        var h = ko.utils.unwrapObservable(valueAccessor());

        // Get the parameters

        var groups = h["groups"],
            groupsCollection,
            groupsLabel = "Label",
            optionsCollProp = "Options",
            optionsTextProp = "Text",
            optionsValProp = "Value";

        if (typeof (groups) === "undefined" || !groups) {
            throw "The \"groupedSelect\" binding requires a \"groups\" object be specified.";
        } else {
            groupsCollection = groups["coll"];
        }
        if (!groupsCollection) {
            throw "The \"groupedSelect\" binding's \"groups\" object requires that a collection (array or observableArray) be specified.";
        }
        if (typeof (groups["label"]) === "string" && groups["label"].length) {
            groupsLabel = groups["label"];
        }
        if (typeof (groups["options"]) === "object") {
            var options = groups["options"];
            if (typeof (options["coll"]) === "string" && options["coll"].length) {
                optionsCollProp = options["coll"];
            }
            if (typeof (options["text"]) === "string" && options["text"].length) {
                optionsTextProp = options["text"];
            }
            if (typeof (options["val"]) === "string" && options["val"].length) {
                optionsValProp = options["val"];
            }
        }


        var defaultOpt = h["defaultOpt"],
            defaultOptText,
            defaultOptVal;
        if (typeof (defaultOpt) !== "undefined" && defaultOpt) {
            defaultOptText = defaultOpt["text"];
            defaultOptVal = defaultOpt["val"];
        }
        // only specify a default value for 'defaultOptVal' if 'defaultOptText' has been specified
        if ((typeof (defaultOptVal) !== "string" || !defaultOptVal || !defaultOptVal.length)
            && (typeof (defaultOptText) === "string" && defaultOptText && defaultOptText.length)) {
            defaultOptVal = "";
        }

		
        // find how many elements have already been added to 'element'
        var childCount = 0,
            children = element.childNodes,
            childMax = children.length;
        for (var c = 0; c < childMax; c++) {
            if (children[c].nodeType != 3) {
                childCount++;
            }
        }

        // Default <option> element

        // if 'element' is currently empty then add the default <option> element
        if (!childCount) {

            if (typeof(defaultOptText) === "string" && defaultOptText && defaultOptText.length) {
                var defaultOption = document.createElement("option");
                defaultOption.setAttribute("value", defaultOptVal);
                defaultOption.innerHTML = defaultOptText;
                element.appendChild(defaultOption);
            }
        } else {

            // if 'element' is not empty then decrement realChildren by 1, which represents the default <option> element
            childCount--;
        }


        // now it's time to loop through each <optgroup>
        // in this loop, i is set to the the index in the collection which marks the start of the newly-added items, skipping items already added (which were counted above)
        var coll = ko.utils.unwrapObservable(groupsCollection);
        childMax = coll.length;
        for (; childCount < childMax; childCount++) {

            var groupLabel = ko.utils.unwrapObservable(coll[childCount][groupsLabel]);

            // if there is no label for this <optgroup> then don't add the <optgroup>
            if (!groupLabel || !groupLabel.length) {
                continue;
            }

            var optGroup = document.createElement("optgroup");
            optGroup.setAttribute("label", groupLabel);

            // loop through each <option>
            // determine whether the <option>s collection is an array or an observableArray
            var options = ko.utils.unwrapObservable(coll[childCount][optionsCollProp]);
            for (var j = 0, jMax = options.length; j < jMax; j++) {

                var optionText = ko.utils.unwrapObservable(options[j][optionsTextProp]);

                // if there is no text for this <option> then don't add the <option>
                if (!optionText || !optionText.length) {
                    continue;
                }

                var option = document.createElement("option");
                option.innerHTML = optionText;

                // add the 'value' attribute if it exists
                var val = ko.utils.unwrapObservable(options[j][optionsValProp]);
                if (val && val.length) {
                    option.setAttribute("value", val);
                }

                // now add this <option> to the parent <optgroup>
                optGroup.appendChild(option);
            }

            element.appendChild(optGroup);
        }

        return true;
    }
};

Discussion

Yes, there's quite a lot of code for a custom binding, but almost half is dealing with the parameters.

Something that might cause confusion is why we have to find out how many elements have already been added to the <select> element and start the next loop using this index. This is because of the way that Knockout works when binding observables: this custom binding uses the update handler of Knockout's binding handler. This means that this binding will be called each time the outer collection is modified. Therefore, and using our above example, each time an instance of vmTextTypeGroup is added to the TextTypeGroups observableArray, this binding will be called. If we were to simply enumerate each element each time this binding would be called then suffice to say that we'd be rendering too many elements, and only the last instance of vmTextTypeGroup in the TextTypeGroups observableArray would appear once.

So what happens instead is that each time that this binding is called, we count how many elements have already been added, then we make the assumption that these elements correspond to elements in our Options parameter and by starting at our counted index we then render only the observableArray items which are new.

There are other ways around this but these would require an assumption on the part of the custom binding that the developer had always implemented them. And to make this custom binding more portable this safety measure has been implemented inside.

So there you have it, a pre-built handler which will parse an observableArray of observableArrays and render these as <optgroup>s containing <option>s.

The groupedSelect custom Knockoutjs binding is made available under the MIT license.

You can download the JavaScript and demo files here.

 


 

UPDATE: Since this blog post was written the custom binding has had a GitHub repository created. There have also been some small changes - including changing the name to knockout-groupedOptions and a couple of the parameters in order to align it with Knockout's native options binding.

Although this blog posts still serves as a valid discussion of the custom binding, you should reference the GitHub version rather than this version.


The perfect multilingual plugin for WordPress?

November 8th, 2013 by

As I already stated in my blogpost about “Multilingual Content Management Systems” (in German) I’m not a big fan of using WordPress for multilingual sites. I think there are better options out there.

Nevertheless, it is possible, with the aid of various plugins available.

One of the most popular is WPML. While I haven’t used it much personally, I’ve heard many people complain about it. All I know is that the XLIFF export from WPML is close to useless.

I started a discussion on this topic a while ago on stack exchange:

http://wordpress.stackexchange.com/questions/7242/best-wordpress-localization-plugin

Another plugin that always comes up is qTranslate. Although this plugin seems to work well, it stores all translations into the same database field as the original post. I don’t think this is a technically good solution; it means that if you remove the plugin, you will see every post in all languages. For me, that is not an acceptable solution.

Personally I think Multilingual Press is the most promising and clean solution currently on the market. But it requires a Multisite setup, which is not for everybody.

Of course Blogwerk has a very nice multilingual solution, but the extension is not freely available to download.

 

What’s the perfect multilingual WP plugin?

Usability:

  • Pages in different languages linked together (on the public page and in the admin interface)
  • Automatic fallback available if translation is missing
  • Real XLIFF or HTML export/import for posts and pages (individual and bundled)
  • Workflow approval
  • Translates site features (e.g. “Leave a reply”, “Submit comment”, etc) AND the blogpost content

Technical:

  • Does not affect the DB structure
  • Individual languages in individual posts
  • Localized URL: e.g. site.com/en/2013/12/english-title
  • API to integrate external translation providers
  • Clean and simple implementation
  • Does not require a Multisite installation

Let’s hope the perfect plugin is being developed soon. Or have I missed it so far?


Knockout – Custom bind to any function

November 8th, 2013 by

We’ve recently been using Knockout for a lot of our work.

Knockout is an MVVM library for JavaScript, which means that a model (i.e. an object which represents your data) can be bound to the UI in such a way that changes to the UI update the underlying model, and changes to the underlying model update any part of the UI to which the model is bound. Thus, a two-way binding exists between the UI and the underlying view-model.

There are obvious benefits to using Knockout but one which is probably overlooked by most developers is the ability to add your own custom bindings.

Let us explain…

When using Knockout, properties in the view-model are bound to the UI by means of a data-bind attribute added to DOM elements.

So let’s describe a basic Knockout view-model that we’ll use for our example.

function vmSimple() {
    this.ID = null;
    this.Description = ko.observable();
    this.Children = ko.observableArray([]);
}

Now let’s define a template to which this view-model is bound.

<div data-bind="attr: { id: ID }">
    <p class="description"></p>
    <!-- ko foreach Children -->
        <div class="child">
            // do something with each item in the Children collection
        </div>
    <!-- /ko -->
</div>

This will result in HTML resembling the following.

<div id="84">
    <p class="description">
        An example description.
    </p>
    <div class="child">
        I am the eldest child.
    </div>
    <div class="child">
        I am the youngest child.
    </div>
</div>

Now, what if we wanted to modify the exact value or format of one of those properties when it gets bound to the UI, or write out a value computed from one of those properties? Well, one example that Knockout offers is a computed observable. To implement this we’ll modify our model.

function vmSimple() {
    this.ID = null;
    this.Description = ko.observable();
    this.Children = ko.observableArray([]);
 
    <span class="highlight">this.NumberOfChildren = ko.computed(function() {
        return this.Children().length;
    });</span>
}

This now allows us to write out, or bind, a value from our view-model which was not primarily available.

<div data-bind="attr: { id: ID }">
    <p>
        This view-model contains <span class="highlight"><span data-bind="text: NumberOfChildren"></span></span> child items.
    </p>
</div>

This will result in HTML resembling the following.

<div id="84">
    <p>
        This example contains 2 child items.
    </p>
</div>

Another, perhaps more useful example of this might be to preface a value with a currency symbol. Let’s propose that a view-model has a property Price and another property Currency: we could add a computed observable which chooses whether to write out “$50″ or “£50″. However, a slight downside to this is that the computed observable exists in every instance of that view-model (the number of view-models on your page could feasibly run into the thousands).

Ideally there would be a way to call any JavaScript function that you have defined in your client-side script. Of course, you could add any function you like as a Knockout custom binding, but if you wish to use this same function outside of the Knockout binding then you either have repetition or unnecessary overhead.

What I will show you is how we’ve gone half a step further and created a custom binding that allows us to call any function, whether it has been declared as a Knockout custom binding or not.

First of all, in one of our JavaScript files which is always served-up whenever we serve the Knockout script (we’ve implemented the ASP.NET Web Optimization Framework, so the following script is bundled together with our Knockout script, and this bundle is then referenced instead of the individual scripts) we have the following custom binding declared.

ko.bindingHandlers.call = {
    init: function(domElement, viewModelProp) {
              var callback = viewModelProp();
              if (callback && typeof (callback) === "function") {
                  return callback();
              }
              return null;
          }
};

Now let’s write a function which we can call via this custom binding:

function log(value) {
    console.log(value);
}

Now we can rewrite our HTML template.

<div data-bind="attr: { id: ID }">
    <p data-bind="<span class="highlight">call: function() { log(ID); }</span>">
        This view-model contains <span data-bind="text: NumberOfChildren"></span> child items.
    </p>
</div>

The value that we pass to the call binding handler takes the form of a callback. Going by the strict ECMA Script standards, this should be wrapped in a function() { ... } statement, but most modern browsers will actually let you get away with something more readable, such as the following.

<div data-bind="attr: { id: ID }">
    <p data-bind="<span class="highlight">call: log(ID)</span>">
        This view-model contains <span data-bind="text: NumberOfChildren"></span> child items.
    </p>
</div>

The function specified will then be called when the binding takes place, and in our trivial example, the ID property will be written the browser’s console.

There are a couple of points worth making here regarding the format of the parameters that we’re passing to the callback function: I’ve specified ID without parentheses because in our example view-model ID is not an observable property, and therefore ID is simply a value. However, for the sake of example, let’s tweak our view-model so that ID is now an observable:

function vmSimple() {
    this.ID = <span class="highlight">ko.observable();</span>
    this.Description = ko.observable();
    this.Children = ko.observableArray([]);
}

In order to have the value from this observable passed as a usable parameter we need to similarly tweak our template.

<div data-bind="attr: { id: ID }">
    <p data-bind="call: function() { log(<span class="highlight">ID()</span>); }">
        This view-model contains <span data-bind="text: NumberOfChildren"></span> child items.
    </p>
</div>

Also, remember that if the parameter is to be passed as a string then it should be wrapped in quote marks

<div data-bind="attr: { id: ID }">
    <p data-bind="call: function() { log(<span class="highlight">'Description()'</span>); }">
        This view-model contains <span data-bind="text: NumberOfChildren"></span> child items.
    </p>
</div>

So with the addition of this one, fairly simple Knockout custom binding handler, call we are now able to pass the view-model properties to any function in our client-side JavaScript and have the value processed during both the initial Knockout binding and any updates which the specified properties (if any) are involved in.


Azure Support E-Mails

June 24th, 2013 by

I recently signed up for Azure and got the following E-Mail:

 

azure-mail1

Ok, very nice. I actually just run into some Azure issues that I would like some input too, so I wrote her back:

 

azure-mail2

 

Now by all means, I didn’t expect much, but it’s worth a try, no?

Funny enough, 1 months later I get this email:

azure-mail3

How, nice. Microsoft offers me their help. Again. But without actually answering me if I take them up on the offer….

Don’t get me wrong, I know e-mails get lost in spam filter and about 100 other places, but still, not the experience you expect.

Otherwise I’m pretty happy with Azure. Our Windows XP VM is online and works, if a bit slow. Just have not found an easy way to start the VM without the Admin Portal.


Multilingual Websites – My talk at UX Camp Europe 2013

June 23rd, 2013 by

Language Selection on the MSN Homepage

Like the last few years,  had a talk at UX Camp Europe 2013. An unconference about:

  • Information Architecture
  • Interaction Design
  • Usability Engineering
  • Visual Design
  • Prototype Engineering
  • UI Development

I my talk I show some good and bad examples for multilingual websites (e.g. websites in multiple languages). Common mistakes and how to avoid them.

Two examples:

Flags are often used to indicate languages, but that is a bad choice, as you never know if it should indicate the language or the country.

Another common mistake is that the language selection is only done in the current language. But if you write the word English in Chinese, only Chinese people can understand it.

Then I move on to some more technical issues on how to prepare strings that need to be translated.

You can check out the presentation on slideshare.

 

 

 

 


Defining TermBases

February 13th, 2013 by

Here at Supertext we’re contemplating – just contemplating, mind – the thought of building our own termbase. To this end I recently looked into what features are currently available if we were to choose an off-the-shelf termbase, and what we would want to implement in our own.

The Purpose

I’ll spare you the details of the work process behind each job at Supertext, but suffice to say that our aim is to improve the workflow while hopefully making our freelancers’ jobs easier. The overwhelming majority of our work is carried out using Trados. We believe that we can automate part of our workflow by semi-automating the Trados packages. Furthermore, we also have plans which will not only make it easier but also encourage freelancers to work with the termbase.

Another aspect of our purpose is to foment not only a house style but also a homologated style for each customer, and to preserve this style across jobs.

The Fundamentals

We found a helpful online course here which explains the fundamentals of a termbase. For example, the decision must be taken early on whether the termbase will be term-led or concept-led. In the case of the former, the database stores its terms with one or more concepts being ‘attached’ to this term. For conceptual-modeled termbases the database stores a collection of concepts, with one or more terms then being attached. It is likely that any Supertext termbase would be a conceptually-oriented (onomasiological) model.

At this stage I should probably explain some basic points: the concept in a termbase can be regarded as the overarching word or combination of words, under which the explanatory translations, or terms, can be collected. Or to put it another way, the concept is the collection of terms which serve to describe the concept: a herding of contextually-similar words. Furthermore, in a multi-lingual termbase (as we’re thinking of building) it is the terms which contain the translations.

Another recurring notion across termbases is the dictionary, though some termbase applications simply refer to these as the termbases. The dictionary is simply a collection of concepts and is thus the uppermost entity in the model. Concepts are collected into dictionaries so that a context may be set. For example, the term developer in the world of software engineering has a similar but distinctly different connotation to developer in the wold of real estate or property management, and in the domain of startup companies or business management developer relates to yet a different person.

Something that we’ll probably aim for, should we construct a Supertext termbase, is public dictionaries which are accessible by all, and customer/company-specific dictionaries which are writable by only personnel with specific access.

So, by this point our potential termbase is going to contain a collection of public and private dictionaries, inside of which are collections of concepts, each of which has terms which themselves are also translations.

First Impressions

The first thing that struck me about the current crop of termbases is how dated they look. There is one, termbases.eu, which has a modern appearance (which it clearly owes to Twitter’s Bootstrap), but the rest look like the height of 1990s web design. Yes, they look professionally-built and with a sense of security and reliability, but I was left wondering when any development work last took place. Maybe we’re just spoilt by web trends changing so frequently – after all, how many offices are renovated every 4 or 5 years?

TermWeb's appearance suggests that HTML 4.1 is still in vogue and that proprietary user controls are still acceptable.

TermWeb’s appearance suggests that HTML 4.1 is still in vogue and that proprietary user controls are still acceptable.

termbases.eu has the most contemporary appearance, due entirely to Twitter's Bootstrap CSS framework.

termbases.eu has the most contemporary appearance, due entirely to Twitter’s Bootstrap CSS framework, though this doesn’t make the UI any easier to use.

Some termbases, such as TermWeb, refer to the notion of a dictionary as a domain. Another termbase, WebTerm, either doesn’t use such a concept as a dictionary/domain or else masks this from the user. Another, termbases.eu actually refers to these dictionary-like collections as termbases.

WebTerm appears to offer nothing in the way of options, other than specifying the language.

WebTerm appears to offer nothing in the way of options, other than specifying the language.

Another feature that is commonly listed on existing termbases is that they support unicode. If there is to be any hope of a termbase being multilingual this is surely a prerequisite.

As well as being multilingual, another mandatory feature is the ability to import from existing termbases. As is common to almost all, we’ll most likely allow import/export using the standardised TBX format. Others allow importing from Excel and CSV files. Both of these are readily possible for us, the only problem is that once uploaded you have to present other options to the user in order for them to specify what data is where. There’s no set format for the data structure of a termbase so without having a hierarchical document format like TBX then you need to ask the user to provide some sort of hierarchical statement.

Other Features

The notion of exporting brings about another possibility: commonly files are sent along with the jobs to freelance translators which aid them in translating the work in a previously-accepted style. In line with this it might be useful to present a URL which directly downloads a TBX containing the pertinent terms. But certainly, one of our goals is to be able to provide URLs to specific dictionaries, concepts and terms. I mentioned at the start of this post that we’re hoping to streamline the workflow of both our language managers and freelancers, and something that we’ll take a look at is building a custom provider for Trados which will allow our Freelancers to hook up to our termbase with minimal effort, thus removing the need to pass extra files back and forth.

The ability to search is also pretty much mandatory, but the strength of the tool will rely upon how easily users are allowed to search, how many options they are presented with. I’m sure fuzzy search and wildcard characters, for example, would be welcome and well-used.

As far as results go, the intention would be to search for a matching term and when found, display all other terms collected under the same concept. However, to take it one step further, typically a termbase will allow one record (i.e. term or concept) to reference another, and some can go into a fairly technical depth to allow the user to declare what type of reference. For example, the relationship may be expressed as a homonym, paronym, antonym, hyponym, hypernym (superordinate), meronym, or holonym. There’s definitely some value to this but I would question – and please comment if your experience or opinion is to the contrary – that translators would rarely search a termbase to find the hyponym of a term. On the other hand I do see good reason for encouraging links based on synonyms. I also see merit in providing the ability to allow an editor to create a reference to any other term in the entire termbase, along with an explanation of why this might be relevant.

The whole idea of having a Supertext termbase is to greater enable our freelance translators to do their job accurately and expediently. However, a termbase is only as good as its contents, and to this end we envisage that out termbase will be set up similar to a wiki. That is to say that the freelance translators may update it as well as query it, both via a number of ways. That said, anything open in this manner is susceptible to inaccuracy, or simple misuse, and therefore we also plan to implement some sort of changelog and rights management. Both WebTerm and TermWeb offer a fairly full-featured changelog which allows users to see the date/time it was created and by whom, as well as all edits, comments, etc., but a great feature of this is being able to click on a historical change and have that iteration become current. Accordingly, this also raises questions about the approval process and so forth. All of which have yet to be fully fathomed here in the Supertext office.

Who knows, a Supertext termbase may never see the light of day, but it would certainly be an interesting project to work on.

 


  • Topics
  • Archive
  • Subscribe
  • Facebook
  • Twitter