Die Tage musste ich in eine mir weitgehend unbekannte SharePoint Solution eine neue Übersetzung einpflegen. Leider war die Struktur der ResX Datei beim Übersetzer zerstört worden und zurück kam eine schwach strukturierte Mischung verschiedener Resourcefiles in Form eines Excel Files bei der einzelne Übersetzung gleich komplett fehlten. Der ZetaResource Editor stürzte angeblich bei den Resourcen ab, was ich dummerweise einfach geglaubt habe. Da ich ziemlich viel Zeit verschwendet habe, bis ich alles mit Hilfe eines Kollegen (Danke Mario) gerade gebogen hatte, möchte ich hier die Lehren daraus zur Diskussion stellen.

Resourcen in SharePoint

SharePoint benötigt seine Resourcen je nach Kontext an verschiedenen Stellen. Features und Backend Solution können sich eine Resourcedatei teilen, sofern diese im Hive in …/14/Resources liegt.Alternativ kann man die Featureresourcen wie die Namen von SiteColumns, ListDefinitions usw. auch auf Featurescope lassen. SharePoint nutzt hier die $Resources: Syntax in den elements.xml usw. Nutzt man eine gemeinsame/globale Resource muss man darauf achten den Namen der Resourcedatei mit anzugeben, also z.B. $Resources:Wulf.Absence,Start_SiteColumnDisplayName.

Backendlogik kann die globale Resourdatei entweder über einen ResourceManager oder über die Codegenerierung der Resource nutzen. Ich persönlich ziehe den Zugriff über die generierte Klasse vor, da sie Typsicherheit und Intellisense bietet. In diesem Fall wurde aber eine Zugriffsklasse mit hunderten von const string Feldern benutzt, was die gleichen Vorteile bietet, aber manuell gepflegt werden muss. 

Application Pages, visuelle WebParts oder Usercontrols brauchen eine Resourcendatei in AppResources und nutzen ebenfalls die $Resources: Syntax. Diesmal muss man bei der Referenzierung auf die Endung .AppResource achten also z.B.  Text=”<%$Resources:Wulf.Absence.AppResource,Page_Title%>”

Aufbereitung

Um erst einmal eine laufende Übersetzung zu bekommen kopierte ich das ganze Sammelsurium aus Excel in die neue Sprachresource. Dann habe ich sortiert und Feld für Feld verglichen.

Grober Patzer! Der viel einfachere und bessere Weg ist es, die Namensspalten von Original und Übersetzung nach dem Sortieren in je eine Textdatei zu kopieren. Danach kann man sie mit der Vergleichsfunktion von VisualStudio 12, CodeComparer, WinMerge oder andere Vergleicher vernünftig auf Unterschiede kontrollieren. Leider habe ich eine Weile gebraucht, bis ich mich an den einfachen Weg erinnert habe.

In diesem Schritt sind dann erst mal alle Übersetzungen rausgeflogen, die nicht dazugehörten und die fehlenden aus dem Original kopiert. Zum Glück war ich in der Lage in diesem Falle die fehlenden Übersetzungen selber zu machen, normalerweise würde man hier eine Liste für den Übersetzer machen (Verbot sich hier aus Zeitgründen)

Kontrolle im Code

Um eine Liste aller tatsächlich verwendeten Resource Keys zu bekommen, muss man sie aus dem Code  bzw. den XML Dateien extrahieren. Das geht prima mit einer Suche in Visual Studio. Um sicher zu stellen, das auch alle Zugriffe  auf die globale Resource die Datei mit angeben nutze ich Suchen und Ersetzen mit Regular Expressions:

Suche \$Resources\:{:i};

Ersetze durch $Resources:Wulf.Absence,\1;

image

(wir erzeugen die XML Dateien normalerweise über die SharePoint Software Factory, die legt die Texte in den Featurespezifischen Resourcen ab. Nicht sicher, ob das eine Einstellungssache ist)

Alles was der $Resouce: Datei, Key Syntax folgt, findet man mit der Suche nach

\$Resources\:{:i},{:i};

Das Suchergebnis packt man wieder in eine Textdatei, befreit es von Rauschen über Suchen und Ersetzen mit

Suchen: ^{.*}\$Resources\:{:i},{:i};{.*}$

Ersetzen: \2, \1

Dann hat man Einträge der Form Key, Datei. Alternativ kann man auch mit \2 ersetzen, dann hat man wieder nur die Keys und kann die via WinMerge mit den Keys in der Resource abgleichen.

Fehlersuche

Natürlich lief die Solution nach dem Deployment auf Fehler wegen fehlender Resourcen (Ich hatte ja von Hand verglichen Trauriges Smiley) Hier ein paar Tips, die man dann als erstes kontrollieren sollte.

  1. AppPool recyclen oder IISReset /noforce
  2. Liegen die aktuellen Resourcen im Hive? Die globale Resource muss im Hive in Program Files\Common files\Microsoft Shared\Web Server Extensions\14\Resources liegen. Die AppResource unterhalb von c:\Inetpub\wwwroot\wss\<Ordner der Webapplication>\AppResources
  3. Liegen Resoucen tatsächlich im Ordner der richtigen Webapplication? Unsere Lokalisierungen liegen als eigene Solution vor, damit man bei neuen Übersetzungen die Hauptapplication nicht neu deployen muss. Deshalb darauf achten, das Resource und Applikation auch auf die gleiche Webapplikation deployed werden.
  4. ULS Log via ULSViewer beim Aufruf mitlaufen lassen. Fehlende Resourcen werden als MissingResourceManifestException gemeldet. Wenn man weiß wo man suchen muss,  mit WinMerge die Keys vergleichen
  5. Debuggen für den Zugriff auf fehlende Resourcen vom Code. In diesem Fall ging jeder Zugriff über eine gemeinsame Methode. Die wurde von mir erweitert, das sie für jede fehlende Resource einen Eintrag ins Log machte und dann den Schlüssel zurückgab. Allein durch den Fallback auf den Schlüssel fielen eine ganze Reihe fehlender Felder sofort auf.
Advertisements

Gerade einen Fehler behoben der mich sehr viel Zeit gekostet hat. Beim Publishen eines Projektes erhielt ich den Fehler

The target "_CopyBinDeployableAssemblies" does not exist in the project.

Ursache war ein modifiziertes .target File das nicht durch das ServicePack 1 von Visual Studio upgedated wurde.

Zum Glück hatte ich eine Maschine, bei der die Installation von SP1 funktioniert hatte. Habe die Daten aller .target Files verglichen und fand dabei eine Datei deren Datum abwich. Das auch noch eine Kopie der Datei mit der Endung .target.org daneben lag, half mir dann auch noch auf die Sprünge Smiley