Coder Social home page Coder Social logo

rickstrahl / westwind.globalization Goto Github PK

View Code? Open in Web Editor NEW
545.0 56.0 138.0 26.88 MB

Database driven resource localization for .NET applications

C# 46.02% HTML 13.28% JavaScript 36.45% CSS 1.51% Pascal 1.35% PowerShell 0.21% ASP.NET 1.19%
localization dotnet-core dotnet database-provider resx-resources

westwind.globalization's Introduction

West Wind Globalization

Database Resource Localization for .NET

Westwind.Globalization (.NET Standard, .NET 4.5+):
NuGet

Westwind.Globalization.AspNetCore (.NET Core 6.0, 5.0):
NuGet

Westwind.Globalization.Web (.NET 4.5+):
NuGet

This library and tooling provides easy to use database resource managers and providers that allow you to use a database for storing localization resources. Unlike static Resx resources, database resources are dynamic, can be changed at runtime and are editable by multiple users at the same time. The custom resource managers, providers and ASP.NET Core StringLocalizers use the standard .NET resource infrastructure, so other than startup configuration there are no code changes when switching from using traditional Resx resources.

It's also possible to import resources into a database, edit them dynamically, and then export them back out into Resx and optionally strongly typed classes so your deployed applications can run with Resx resources, while you can use dynamic Database resources during development.

A rich, Web based resource editor is also provided that makes it easy to create resource content and translate it interactively in a running application where you can see resource changes immediately applied without recompilation. You can import and export Resx resources, generate strongly typed classes and serve resources to JavaScript applications using the database resources. Web Resource Editor

Quick Links

Requirements:

  • works .NET Standard 2.0/.NET Core 2.0 or .NET 4.5 or later
  • SQL Server 2008-2019, SQL Server Express, SQL Compact 4, MySql, SqLite
  • .NET Core Sample Project uses .NET Core 2.2 and needs VS 2019

.NET Version Support

Version 3.0 adds support for the 2.0 versions of .NET Standard, .NET Core and ASP.NET Core. The following versions are provided:

  • Westwind.Globalization (net45 and netstandard2.0)
    NuGet
  • Westwind.Globalization.AspNetCore (net5.0, net6.0)
    NuGet
  • Westwind.Globalization.Web (net45)
    NuGet
  • Westwind.Globalization.Sample (net45)
  • Westwind.Globalization.Sample.AspNetCore (netcore3.1+, netcore2.1+)

Installation

Installation is different depending on which version of .NET you are running under .NET Core and Full Framework use different project types and NuGet Packages for the Web support.

Limited non-Windows Support for Admin Features

The admin features of this package have not been fully ported to non-Windows platforms. Specifically, any of the RESX and Import Export features will not work on non-windows platforms currently. However runtime database access is fully functional.

For installation use NuGet.

To Install for .NET Core

Please read the installation instructions below to configure once you've installed the project. You will need to configure startup settings in order for the Db Providers to run.

PM> Install-Package Westwind.Globalization.AspNetCore

If you're not using a Web Project you can just use the base package:

PM> Install-Package Westwind.Globalization

ASP.NET Core Administration Web UI: Separate Download

Due to changes in NuGet support in .NET Core we can no longer package static HTML, CSS and JS as part of NuGet packages. As a result a separate download is required to add the Localization Admin UI. You can download it from:

Unzip the contents of the Zip file into your project folder root, which creates the ./wwwroot/LocalizationAdmin Web folder and adds related resources to ./Properties.

To Install on .NET Framework

Please read the Installation Section below or watch the Getting Started Video, which describes how to install the packages, configure the project, import existing resources.

PM> Install-Package Westwind.Globalization.Web.Starter

or if you don't want sample resources and a test page, simply use:

PM> Install-Package Westwind.Globalization.Web

If you're not using a Web Project or you're using MVC/Web API and don't need the Web Resource Editor you can just use the core package:

PM> Install-Package Westwind.Globalization

Resources:

Features

  • .NET Resources in Sql Server, SqlCe, MySql and SqLite
  • Injectable .NET Core StringLocalizers (ASP.NET Core)
  • ASP.NET Database ResourceProviders (ASP.NET/WebForms)
  • .NET ResourceManager (ASP.NET MVC,non-Web apps)
  • Uses standard .NET Resource infrastructure and caching
  • Or: Use our dynamic DbRes string based helper (works anywhere)
  • Interactive Web Resource Editor to edit Resources
  • Keyboard optimized resources editing
  • Translate text resources with Google or Bing
  • Use Markdown in your text resources
  • Import and export Resx resources
  • Generate strongly typed classes from the Db resources
  • Release and reload resources in running Web apps
  • Serve .NET Resources to JavaScript as JSON
  • Create your own custom DbResourceManagers
  • Directly access and manage DbResources with code

Because this library uses the standard .NET resource infrastructure using the DbResourceProvider or DbResourceManager requires no code changes from ResX resources, other than provider configuration in your .config file and potentially a few lines of startup code. You can import existing Resx resources and edit them interactively. Serve your resources either with the DbResourceProvider or DbResourceManager, or if you chose export them back out to Resx files for your final application code. Importing and exporting is a easily done from the Web admin interface or can be fired using code including as part of your build process.

Note: The database is accessed only once per ResourceSet and per Locale, using the standard .NET Resource caching architecture used in Resource Providers and Resource Managers, so database access and usage is minimal. You can use these Providers/Manager in MVC, WebForms and even in non Web applications.

Web Resource Editor

One of the main reasons people want to use Database resources rather than Resx resources is that it allows for dynamic updates of resources. Resx resources are static and compiled into an application and so are typically tied to the development process, while dynamic resources can be updated separately even after the application has been completed and deployed.

Since data is stored in a database it's easy to create editing front ends or programmatic tools that simply manipulate the database. This library ships with a Web interface that allows editing of resources interactively and an easy to use data API to update resources programmatically.

Web Resource Editor

Web Resource Translator Dialog

The resource editor is an easy way to localize resources interactively, but it's not the only way you can do this of course. Since you have access to the data API underneath it as well as the database itself, it's easy to create your own customized UI or data driven API that suits your application needs exactly.

Markdown Support The resource edit form allows you to optionally use Markdown for resource editing which in addition to localization makes it possible to use this libary as a basic CMS to manage user manageable content in your applications.

How the database Providers work

This library implements a custom .NET ResourceManager and ASP.NET ResourceProvider (for .NET 4.x) that are tied to a database provider interface (although you can implement non-data providers as well). This means you can access resources using the same mechanisms that you use with standard Resx Resources in your .NET applications. It also means although resources are initially loaded from the database for the first load of each ResourceSet. .NET then caches resources for each individual ResourceSet and locale the same way that Resx resources are read from the assembly resources and then cached.

The database is hit only for the first read of a given ResourceSet/Locale combination - not every resource access hits the database!

The DbResourceManager can be used in any type of .NET application using the DbRes or DbResInstance class methods, generated strongly typed classes, or using the Resource Manager directly. The DbResourceProvider classes can be used in ASP.NET applications - especially for WebForms with support for local and global resources, implicit resources and control meta tags. MVC applications typically use the ResourceManager with strongly typed resources or the DbRes classes or by exporting resources back into RESX.

Underneath the .NET providers lies a the Westwind.Globalization data access layer (IDbResourceDataManager) that provides the data interface to provide access to various providers. The default provider uses SQL Server as a data source with additional providers available for MySql, SqLite and SqlCompact. This API is accessed by the Resource Provider and Resource Manager implementations to read the resources from the database.

Additionally the API can be directly accessed to provide resource access, and the DbRes helper class provides very easy access to these resources using the DbRes.T() method which can be thought of as a high level translation method.

This interface is also directly accessible and allows your code as well as support code like the UI Web Resource editor to easily access and manipulate resources in real-time at runtime.

This library includes quite a proliferation of classes most of it due to the implementation requirements for the .NET providers which require implementation of a host of interface based classes for customization.

There are three distinct resource access mechanisms supported:

  • ASP.NET Resource Provider (best used with WebForms)
  • .NET Resource Manager and strongly typed resources (Non-Web apps, ASP.NET Core, classic MVC apps or anywhere where you already use Resx)
  • Direct Db Provider access using DbRes and DbResInstance helpers
    (easiest overall - works everywhere)

Running the Sample Application

To run the sample application you have to set up a database to provide the resources. The following assumes you are using the default configuration which uses SQL Server and a database named Localizations - you can change this via web.config settings (see the following section for more details).

  • Create a SQL Server/Express Database called Localizations
  • Make sure the Web User using has rights to create a Table in this DB
  • Open http://localhost:xxxxx/LocalizationAdmin/index.html in your browser
  • You'll get an error message like:
    ResourceLoadingFailed: Invalid Object Name Localizations
  • Use the Create Table button to create the Localizations table
  • Use Import or Export Resx button to import resources from the project into the db
  • Select Import Resources from the dropdown
  • Use the path of ~/Properties/ for Resx Import Folder
  • Click on the Import Resources button
  • You should now have all the sample and LocalizationForm resources in the db

Installation and Configuration

The easiest way to use this library in your own applications is to install the NuGet package into an ASP.NET application.

ASP.NET Core Packages

pm> Install-Package Westwind.Globalization.AspNetCore

@icon-info-circle Download the Localization Admin UI Files

Nuget no longer allows distribution of static content, so in order to use the Localization Admin UI you also need to download the resources for the Localization Admin UI if you want to integrate the Localization interface into your application.

Once you've downloaded the Localization Admin UI zip file, unzip the entire content into the project's root folder.

For non-Web applications or if you use only the DbRes based localization features, you can just install the base package.

pm> Install-Package Westwind.Globalization

ASP.NET Core integration works in combination with ASP.NET Core new Localization features. Westwind.Globalization builds on top of this, or you can just use its native features. West Wind Globalization supports:

  • IStringLocalizer DI via custom IDbResourceStringLocalizer
  • appSettings.json configuration (optional)

ASP.NET Core Configuration

Configuration can be accomplished in 3 ways:

  1. Using a standalone dbResourceConfiguration.json file
  2. Using appsettings.json in a DbResourceProvider section
  3. Additional ASP.NET Core IConfiguration functionality configured (ie. Environment variables, user secrets)
  4. Explicit configuration via AddWestwindGlobalization(opt => return true)

Configuration values are applied in the order listed, with later assignments over-writing earlier settings.

DbResourceConfiguration.json

You can create a standalone DbResourceConfiguration.json file for configuration that works both in full framework and .NET Core:

{
  "ResourceAccessMode": "DbResourceManager",
  "ConnectionString": "server=.;database=localizations;integrated security=true;",
  "DataProvider": "SqlServer",
  "ResourceTableName": "Localizations",
  "ResxExportProjectType": "Project",
  "ResxBaseFolder": "~/Properties/",
  "StronglyTypedGlobalResource": "~/Properties/Resources.cs",
  "ResourceBaseNamespace": "AppResources",
  "AddMissingResources": true,
  "LocalizationFormWebPath": "~/LocalizationAdmin/",
  "GoogleApiKey": "XXXfaSyDcvmGhGN7FlynP9QUZOLF8_4K8iF9ChWo",
  "BingClientId": "12345-4b99-47ed-be7e-caf733526020"
}

If this file exists configuration values are read from it.

@icon-warning Copy to Output Directory

If you want to use DbResourceConfiguration.json for configuration storage make sure you set the Copy to Output Directory option to Copy if newer or Copy always to ensure the file is copied into the published output folder.

ASP.NET Core IConfiguration

For ASP.NET Core operation Westwind.Globalization also registers the DbResourceConfiguration instance as IOptions<DbResourceConfiguration> which gives strongly typed access to the configuration via depedency injection.

This means you can use any configured configuration providers - most commonly:

  • appsettings.json using a DbResourceConfiguration object
  • Environment variables
  • User Secrets store

You can store configuration settings in appsettings.json like this:

{
  "Logging": {...},
  "DbResourceConfiguration": {
    "ResourceAccessMode": "DbResourceManager",
    "ConnectionString": "server=.;database=localizations;integrated security=true;",
    "DataProvider": "SqlServer",
    "ResourceTableName": "Localizations",
    "StronglyTypedGlobalResource": "~/Properties/Resources.cs",
    "ResourceBaseNamespace": "AppResources",
    "ResxExportProjectType": "Project",
    "ResxBaseFolder": "~/Properties/",
    "AddMissingResources": true,
    "LocalizationFormWebPath": "~/LocalizationAdmin/",
    "BingClientId": "12345-4b99-47ed-be7e-caf733526020",
    "GoogleApiKey": "XXXfaSyDcvmGhGN7FlynP9QUZOLF8_4K8iF9ChWo"
  }
}

If provided the appsettings.json file overrides DbResourceConfiguration.json.

We recommend you only use one of the files to avoid confusion. For ASP.NET Core projects we recommend you store settings in appsettings.json since that gives you dependency injection for IOptions<DbResourceConfiguration> as well as putting configuration settings into a well-known location.

Enabling West Wind Globalization in ASP.NET Core

You also need to explicitly enable localization features in ASP.NET Core using the following code in the Startup.cs file's ConfigureServices() method:

public void ConfigureServices(IServiceCollection services)
{
    // Standard ASP.NET Localization features are recommended
    // Make sure this is done FIRST!
    services.AddLocalization(options =>
    {
        // I prefer Properties over the default `Resources` folder
        // due to namespace issues if you have a Resources type as
        // most people do for shared resources.
        options.ResourcesPath = "Properties";
    });
    

    // Replace StringLocalizers with Db Resource Implementation
    services.AddSingleton(typeof(IStringLocalizerFactory), 
                          typeof(DbResStringLocalizerFactory));
    services.AddSingleton(typeof(IHtmlLocalizerFactory),
                          typeof(DbResHtmlLocalizerFactory));
                          
    
    // Required: Enable Westwind.Globalization (opt parm is optional)
    // shown here with optional manual configuration code
    services.AddWestwindGlobalization(opt =>
    {                
        // the default settings comme from DbResourceConfiguration.json if exists
        // you can override the settings here, the config you create is added
        // to the DI system (DbResourceConfiguration)

        // Resource Mode - from Database (or Resx for serving from Resources)
        opt.ResourceAccessMode = ResourceAccessMode.DbResourceManager;  // .Resx
        
        // Make sure the database you connect to exists
        opt.ConnectionString = "server=.;database=localizations;uid=localizations;pwd=local";
        
        // Database provider used - Sql Server is the default
        opt.DataProvider = DbResourceProviderTypes.SqlServer;

        // The table in which resources are stored
        opt.ResourceTableName = "localizations";
        
        opt.AddMissingResources = false;
        opt.ResxBaseFolder = "~/Properties/";

        // Set up security for Localization Administration form
        opt.ConfigureAuthorizeLocalizationAdministration(actionContext =>
        {
            // return true or false whether this request is authorized
            return true;   //actionContext.HttpContext.User.Identity.IsAuthenticated;
        });

    });

    ...
    
    // .NET Core 2.x
    // services.AddMvc()
    //         .AddViewLocalization()
    //         .AddDataAnnotationsLocalization();
    //         .AddApplicationPart(typeof(DbResViewLocalizer).Assembly);
    
    // .NET Core 3.1
    services.AddMvc(opt =>  
        // required for the Administration interface for dynamic serialization
        .AddNewtonsoftJson();
        // for MVC/RazorPages localization
        .AddViewLocalization()
        // for ViewModel Error Annotation Localization
        .AddDataAnnotationsLocalization();

    // this *has to go here* after view localization has been initialized
    // so that Pages can localize - note required even if you're not using
    // the DbResource manager.
    services.AddTransient<IViewLocalizer, DbResViewLocalizer>();
}

Any code changes made override any of the file values. You can also replace the entire DbResourceConfiguration object entirely in this handler.

In addition you probablywant to add standard ASP.NET Core Localization features to the Configure() method in Startup.cs:

public void Configure(IApplicationBuilder app) 
{
    ..
  
    var supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("en"),
        new CultureInfo("de-DE"),
        new CultureInfo("de"),
        new CultureInfo("fr")
    };
    app.UseRequestLocalization(new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture("en-US"),
        SupportedCultures = supportedCultures,
        SupportedUICultures = supportedCultures                 
    });
    
    ..
    
    // .NET Core 3.1
    app.UseRouting();

    app.UseDefaultFiles();
    app.UseStaticFiles();

    // .NET Core 3.1 required for Endpoints
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapDefaultControllerRoute();
    });
    
    // .NET Core 2.x only needs this
    //app.UseMvc()
}

Note

Dependency Injection for DbResourceConfiguration

You can get access to DbResourceConfiguration in a number of ways:

  • Via DI by asking for DbResourceConfiguration
  • Via DI by asking for IOptions<DbResourceConfig> (if loaded through IConfiguration)
  • Static DbResourceConfiguration.Current

Dependency Injection for IDbResStringLocalizer

One of the base features of ASP.NET Core's Localization is IStringLocalizer which provides the provides an interface to map type signatures to instances of Resx resources. DbResStringLocalizer uses the DbResourceManager (which supports switchable Db or Resx resource access).

To use the DbRes localizer, override the default IStringLocalizer with:

services.AddSingleton(typeof(IStringLocalizerFactory), typeof(DbResStringLocalizerFactory));

Once this is done you can use standard ASP.NET localization to use data from the configured localization provider (Db or Resx typically).

Use of IStringLocalizer is optional.

You can use DbRes.T() or strongly typed resources directly if you prefer. However, for DataAnnotation localization IStringLocalizer is required in ASP.NET Core (shrug), so generally you'll want to add DbResStringLocalizer in ConfigureServices().

Full Framework Configuration

pm> Install-Package Westwind.Globalization.Web

If you're not using a Web Project, or an MVC/Web API project that doesn't use the Web Resource Editor you can use the core package:

pm> Install-Package Westwind.Globalization

which doesn't install the web related components and HTML resources.

The .Web version installs the required assemblies, adds a few configuration entries in web.config and enables the resource provider by default. The Starter package adds sample resources and a couple of test pages. I recommend you use the .Starter package so you can ensure the provider is working and serving resources - once up and running you can remove the starter package, leaving the dependent assemblies in place.

Full Framework Configuration

ASP.NET Classic uses the web.config Configuration file for configuration

<configuration>
  <configSections>
    <section name="DbResourceConfiguration" requirePermission="false" 
			 type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>  

  <DbResourceConfiguration>
    <add key="ConnectionString" value="server=.;database=Localizations;integrated security=true;" />
    <add key="ResourceTableName" value="Localizations" />
    <add key="AddMissingResources" value="False" />

    <!-- Resource Imports and Exports -->
    <add key="ResxExportProjectType" value="Project" />
    <add key="StronglyTypedGlobalResource" value="~/Properties/Resources.cs" />
    <add key="ResourceBaseNamespace" value="WebApplication1.Properties" />    
    <add key="ResxBaseFolder" value="~/Properties" />

    <!-- WebForms specific only -->
    <add key="LocalizationFormWebPath" value="~/LocalizationAdmin/" />
    <add key="DesignTimeVirtualPath" value="" />
    <add key="ShowLocalizationControlOptions" value="False" />
    <add key="ShowControlIcons" value="False" />

    <!-- Google Translate API -->
    <add key="GoogleApiKey" value="" />

    <!-- Bing Translation -->
    <add key="BingClientId" value="" />
  </DbResourceConfiguration>

  <!-- Enable ASP.NET Resource Provider  -->
  <system.web>
    <globalization resourceProviderFactoryType=
     "Westwind.Globalization.DbSimpleResourceProviderFactory,Westwind.Globalization.Web" />
  </system.web>
</configuration>

Overriding Configuration Settings (Full Framework)

To override configuration settings that are set in web.config you can access the DbResourceConfiguration.Current instance that's used to configure the application.

protected void Application_Start()
{                       
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    // Specify where config information comes from (config file is default - can be separate Json/Xml)
    //DbResourceConfiguration.ConfigurationMode = ConfigurationModes.ConfigFile;
    
    var config = DbResourceConfiguration.Current;
    
    config.ConnectionString = "SqlServerLocalizations";
    config.DbResourceDataManagerType = typeof(DbResourceSqlServerDataManager);

    //DbResourceConfiguration.Current.ConnectionString = "MySqlLocalizations";
    //DbResourceConfiguration.Current.DbResourceDataManagerType = typeof(DbResourceMySqlDataManager);

    // force ResourceMode explicitly. Default is AspNetResourceProvider
    GeneratedResourceSettings.ResourceAccessMode = ResourceAccessMode.DbResourceManager;
    //GeneratedResourceSettings.ResourceAccessMode = ResourceAccessMode.Resx;

    // *** Remove or Add custom resource converters
    // *** By default the MarkdownResourceConverter is provided
    //DbResourceConfiguration.Current.ResourceSetValueConverters.Clear();
    //DbResourceConfiguration.Current.ResourceSetValueConverters.Add(new MarkdownResourceSetValueConverter());
}

Settings made here occur before any requests are made to the providers, so Application_Start is a good way to initialize settings, including selecting a provider. Any of the Configuration values can be set here, or anywhere else by using the static DbResourceConfiguration.Current instance.

Setting ASP.NET Locale based on Browser Locale (Full Framework)

In order to do automatic localization based on a browser's language used you can sniff the browser's default language and set the UiCulture in the Begin_Request handler of your ASP.NET application class. A helper method to provide this functionality automatically is provided.

protected void Application_BeginRequest()
{
    // Automatically set the user's locale to what the browser returns
    // and optionally only allow certain locales/locale-prefixes
    WebUtils.SetUserLocale(allowLocales: "en,de");
}

This forces the user's Culture and UI Culture to whatever the browser is using, and explicitly. Now when a page is rendered it will use the UiCulture of the browser. The optional allowLocales enforces that only certain locales can be set - anything not matched is defaulted to the server's default locale.

The way .NET resource managers work, if there's no match for the locale the user provides, resources fall back to the closest matching locale or the invariant locale. So if the user comes in with it-IT but you don't support it or it-IT in your resources the user will see resources for invariant. Likewise if a user comes in with de-CH (Swiss german) and you de (without a locale specific suffix) the de German version will be returned. Resource Fallback tries to ensure that always something is returned.

Configuration Settings (.NET Core and Full Framework)

ConnectionString and ResourceTableName
The two most important keys are the connectionString and resourceTableName which point at your database and a table that holds resources. On full framework you can use either a raw connection string or a Connection String Name defined in <ConnectionStrings> of your web.config file.

AddMissingResources
When set to true causes any resource lookup that fails to produce matching resource ID to write the invariant resource into the database. Use with caution - as this might slow down your application significantly as you now write to the database on any missing resources. Use only during development and testing to get resources into the system for easier debugging later.

ResxExportProjectType
This option determines how the Resx export feature works. The two options are Project or WebForms. Project exports all resource files into \Properties folder underneath the resxBasePath and excludes any resource sets that include a . in their name (assumed to be ASP.NET resources). WebForms writes out resources into folder specific App_LocalResources and App_GlobalResources folders based on the root folder

ResxBaseFolder
The base folder that's used for all Resx Import and Export operations. The default is ~/ which is the root web folder, but you can specify a full OS path here. Note that this allows you to read/write resources in other non-web projects - as long as your Web application has writes to the folder specified.

StronglyTypeGlobalResource and ResourceBaseNamespace If you do a strongly typed class export from the admin manager all resources will be created in a single file in the this file using the ResourceBaseNameSpace as the namespace in the generated class.

Run the Web Resource Editor

In order to use database resources you'll actually have to create some resources in a database. Make sure you've first added a valid connection string in the config file in the last step! Then open the /LocalizationAdmin/index.html in your browser and click on the Create Table button in the toolbar.

Once the table's been created you can now start creating resources interactively, by directly adding values to the database table, or by using the DbResourceDataManager API to manipulate the data programmatically.

By default a Resources ResourceSet has been provided for you the resources of which are used in the test page. You can remove those resources or the resource set as needed once you know the provider works. ResourceSets are logical groups of resources that belong together - I like to use one ResourceSet per form or per application feature depending on how much content is involved. But you can also use a single ResourceSet for your entire application if you want. Whatever works for you to make it easy to find resources.

Import Existing Resources

I also recommend that you first perform an Import Resx step to pull any existing Resx resources from the ~/Properties/ folder (or whereever) into your project. This will also import the Localization form's resources into your database so that the localization form properly localizes when running with the DbResource Provider.

Using Resources in your Application

There are a number of different ways to access resources from this provider.

  • Direct access with DbRes
  • ASP.NET Resource provider
  • .NET Resource Manager
  • Strongly Typed Resources

DbRes Helper Class

The DbRes and DbResInstance Helper class are wrappers around the DbResourceManager and DbResouceDataManager object. The DbRes class contains a handful of common use methods that are used to retrieve and manipulate resources by name and resource set.

In an ASP.NET Web MVC (or WebPages) application you can use:

// Using current UiCulture - empty resource set uses default (Resources)
DbRes.T("HelloWorld")

// Exact match with resource - Hallo Welt
DbRes.T("HelloWorld","Resources","de")

// Resource Fallback to de if de-CH doesn't exist - Hallo Welt
DbRes.T("HelloWorld","Resources","de-CH")

This is an easy mechanism that's tied closely to the database resources created and can be applied with minimal fuss in any kind of .NET application.

ASP.NET MVC or ASP.NET WebPages

Say Hello: @DbRes.T("HelloWorld") at @DateTime.Now

ASP.NET WebForms

Say Hello: <%: DbRes.T("HelloWorld") %> at <%= DateTime.Now %>

In .NET code

string value = DbRes.T("HelloWorld");

The DbRes.T() method returns the ResourceId passed in if a resource is missing in the ResourceSet which can be useful for providing 'default' text. Some people like to use full resource strings as their resource Ids so default values are always available even if a resource is missing or the provider is not available.

Using the ASP.NET Resource Provider (Full Framework, WebForms)

If you're using an existing WebForms application or you want to use the ASP.NET based Resource Provider model for accessing resources you can use the DbSimpleResourceProvider. This implementation is an ASP.NET Resource Provider implementation that directly accesses the DbResourceDataManager to retrieve resources. A second Resource Provider implementation that uses the DbResourceProvider uses the DbResourceManager to indirectly access resources. Typically the DbSimpleResourceProvider is the more efficient interface.

To use this provider you have to enable it in web.config. To do so:

<configuration>  
  <system.web>
       <globalization resourceProviderFactoryType="Westwind.Globalization.DbSimpleResourceProviderFactory,Westwind.Globalization.Web" />    
       <!--<globalization resourceProviderFactoryType="Westwind.Globalization.DbResourceProviderFactory,Westwind.Globalization" />-->    
  </system.web>
</configuration>

Once enabled you can use all the standard ASP.NET Resource Provider features:

  • GetGlobalResourceObject, GetLocalResourceObject on Page and HttpContext
  • Using meta:resourcekey attributes on Web Controls

Page.GetGlobalResourceObject() or HttpContext.GetGlobalResourceObject()

<legend>ASP.NET ResourceProvider</legend>
<label>Get GlobalResource Object (default locale):</label>
<%: Page.GetGlobalResourceObject("Resources","HelloWorld") %>

Page.GetLocalResourceObject()

<label>GetLocalResourceObject via Expression:</label>                 
<%: GetLocalResourceObject("lblHelloWorldLabel.Text") %>

WebForms Control meta:resourcekey attribute

<label>Meta Tag (key lblHelloWorldLabel.Text):</label>
<asp:Label ID="lblHelloLabel" runat="server" meta:resourcekey="lblHelloWorldLabel"></asp:Label>

WebForms Resource Expressions

<label>Resource Expressions (Global Resources):</label>
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Resources,HelloWorld %>"></asp:Label>

Strongly typed Resources

The Web Resource Editor form allows you to create strongly typed resources for any global resources in your application. Basically it'll go through all the non-local resources in your file and create strongly type .NET classes in a file that is specified in the DbResourceProvider configuration settings.

<add key="StronglyTypedGlobalResource" value="~/Properties/Resources.cs" />
<add key="ResourceBaseNamespace" value="WebApplication1.Properties" />

You specify the namespace and filename to generate it to. Once you've generated the strongly typed resource file with the embedded resource class(es), you need to recompile your application to make the resource properties available to it.

The generated resources can use either the ASP.NET resource provider (which uses whatever provider is configured - Resx or DbResourceProvider) or the DbResourceManager which only uses the DbResourceManager. Using the latter allows you to also generate resources for use in non-Web applications.

Here's what generated resources look like:

namespace WebApplication1.Properties
{
    public class GeneratedResourceSettings
    {
        // You can change the ResourceAccess Mode globally in Application_Start  
        // AspNetResourceProvider, Project (MVC, Windows), Resx      
        public static ResourceAccessMode ResourceAccessMode = ResourceAccessMode.AspNetResourceProvider;
    }

	public class Commonwords
	{
		public static System.String Ready
		{
			get
			{
				if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider)
					return (System.String) HttpContext.GetGlobalResourceObject("Commonwords","Ready");
				return DbRes.T("Ready","Commonwords");
			}
		}

		public static System.String ThisIsALongLineOfText
		{
			get
			{
				if (GeneratedResourceSettings.ResourceAccessMode == ResourceAccessMode.AspNetResourceProvider)
					return (System.String) HttpContext.GetGlobalResourceObject("Commonwords","This is a long line of text");
				return DbRes.T("This is a long line of text","Commonwords");
			}
		}
	}
}

These can then be used in any ASP.NET application:

ASP.NET MVC or WebPages
<div class="statusbar">@CommonWords.Ready</div>
ASP.NET WebForms
<div class="statusbar"><%: WebApplication1.CommonWords.Ready %></div>

.NET Code

string ready = CommonWords.Ready;

Note that strongly typed resources must be re-generated whenever you add new resources, so this is an ongoing process. As with the Resx Generator if you remove or rename a resource you may break your code.

Strongly typed resources are generated into a single file for all the resource sets exported in order to not clutter up your application with unnecessary generated files.

ASP.NET MVC ModelValidation (Full Framework)

ASP.NET and Entity Framework support model validation and you can also use the database provider to localize these validation messages. To do so you have to generate strongly typed resources, or export to Resx and then enable strong resource typing. ASP.NET/EntityFramework Model validation works based on class property access so in order to use it a type has to exist.

To do this:

  • Open the Localization Administration form
  • Use Export Class to export create a class
  • Or: Use Export to Resx to export Resx files (in Project mode)
    then make sure to enable the strong type generation on the Resx and choose Public class

Once you've done this you can create your validation classes like you always would:

public class ViewModelWithLocalizedAttributes
{
    [Required(ErrorMessageResourceName = "NameIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Name { get; set;  }

    [Required(ErrorMessageResourceName = "AddressIsRequired", ErrorMessageResourceType = typeof(Resources))]
    public string Address { get; set;  }
}

The type will be your exported class or generated Resx class and the name is the name of the property on the generated object. DataAnnotations use Reflection to lookup the property name, so if for some reason the validation does not work check the following:

  • Make sure the property name is typed correctly and matches a property name.
  • Try writing out the actual property using @Resources.AddressIsRequired to ensure the value is valid (on a simple test page perferrably).

Model Validation ASP.NET Core Mvc

ASP.NET MVC uses a completely different model for Model validation based on IStringLocalizer. In this initial .NET Core release we don't have support for this yet, but we're working on it. It's coming in an update soon.

Switching Database Providers

By default the resource providers and manager use SQL Server to hold the database resources. If you don't do any custom configuration in code to specify the DbResourceConfiguration.DbResourceDataManagerType you'll get the Sql Server provider/manager.

The following providers are supported:

  • Sql Server (2008-2016, Sql Azure)
  • MySql
  • SqLite
  • Sql Server Compact (no .NET Core support)

To use a specific provider, assign the DbResourceConfiguration.DbResourceDataManagerType to the appropriate engine you want to use during startup configuration.

  • typeof(DbResourceSqlServerDataManager)
  • typeof(DbResourceMySqlDataManager)
  • typeof(DbResourceSqLiteDataManager)
  • typeof(DbResourceSqlCompactDataManager)

In .NET Core you set the value in the AddDbResourceLocalization(opt) configuration:

services.AddWestwindGlobalization(opt =>
    ...
    opt.DbResourceDataManagerType = typeof(DbResourceMySqlDataManager);
    opt.ConnectionString = "server=localhost;uid=testuser;pwd=super10seekrit;" + 
                           "database=Localizations;charset=utf8"

For full framework you can set the value in the Application_Start handler and set the singleton configuration value:

DbResourceConfiguration.Current.DbResourceDataManagerType =
                  typeof(DbResourceSqLiteDataManager);
DbResourceConfiguration.Current.ConnectionString = 
    "server=localhost;database=Localizations;" +
    "uid=testuser;pwd=super10seekrit;charset=utf8" 

Note that the connection string can be set in configuration files (app/web.config in full framework, DbResourceConfiguration.json or appsettings.json in .NET Core), but the provider configuration has to be set in code.

Here's a little more info on how to specify each provider and the dependencies that are required.

Sql Server

no additional package needed

.NET Core:

opt.ConnectionString = "server=.;database=localizations;integrated security=true";
// not required since it's the default
opt.DbResourceManagerType = typeof(DbResourceSqlServerManager);

web.config file connection String Example:

<add name="SqlServerLocalizations"
    connectionString="server=.;database=localizations;integrated security=true;"
    providerName="System.Data.SqlClient" />

MySql

add NuGet Package: MySql.Data

Code Configuration:

opt.ConnectionString = "server=localhost;uid=testuser;pwd=super10seekrit;database=Localizations;charset=utf8";
opt.DbResourceDataManagerType = typeof(DbResourceMySqlDataManager);                

web.config file connection String Example:

<add name="MySqlLocalizations"
    connectionString="server=localhost;uid=testuser;pwd=super10seekrit;database=Localizations;charset=utf8" 
    providerName="MySql.Data.MySqlClient" />

SqLite

.NET Core add Nuget Package: Microsoft.Data.SqLite

Full Framework add NuGet Package: System.Data.SQLite

Code Configuration:

opt.ConnectionString = "Data Source=./data/SqLiteLocalizations.db";
opt.DbResourceDataManagerType = typeof(DbResourceSqLiteDataManager);

Make sure to use a valid path where the database file can be found and used.

web.config file connection String Example:

<add name="SqLiteLocalizations"
    connectionString="Data Source=|DataDirectory|\SqLiteLocalizations.db"
    providerName="System.Data.SQLite" />

Sql Server Compact

add NuGet Package: Microsoft.SqlServer.Compact

not supported on .NET Core

DbResourceConfiguration.Current.DbResourceDataManagerType = typeof(DbResourceSqlServerCeDataManager);

web.config connection string example:

<add name="SqlServerCeLocalizations" 
     connectionString="Data Source=|DataDirectory|\Localizations.sdf;Persist Security Info=False;" 
     providerName="System.Data.SqlServerCe.4.0" />

Global Data Manager Configuration

This code configures the data manager globally so every time a data access operation occurs it instantiates the data manager configured here. It's important that you add the appropriate assembly first, otherwise these provider types will not be available and your code won't compile.

JavaScript Resource Handler

If you're building applications that include JavaScript logic it's likely that you also need to access localized resources on the client. This library provides a JavaScript Resource Handler that can serve resources in the proper localized locale to your client application.

The resource handler allows you to specify which resources to serve and which locale - or auto-detected locale - to serve the data to your JavaScript client application.

The handler produces a JavaScript object map that is exposed as a global variable with properties for each of the resource keys:

resources = {
	"HelloWorld": "Hallo schn\u00F6de Welt",
	"Ready": "Los",
	"Today": "Heute",
	"Yesterday": "Gestern",
    "dbRes": function dbRes(resId) { return resources[resId] || resId; }    
};

Resource values are normalized, meaning if a localized doesn't exist, resource fallback is used to fill the value. A dbRes() function is added to the object to allow returning the resource ID if a value can't be matched or if the resources for some reason fail to load.

Resources can be accessed in client code:

<script>
    var hello = resources.HelloWorld;
    var hello2 = resources.dbRes("HelloWorld");
</script>

The latter will always return some value (HelloWorld) even if there's no matching property value to return.

Configuration and Usage (ASP.NET Core)

For ASP.NET Core the JavaScript resource handling does not have to be configured - it's always available as long as ASP.NET MVC is active and running on the Web site. The handler is hooked in via custom routing that lives at a fixed URL.

To add client side script to the api/JavaScriptLocalizationResources endpoint (all in one line):

<script src="/api/JavaScriptLocalizationResources?  
                ResourceSet=LocalizationForm&
                VarName=resources&
                localeId=auto&
                ResourceMode=ResDb"></script>

The following parameters are passed:

  • resourceSet - The ResourceSet name to serve resources to serve.
  • varName - The name of the global variable to declare the map on. This can also be a property value of an existing object like page.resources to avoid cluttering up global scope.
  • localeId - Optional locale id like de-DE or de. If not passed the value is auto which reads the value from the users accept-language header.
  • resourceMode - Either ResDb or Resx which determines whether resources are served from the database or resx. If not passed or auto the DbResourceConfig setting is used.

Configuration (.NET Framework)

To configure the Resource Handler for classic ASP.NET it has to be registered in web.config as follows:

<configuration>
<system.webServer>
<handlers>
    <add name="JavaScriptResourceHandler"
        verb="GET"
        path="JavascriptResourceHandler.axd"
        type="Westwind.Globalization.JavaScriptResourceHandler,Westwind.Globalization.Web" />
</handlers>
</system.webServer>
</configuration>

Note this entry is automatically made for you when you install the NuGet package.

Usage

The resource handler is then accessed as a script resource in your code by calling the static JavaScriptResourceHandler.GetJavaScriptResourcesUrl() method:

<script src="@JavaScriptResourceHandler.GetJavaScriptResourcesUrl("resources","Resources")"></script>
<script>
    document.querySelector("#JavaScriptHelloWorld").innerText = resources.HelloWorld;
    document.querySelector("#JavaScriptYesterday").innerText = resources.dbRes("Yesterday");
</script>

or if you're using a plain HTML page (all one line):

<script src="JavaScriptResourceHandler.axd?ResourceSet=Resources&
                            LocaleId=auto&
                            VarName=resources&
                            ResourceType=resdb"></script>
<script>
    document.querySelector("#JavaScriptHelloWorld").innerText = resources.HelloWorld;
</script>

Either of the above generate the following script code (shown here localized in German):

resources = {
	"HelloWorld": "Hallo schn\u00F6de Welt",
	"Ready": "Los",
	"Today": "Heute",
	"Yesterday": "Gestern",
    "dbRes": function dbRes(resId) { return resources[resId] || resId; }    
};

Handler Url Formatting

A full JavaScript resource handler URL looks like this:

JavaScriptResourceHandler.axd?ResourceSet=Resources&LocaleId=auto&VarName=resources&ResourceType=resdb&ResourceMode=1

The QueryString parameters on the URL are used as follows:

ResourceSet

The ResourceSet name as defined in the database or the name of the ResX file relative to the ResxBaseFolder defined in the configuration.

LocaleId

This can be either a specific localeId like de, or de-de. Or it can be auto in which ASP.NET will use its default locale, which you can override to match the browser's locale as described in Auto-detecting browser locale. The recommendation is to use auto and have IIS detect the browser locale and switch the thread UiCulture.

ResourceType - resdb,resx

You can specify what type of resources are loaded with this Resource handler. The options are resdb, which uses the dbResourceProvider/Manager or resx which uses Resx resources. Again in order for Resx resources to work the ResxBaseFolder must be set.

ResourceMode 0 (WebForms), 1 (Project/folder)

Determines how Resx Resources are loaded using either project (1) or WebForms (0) style resources. If project (0) resources are used make sure the ResxBaseFolder points to the path where your Resx resources like. If you use WebForms mode, resources are located using App_GlobalResources and App_LocalResources folders.

Project Sponsors

The following people/organizations have provided sponsorship to this project by way of direct donations or for paid development as part of a development project using these tools:

  • Frank Lutz - Monosynth
    Frank provided a sizable donation to the project and valuable feedback for a host of improvements and bug fixes.

  • Craig Tucker - Alabama Software
    Craig offered early support and feedback for this project and billed project time for a number of additions to the library as part of a larger project.

  • Dan Martin - WeatherMaker
    Dan and his company provided a block of my billable hours dedicated to this project for adding support for MySql.

Want to sponsor this project, need customization or make a donation? You can contact me directly at [email protected] or you can also make a donation online via PayPal.

License

The Westwind.Globalization library is licensed under the MIT License and there's no charge to use, integrate or modify the code for this project. You are free to use it in personal, commercial, government and any other type of application.

Commercial Licenses are also available as an option. If you are using these tools in a commercial application please consider purchasing one of our reasonably priced commercial licenses that help support this project's development.

All source code is copyright West Wind Technologies, regardless of changes made to them. Any source code modifications must leave the original copyright code headers intact.

Warranty Disclaimer: No Warranty!

IN NO EVENT SHALL THE AUTHOR, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS PROGRAM AND DOCUMENTATION, BE LIABLE FOR ANY COMMERCIAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS, EVEN IF YOU OR OTHER PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

westwind.globalization's People

Contributors

3x0dv5 avatar bliles avatar hobwell avatar pwojciak avatar rickstrahl avatar twenzel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

westwind.globalization's Issues

Newtonsoft.Json is referenced twice in Sample project

Newtonsoft.Json is referenced twice in the Sample project. The hint path for the second reference points to a version of the Nuget package (7.0.1) that doesn't match with the version listed in packages.config.

The sample project will build if the following section of the project file is removed:

<ItemGroup> <Reference Include="Newtonsoft.Json"> <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> </ItemGroup> <ItemGroup>

ResourceManager.GetString("SomeResourceId") throws exception

Hi Rick,
After generating a strongly typed class from the LocalizationAdmin interface I tried to call App.Properties.ResourceSetNameClass.ResourceManager.GetString("SomeResourceId") inside my MVC Controller class. But GetString() throwed an exception: "MissingManifestResourceException" (https://msdn.microsoft.com/de-de/library/d17ax2xk(v=vs.110).aspx). But accessing the properties directly like @App.Properties.ResourceSetNameClass.SomeResourceId worked fine. Any idea what went wrong?
Thanks!
Andreas

XLIFF and Pseudo-translation support

  1.  Any plans to add features to exporting and importing from db/resx to XLIFF file formats for conversion by industry standard tools?
    
  2. AddPseudo-translator language generator in the admin tool so that development and unit testing can happen on this test language independent of actual conversion.
    

Resx export trouble if resource set contains a period

To help myself find a resource set real quick, I named them after my MVC views: Home.cshtml, _Layout.cshtml, etc. When exporting 'all resource sets' to resx, just a '.resx' file is produced, with the contents of a single resource set.

config value for DbResourceDataManagerType is not taken into account

Even if you declare MySql provider in web.config:

<DbResourceConfiguration>
   (...)
   <add key="DbResourceDataManagerType" value="Westwind.Globalization.DbResourceMySqlDataManager" />
</DbResourceConfiguration>

, it is not taken into account (ResourceProviderInfo on /LocalizationAdmin/index.html shows that default DbResourceSqlServerDataManager is in use).

A workaround is to set it explicitly in Application_Start:

DbResourceConfiguration.Current.DbResourceDataManagerType = typeof(DbResourceMySqlDataManager);

It would be nice to have it working based on web.config only, especially, that this option seems to be supported (https://github.com/RickStrahl/Westwind.Globalization/blob/6c9f73f334739ec0be4eb8b0506d81de386b1131/Westwind.Globalization.Test/App.config).

For MySQL the method IsLocalizationTable should also filter by current database

On MySQL, running the query:

SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME=@0

returns tables from all databases, that exist on the server.
It means, that additional condition with database name should be added:

SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME=@0
AND TABLE_SCHEMA = @1

This change should be performed for MySQL only, as MySQL returns in this column name of database, and MSSQL returns schema name ('dbo' by default).
But, anyway, good job with changing the way of how the IsLocalizationTable works!

Sort Order on Export Resx Files

The way that we've decided to work with this is that the "Master" version of all the resource strings is in the resx file, because we can check that into source control. Normally, in production, we'll be building the strings into the application. Only when we need to activate translations for someone in particular will we use the database localization provider, and we'll sync the database from dev up to the server, let them do their thing, sync the database back down, and then export those strings to resx files and save them in source control.

One fly in the ointment: at the moment, there doesn't seem to be a defined sort order on exporting strings to resx, it just depends on the order they were added to the database. Which means that different developers generate resx files with strings in different orders, leading to lots of unnecessary changes and conflicts. I don't need to be able to configure what the sort order is - I just need there to be one!

That is, in DbResourceDataManager, at the statement

string sql = "select ResourceId,Value,LocaleId,ResourceSet,Type,TextFile,BinFile,FileName,Comment,ValueType,Updated from " + Configuration.ResourceTableName +
" where ResourceSet " +
(!localResources ? "not" : string.Empty) + " like @ResourceSet " +
"ORDER BY ResourceSet,LocaleId";

Just add ResourceId on to the end of the ORDER BY clause.

I would do it myself and issue a pull request, but it's going to take hours to install and remember how to operate Git 'cos I'm just on Subversion at the moment, and changing it to

                         "ORDER BY ResourceSet,LocaleId, ResourceId"; 

should take all of 2 seconds if you already have a development environment up and running.

Mixed Case Resource Sets

I'm not sure how I got into this state, but:

If you manage to get different records for the same resource set with difference cases in the database
e.g.

UserControls/LoginInfo.ascx
usercontrols/LoginInfo.ascx

Then in GenerateWebResourceResXFiles(bool localResources) they will come out next to each other from the query in GetAllResources because it's ordered by ResourceSet, but as we loop through them, where it detects if the ResourceSet has changed

// Create a new output file if the resource set or locale changes
if (ResourceSet != LastSet || LocaleId != LastLocale)

the change of case will make it appear to be a different Resource set because "UserControls/LoginInfo.ascx" != "usercontrols/LoginInfo.ascx"

So it will end the file and start a new one - but of course, the file system isn't case sensitive, so the new file will overwrite the old one, which will leave you with a file containing only some of the expected resources.

MIght possibly fix by changing the line to use String.Compare with Ignore Case

if (String.Compare(ResourceSet, LastSet, true) != 0 || String.Compare(LocaleId, LastLocale,true) != 0)

thought the best workaround for me in the short term is to update the database to normalise the cases of the Resource Set names.

HTML Text

Is there any way to add HTML string for content localization ?
Like:

<p>some content that needs <b>to be localized</b></p>
<p>this content is HTML <a href="google.com">Click here</a></p>

Problems with instructions in section: Using the ASP.NET Resource Provider

I have an existing WebForm application that uses meta:xxxxx tags in the HTML. So, I followed the instructions you listed in the section labeled "Using the ASP.NET Resource Provider". Now I am getting an error when I start up the application. Below are pictures of the Error screen that appears when the application starts up and the second is a "listing" of my Web Config file

webconfig file error when switching to support using the asp net resource provider

Web Config file:

web config file

Smart Search on Web AdminLocalization

The search only within the Current resourceSet, I beleive It is something we can improve. because in real world Projects we have to deal with multiple resourceSet, and not have to know each resource within which resourceSet before searching so searching on Whole will be great. And will be more cool if you offer search within content of strings not only the keys.

Abend with FireFox

Per your suggestion, I am using FireFox instead of IE 8 to execute my application. I am now getting an abend
"Make sure data column names are correct"
"Make sure the index is not a negative number."
"Make sure that the maximum index on a list is less than the list size"

abend with firefox

Binary Upload for new Resource is broken

When uploading a binary on a resource that doesn't exist yet (ie. a new ResourceId) the upload overwrites the currently selected resource value.

Code incorrectly captures the wrong resource ID when uploading the file.

Works correctly if you have an existing resource or even another resource in a different locale.

Possibility of managing the synchronisation between DbResources on Dev machine and on Production

In most of real world Apps, we do have Developers team still working on project after launching the web site on production. The situation is : that managers and web masters of Web site can change, edit the resources while in production, and development Team will continue work on their local Dbs, so when deploying the bugs corrections or evolution, with new resources, there are 2 possibilities :

  • Export Whole from Dev MAchine => Prod : Problem erasing what has been done on production DB.
  • Manually add (or with scripts) which is a pain

can we have Export, Import with duplicate control ?

IE 11 Issue

image

IE11 doesn't seem to like the resource editor. The listbox down the left is always on two items high and if you do find your items in that list box it also doesn't appear to then save any changes you make.

Tested on Windows 10

Question about Westwind.Globalization extensibility

We plan to use Westwind.Globalization not only for localization, but also as a tool for customization of labels / headlines via web UI by administrator, kind of mini CMS.

The catch is that customizations can be different for different users, more specifically for user companies. Think of overriding a logo, company name or welcome text for different companies. So when user logs in he sees a welcome text based on a company he belongs to.

Ideally we would use some extensibility points in Westwind.Globalization to have this functionality, but I don't think there are any. Looks like we will need to fork the project, add CompanyId column to the database table and update UI to allow overriding of resources per company.

Is there a way to have this kind of functionality without forking? Any suggestions what the best approach would be?

Syncing translations between development and production.

This is a little more rambly than an "issue" - but I don't think you have a forum for me to post this on =)

We have some software which we develop on our development machines, and multiple instances are installed on production servers. Different production instances might be on different versions of the software, if we're e.g. beta testing some new features on one instance before we upgrade everyone else.

We need:

  1. To make sure that all the strings live in source control, because i don't want to have to start versioning sql server databases.
  2. To make it easy for developers to add new strings
  3. To make it easy for translators to translate strings on the production servers, writing their translated values to a database up there.
  4. To have some way of syncing new strings from the developers up, and translated strings from the translators down, without overwriting each other on the way.

So, we regard the contents of the resx files as the master version of the strings, and the database a temporary store for translation purposes. Most instances run on compiled resx files. Supposing we want to activate translation on an instance, we will import the strings from the resx files into the database in development, then script that database to insert statements, upload the insert statements to the server, and then wipe the Localizations database on the server and fill it from the insert statements, and add the DbProvider to the config file. That's a bit fussy, but easy enough.

Once the translator has finished though... how are we going to get the strings out of the database on the server and back into source control? Given that we've been doing development whilst the translator has been working, and we have new untranslated strings in the development version that we don't want wiped out by simply replacing it with the database from the server.

What we're thinking is:

  • Export the localization database on the server to resx files, and download them.
  • Create a branch in source control based on the tag of the build that the translator was working on (We're using subversion; not sure what the equivalent in git-speak is)
  • Check the downloaded resx files into that branch, and then merge the branch with the trunk, catching conflicts on the way
  • Import the resx files from the trunk back into the database in development, and then send that database up to production, so we have all the translated strings and new development strings side by side for the translator to work on.

Which would work without any code changes. But the process creating files for syncing to and from the Localization database on the production server might be simplified by some additional functionality: neither resx files not SQL insert statements feel like the ideal format for that. It would also be handy to be able to export and import only one language at a time.

Not really asking for anything; if it's ok with you, I might write that sync process and then contribute it, but I thought it would be worth posting this to see if any others had the same requirements, or if you thought it was a good idea, or if you had a better idea.

And, thanks very much for making the project exist. I've already got a bunch of SQL scripts for things like "Ensure that every string is created, blank in every language" which I'd be happy to contribute too.

-- Create strings in all other locales
DECLARE @newLocaleId nvarchar(255) 
DECLARE Locales CURSOR FOR SELECT DISTINCT LocaleId FROM Localizations WHERE LocaleId <> ''

OPEN Locales

FETCH NEXT FROM Locales INTO @newLocaleId
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @newLocaleId  

INSERT INTO Localizations
(ResourceId, Value, LocaleId, ResourceSet, Type, BinFile, TextFile, Filename, Comment, ValueType, Updated)
SELECT 
ResourceId, @newLocaleId +' ' + Value, @newLocaleId, ResourceSet, Type, BinFile, TextFile, Filename, Comment, ValueType, GETDATE()
FROM Localizations AS loc1
WHERE loc1.LocaleId = '' AND loc1.Value <> ''
AND NOT EXISTS (SELECT * FROM Localizations AS loc2 WHERE loc2.LocaleId = @newLocaleId AND loc2.ResourceId = loc1.ResourceId AND loc2.ResourceSet= Loc1.ResourceSet)

FETCH NEXT FROM Locales INTO @newLocaleId
END

CLOSE Locales
DEALLOCATE Locales

Missing files

Won't build from a fresh clone.
There seem to be at least 2 missing files

  • ".nugte\nuget.exe"
  • "Westwind.Globalization.Web\app.config"

Am I missing a step?

Flag resource to prevent overwrite on update

We have a use case where localizations are initially supplied by us, but the customer may modify them.

When a software updated happens and the initially installed localization resources would be updated we do not want to overwrite the customer's modifications.

How can that be achieved?

Asian language support

Does this product provide support for languages that read from the right to the left like Chinese, Arabic, or Hebrew?

If so, how can I trigger that?

If not, how would you suggest that this be done?

Google Translation issue

Google translation not working
.
Error Details:

Connection failed: : The remote server returned an error: (403) Forbidden.

Solution not working with SQL CE

After trying various configurations, it doesn't seem possible to make it work with SQL CE local file :(

It looks like it is not using correct Provider for CE file, but always use full SQL Server provider

More intuitive Web AdminLocalization Interface

regarding the Web AdminLocalization Interface : The Selector of resourceSet on bottom is not so intuitive. I believe making it on the left side and presenting Keys/resourceSets in hierarchical view will be better.

Synch lock should be shared

In DbResourceProvider.cs line 62 the lock should be shared(static). Otherwise, you will have a lock for each instance of dbresourceprovider causing index out of bounds exception while tyring to add : DbResourceConfiguration.LoadedProviders.Add(this);

Switching ResourceAccessMode from ASPNet to Resx errors for typed class

Example:

<%= Resources.CalculatorHome_Title %> errors when switching ResourceAccessMode from ASPNet to Resx

Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "Westwind.Globalization.Sample.Properties.Resources.resources" was correctly embedded or linked into assembly "" at compile time, or that all the satellite assemblies required are loadable and fully signed.

manager has 0 resource sets in memory.

public static string GetResourceString(string resourceSet, string resourceId,
ResourceManager manager,
ResourceAccessMode resourceMode)
{
if (resourceMode == ResourceAccessMode.AspNetResourceProvider)
return GetAspNetResourceProviderValue(resourceSet, resourceId) as string;
if (resourceMode == ResourceAccessMode.Resx)
return manager.GetString(resourceId);

        return DbRes.T(resourceId, resourceSet);

GeneratedResourceHelper

Hello!

If we'll see at GeneratedResourceHelper, we can see lines of code like:
return DbRes.T(resourceSet, "LocalizationForm");

First of all, why resourceSet passed as first argument of DbRes.T which first argument is really resourceId (not resource set)?
And also why it uses "LocalizationForm" constant string? So, this code means that i cannot use any resource set name except LocalizationForm?.. It is impossible condition in world of modularity.

May be I misunderstood something, but that makes whole strongly typed resource export classes not usable.
If I wrong, explain, please, how strongly typed code should work then. I've exported database localization resource set into class and got such code:

public static System.String Hello
        {
            get
            {
                return GeneratedResourceHelper.GetResourceString("TPreinstall","Hello",ResourceManager,GeneratedResourceSettings.ResourceAccessMode);
            }
        }

Then in template I've typed:

@TPreinstall.Hello

And result was not what I've been expected:

TPreinstall

It outputs "TPreinstall" (which is really resource set name) instead of "Hello, World!" that is already in database.
If I look at database then, I can find there new row:

| ResourceId   |     Value   | LocaleId   |     ResrouceSet  |
| TPreinstall  | TPreinstall |   <null>   | LocalizationForm |

Notice: I've manully changed

public static ResourceAccessMode ResourceAccessMode = ResourceAccessMode.AspNetResourceProvider;

to

public static ResourceAccessMode ResourceAccessMode = ResourceAccessMode.DbResourceManager;

To let strongly typed resources be loaded from database.

JavaScript Resource Export - Exposing resources to callers

There's a new JavaScript resource exporter that can export server side resources to JavaScript files. So rather than serving server resources dynamically (which is a lot simpler IMHO) you can now also export the resources directly to files on the site which can then be loaded by the client directly without the server having to create the resource sets on the fly.

You can use the provided helper classes to generate resources. You can specify a resource 'namespace' when you render your resources to file, with the namespace being prefixed to the name of the resource set. The namespace is user configurable when you run the export.

To use the resources you then ensure that the 'namespace' exists:

// Define your 'namespace/module' somewhere in startup code
// before resources are referenced
global = { resources: {} };

You then load any resources you into a page:

<script src="resources/MyResources.js"></script>

The generated resources look like this:

// generated resource code
global.resources.MyResources = {
    "__localeId": "en";
    "AddressIsRequired": "An address has to be entered.",
    "HelloWorld": "Hello Cruel World",
    "NameIsRequired": "A name must be provided.",
    "Today": "Aujourd'hui 2",
    "Yesterday": "Yesterday",
    "dbRes": function dbRes(resId) { return global.resourcs.MyResources[resId] || resId; }      
}

where global.resources is the variable name that you supply and to which the name of the resource set is appended (ie. global.resources.MyResources). In effect the name you specify becomes a 'namespace' prefix, but you are responsible for making sure this namespace/module object exists and can be appended to.

The code to generate the resources looks like this:

var js = new JavaScriptResources();
bool result = js.ExportJavaScriptResources(".\\JavascriptResources\\","global.resources");
Assert.IsTrue(result);
Console.WriteLine(File.ReadAllText(".\\JavascriptResources\\" + "LocalizationForm.de.js"));

Files are exported one file per resourceset per locale into the same flat folder specified. So you get:

MyResources.js && invariant (should we use a default here?)
MyResources.de.js
MyResources.fr.js

LocalizationForm.js && invariant (should we use a default here?)
LocalizationForm.de.js
LocalizationForm.fr.js

All of the above works now, but I'm not sure what pattern should be used here to export the resources that makes it easy to use it generically not tying down to specific module syntax.

With the server side code this is pretty straight forward because you don't have to figure out which version of the resources to load - the server actually figures that out on its own and the syntax to pull it down is automated and can be controlled by the client as part of the script parameters.

With the pre-generated JavaScript resources those names are hardcoded once generated and that doesn't feel right.

Obviously module syntax would help here, but how would that work if someone is not using any module loaders?

Any input would be greatly appreciated.

Updated from previous version I got `ValueType` error.

On localizationAdmin I receive a ValueType error. Diggin into the xhr request the informations is as follows :

Remote Address:[::1]:56137
Request URL:http://localhost:56137/localizationAdmin/localizationService.ashx?method=GetResourceItems
Request Method:POST
Status Code:500 Internal Server Error

**Response Headers**
HTTP/1.1 500 Internal Server Error
Cache-Control: private
Content-Type: application/json
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUmVwb3NcQmFydFxCaW9sb2dpYVRvdGFsXEJvdXJvblxzcmNcQXBwc1xCb3Vyb24uV2ViXGxvY2FsaXphdGlvbkFkbWluXGxvY2FsaXphdGlvblNlcnZpY2UuYXNoeA==?=
X-Powered-By: ASP.NET
Date: Mon, 25 May 2015 22:57:24 GMT
Content-Length: 815

**Request Headers**
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8,es;q=0.6
Connection:keep-alive
Content-Length:97
Content-Type:application/json;charset=UTF-8
Cookie:__RequestVerificationToken=BTSTNtLyTInr5WppJhM8xsYDOjO3-LqXgj3sf5gEsMCFiDsyoMvAuwGcAQgcSk7WPpMLUMiJyFperyezB1i3pUrx6tjxOLS2bzqRQvwGn2A1; RE30feT1d9Q=CfDJ8AH5CUgCE_9Nq2falogucjcUay2l6-Kf3fjvTDcv5DyCq8SC5V5Ig4qRVlt2Xmv7lvMeDFiaGncqipxd6Hiyeiq0YPVTSQP1SHSfLxPQ0qETVq_EpIRm5c6EgQjLvO8asz9IxzRhOzs4BT8MOT9kJxk; .AspNet.ApplicationCookie=TGBtACaHnEjZ09u3f8eEuQSCG1SYRsmoxBpHDSL5YShasAIDyMgo58fdqb-FCBskm4DRj866Hih-IZTQ-UmbGINSnFuEPiCM25YbL6A3TfysiFR94hZGF6rTbPD1UWsRQaTM7XgjcmYYZ7QtRaVv1mof_Y2RKAlCR1Df4oa98uMVafkuwVQ9c7_3J88AQPXt4V-v7S9-hums15sBm69_CkyoA24fuQNZteIp48YGWUTC0Lfsp6bg4Dl03KYexsSxJShf2_VVJBVpZ8h8hMt6iFkUy6sFotNINq05ZRKRxiwX-KKW4ZLSIoxocJbTdH7iXDcZqGQneC1zarwBDsEfmg-1_fpry4DDxRiHp1VG9HP1B_Lnh05xUSs7gZCQUoLcRF7KlYMenxtVAMg-Xtz6O7O8iL8382TvGTEClkRU9kwgvXCfn3ifIweLOS7bxDWmWsdyYLZVYKmHp_IeI02t9preLte4E7XoBQzJ4UtK81I; _ga=GA1.1.730622452.1432540800; _gat=1
Host:localhost:56137
Origin:http://localhost:56137
Referer:http://localhost:56137/localizationAdmin/
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36

**Query String Parameters**
method=GetResourceItems

**Request Payload**
{"ResourceId":"Diccionario de enfermedades y sus conflictos asociados.","ResourceSet":"Products"}
data: null
detail: " at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
↵ at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
↵ at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
↵ at Westwind.Web.CallbackMethodProcessorHelper.ExecuteMethod(String method, Object target, String[] parameters, CallbackMethodParameterType paramType, CallbackMethodAttribute& callbackMethodAttribute)
↵ at Westwind.Web.JsonCallbackMethodProcessor.ProcessCallbackMethodCall(Object target, String methodToCall)"
isError: true
message: "ValueType"
resultData: null
source: null
statusCode: 500

object does not contain a definition for ObjectCreationHandling

Hi Rick,

i can't get this to work in my solution?
I always get this error in your resource editor:
'object' does not contain a definition for 'ObjectCreationHandling'
and i'm unable to fix it. Refreshing the screen a couple of times results into a blank page.
All settings in webconfig are correct and existing resources are pulled from DB.
I have migrated all the resources in old version to new localization table.

Is there some kind of json size limit (i have 219 resource records)?

Another error i'm getting is :

'object' does not contain a definition for 'Formatting'

import resources not possible when table in DB is empty

Import resources is not possible when table in DB is empty.
How to reproduce:

  1. create table
  2. remove sample values from db (delete from localizations;)
  3. bug: only 'create table' button is available and when clicked there is a message 'LocalizationTableNotCreated Localization Table exists already'

Move web resource editor to a separate nuget package

Hello,

Can you move the web resource editor to a separate nuget package to WestWind.Globalization, please? Or provide it as a separate git repository that people can clone. I was quite surprised when I added the nuget package to a .dll assembly, and it added some javascript and aspx files.

We have a big solution, and several assemblies use DbResourceDataManager (currently we have a copy of this repo, which we added as a project reference, but I want to use the nuget package instead). Most of these assemblies are not web projects, and it will take a lot of time to delete these files from all the projects that the nuget package would add. We don't use the resource editor on our web application either. We have it on a separate, internal-only website, so I would need to delete these files from our web projects too.

PS, I'm happy to see you extracted interfaces on some of the classes recently. Currently I can't unit test anything that uses DbResourceDataManager, unless I create my own wrapper. But, it would be a lot of work to change all references to the new wrapper class. Although, if I did, then I would only need to delete the web files added by the nuget package once, so maybe it's a good idea for now.

Use in Non-MVC ASP.NET project

I installed your package inside a non-MVC ASP.NET project. When I navigate to the Localization Admin, I get a whole bunch of JAVA errors. I am using Visual Studio 2013 and using IE 8.

I put in the Web.CONFIG file changes as you indicated. How do I get the Localization Admin page to show?

BTW, I am newbie here.

Any help would be appreciated!

Thank you!

WestWind Globalization causes security exception

After installing the NuGet package Westwind.Globalization.Web, my MVC site throws System.Security.Permissions.ReflectionPermission on access. After uninstalling the package, the exception goes away.

Obviously this can be remedied by the package installer making a trust level change in web.config. On the other hand, is reflection really necessary for this library?

Full exception below:

Server Error in '/' Application.

Security Exception

Description: The application attempted to perform an operation not allowed by the security policy. To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.

Exception Details: System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[SecurityException: Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.]
System.Delegate.DelegateConstruct(Object target, IntPtr slot) +0
Owin.Loader.DefaultLoader..ctor(Func3 next, Func2 activator, IEnumerable1 referencedAssemblies) +169 Owin.Loader.DefaultLoader..ctor(IEnumerable1 referencedAssemblies) +41
Microsoft.Owin.Host.SystemWeb.OwinBuilder.GetAppStartup() +143
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +103
System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func1 valueFactory) +115 System.Threading.LazyInitializer.EnsureInitialized(T& target, Boolean& initialized, Object& syncLock, Func1 valueFactory) +72
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +104
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +534
System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +339
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

Blank values

Sometimes my website's labels and button texts turn blank. The only way to restore them is to restart the application pool.

Running WebForms on .NET Framework 4

Local resource set on MVC

Is it possible to have a Local Resource set on MVC where there's no .aspx file extension ?

So you have a resource set like /Home/Index ?

if that's not possible, is there anyway to set the Default resource set when none is found like:
defaultResourceSet = "General" ?

Resx file generator does not write xml declarations

Hi Rick,

DbResXConverter writes resx files without a proper xml declaration.

The culprit seems to be Westwind.Globalization/SupportClasses/DbResXConverter.cs, line 337. This line intends to write the xml template to file by writing the root child element, thereby ignoring the xml declaration.

Could not load types

I'm not sure what I am missing, but I don't seem to be able to shake these two errors when installing this package via nuget:

Error 32 Could not load type 'Westwind.GlobalizationWeb.LocalizationAdmin_StronglyTypedGlobalResources'.

Error 31 Could not load type 'Westwind.GlobalizationWeb.LocalizeAdmin'. Z:\LanguageTest2

I thought it might have something to do with the DbResourceProvider configuration, but I can't seem to figure it out.

In the auto-configuration, I get nearly what you have posted on the GitHub page, but instead of:

resourceNamespace="AppResources"

it is set to:

resourceBaseNamespace="MyApplication.Properties"

Note that the attributes have different names. Changing this around doesn't seem to help.

Any ideas on how I might correct this error?

Exception on Installation

When I try to install the NuGet package "Westwind.Globalization.Web" (version 2.2.0), I get the following exception. This prevents it from being installed properly. Any ideas?

System.IO.IOException: The process cannot access the file 'C:\Users\rob\.nuget\packages\Westwind.Globalization.Web\2.2.0\Westwind.Globalization.Web.2.2.0.nupkg' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at NuGet.Packaging.NuGetPackageUtils.ExtractFiles(ZipArchive archive, String targetPath, Func`2 shouldInclude)
   at NuGet.Packaging.NuGetPackageUtils.ExtractNupkg(ZipArchive archive, String targetPath)
   at NuGet.Packaging.NuGetPackageUtils.ExtractPackage(String targetPath, FileStream stream)
   at NuGet.Packaging.NuGetPackageUtils.<>c__DisplayClass1_0.<<InstallFromSourceAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at NuGet.Common.ConcurrencyUtilities.<ExecuteWithFileLocked>d__0`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at NuGet.Common.ConcurrencyUtilities.<ExecuteWithFileLocked>d__0`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at NuGet.Packaging.NuGetPackageUtils.<InstallFromSourceAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at NuGet.Protocol.Core.v3.GlobalPackagesFolderUtility.<AddPackageAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at NuGet.Protocol.Core.v3.DownloadResourceV3.<GetDownloadResourceResultAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at NuGet.PackageManagement.PackageDownloader.<GetDownloadResourceResultAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at NuGet.PackageManagement.NuGetPackageManager.<ExecuteNuGetProjectActionsAsync>d__42.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at NuGet.PackageManagement.NuGetPackageManager.<ExecuteNuGetProjectActionsAsync>d__42.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at NuGet.PackageManagement.UI.UIActionEngine.<ExecuteActionsAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at NuGet.PackageManagement.UI.UIActionEngine.<PerformActionAsync>d__3.MoveNext()
========== Finished ==========

Web AdminLocalization - comment not visible

It would be great to have a comment relating to the resource visible on the translation page of the Web LocalizationAdmin. Thanks to it translators could know the context of use for the currently translated resource.

I can see that comments are stored in DB and there is even button 'Comment', but disabled by default.
What's the status of this feature, is it work in progress?

Caching issue with globalization library

How can we turn off caching or dynamically clear caching when user changes language data? We need to be able to see data changes instantly but it is requiring us to reboot server after changes are made. Is there a setting within library to turn off caching?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.