finally, the app’s done, let’s test in the beta server…Whoa! What the heck?????

After a few months of work the app is done, it runs smoothly with your local MSSQL, all is good, all is good, let’s move it to the beta server and run a few tests before handling to QA.

@#@%#@ what the heck???? My local sql server had gotten the data from the beta server, it should all work the same…it doesn’t. Lemme see the SPROCs’ code, lemme check it again, it should work! Let’s roll back, let’s commit again, it should work??? Wait, is the data what it’s supposed to be?

The relief of the nightmare, here’s the medicine, gal: Tools for comparing and sync databases, the SQL Bundle

And the always sweet file/folder comparing tool, here’s the super Araxis Merge

There you have! Yo data!

A Resource Manager Wrapper User Control, used on ASP.NET 1.1 for Globalization and Localization of apps

Here it is as promised, a user control that wraps the functionality of the ResourceManager class and save you lots of coding:

Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Resources
Imports System.Reflection
Imports System.Threading

Public Class SiteTextControl
Inherits System.Web.UI.UserControl

#Region ” Web Form Designer Generated Code “

‘This call is required by the Web Form Designer.
Private Sub InitializeComponent()

End Sub

‘NOTE: The following placeholder declaration is required by the Web Form Designer.
‘Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
‘CODEGEN: This method call is required by the Web Form Designer
‘Do not modify it using the code editor.
InitializeComponent()
End Sub

#End Region

Private _Name As String
Private lit As LiteralControl
Private res As ResourceManager
Private Shared _myCulture As String

Public Shared ReadOnly Property myCulture() As String
Get
Return Thread.CurrentThread.CurrentCulture.ToString
End Get

End Property
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal Value As String)
_Name = Value
End Set
End Property
Public WriteOnly Property Text() As String

Set(ByVal Value As String)
If Controls.Count > 0 Then
lit.Text = Value
End If

End Set
End Property

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
‘Put user code to initialize the page here
End Sub
Protected NotOverridable Overrides Sub CreateChildControls()
res = New ResourceManager(“machinelocator.Resource1”, [Assembly].GetExecutingAssembly())

Dim _Text As String = res.GetString(_Name, Thread.CurrentThread.CurrentCulture)
lit = New LiteralControl(_Text)
Controls.Add(lit)
lit.Dispose()
End Sub

Public Shared Function GetResourceContent(ByVal name As String) As String
Dim rm As ResourceManager = New ResourceManager(“machinelocator.Resource1”, [Assembly].GetExecutingAssembly())
Return rm.GetString(name)
End Function

End Class

On the Application events at the Global.asax your code should look like:
mports System.Web
Imports System.Web.SessionState
Imports System.Globalization
Imports System.Threading
Imports System.Resources
Imports System.Reflection
Imports System.Collections
Imports System.IO

Public Class Global

Inherits System.Web.HttpApplication
Private rm As ResourceManager

#Region ” Component Designer Generated Code “

Public Sub New()
MyBase.New()

‘This call is required by the Component Designer.
InitializeComponent()

‘Add any initialization after the InitializeComponent() call

End Sub

‘Required by the Component Designer
Private components As System.ComponentModel.IContainer

‘NOTE: The following procedure is required by the Component Designer
‘It can be modified using the Component Designer.
‘Do not modify it using the code editor.
Private Sub InitializeComponent()
components = New System.ComponentModel.Container
End Sub

#End Region

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires when the application is started

End Sub

Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires when the session is started
End Sub

Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires at the beginning of each request
Dim culturePref As String = Nothing
Dim cultureSet As Boolean = False

Try
If Request.Cookies.Count <> 0 Then
If Request.Cookies(“CulturePref”).Value <> Nothing Then
culturePref = Request.Cookies(“CulturePref”).Value
cultureSet = True

End If
End If
Catch ex As Exception
‘nothing
End Try

If cultureSet Then
Try
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culturePref)
Catch
‘Set the default culture
Thread.CurrentThread.CurrentCulture = New CultureInfo(“en-US”)
End Try
End If

Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture

End Sub

Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires upon attempting to authenticate the use
End Sub

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires when an error occurs
End Sub

Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires when the session ends
End Sub

Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
‘ Fires when the application ends
End Sub

End Class

And finally for using this control you can either put it on your HTML or use it on your CodeBehind like:

strDetail =SiteTextControl.GetResourceContent(“AproxResource”)

Special thanks to Mark Kucera the original creator of the control, the above code have very few modifications.

What I’ve been working on: Localization and Globalization with ASP.NET 1.1

Good resources for this topic can be found at dasBlonde’s blog
You might like to read this thread in particular: A hot discussion thread on Localization Architectures
If you’re racking your head off with a database design that suits your Globalization needs a good article for that is: database design examples
Last and no least the whole fallback process for locating resources files in satellite assemblies…
The most tricky thing about Localization in ASP.NET 1.1 is the assemblies naming convention, without it, the whole process of locating the proper resource file will fail and your application won’t speak all the languages that it’s supposed to. Notice that the naming convention for ASP.NET 1.1 differs a litle from the naming convention in Windows apps.
A good article for that is MSDN Locating Resources on Satellite Assemblies
Note: You don’t have to create the assemblies by hand using the AL.exe utility, VS 2003 will do it for you, you don’t even need to create a separate project with the .resX file so it will be compiled into a dll, no need, VS 2003 automatically converts your .resX files into dlls, each one will be a satellite assembly.
My directory structure looks like this:
c:\Inetpub\wwwroot\MyApp\bin
|_ \en\Resources.resx
|_ \fr\Resources.fr.resx
MyApp so far speaks only two languages, English, from the en-US culture and French from the fr-FR culture.

Stay tuned, I’ll publish a good user control wrapper to save you lots of code while using the ResourceManager class…

Cheers,
Lizet