Supertext Home
Chief of the System Blog

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.

 


Extending the ASP.NET Optimization Framework

February 4th, 2013 by

We recently decided to implement .NET 4.5’s Optimization Framework, the in-built feature-set which allows you to configure bundles of JavaScript and CSS and reference these on your page in such a manner that combines-and-minifies the bundles into a single file (one for JavaScript and one for CSS).
Up until this implementation of .NET’s minification-and-bundling we had been using Cassette, and the only real cause for the changeover was the desire for integration across our .NET feature implementations.

More can be read on .NET’s minification-and-bundling here which, as far as I can tell, is the only webpage on this new feature to be published by Microsoft post-release. Like other bundling-and-minification frameworks Microsoft’s solution only combines-and-minifies when debug=”false” (which is set in the web.config), otherwise it renders references to the files in the traditional manner.

In the process of implementing this minification-and-bundling we came across a couple of shortcomings, and having spent a couple of days trying to work around these we’d like to share the solution that we came up with.

First of all, let’s cover the machinations of what goes on inside Microsoft’s bundling-and-minification Optimization Framework.
The Works

Typically to implement the Optimization Framework you will create a class, conventionally named BundleConfig. You will then be required to pass a collection of Bundles to this class. Again, conventionally you will have the following in your global.asax:

BundleConfig.RegisterBundles(BundleTable.Bundles);

BundleTable is a static class, and Bundles is a singleton instance of BundleCollection which is derived from IEnumerable.
For completeness, here are the two pertinent class declarations.

public static class BundleTable
{
    // Fields
    private static bool _enableOptimizations;
    private static bool _enableOptimizationsSet;
    private static BundleCollection _instance;
    private static Func<string, string> _mapPathMethod;
 
    // Methods
    static BundleTable();
 
    // Properties
    public static BundleCollection Bundles { get; }
    public static bool EnableOptimizations { get; set; }
    public static Func<string, string>; MapPathMethod { get; set; }
}
 
public class BundleCollection : IEnumerable, IEnumerable
{
    // Fields
    private Dictionary<string, Bundle> _bundles;
    private HttpContextBase _context;
    private Dictionary<string, DynamicFolderBundle> _dynamicBundles;
    private IgnoreList _ignoreList;
    private List _orderPriority;
    private FileExtensionReplacementList _replacementList;
    private Dictionary<string, Bundle> _staticBundles;
 
    // Methods
    public BundleCollection();
    public void Add(Bundle bundle);
    public static void AddDefaultFileExtensionReplacements(FileExtensionReplacementList list);
    public static void AddDefaultFileOrderings(IList list);
    public static void AddDefaultIgnorePatterns(IgnoreList ignoreList);
    public void Clear();
    public Bundle GetBundleFor(string bundleVirtualPath);
    protected virtual IEnumerator GetEnumerator();
    public ReadOnlyCollection GetRegisteredBundles();
    public bool Remove(Bundle bundle);
    public void ResetAll();
    public string ResolveBundleUrl(string bundleVirtualPath);
    public string ResolveBundleUrl(string bundleVirtualPath, bool includeContentHash);
    IEnumerator IEnumerable.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator();
    internal static Exception ValidateBundleVirtualPath(string bundleVirtualPath);
 
    // Properties
    internal HttpContextBase Context { get; set; }
    public int Count { get; }
    internal Dictionary<string, DynamicFolderBundle> DynamicBundles { get; }
    public FileExtensionReplacementList FileExtensionReplacementList { get; set; }
    public IList FileSetOrderList { get; }
    public IgnoreList IgnoreList { get; }
    internal Dictionary<string, Bundle> StaticBundles { get; }
    public bool UseCdn { get; set; }
}

So to summarise so far, we have a static class containing a singleton collection of Bundle objects. In other words, we have a collection of Bundle objects which is accessible across our solution.

To make use of the Optimization Framework we instantiate a Bundle object, and pass this object a list of virtual paths which each point towards a file which we wish to have it bundle.
For example, inside BundleConfig.RegisterBundles

bundles.Add(new ScriptBundle("~/scripts-1")
       .Include("~/Resources/JavaScript/jquery-1.8.3.js",
                "~/Resources/JavaScript/modernizr.form-placeholder.js"));

The above code snippet creates a Bundle and passes in a string denoting the virtual path which will be used to retrieve this Bundle, ~/scripts-1. The virtual paths for two physical script files are then passed in using the Include() method.

An important point to note here is that the specific implementation of Bundle created here is a ScriptBundle. To explain the specifics of this I’ll need to explain a little more about what’s going on inside the Optimization Framework.

We’ve already looked at the BundleTable and BundleCollection classes, and there are two other items which should also be discussed: the Bundle class and the IBundleTransform interface.
The Bundle class maintains a collection of virtual paths that should be bundled together. It also contains related properties and methods to get or set, for example, whether you have specified all files in a directory rather than specific files, returning the URL for a bundle, or for enumerating all the files in a bundle. [There are actually 15 methods and 8 properties, but there’s no need for a fuller explanation in this post.] For now, the most important property is

public IBundleTransform Transform { get; set; }

Which allows us to specify our own implementations of IBundleTransform that this Bundle will use.

Of course, at this stage you’re no doubt wondering what an IBundleTransform does. At runtime the webpage will make a call to the Optimization Framework to render the specified Bundle. The implementation of Bundle will then iterate through its Transform collection and pass the collection of physical files into that instance of IBundleTransform. For example, it is inside the built-in JsMinify implementation of IBundleTransform that minification takes place; the ScriptBundle class will automatically implement JsMinify whereas the StyleBundle class will automatically implement the built-in CssMinify implementation.

Of course, if you wished to you could exclude JsMinify or CssMinify from their respective Transforms collections if, for example, you had a class which could better perform the same task.
So to summarise once again, we have a static class (conventionally called BundleConfig) containing a singleton collection of Bundle objects. Each of these Bundle objects contains a collection of entities to be bundled together (its Items collection) and a collection of IBundleTransform implementations (its Transforms collection); the act of rendering the output of each Bundle will cause the files in that Bundle’s Items collection to be passed sequentially to each of the IBundleTransform implementations and the output from the final implementation to be rendered onto the webpage.

The final part of the jigsaw is rendering the output of a Bundle onto the webpage:

<%: Scripts.Render("~/scripts -1") %>

In the example above Scripts is a static class:

public static class Scripts
{
    // Fields
    private static HttpContextBase _context;
 
    // Methods
    public static IHtmlString Render(params string[] paths);
    public static IHtmlString Url(string virtualPath);
 
    // Properties
    internal static HttpContextBase Context { get; set; }
    private static AssetManager Manager { get; }
}

As you can see from the class declaration, if you choose to you can also have just the URLs rendered.

So the secret to customising the Optimization Framework and bending it to your will is to create your own Bundle and IBundleTransform implementations. The rest of this blog post will look into a couple of examples.

jQuery Templates

Anyone not familiar with jQuery Templates should either read this article first, or just accept this simple explanation: jQuery Templates are typically *.htm or *.html files which contain HTML as well as instructional script (which is similar to JavaScript). When these templates are passed to the jQuery plugin the plugin returns a string which will normally contain HTML, and which can then be added to the DOM. In typical use you would pass an object or an array of objects to the template and have it render HTML dependent upon the properties in the object(s).

There are a myriad of methods for maintaining these templates. In our Supertext solution we have each of them as discrete files, with the name of the template as the filename.

There are trivial methods for obtaining each template file asynchronously using jQuery (i.e. on-demand) but since we’re looking at bundling-and-minification we have good reason to investigate bundling these templates together and rendering them onto the page.

One important point here is that the recommended method of including jQuery Templates on the page is inside tags such as

<script id="your_template_name" type="text/html">// <![CDATA[

This prevents the page from both rendering the HTML contained within and from interpreting the content as JavaScript.

So with that introduction to jQuery Templates done, how can we now create a Bundle, pass it the virtual paths of jQuery Template files, and have it render automatically on to the page? There are four steps to this, the first two are as stated in the summary to the previous section: we need to create our own Bundle and IBundleTransform implementations. After this we need to build a static class which we can call from any of our pages, and the final step is to call a method on this class.

Let’s take the IBundleTransform implementation first. This class will be passed, amongst other objects, a collection of virtual paths representing each jQuery Template file. What we then want to do is read the contents of each of these, wrap this content in a tag. The implementation should really be quite trivial but the example demonstrates how we can obtain the collection of virtual paths which have been passed to this Bundle.

Notice also that the Process method has return type void. We don’t actually return the contents of the StringBuilder, we pass them to the BundleResponse object (which is derived from HttpContext) along with the relevant HTTP content type.

That’s all there is to the IBundleTransform implementation (unless you wish to add more functionality by pre-compiling your jQuery Templates). So let’s move up the order of things and look at the Bundle class that we need to build and which will use our custom Transform.

public class TemplateBundle : Bundle
{
    public TemplateBundle(string virtualPath) : base(virtualPath)
    {
        Transforms.Add(new TemplateTransform());
    }
}

As you can see, this is really quite straightforward. We’re simply deriving a class from Bundle and in the constructor we add the implementation that we’ve just created.
To demonstrate usage of our TemplateBundle class. The following code should be added to the BundleConfig class.

bundles.Add(new TemplateBundle("~/templates")
       .IncludeDirectory("~/templates/", "*.htm"));

What you can see above is that we instantiate an instance of our TemplateBundle class, passing to the constructor the virtual path which also serves as a key for each bundle, then we point it towards the templates directory and tell it to include every file with the extension .htm. Note that this IncludeDirectory() method is not specific to our Bundle implementation, the above can also be written out as, for example

bundles.Add(new TemplateBundle("~/templates")
       .Include("~/templates/template-1.htm",
                "~/templates/template-2.htm"));

Once we’ve created this bundle we add it to the bundles singleton that was discussed earlier.

So at this stage we’ve created our implementation of IBundleTransform which will be used for reading and transforming the content, we’ve derived our own Bundle class which will pass our list of content items on to our IBundleTransform implementation, and lastly for now we’ve written the code to utilise these classes and bundle the content.

The final step is now to render the content onto the webpage. We saw in an earlier example that the scripts were rendered by using the Render method on a static Scripts class. Following this logic we should derive our own version of the Scripts object, and in a flash of inspiration let’s call it Templates so we can then call a Templates.Render() method.

Unlike, say, TemplateBundle or TemplateTransform, the Templates class, like the Scripts and Styles classes, does not derive from anything. We could of course derive it from Scripts or Styles and then declare the Render and Url methods as override or new, but there seems little to be gained from this.

public static class Templates
{
    private static HttpContextBase _context;
    private static HttpContextBase Context
    {
        get { return (_context ?? new HttpContextWrapper(HttpContext.Current)); }
        set { _context = value; }
    }
 
    public static IHtmlString Render(params string[] paths)
    {
        var sb = new StringBuilder();
 
        foreach (var path in paths)
        {
            var b = BundleTable.Bundles.GetBundleFor(path);
            if (b == null) continue;
 
            var context = new BundleContext(Context, BundleTable.Bundles, path);
 
            sb.AppendLine(b.GenerateBundleResponse(context).Content);
        }
 
        return new HtmlString(sb.ToString());
    }
}

In the above example we haven’t included a Url method as this wouldn’t be relevant to jQuery Templates.

The final step is to call this static class from your webpage with

<%: Templates.Render("~/templates") %>

Remember to include a reference to the Template class’ namespace on that webpage.

So that’s the steps involved in bundling your own content: an implementation of IBundleTransform, derive your own Bundle class, configure the bundle in the BundleConfig class, create a static class that can obtain the output from the bundle implementation, then write it onto the webpage.

Bundles of Bundles

This one turned out to be a challenge that someone was looking for a solution to on StackOverflow, and also out of curiosity. It should be possible… so how do you do it?

Well first of all, what is meant by ‘Bundles of Bundles’? Let’s suppose that you’ve configured some JavaScript files to be compiled into bundle_1, and another couple of JavaScript files to be compiled into bundle_2. Is there a way in which we can create a bundle_3 which constitutes bundle_1 and bundle_2 without explicitly referencing each of the files within those respective bundles, but simply including those bundles by:

  1. declaring a new bundle, and then
  2. declaring which other bundles should be included in this bundle.

So, for example, if we have

bundles.Add(new ScriptBundle("~/bundle_1")
       .Include("~/JavaScript/jquery-1.8.3.js",
                "~/JavaScript/modernizr.form-placeholder.js"));
bundles.Add(new ScriptBundle("~/bundle_2")
       .Include("~/JavaScript/jquery.plugin.1.js",
                "~/JavaScript/another.jquery.plugin.js"));

Then how about declaring a new bundle with

bundles.Add(new BundleOfBundles("~/bundle_3")
       .Include("bundle_1", "bundle_2"));

I’ll point out that we’re not currently using this, and I’m not entirely sure that I see a good reason for compiling bundles together. I’ll offer an example of why I think this isn’t a great idea to use on your site. We have two aspects to the Supertext website: there’s the public-facing webpages, and there’s also an administration side which is used only by the Supertext staff. The public-facing webpages have a bundle called st-scripts-common-1, and the administration references that and a bundle called st-scripts-common-2. We didn’t want to combine these into a single file because to get to the administration pages the Supertext staff will login via the public-facing pages. At that stage their browsers already have the JavaScript contained with st-scripts-common-1 and so there’s nothing to be gained by bundling them once again into a single file; their browsers will see that it is referenced once again on the next page, alongside a new JavaScript file, st-scripts-common-2, but the browser will recognise that it already has this first file and therefore won’t attempt to re-download content that it already has. If we were to reference a third file, say st-scripts-common-3, which contained st-scripts-common-1 and st-scripts-common-2 then the browser would be re-downloading content that it already had.

So there’s something to be gained by not bundling everything together. This point only emphasises the need for proper management and maintenance of the contents in each bundle.

Anyway, if you do wish to compile bundles together, here’s how to do it.

As with the jQuery Templates example, create a class which implements IBundleTransform. As explained earlier, these Transform classes are the ones which are given a collection of virtual paths at runtime and which can be manipulated to do whatever you wish with the virtual paths or the contents within.

So in this specific class we’re going to receive a collection of virtual paths which don’t currently exist, but which we can take one by one, search for bundles with these names in the BundleCollection, and when we obtain each bundle we can then examine its collection of virtual paths. When we’ve done this for each bundle which this Transform class is passed we will then have a superset of virtual paths for this bundle of bundles.

So just to clarify, we are not actually going to add one bundle to another, instead we are going to look at the files which constitute each bundle, then add these into a new list.

Now, this is the stage where things get slightly awkward. Microsoft has implemented a lot of the classes and fields in the Optimization Framework as either private or internal. While this doesn’t make it impossible to use them, it really could be a lot easier. For now, to get around this (and to make this explanation easier to follow, and to prevent having to paste lots of code into this post…) I’m going to break away from the .Include(string virtualPath) convention. Instead I’ll create a private property on the implementation of IBundleTransform which will store the collection of bundle names rather than using the internal Items collection.

It’s also worth adding that the internal Include method also checks that the files exist in the file system, whereas in the case of bundles, the 'files' don’t exist because they are bundles. I hope this distinction between files and bundles is clear.

So let’s dive straight in. Here is the implementation of IBundleTransform.

public class BundleTransform : IBundleTransform
{
    private const string ContentTypeJavascript = "text/javascript";
    private const string ContentTypeCss = "text/css";
 
    // Stores the name of all the bundles that we want to combine
    public Collection Bundles { get; set; }
 
    // Stores the content-type for the HTTP response. Eg, "text/javascript", "text/css", etc.
    public string ContentType { get; set; }
 
    public BundleTransform(Collection bundles, string contentType)
    {
        Bundles = bundles;
        ContentType = contentType;
    }
 
    public void Process(BundleContext context, BundleResponse response)
    {
        // we'll use a StringBuilder to compile all our minified files into when debug="false"
        var strBundleResponse = new StringBuilder();
 
        // the collection of files is used when debug="true"
        var files = new List();
 
        // foreach bundle that we want to compile into this super-bundle...
        foreach (var bundleName in Bundles)
        {
            // ...obtain the specified bundle from the BundleCollection
            var bundle = context.BundleCollection.GetBundleFor(bundleName);
            if (bundle == null) continue;
 
            var output = bundle.GenerateBundleResponse(context);
 
            // the Content is the actual JavaScript or CSS
            var outputContent = output.Content;
 
            // append this to our output string buffer
            strBundleResponse.Append(outputContent);
 
            // looks like when a bundle has been completed the ';' is omitted from the end
            if (output.ContentType == "text/javascript" &amp;&amp; !outputContent.EndsWith(";"))
            {
                strBundleResponse.Append(";");
            }
        }
 
        // this assigns our super-list of all bundled files to the collection which is rendered inside the HTML tags
        // - used when debug="true"
        response.Files = files;
 
        // this assigns the total combined-and-minified output from all the files
        // - used when debug="false"
        response.Content = strBundleResponse.ToString();
        // of course we should let the client know what the content-type is
        response.ContentType = ContentType;
    }
}

Ok, there’s a lot of code in there, but I hope that the comments will keep you on the straight and narrow. What can be seen is that we have an internal collection property

public Collection Bundles { get; set; }

And that it is this internal collection that we loop through to obtain each bundle rather than the response.Files collection, which in this case will be empty because we haven’t made any calls to Include().

So when the Process method is called it accesses the implementation’s own collection of bundle names, then uses the static context.BundleCollection to retrieve the requested Bundle.

As in the jQuery Template example, we then simply open the file and read its contents into a string buffer, then we move onto the next file in that bundle, and then we move onto the next bundle.
There are certainly optimisations available here: for example, each generated bundle has a ContentType property. It’s probably a good idea to check this to ensure that each bundle which is being appended has the same ContentType as its sibling bundles; it’s no good throwing a JavaScrpt bundle in with a CSS bundle. It’s also possible at this stage to read in the unminified contents and append them together, then minify the contents in a single pass.

Anyway, that’s the IBundleTransform implementation over with. Let’s now look at the Bundle class which we’ll derive which will implement our transform.

public class BundledScriptsBundle : Bundle
{
    private const string ContentTypeJavascript = "text/javascript";
 
    public BundledScriptsBundle(string virtualPath, Collection bundles) : base(virtualPath)
    {
        Transforms.Add(new BundleTransform(bundles, ContentTypeJavascript));
    }
}

The only great point to take note of here is that the constructor requires a collection of strings which is where we enumerate each of the bundle names. This gets passed straight onto the Transform which we’ve just looked at. Also, we’re hard-coding the content-type, but then we have called this class BundledScriptsBundle so there are no excuses for confusion here.

So a quick summary: we’ve created an implementation of IBundleTransform which enumerates a collection of bundle names and appends them into one string buffer. We then derived our own Bundle class which requires a collection of bundle names, then implements the aforementioned transform.

Not far to go now, all we have to do is create an instance of our derived Bundle class and pass in the names of the bundles that we wish to have compiled into a single entity.

bundles.Add(new BundledScriptsBundle("~/super-bundle",
    new Collection {
        "~/scripts-1",
        "~/scripts-2" }));

Like in earlier examples, we create a new instance of BundledScriptsBundle, declare the name that we wish to retrieve this super-bundle by, and then we declare a collection of strings containing, in this case, two pre-declared bundles, scripts-1 and scripts-2.

The last step is to render the contents of this super-bundle onto a webpage. For this we can utilise the out-of-the-box static Scripts class.

<%: Scripts.Render("~/super-bundle") %>

And that’s it. Load the page, there’s your super-bundle of bundles.

Of course, there are loads of optimisations that can be done to this but the purpose of putting together this blog post was to look at how we can customise the Optimization Framework to achieve what we want, and the exact implementation is not directly relevant.

As a last point we’d like to discuss another area which I’m sure lots of developers will be trying to implement.

LESS

We started using LESS in place of CSS a few months ago. Out of the box the Optimization Framework does not support LESS but Microsoft’s only page on the Optimization Framework contains an example of how it can be added. Unfortunately this suggested solution only works when the application is not running in debug mode.

When debug="false" the referenced files are combined-and-minified into a CSS file and a link to this is rendered onto the page.

However, when debug="true" this combining-and-minification doesn’t take place, so what you’re left with is on-page references to your LESS files. Not only will a client browser be unable to comprehend these (wouldn’t it be great if browsers could natively read LESS?), IIS doesn’t have a handler for that file extension.

The solution for this was quite straightforward: all the developers here at Supertext have the Web Essentials plugin for Visual Studio installed. If configured correctly Web Essentials will create a 'buddy file' for each *.less file at the point of file creation. [A 'buddy file' is one of those dependant files which Visual Studio shows underneath another file when you click the adjacent arrow. For example, an ASPX file has *.aspx.cs and *.aspx.designer.cs buddy files.] These buddy files are the *.css version and the *.min.css version. Therefore, all you have to do to implement bundling-and-minification of LESS files regardless of debug mode is to ignore the advice on Microsoft’s page for achieving this and instead use the Web Essentials plugin. It’s not as neat and tidy as it should be because every developer working on the solution must have this plugin installed, but once it is installed it requires no further configuration to achieve the bundling-and-minification of CSS.

As an addendum to this point about using LESS, something else to keep in mind is that when debug=”true” the Optimization Framework will not bundle files which contain ".min" in the filename. Again, Web Essentials has us covered because it creates both un-minified and minified versions of the files, so no further configuration is necessary.

Summary

I hope this blog post has helped you to solve a problem, or has helped create an idea you’re now going to follow-up. Like we said earlier, please don’t pay too much attention to the exact specifics of the code we’ve used to, for example, read the contents of a file, or how we’ve appended the files together; the purpose of this post was to disassemble Microsoft’s bundling-and-minification solution, the Optimization Framework, and discuss how we can derive and implement our own classes to achieve what we want.


ASP.NET Server Tags

December 20th, 2012 by

<% %> An embedded code block is server code that executes during the page’s render phase. The code in the block can execute programming statements and call functions in the current page class.

http://msdn2.microsoft.com/en-gb/library/ms178135(vs.80).aspx

<%= %> most useful for displaying single pieces of information.

http://msdn2.microsoft.com/en-us/library/6dwsdcf5(VS.71).aspx

<%# %> Data Binding Expression Syntax.

http://msdn2.microsoft.com/en-us/library/bda9bbfx.aspx

<%$ %> ASP.NET Expression.

http://msdn2.microsoft.com/en-us/library/d5bd1tad.aspx

<%@ %> Directive Syntax.

http://msdn2.microsoft.com/en-us/library/xz702w3e(VS.80).aspx

<%– –%> Server-Side Comments.

http://msdn2.microsoft.com/en-US/library/4acf8afk.aspx

<%: %> Like <%= %> But HtmlEncodes the output (new with Asp.Net 4).

http://weblogs.asp.net/scottgu/archive/2010/04/06/new-lt-gt-syntax-for-html-encoding-output-in-asp-net-4-and-asp-net-mvc-2.aspx

 

Direct copy from http://forums.asp.net/p/1139381/1828702.aspx


Getting started with a Windows 8 Store App

November 29th, 2012 by

It’s currently a bit difficult to find good resources for Windows 8 App development.

Do you search for WinRT or Metro? Or even Windows Runtime?

Using XAML leads to WPF results or WP7 stuff.

 

Anyway here are few good resources:

 

General Support:

 

Free consulting services

http://www.generationapp.com

 

31 Days of Windows 8

http://31daysofwindows8.com/days

 

Programming:

 

Windows.UI.Xaml.Controls classes (WinRT)

http://msdn.microsoft.com/en-us/library/windows/apps/jj203560.aspx

 

UX:

 

UX guidelines for Store Apps

http://msdn.microsoft.com/en-us/library/windows/apps/hh465424.aspx

 

Some good Stackexchange Answers

http://ux.stackexchange.com/questions/7219/where-can-i-find-style-guide-for-metro-ui-for-web-applications

http://ux.stackexchange.com/questions/22284/are-there-any-recommendations-for-the-layout-of-metro-forms-ui-with-hierarchical

 

Design Templates

http://blogs.msdn.com/b/dorischen/archive/2013/01/15/free-windows-8-design-templates-are-now-available-for-all-developers.aspx


How to get a Supertext API Token

September 13th, 2012 by

image

First, login to your Supertext AG account with your normal username and password:

https://www.supertext.ch/de/login

Then click on Einstellungen below Konto.

Scroll all the way down until you see the part “API Authentifizierungs Token”

 

 

image

Here you see your token. Use this and your username in the system that sends requests to the Supertext API.

 

If you don’t have an account yet, you can create one here:

https://www.supertext.ch/de/signup


Clean filenames

August 31st, 2012 by

As soon as you have people uploading files with all kind of funky characters and umlauts in it, you might run into troubles once you have to download them again.

Same if you create files on the fly that contain for example the name of a person or a company.

This was exactly the situation we had, so I came up with a little cleaning function in C#.

Feel free to adjust the mapping list according to your requirements. If mapped some  of the more common special chars to the minus sign (-) and tried to get a sensible mapping for all the umlauts and other special characters.

The method CleanFileName() first maps all the known chars and then gets rid of everything that is left.

 
//http://www.pjb.com.au/comp/diacritics.html
private static string[,] CharacterReplacements = { 
    { " ", "-"},
    { "&", "-"},
    { "?", "-"},
    { "!", "-"},
    { "%", "-"},
    { "+", "-"},
    { "#", "-"},
    { ":", "-"},
    { ";", "-"},
    { ".", "-"},
 
    { "¢", "c" },   //cent
    { "£", "P" },   //Pound
    { "€", "E" },   //Euro
    { "¥", "Y" },   //Yen
    { "°", "d" },   //degree
    { "¼", "1-4" }, //fraction one-quarter
    { "½", "1-2" }, //fraction half    
    { "¾", "1-3" }, //fraction three-quarters}
    { "@", "AT)"}, //at                                                  
    { "Œ", "OE" },  //OE ligature, French (in ISO-8859-15)        
    { "œ", "oe" },  //OE ligature, French (in ISO-8859-15)        
 
    {"Å","A" },  //ring
    {"Æ","AE"},  //diphthong
    {"Ç","C" },  //cedilla
    {"È","E" },  //grave accent
    {"É","E" },  //acute accent
    {"Ê","E" },  //circumflex accent
    {"Ë","E" },  //umlaut mark
    {"Ì","I" },  //grave accent
    {"Í","I" },  //acute accent
    {"Î","I" },  //circumflex accent
    {"Ï","I" },  //umlaut mark
    {"Ð","Eth"}, //Icelandic
    {"Ñ","N" },  //tilde
    {"Ò","O" },  //grave accent
    {"Ó","O" },  //acute accent
    {"Ô","O" },  //circumflex accent
    {"Õ","O" },  //tilde
    {"Ö","O" },  //umlaut mark
    {"Ø","O" },  //slash
    {"Ù","U" },  //grave accent
    {"Ú","U" },  //acute accent
    {"Û","U" },  //circumflex accent
    {"Ü","U" },  //umlaut mark
    {"Ý","Y" },  //acute accent
    {"Þ","eth"}, //Icelandic - http://en.wikipedia.org/wiki/Thorn_(letter)
    {"ß","ss"},  //German
 
    {"à","a" },  //grave accent
    {"á","a" },  //acute accent
    {"â","a" },  //circumflex accent
    {"ã","a" },  //tilde
    {"ä","ae"},  //umlaut mark
    {"å","a" },  //ring
    {"æ","ae"},  //diphthong
    {"ç","c" },  //cedilla
    {"è","e" },  //grave accent
    {"é","e" },  //acute accent
    {"ê","e" },  //circumflex accent
    {"ë","e" },  //umlaut mark
    {"ì","i" },  //grave accent
    {"í","i" },  //acute accent
    {"î","i" },  //circumflex accent
    {"ï","i" },  //umlaut mark
    {"ð","eth"}, //Icelandic
    {"ñ","n" },  //tilde
    {"ò","o" },  //grave accent
    {"ó","o" },  //acute accent
    {"ô","o" },  //circumflex accent
    {"õ","o" },  //tilde
    {"ö","oe"},  //umlaut mark
    {"ø","o" },  //slash
    {"ù","u" },  //grave accent
    {"ú","u" },  //acute accent
    {"û","u" },  //circumflex accent
    {"ü","ue"},  //umlaut mark
    {"ý","y" },  //acute accent
    {"þ","eth"}, //Icelandic - http://en.wikipedia.org/wiki/Thorn_(letter)
    {"ÿ","y" },  //umlaut mark
    };
 
 
 
//http://stackoverflow.com/questions/3885964/regex-to-replace-invalid-characters
public static string RemoveNonWordChars(string source)
{
    return RemoveNonWordChars(source, "");
}
 
 
//http://stackoverflow.com/questions/3885964/regex-to-replace-invalid-characters
public static string RemoveNonWordChars(string source, string replacement)
{
    //\W is any non-word character (not [^a-zA-Z0-9_]).
    Regex regex = new Regex(@"[^a-zA-Z0-9-]+");
    return regex.Replace(source, replacement);
}
 
 
public static string CleanFileName(string filename)
{
    string fileEnding = null;
    int index = filename.LastIndexOf(".");
 
    //removes the file ending.
    if (index != -1)
    {
        fileEnding = filename.Substring(index + 1);
        filename   = filename.Substring(0, index);
 
        //remove based on the CharacterReplacements list
        for (int i = 0; i < CharacterReplacements.GetLength(0); i++)
        {
            fileEnding = fileEnding.Replace(CharacterReplacements[i, 0], CharacterReplacements[i, 1]);
        }
 
        //remove everything that is left
        fileEnding = "." + StringUtil.RemoveNonWordChars(fileEnding);
    }
 
    //remove based on the CharacterReplacements list
    for (int i = 0; i < CharacterReplacements.GetLength(0); i++)
    {
        filename = filename.Replace(CharacterReplacements[i, 0], CharacterReplacements[i, 1]);
    }
 
    //remove everything that is left
    filename   = StringUtil.RemoveNonWordChars(filename);
 
    return filename + fileEnding;
}

You could use HttpUtility.UrlEncode, but that does not solve all the issues that are solved with my solution. And it should work in all browsers. My tests with UrlEncode worked fine in Chrome, but not in IE.


  • Topics
  • Archive
  • Subscribe
  • Facebook
  • Twitter