22

There are plenty of tutorials how to create multilanguage RESX files and how to create satellite assemblies with AL.exe, but I haven't found working example how to embed RESX/Resources/satellite-DLL files in single EXE file and distribute whole multilanguage app as such EXE.

I tried to use ilmerge.exe, but it looks like it doesn't work for multiple DLLs with the same name (culture satellite DLLs have identical names, originally residing in different subdirs named after culture).

I also don't know how to create ResourceManager instance to work with embedded resources.

My goals is to enable dynamical switching between closed, pre-defined set of languages. I need class/method which will get culture string (i.e. "de-DE"), resource name (i.e. "CancelText") and return translated text based on embedded resx/resource/dll.

I'm using VS2008, please note what setting for "build action" is needed in resx/resource files properties sheet. Working code sample or link to tutorial project would be the best.

tomash
  • 12,742
  • 15
  • 64
  • 81
  • 2
    I know it is creepy to re-activate such an old thread, but I am interested to find out if you have solved your problem and if so, how? Are you still, to this date, using your approach (the answer that you gave below)? I am working on a similar project in VS 2012 and would like to combine my language resource DLLs into the main assembly/exe file, while still using ResourceManager to switch between German and English. Thanks! – Sebastian Apr 15 '14 at 08:51
  • @Sebastian I didn't explore any other way, please share if you find better one – tomash Apr 15 '14 at 12:18

4 Answers4

10

My solution: program contains only one default language resource file (resx). All other languages are compiled from .resx to .resources and embedded as resource file. Important! I have changed extension because ".resources" is recognized as a special type of resource, so my French files is named "PIAE.LangResources.fr".

Here is simple code to retrieve translated string (it should be improved with caching values from resource):

    internal static string GetString(string str, string lang)
    {

        if (string.IsNullOrEmpty(str)) throw new ArgumentNullException("empty language query string");
        if (string.IsNullOrEmpty(lang)) throw new ArgumentNullException("no language resource given");

        // culture-specific file, i.e. "LangResources.fr"
        Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PIAE.LangResources."+lang);

        // resource not found, revert to default resource
        if (null == stream)
        {                                                                   
            stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PIAE.Properties.LangResources.resources");
        }

        ResourceReader reader = new ResourceReader(stream);
        IDictionaryEnumerator en= reader.GetEnumerator();
        while (en.MoveNext())
        {
            if (en.Key.Equals(str))
            {
                return en.Value.ToString();
            }
        }

        // string not translated, revert to default resource
        return LangResources.ResourceManager.GetString(str);
    }
tomash
  • 12,742
  • 15
  • 64
  • 81
  • 8
    +1, we use a similar approach. It hurts that .net forces you to reinvent the wheel if you want to avoid satellite assemblies. A very bad design decision, IMO. – Heinzi Dec 17 '09 at 22:07
  • Nice approach. Just wanted to add that a `ResourceSet` can be used around the reader, to make retrieving values by name easier. – Julien Lebosquain Jul 06 '12 at 11:33
6

You didn't find it because it's not the way the .NET framework works. .NET expects satellite DLLs in specifically named location (iow directories named after the language of the resources it contains. eg. de, de-DE, chs,...). If you don't work that way, .NET won't be able to apply its magic (which is to automatically pick the correct resource according to the current UI culture: Thread.CurrentThread.CurrentUICulture).

Serge Wautier
  • 21,494
  • 13
  • 69
  • 110
  • 4
    It's OK for me, I can pass proper culture by myself - but I distribute small tool and I definitely don't want to add any subdirs/dlls. Only one EXE, even if .NET framework brings little help here. – tomash Nov 24 '09 at 22:23
-1

I used the GetString approach above. The article Can't load a manifest resource with GetManifestResourceStream() describes how to correctly retrieve your resource as a stream object. After that, everything worked.

Community
  • 1
  • 1
-1

Use this program, it Works with me: EXEPack

You just need to do manually everytime you compile, not sure if there is a command tool.

Nicke Manarin
  • 3,026
  • 4
  • 37
  • 79