I am currently trying to setup a project to implement localization on javascript files (as described here) but at the same time I'd like to bundle and minify the javascript in the project. I followed a tutorial on bundling and minification here
I have been able to get both working separately, but when I try to get them working together I cannot get the localisation working properly. I think this is because bundling creates it's own route handling for the bundled/minified javascript it generates, so the httpHandler I have defined in the webconfig gets ignored. I keep getting javascript errors saying "CustomTranslate is not defined".
I am trying to do this because we are building a number of controls using ExtJS, but we need to be able to apply localisation to those controls. Any help/ideas on how I can get them to work together would be appreciated.
I am not using MVC, but doing this in asp.net in Visual Studio 2012.
Here is my code:
BundleConfig.cs
namespace TranslationTest
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
//default bundles addeed here...
bundles.Add(new ScriptBundle("~/bundles/ExtJS.axd").Include("~/Scripts/ExtJS/ext-all.js", "~/Scripts/ExtJS/TestForm.js"));
}
}
}
web.config:
<globalization uiCulture="auto" />
<httpHandlers>
<add verb="*" path="/bundles/ExtJS.axd" type="TranslationTest.ScriptTranslator, TranslationTest" />
</httpHandlers>
Default.aspx
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TranslationTest._Default" %>
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
<script src="/bundles/ExtJS.axd"></script>
</asp:Content>
TestForm.js:
Ext.require([
'Ext.form.*',
'Ext.layout.container.Column',
'Ext.tab.Panel'
]);
Ext.onReady(function () {
Ext.QuickTips.init();
var bd = Ext.getBody();
bd.createChild({ tag: 'h2', html: 'Form 1' });
var simple = Ext.create('Ext.form.Panel', {
url: 'save-form.php',
frame: true,
title: 'Simple Form',
bodyStyle: 'padding:5px 5px 0',
width: 350,
fieldDefaults: {
msgTarget: 'side',
labelWidth: 75
},
defaultType: 'textfield',
defaults: {
anchor: '100%'
},
items: [{
fieldLabel: CustomTranslate(FirstName),
name: 'first',
allowBlank: false
}, {
fieldLabel: CustomTranslate(LastName),
name: 'last'
}, {
fieldLabel: CustomTranslate(Company),
name: 'company'
}, {
fieldLabel: CustomTranslate(Email),
name: 'email',
vtype: 'email'
}, {
xtype: 'timefield',
fieldLabel: CustomTranslate(Time),
name: 'time',
minValue: '8:00am',
maxValue: '6:00pm'
}],
buttons: [{
text: CustomTranslate(Save)
}, {
text: CustomTranslate(Cancel)
}]
});
simple.render(document.body);
});
Currently the FirstName, LastName, etc are all stored in resource files, as in the linked example above.
ScriptTranslator.cs
namespace TranslationTest
{
public class ScriptTranslator : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
string relativePath = context.Request.AppRelativeCurrentExecutionFilePath.Replace(".axd", string.Empty);
string absolutePath = context.Server.MapPath(relativePath);
string script = ReadFile(absolutePath);
string translated = TranslateScript(script);
context.Response.Write(translated);
Compress(context);
SetHeadersAndCache(absolutePath, context);
}
#endregion
private void SetHeadersAndCache(string file, HttpContext context)
{
context.Response.AddFileDependency(file);
context.Response.Cache.VaryByHeaders["Accept-Language"] = true;
context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
context.Response.Cache.SetLastModifiedFromFileDependencies();
context.Response.Cache.SetExpires(DateTime.Now.AddDays(7));
context.Response.Cache.SetValidUntilExpires(true);
context.Response.Cache.SetCacheability(HttpCacheability.Public);
}
#region Localization
private static Regex REGEX = new Regex(@"CustomTranslate\(([^\))]*)\)", RegexOptions.Singleline | RegexOptions.Compiled);
private string TranslateScript(string text)
{
MatchCollection matches = REGEX.Matches(text);
ResourceManager manager = new ResourceManager(typeof(TranslationTest.App_GlobalResources.text));
foreach (Match match in matches)
{
object obj = manager.GetObject(match.Groups[1].Value);
if (obj != null)
{
text = text.Replace(match.Value, CleanText(obj.ToString()));
}
}
return text;
}
private static string CleanText(string text)
{
text = text.Replace("'", "\\'");
text = text.Replace("\\", "\\\\");
return text;
}
private static string ReadFile(string absolutePath)
{
if (File.Exists(absolutePath))
{
using (StreamReader reader = new StreamReader(absolutePath))
{
return reader.ReadToEnd();
}
}
return null;
}
#endregion
#region Compression
private const string GZIP = "gzip";
private const string DEFLATE = "deflate";
private static void Compress(HttpContext context)
{
if (IsEncodingAccepted(DEFLATE, context))
{
context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
SetEncoding(DEFLATE, context);
}
else if (IsEncodingAccepted(GZIP, context))
{
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
SetEncoding(GZIP, context);
}
}
private static bool IsEncodingAccepted(string encoding, HttpContext context)
{
return context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains(encoding);
}
private static void SetEncoding(string encoding, HttpContext context)
{
context.Response.AppendHeader("Content-encoding", encoding);
}
#endregion
}
}
global.asax.cs
namespace TranslationTest
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
Microsoft.Web.Optimization.BundleTable.Bundles.EnableDefaultBundles();
BundleConfig.RegisterBundles(System.Web.Optimization.BundleTable.Bundles);
AuthConfig.RegisterOpenAuth();
}
}
}
I hope I've covered everything, but please let me know if there's anything missing. Thanks in advance!!
Bundling and minification are two techniques you can use in ASP.NET 4.5 to improve request load time. Bundling and minification improves load time by reducing the number of requests to the server and reducing the size of requested assets (such as CSS and JavaScript.)
Both bundling and minification are the two separate techniques to reduce the load time. The bundling reduces the number of requests to the Server, while the minification reduces the size of the requested assets.
Bundling is a new feature in ASP.NET 4.5 that makes it easy to combine or bundle multiple files into a single file. You can create CSS, JavaScript and other bundles.
Bundling and minification can be enabled or disabled in two ways: either setting the value of the debug attribute in the compilation Element in the Web. config file or setting the enableOptimizations property on the BundleTable class. In the following example, debug is set to true in web.
Ok, I've set up everything in your example and I've got it to work but you need to use the IBundleTransform interface. The details of everything I did are posted below..
I had to create a class to handle the bundle transformation (i.e the translation) instead of allowing the default behaviour.
public class JsLocalizationTransform : IBundleTransform
{
public JsLocalizationTransform(){}
#region IBundleTransform Members
public void Process(BundleContext context, BundleResponse response)
{
string translated = TranslateScript(response.Content);
response.Content = translated;
}
#endregion
#region Localization
private static Regex REGEX = new Regex(@"CustomTranslate\(([^\))]*)\)", RegexOptions.Singleline | RegexOptions.Compiled);
private string TranslateScript(string text)
{
MatchCollection matches = REGEX.Matches(text);
ResourceManager manager = new ResourceManager(typeof(TranslationTest.App_GlobalResources.text));
foreach (Match match in matches)
{
object obj = manager.GetObject(match.Groups[1].Value);
if (obj != null)
{
text = text.Replace(match.Value, CleanText(obj.ToString()));
}
}
return text;
}
private static string CleanText(string text)
{
//text = text.Replace("'", "\\'");
text = text.Replace("\\", "\\\\");
return text;
}
#endregion
}
Then in BundleConfig.RegisterBundles method you need to create and add the bundle like this:
var extjsBundle = new Bundle("~/bundles/ExtJS").Include("~/Scripts/ExtJS/ext-all.js", "~/Scripts/ExtJS/TestForm.js");
extjsBundle.Transforms.Clear();
extjsBundle.Transforms.Add(new JsLocalizationTransform());
extjsBundle.Transforms.Add(new JsMinify());
bundles.Add(extjsBundle);
I could then remove the HttpHandler
from web.config as that gets configured automatically through the bundler. I also had to make some changes to the Application_Start method in global.asax.cs
void Application_Start(object sender, EventArgs e)
{
//Microsoft.Web.Optimization.BundleTable.Bundles.EnableDefaultBundles();
BundleTable.EnableOptimizations = true; //Added this line..
BundleConfig.RegisterBundles(System.Web.Optimization.BundleTable.Bundles);
AuthConfig.RegisterOpenAuth();
}
Because the JSLocalisationTransform
class is handling the bundle transformation and translation, I completely removed the ScriptTranslator
class.
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With