Importing and Exporting Resx Resources - RickStrahl/Westwind.Globalization GitHub Wiki

Westwind.Globalization supports importing and exporting of Resx resources. This means you can import existing Resx resources into your database, modify them and eventually write those database resources back out into Resx files.

The default behavior works with Web project resources. But imports and exports also support arbitrary folder locations (assuming the Web application has rights to read/write) so it's possible to import resources from arbitrary projects like class libraries, modify them interactively in the Web Resource Editor and then write them back out as Resx files.

Importing and Exporting to and From Resx Resources

Resources are imported and exported in different ways depending on the project type which can be either Project or WebForms.

Project Mode

Project mode uses standard .NET project management of resources, which means resources are stored in a single folder - typically the ./Properties folder of your project. This folder is configured in the configuration file via the ResxBaseFolder which is used to read and write Resx resources.

Typical configuration settings for Project based Imports/Exports:

<add key="ResxExportProjectType" value="Project" />
<add key="ResxBaseFolder" value="~/Properties" />

WebForms Mode

WebForms mode relies on the ASP.NET Resource Provider and Global and Local Resource Provider layout for Resx files. In this scheme the .\App_LocalResources and .\App_GlobalResources folders are used to read and write Resx resources. A relative paths in the ResourceSet name determines which folder local resources are written to as you would expect in WebForms applications.

Typical configuration settings for WebForms based Imports/Exports:

<add key="ResxExportProjectType" value="WebForms" />
<add key="ResxBaseFolder" value="~/" />

Working with Exported Resx Files

If you export resources and want to use them in your application, you typically have to perform a few additional steps for the resources to behave like standard .NET project resources.

  • Export resources from the Resource Administration Form (to ResxBaseFolder)
  • Include the Resx files in your .NET project
  • If you are using strongly typed resources, set the .Resx file build action to Resx to PublicResXFileCodeGenerator (or set it in the designer)
  • If you generated strongly typed resources from the dbProvider - remove the generated class file as it will conflict with the Resx generated classes.

Resx Namespaces and Locations matter!!!

When creating strongly typed Resx resources, .NET projects use a strict naming convention which is based on the project's defined base namespace. In a typical project generated strong resource names are:

projectbasenamespace.Properties.Resourceset.resx(.localeid)

When you create your database resources and create strongly typed resources we highly recommend you use the exact same resource naming to facilitate moving easily between Resx and Database resources.

You can do this using the configura

<add key="ResxExportProjectType" value="Project" />
<add key="ResxBaseFolder" value="~/Properties" />
<add key="ResourceBaseNamespace" value="Westwind.Globalization.Sample.Properties" />

If you match the namespace and file location you can seamlessly switch between .Resx and DbResources simply by switching the strongly typed export class or using raw .Resx resources without any code changes.

Programmatic Import and Export

The easiest way to manage import and export is via the Web Resource Editor and the Import and Export Resx dialog. However, you can also perform these tasks using code.

Resx Export Functions

The following demonstrates the code that is used by the Web Resource Editor to handle Resx Export:

[CallbackMethod]
public bool ExportResxResources(string outputBasePath = null)
{
    if (string.IsNullOrEmpty(outputBasePath))
        outputBasePath = DbResourceConfiguration.Current.ResxBaseFolder;
    else if(outputBasePath.StartsWith("~"))
        outputBasePath = Context.Server.MapPath(outputBasePath);

    outputBasePath = outputBasePath.Replace("/", "\\").Replace("\\\\", "\\");

    DbResXConverter exporter = new DbResXConverter(outputBasePath);

    if (DbResourceConfiguration.Current.ResxExportProjectType == GlobalizationResxExportProjectTypes.WebForms)
    {
        if (!exporter.GenerateLocalWebResourceResXFiles())
            throw new ApplicationException(WebUtils.GRes(STR_RESOURCESET, "ResourceGenerationFailed"));
        if (!exporter.GenerateGlobalWebResourceResXFiles())
            throw new ApplicationException(WebUtils.GRes(STR_RESOURCESET, "ResourceGenerationFailed"));
    }
    else
    {
        if (!exporter.GenerateResXFiles())
            throw new ApplicationException(WebUtils.GRes(STR_RESOURCESET, "ResourceGenerationFailed"));
    }

    return true;
}

Resx Import Functions

The following demonstrates the code that is used by the Web Resource Editor to handle Resx Import:

[CallbackMethod]
public bool ImportResxResources(string inputBasePath = null)
{

    if (string.IsNullOrEmpty(inputBasePath))
        inputBasePath = DbResourceConfiguration.Current.ResxBaseFolder;

    if (inputBasePath.Contains("~"))
        inputBasePath = Context.Server.MapPath(inputBasePath);

    inputBasePath = inputBasePath.Replace("/", "\\").Replace("\\\\", "\\");

    DbResXConverter converter = new DbResXConverter(inputBasePath);

    bool res = false;

    if (DbResourceConfiguration.Current.ResxExportProjectType == GlobalizationResxExportProjectTypes.WebForms)
        res = converter.ImportWebResources(inputBasePath);
    else
        res = converter.ImportWinResources(inputBasePath);

    if (!res)
        new ApplicationException(WebUtils.GRes(STR_RESOURCESET, "ResourceImportFailed"));

    return true;
}

Note that there are 3 different import/export functions for project Resx resources and for Local and Global Resources. The underlying exports are the same, but they vary in how they discover their resource paths.

Should you export Resources?

Since applications can run using the DbResourceProvider directly, should you even consider exporting your resources to Resx files?

Functionally it doesn't matter - the behavior of both will be practically the same and you can even switch between either mode if you use the strongly typed classes that the Westwind.Globalization creates.

However, for final distribution it's often easier to have embedded resources in applications which ensures that resources are always available. Because Resx resources are compiled into the primary assembly they have no external dependencies. On the other hand databases or network connections can fail which can result in resources being unavailable. This may or may not figure into your decision which to use.

As a general guideline using Database resources in production makes great sense if you actively modify the resource content as part of your application even after development. If you build your application, and once you are done you don't foresee resources changing much, then Resx might make more sense.

In the end it depends on the application and how dynamic the localized interface needs to be, but the options to do either are available.