Showing posts with label Resources. Show all posts
Showing posts with label Resources. Show all posts

Wednesday, 4 November 2009

ListTemplate Name attribute doesn't resolve $Resources

Further to my previous 0x81070215 post, I've since discovered this error code also crops up when specifying an invalid ListTemplate value for the Name attribute.

In my case I was attempting to supply the value from a .resx file something like Name="$Resources:List_Name;". While resources are successfully resolved on other ListTemplate and List attributes, this doesn't seem to be the case with Name and its value must be specified explicitly; incidentally, the Name must match the name of the folder containing the list definition. The OOB 12-hive examples I've cross-checked also specify a literal value.

While this error code was presented in the UI, the SharePoint log files revealed how SharePoint was parsing the attribute value:

Cannot open "schema.xml": no such file or folder.
Failed to retrieve the list schema for feature {1B3FC94C-2FC6-4528-B968-4E91843C2005}, list template 13133; expected to find it at: "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\Template\Features\TeMCorporateBannerListFeature\$Resources:List_Name;".
Unknown SPRequest error occurred. More information: 0x81070215

No biggie since DisplayName does handle resources for localisation however in this case I'm attempting to use resource files for consistency and to simplify maintenance across my list definition.

For reference, 0x81070201 seems to mean about the same thing (a similar error message I received after reverting the Name value to a literal with a typo).

Sunday, 22 February 2009

Localizing MOSS Webparts Alternative

In the past I've blogged about one way to localize your web parts using really cool and surprisingly simple ASP.NET 2.0 localization techniques. 

Today I stumbled across an old but extremely thorough post on the same subject by Mikhail Dikov; Mikhail's approach deploys .resx files directly to the file system instead of satellite assemblies and lets the platform handle the bulk of the work involved--until you've stepped in to create a custom job definition. 

While my solution isn't necessarily the correct SharePoint way of handling resources I still think it's beautiful in its simplicity and will be familiar to any ASP.NET developer crossing the line into WSS/MOSS. The application of my suggested approach will depend somewhat on your solution structure but I'll always believe less code to build and maintain is a good thing.


Custom-Built Microsoft Office SharePoint Server 2007 Branded Sites and Webpart Development - info@mediawole.com

Sunday, 2 March 2008

Content editor web part dislikes neutral cultures

With the CurrentUICulture property of the current thread set to a neutral culture like 'de', attempting edit the content in a MOSS Content Editor Web Part results in the following exception:

Server Error in '/' Application.

Culture 'de' is a neutral culture. It cannot be used in formatting and parsing and therefore cannot be set as the thread's current culture.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NotSupportedException: Culture 'de' is a neutral culture. It cannot be used in formatting and parsing and therefore cannot be set as the thr...

Very boring. On a hunch, I think you can probably get around this by using a specific culture but that may not be possible in very case. This was only an issue while in edit mode, for us, so I got around it by simply setting the CurrenUICulture as en-US whenever a page is in edit mode. Kind of a cheat but our content editors are all English so it doesn't matter.

Wednesday, 6 February 2008

Japanese Site Launched

The Japanese version of my favourite tourism web site www.westernaustralia.com launched on Monday. This is the second foreign language site rolled out by the team with no less than five more sites to come (simplified Chinese, traditional Chinese, Korean, Italian, and French). The German site launched before Christmas.

I was tasked with localising the existing site using the .NET resource system last year and rolling out new language sites are now a breeze from a technical standpoint. A preliminary posting on how this was accomplished with SharePoint is soon to be followed by a follow up piece examining another huge set of issues... stay tuned (or check out my posts tagged "Resources")!

Wednesday, 12 September 2007

German site ist wunderbar!

The German version of the www.westernaustralia.com site will launch officially tomorrow via Minister Sheila McHale but it's live now: http://www.westernaustralia.com/de
This site is just a little bit of my handiwork here at Tourism Western Australia...

Tuesday, 11 September 2007

International Resource Identifier Support in .NET 3.5

The September 2007 issue MSDN Magazine contains an interesting article about changes to the System.Net namespace. Among the discussion about sockets and other low-level changes, the authors discuss support for International Resource Identifiers (IRIs) and their benefits over URIs.

http://msdn.microsoft.com/msdnmag/issues/07/09/Networking/default.aspx

I didn't realise it was possible to use non-ASCII characters in a domain name and while many DNS servers don't support non-ASCII domain names, Punicode provides a mechanism to work around this limitation.

So in essence, you can now take a domain name containing Unicode characters (like this: http://微軟香.com) and work with it directly using the URI class. This is certainly a great thing for international visitors to our web sites but as I only read English and French, I was really clinging to the English URLs as the last remaining way to identify our pages in SharePoint! Ah well, modern times, 'tis a global world...

Tuesday, 28 August 2007

Generating Public, Strongly-Typed Resource Classes with Visual Studio

Visual Studio 2005 does a great job of managing your .resx files and automatically generates strongly-typed classes exposing the contents of those files. If you create a new resource file and add it to your project you'll notice the Custom Tool property has a value of ResXFileCodeGenerator to suppor this behaviour.

This is generally all well and good but there is a catch: ResXFileCodeGenerator generates classes with members marked internal; in other words, you won't be able to access your resources using the generated class if you're working in another project (i.e. another assembly).

The resgen.exe tool does all the hard work behind the scenes and does have a flag called PublicClass that will override this behaviour--set this flag and your classes will be generated with public visibility. Unfortunately you can't run this tool automatically until compile-time, which means your resources won't be as conveniently accessible as they are by default; you'll also have to write a post-build script or use another method to do all the hard work moving your generated files around.

Luckily Visual Studio 2008 solves this problem by allowing you to set the Custom Tool property to PublicResXFileCodeGenerator. As the name suggests, the generated methods come out the other end marked as public and this all happens from within Visual Studio.

If you really can't wait for Visual Studio 2008 (and it's not far away), you may want to look into a handy little extension called ResXFileCodeGeneratorEx. In addition to allowing you to create publically-accessible, strongly-typed classes for your resources, it also helps out if you're dealing with format strings in your resource files. The only downsides I can think of are the fact that Visual Studio 2008 will make this tool less necessary (apart from the format bit) and that every developer will need to install it on their machine. No biggie but the sort of thing that can cause headaches for new developers joining your team.

Thursday, 9 August 2007

Creating a Custom CultureInfo

The System.Globalization.CultureInfo class comes with a number of pre-defined cultures but thankfully Microsoft recognises it hasn't supplied all culture/language combinations (real or imagined) and will allow you to build your own. One way you can do this is using the System.Globalization.CultureAndRegionBuilder class.

We ran into trouble while attempting to localise the westernaustralia.com English-language sites targeted at specific regions (we've got a "global" EN site, a "domestic" AU site, and UK, NZ, and SG variants). .NET 2.0 (and Windows XP/Server 2003) define CultureInfos and locales for all of our language/region combinations except for Singapore; the closest in-built option we could find was zh-SG (which is Chinese/Singapore). Although we could have cheated and used zh-SG, we're also running a number of foreign-language variations of the site; to be explicit, avoid confusion, future-proof this aspect of the site, and--most importantly--to make use of .NET's resource fallback mechanism (from en-SG to en), we decided to define a custom CultureInfo.

While creating a new CultureInfo isn't a difficult task, it's not as easy as supplying an "en-SG" string to the CultureInfo constructor or deriving a new class from the CultureInfo class (you can derive a new CultureInfo from an existing CultureInfo, however).

CultureInfo ci = new CultureInfo ("en-SG"); // This will fail at runtime
internal class MyCultureInfo : CultureInfo // There's an easier way...

MSDN provides a succinct article on building a custom CultureInfo class and trust me, it's really quite easy. The article fails to mention that you need to add a reference to the sysglobl assembly to gain access to the CultureAndRegionBuilder class so as long as you remember that step you should be fine. The sample provided also prefixes the new CultureInfo with "x-" and I think this is a great idea: doing so should avoid any conflict when you move to the next version of the .NET Framework or a new platform. Vista and, presumably, Server 2008, include the en-SG locale so naming our new CultureInfo "x-en-SG" means we can anticipate a smooth transition if the existing wa.com code is ever moved to Windows Server 2008.

You don't need to create and register a new CultureInfo every time your application runs (and you probably don't want to since the CultureInfo is written to the filesystem when its registered and invoking Register () again will fail) so we've built the create/register code into our deployment script. We simply try to unregister the existing CultureInfo, create x-en-SG from scratch based on the en-US CultureInfo and the SG RegionInfo, and register the new CultureInfo. Our English-Singapore .resx files reflect the new CultureInfo and are named as though x-en-SG were an in-built CultureInfo: *.x-en-SG.resx.

Broken .resx files in Visual Studio 2005/.NET 2.0

Working with resources is so much easier in .NET 2.0 but things do occasionally go awry. Most of the time the problem is really easy to fix.

As discussed in another post, Visual Studio 2005 does a lot of work behind the scenes to surface your resources as strongly-typed objects; if you're not careful with your .resx files, however, you might end up in a situation where your .resx files aren't being compiled for you. As a result, you lose Intellisense for your resources, the ResourceManager may end falling back to your default resource file when it shouldn't, or your default resource file might not load at all. Copying and renaming Visual Source Safe-controlled .resx files is one little culprit that occasionally brings everything to a halt.

It's important to remember Visual Studio doesn't just "do" things for you--it must be told what to do and frequently relies on stand-alone tools included with .NET or sitting outside of the VS shell. A good example of this can be seen by inspecting the property sheet (in Visual Studio) for one of your .resx files.

When you create a new .resx file Visual Studio does all the right things by setting the Build Action to "Embedded Resource" and setting the Copy to Output Directory as "Do not copy". Just as importantly, Visual Studio also sets the Custom Tool property as "ResXFileCodeGenerator" and this particular setting can occasionally get stripped away when you're renaming or moving resource files. If in doubt and your resources are not being made available to your application, check this property; if it's not set, set it to "ResXFileCodeGenerator".

If the Custom Tool property on your .resx files is set correctly, Visual Studio will help you out by running the specified tool for you every so often to ensure your resources are available programmatically (this can actually be a pain in the neck sometimes so I recommend using a tool like Resourcer to edit your resource files...). If this isn't happening, you can simply right-click on your .resx file in Visual Studio and select Run Custom Tool.

Thursday, 31 May 2007

Localizing Web Parts, Custom Controls, and Class Libraries

If you've dealt with the ResourceManager in .NET 1.x--or never touched resources altogether, moving to .NET 2.0 simplifies the process to the point where it all seems too simple. Simplicity is generally a good thing, of course, and once you understand localization in .NET 2.0 you'll probably agree there's no reason why pretty much every application shouldn't be localized from the beginning (I'm in the process of localizing and existing MOSS 2007 site, hence the sentiment just expressed).

Localization is a good thing because it:

  • Allows you to refactor hard-coded strings for labels, menus, and so on out of your code and into .resx files;
  • Enables your application to display a user interface relevant to users of different languages.

Localization won't translate your interface for you--that still has to be done manually.

Visual Studio 2005 now takes care of all the hard work needed to develop a fully localized app in no time at all. Firstly, it provides a convenient designer for creating resource files through which you can add strings, images, icons, and text/script files. Secondly, any .resx file added to your project are automatically compiled to a .resources file and linked to your main assembly. Finally, a resource file wrapper class is generated automatically, granting you strongly-typed access to the contents of your resource files. What more could you ask for?!?

Working with resources is really straight forward if you're writing user controls or web pages (apparently WinForms are just as straightforward but I don't know much about WinForms) because both the UserControl class and Page class both derive from System.Web.UI.TemplateControl. TemplateControl exposes the protected GetGlobalResourceObject ( ) and GetLocalResourceObject ( ) methods. When a user control or web page has a resource file available to it, you also get strongly-typed access to the file like this:

Resources.MyResourceFile.MyKey;

(where 'Resources' is some weird namespace that only comes to life when you've got .resx files in your project)

On the other hand, if you're writing web parts, custom controls, or class libraries, you're probably not deriving from TemplateControl (especially in the web part case where you class inherits WebPart). You can still add resource files to you project and use a ResourceManager to pull out the stuff you need but there's an easier way.

  1. Create a new class library project; name it MyCustomWebParts or whatever you like. The name you give your project will also become the name of the assembly and be used as the default namespace for any classes you add and any classes added by Visual Studio (this last bit is really important).
  2. Create a new resource file (Add new item...) named strings.resx in the root of the project (you can also place your resource files in an arbitrarily-named folder but doing so will change how the file is accessed programmatically). Add a new string with a name of labelText and a value of Hello World.
  3. Create a new class that derives from
    System.Web.UI.WebControls.WebParts.WebPart; name it MyWebPart or whatever you like.
  4. Add a CreateChildControls ( ) method to your class and within the method body, instatiate a new Label object and add it too the Controls collection.
  5. Immediately after the Label is instantiated, set its Text property from the resource file. You do this by referencing [the default namespace for the assembly].[the name of the resource file with no extension].[the resource key name].
  6. Add or update a globalization element to your web.config file and set both the culture and uiCulture attributes to auto:
    <globalization uiculture="auto" culture="auto" />
    This works on a regular ASP.NET site but MOSS doesn't automatically assign the browser's current language to the System.Threading.Thread.CurrentThread.CurrentCulture and CurrentUICulture properties so you may wish to do this yourself and forego the above web.config setting.
  7. Build the project and register the web part in a .aspx page--and that's it!

namespace MyCustomWebParts
{
public class MyWebPart : WebPart
{
protected override void CreateChildControls ()
{
Controls.Clear ();
Label label = new Label ();
label.Text = MyCustomWebParts.strings.labelText;
Controls.Add (label);
}
}
}

As mentioned, adding a .resx file to your project prompts Visual Studio to generate a strongly-typed wrapper class for accessing the file; this class is created in the project's default namespace. If your web part or control is the same namespace you don't need to qualify your resource file within the MyCustomWebParts namespace--you can access it directly: strings.labelText. If you've got multiple namespaces in your project (or not) it's probably a good practice to always qualify the resource class name.

If you do drop your .resx files into a folder you'll need to modify the way you reference them:

MyCustomWebParts.MyFolder.strings.labelText;

Either way, IntelliSense will help you out as you go if everything's in the right place.

To tweak performance, you can tell ASP.NET which resources are contained in the default assembly by adding an attribute to your class library's AssemblyInfo file:

[assembly: System.Resources.NeutralResourcesLanguageAttribute ("en")]

And the end result: localized web parts, custom controls, and class libraries. Cool.