In the results of the scan from the previous lesson, we see
that Globalyzer detected three categories of internationalization
issues within the project's C# source. As before, it detected
embedded strings. But this time it also uncovered some
locale-sensitive methods and general patterns.
It is up to the developer to ensure that
those methods and constructors are refactored as needed to work in
the desired culture. We will start our fixing process by examining
these constructors and methods.
-
Select Locale-Sensitive Methods in the Results
dropdown in the Scan Results pane. This will display the
list of culture-sensitive constructors discovered during the scan.
Order the results alphabetically by clicking on the column header
"Issue".
-
Double click anywhere in the top row, which should be a
CreateSpecificCulture
declaration. You will be taken to a block of code in the source
viewer that handles the instantiation of a
CreateSpecificCulture
object within the application. This block of code contains
several of the Locale-Sensitive Method and General Pattern detections
within your Scan Results.
CultureInfo locale =
CultureInfo.CreateSpecificCulture(Context.Request.UserLanguages[0]);
Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale;
The purpose of scanning for
CreateSpecificCulture
is to ensure that it is dynamically instantiated according to the
culture of the current user. In addition, the correct
instantiation of
CreateSpecificCulture
needs to be set in
Thread.CurrentThread
so that it is accessible to all of the culture-sensitive methods
throughout your code.
It would be a mistake to instantiate
CreateSpecificCulture
in various places throughout the code passing in different
culture arguments. This would result in inconsistent formatting
of DateTime values, strings, currencies, etc. in your User
Interface.
CurrentCulture
and
CurrentUICulture
are the properties of
Thread.CurrentThread
that must be set, as mentioned earlier, to ensure that the
culture-sensitive methods in your application perform according
to the correct
CreateSpecificCulture
.
To better understand the purpose of these classes, let's look
at Globalyzer's just-in-time help system.
-
Look in the All Predicted Scan Summary view, located at the
bottom left of the Workbench. Scroll down so you can see
Locale-Sensitive Methods and General Patterns summaries.
-
Click on the top link for
CreateSpecificCulture
.
You will see a page that provides helpful information about
this class and including its purpose within C# software
internationalization and how to use it properly in your code.
-
After reading about
CreateSpecificCulture
you should see that it is used properly within our sample code.
It is set according to the locale of the end user through the
Context
object in this case. Next, the sample code sets the newly
instantiated
CreateSpecificCulture
as the
CurrentCulture
and
CurrentUICulture
in the
Thread.CurrentThread
object. This will provide access to the correct set of culture
rules for all other culture-sensitive functions in our
application.
-
Now look at the General Patterns section of the
All Predicted Scan Summary.
If you click on the link for
CurrentCulture
and then for
CurrentUICulture
in the All Predicted Scan Summary, you will see that
CurrentCulture
is responsible for the proper functioning of culture-specific
methods throughout the code and
CurrentUICulture
is responsible for the proper retrieval of translated-strings at
runtime. Both are crucial to the proper functioning of an
internationalized C# application.
-
Similarly, the
Write
method also relies on the culture being set properly, and so is
not an issue here.
-
Return to the Results Table, switch to General Patterns, and double click on the first
row where the Reason field contains the text
\bDateTime\.Now\b
.
Note: The "\b" is a regular expression code for word boundary, while the
"\." causes the period to be used as such, instead of as a wildcard. This
expression is therefore looking for "DateTime.Now".
If you look at the information Globalyzer provides under the Predicted Active
Scan Summary for
\bDateTime\.Now\b
,
you'll see that developers are cautioned to make sure that the
CultureInfo
value is set properly in the current thread. We already know that
the culture has been set properly. If you look specifically in
the help provided via the
DateTime.Now
link, you'll see a reference to the very block of code being used
in this example.
As pointed out in the Globalyzer help links for
DateTime.Now
,
the code in our project demonstrates the wrong way to
use the
DateTime.Now
property. This code example pieces together a display date in a
fashion that is specific to U.S. English. It will be incorrect
for even other English-speaking locales, such as the United
Kingdom.
string theMonth =
DateTime.Now.Month.ToString();
string theDay = DateTime.Now.Day.ToString();
string theYear = DateTime.Now.Year.ToString();
string theTime = DateTime.Now.Hour.ToString() + ":" +
DateTime.Now.Minute.ToString() + ":" +
DateTime.Now.Second.ToString();
string displayTime = "Timestamp: " +
theTime + " " + theMonth + "/" +
theDay + "/" + theYear;
return displayTime;
The correct way to format a long date string such as the one
attempted above, would be to call the
ToString
method on
DateTime.Now
,
passing in the
U
format string, which specifies a long date value. This way, if
the correct culture has been set in the current thread, the date
formatting rules of the user's culture will be used to generate
the correct long-date string. Even better, the above block of
code is rewritten in two lines instead of eight. Replace the
existing
makeTimeStamp
method with the following code and press Ctrl-S to save
your changes:
public string makeTimeStamp()
{
string timestamp = "Timestamp: " +
DateTime.Now.ToString("U");
return timestamp;
}
-
Globalyzer will automatically rescan the modified
source file. You should now see only the Locale-Sensitive Methods
and General Patterns that you have already reviewed along with a
single mention to the new line containing "DateTime.Now".
-
Let's tell Globalyzer to ignore these calls in subsequent scans
so we don't waste time checking them again later. There are two
ways to do this: inserting Ignore This Line Comment text
into the source files, or setting the Status of the rows
to something other than Active or ToDo. The first
approach is more permanent: View the Locale-Sensitive Method
results and Double-click on the Issue CreateSpecificCulture
from the source file "AControl.cs". This will open the source
file in the Source Files view.
-
To preserve the integrity of the following block of code, move your cursor up
to the line "CultureInfo locale = "
. Select
Fix Code => Start Ignore Comment from the
Menu Bar. A comment is placed in a new line above the row with the
CultureInfo
reference. Looking at the source code, you can see there are several
of the General pattern issues present in the next few subsequent lines.
Click at the start of the code line following the
CurrentUICulture
reference and select Fix Code => End Ignore Comment.
Globalyzer will automatically rescan the file, ignoring any issues
found between the Start Ignore
and End Ignore
comments. The following times Globalyzer scans this file it
will continue to ignore these issues.
-
The second, though less permanent way, to skip lines in
subsequent scans is to set the Status of the rows to something
other than Active or ToDo. As long as you don't
change the Rule Set, delete your scan results
or significantly alter the source file,
Globalyzer will maintain the Status setting. The rows will therefore
not appear in any Active Scan Results. Select the rest of the
rows in the Scan Results for either General Patterns or
Locale Sensitive Methods (i.e. click on first row,
then press ctrl-A.). Right-click them and select Ignore.
Repeat so that the step is performed for both General Patterns and Locale Sensitive Methods. The Active Scan Results
should now be empty.
In the next lesson we will address the embedded C# strings.