How to read data from multiple (multi language) resource files? - c#

I am trying the multi language features in an application. I have created the resource files GlobalTexts.en-EN.resx GlobalTexts.fr-FR.resx and a class that sets the culture and returns the texts like (I will not go in all the details, just show the structure):
public class Multilanguage
{
...
_res_man_global = new ResourceManager("GlobalResources.Resources.GlobalTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public virtual string GetText(string _key)
{
return = _res_man_global.GetString(_key, _culture);
}
}
...
Multilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
This works just fine.
Now, I would like to use this application in another solution that basically extends it (more windows etc.) which also has resource files ExtendedTexts.en-En.resx ExtendedTexts.fr-FR.resx and a new class like:
public class ExtendedMultilanguage : Multilanguage
{
...
_res_man_local = new ResourceManager("ExtendedResources.Resources.ExtendedTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public override string GetText(string _key)
{
string _result;
try
{
_result = _res_man_local.GetString(_key, _culture);
}
catch (Exception ex)
{
_result = base.GetText(_key);
}
}
...
ExtendedMultilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
the idea being that if the key is not found in ExtendedTexts the method will call the base class which is looking into GlobalTexts. I did this in order to use the call GetText(wantedKey) everywhere in the code without having to care about the location of the resource (I do not want to include the translations from the extensions in the GlobalTexts files); it is juts the used class that is different from project to project.
The problem I am facing is that the try/catch is very slow when exceptions raise- I wait seconds for one window to populate. I tested with direct call and works much faster, but then I need to care all the time where the resource is located...
The question is: is there an alternative way of doing this (having resources spread in various files and have only one method that gives the desired resource without throwing an error)?

In the end I took a workaround solution and loaded all the content of the resource files in dictionaries. This way I can use ContainsKey and see if the key exists or not.

Related

How to cache reading .csv files in C#

This may be a noob question, but I need some help. I have written two simple methods in C#: ReadCsv_IT and GetTranslation. The ReadCsv_IT method reads from a csv file. The GetTransaltion method calls the ReadCsv_IT method and returns the translated input (string key).
My problem is that in the future I will have to request a lot of times GetTranslation, but I obviously don't want to read the .csv files every time. So I was thinking about ways to use cache Memory to optimize my program, so that I don't have to read the .csv file on every request. But I am not sure how to do it and what I could do to optimize my program. Can anyone please help ?
public string ReadCsv_IT(string key)
{
string newKey = "";
using (var streamReader = new StreamReader(#"MyResource.csv"))
{
CsvReader csv = new CsvReader(streamReader);
csv.Configuration.Delimiter = ";";
List<DataRecord> rec = csv.GetRecords<DataRecord>().ToList();
DataRecord record = rec.FirstOrDefault(a => a.ORIGTITLE == key);
if (record != null)
{
//DOES THE LOCALIZATION with the help of the .csv file.
}
}
return newKey;
}
Here is the GetTranslation Method:
public string GetTranslation(string key, string culture = null)
{
string result = "";
if (culture == null)
{
culture = Thread.CurrentThread.CurrentCulture.Name;
}
if (culture == "it-IT")
{
result = ReadCsv_IT(key);
}
return result;
}
Here is also the class DataRecord.
class DataRecord
{
public string ORIGTITLE { get; set; }
public string REPLACETITLE { get; set; }
public string ORIGTOOLTIP { get; set; }
}
}
Two options IMO:
Turn your stream into an object?
In other words:
Make a class stream so you can refer to that object of the class stream.
Second:
Initialize your stream in the scope that calls for GetTranslation, and pass it on as an attribute to GetTranslation and ReadCSV_IT.
Brecht C and Thom Hubers have already given you good advice. I would like to add one more point, though: using csv files for localization in .NET is not really a good idea. Microsoft recommends using a resource-based approach (this article is a good starting point). It seems to me that you are trying to write code for something that is already built into .NET.
From a translation point of view csv files are not the best possible format either. First of all, they are not really standardized: many tools have slightly different ways to handle commas, quotes, and line breaks that are part of the translated text. Besides, translators will be tempted to open them in Excel, and -unless handled with caution- Excel will write out translations in whatever encoding it deems best.
If the project you are working on is for learning please feel free to go ahead with it, but if you are developing software that will be used by customers, updated, translated into several target languages, and redeployed, I would recommend to reconsider your internationalization approach.
#Brecht C is right, use that answer to start. When a variable has to be cached to be used by multiple threads or instances: take a look at InMemoryCache or Redis when perfomance and distribution over several clients gets an issue.

Store a user's score in Windows 10 UWP app?

I'm making an educational game (Windows 10 UWP, C# + XAML) and I need to store user information (in particular, their current score) and retrieve it when they start the app again. I've found a way to do this (see code below) but I have no idea if this is a normal solution to this problem. I'm currently creating a txt file and storing and retrieving data in/from it. Are there more common, or simpler ways to do this?
Here's what I'm currently doing:
Create the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync("nameOfTextFile.txt", CreationCollisionOption.OpenIfExists); //other options are ReplaceExisting
Open the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.GetFileAsync("nameOfTextFile.txt");
Write text to the file:
await FileIO.WriteTextAsync(sampleFile, "Put the added text here");
Read text from the file:
string someVariableName = await FileIO.ReadTextAsync(sampleFile);
-Thanks in advance for any help!!
While the file-based approach is valid, there are easier ways, at least for simple data: You can use roaming (or local) settings. Roaming settings are roamed between devices, as long as their size don't exceed 64K, and would carry the score from the user's desktop to the user's phone, for example. Local settings stay on the machine.
Settings are easy to use:
IPropertySet propertySet = ApplicationData.Current.RoamingSettings.Values;
// Get previous score (or 0 if none)
int score = (int)(propertySet["Score"] ?? 0);
// ...play game...
// Set updated score:
propertySet["Score"] = score;
The way I go about doing projects and settings like this is creating a propery setting in Visual Studio, then Setting and Getting the setting / Value.
You can access this by going to the application properties.
This allows access to read,write, and save information / onload restore information.
Some Informational Links:
https://msdn.microsoft.com/en-us/library/bb397755(v=vs.110).aspx
and (Suggested)
https://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
OK, so here goes an example of using a class to store your settings in.
There are many, many more ways you could do this. Too many to list.
Create a settings class:
public class YourSettingsClass
{
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public string UserScore { get; set; }
}
Create an AppSettings helper
public AppSettings
{
private static YourSettingsClass _settings = new YourSettingsClass();
public static string UserFirstName
{
get { return _settings.UserFirstName; }
set { _settings.UserFirstName = value; }
}
public static string UserLastName
{
get { return _settings.UserLastName; }
set { _settings.UserLastName = value; }
}
public static string UserScore
{
get { return _settings.UserScore; }
set { _settings.UserScore = value; }
}
public static void SaveSettings()
{
// Now, use your "settingsfile.xml" (or whatever you're saving as)
// to write your settings to from your _settings static field object.
// I'll let you have a play as to how you want to do this...
}
public static void LoadSettings()
{
YourSettingsClass tempSettingsClass = new YourSettingsClass();
// Now, use your "settingsfile.xml" (or whatever you've saved it as)
// to load in your settings and assign to your tempSettingsClass variable.
// I'll let you have a play as to how you want to do this...
// Assign the settings from your loaded object.
_settings = tempSettingsClass;
}
}
Now, from any other class, you can call AppSettings.LoadSettings(). You could do this on App Startup, or on-demand.
When you've loaded the settings in, just reference AppSettings.UserFirstName or whatever property you want to either get the value or set the value.
When you're ready to, you can then save the settings back to the XML file on disk, through AppSettings.SaveSettings().
I've purposely omitted the code for loading and saving from the storage, and for se/deserializing class objects as I haven't got any UWP components on this PC and I've done this all from memory so I don't want to put anything in to throw you off.
Plus it's a little more learning (even trial/error) for you to do.
Lastly
In the getters for your AppSettings static properties you could also do a null or string.IsNullOrWhiteSpace check for the _settings' property in question, and call the LoadSettings() method if so.
This would save you having to manually call it in-code elsewhere.
Useful links
XmlSerializer and how to use the Serialize method
All about what you can do with the FileIO.WriteTextAsync
Not an article, but a similar question: UWP C# Read & Write XML File
I really hope this helps, somewhat.
Good luck!

Keeping track of user customization's c#

Good evening; I have an application that has a drop down list; This drop down list is meant to be a list of commonly visited websites which can be altered by the user.
My question is how can I store these values in such a manor that would allow the users to change it.
Example; I as the user, decide i want google to be my first website, and youtube to be my second.
I have considered making a "settings" file however is it practical to put 20+ websites into a settings file and then load them at startup? Or a local database, but this may be overkill for the simple need.
Please point me in the right direction.
Given you have already excluded database (probably for right reasons.. as it may be over kill for a small app), I'd recommend writing the data to a local file.. but not plain text..
But preferably serialized either as XML or JSON.
This approach has at least two benefits -
More complex data can be stored in future.. example - while order can be implicit, it can be made explicit.. or additional data like last time the url was used etc..
Structured data is easier to validate against random corruption.. If it was a plain text file.. It will be much harder to ensure its integrity.
The best would be to use the power of Serializer and Deserializer in c#, which will let you work with the file in an Object Oriented. At the same time you don't need to worry about storing into files etc... etc...
Here is the sample code I quickly wrote for you.
using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
public class UrlSerializer
{
private static void Write(string filename)
{
URLCollection urls = new URLCollection();
urls.Add(new Url { Address = "http://www.google.com", Order = 1 });
urls.Add(new Url { Address = "http://www.yahoo.com", Order = 2 });
XmlSerializer x = new XmlSerializer(typeof(URLCollection));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, urls);
}
private static URLCollection Read(string filename)
{
var x = new XmlSerializer(typeof(URLCollection));
TextReader reader = new StreamReader(filename);
var urls = (URLCollection)x.Deserialize(reader);
return urls;
}
}
public class URLCollection : ICollection
{
public string CollectionName;
private ArrayList _urls = new ArrayList();
public Url this[int index]
{
get { return (Url)_urls[index]; }
}
public void CopyTo(Array a, int index)
{
_urls.CopyTo(a, index);
}
public int Count
{
get { return _urls.Count; }
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return _urls.GetEnumerator();
}
public void Add(Url url)
{
if (url == null) throw new ArgumentNullException("url");
_urls.Add(url);
}
}
}
You clearly need some sort of persistence, for which there are a few options:
Local database
- As you have noted, total overkill. You are just storing a list, not relational data
Simple text file
- Pretty easy, but maybe not the most "professional" way. Using XML serialization to this file would allow for complex data types.
Settings file
- Are these preferences really settings? If they are, then this makes sense.
The Registry - This is great for settings you don't want your users to ever manually mess with. Probably not the best option for a significant amount of data though
I would go with number 2. It doesn't sound like you need any fancy encoding or security, so just store everything in a text file. *.ini files tend to meet this description, but you can use any extension you want. A settings file doesn't seem like the right place for this scenario.

IsolatedStorage and navigation

I can't sort this weird issue out and I have tried anything and everything I can think of.
I got 5 pages, everyone of them passing variables with navigation this way:
Pass:
NavigationSerice.Navigate(new Uri("/myPage.xaml?key=" + myVariable, UriKind.Relative));
Retrieve:
If (NavigationContext.QueryString.ContainsKey(myKey))
{
String retrievedVariable = NavigationContext.QueryString["myKey"].toString();
}
I open a list on many pages and one of the pages automatically deletes an item from the list actualProject (actualProject is a variable for a string list). Then, when I go so far back that I reach a specific page - the app throws an exception. Why? I have no idea.
The code that deletes the item:
// Remove the active subject from the availible subjects
unlinkedSubjects.Remove(actualSubject);
unlinkedsubjectsListBox.ItemsSource = null;
unlinkedsubjectsListBox.ItemsSource = unlinkedSubjects;
Then the page that throws the exception's OnNavigatedTo event:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("key"))
{
actualProject = NavigationContext.QueryString["key"];
try
{
//Read subjectList from IsolatedStorage
subjectList = readSetting(actualProject) != null ? (List<String>)readSetting(actualProject) : new List<String>();
//Put the subjectList into the subjectListBox
subjectListBox.ItemsSource = subjectList;
//Set the subjectsPageTitle to the "actualProject" value, to display the name of the current open project at the top of the screen
subjectsPageTitle.Text = actualProject;
}
catch (Exception)
{
if (language.Equals("en."))
{
// Language is set to english
MessageBox.Show("Couldn't open the project, please try again or please report the error to Accelerated Code - details on the about page");
}
else if (language.Equals("no."))
{
// Language is set to norwegian
MessageBox.Show("Kunne ikke åpne prosjektet, vennligst prøv igjen eller rapporter problemet til Accelerated Code - du finner detaljer på om-siden");
}
}
}
}
Exception:
_exception {System.ArgumentException: Value does not fall within the expected range.} System.Exception {System.ArgumentException}
My theory:
The app kind of loads the currently opened and modified List. Is that possible? No idea.
So there are a number of ways to pass data between pages.
The way you have chosen is the least suggested.
You can use the PhoneApplicationService.Current dictionary but this is messy also if you have a ton of variables, doesn't persist after app shut down and could be simplified.
I wrote a free DLL that kept this exact scenario in mind called EZ_iso.
You can find it here
Basically what you would do to use it is this.
[DataContractAttribute]
public class YourPageVars{
[DataMember]
public Boolean Value1 = false;
[DataMember]
public String Value2 = "And so on";
[DataMember]
public List<String> MultipleValues;
}
Once you have your class setup you can pass it easily between pages
YourPageVars vars = new YourPageVars { /*Set all your values*/ };
//Now we save it
EZ_iso.IsolatedStorageAccess.SaveFile("PageVars",vars);
That's it! Now you can navigate and retrieve the file.
YourPageVars vars = (YourPageVars)EZ_iso.IsolatedStorageAccess.GetFile("PageVars",typeof(YorPageVars));
This is nice because you can use it for more than navigation. You can use it for anything that would require Isolated storage. This data is serialized to the device now so even if the app shuts down it will remain. You can of course always delete the file if you choose as well.
Please make sure to refer to the documentation for any exceptions you have. If you still need help feel free to hit me up on twitter #Anth0nyRussell or amr#AnthonyRussell.info

Finding out folder cyclic references

Suppose I have an app that transfers files placed inside a main folder to some other location.
For example, user can configure the app as below:
If placed in C:\X\A Transfer to C:\Z\A
If placed in C:\Y\B Transfer to C:\Z\B
. . .
. . .
Till now, all is well. But the following configuration would create endless transfer loops:
if placed in C:\X\A Transfer to C:\Z\A
if placed in C:\Z\A Transfer to C:\Z\B
if placed in C:\Z\B Transfer to C:\X\A
Such hierarchies can get quite complex. What would be the best way to predict them and prevent such configurations in the first place?
Assume that there is a class like this:
class Rule
{
public string sourceDir; // dir file placed
public string targetDir; // dir to move to
}
And a dictionary that contains all your rules indexed by the sourceDir named rules.
You can write a function like this:
public bool RuleCausesCycle(Rule rule)
{
return RuleCausesCycle(rule, new HashSet(CaseInsensitiveComparer.Default));
}
private bool RuleCausesCycle(Rule rule, Set visited)
{
Rule targetRule;
if (visited.Contains(rule.sourceDir))
{
return true;
}
if (rules.TryGetValue(rule.targetDir, out targetRule))
{
visited.Add(rule.sourceDir);
return RuleCausesCycle(targetRule, visited);
}
return false;
}
You are basically looking for cycles in a directed graph. I would use a graph Library like QuickGraph: http://quickgraph.codeplex.com/wikipage?title=Strongly%20Connected%20Components&referringTitle=Documentation

Categories

Resources