Internationalize HelloWorld program .NET - c#

I have small test app which has 2 resource files (Resources.resx & Resources.de-DE.resx) with the same exact string names, but one has the strings converted to German.
For my form I set the Localize property to ture.
In my application I am getting the strings as such:
this.Text = Properties.Resources.frmCaption;
In my release folder I get a de-DE folder with a dll named International_test.resources.dll.
I try to distribute this to a machine which is set to German and all of the strings pulled are still english.
I tried keeping the International_test.resources.dll in the de-DE folder or just put in in my apps directory.
What am I doing wrong or what do I need to do to get the German resource file to be used?

As luck would have it, I use hello world prototype project to test a whole bunch of stuff in our build pipeline.
Assuming you have setup your resource files correctly, here's some example code that may help. Code documentation removed for brevity.
public class HelloWorld
{
public CultureInfo CultureInfo { get; private set; }
public HelloWorld()
{
CultureInfo = CultureInfo.CurrentCulture;
}
public HelloWorld(string culture)
{
CultureInfo = CultureInfo.GetCultureInfo(culture);
}
public string SayHelloWorld()
{
return Resources.ResourceManager.GetString("HelloWorld", CultureInfo);
}
}
[TestFixture]
public class HelloWorldFixture
{
HelloWorld helloWorld;
[Test]
public void Ctor_SetsCultureInfo_ToCurrentCultureForParameterlessCtor()
{
helloWorld = new HelloWorld();
Assert.AreEqual(helloWorld.CultureInfo, CultureInfo.CurrentCulture,
"Expected CultureInfo to be set as CurrentCulture");
}
[Test]
public void Ctor_SetsCultureInfo_ToAustralianCulture()
{
helloWorld = new HelloWorld("en-AU");
Assert.AreEqual(helloWorld.CultureInfo.Name, "en-AU",
"Expected CultureInfo to be set to Australian culture");
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void Ctor_ThrowsException_InvalidCultureName()
{
helloWorld = new HelloWorld("Bogus");
}
[Test]
public void SayHelloWorld_ReturnsFallbackResource_OnUndefinedResource()
{
helloWorld = new HelloWorld("en-JM");
string result = helloWorld.SayHelloWorld();
Assert.AreEqual("Hello, World.", result, "Expected fallback resource string to be used");
}
[Test]
public void SayHelloWorld_ReturnsAustralianResource_OnAustralianResource()
{
helloWorld = new HelloWorld("en-AU");
string result = helloWorld.SayHelloWorld();
Assert.AreEqual("G'Day, World.", result, "Expected australian resource string to be used");
}
}
This project has Resources.resx file with HelloWorld string key item and "Hello, World" value, along with corresponding Resources.en-AU.resx with HelloWorld string key item and "G'Day, World" value, plus others such as zh-CH (我隻氣墊船裝滿晒鱔.:) to test display of non-English characters, as it gets displayed in the associated hello world web project.
Finally, Add some logging to show the culture being used (I took it out of this example for brevity), and also check your compiler output to ensure AL.exe is being invoked to link your resource assemblies (sounds like it's OK though).

I think my problem is that I am using a US version of XP with its language set to German as well other culture settings. The CurrentCulture and CurrentUICulture still appear to be still be "en-US"
I guess the best way to test then is to manually set the cultures at the start of the program like:
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE");
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("de-DE");

thanks a ton Si.
I finally got my problem figured out.
My Resource files are in the Properties folder so I need to put the following
//using embedded resources
ResourceManager rm = new ResourceManager("International_test.Properties.Resources", Assembly.GetExecutingAssembly());

Related

C# Class library - resource files not loading

I am working on adding localisation to my class library. Currently I have two resource files: Strings.resx and Strings.es.resx.
Both files are under the 'internal' access modifier, although I have tried setting both to 'public' without any help.
My problem is that the Spanish resource file (Strings.es.resx) is not being loaded; and this problem will repeat with any more resource files I add for other languages. The Strings.resx works fine as it is the default resource file.
This code is used to grab which string resource files have been loaded; currently only the default file is loaded. Spanish does not appear:
private static void LoadLanguages()
{
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var culture in cultures)
{
try
{
var rs = Properties.Lang.Strings.ResourceManager.GetResourceSet(culture, true, false);
if (rs != null) SupportedLanguages.Add(culture.Name.ToLower(), culture.NativeName);
}
catch (Exception)
{
// ignored
}
}
Log.Info("Loaded languages: " + SupportedLanguages.Count); //OUT: 1
}
I have made a discovery though. In my build output, there is a folder "es", and within that folder is a DLL called Project.resources.dll. If I copy that DLL to the root folder of the build output, the resource gets loaded.
The solution to this problem is to get those resource files loaded from the folders. For some reason this is not happening. Is there a known solution to this? Thanks.
It works out the threads current culture. An example can be seen in the docs over at Microsoft https://learn.microsoft.com/en-us/dotnet/framework/resources/creating-satellite-assemblies-for-desktop-apps (check code at step 13 in the end)
Below the example from the documentation. The localized resource is StringLibrary
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-GB", "en-US", "fr-FR", "ru-RU" };
Random rnd = new Random();
string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
Console.WriteLine("The current UI culture is {0}",
Thread.CurrentThread.CurrentUICulture.Name);
StringLibrary strLib = new StringLibrary();
string greeting = strLib.GetGreeting();
Console.WriteLine(greeting);
}
}

How to use a common method across all projects without having to pass the namespaces

I am working on localization of texts across all the 5 projects which together forms the product.
Localization: So If user is from USA, they will see the product in en-US, if they are from China they will see the text in ch-CH.
And I am stuck at below stuff.
Each project will have its OWN bucket of Resx file (file where I am keeping for translations).
Project A - en-US.resx file
cn-CH.resx file
Project B - en-US.resx file
ch-CH.resx file
Project C - en-US.resx file
ch-CH.resx file
.
.
.
Now I have a Project Common which gets referenced by all the projects.
So What I wrote a singleton class in Common
public sealed class Translation
{
private static readonly Translation translation = new Translation();
public static Translation GetTranslation { get { return translation; } }
private Translation() { }
static Translation() { }
public string GetTranslatedMessage(string key, CultureInfo culture, string message, string namespace)
{
var rm = new ResourceManager("namespace", Assembly.GetExecutingAssembly());
message = rm.GetString(key, culture);
return message;
}
}
So far so good, As you can see I am using namespace as 4th parameter with resource manager so that I can look for the translation in the right project bucket , I just do something like below:
Translation.Translate(key, culture, message, namespace) // singleton class taking in the namespace to find the right bucket
And it works fine.
Question/Problem: But from every project I need to pass the namespace, I mean where ever I call I need to pass the namespace. I am wondering is there any way, I can implicitly tell which bucket each project needs to look into. Can I use Abstract or 2 singleton classes, factory may be?, or something like that. I am newbie so I am not familiar on how to tackle this issue. I just don't want to pass namespace in every call.
WorkAround: I can repeat this same singleton code in each project and get the stuff working, but then I will be repeating same singleton code in each project/
If you are open to a hack-y solution, and your various namespaced files are in folders named after the namespace, you could use the CallerFilePathAttribute and split the namespace out of the path. It does seem fragile:
Look up C# Caller Information. The sample they show is:
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}
// Sample Output:
// message: Something happened.
// member name: DoProcessing
// source file path: c:\Users\username\Documents\Visual Studio 2012\Projects\CallerInfoCS\CallerInfoCS\Form1.cs
// source line number: 31
Then take what they have labeled as source file path and play path games to get the namespace. Parameters using these attributes are always optional (typically with they defaults set to default(T)). The compiler injects the value, the caller should not.

.net core app parsing decimals in a linux docker container

The following code, in a .net core 2.0.0 app works fine when the app is run on the windows development machine.
When the app is deployed in a linux docker container, it fails with exception message: System.FormatException: 'Input string was not in a correct format.'
Why? And what's the workaround?
class Program {
static void Main(string[] args) {
var value = "$291.00";
var valueAsDecimal = decimal.Parse(value, System.Globalization.NumberStyles.Any);
Console.WriteLine(valueAsDecimal);
Console.ReadLine();
}
}
Here's the code that worked. I had to manually set the correct culture.
Thanks Richard Schneider and Evk for your comments leading the way.
class Program {
static readonly CultureInfo USEnglish = new CultureInfo("en-US");
static void Main(string[] args) {
var value = "$291.00";
var valueAsDecimal = decimal.Parse(value, System.Globalization.NumberStyles.Any, USEnglish);
Console.WriteLine(valueAsDecimal);
Console.ReadLine();
}
}
It appears that the default culture running on the docker container FROM microsoft/dotnet:2.0-runtime AS base is invariant culture:
The currency sign is locale specific. Have you checked what the Culture settings are?
If you always want to use '$' and '.' then use a format provifeer.
var valueAsDecimal = decimal.Parse(
value,
NumberStyles.Any,
new CultureInfo("en-AU");

Automation Add-ins for MS Excel 2013

I am trying to write a C# automation add-in in Visual Studio 2013. The objective is to be able to call UDFs written in C# from within MS Excel 2013. I have read most of the publicly available materials on the subject and have tried to adapt several simple examples, such as.
Unfortunately, neither of them is written under VS 2013 and MSExcel 2013. Code sample:
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Globalization;
using Microsoft.Win32;
namespace MyFirstAddIn
{
// Early binding. Doesn't need AutoDual.
[Guid("5E6CD676-553F-481E-9104-4701C4DAB272")]
[ComVisible(true)]
public interface IFinancialFunctions
{
double Bid(string symbol);
double Ask(string symbol);
double[,] BidnAsk(string symbol, string direction = null);
}
[Guid("B9B7A498-6F84-43EB-A50C-6D26B72895DA")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class FinancialFunctions : IFinancialFunctions
{
// Private class members.
private static readonly WebClient webClient = new WebClient();
private const string UrlTemplate = "http://finance.yahoo.com/d/quotes.csv?s={0}&f={1}";
// Private method - data download.
private static double GetDoubleDataFromYahoo(string symbol, string field)
{
string request = string.Format(UrlTemplate, symbol, field);
string rawData = webClient.DownloadString(request);
return double.Parse(rawData.Trim(), CultureInfo.InvariantCulture);
}
// Public "interface" methods.
public double Bid(string symbol)
{
return GetDoubleDataFromYahoo(symbol, "b3");
}
public double Ask(string symbol)
{
return GetDoubleDataFromYahoo(symbol, "b2");
}
public double[,] BidnAsk(string symbol, string direction = null)
{
double bid = GetDoubleDataFromYahoo(symbol, "b3");
double ask = GetDoubleDataFromYahoo(symbol, "b2");
return direction == "v" ? new[,]{{bid}, {ask}} : new[,]{{bid, ask}};
}
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
RegistryKey key = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("",System.Environment.SystemDirectory + #"\mscoree.dll",RegistryValueKind.String);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false);
}
private static string GetSubKeyName(Type type, string subKeyName)
{
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(#"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(#"}\");
s.Append(subKeyName);
return s.ToString();
}
}
}
I have made the assembly COM-visible via:
Project->Properties->Application->Assembly Information
and I've also registered COM interop in the "Build" tab.
After building, I can see in the registry that registration was successful and the add-in is registered under the correct GUID. However, when I open Excel and go to Developer->Add-ins->Automation, I cannot see my add-in in the list. I verified that the code I'm posting works with Excel 2010 but for some reason I fail to see my add-in in Excel 2013.
Can anyone help me with this?
OK, I have found the cause of the problem. Surprisingly, it turns out that Visual Studio does not have the capability to automatically register 64bit dlls under the correct tree in the registry. The solution is not to register the project for COM interop and manually add commands to invoke the 64bit version of RegAsm as a post-build event. The full path and name of the dll need to be included, so unfortunately this is not a fully automated solution.

Disable vs2010 XNA Content Pipeline warning

I am building a game with XNA, and I have a custom file format for my game's levels. I want to load them and parse them myself, without using XNA's content pipeline. I have this much working, and by adding the files to the Content project I can even edit them in Visual Studio (which I also want).
The Problem: I get a warning stating "Project item 'item.lvl' was not built with the XNA Framework Content Pipeline. Set its Build Action property to Compile to build it."
I do not want XNA to Compile it, since I am doing my own parsing. How can I disable the warning?
Set the file's Build Action to None, and then set it to Copy if newer. That will cause the file to be written to the proper output directory without putting it through the Content Pipeline.
The solution could be create a custom content importer as explained here: Creating a Custom Importer and Processor. To create a simple content importer you have to inherit your class from the ContentImporter<T> (abstract class) and override the Import() method.
Here is a simple example from the msdn:
//...
using Microsoft.Xna.Framework.Content.Pipeline;
class PSSourceCode
{
const string techniqueCode = "{ pass p0 { PixelShader = compile ps_2_0 main(); } }";
public PSSourceCode(string sourceCode, string techniqueName)
{
this.sourceCode = sourceCode + "\ntechnique " + techniqueName + techniqueCode;
}
private string sourceCode;
public string SourceCode { get { return sourceCode; } }
}
[ContentImporter(".psh", DefaultProcessor = "PSProcessor", DisplayName = "Pixel Shader Importer")]
class PSImporter : ContentImporter<PSSourceCode>
{
public override PSSourceCode Import(string filename,
ContentImporterContext context)
{
string sourceCode = System.IO.File.ReadAllText(filename);
return new PSSourceCode(sourceCode, System.IO.Path.GetFileNameWithoutExtension(filename));
}
}

Categories

Resources