Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

ASP.NET MVC, Localization and Westwind.Globalization for Db Resources


:P
On this page:

I’ve been hard at work with version 2.0 of the Westwind.Globalization library, which is a major update from version 1.x in terms of database support and general features, as well as a brand new much more user friendly (IMHO) Web resource editing interface. As a result I’m going to be posting a few related blog entries in the next couple of weeks.

Westwind.Globalization is a library to provide database driven resources in .NET using Sql Server, MySql, SqLite and SQLCe (for now) for providers. It implements standard resource providers and resource managers  and as such it can be used as a drop in replacement for Resx resources. There’s a ton of additional functionality including serving resources to JavaScript client applications, to importing and exporting Resx resources, generating strongly typed classes and of course the interactive Web Resource editor that makes it easy to edit resources interactively.

Westwind.Globalization and MVC

The focus of this post is to discuss ASP.NET MVC support of Westwind.Globalization. I’ve seen a number of questions and discussions that show confusion over whether Westwind.Globalization supports MVC properly. The short answer is – it does support MVC in every way. For the long answer read on. In this post I’ll give a short overview of how localization in MVC works in general, and then show how you can easily use Westwind.Globalization with MVC for those same scenarios.

Version 2.0 (in late beta now) in particular focuses on the MVC and also SPA scenarios (which I’ll leave for another post soon) and the new documentation that’s on GitHub along with the rest of the project now is much more generic and provides examples for both MVC (and non-Web projects as well) as well as WebForms.

Before we look at Westwind.Globalization let’s quickly review how localization – and specifically text resource localization – works in ASP.NET MVC in general.

Strongly Typed Resources and MVC

ASP.NET MVC unlike WebForms takes a much simpler approach to localization, relying mostly on strongly typed resources to localize content. MVC uses resources much more like all other project types in .NET, rather than the custom ResourceProvider model that ASP.NET has traditionally used.

MVC applications instead rely on strongly typed resources which in turn are tightly coupled wrapper classes around the Resx ResourceManager. The generated strongly typed resource classes are hardcoded to the standard .NET ResourceManager that uses Resx and there’s no easy way – short of generating the classes differently – to swap out and use a different ResourceManager. Westwind.Globalization provides a custom strongly typed resource generation mechanism via the Resource Editor Tool or via code based classes you can call and it essentially creates classes similar to the Resx generated ones but uses database (as well as Resx) resources.

Strongly typed resources work via static classes that attach to a static ResourceManager instance property. This resource manager is associated with a given ResourceSet which is a series of .Resx files with the same base name: ie. Resource.resx, Resources.resx.de, Resources.resx.fr. A given resource manager caches all its loaded ResourceSets internally in memory after a resource is accessed for the first time in a given resource set. This is an important point for all resource related tasks in .NET – resource sources tend to be accessed only once for each ResourceSet/Locale – after that all the resources are cached in memory for very fast subsequent loading. This also means that database resources use the database only initially before also using those same caching mechanisms that Resx resources enjoy.

Using Strongly Typed Resources in MVC

For MVC Views you can simply embed strongly typed resource properties as values into Razor views:

@using Westwind.Globalization.Sample.Properties
…
<button id="btnAdd" title="@Resources.Add_Title">@Resources.Add</button>

There’s nothing tricky about this and IMHO it’s a great simplification over the morass of features that existed in WebForms. WebForms had global resources, local resources, a Meta resources provider and resource expressions all using some obscure interfaces that could often fail horribly if resources weren’t available. MVC’s usage of plain Resx resources and strongly typed resources makes this process much easier even if it is tightly coupled to Resx resources and – it’s type safe so the compiler can help you find missing resource errors to boot.

In case you’re new to localization in .NET, standard .Resx localization works with Resx files that are essentially XML text files that contain resources as key value pairs. Resources values often are text, but they can also be images or other binary content. One or more .Resx files comprise a ResourceSet with an individual version for each localeId that you have localized for. So a typical .Resx setup looks like this:

ResxMVC

Each ‘ResourceSet’ is a group of .Resx files, one for each locale ID you localize for. The Resx files are essentially XML files that contain the localized resource content. You have a base .resx file which is considered the Invariant locale (the one without an explicit locale extension) and typically that one will contain the primary language – English for me. Then you have additional resx files with the same name but a different locale extension to provide the localized versions of the resources. Above there’s only LocalizationForm.de.resx but if you had additional locales to localize for you would add additional resource files for each of those locales. Locales can be top level locales like de, fr, en or culture specific versions such as en-US, de-DE, de-CH. ResourceManagers can retrieve resources using Resource Fallback which allows for resources to fall back to the next less specific region that has a value. So if you ask for a resources in en-US but you don’t have en-US or en resources, it’ll fall back to invariant: en-US->en->Invariant.

The designers show one resourceset for a specific locale id. When you open the designer for specific resource file you see the resources for that specific locale (Invariant/English in my case):

ResourceEditor

At the top of the designer there’s the Access Modifier dialog that allows you to specify if and how strongly typed resources are generated. Set to Public or Internal generates a class with the specified project visibility. The class then maps each of the resource keys in the invariant .resx file to properties of a class:

public class LocalizationForm { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal LocalizationForm() { } /// <summary> /// Returns the cached ResourceManager instance used by this class. /// </summary> [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Westwind.Globalization.Sample.LocalizationAdmin.Properties.LocalizationForm", typeof(LocalizationForm).Assembly); resourceMan = temp; } return resourceMan; } } /// <summary> /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// </summary> [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// <summary> /// Looks up a localized string similar to Add. /// </summary> public static string Add { get { return ResourceManager.GetString("Add", resourceCulture); } } /// <summary> /// Looks up a localized string similar to Add a new individual resource.. /// </summary> public static string Add_Title { get { return ResourceManager.GetString("Add.Title", resourceCulture); } }

}

Essentially this code creates a static ResourceManager instance that holds a dictionary of resource sets for each of the supported locales which are loaded when you access the first resource. The resource manager loads each used resource set, and then performs a look up with resource fallback if an exact match can’t be found to return the resource in question.

You can then use these resources in your Razor views or inside of code as simple type names.

In Razor:

@using Westwind.Globalization.Sample.Properties
…
<button id="btnAdd" title="@Resources.Add_Title">@Resources.Add</button>

Inside of Controller or other MVC application code you can simply access the resource:

string text = string.Format("{0} - {1}", Resources.Add_Title, Resources.Title);

Easy enough, right? It works the same resources have always worked in library or console applications. There’s no special mechanism to bind resource names or designers to step in between because MVC’s UI is simply text and the strongly typed resources work very well for that.

Model Validation

Model Validation to show error information is a big feature in ASP.NET MVC and localizing the validation messages is a just as  important. The process of localizing these validation messages is a bit ugly, but due to the attribute based nature of validation which doesn’t directly allow for assigning dynamic values at runtime –  the ugliness is a necessary side effect.

Model Validation with resources looks like this:

public class ViewModelWithLocalizedAttributes
{
    [Required(ErrorMessageResourceName = "NameIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Name { get; set; }

    [Required(ErrorMessageResourceName = "AddressIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Address { get; set; }
}

The key here are the Attribute parameters ErrorMessageResourceName and ErrorMessageResourceType which point at a resource type and property name respectively. The first attribute effectively points at the Resources.NameIsRequired property of my  strongly typed resource class. As you can see for this to work you have to have strongly typed resources in place. Behind the scenes MVC uses Reflection to call GetProperty() on the provided type to retrieve the localized value via the ResourceManager.

Setting Resource Culture

One of the key aspects of localization is to detect the browser’s locale and then setting the applications Culture and UICulture to match the browser’s settings. ASP.NET provides some basic features for this using a configuration value in web.config and the system.Web section:

 <globalization uiCulture="auto:en-US" culture="auto:en-US" />

This automatically sniffs the client browsers locale (if available) and sets ASP.NET request thread’s Culture and UICulture to that locale. If the client doesn’t provide a Accept-Language header, the application falls back to en-US as the default locale I’ve specified after the colon.

This works fairly well in some scenarios, but usually this is not what you actually want – more typically your applications are localized only for a few specific locales. Very few applications on Western servers are likely to be localized for Mongolian or Zulu. Rather you typically want your application to switch to only a few locales that your application is actually localized for.

I wrote about how to do this before but with the Westwind.Web NuGet package and the WebUtils class (also part of the Westwind.Globalization package) you can simply do:

WebUtils.SetUserLocale(currencySymbol: "$",allowedLocales: "en,de,fr");

This limits the locale switching to en,de,fr – all others fall back to the server’s default locale which in my case is en-US. This way I can localize for the languages I support, but for everything else that’s not supported it falls back to the default of English.

Westwind.Globalization – Database Resources

Ok so now we know how MVC works with localization, lets look at Westwind.Globalization and MVC support.

Database driven resources are a powerful thing in that let you more easily manage your resources. Rather than the limited Resource editor in Visual Studio you can create a more flexible resource editor interface or update resources programmatically. To me personally, I like the fact that I can dynamically edit resources in a live application and see the change immediately. I also like the fact that I can create custom views where I can see all the resources associated with a given ResourceID at a glance and then edit and use localization tools to translate the text in one place.

Westwind.Globalization comes with this online resource editor that makes it very quick to edit and enter resources manually:

or you can use translation services to provide at least some rudimentary translation that you can modify or adjust:

Resources can be imported or manually created. The Resx import can pull in existing resources from a project. The ResourceProviders and ResourceManagers in Westwind.Globalization can then expose the database resources in the same way you use .Resx resources today, as well as providing strongly typed resources or exporting resources back out to Resx resources if you rather run production apps with Resx instead of dynamic database resources.

Westwind.Globalization and MVC

The original version of Westwind.Globalization was built for WebForms because that’s what the prominent platform was at the time. However, the library and related tooling has always supported ASP.NET MVC features all along – it just wasn’t quite as obvious as the WebForms features. In Version 2 a lot more focus is on the core features and thus the MVC support is featured more prominently and the documentation has been completely rewritten.

Westwind.Globalization works by implementing custom ASP.NET ResourceProviders (2 different ones) as well as a standard .NET ResourceManager that can pull resources out of a database. Both the providers and resource manager use a custom data layer to pull resources out of a database instead of from Resx resources. The data-layer provides for the basic ResourceSet loading as well as more sophisticated support for loading, updating and generally managing resources of resources including generation of strong classes, Resx and JavaScript exports etc. across all supported providers.

Because the library relies on standard .NET resource interfaces, to expose the actual resources, the ResourceProviders and ResourceManager cache ResourceSets after initially getting loaded on first access. The database gets hit only once per resource set for the lifetime of an AppDomain, so if I have 2 resourceSets with two localized languages the database effectively gets hit a max of 4 times. Not every resource access fires through the database as resources are cached in the standard .NET ResourceSet resource dictionaries. For this reason there is also support for local data engines like SQLite and SqlCompact, because resource management and access has very low impact on the database while providing the ability to dynamically edited resources in real time.

Westwind.Globalization offers a number of different ways to expose databased resources.

DbRes – Explicit Resource Loading

The static DbRes class is a very simple resource implementation that lets you easily translate resources based on string based values. This is similar to ResourceManager.GetObject() or GetString(), but with a twist that it will return the resource Id value you passed in. What this allows you to do is use a string based localization where default values are always available. This implementation uses ResourceManagers and writes out resources to disk:

// Using current UiCulture – empty resource set name
DbRes.T("HelloWorld");

// Exact match with resource - Hallo Welt
DbRes.T("HelloWorld", "Resources", "de");

// Resource Fallback to de if de-CH doesn't exist - Hallo Welt
DbRes.T("HelloWorld", "Resources", "de-CH");

DbRes.T() returns the matching resource value for the specified locale or the default locale the application is currently running in. You can explicitly override the locale. If a resourceId cannot be found the resource ID value is returned – so you always get something back, never empty resources.

You can also easily use DbRes in Razor markup:

@DbRes.T("HelloWorld", "Resources")

This is a real easy way to use Db generated resources without any further setup or configuration.

This was a feature a number of developers have requested as they didn’t want to have to explicitly pre-create resources in a resource editor and didn’t want to have to deal with exporting resources to strongly typed resources constantly. Rather – combined with Westwind.Globalization’s AddMissingResources option – you could simply embed resource expressions into the page and the resource keys are then automatically generated for later editing (or simply leaving them as is).

Further some people prefer using the Resource ID as the actual default text. Resource IDs don’t have to be ‘ids’ but can be any string and so you can easily have a resource key like “Hello World” which then provides the default ‘key’. The idea is that you can use these text based resource Ids to provide the default locale value that ensures that at least the default text will always show up rather than a resource key. It’s not for every application but I’ve heard this request over and over again, and I’ve been involved in several projects that use this approach to great effect. It’s especially appealing to those applications that used in the ‘make it localization ready camp’ where the actual translation to other languages doesn’t occur until much later (or maybe never at all). Using this approach the resources get into the project and the database fairly transparently.

Strongly Typed Resources

As mentioned at the beginning of this post ASP.NET MVC internally is more or less designed around strongly typed resources. You can also generate strongly typed resources from database resources with Westwind.Globalization. This can be handled through the Web Administration interface of the tooling or via a class that can automate the process.

CreateClasses

ResourceExport2

Clicking the Create Classes button generates a set of strongly typed classes for each of the resource sets maintained in the database. The class handles several different ways of returning resources using either DbResourceManager, or using the configured ASP.NET ResourceProvider or using plain .NET Resx resources. The latter is useful if you decide to later export your database resources to Resx – a simple switch in the generated source file via a static property can switch between any of these operational modes.

Here’s what this generated file looks like:

public class GeneratedResourceSettings
{
    // You can change the ResourceAccess Mode globally in Application_Start        
    public static ResourceAccessMode ResourceAccessMode = ResourceAccessMode.AspNetResourceProvider;
}

public class Resources { public static ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { var temp = new ResourceManager("Westwind.Globalization.Sample.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } private static ResourceManager resourceMan = null; public static System.String Add { get { if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider) return (System.String) HttpContext.GetGlobalResourceObject("Resources","Add"); if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.Resx) return ResourceManager.GetString("Add"); return DbRes.T("Add","Resources"); } } public static System.String Add_Title { get { if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider) return (System.String) HttpContext.GetGlobalResourceObject("Resources","Add_Title"); if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.Resx) return ResourceManager.GetString("Add_Title"); return DbRes.T("Add_Title","Resources"); } } public static System.String NameIsRequired { get { if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider) return (System.String) HttpContext.GetGlobalResourceObject("Resources","NameIsRequired"); if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.Resx) return ResourceManager.GetString("NameIsRequired"); return DbRes.T("NameIsRequired","Resources"); } } public static System.String HelloWorld { get { if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider) return (System.String) HttpContext.GetGlobalResourceObject("Resources","HelloWorld"); if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.Resx) return ResourceManager.GetString("HelloWorld"); return DbRes.T("HelloWorld","Resources"); } } public static System.String AddressIsRequired { get { if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider) return (System.String) HttpContext.GetGlobalResourceObject("Resources","AddressIsRequired"); if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.Resx) return ResourceManager.GetString("AddressIsRequired"); return DbRes.T("AddressIsRequired","Resources"); } } }

Unlike string resources using DbRes, strongly typed resources require that you recompile your application after the classes have been generated. The process of generation of the strongly typed classes is not automatic so whenever you add resources in the resource editor you have to explicitly regenerate – unless you create some build drive code that can automatically perform this task which can be accomplished with a couple of lines of code.

Once generated, you can then use the generated strongly typed resource classes in the same way you would use strongly typed resources in any other ASP.NET MVC or other project.

So this still works just as it did before:

@using Westwind.Globalization.Sample.LocalizationAdmin.Properties
…
<button id="btnAdd" title="@Resources.Add_Title">@Resources.Add</button>

as does:

string text = string.Format("{0} - {1}", Resources.Add_Title, Resources.Title);

IOW, exactly the same way as you could before with Resx resources.

Likewise MVC Model Validation also works off these strongly typed classes in the same way as before:

public class ViewModelWithLocalizedAttributes
{
    [Required(ErrorMessageResourceName = "NameIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Name { get; set; }

    [Required(ErrorMessageResourceName = "AddressIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Address { get; set; }
}

As you can see you can pretty much do everything you can do with standard Resx resources using the database provider directly. If you still find scenarios where you can’t work with DbResources – maybe you have to pull resources from several different sources or you have to work with library projects – you also have the option to export resources back to Resx resources.

Exporting Database Resources to .Resx

The administration interface of Westwind.Globalization – as well as some of the support classes – allow you to import and export resources from and to .Resx. So if you find that after you’ve localized your application using database resources that you’d rather ship the application with fixed Resx resources that are guaranteed to always work regardless of whether a database is live or not – you can do that as well.

On the Web Resource Editor you can use the Import or Export Resx option. To export resources you can specify a base path to export to:

ExportResources

This creates individual .Resx files for each resource set and resource locale in the specified folder. There’s a project mode – Project here and what is appropriate for MVC or standard .NET projects that use Resx resources – or WebForms mode which creates Local and Global resources instead.

As you can see on the resource dialog you can actually use the resource editor to export resources to an arbitrary location on the local disk. This actually lets you export resources to non-Web projects and lets you use the Resource Editor as a general purpose resource editor, which is pretty cool…

Once the resources have been exported you won’t automatically have strongly typed resources. In order to get them you have two choices:

  • Manually set the Resource Generator on invariant .Resx file
  • Generate strongly typed resources from the db resources

Manually set the Visual Studio Resource Generator

The first option is to simply open each invariant .Resx file and set the resource generator to the appropriate Internal or Public class generator as shown earlier on the Resx editor form earlier. You can also do this at the Visual Studio Project file level and select all of the .Resx files, then set the Custom Tool in one go:

ResxGenerator

Recompile and you have your strongly typed resources. Once the .Resx files and strongly typed resource files exist, you can simply re-export when you have changes and those changes will update the Resx files. Recompiling will re-generate the strongly typed classes automatically so you’re good to go – it’s a one time configuration thing.

Note that it’s entirely possible to work with database resources for editing purposes only and then simply dump the resources out to Resx as you make changes for testing and deploying. You don’t actually have to run with db resources in a live environment using this approach. Live db resources are useful if you want to change resources in real time while the application is running and see the changes reflected immediately. Otherwise exporting to Resx files offers a good option that provides all the features you expect from resx features (portability as part of the compiled code, always available etc.).

Export Strongly Typed Classes from Web Db Resource Editor

The other option is to export Resx resources and then also generate the strongly typed classes from the Web Resource Editor interface as shown earlier. When the strongly typed classes have been exported you can specify that you want resources served from Resx files instead.

To make this work you’ll have to set one switch on application startup:

protected void Application_Start() {
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes);
GeneratedResourceSettings.ResourceAccessMode = ResourceAccessMode.Resx;

}

This directs the Property retrieval to look for resources in Resx files using the static resource manager. This works exactly the same as strongly typed Resx resources but you have a single step to generate this class and setting.

So now if you want to switch between db resources and resx resources you can simply comment/uncomment the above flag.

Summary

As you can see Westwind.Globalization can work great in ASP.NET MVC and there are many options of how you can work with resources using this tool. You can run entirely with Db resources, or you can choose to import .Resx resources, edit them and then run your production code with standard .Resx resources with just a couple of extra steps. You have lots of choices on how you can run your localized resources and the localization tasks with this library.

Version 2.0 brings a number of big improvements and is much more focused on the MVC scenario since that paradigm has pretty much taken over. WebForms users don’t need to fret though: All the WebForms specific features like ResourceProvider, Meta Resources and Resource expressions, designer  support etc. all continue to work.

Version 2.0 is in late beta and packages are available on NuGet, but since this is a beta make sure you allow Pre-Release packages to be loaded. The current release is beta 3 which released last night and I hope to get the final release out in the next couple of weeks at the latest.

Resources


The Voices of Reason


 

Douglas Hammon
March 18, 2015

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Good stuff. Looking forward to your post about SPA scenarios

KA
March 24, 2015

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Hey Rick,

Really cool stuff, I am impressed.
I have one question. Saw that for retrieving the resources you use HttpContext.GetBlobal or local resources. Is there any other way to retrieve the resources. For example if you use a WCF Service and is on TCP the HttpContext is null.
I mean somehow to be protocol independent.

Thanks.

Rick Strahl
March 24, 2015

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

@KA - The core library can be used completely outside of the ASP.NET context, so if you have a Windows (non Web) app you can use database resources by using DbResourceManager, DbRes as well as generated strongly typed resources (which can switch between the db provider and Resx). 

When running under ASP.NET using the Web package you get two different project options to run under: WebForms or Project where WebForms uses Local/Global Resource folders and naming conventions or Project which uses simple files and that can be used with any .NET project and application.

All the front end stuff in the localization UI depends on the Web package and it assumes that ASP.NET is available, so if you run the Web Admin form HttpContext is always there. All the tooling that is used however is available in classes that you can call directly from your own applications/code.

For a WCF project you would just use the core library that has no dependencies on HttpContext - there are only two support classes in the core library that rely on System.Web - DbRes (which has helpers that return HtmlString) and the various exporters that default paths to the default web root path *if* Http context is available.

if you find other places in the core library, please file an issue on GitHub and I'll take a look at it.

David
April 30, 2015

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

By the way Rick, it looks like a little pressure on the ASP.Net team seems to have worked, and they have now committed to bringing back Single File Generators, to support T4 and resx files. The team hope to have that for next release of VS2015.

Amine
June 27, 2015

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Hello
and Thanks for post,
I am wondering if :
- multiple selection-deletion of resource elements is offered on Backend WebApp
- if the backup, import-restore is available as option because in many cases, we have the machines of developers that need to import resources filled by Virtual Assistants and persons that have more time for localization/globalization stuff. I know that with Sql scripts we can do it, but mainly in managing duplicate entries, will be a pain between more than 2 machines.

V Grimes
February 23, 2016

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Hello Rick, this is fantastic. It is easy to set up, easy to use and covers all my needs for the most part. I have already got a proof-of-concept working directly in my MVC application, but I have one question, which may be more a "best practices" question in addition to - is this possible? So, you show an example for a Model's validation data annotations, but just for Required. Is this possible for the Display attribute as well? Also, does this technique you show for validation only apply to strongly-typed resources? I suspect it does. If we only plan to use dynamically-generated resource values from the database, I think I could probably override the implementation System.ComponentModel.DataAnnotations.DisplayAttribute (haven't tried it yet) to retrieve the dynamically localized value, however would that be a bad practice since these annotations are intended to be static?

I hope I am making sense, but if you need further clarification about what I'm trying to do, please feel free to email me.
Thank you!
-V Grimes

Sergey
June 16, 2016

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Hi Rick,

I use your project to localize MVC application and it does work. However, I've noticed very unpleasant behavior of the resourceEditor. If I click the red flag near to the element to be localized, it opens the resource editor and pops up the 'Add resource' dialog with wrong ID. For example, the data-resource-id of the wrapping element is 'Logs' and this resource does not exist in the database. However, when I click the red flag, the 'Add resource' dialog always displays the first existing resource in the database.

Do you know by chance what might be the problem?

Below is an example markup.

<div data-resource-id="Logs">Test</div>
<div data-resource-id="Users">Test</div>

Both resources do not exist in the database. When I click 'Users' it works as expected. If I click 'Logs', it opens the 'Add resource' dialog with another existing resource. I completely do not understand that.

Best regards,
Sergey

Saravanan
July 07, 2016

# re: ASP.NET MVC, Localization and Westwind.Globalization for Db Resources

Hi Rick,

I have a question like how to maintain the user level language in Global file?

#My Scenario

User able to select the language while creating a account and login to the respective account application will run based on user selected language. How to maintain the above scenario in Global.asax file. Please advice me how to do that.

Thanks,

Saravanan

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024