How to switch between resx files (not using culture) - c#

What I am trying to do:
I am working on a chat bot service where I have different channels of access. On one channel I want to use my Customer.resx file and on another channel I want to use my Worker.resx file.
The problem:
I know how localization works to switch between Worker.en-US.resx and Worker.es-MX.resx, but I am unsure how to switch from one normal resx file to a completely different context one. These will each be translated into other languages so I can't simply use Customer.en-US.resx and Worker.es-MX.resx as a workaround. I do know that I can build a custom culture using the CultureAndRegionInfoBuilder, but I would rather do this in a simpler way than that, something along the lines of how you do localization. I am currently building custom cultures using this method and I have Resources.worker-en-US.resx and similar for customer, but this is a round-about way and I know there must be a better way.
For clarification I know I can grab a resource file with ResXResourceSet, but the idea was that I would like to have two separate resource files with localization for each (worker.en-US and customer.en-US).

I would combine both of these as into one because resx's are really tied to cultures/languages. You can use a strategy pattern to get the right strings at the right time.
interface IStrings { string Foo { get; } }
class WorkerStrings : IStrings { ... }
class CustomerStrings : IStrings { ... }

Related

Generic transform/map source JSON to different destination JSON's

I built a dynamic Survey type application where users can set up their template of questions/attributes that need to be captured. A template can have multiple sections, where each section contains multiple questions/attributes.
We write out the data in JSON according to the template, to be consumed or sent by webhooks, for external systems. Rather than developing a layer for each external integration to deserialize our system's object and transform it for the external system's consumption, I was thinking of writing dynamic mapper that uses a configuration schema(maybe JSON) that will map your source to your destination. Which will be great, as soon as either the source or destination changes, the configuration can just be adjusted accordingly, without having to change code and re-publish.
I'm using this system as an example, but I'm seeing multiple applications for different use cases, with the main one being a source API that needs to integrate with multiple external API's.
Source JSON example:
{
"Section1": {
"S1Attribute1": "S1Attribute1Answer",
"S1Attribute2": "S1Attribute2Answer",
"S1Attribute3": "S1Attribute3Answer"
},
"Section2": {
"S2Attribute1": "S2Attribute1Answer",
"S2Attribute2": "S2Attribute2Answer"
}
}
Destination 1 Example:
{
"SectionsFlattened": {
"S1Attribute1": "S1Attribute1Answer",
"S1Attribute2": "S1Attribute2Answer",
"S1Attribute3": "S1Attribute3Answer",
"S2Attribute1": "S2Attribute1Answer",
"S2Attribute2": "S2Attribute2Answer"
}
}
Destination 2 Example:
{
"NewSection1Name": {
"NewS1Attribute1Name": "S1Attribute1Answer",
"NewS1Attribute2Name": "S1Attribute2Answer",
"NewS1Attribute3Name": "S1Attribute3Answer",
"NewSection2Name": {
"NewS2Attribute1Name": "S2Attribute1Answer",
"NewS2Attribute2Name": "S2Attribute2Answer"
}
}
}
This is pretty simple examples of just moving properties around, but the possibilities are endless of how one would need to transform it.
It feels like a problem that somebody would have investigated or solved maybe, but in all my research I don't seem to find anything concrete, or I'm struggling to find a good starting point - maybe I'm approaching the problem incorrectly. Any suggestions/guidance/articles/libraries would be appreciated.

Supporting custom user vocabulary/labels in WPF application

In the application I'm developing, the same software package serves many industries, and those industries have different vocabulary for what is essentially the same thing. The application is a C# server, to which a WPF desktop app makes socket-based XML requests.
For example, some customers may call something an "Item", some call it a "Part", or some call it a "SKU".
The goal is for the application to be able to relabel itself based on a "Vocabulary" setting that we create for the user. Typically, a given customer's vocabulary will only differ by perhaps 5-25 words/phrases out of the entire application. These custom vocabularies are specific to/created by the customer, and wouldn't be kept with the main application distribution.
My initial thought was to do this with custom CultureInfo, (e.g. "en-AC" for "Acme" company), supply just values that differ from the base en-US in that resource file.
The en-AC.resx resource could be kept on the server, loaded by the server, and also transmitted for loading into the WPF client app.
Problem with that thus far seems to be that the ResourceManager does not correctly pick strings for custom cultures, a'la this thread, and I've not been able to solve that yet. As well, as the app is ClickOnce deployed, we may not have permission to register a new culture.
My next thought, since the number of phrases to modify is so small, was to replace the resource value at runtime, but that seems to be a bit of a no-no as well, searching around.
So, thought I would ask the community for their suggestions on how to handle this.
Open to suggestions and ideas...
Because it's only about a few words I think I'd do it via a naming convention. Suppose you defined the string key "MyCompany". You usually access this way:
string myString1 = Properties.Resource.MyCompany;
But it is also ok to Access it that way:
string myString2 = Properties.Resource.ResourceManager.GetString ("MyCompany")
It's exactly the same (but dealing with strings as identifiers - which is somewhat error prone). What you now can do is to check for a special name first that you syntesize like "MyCompany_AC". The drawback is you need your own wrapper for each string:
string MyCompany
{
get
{
string myString = Properties.Resource.ResourceManager.GetString ("MyCompany_" + theCompanyPostfix);
if (myString == null)
{
myString = Properties.Resource.ResourceManager.GetString ("MyCompany");
}
return myString;
}
}

Localization vs Customization

I am currently involved in writing an ASP.NET MVC 4 web version (using the Razor view engine) of an existing (Delphi) desktop based software product which at present allows customers (businesses) to completely customise all of the text in their instance of the application, both to localise it and to customise it to their specific environments.
For example the terms-
My tasks
Products
Workflows
Designs
Might all be changed to individual terms used within the business.
At present this customisation is simply done within the text strings which are stored within the application database, and compared and loaded on every form load in the Delphi database. I.e. every string on the form is compared with the database English strings and a replacement based on the selected locale is rendered on the form if available. I don't feel this is either scalable or especially performant.
I am also not personally comfortable with the idea of customisation happening within the localization method, that every string in the application can be changed by the end customer - it can lead to support issues in terms of consistency in text, and confusion where instructions are incorrectly changed or not kept up to date. There are lots of strings within an application that probably should not be changed beyond localizing them to the locale of the user - local language and/or formatting conventions.
I personally would rather stick with the ASP.NET APIs and conventions in localizing the web version of the application, using RESX resource files and resource keys rather than string matching. This is much more flexible than string matching where strings may have different contexts or cases and cannot simply be changed en-mass (there many English words which may have different meanings in different contexts, and may not map to the same set of meanings in other languages), crucially avoids round trips to the database to fetch the strings needed to fetch the page and also allows for ease of translation with a great set of tools around the standard RESX files. It also means no custom implementation is needed to maintain or document for future developers.
This does however give a problem of how we cope with these custom terms.
I'm currently thinking that we should have a separate RESX file for these terms, which lists defaults for the given locale. I'd then create a new database table which will be something like
CREATE TABLE [dbo].[WEB_CUSTOM_TERMS]
(
[TERM_ID] int identity primary key,
[COMPANY_ID] int NOT NULL, -- Present for legacy reasons
[LOCALE] varchar(8) NOT NULL,
[TERM_KEY] varchar(40) NOT NULL,
[TERM] nvarchar(50) -- Intentionally short, this is to be used for single words or short phrases
);
This can potentially read into a Dictionary<string, string> when needed and cached by IIS to provide lookup without the delay in connecting to the SQL server and conducting the query.
public static class DatabaseTerms
{
private static string DictionaryKey
{
get { return string.Format("CustomTermsDictionary-{0}", UserCulture); }
}
private static string UserCulture
{
get { return System.Threading.Thread.CurrentThread.CurrentCulture.Name; }
}
public static Dictionary<string, string> TermsDictionary
{
get
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
var databaseTerms = HttpContext.Current.Cache[DictionaryKey] as Dictionary<string, string>;
if (databaseTerms != null)
{
return databaseTerms;
}
}
var membershipProvider = Membership.Provider as CustomMembershipProvider;
int? companyId = null;
if (membershipProvider != null)
{
companyId = CustomMembershipProvider.CompanyId;
}
using (var context = new VisionEntities())
{
var databaseTerms = (from term in context.CustomTerms
where (companyId == null || term.CompanyId == companyId) &&
(term.Locale == UserCulture)
orderby term.Key
select term).ToDictionary(t => t.Key, t => t.Text);
HttpContext.Current.Cache.Insert(DictionaryKey, databaseTerms, null, DateTime.MaxValue,
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
return databaseTerms;
}
}
set
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
HttpContext.Current.Cache.Remove(DictionaryKey);
}
HttpContext.Current.Cache.Insert(DictionaryKey, value, null, DateTime.Now.AddHours(8),
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
}
}
}
I can then have a class which exposes public properties, returning a string based on either this dictionary value or the value in the RESX file - whichever is not null. Something like-
public static class CustomTerm
{
public static string Product
{
get
{
return (DatabaseTerms.TermsDictionary.ContainsKey("Product") ?
DatabaseTerms.TermsDictionary["Product"] : CustomTermsResources.Product);
}
}
}
These can then be added to larger localised strings using string formatting if required, or used by themselves as labels for menus etc.
The main disadvantage of this approach is the need to anticipate in advance which terms the end customers may wish to customise, but I do feel this might present the best of both worlds.
Does this seem like a workable approach and how have other devs approached this problem?
Thanks in advance.
I once designed an MVC application, whereby any string could be changed. In my case it was to handle other languages, but conceivably you could change anything just for aesthetic purposes. That and there is potential for the system to be marketed to other shops, and they may well call the same things different name (You say "Deferred Payment", I say "Lease Payment", etc.)
Warning: This solution is not about globalization and localization (e.g. left-to-right, word/verb ordering - it only needed to do what it did!)
It also considered the possibility of American English (en-US) vs British English (en-GB) vs Australian English (en-AU).
In the end, A Locale table was created in the database:
_id _localeName _idRoot
---------------------------
1 en-GB null
2 en-US 1
3 en-AU 2
Note how US and AU effectively have en-GB as their parent. en-GB therefore had every conceivably string that can be used in the application, in our translation table:
_id _idCulture _from _to
--------------------------------------
1 1 msgyes Yes
2 1 msgno No
3 1 msgcolour Colour
4 2 msgcolour Color
Now, during application initalisation, there was a config flag that specified the culture, which in my case happened to be en-AU. The system looks up the culture tree (en-AU derives from en-GB), and loads all the translations bottom up in to a dictionary cache. Therefore any en-AU specific translations overwrote the GB ones.
So, to describe it in your case - you'd have ALL translations in your database anyway, and that's your default setup. When the customer wishes to customise the text, they basically get a new node (or a derived culture in my example), and you build your cache again. Any terms they customised override the defaults. You no longer have to worry about what terms were done, it just works.
We have a similar setup in our application, we allow certain modules to have a custom names to fit the customers brand.
the first step to this solution is we know our client context at runtime and we stuff it into the HttpContext.Items.
For those items that can be customized, we introduced resource file containing the base keys. If the enterprise wants it customized we add a prefix in front of the key name (ie Client_key)
At once all this is in place its a simple coalesce to fetch the customized or default value.
Resx file snippet
<data name="TotalLeads" xml:space="preserve">
<value>Total Leads</value>
</data>
<data name="Client_TotalLeads" xml:space="preserve">
<value>Total Prospects</value>
</data>
Class to handle switch between custom and base resources
public static class CustomEnterpriseResource
{
public static string GetString(string key)
{
return GetString(key, Thread.CurrentThread.CurrentUICulture);
}
public static string GetString(string key, string languageCode)
{
return GetString(key, new CultureInfo(languageCode));
}
public static string GetString(string key, CultureInfo cultureInfo)
{
var customKey = ((EnterpriseContext)HttpContext.Current.Items[EnterpriseContext.EnterpriseContextKey]).ResourcePrefix + key;
return Resources.Enterprise.ResourceManager.GetString(customKey, cultureInfo)
?? Resources.Enterprise.ResourceManager.GetString(key, cultureInfo);
}
}
Also to assist in the views we create a html helper for this.
public static class EnterpriseResourceHelper
{
/// <summary>
/// Gets a customizable resource
/// </summary>
/// <param name="helper">htmlHelper</param>
/// <param name="key">Key of the resource</param>
/// <returns>Either enterprise customized resource or base resource for current culture.</returns>
public static string EnterpriseResource(this HtmlHelper helper, string key)
{
return CustomEnterpriseResource.GetString(key);
}
}
The requirement you have is not very common. I have worked in projects where localization is done purely using satellite assemblies and in projects where localization is done purely using database tables. In .NET, the recommended approach is RESX files compiled into satellite assemblies. It is a real good approach if you adopt it fully.
Your requirements are some where in between. Though the approach you plan to take, at this point sounds good on paper, I have a feeling over the course of time, maintenance will be difficult, since some of the strings will be in RESX and some will be in database. Even if the distribution is 90% - 10%, people will have difficulty figuring out where it takes the strings when you have all the strings loaded up in production. You will get queries from your users why a particular string is not showing up correctly and at that time it can get difficult for a developer (other than you) to figure out. You will be the best judge for your needs but I believe either you embrace RESX approach fully (which is not possible in your case) or go the full database route. If I have every thing in tables, all I need to do is to run a query for a given profile and I will see all the strings. This will be easier to support.
Even with database, you can follow a RESX-style approach of storing the full string against a key for a culture. The older approach of storing word by word is definitely not a good solution and will not work for different languages, since only sentences can be translated and not individual words. Your idea of caching is definitely needed for performance. So, basically having every thing in a bunch of tables, caching the same in memory and pulling the strings from cache based on the current culture is something I will go for. Of course, my opinion is based on what I could understand by reading your question :).
Also, check this out.
Very interesting question, thanks for bringing it up.
I have localized applications in very different ways, and your case is very specific. Let's start from the fact that everything comes down to localizing the labels/titles of the UI. Therefore, these elements must become localizable. On many platforms (such as WinForms, ASP.NET) they are localizable by design, and all it takes is extending the resource management model. I would say, this is the most natural way of localization if you are writing for such a platform.
In case of ASP.NET MVC, even though it's built on top of ASP.NET engine, we are not recommended to use the ASP.NET's server side tags and therefore the solution does not work. Why I provided it above is to give the clarity to my solution which I'm describing below.
Step 1 - Modularity
All labels and titles are part of some particular screen of the application. Since the screen is what groups them, I often use it for this exact purpose when describing localization resources. BTW, this is why we have one resx file per screen for the applications. So, we are following the consistent standard here.
To express modularity, define classes that correspond to each screen, and have properties defined on it that correspond to each localizable label or title on the screen.
Pseudo example:
class ProductPageResources
{
public string PageTitle { get; set; }
public string ProductNameLabel { get; set; }
}
Step 2 - Localization
When designing your application screens, stick to the modular resource classes defined above. Use localized strings from the modular resource class to display the labels and titles. If there's a need to add a label to the screen, don't forget to add a new property to the modular resource class too. Since it's ASP.NET MVC, we need to pass the resources alongside with the model. Conceptually it would not be necessary, but doing so gives us the flexibility to replace the resource implementation in the future (e.g. from MS SQL to some other source).
Usage example:
#{
ViewBag.Title = string.format(Model.Resources.PageTitle, Model.Product.Name);
}
...
<label>#Model.Resources.ProductNameLabel</label>
Note that the resource class property returns the localized string for the current culture, or the fallback value (described below) if not found. For the default value to appear as a value, I prepare the resource object by iterating the properties and assigning default values to them if they are empty (because the override was not found).
Step 3 - Customization
[Very nice and descriptive term you've got here, so I will use it.]
I personally don't think that the resource management should be data-driven. Main reason is that it's not dynamic enough. Just recall, that we have modular classes, and we start adding properties to it when we need to display something new on the screen. On the other hand, if you add something to the database, it's not appearing on the screen just so.
Therefore, we have a strongly-typed localization here, and it's very natural way of localizing things. The rest comes from this conclusion.
On your customization/resource administration screen you can use reflection to detect all the modular resource classes and display their properties on the screen for customization. To find the resource classes, you can put them under the same namespace, or you could mark them with some attributes to easier find them in the assembly. Either way works.
To make the modular resource class more display-friendly, you can use attributes to assign descriptions that should display instead of their Pascal-Case names on the screen.
So, our modular resource class becomes something like this:
[Description("Product Page")]
class ProductPageResources
{
[Description("Page Title")]
[DefaultValue("Product Details: {0}")
public string PageTitle { get; set; }
[Description("Product Name (label)")]
[DefaultValue("Name:")]
public string ProductNameLabel { get; set; }
}
Basically, on the customization screen we will see default values for Product Page, and each available localized value. For the last part, you can enumerate all the active cultures of the application and extract the value from the property again. Alternatively, you can use some other way depending on the implementation.
This seems to be an extensive reflection, but after all, Visual Studio does something very similar by allowing us to edit the resource files in the special editor. Bottom line is that you have a precisely working framework.

How can I pass a runtime method to a custom attribute or viable alternative approach

Basically I would like to do something like this at the top of my class (I know this doesn't work as it isn't a constant)....
[XmlStorage(IsSingleStorageFile = false, IsSubordinate = true,
StorageLocation = "posts" + GetBlogId()]
Where GetBlogId() would be a static utility method.
I'm building an xml storage framework for a blogging engine I am writing (partly a learning excercise, partly as I want to give something back to the open source) and I thought that the tidiest way to determine the storage location would be to use custom attributes since I would be to use a datacontractserializer anyway.
My only problem at present is determining the location for subordinate type whose location would be determined by the id of their parent. e.g Post < Blog.
My storage path would be something like this...
posts\blogid\postid.xml
Where the blog id would be determined by parsing the url and returning the associated blog. This would allow me to host multiple blogs in one installation whilst keeping post storage files separate to reduce memory overheads when loading posts.
Is this a straight no or is there a better way for me to do what I am attempting?
Edit:
Following John answer I tried this....
private static string GetSubordinatePath(Type type)
{
if (typeof(ISubordinate).IsAssignableFrom(type))
{
object instance = Activator.CreateInstance(type);
return (instance as ISubordinate).ParentGuid.ToString();
}
else
{
// TODO: Localize this.
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
"The specified type '{0}' does not impliment the ISubordinate interface. Please edit the source appropriately to enable storage.",
type.GetType().Name));
}
}
Which would be called from the class reading the custom attribute.
This works nicely..
It's a straight no for attributes... the values are constants baked into the metadata.
One option you could use would be to have some sort of templating built into whatever uses the attributes... so you could have a storage location of posts\{GetBlogId()} and call the method at execution time. It's not exactly elegant though... you might want to consider using an interface instead.

Where to store configuration information

I have a console application that I am rebuilding from C to C#. This application has to be able to support the legacy method of storing information like parameters from a command-line and parameters from a file (called the system parameters) that customize each run. The system parameters file is in plain-text with a simple key-value structure.
My questions are:
Should I combine these different parameters into a single Configuration object?
How would I call this configuration object from the code to store parameters?
How would I call this configuration object from the code to retrieve parameters?
Should this object be strongly-typed?
I will need access to this structure from a lot of different places in the code. What is the most elegant way to retrieve the values in the object without passing the object itself around everywhere?
I have a feeling that it should be a single, strongly-typed object and that it should be an instantiated object that is retrieved from a repository with a static retrieval method however I really want validation of this method.
I would use a single configuration object like the following:
using System;
using System.IO;
using System.Reflection;
public sealed class Setting {
public static int FrameMax { get; set; }
public static string VideoDir { get; set; }
static readonly string SETTINGS = "Settings.ini";
static readonly Setting instance = new Setting();
Setting() {}
static Setting() {
string property = "";
string[] settings = File.ReadAllLines(SETTINGS);
foreach (string s in settings)
try {
string[] split = s.Split(new char[] { ':' }, 2);
if (split.Length != 2)
continue;
property = split[0].Trim();
string value = split[1].Trim();
PropertyInfo propInfo = instance.GetType().GetProperty(property);
switch (propInfo.PropertyType.Name) {
case "Int32":
propInfo.SetValue(null, Convert.ToInt32(value), null);
break;
case "String":
propInfo.SetValue(null, value, null);
break;
}
} catch {
throw new Exception("Invalid setting '" + property + "'");
}
}
}
Since this is a singleton, it will create one and only one instance of itself the first time a public static property is referenced from the Setting object.
When the object is created, it reads from the Settings.ini file. The settings file is a plain-text file with a simple key : value structure that might look like this:
FrameMax : 12
VideoDir : C:\Videos\Best
The object uses reflection to discover each property and to store its initial value. In this example, two properties have been defined:
public static int FrameMax { get; set; }
public static string VideoDir { get; set; }
The code as written handles Int32 and String types. By adding additional case statements to the switch statement, you could easily add support for types like Float and Decimal.
To change a setting, you would use something like:
Setting.FrameMax = 5;
To retrieve a setting, you would use something like:
if (Setting.FrameMax > 10) ...
You'll notice that all the properties are strongly-typed. Also, you don't have to pass the Setting object around, as all the Setting properties are static and always available everywhere.
I hope this idea is helpful.
I like using Settings. These can be generated automatically either by creating a settings file using the Add New File dialog box, or by adding a default settings file from project properties.
Each setting may be in user or application scope, which controls whether or not the user can change them or they are restricted to their default values. They are easily saved with the Save() method and loaded automatically into the static Default property.
This class seems to be for application or user-based settings. I'm looking for per-run settings. Would you still recommend using this class in that case? – x97mdr
Yes. If you have both user/application based settings and per-run settings you should use two different classes - the normal (saved) settings and the per-run settings.
As long as you don't save the per-run settings, you should be safe and settings are still quite easy to use. These are static settings though. If the same application run needs several instances - this is the wrong approach.
I find that whenever I have to deal with a legacy system, sticking with the old format almost always works best. Often times there are other people using the legacy formats for other tasks (like automation of the app, for example), so if you recode the way the application handles inputs, you might break other systems.
On the other hand, if you are pretty confident that you know all the people using the system, and they tell you that they don't care if you change these types of things, I would probably move everything to XML. Besides all the nice features of XML from an application point of view (like being in ASCII so it's easily modified by humans, being self-documenting, etc ...), XML is also time-saving, in that you don't have to write your own I/O or parser. There's already a wide variety of libraries out there, particularly in .NET 3.0/3.5, that do very well. (As you're moving to C#, I'm guessing you're already thinking along these lines :)
So ultimately, you'd have to base your decision on cost-to-implement: if you lower your cost of implementation by moving to XML or similar, make sure that you don't raise other people's cost of implementation to move to your new application framework.
Good luck!
XmlDocument - you can generate a class definition using XSD.exe

Categories

Resources