I am trying to get localization to work in an asp.net mvc project using monodevelop on mac. I have added a translation project and translated the text 'Welcome' in danish.
public class HomeController : Controller
{
public ActionResult Index ()
{
var culture = CultureInfo.CreateSpecificCulture("da");
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
Mono.Unix.Catalog.Init("i8n1", "./locale");
ViewData ["Message"] = Mono.Unix.Catalog.GetString("Welcome");
return View ();
}
}
But the text does not get translated.
Any ideas?
You'll need the full path to your locale folder.
MonoDevelop does something like this (edited for brevity)
string location = System.Reflection.Assembly.GetExecutingAssembly().Location;
location = Path.GetDirectoryName(location);
string catalogPath = Path.Combine (location, "locale");
Catalog.Init ("monodevelop", catalogPath);
Mono.Unix.Catalog is not suitable for ASP.NET. It uses a 'per environment' approach whereas for ASP.NET you need a 'per thread' approach.
This library is definitely worth a look as an alternative http://sourceforge.net/p/gettextnet/
For reference: http://lists.ximian.com/pipermail/mono-devel-list/2008-March/027174.html
The answer is here: http://mono.1490590.n4.nabble.com/Mono-Unix-Catalog-Init-where-does-it-get-the-locale-from-td1532586.html
public static void Main (string[] args)
{
var culture = CultureInfo.CreateSpecificCulture ("de");
Thread.CurrentThread.CurrentCulture = culture;
Environment.SetEnvironmentVariable ("LANGUAGE", "de_DE");
Catalog.Init ("i8n1", "./locale");
Console.WriteLine (Catalog.GetString("Hello World!"));
}
And it works for me on Ubuntu/Mono. Thanks to Vladimir for good question and to Jonathan for great answer.
Related
What is the correct way to find the absolute path to the App_Data folder from a Controller in an ASP.NET MVC project? I'd like to be able to temporarily work with an .xml file and I don't want to hardcode the path.
This does not work:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
string path = VirtualPathUtility.ToAbsolute("~/App_Data/somedata.xml");
//.... do whatever
return View();
}
}
I think outside of the web context VirtualPathUtility.ToAbsolute() doesn't work.
string path comes back as "C:\App_Data\somedata.xml"
Where should I determine the path of the .xml file in an MVC app?
global.asax and stick it an application-level variable?
ASP.NET MVC1 -> MVC3
string path = HttpContext.Current.Server.MapPath("~/App_Data/somedata.xml");
ASP.NET MVC4
string path = Server.MapPath("~/App_Data/somedata.xml");
MSDN Reference:
HttpServerUtility.MapPath Method
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
This is probably a more "correct" way of getting it.
I try to get in the habit of using HostingEnvironment instead of Server as it works within the context of WCF services too.
HostingEnvironment.MapPath(#"~/App_Data/PriceModels.xml");
The most correct way is to use HttpContext.Current.Server.MapPath("~/App_Data");. This means you can only retrieve the path from a method where the HttpContext is available. It makes sense: the App_Data directory is a web project folder structure [1].
If you need the path to ~/App_Data from a class where you don't have access to the HttpContext you can always inject a provider interface using your IoC container:
public interface IAppDataPathProvider
{
string GetAppDataPath();
}
Implement it using your HttpApplication:
public class AppDataPathProvider : IAppDataPathProvider
{
public string GetAppDataPath()
{
return MyHttpApplication.GetAppDataPath();
}
}
Where MyHttpApplication.GetAppDataPath looks like:
public class MyHttpApplication : HttpApplication
{
// of course you can fetch&store the value at Application_Start
public static string GetAppDataPath()
{
return HttpContext.Current.Server.MapPath("~/App_Data");
}
}
[1] http://msdn.microsoft.com/en-us/library/ex526337%28v=vs.100%29.aspx
Phil Haak has an example that I think is a bit more stable when dealing with paths with crazy "\" style directory separators. It also safely handles path concatenation. It comes for free in System.IO
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
However, you could also try "AppDomain.CurrentDomain.BaseDirector" instead of "Server.MapPath".
string filePath = HttpContext.Current.Server.MapPath("~/folderName/filename.extension");
OR
string filePath = HttpContext.Server.MapPath("~/folderName/filename.extension");
This way i got the hosting path.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace IHostingEnvironmentExample.Controllers
{
public class HomeController : Controller
{
private IHostingEnvironment _env;
public HomeController(IHostingEnvironment env)
{
_env = env;
}
public IActionResult Index()
{
var webRoot = _env.WebRootPath;
var file = System.IO.Path.Combine(webRoot, "test.txt");
System.IO.File.WriteAllText(file, "Hello World!");
return View();
}
}
}
https://forums.asp.net/t/1696005.aspx?How+to+get+Local+Server+path+in+mvc
string Index = i;
string FileName = "Mutton" + Index + ".xml";
XmlDocument xmlDoc = new XmlDocument();
var path = Path.Combine(Server.MapPath("~/Content/FilesXML"), FileName);
xmlDoc.Load(path); // Can use xmlDoc.LoadXml(YourString);
this is the best Solution to get the path what is exactly need for now
Would it be a good idea, and executable, to use the ASP.NET MVC View Engine to render html to be sent out by email?
I know it's possible to let the view render into a string. So that could be use to build the mail message.
Since ASP.NET MVC is already used in the application, I get to use all practical ASP.NET MVC stuff without having to use Brail or NVelocity for my "mail views".
Good idea? Any caveats? Examples :) ?
Yes it is a good idea and relatively easy to implement.
I personally think it's a good idea. Definitely better than putting a piece of markup with placeholders into the database.
The disadvantage is that you will need Visual Studio to edit those templates then recompile and redeploy the project. You wouldn't be able to "outsource" working with templates to other non-technical staff.
And yes, adding new templates would also require your personal intervention.
Here,s my version of the RenderPartialToString as an extension method (which also takes care of paths etc..):
public static class ExtensionMethods
{
public static string RenderPartialToString(this ControllerBase controller, string partialName, object model)
{
var vd = new ViewDataDictionary(controller.ViewData);
var vp = new ViewPage
{
ViewData = vd,
ViewContext = new ViewContext(),
Url = new UrlHelper(controller.ControllerContext.RequestContext)
};
ViewEngineResult result = ViewEngines
.Engines
.FindPartialView(controller.ControllerContext, partialName);
if (result.View == null)
{
throw new InvalidOperationException(
string.Format("The partial view '{0}' could not be found", partialName));
}
var partialPath = ((WebFormView)result.View).ViewPath;
vp.ViewData.Model = model;
Control control = vp.LoadControl(partialPath);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
}
usage:
return this.RenderPartialToString("YourPartialView", yourModel);
hope this helps..
jim
You can use MVCMailer NuGet - it uses the MVC view templates and you just write a single line of code to do this!
So for example in the code behind of a web form aspx page I would like to be able to do things like
string textBoxHtml = Html.TextBox("MyTextBox");
Is this possible?
Is the source code available to fork for webforms?
Possible? Yes.
The entire MVC source is available at:
http://www.microsoft.com/downloads/details.aspx?FamilyID=53289097-73ce-43bf-b6a6-35e00103cb4b&displaylang=en
Good luck!
You'll quickly find that pulling bits of code out of MVC is like only wanting a banana and getting the gorilla holding it. ;)
Here's something that is working for me so far.
public static class PageCommon
{
public static System.Web.Mvc.UrlHelper GetUrlHelper(this System.Web.UI.Control c)
{
var helper = new System.Web.Mvc.UrlHelper(c.Page.Request.RequestContext);
return helper;
}
class ViewDataBag : IViewDataContainer
{
ViewDataDictionary vdd = new ViewDataDictionary();
public ViewDataDictionary ViewData
{
get
{
return vdd;
}
set
{
vdd = value;
}
}
}
public static System.Web.Mvc.HtmlHelper GetHtmlHelper(this System.Web.UI.Control c)
{
IViewDataContainer x;
var v = new System.Web.Mvc.ViewContext();
var helper = new System.Web.Mvc.HtmlHelper(v, new ViewDataBag());
return helper;
}
I am getting crazy with the localization of an MVC application.
After a recent question of mine I have followed this approach:
The language is stored in Session["lang"]
Each controller inherits from my own BaseController, which overrides OnActionExecuting, and in this method reads the Session and sets CurrentCulture and CurrentUICulture
This works great, until the Data Annotation Layer comes in. It seems like it gets called BEFORE the action itself is executed, and therefore it always gets the error messages in the default language!
The fields declarations go like this:
[Required(ErrorMessageResourceName = "validazioneRichiesto", ErrorMessageResourceType = typeof(Resources.Resources))]
public string nome { get; set; }
So, is there any reasonable place where I can put the call?
I initialize the Data Annotation Model Binder in my Controller constructor.
public CardController() : base() {
ModelBinders.Binders.DefaultBinder =
new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();
}
So, since Session is always null in the controller's constructor, and the action override is called AFTER the data annotation has validated the fields, where can I possibly set the CurrentCulture and CurrentUICulture to get localized errors?
I tried putting the CurrentCulture and CurrentUiCulture in Application_* (e.g. Application_AcquireRequestState or Application_PreRequestHandlerExecute) seems to have no effect...
As the culture is a global user setting, I am using it in the global.asax.cs file in the Application_BeginRequest method.
It does the work for me (using cookies) but the Session is also available there.
EDIT:
/by Brock Allen:
http://www.velocityreviews.com/forums/t282576-aspnet-20-session-availability-in-globalasax.html/
Session is available in PreRequesthandlerExecute.
The problem is that your code is being executed for every request into the server, and some requests (like ones for WebResourxe.axd) don't utlilize
Session (because the handler doesn't implement IRequireSessionState). So change your code to only access Session if that request has access to it.
Change your code to do this:
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
SetCulture();
}
Anyway, not sure if it works with mvc
After reading your question more carefully, I think that your problem is more in the way the resources are compiled.
Check in the Resource.resx properties, find Build Action and set it to Embedded Resource
Also change Custom Tool to PublicResXFileCodeGenerator
alt text http://img208.imageshack.us/img208/2126/captuream.png
I have tested a mini solution and works perfectly.
If you have more problem, I can send the example to you.
Another approach you can use is to put the lang in the URL, with this benefits:
The site is spidered by search engines in different languages
The user can send a URL to a friend in the selected language
To do this, use the Application_BeginRequest method in Global.asax
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim lang As String
If HttpContext.Current.Request.Path.Contains("/en/") Then
lang = "en"
Else
lang = "es"
End If
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(lang)
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang)
End Sub
See this question for more info on how to implement it
The OP posted the final solution as the following, thanks to the accepted answer by twk:
void Application_PreRequestHandlerExecute(object sender, EventArgs e) {
if (Context.Handler is IRequiresSessionState ||
Context.Handler is IReadOnlySessionState) {
if (Session["lang"] == null) {
Session["lang"] = "it";
}
if (Request.QueryString["lang"] == "it" || Request.QueryString["lang"] == "en") {
Session["lang"] = Request.QueryString["lang"];
}
string lang1 = Session["lang"].ToString();
string lang2 = Session["lang"].ToString().ToUpper();
if (lang2 == "EN")
lang2 = "GB";
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
}
}
I'm looking for a method of storing routing information in my web.config file in addition to the Global.asax class. The routes stored in the configuration file would need to take higher precedence than those added programmatically.
I've done my searching, but the closest I can come up with is the RouteBuilder on Codeplex (http://www.codeplex.com/RouteBuilder), but this doesn't work with the RTM version of MVC. Does a solution out there exist compatible with the final 1.0?
I can't guarantee the following code will work, but it builds :) Change the Init method in RouteBuilder.cs to the following:
public void Init(HttpApplication application)
{
// Grab the Routes from Web.config
RouteConfiguration routeConfig =
(RouteConfiguration)System.Configuration.ConfigurationManager.GetSection("RouteTable");
// Add each Route to RouteTable
foreach (RouteElement routeElement in routeConfig.Routes)
{
RouteValueDictionary defaults = new RouteValueDictionary();
string[] defaultsArray = routeElement.Defaults.Trim().Split(',');
if (defaultsArray.Length > 0)
{
foreach (string defaultEntry in defaultsArray)
{
string[] defaultsEntryArray = defaultEntry.Trim().Split('=');
if ((defaultsEntryArray.Length % 2) != 0)
{
throw new ArgumentException("RouteBuilder: All Keys in Defaults must have values!");
}
else
{
defaults.Add(defaultsEntryArray[0], defaultsEntryArray[1]);
}
}
}
else
{
throw new ArgumentException("RouteBuilder: Defaults value is empty or malformed.");
}
Route currentRoute = new Route(routeElement.Url, defaults, new MvcRouteHandler());
RouteTable.Routes.Add(currentRoute);
}
}
Also, feel free to delete the DefaultsType class. It was needed because the defaults system was much more complicated back in CTP than in RTM.
Edit: Oh and add using System.Web.Routing; to the top and make sure you add System.Web.Mvc and System.Web.Routing as references.
look at this:
http://weblogs.asp.net/fredriknormen/archive/2008/03/11/asp-net-mvc-framework-2-define-routes-in-web-config.aspx