@@ -13,6 +13,7 @@ A lightweight, extensible, and pluggable internationalization library with hot-r
1313- 🌍 ** Multi-language Support** - Support for any number of languages
1414- 🔄 ** Hot Reload** - Dynamically switch languages at runtime without restart
1515- 🔌 ** Pluggable Architecture** - Support for custom data source providers
16+ - 🧩 ** Plugin Support** - Dynamic provider registration/unregistration for plugin scenarios
1617- 📦 ** JSON Support** - Built-in JSON localization file support (flat and nested formats)
1718- 📄 ** RESX Support** - Built-in RESX resource file support
1819- 🎯 ** XAML Friendly** - Provides clean XAML markup extensions
@@ -481,6 +482,7 @@ Core culture service interface:
481482| ` RegisterProvider(ILocalizationProvider provider) ` | Registers a localization provider |
482483| ` UnregisterProvider(string providerName) ` | Unregisters a localization provider by name |
483484| ` CultureChanged ` | Culture changed event |
485+ | ` ProvidersChanged ` | Provider registered/unregistered event |
484486
485487### ILocalizationProvider
486488
@@ -548,6 +550,151 @@ public class DatabaseLocalizationProvider : ILocalizationProvider
548550}
549551```
550552
553+ ## Plugin Integration
554+
555+ DynamicLocalization supports dynamic provider registration/unregistration, making it ideal for plugin architectures.
556+
557+ ### Plugin Localization Setup
558+
559+ ``` csharp
560+ public class PluginLocalizationProvider : ILocalizationProvider
561+ {
562+ private readonly Dictionary <string , Dictionary <string , string >> _cache = new ();
563+
564+ public string Name => " MyPlugin" ; // Use a unique name to avoid conflicts
565+
566+ public PluginLocalizationProvider ()
567+ {
568+ LoadFromEmbeddedResources ();
569+ }
570+
571+ private void LoadFromEmbeddedResources ()
572+ {
573+ var assembly = typeof (PluginLocalizationProvider ).Assembly ;
574+ var resourceNames = assembly .GetManifestResourceNames ();
575+
576+ foreach (var name in resourceNames )
577+ {
578+ if (! name .Contains (" .Localization." ) || ! name .EndsWith (" .json" ))
579+ continue ;
580+
581+ var cultureName = ExtractCultureName (name );
582+ if (string .IsNullOrEmpty (cultureName )) continue ;
583+
584+ using var stream = assembly .GetManifestResourceStream (name );
585+ if (stream == null ) continue ;
586+
587+ using var reader = new StreamReader (stream );
588+ var json = reader .ReadToEnd ();
589+ var dict = JsonSerializer .Deserialize <Dictionary <string , string >>(json );
590+ if (dict != null )
591+ {
592+ _cache [cultureName ] = dict ;
593+ }
594+ }
595+ }
596+
597+ // ... implement other interface methods
598+ }
599+ ```
600+
601+ ### Plugin Lifecycle Management
602+
603+ ``` csharp
604+ public class PluginEntryPoint
605+ {
606+ private readonly ICultureService _cultureService ;
607+ private readonly PluginLocalizationProvider _provider ;
608+
609+ public PluginEntryPoint (ICultureService cultureService )
610+ {
611+ _cultureService = cultureService ;
612+ _provider = new PluginLocalizationProvider ();
613+ }
614+
615+ public void Initialize ()
616+ {
617+ _cultureService .RegisterProvider (_provider );
618+ // UI automatically refreshes to include plugin translations
619+ }
620+
621+ public void Unload ()
622+ {
623+ _cultureService .UnregisterProvider (_provider .Name );
624+ // UI automatically refreshes to remove plugin translations
625+ }
626+ }
627+ ```
628+
629+ ### Key Naming Convention
630+
631+ Use a prefix to avoid key conflicts with the main application or other plugins:
632+
633+ | Format | Example |
634+ | --------| ---------|
635+ | ` {PluginName}.{Feature}.{Item} ` | ` MyPlugin.Menu.Open ` |
636+ | ` {PluginName}.{Item} ` | ` MyPlugin.Title ` |
637+
638+ ## Extending Providers
639+
640+ Both ` JsonLocalizationProvider ` and ` ResxLocalizationProvider ` are designed for inheritance. Key methods are ` protected virtual ` for easy customization.
641+
642+ ### Extending JsonLocalizationProvider
643+
644+ ``` csharp
645+ public class CustomJsonProvider : JsonLocalizationProvider
646+ {
647+ public override string Name => " CustomJson" ; // Custom provider name
648+
649+ protected override string ? ExtractCultureName (string resourceName )
650+ {
651+ // Custom resource name parsing logic
652+ return base .ExtractCultureName (resourceName );
653+ }
654+
655+ protected override Dictionary <string , string >? ParseJsonToFlatDictionary (string json )
656+ {
657+ // Custom JSON parsing (e.g., support YAML or other formats)
658+ return base .ParseJsonToFlatDictionary (json );
659+ }
660+ }
661+ ```
662+
663+ ### Extending ResxLocalizationProvider
664+
665+ ``` csharp
666+ public class CustomResxProvider : ResxLocalizationProvider
667+ {
668+ public override string Name => " CustomResx" ;
669+
670+ protected override void DetectAvailableCultures ()
671+ {
672+ // Custom culture detection logic
673+ base .DetectAvailableCultures ();
674+ }
675+ }
676+ ```
677+
678+ ### Overridable Members
679+
680+ ** JsonLocalizationProvider:**
681+ | Member | Description |
682+ | --------| -------------|
683+ | ` Name ` | Provider identifier |
684+ | ` LoadAll() ` | Load all resources |
685+ | ` LoadFromEmbeddedResources() ` | Load from embedded resources |
686+ | ` LoadFromFiles() ` | Load from file system |
687+ | ` ExtractCultureName() ` | Extract culture from resource name |
688+ | ` ParseJsonToFlatDictionary() ` | Parse JSON to dictionary |
689+ | ` FlattenJsonObject() ` | Flatten nested JSON |
690+ | ` TryGetFromCulture() ` | Get string from specific culture |
691+
692+ ** ResxLocalizationProvider:**
693+ | Member | Description |
694+ | --------| -------------|
695+ | ` Name ` | Provider identifier |
696+ | ` DetectAvailableCultures() ` | Detect available cultures |
697+
551698## Architecture
552699
553700```
0 commit comments