Globalisasi ASP.NET MVC

ASP.NET MVC baru memasuki versi 1.0 dan harus kita maklumi beberapa fitur masih belum memiliki garis yang jelas, dalam artian masih digodok dalam MvcFutures atau masih bisa meminjam implementasi di Web Form. Salah satunya adalah implementasi globalisasi. Kalau di Web Form kita sudah bisa membaca satu pedoman yang jelas bagaimana mengimplementasikan globalisasi, di ASP.NET MVC bacaan ini belum ada. Dalam salah satu posting berseri StoreFront, Rob Conery mencoba membahas implementasi globalisasi dalam ASP.NET MVC. Posting Rob bisa dibaca di sini.

Dalam posting ini saya akan mencoba mengetengahkan salah satu implementasi globalisasi di ASP.NET MVC dengan contoh kasus tertentu. Dalam contoh kasus ini kita punya dua domain misalnya www.mydomain.net dan www.mydomain.co.id. Jika seseorang mengakses domain .net, maka bahasa Inggris lah yang akan digunakan sebagai bahasa website tersebut baik dari sisi content maupun label. Sebaliknya jika domain .co.id yang diakses maka bahasa Indonesia lah yang akan disajikan. Untuk menyajikan content multibahasa tersebut hanya ada satu website dengan satu database saja.

OK, kalau berkaitan dengan fitur multi bahasa dalam content, tentu kita harus menyimpan multiple record untuk content yang sama dalam database. Terdapat dua design-decision untuk melakukan hal ini. Salah satunya adalah dengan menambahkan satu field penanda bahasa yang lazim disebut dengan culture id. Dalam gambar berikut ini, tabel News ditambahkan column CultureId sebagai penanda bahasa. Pemilihan design decision dengan cara demikian berarti kita membolehkan news id yang berbeda untuk item yang 'dianggap sama' dalam bahasa yang berlainan.


Berikutnya adalah menemukan cara bagaimana agar akses dari domain .net dan domain .co.id bisa diidentifikasi. Informasi ini tentunya terdapat dalam host header dari setiap web request. Kalau kita periksa HttpContext, kemudian periksa lebih lanjut object Request, di sana ada satu collection yang bernama Headers. Collection ini berbentuk dictionary, dan kita bisa mengakses informasi tentang host dari Request.Headers["Host"].

Dalam ASP.NET MVC, HttpContext ini available sebagai salah satu property dari Controller. Oleh karena itu dalam action-method kita bisa membaca informasi ini dan menentukan bahwa Culture Id dari content yang akan kita sajikan berbahasa Inggris atau Indonesia sesuai dengan informasi host header. Namun kalau kita melakukan hal ini dalam setiap action method, berarti semua action method akan diawali dengan pemanggilan method untuk mendapatkan informasi Culture Id ini, seperti terlihat dalam contoh source code di bawah ini.

    9     [HandleError]

   10     public class HomeController : Controller

   11     {

   12         public ActionResult Index()

   13         {

   14             CheckCultureId();

   15             return View();

   16         }

   17 

   18         public ActionResult Browse()

   19         {

   20             CheckCultureId();

   21             return View();           

   22         }

   23 

   24         public ActionResult About()

   25         {

   26             CheckCultureId();

   27             return View();

   28         }

   29     }

Perhatikan dalam source code di atas bahwa kita selalu memanggil method CheckCultureId() dalam setiap action-method. Hal ini tentu saja ugly selain memungkinkan kita kelupaan memanggil method tersebut dalam salah satu action-method. Untungnya ASP.NET MVC memiliki satu fitur yang bernama filter. Dengan filter ini sebuah class bisa di-dekorasi dengan atribut tertentu yang bisa mengontrol behavior pemanggilan action method. Sebuah filter memiliki method OnActionExecuting(), OnActionExecuted(), dan lain-lain yang menunjukkan action-apa saja yang 'otomatis' dilakukan di setiap action method.

Agar lebih efektif menyimpan informasi Culture Id yang nantinya dikerjakan dalam sebuah filter, kita perlu memiliki sebuah base class dari controller-controller yang nantinya akan kita buat. Base class berisi state-state dan behavior yang secara umum dimiliki oleh semua controller, termasuk Culture Id ini. Secara hirarki diagram class dari controller kita akan tampak seperti dalam gambar berikut ini.


Dengan struktur class seperti di atas, kita bisa memiliki implementasi filter Culture Id seperti terlihat dalam source code di bawah ini.

    public class CultureIdFilterAttribute : ActionFilterAttribute

    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)

        {

            var controller = filterContext.Controller as BaseController;

 

            var httpContext = filterContext.RequestContext.HttpContext;

            if (httpContext == null)

                return;

 

            var host = httpContext.Request.Headers["Host"];

            if (string.Compare(host, Constants.BahasaHostHeader,

                StringComparison.OrdinalIgnoreCase) == 0)

            {

                controller.CultureId = "id-ID";

                controller.ViewData["CultureId"] = "id-ID";

            }

            else

            {

                controller.CultureId = "en-US";

                controller.ViewData["CultureId"] = "en-US";

            }

        }

    }

Sehingga kita bisa memakai filter tersebut dalam setiap controller, seperti contoh pemakaian filter dalam NewsController di bawah ini. Perhatikan bahwa atribut CultureIdFilter ini bisa kita terapkan dalam class ataupun masing-masing action-method. Jika diterapkan dalam deklarasi class, artinya setiap action-method dari class tersebut akan secara otomatis diterapkan filter ini.

   11     [HandleError]

   12     [CultureIdFilter]

   13     public class NewsController : BaseController

   14     {

   15         private readonly INewsRepository newsRepository;

   16 

   17         public NewsController(ISettingRepository settingRepository,

   18             INewsRepository newsRepository): base(settingRepository)

   19         {

   20             this.newsRepository = newsRepository;

   21         }

Jika Culture Id sudah ketahuan berarti tinggal masalah passing informasi ini ke 'data service' sebagai salah satu parameter data retrieval. Dalam contoh di bawah ini kita menggunakan Entity Framework yang dibungkus dalam sebuah repository. Berikut ini adalah source code dari NewsRepository. Perhatikan bahwa Culture Id menjadi parameter dari setiap method yang mengambil data.

    public class NewsRepository: INewsRepository

    {

        public const int LatestNewsCount = 3;

        private EfindoEntities entities;

 

        public NewsRepository(EfindoEntities entities)

        {

            this.entities = entities;

        }

 

        public IList<News> GetLatestNews(string cultureId)

        {

            return (

                from n in entities.News

                where n.CultureId == cultureId

                    && n.Status == Constants.Published

                select n).Take(LatestNewsCount).ToList();

        }

 

        public IPagedList<News> GetAllNews(string cultureId, int pageIndex,

            int pageSize)

        {

            return (

                from n in entities.News

                where n.CultureId == cultureId

                    && n.Status == Constants.Published

                orderby n.NewsDate descending

                select n).ToPagedList(pageIndex, pageSize);

        }

 

        public News GetNewsByName(string cultureId, string name)

        {

            return (

                from n in entities.News

                where n.CultureId == cultureId

                    && n.Name == name

                select n).FirstOrDefault();

        }

    }

OK, semoga metode yang saya sajikan dalam posting yang singkat ini bisa menjadi salah satu pertimbangan Anda mengimplementasikan content multibahasa pada sebuah website ASP.NET MVC.

Share this post: | | | |
Published Monday, April 27, 2009 5:42 PM by Agus Suhanto
Filed under: ,

Comments

# Globalisasi ASP.NET MVC (2)

Friday, May 08, 2009 7:30 AM by Agus Suhanto

Sebelumnya saya pernah menulis globalisasi dalam ASP.NET MVC khusus untuk data. Kali ini saya akan membahas

Powered by Community Server (Commercial Edition), by Telligent Systems