Last active
September 19, 2018 10:45
-
-
Save ukcoderj/d39565aa507438763f771361bfb36dcf to your computer and use it in GitHub Desktop.
.NET Core Port Of afana.me's excellent i18n internationalisation (no magic strings in code - http://afana.me/archive/2013/11/01/aspnet-mvc-internationalization-store-strings-in-database-or-xml.aspx/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
namespace YourWebsite.i18n | |
{ | |
/// <summary> | |
/// This is a .net core port of | |
/// http://afana.me/archive/2013/11/01/aspnet-mvc-internationalization-store-strings-in-database-or-xml.aspx/ | |
/// The only major change is in i18n InitResources, where paths are now different. See startup.cs for initialization. | |
/// </summary> | |
public interface IResourceProvider | |
{ | |
object GetResource(string name, string culture); | |
} | |
public abstract class BaseResourceProvider : IResourceProvider | |
{ | |
// Cache list of resources | |
private static Dictionary<string, ResourceEntry> resources = null; | |
private static object lockResources = new object(); | |
public BaseResourceProvider() | |
{ | |
Cache = true; // By default, enable caching for performance | |
} | |
protected bool Cache { get; set; } // Cache resources ? | |
/// <summary> | |
/// Returns a single resource for a specific culture | |
/// </summary> | |
/// <param name="name">Resorce name (ie key)</param> | |
/// <param name="culture">Culture code</param> | |
/// <returns>Resource</returns> | |
public object GetResource(string name, string culture) | |
{ | |
if (string.IsNullOrWhiteSpace(name)) | |
throw new ArgumentException("Resource name cannot be null or empty."); | |
if (string.IsNullOrWhiteSpace(culture)) | |
throw new ArgumentException("Culture name cannot be null or empty."); | |
// normalize | |
culture = culture.ToLowerInvariant(); | |
if (Cache && resources == null) | |
{ | |
// Fetch all resources | |
lock (lockResources) | |
{ | |
if (resources == null) | |
{ | |
resources = ReadResources().ToDictionary(r => string.Format("{0}.{1}", r.Culture.ToLowerInvariant(), r.Name)); | |
} | |
} | |
} | |
if (Cache) | |
{ | |
return resources[string.Format("{0}.{1}", culture, name)].Value; | |
} | |
return ReadResource(name, culture).Value; | |
} | |
/// <summary> | |
/// Returns all resources for all cultures. (Needed for caching) | |
/// </summary> | |
/// <returns>A list of resources</returns> | |
internal abstract IList<ResourceEntry> ReadResources(); | |
/// <summary> | |
/// Returns a single resource for a specific culture | |
/// </summary> | |
/// <param name="name">Resorce name (ie key)</param> | |
/// <param name="culture">Culture code</param> | |
/// <returns>Resource</returns> | |
protected abstract ResourceEntry ReadResource(string name, string culture); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
<p>@YourWebsite.i18n.i18n.Site_Name</p> | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Hosting; | |
using System; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.IO; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Runtime.CompilerServices; | |
using System.Threading.Tasks; | |
namespace YourWebsite.i18n | |
{ | |
public partial class i18n | |
{ | |
#region "Standard content - do not change" | |
private static IResourceProvider resourceProvider; | |
private static IList<ResourceEntry> _resources; | |
public static void InitResources(IHostingEnvironment hostingEnvironment) | |
{ | |
var x = hostingEnvironment.WebRootPath; | |
resourceProvider = | |
new XmlResourceProvider(Path.Combine(hostingEnvironment.WebRootPath, @"App_Resources\Resources.xml")); | |
_resources = ((XmlResourceProvider)resourceProvider).ReadResources(); | |
} | |
internal static void ReloadResources(IHostingEnvironment hostingEnvironment) | |
{ | |
InitResources(hostingEnvironment); | |
} | |
public static void TestLocaleSpecificRequest() | |
{ | |
var x1 = i18n.GetLocalisedStringValue(() => i18n.Site_Name, "en-GB"); | |
var x2 = i18n.GetLocalisedStringValue(() => i18n.Site_Name, "fr-FR"); | |
} | |
public static string GetLocalisedStringValue<T>(Expression<Func<T>> propertyLambda, string cultureName) | |
{ | |
var me = propertyLambda.Body as MemberExpression; | |
if (me == null) | |
{ | |
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'"); | |
} | |
var propertyName = me.Member.Name; | |
if (String.IsNullOrWhiteSpace(cultureName)) | |
{ | |
cultureName = "en-GB"; | |
} | |
// Try to get a value in the locale requested. | |
var returnValue = _resources.FirstOrDefault(i => i.Name == propertyName && i.Culture == cultureName)?.Value; | |
// No return value for the culture, fall back to english | |
returnValue = returnValue ?? _resources.FirstOrDefault(i => i.Name == propertyName && i.Culture == "en-GB")?.Value; | |
// Still no value, show a really obvious error. | |
returnValue = returnValue ?? $"(RESOURCE NOT FOUND FOR CULTURE {CultureInfo.CurrentUICulture.Name})"; | |
return returnValue; | |
} | |
/// <summary> | |
/// Get the value of a resource based on the current culture. | |
/// </summary> | |
/// <param name="propertyName">The calling property.</param> | |
/// <returns>the value of a resource based on the current culture</returns> | |
private static string GetStringValue([CallerMemberName] string propertyName = null) | |
{ | |
// Try to get a value in the locale requested. | |
var returnValue = _resources.FirstOrDefault(i => i.Name == propertyName && i.Culture == CultureInfo.CurrentUICulture.Name)?.Value; | |
// No return value for the culture, fall back to english | |
returnValue = returnValue ?? _resources.FirstOrDefault(i => i.Name == propertyName && i.Culture == "en-GB")?.Value; | |
// Still no value, show a really obvious error. | |
returnValue = returnValue ?? $"(RESOURCE NOT FOUND FOR CULTURE {CultureInfo.CurrentUICulture.Name})"; | |
return returnValue; | |
} | |
internal static List<string> GetAllCultures() | |
{ | |
return _resources.Select(_ => _.Culture).Distinct().OrderBy(_ => _).ToList(); | |
} | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
namespace YourWebsite.i18n | |
{ | |
public partial class i18n | |
{ | |
public static string Site_Name { get { return GetStringValue(); } } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1. This assumes you have placed the resources.xml in {project}/wwwroot/App_Resources/resources.xml | |
2. This also has a method to choose your culture at runtime e.g. Resources.i18n.GetLocalisedStringValue(() => i18n.Site_Name, "fr-FR"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
namespace YourWebsite.i18n | |
{ | |
public class ResourceEntry | |
{ | |
public string Name { get; set; } | |
public string Value { get; set; } | |
public string Culture { get; set; } | |
public string Type { get; set; } | |
public ResourceEntry() | |
{ | |
Type = "string"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<resources> | |
<!-- ENGLISH (this could be en-GB) --> | |
<resource culture="en-GB" type="string" name="Site_Name" value="Site Name (English)"></resource> | |
<!-- FRENCH --> | |
<resource culture="fr-FR" type="string" name="Site_Name" value="Site Name (French)"></resource> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.HttpsPolicy; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.EntityFrameworkCore; | |
using RegAndVote.Data; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.IdentityModel.Tokens; | |
using System.Text; | |
using Microsoft.Extensions.Logging; | |
namespace YouWebsite | |
{ | |
public class Startup | |
{ | |
public Startup(IConfiguration configuration) | |
{ | |
Configuration = configuration; | |
} | |
public IConfiguration Configuration { get; } | |
// This method gets called by the runtime. Use this method to add services to the container. | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
//... | |
} | |
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |
{ | |
//... | |
app.UseMvc(routes => | |
{ | |
routes.MapRoute( | |
name: "default", | |
template: "{controller=Home}/{action=Index}/{id?}"); | |
}); | |
// *** i18n Initialisation!!! *** | |
i18n.i18n.InitResources(env); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Xml.Linq; | |
namespace YourWebsite.i18n | |
{ | |
/// <summary> | |
/// .net core port of | |
/// http://afana.me/archive/2013/11/01/aspnet-mvc-internationalization-store-strings-in-database-or-xml.aspx/ | |
/// </summary> | |
public class XmlResourceProvider : BaseResourceProvider | |
{ | |
// File path | |
private static string filePath = null; | |
public XmlResourceProvider() { } | |
public XmlResourceProvider(string filePath) | |
{ | |
XmlResourceProvider.filePath = filePath; | |
if (!File.Exists(filePath)) throw new FileNotFoundException(string.Format("XML Resource file {0} was not found", filePath)); | |
} | |
internal override IList<ResourceEntry> ReadResources() | |
{ | |
// Parse the XML file | |
return XDocument.Parse(File.ReadAllText(filePath)) | |
.Element("resources") | |
.Elements("resource") | |
.Select(e => new ResourceEntry | |
{ | |
Name = e.Attribute("name").Value, | |
Value = e.Attribute("value").Value, | |
Culture = e.Attribute("culture").Value | |
}).ToList(); | |
} | |
protected override ResourceEntry ReadResource(string name, string culture) | |
{ | |
// Parse the XML file | |
return XDocument.Parse(File.ReadAllText(filePath)) | |
.Element("resources") | |
.Elements("resource") | |
.Where(e => e.Attribute("name").Value == name && e.Attribute("culture").Value == culture) | |
.Select(e => new ResourceEntry | |
{ | |
Name = e.Attribute("name").Value, | |
Value = e.Attribute("value").Value, | |
Culture = e.Attribute("culture").Value | |
}).FirstOrDefault(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment