LocalizationProvider
repository·master·Indexed 41 days ago
https://github.com/valdisiljuconoks/localizationproviderA database-driven localization framework for ASP.NET MVC applications. It replaces static resource files with database storage, featuring an Administration UI for editors, support for automatic machine translations via Azure AI, and client-side localization for JavaScript. Version 9.0 targets .NET 10 and supports SQL Server, PostgreSql, MongoDb, and Azure Tables storage providers.
What's inside LocalizationProvider
- DbLocalizationProvider v9.0 is a developer-friendly, strongly-typed localization package designed to simplify the localization process in .NET applications. It provides a structured way to manage localized content, moving away from loosely-typed strings towards a more robust, type-safe implementation.
What is LocalizationProvider?
masterLocalizationProvider is a database-driven localization provider for ASP.NET MVC web applications. It provides advanced features beyond standard resource files, including:
- Database-driven storage: Manage translations in a database rather than static files.
- Easy registration: Register resources directly via code.
- Hierarchical organization: Support for hierarchical resource structures using child classes.
- Administration UI: A built-in interface for editors to manage, add, and edit translations for various languages.
New features in LocalizationProvider v9.0
masterVersion 9.0 targets .NET 10 and introduces several productivity and performance enhancements:
Translations & Admin UI
- Automatic translations: Pluggable model supporting Azure AI / Cognitive Services. Supports single-resource translation and batch translations.
- Batch translations: Select multiple resources, choose source/target languages, and preview proposed translations before saving.
- Per-resource notes: Attach translator-facing hints using the
[Notes]attribute in code or via the Admin UI. These round-trip through CSV import/export. - Bulk delete: Remove multiple resources at once.
- Polished UI: Styled in-app confirmation dialogs.
Performance
- Optimized Sync: SQL Server synchronization uses a temp-table + bulk-copy path for large datasets.
- Refactored Caching: Uses
CachedTranslationswith targeted invalidation and reduced allocations. - Memoization: Expression-based resource keys and cached handler chains.
How the Synchronization Process works
masterThe synchronization process runs during application startup to scan and register localization resources. It discovers resources by looking for types annotated with
[LocalizedResource]or[LocalizedModel]. Once a type is found, the process scans its properties to identify localization keys.There are two types of localization resources:
- Resources: POCO objects consisting of key/value pairs (the key is the resource identifier, and the value is the translation).
- Models: Typically view models that use
DataAnnotationattributes (such as[Display]or[Required]) for localization.
Translate Enums from external assemblies
masterIf you need to provide translations forSystem.Enumtypes located in assemblies where you do not own the source code, use the foreign resources concept. Refer to theforeign-resources.mddocumentation for implementation details.Fetch translations as JSON via API
masterIf you want to avoid polluting the global
windowobject and prefer to fetch translations dynamically (e.g., via AJAX/XHR), you can request JSON directly from the localization provider endpoint.Direct JSON Request
Append the
jsonquery parameter to your request URL to receive only the JSON representation of the resource class translations.AJAX/XHR Detection
The localization provider automatically detects standard XHR/AJAX requests and will serve translations in JSON format by default.
How fallback language lookup logic works
masterThe translation lookup follows a sequential priority based on your configuration:
- Requested Culture: The provider first attempts to find the translation for the culture requested via
ILocalizationProvider.GetString(() => ...). - Fallback Chain: If the requested culture fails, the provider checks the
FallbackCultureslist in the order provided (e.g., if configured withsv->no->en, it checksnonext). - Invariant Culture: If all fallback languages fail, the provider will attempt to return a translation in the
InvariantCultureifEnableInvariantCultureFallbackis set totrue.
- Requested Culture: The provider first attempts to find the translation for the culture requested via
Use Export & Import with View Diff
masterThe Administration UI includes an enhanced Export & Import feature designed to handle complex synchronization scenarios between environments (e.g., moving translations from Staging to Production).
Unlike previous versions that only supported 'partial import' (new resources only) or 'full import' (flushing the target database), the current version allows you to view differences between your exported file and the target database. This helps prevent overwriting manual changes made by editors in the production environment while still allowing for the synchronization of new or updated translations.
Localize Model Data Annotations
masterWhen using
[LocalizedModel], the library automatically discovers resources for standard ASP.NETDataAnnotationattributes. This allows you to translate validation error messages.If a property has
[Required]and[StringLength(5)], the library discovers keys for the property itself and the specific validation constraints (e.g.,Property-Required).Supported attributes include:
[Display(Name = "...")]or[DisplayName][Required][StringLength]- Any attribute where you specify a custom
ErrorMessage.
namespace MySampleProject { [LocalizedModel] public class MyViewModel { [Display(Name = "This is default value")] [Required] [StringLength(5)] public string SampleProperty { get; set; } } } // Discovered keys: // MySampleProject.MyViewModel.SampleProperty // MySampleProject.MyViewModel.SampleProperty-Required // MySampleProject.MyViewModel.SampleProperty-StringLengthOrganize resources with Nested Localized Resources
masterYou can create a resource hierarchy to avoid polluting the global namespace. To enable a natural usage path in code (e.g.,
MyPageResources.Header.HelloMessage), define the nested resource type as an instance property (do not use thestatickeyword on the nested property) within the parent[LocalizedResource]class.Usage Pattern:
- Instance Property (Recommended): Allows usage like
@Html.Translate(() => MyPageResources.Header.HelloMessage). The key becomesMyProject.MyPageResources.Header.HelloMessage. - Static Property: Requires usage like
@Html.Translate(() => MyPageResources.HeaderResources.HelloMessage). This makes the path harder to discover in code.
namespace MyProject { [LocalizedResource] public class MyPageResources { public static string ThisIsErrorMessage => "This is default value from code"; public static HeaderResources Header { get; } public class HeaderResources { // Note: No 'static' keyword here to allow nested path usage public string HelloMessage => "Well, hello there!"; } } } // Usage in View: // @Html.Translate(() => MyPageResources.Header.HelloMessage)- Instance Property (Recommended): Allows usage like
Understand DbLocalizationProvider benchmark scenarios
masterThe benchmarks focus on the performance of the
ILocalizationProvider.GetString(...)method. The following scenarios are measured:- string-key, exact culture (cache hit): Calls
GetString("known.key", en)where the translation is already in the cache. This measures the dispatcher, cache-hit, and return path without a fallback walk. - string-key, fr-BE -> fr fallback walk (cache hit): A request for
fr-BEwhere the translation only exists for the parent culturefr. This measures the parent-culture fallback walk logic on top of a cache hit. - expression-key, exact culture (cache hit): Calls
GetString(() => Resources.X)whereXis already in the cache. This measures expression-tree walking and resource-key resolution on top of a cache hit.
- string-key, exact culture (cache hit): Calls
Reference shared resources with [UseResource]
masterTo prevent polluting your localization database with duplicate entries for common properties (like
Yes,No, orFirstName), you can use the[UseResource]attribute to point a model property to an existing resource defined in a different class.When a property is decorated with
[UseResource], the localization engine will use the key from the referenced resource instead of generating a new key based on the current model's type and property name. This ensures that updating a single shared resource updates all properties that reference it.// 1. Define a class for shared/common resources [LocalizedResource] public class CommonResources { public static string CommonProp => "Common Value"; } // 2. Reference that resource in your specific model [LocalizedModel] public class ModelWithOtherResourceUsage { [UseResource(typeof(CommonResources), nameof(CommonResources.CommonProp))] public string SomeProperty { get; set; } } // 3. Use standard translation helpers in your views // This will now resolve to 'CommonResources.CommonProp' instead of 'ModelWithOtherResourceUsage.SomeProperty' @Html.TranslateFor(m => m.SomeProperty)