Dynamic localization of messages - c#

I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.

I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.

Related

How are paths to embedded resources stored?

Is it possible to retain the logical layout of the project file?
If I have 2 embedded resources laid out like the following
When I call Assembly.GetManifestResourceNames(), the two of them get named:
MyLibrary._MyItems.SubItems.SubItem1.xml
MyLibrary._MyItems.SubItems.SubItem2.xml
However, this gives me no real insight into how they're logically ordered in my project. For all I know, they could both be in the same directory.
I worry that this may not be possible, because if I name the files like so, my app will not even compile:
I want to be able to distinguish between
MyLibrary._MyItems\SubItems\SubItem1.xml
and
MyLibrary._MyItems\SubItems.SubItem2.xml
Similar question, but less detail than I am looking for
Exact duplicate, no traction
One simple solution would be to come up with a convention to indicate where a file name starts. You could then "safely" assume the rest of the name indicates the folder structure.
However, since these are embedded resources you could create a T4 script (or some similar piece of code) that runs prior to compilation that examines the folder structure and builds a class detailing that structure.
Here is an example of what the generated class could look like.
public static class MyResources
{
private readonly Dictionary<String, String> ResourceNameToDirPathMappingSetSource = new Dictionary<String, String>();
static MyResources()
{
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem1.xml", #"MyLibrary._MyItems\SubItems");
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem2.xml", #"MyLibrary._MyItems");
}
public static IReadOnlyDictionary<String, String> ResourceNameToDirPathMappingSet
{
get
{
return ResourceNameToDirPathMappingSetSource;
}
}
}
My assumption being that you want to take the resource name and determine the folder path it was originally in. The dictionary of course could contain whatever values (read: custom class) you need to map to.

Avoiding magic strings and numbers

I am working on an application that has been edited by various programmers over the past few years and I have stumbled across a problem with using String Literals to access MenuItems.
For Example: in many places there is code like
mainMenu.MenuItems[1].MenuItems[0].Visible=true;
or
mainMenu.MenuItems["View"].MenuItems["FullScreen"].Visible=true;
how do I change the Strings used to identify the MenuItem and catch all of the places that it is being used for access? The menus and menuitems are declared as public and are used throughout this large application
What is the right way prevent the use of these magic indexes from being used. I forsee things being broken everytime a new item is added or the name is changed.
P.S. I have started using an enumerated dictionary approach in which every menuItem is paired with a key. but this still does not force other developers to use my implementation nor is it the most elegant solution to question 2
Give each menu item a name in the WinForms designer (I assume), and then refer to it by that name.
Then just use this in your code:
menuExit.Visible = false;
If the menu items are added programmatically, do this:
class MyForm : Form
{
private MenuItem menuExit;
...
myMenu.Items.Add(menuExit = new MenuItem(...));
...
}
and then still access it by the menuExit name. The key to avoiding magic numbers and strings is to just keep a direct reference to whatever it is you want to refer to. As a bonus, you can now rename this vairable safely using F2.
Romkyns answer is the correct one for this scenarion however if you do need to use string literals in your code I would alwasy keep them in public static classes such as:
public static class Constants
{
public static class Menu
{
public static readonly string FirstMenuName = "Menu 1";
...
}
public static class OtherCateogry
{
...
}
}
You can then access them by Constants.Menu.FirstMenuName.
As for definitively preventing other devs from using literals throughout code - you might have to make recourse to the Rod of Correction (sturdy metal ruler) ;).

Build C# dll from SQL table

I have a SQL-table with three columns: Id, English and Norwegian. Id is the primary key. In my application I have a flag (EN/NO) to decide which language to use for labels, buttons ++ in the GUI.
The application is now doing a select * everytime the application loads, and the application is looking up all required values at runtime. But instead of loading the whole dataset for every instance, i want to export these values and create a dll so i can store these values locally.
Is there any possibility of creating this in-code so the dll will renew itself with every build? Or do I have to run some external program to dynamically create ex. a .cs code to copy/paste into my class? (I need to be able to re-run the process because rows will be added every time there is a need for a new label/text)
I have so far thought out three solutions on how to structure my export, but no clue on how to export the data:
Preserve the state of the DataTable in a static context and provide help-methods to standardize the way of getting the values out.
Create a class containing each unique ID as method-name, and a parameter to decide which value to return:
public static class Names
{
public static string 12345(string language)
{
switch (language)
{
case "EN":
return "Hello";
case "NO":
return "Hei";
default:
return "Hello";
}
}
}
Create a class containing a searchable list for each language with ID as key and the value (as value)
Why don't you create different resource files for different languages and load the appropriate one depending you the settings. You can do this by using System.Resources.ResourceManager. This article here explains this in detail.
EDIT: Following SO post also discuss this in detail Best practice to make a multi language application in C#/WinForms?
No, i don't like the idea to put internationalization strings into a class library, Why you don't just use the .NET internationalization feature already built in in the framework ?
Resource files are the best solution, not class library for this kind of work ...

Retrieve class name hierarchy as string

Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH

Centralizing Messagebox handling for application

I'm wondering how others deal with trying to centralize MessageBox function calling. Instead of having long text embedded all over the place in code, in the past (non .net language), I would put system and application base "messagebox" type of messages into a database file which would be "burned" into the executable, much like a resource file in .Net. When a prompting condition would arise, I would just do call something like
MBAnswer = MyApplication.CallMsgBox( IDUserCantDoThat )
then check the MBAnswer upon return, such as a yes/no/cancel or whatever.
In the database table, I would have things like what the messagebox title would be, the buttons that would be shown, the actual message, a special flag that automatically tacked on a subsequent standard comment like "Please contact help desk if this happens.". The function would call the messagebox with all applicable settings and just return back the answer. The big benefits of this was, one location to have all the "context" of messages, and via constants, easier to read what message was going to be presented to the user.
Does anyone have a similar system in .Net to do a similar approach, or is this just a bad idea in the .Net environment.
We used to handle centralized messages with Modules (VB). We had one module with all messages and we call that in our code. This was done so that we change the message in one place (due to business needs) and it gets reflected everywhere. And it was also easy to handle change in one file instead of multiple files to change the message. Also we opened up that file to Business Analysts (VSS) so that they can change it. I don't think it is a bad idea if it involves modules or static class but it might be a overkill to fetch it from DB.
HTH
You could use resource files to export all text into there (kinda localization feature as well). Resharper 5.0 really helps in that highlighting text that can be moved to resource.
Usually it looks like this:
Before: MessageBox.Show(error.ToString(), "Error with extraction");
Suggestion: Localizable string "Error with extraction"
Right click Move to Resource
Choose resource file and name (MainForm_ExtractArchive_Error_with_extraction), also check checkbox Find identical items in class ...
Call it like this MessageBox.Show(error.ToString(), Resources.MainForm_ExtractArchive_Error_with_extraction);
Best of all it makes it easy to translate stuff to other languages as well as keeping text for MessageBox in separate Resource. Of course Resharper does it all for you so no need to type that much :-)
I suppose you could use a HashTable to do something similar like this, this can be found in:
using System.Collections;
To keep it globally accessable i was thinking a couple of functions in a class holding the hashtable to get/set a certain one.
lets see now.
public class MessageBoxStore
{
private HashTable stock;
public string Get(string msg)
{
if (stock.ContainsKey(msg))
return stock[msg];
else
return string.Empty;
}
public string Set(string msg, string msgcontent)
{
stock[msg] = msgcontent;
}
}
or something like that, you could keep multiple different information in the hashtable and subsequently compose the messagebox in the function too.. instead of just returning the string for the messagebox contents...
but to use this it would be quite simple.
call a function like this on program load.
public LoadErrorMessages()
{
storeClass = new MessageBoxStore();
storeClass.Set("UserCantDoThat", "Invalid action. Please confirm your action and try again");
}
for example, and then.
MessageBox.Show(storeClass.Get("UserCantDoThat"));
i put this in a new class instead of using the HashTable get/set methods direct because this leaves room for customization so the messagebox could be created in the get, and more than 1 piece of information could be stored in the set to handle messagebox title, buttontype, content, etc etc.

Categories