Lazy Loading von Plugins in multilingualen .NET Applikationen

Letztes Jahr hab ich eine kleine Anwendungs-Shell gebaut, welche mir die Entwicklung von Desktopanwendungen einfacher machen soll. Schwerpunktmäßig habe ich mich dabei auf die Features Erweiterbarkeit, Multilingualität, Lazy Loading und gute Trennung der Logik von der Oberfläche konzentriert. Als festes Standbein habe ich das Framework Mono.Addins genutzt, mit dem ich in meinen Anwendungen Erweiterungspunkte in einem XML-Baum definieren kann.

Ähnlich wie in XUL oder XAML erzeuge ich außerdem etliche Teile meiner Oberflächen direkt aus dem XML-Baum, der sich aus allen XML-Knoten der geladenen Plugins ergibt. Dies ermöglichst es mir Menüs und Werkzeugleisten darzustellen ohne alle Plugin-Assemblies zu laden. Denn die Infos über Struktur, Titel und Icons der Oberfläche befinden sich bereits in den XML-Daten und müssen nicht erst vom .NET Code erzeugt werden. Mit solchen Prinzipien kann man auch große Anwendungen zu einem schnellem Start verhelfen, Visual Studio, #Develop und Co machen es genauso.

Anmerkung: Mono.Addins speichert die sogenannten XML-Addin-Manifests entweder in separaten Dateien (neben den Assemblies) oder in eingebetteten Resourcen innerhalb der .dll’s. In dem Fall wird beim ersten Start der Anwendung das Addin-Manifest extrahiert und binär auf der Festplatte abgelegt, um den nächsten Programmstart zu beschleunigen.

Schwieriger wird es, wenn man eine multilinguale Anwendung entwickelt und die Texte für die Oberfläche nicht mehr fix in die Metadaten eingetragen werden können. Im .NET-Umfeld ist es üblich alle zu lokalisierenden Resourcen in Satelliten-Assemblies (eine Assembly pro Kultur, z.B. de-DE) auszulagern. Aus dieser kann man dann die Strings und Bilder laden, doch verhindert man dadurch das verzögerte Laden des Plugins, denn die Sattelite-Assemblies und damit das gesamte Plugin werden bereits beim Laden der GUI benötigt. Folge ist, dass der Programmstart wieder ewig dauert.

Lösung: Wir müssen die für den Start so wichtigen Texte der Oberfläche separat speichern. Das muss immer dann gemacht werden, wenn ein Plugin hinzugefügt wird, d.h. genau dann wenn auch Mono.Addins einen Scan durchführt. Zu Hilfe kommt uns dabei eine recht neue Funktion von Mono.Addins: Localizer. Mit diesen kann man automatisch Strings aus Satellite-Assemblies oder anderen Quellen (z.B. l18n-Verfahren) laden. Perfekt für dieses Problem, da wir nur an die Oberflächentexte wollen, sonst aber nichts über die Plugins wissen.

addin-gui-xml

XML-Manifest zur Erweiterung eines Menüs; Titel ist in Sprachresource mit Schlüssel "count-word-string" gespeichert

Das einfachste wäre sich an den Scanvorgang von Mono.Addins ranzuhängen und dann die Resource Strings zu laden und selber abzuspeichern. Ich werde wenn ich etwas Zeit finde mal versuchen das ganze in Mono.Addins zu integrieren und dann öffentlich verfügbar machen. Die nahtlose Integration hätte den Vorteil, dass die Texte einfach in den sowieso schon vorhandenen Binärdateien aufgenommen werden können. Das würde die Anzahl der zu lesenen Dateien erheblich verringern.