Вопрос

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!!

Это было полезно?

Решение

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top