MVC dil kodunu dinamik olarak URL'ye yazma

khalannz7

Hectopat
Katılım
18 Ağustos 2022
Mesajlar
858
Çözümler
15
Amacım kullanıcının seçtiği dilin kodunu ve id'sini sessionda tutabilmek. Çünkü id'yi veri tabanı sorgularında kullanıyorum örneğin "GetContactByLanguage" proceduresi ile veri tabanına sorgu atıyorum. Kodu da sessionda tutmak istiyorum çünkü linkleri orneksite.com/tr/anasayfa veya orneksite.com/en/home şeklinde kullanıcıya göstermek istiyorum. Aşağıdaki gibi yapıyorum ama maalesef sonuca ulaşamadım.

_LanguagePartial.cshtml:

HTML:
@model List<GAP_Compressor.Models.Language>

@if (Model != null && Model.Any())
{
    var returnUrl = Context.Request.Url?.PathAndQuery ?? "";

    foreach (var lang in Model)
    {
        <li>
            <a href="@Url.Action("SetLanguage", "_Layout", new { languageID = lang.language_id, languageCode = lang.code, returnUrl = returnUrl })">
                <img src="@lang.image_path" alt="@lang.code"> @lang.code
            </a>
        </li>
    }
}

_LayoutController:

C#:
 public ActionResult SetLanguage(int languageID, string languageCode, string returnUrl = "/")
 {
     LanguageHelper.SetLanguage(languageID, languageCode);
     return Redirect(string.IsNullOrEmpty(returnUrl) ? Url.Action("Index", "Home") : returnUrl);
 }

LanguageHelper:

C#:
  public static class LanguageHelper
  {
      private const int defaultLanguageID = 1;
      private const string defaultLanguageCode = "tr";

      public static int GetCurrentLanguageID()
      {
          return HttpContext.Current.Session["languageID"] as int? ?? defaultLanguageID;
      }

      public static string GetCurrentLanguageCode()
      {
          if (HttpContext.Current.Session != null)
          {
              return HttpContext.Current.Session["languageCode"] as string ?? defaultLanguageCode;
          }

          return defaultLanguageCode;
      }

      public static void SetLanguage(int languageID, string languageCode)
      {
          HttpContext.Current.Session["languageID"] = languageID;
          HttpContext.Current.Session["languageCode"] = languageCode;
      }
  }

RouteConfig:

C#:
            routes.MapRoute(
                name: "Localized",
                url: "{languageCode}/{controller}/{action}/{id}",
                defaults: new { languageCode = "tr", controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
 
Son düzenleyen: Moderatör:
Reposunu verirsen Debug yaparak çözebilirim.
 
Tam olarak anlayamadım durumu? Session'a Lang'i zaten URL'e göre yazmıyor musunuz?
 
Tam olarak anlayamadım durumu? Session'a Lang'i zaten URL'e göre yazmıyor musunuz?

Hocam pek tecrübeli değilim o yüzden hatalı anlatıyor olabilirim kusura bakmayın elimden geldiğince anlatmaya çalışayım.

DropBox ile dilleri veri tabanından çekiyorum. Kullanıcı bir dil seçtiği gibi o dilin ID'sini alıp servislere yolluyorum. Çünkü veri tabanına sorgu atarken procedureler ile çalışıyorum mesela "GetAboutByLanguage". Aynı şekilde mevcut sessiondaki kodu da alıp URL'ye yazmak istiyorum. Bir sürü yöntem denedim ama bazıları dinamik bir çözüm değil. Anlayacağınız işin üzerinden çıkamadım bir iş aldım başıma bela oldu. Şu an güncel kodlarımı paylaşmaya çalışıyorum ama günlerdir ne Technopat'a adam gibi girebiliyorum ne kod paylaşabiliyorum sitede bir sıkıntı var. Etrafımda sorabileceğim en ufak yazılımcı bile yok.
 
Anladim, yani normalde Localization icin Core MVC'nin built-in destegi vardi neden onu kullanmadiniz bilemiyorum. Sorununuza soyle yaklasalim, siz her url'in "/tr/anasayfa" gibi bir dil belirteci ile alinmasini istiyorsunuz. Bunun icin gerekli route'u da olusturmussunuz.

Siradaki adim olarak bir Middleware ile bu languageCode'u alip servislerinize iletmeniz gerekiyor.

Fakat kilit nokta şu, .NET MVC mi yaziyorsunuz yoksa .NET Core MVC mi? Ona gore yardima devam edeyim.
 

.NET MVC 4.7.2 yazıyorum hocam. Staj yaptığım yerde bu şekilde öğrendiğim için projeye bu şekilde başladım ve yapımı da değiştiremiyorum. Tıkıldım kaldım.

Biliyorum buradan özelden yardımlaşmaya çağırmak yasak, fakat bu buradan çözülebilecek bir konu değil gibi görünüyor çok karmaşık. Eğer moderatörler(etiketleyemedim) ve @pottie4r iki tarafında izni olursa gerekirse ücreti ile yardım almak isterim.
 
Ücrete gerek yok, ben size yine yardım ederim. Dediğiniz gibi işler buradan baya karmaşık oluyor.

.NET MVC'de Middleware desteği yoktu, dolayısıyla request pipeline'ında bu lokalizasyonu farklı bir yere koymamız gerek.

Simdi .NET MVC'nin yasam dongusunde her istek icin Controller ornegi yaratilir. Hem de RouteData kullanilarak ki bu da burayi interception icin mantıklı kıldı benim gözümde.

Bahsettiğimiz noktayı intercept etmek için özel bir ControllerFactory'e ihtiyacımız var. Yine de biz bütün methodları kendimiz implemente etmek istemediğimiz için .NET MVC'de built-in gelen DefaultControllerFactory'i dekore ederek ilerleyelim.

Kod:
public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if(requestContext.HttpContext.IsPostNotification || requestContext.HttpContext.Request.IsAjaxRequest() || requestContext.HttpContext.Method != "GET")
            return base.GetControllerInstance(requestContext, controllerType);
            
        var langId = LanguageHelper.GetCurrentLanguageID();
        var langCode = LanguageHelper.GetCurrentLanguageCode();
        
        if(langCode != requestContext.RouteData.Values["lang"].ToString()){
            var controller = requestContext.RouteData.Values["controller"];
            var action = requestContext.RouteData.Values["action"];
            var id = requestContext.RouteData.Values["id"];
            
            requestContext.HttpContext.Response.RedirectToRoute(new { lang = langCode, controller = controller, action = action, id = id });
        }
        
        return base.GetControllerInstance(requestContext, controllerType);
    }
}

Tabii bunun çalışması için RouteConfig'deki ayarınız şöyle olmalı:
C#:
routes.MapRoute(
    name: "Localized",
    url: "{lang}/{controller}/{action}/{id}",
    defaults: new { lang = "tr", controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Bu sayede artık seçilen dile göre URL'ye otomatik olarak "/tr" "/en" olarak eklenecektir. Controller adlarını da lokalize etmek istiyorsanız size sağladığım ControllerFactory üzerinde matching yapabilirsiniz ki bu maalesef performans zaafiyetine sebep olur.

Ayrıca kodda elimden geldiğince POST, Ajax isteklerini redirect etmemesi için uğraştım. Fakat gözümden kaçan noktalar olabilir. Burası için kendi kurallarınızı belirleyebilirsiniz, zira HttpContext'e erişip dilediğiniz noktayı kontrol edebilirsiniz.

Ardından yazdığımız bu ControllerFactory'i kullanması için Global.asax.cs üzerinde şu kodu eklememiz gerekiyor:

C#:
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
 
Çok teşekkür ederim hocam. Yarın bilgisayara geçtiğim gibi deneyeceğim. İlginiz için çok teşekkürler.
 
Bu siteyi kullanmak için çerezler gereklidir. Siteyi kullanmaya devam etmek için çerezleri kabul etmelisiniz. Daha Fazlasını Öğren.…