N
N4709L
Would you please review the methods that we use to Globalize our software
applications? I would like to obtain your critique of the practices that we
use and have outlined below.
In our Module Main we declare a Public (global) variable for a
ResoureManager that is used throughout the application for strings that
appear within our code.
Public rm As ResourceManager
Then, within our Sub Main, our code begins with the following:
Function Main(ByVal CmdArgs() As String) As Integer
' Use the current language and culture that has been selected using
' Regional and Language Options in Control Panel. Otherwise, Resource
Manager
' will use GetUserDefaultUILanguage, which is the system's installation
language
' and culture.
Try
Thread.CurrentThread.CurrentUICulture =
Thread.CurrentThread.CurrentCulture
rm = New ResourceManager("ProblemReportUtility.ProblemReportUtility", _
System.Reflection.Assembly.GetExecutingAssembly())
Catch ex As Exception
' Do not translate the error message below, because ResourceManager
failed.
MsgBox("Unable to set the current user interface culture and create a
new resource manager to handle language strings." _
& vbCrLf & vbCrLf _
& ex.Message, MsgBoxStyle.Critical Or MsgBoxStyle.OKOnly, _
"Problem Report and Enhancement Utility")
Return Err.Number
End Try
Now we are ready whenever a string might be required. When a string must be
used within the code we do three important things:
1.. We provide a comment line that documents the actual string that is
returned from the Resource Manager.
2.. We use rm.GetString to fetch the actual translated string.
3.. We use String.Format and place holders like {0} and {1} for tokens
that must be inserted into the string.
The actual code looks like this:
' "This report was originally addressed to {0} on {1}."
strMessage = String.Format(rm.GetString("strReportAddressedTo"), _
g_strSendReportTo, FormatDateTime(Now, DateFormat.LongDate))
We include the string as a comment, to simplify the process of locating the
appropriate section of the code when we must trouble-shoot the application
and we know what message was displayed. Of course, we are careful to use
tokens like {0} very sparingly because we are aware of the translation
difficulties that it presents. Nonetheless, where required, it is much
better than simply using string concatenation to build up a string!
Are there any comments or suggestions that you might have on the above?
We have a few specific questions and concerns about the above procedures.
Our strings are maintained in a .resx file. Unfortunately, the software
developer must provide a unique name for each string and there appears that
the VisualStudio development environment does not ensure that string names
within the .resx file are unique. When two or more strings have the same
Name, the ResourceManager may return either. This is very difficult to
debug.
Secondly, if the string Name that is passed to the .GetString method does
not appear in the .resx file, then an empty string is returned. Frequently,
this results in a Message Box that is completely empty! In a few "worst
case" situations, the empty string may actually cause the application to
fail in unexpected ways. This is also very difficult to troubleshoot. We
would have preferred that GetString raise an error event or return its
argument if no matching Name were found. Or it could return "No string found
with the Name: " & strArg. Is there a better approach to this? Our
alternative is to provide our own .GetString-like method that handles this
type of failure in a more debug-friendly manner. Do others not run into this
type of problem? Is there a better approach?
Finally, it is a chore to write code following the above style whenever an
in-code string must be utilized. Oh how we wish that VisualStudio could have
provided a facility for handling strings that must be translated! For
example, imagine how much easier our life would be if VisualStudio
recognized special strings that were marked using, for example, tilde before
the opening quote character. Such a string would be automatically placed
into the .resx file and a guaranteed unique string Name would be inserted
into the application at that point. There would be no need for a comment,
and when the string had to be corrected, there would be only one place in
the source that would require updating. And most importantly, there would
never be a mismatch between the string Name in the code and the Name in the
..resx file, and never a duplicate. Here's what the code would look like if
this concept were implemented by VisualStudio.NET:
strMessage = String.Format(rm.GetString(~"This report was originally
addressed to {0} on {1}."), _
g_strSendReportTo, FormatDateTime(Now, DateFormat.LongDate))
Are you aware of any tools that would accomplish this as cleanly and easily
as the above example shows?
Thank you very much!
applications? I would like to obtain your critique of the practices that we
use and have outlined below.
In our Module Main we declare a Public (global) variable for a
ResoureManager that is used throughout the application for strings that
appear within our code.
Public rm As ResourceManager
Then, within our Sub Main, our code begins with the following:
Function Main(ByVal CmdArgs() As String) As Integer
' Use the current language and culture that has been selected using
' Regional and Language Options in Control Panel. Otherwise, Resource
Manager
' will use GetUserDefaultUILanguage, which is the system's installation
language
' and culture.
Try
Thread.CurrentThread.CurrentUICulture =
Thread.CurrentThread.CurrentCulture
rm = New ResourceManager("ProblemReportUtility.ProblemReportUtility", _
System.Reflection.Assembly.GetExecutingAssembly())
Catch ex As Exception
' Do not translate the error message below, because ResourceManager
failed.
MsgBox("Unable to set the current user interface culture and create a
new resource manager to handle language strings." _
& vbCrLf & vbCrLf _
& ex.Message, MsgBoxStyle.Critical Or MsgBoxStyle.OKOnly, _
"Problem Report and Enhancement Utility")
Return Err.Number
End Try
Now we are ready whenever a string might be required. When a string must be
used within the code we do three important things:
1.. We provide a comment line that documents the actual string that is
returned from the Resource Manager.
2.. We use rm.GetString to fetch the actual translated string.
3.. We use String.Format and place holders like {0} and {1} for tokens
that must be inserted into the string.
The actual code looks like this:
' "This report was originally addressed to {0} on {1}."
strMessage = String.Format(rm.GetString("strReportAddressedTo"), _
g_strSendReportTo, FormatDateTime(Now, DateFormat.LongDate))
We include the string as a comment, to simplify the process of locating the
appropriate section of the code when we must trouble-shoot the application
and we know what message was displayed. Of course, we are careful to use
tokens like {0} very sparingly because we are aware of the translation
difficulties that it presents. Nonetheless, where required, it is much
better than simply using string concatenation to build up a string!
Are there any comments or suggestions that you might have on the above?
We have a few specific questions and concerns about the above procedures.
Our strings are maintained in a .resx file. Unfortunately, the software
developer must provide a unique name for each string and there appears that
the VisualStudio development environment does not ensure that string names
within the .resx file are unique. When two or more strings have the same
Name, the ResourceManager may return either. This is very difficult to
debug.
Secondly, if the string Name that is passed to the .GetString method does
not appear in the .resx file, then an empty string is returned. Frequently,
this results in a Message Box that is completely empty! In a few "worst
case" situations, the empty string may actually cause the application to
fail in unexpected ways. This is also very difficult to troubleshoot. We
would have preferred that GetString raise an error event or return its
argument if no matching Name were found. Or it could return "No string found
with the Name: " & strArg. Is there a better approach to this? Our
alternative is to provide our own .GetString-like method that handles this
type of failure in a more debug-friendly manner. Do others not run into this
type of problem? Is there a better approach?
Finally, it is a chore to write code following the above style whenever an
in-code string must be utilized. Oh how we wish that VisualStudio could have
provided a facility for handling strings that must be translated! For
example, imagine how much easier our life would be if VisualStudio
recognized special strings that were marked using, for example, tilde before
the opening quote character. Such a string would be automatically placed
into the .resx file and a guaranteed unique string Name would be inserted
into the application at that point. There would be no need for a comment,
and when the string had to be corrected, there would be only one place in
the source that would require updating. And most importantly, there would
never be a mismatch between the string Name in the code and the Name in the
..resx file, and never a duplicate. Here's what the code would look like if
this concept were implemented by VisualStudio.NET:
strMessage = String.Format(rm.GetString(~"This report was originally
addressed to {0} on {1}."), _
g_strSendReportTo, FormatDateTime(Now, DateFormat.LongDate))
Are you aware of any tools that would accomplish this as cleanly and easily
as the above example shows?
Thank you very much!