Lokalizace je zábavná, když funguje, ale když ne, dokáže způsobit pořádný bolehlav. Zažil jsem jej sám ve formě záhadné výjimky MissingManifestResourceException a chci se s vámi podělit o to, kdy k ní dochází a také jako připomínku, abych si na řešení příště vzpomněl rychleji :-) .
Sdílení zdrojů - RESX
UWP aplikace přidal nový formát pro správu zdrojů (resources) - RESW. Prakticky jde o starý dobrý formát RESX, jen s jinou příponou. Pravdou však je, že vývojáři mobilních aplikací pro více platforem preferují sdílení řetězcových zdrojů a za tímto účelem využijí Portable Class Library. Takovéto sdílení zdrojů je poměrně snadné nastavit. Nejprve vytvoříte novou knihovnu PCL, přidáte soubor RESX, nastavíte generování jeho kódu na public a poté přidáte více lokalizovaných resource souborů se stejným názvem ale názevm cílové kultury na konci - např. LocalizedStrings.resx (neutrální), LocalizedStrings.de.resx (německý), LocalizedStrings.cs.resx (český). Za běhu je systémem zkontrolována CultureInfo.CurrentUICulture a podle ní se načtou lokalizované verze požadovaných zdrojů. Toto funguje téměř stoprocentně. Dokud nenarazíte na MissingManifestResourceException .
Dvě formy MissingManifestResourceException
V mém případě výjimka začala vyskakovat při jednoduchém přístupu k lokalizovanému řetězcovému zdroji:
System.Resources.MissingManifestResourceException: 'MissingManifestResource_ResWFileNotLoaded, ResourceLibraryApp.Resources.ResxInResources'
Všiml jsem si v popisu chyby reference na RESW soubory a tak jsem zkusil přidat do projektu UWP aplikace RESW zdrojové soubory pro všechny podporované jazyky Upozorňuji, že toto je nutné v každém případě! Pokud RESW soubory v projektu nezahrnete, při vytváření balíku aplikace nebudou lokalizované zdroje rozpoznány a aplikace pak nebude v těch jazycích, které jste přeložili k dispozici. Důvodem je element <Resource Language="x-generate"/> , kerý najdete v souboru Package.appxmanifest. Po rebuildu a znovuspuštění aplikace výjimka zůstala. Po troše dalšího boje s třídou ResourceManager se popis výjimky změnil na ještě "užitečnější":
An exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.ni.dll but was not handled in user code Additional information: Unable to load resources for resource file "ResourceLibraryApp.Resources.LocalizedStrings" in package "9b09bfd8-2a50-4a1f-baae-e90b760e48c7".
Byl jsem úplně ztracen, dokud jsem nezkusil něco na první pohled neintuitivního - změnil jsem jméno PCL assembly.
Nezakončujte jméno vaší PCL suffixem ".Resources"!
Nadpis říká vše. Pokud zakončíte název vaší PCL suffixem .Resources, vaše aplikace spadne s výjimkou MissingManifestResourceException . Vše ostatní bude fungovat skvěle a kompilace se bude také tvářit nevinně. Také, aby bylo vše trochu více "Sherlockovské", k výjimce dojde pouze při pokusu o přístup na přeložené zdroje, tedy ne pokud budete mít nastavenou jazyk odpovídající neutrální kultuře aplikace. Když jsem přejmenoval assembly tak aby její název končil .Localization (nebo ostatně čímkoliv jiným), aplikace již nepadá a zdroje se načítají správně.
Zdrojový kód
Ukázkový projekt s PCL s .Resources i .Localization v názvu je dostupný na mém GitHubu. Můžete si ověřit, že při pokusu o načtení zdroje z prvního PCL dojde k výjimce.
Shrnutí
Tento RESX + PCL bug není snadné vystopovat, protože způsobuje chybu jen za běhu a to ještě pouze při správném nastavení kultury. Bohužel je poměrně snadné jej způsobit, protože nastavit PCL s resources pojmenování se suffixem ".Resources " je celkem logické :-) .