Martin Devillers

WorkBlogAbout

Localizing Microsoft ReportViewer


Published on May 5th, 2012
How-to.NET

Microsoft ReportViewer Control can be localized by implementing the three IReportViewerMessages interfaces. However, the documentation of these interfaces lacks the original string values, which in turn makes it difficult to provide a proper translation. In this blog, a complete listing of these values and a Dutch implementation of IReportViewerMessages are presented.

Microsoft ReportViewer

Microsoft ReportViewer is a .NET control that can be used in both Windows and Web applications to render Microsoft Reports. Unlike the name suggests, the ReportViewer does a whole lot more than just view reports. The control is capable of loading (remote) RDL files, executing its contents and rendering the end result in various formats (including HTML, Excel, Word, PDF). Once rendered, the user can interact with the control in order to pass report parameters, navigate through multipage reports and more.

ReportViewer localized to the Dutch culture and language.

ReportViewer localized to the Dutch culture and language.

In my current project, we embedded ReportViewer inside an ASP MVC web application. As the audience of the application is Dutch, all content, including the ReportViewer, must be localized to the Dutch culture. Unfortunately, the language packs available for ReportViewer do not include Dutch, so I had to come up with an alternative solution.

The Good

Fortunately, Microsoft included a mechanism in the ReportViewer control which allows developers to override the default messages. This is achieved by implementing various interfaces in the parent application that utilizes ReportViewer and then passing these custom implementations to the ReportViewer control.

More specifically, these interfaces are IReportViewerMessages, IReportViewerMessages2 and IReportViewerMessages3, which reside in the Microsoft.Reporting.WebForms namespace (or WinForms if you’re using that). These interfaces are essentially just collections of read-only string properties with static hardcoded values.

The Bad

While Microsoft was graceful enough to document these interfaces, their documentation lacks the original values of these string properties. As a result, anyone who wants to create a meaningful translation of these properties must deduce the meaning from the property’s name and description, rather than the original value. This can be quite challenging when faced with cryptic names such as ParameterAreaButtonToolTip, NullValueText or ClientPrintControlLoadFailed.

The Ugly

While the lack of original values is a nuisance, it is in no means a showstopper. Poor translations can result in the user being faced with messages that seem out of place, but the application itself will remain functional. Or so I thought:

Input string was not in a correct format.

First attempt at localizing ReportViewer results in a FormatException

Most .NET developers will be familiar with this exception as it is the result of String.Format being invoked with a format string which does not match the expected objects to format. In other words, one or more of the strings which I am translating use the composite formatting feature. Without the original English values of these strings, one is left to guess which of these strings must contain formatting placeholders.

The Solution

To overcome this problem, I decompiled the ReportViewer assembly and extracted the original string values of these properties. Below is a version of my Dutch implementation of the IReportViewerMessages interfaces. Each property includes the original text in English, so you could use it as a template for your own translations.

DutchReportViewerMessages.cs
1using System;
2using System.Globalization;
3using Microsoft.Reporting.WebForms;
4
5namespace InfoSupport.SomeApplication
6{
7 public class DutchReportViewerMessages : IReportViewerMessages, IReportViewerMessages2, IReportViewerMessages3
8 {
9 #region IReportViewerMessages Members
10
11 // English value: Back to Parent Report
12 public string BackButtonToolTip
13 {
14 get { return "Terug naar het vorige rapport"; }
15 }
16
17 // English value: Change Credentials
18 public string ChangeCredentialsText
19 {
20 get { return "Wijzig Rechten"; }
21 }
22
23 // English value: Change Credentials
24 public string ChangeCredentialsToolTip
25 {
26 get { return "Wijzig Rechten"; }
27 }
28
29 // English value: Current Page
30 public string CurrentPageTextBoxToolTip
31 {
32 get { return "Huidige Pagina"; }
33 }
34
35 // English value: Document Map
36 public string DocumentMap
37 {
38 get { return "Document Map"; }
39 }
40
41 // English value: Show / Hide Document Map
42 public string DocumentMapButtonToolTip
43 {
44 get { return "Toon / Verberg Document Map"; }
45 }
46
47 // English value: Export
48 public string ExportButtonText
49 {
50 get { return "Exporteer"; }
51 }
52
53 // English value: Export
54 public string ExportButtonToolTip
55 {
56 get { return "Exporteer"; }
57 }
58
59 // English value: Export Formats
60 public string ExportFormatsToolTip
61 {
62 get { return "Exporteer Formaten"; }
63 }
64
65 // English value: False
66 public string FalseValueText
67 {
68 get { return "Onwaar"; }
69 }
70
71 // English value: Find
72 public string FindButtonText
73 {
74 get { return "Zoek"; }
75 }
76
77 // English value: Find
78 public string FindButtonToolTip
79 {
80 get { return "Zoek"; }
81 }
82
83 // English value: Next
84 public string FindNextButtonText
85 {
86 get { return "Volgende"; }
87 }
88
89 // English value: Find Next
90 public string FindNextButtonToolTip
91 {
92 get { return "Volgend Resultaat"; }
93 }
94
95 // English value: First Page
96 public string FirstPageButtonToolTip
97 {
98 get { return "Eerste Pagina"; }
99 }
100
101 // English value: Enter a valid page number
102 public string InvalidPageNumber
103 {
104 get { return "Voer een geldig paginanummer in"; }
105 }
106
107 // English value: Last Page
108 public string LastPageButtonToolTip
109 {
110 get { return "Laatste Pagina"; }
111 }
112
113 // English value: Next Page
114 public string NextPageButtonToolTip
115 {
116 get { return "Volgende Pagina"; }
117 }
118
119 // English value: The entire report has been searched.
120 public string NoMoreMatches
121 {
122 get { return "Het volledige rapport is doorzocht."; }
123 }
124
125 // English value: NULL
126 public string NullCheckBoxText
127 {
128 get { return "Geen waarde"; }
129 }
130
131 // English value: Null
132 public string NullValueText
133 {
134 get { return "Geen waarde"; }
135 }
136
137 // English value: of
138 public string PageOf
139 {
140 get { return "van"; }
141 }
142
143 // English value: Show / Hide Parameters
144 public string ParameterAreaButtonToolTip
145 {
146 get { return "Toon / Verberg Parameters"; }
147 }
148
149 // English value: Password:
150 public string PasswordPrompt
151 {
152 get { return "Wachtwoord:"; }
153 }
154
155 // English value: Previous Page
156 public string PreviousPageButtonToolTip
157 {
158 get { return "Vorige Pagina"; }
159 }
160
161 // English value: Print
162 public string PrintButtonToolTip
163 {
164 get { return "Afdrukken"; }
165 }
166
167 // English value: Loading...
168 public string ProgressText
169 {
170 get { return "Verwerken..."; }
171 }
172
173 // English value: Refresh
174 public string RefreshButtonToolTip
175 {
176 get { return "Vernieuwen"; }
177 }
178
179 // English value: Find Text in Report
180 public string SearchTextBoxToolTip
181 {
182 get { return "Zoek naar tekst binnen het rapport"; }
183 }
184
185 // English value: <Select a Value>
186 public string SelectAValue
187 {
188 get { return "<Selecteer een waarde>"; }
189 }
190
191 // English value: (Select All)
192 public string SelectAll
193 {
194 get { return "(Selecteer alles)"; }
195 }
196
197 // English value: Select a format
198 public string SelectFormat
199 {
200 get { return "Selecteer een formaat"; }
201 }
202
203 // English value: The search text was not found.
204 public string TextNotFound
205 {
206 get { return "De zoektekst is niet gevonden."; }
207 }
208
209 // English value: Today is {0}
210 public string TodayIs
211 {
212 get { return "Vandaag is {0}"; }
213 }
214
215 // English value: True
216 public string TrueValueText
217 {
218 get { return "Waar"; }
219 }
220
221 // English value: Log In Name:
222 public string UserNamePrompt
223 {
224 get { return "Gebruikersnaam:"; }
225 }
226
227 // English value: View Report
228 public string ViewReportButtonText
229 {
230 get { return "Toon Rapport"; }
231 }
232
233 // English value: Zoom
234 public string ZoomControlToolTip
235 {
236 get { return "Zoom"; }
237 }
238
239 // English value: Page Width
240 public string ZoomToPageWidth
241 {
242 get { return "Paginabreedte"; }
243 }
244
245 // English value: Whole Page
246 public string ZoomToWholePage
247 {
248 get { return "Volledige pagina"; }
249 }
250
251 #endregion
252
253 #region IReportViewerMessages2 Members
254
255 // English value: Your browser does not support scripts or has been configured not to allow scripts.
256 public string ClientNoScript
257 {
258 get { return "Uw browser ondersteunt geen JavaScript of deze ondersteuning is uitgeschakeld."; }
259 }
260
261 // English value: Unable to load client print control.
262 public string ClientPrintControlLoadFailed
263 {
264 get { return "Het laden van het client print control is niet gelukt."; }
265 }
266
267 // English value: One or more data sources is missing a user name.
268 public string CredentialMissingUserNameError(string dataSourcePrompt)
269 {
270 return "Een of meerdere databronnen missen een gebruikersnaam.";
271 }
272
273 // English value is different for each Rendering Extension. See comment behind each type.
274 public string GetLocalizedNameForRenderingExtension(string format)
275 {
276 switch (format)
277 {
278 case "XML" : return "XML databestand (.xml)"; // XML file with report data
279 case "CSV" : return "CSV databestand (.csv)"; // CSV (comma delimited)
280 case "PDF" : return "PDF document (.pdf)"; // PDF
281 case "MHTML" : return "Webarchief (.mhtml)"; // MHTML (web archive)
282 case "EXCEL" : return "Excel rekenblad (.xls)"; // Excel
283 case "IMAGE" : return "Afbeelding (.tif)"; // TIFF file
284 case "WORD" : return "Word document (.doc)"; // Word
285 default : return null;
286 }
287 }
288
289 // English value: Select a value
290 public string ParameterDropDownToolTip
291 {
292 get { return "Selecteer een waarde"; }
293 }
294
295 // English value: Please select a value for the parameter '{0}'.
296 public string ParameterMissingSelectionError(string parameterPrompt)
297 {
298 return String.Format(CultureInfo.CurrentCulture, "Selecteer een waarde voor de parameter '{0}'", parameterPrompt);
299 }
300
301 // English value: Please enter a value for the parameter '{0}'. The parameter cannot be blank.
302 public string ParameterMissingValueError(string parameterPrompt)
303 {
304 return String.Format(CultureInfo.CurrentCulture, "Selecteer een waarde voor de parameter '{0}'. De parameter mag niet leeg zijn.", parameterPrompt);
305 }
306
307 #endregion
308
309 #region IReportViewerMessages3 Members
310
311 // English value: Loading...
312 public string CalendarLoading
313 {
314 get { return "Verwerken..."; }
315 }
316
317 // English value: Cancel
318 public string CancelLinkText
319 {
320 get { return "Annuleer"; }
321 }
322
323 // English value: pageCount if PageCountMode.Actual, else pageCount suffixed with a ?
324 public string TotalPages(int pageCount, PageCountMode pageCountMode)
325 {
326 return string.Format(CultureInfo.CurrentCulture, "{0}{1}", pageCount, pageCountMode == PageCountMode.Estimate ? "~" : String.Empty);
327 }
328
329 #endregion
330 }
331}

Naturally, it would be irresponsible of me to paste code without some matching unit tests:

DutchReportViewerMessagesTests.cs
1using System;
2using Microsoft.VisualStudio.TestTools.UnitTesting;
3using InfoSupport.SomeApplication;
4
5namespace InfoSupport.SomeApplication.Tests
6{
7 [TestClass]
8 public class DutchReportViewerMessagesTests
9 {
10 [TestMethod]
11 public void TranslatedStringsTests()
12 {
13 var m = new DutchReportViewerMessages();
14 Assert.IsTrue(!String.IsNullOrEmpty(m.BackButtonToolTip));
15 Assert.IsTrue(!String.IsNullOrEmpty(m.CalendarLoading));
16 Assert.IsTrue(!String.IsNullOrEmpty(m.CancelLinkText));
17 Assert.IsTrue(!String.IsNullOrEmpty(m.ChangeCredentialsText));
18 Assert.IsTrue(!String.IsNullOrEmpty(m.ChangeCredentialsToolTip));
19 Assert.IsTrue(!String.IsNullOrEmpty(m.ClientNoScript));
20 Assert.IsTrue(!String.IsNullOrEmpty(m.ClientPrintControlLoadFailed));
21 Assert.IsTrue(!String.IsNullOrEmpty(m.CredentialMissingUserNameError(null)));
22 Assert.IsTrue(!String.IsNullOrEmpty(m.CurrentPageTextBoxToolTip));
23 Assert.IsTrue(!String.IsNullOrEmpty(m.DocumentMap));
24 Assert.IsTrue(!String.IsNullOrEmpty(m.DocumentMapButtonToolTip));
25 Assert.IsTrue(!String.IsNullOrEmpty(m.ExportButtonText));
26 Assert.IsTrue(!String.IsNullOrEmpty(m.ExportButtonToolTip));
27 Assert.IsTrue(!String.IsNullOrEmpty(m.ExportFormatsToolTip));
28 Assert.IsTrue(!String.IsNullOrEmpty(m.FalseValueText));
29 Assert.IsTrue(!String.IsNullOrEmpty(m.FindButtonText));
30 Assert.IsTrue(!String.IsNullOrEmpty(m.FindButtonToolTip));
31 Assert.IsTrue(!String.IsNullOrEmpty(m.FindNextButtonText));
32 Assert.IsTrue(!String.IsNullOrEmpty(m.FindNextButtonToolTip));
33 Assert.IsTrue(!String.IsNullOrEmpty(m.FirstPageButtonToolTip));
34 Assert.IsTrue(!String.IsNullOrEmpty(m.InvalidPageNumber));
35 Assert.IsTrue(!String.IsNullOrEmpty(m.LastPageButtonToolTip));
36 Assert.IsTrue(!String.IsNullOrEmpty(m.NextPageButtonToolTip));
37 Assert.IsTrue(!String.IsNullOrEmpty(m.NoMoreMatches));
38 Assert.IsTrue(!String.IsNullOrEmpty(m.NullCheckBoxText));
39 Assert.IsTrue(!String.IsNullOrEmpty(m.NullValueText));
40 Assert.IsTrue(!String.IsNullOrEmpty(m.PageOf));
41 Assert.IsTrue(!String.IsNullOrEmpty(m.ParameterAreaButtonToolTip));
42 Assert.IsTrue(!String.IsNullOrEmpty(m.ParameterDropDownToolTip));
43 Assert.IsTrue(!String.IsNullOrEmpty(m.ParameterMissingSelectionError(String.Empty)));
44 Assert.IsTrue(!String.IsNullOrEmpty(m.ParameterMissingValueError(String.Empty)));
45 Assert.IsTrue(!String.IsNullOrEmpty(m.PasswordPrompt));
46 Assert.IsTrue(!String.IsNullOrEmpty(m.PreviousPageButtonToolTip));
47 Assert.IsTrue(!String.IsNullOrEmpty(m.PrintButtonToolTip));
48 Assert.IsTrue(!String.IsNullOrEmpty(m.ProgressText));
49 Assert.IsTrue(!String.IsNullOrEmpty(m.RefreshButtonToolTip));
50 Assert.IsTrue(!String.IsNullOrEmpty(m.SearchTextBoxToolTip));
51 Assert.IsTrue(!String.IsNullOrEmpty(m.SelectAll));
52 Assert.IsTrue(!String.IsNullOrEmpty(m.SelectAValue));
53 Assert.IsTrue(!String.IsNullOrEmpty(m.SelectFormat));
54 Assert.IsTrue(!String.IsNullOrEmpty(m.TextNotFound));
55 Assert.IsTrue(!String.IsNullOrEmpty(m.TodayIs));
56 Assert.IsTrue(!String.IsNullOrEmpty(m.TrueValueText));
57 Assert.IsTrue(!String.IsNullOrEmpty(m.UserNamePrompt));
58 Assert.IsTrue(!String.IsNullOrEmpty(m.ViewReportButtonText));
59 Assert.IsTrue(!String.IsNullOrEmpty(m.ZoomControlToolTip));
60 Assert.IsTrue(!String.IsNullOrEmpty(m.ZoomToPageWidth));
61 Assert.IsTrue(!String.IsNullOrEmpty(m.ZoomToWholePage));
62 }
63
64 [TestMethod]
65 public void FormattableStringsTest()
66 {
67 var m = new DutchReportViewerMessages();
68 var parameter = "abc123";
69 Assert.IsTrue(m.ParameterMissingSelectionError(parameter).Contains(parameter));
70 Assert.IsTrue(m.ParameterMissingValueError(parameter).Contains(parameter));
71 Assert.IsTrue(String.Format(m.TodayIs, parameter).Contains(parameter));
72 }
73
74 [TestMethod]
75 public void GetLocalizedNameForRenderingExtensionTest()
76 {
77 var m = new DutchReportViewerMessages();
78 string[] knownExtensions = { "XML", "CSV", "PDF", "MHTML", "EXCEL", "IMAGE", "WORD"};
79 foreach (var ext in knownExtensions)
80 Assert.IsTrue(!String.IsNullOrEmpty(m.GetLocalizedNameForRenderingExtension(ext)));
81 }
82
83 [TestMethod]
84 public void GetLocalizedNameForRenderingExtensionTest_UnknownExtensionReturnsNull()
85 {
86 var m = new DutchReportViewerMessages();
87 string[] unknownExtensions = { "DOCX", "PNG", "JPG", "JPEG" };
88 foreach (var ext in unknownExtensions)
89 Assert.IsNull(m.GetLocalizedNameForRenderingExtension(ext));
90 }
91
92 [TestMethod]
93 public void TotalPagesTest()
94 {
95 var m = new DutchReportViewerMessages();
96 Assert.AreEqual("10", m.TotalPages(10, Microsoft.Reporting.WebForms.PageCountMode.Actual));
97 Assert.AreEqual("10~", m.TotalPages(10, Microsoft.Reporting.WebForms.PageCountMode.Estimate));
98 }
99 }
100}

Additionally, below is a table of all properties with their respective English and Dutch translation. Note that this table does not include the methods that are part of the interfaces.

IREPORTVIEWERMESSAGES

PROPERTY NAMEORIGINAL ENGLISH VALUEDUTCH TRANSLATION
BackButtonToolTipBack to Parent ReportTerug naar het vorige rapport
ChangeCredentialsTextChange CredentialsWijzig Rechten
ChangeCredentialsToolTipChange CredentialsWijzig Rechten
CurrentPageTextBoxToolTipCurrent PageHuidige Pagina
DocumentMapDocument MapDocument Map
DocumentMapButtonToolTipShow / Hide Document MapToon / Verberg Document Map
ExportButtonTextExportExporteer
ExportButtonToolTipExportExporteer
ExportFormatsToolTipExport FormatsExporteer Formaten
FalseValueTextFalseOnwaar
FindButtonTextFindZoek
FindButtonToolTipFindZoek
FindNextButtonTextNextVolgende
FindNextButtonToolTipFind NextVolgend Resultaat
FirstPageButtonToolTipFirst PageEerste Pagina
InvalidPageNumberEnter a valid page numberVoer een geldig paginanummer in
LastPageButtonToolTipLast PageLaatste Pagina
NextPageButtonToolTipNext PageVolgende Pagina
NoMoreMatchesThe entire report has been searched.Het volledige rapport is doorzocht.
NullCheckBoxTextNULLGeen waarde
NullValueTextNullGeen waarde
PageOfofvan
ParameterAreaButtonToolTipShow / Hide ParametersToon / Verberg Parameters
PasswordPromptPassword:Wachtwoord:
PreviousPageButtonToolTipPrevious PageVorige Pagina
PrintButtonToolTipPrintAfdrukken
ProgressTextLoading...Verwerken...
RefreshButtonToolTipRefreshVernieuwen
SearchTextBoxToolTipFind Text in ReportZoek naar tekst binnen het rapport
SelectAValue<Select a Value><Selecteer een waarde>
SelectAll(Select All)(Selecteer alles)
SelectFormatSelect a formatSelecteer een formaat
TextNotFoundThe search text was not found.De zoektekst is niet gevonden.
TodayIsToday is 0Vandaag is 0
TrueValueTextTrueWaar
UserNamePromptLog In Name:Gebruikersnaam:
View ReportViewReportButtonTextToon Rapport
ZoomControlToolTipZoomZoom
ZoomToPageWidthPage WidthPaginabreedte
ZoomToWholePageWhole PageVolledige pagina

IREPORTVIEWERMESSAGES2

PROPERTY NAMEORIGINAL ENGLISH VALUEDUTCH TRANSLATION
ClientNoScriptYour browser does not support scripts or has been configured not to allow scripts.Uw browser ondersteunt geen JavaScript of deze ondersteuning is uitgeschakeld.
ClientPrintControlLoadFailedUnable to load client print control.Het laden van het client print control is niet gelukt.
ParameterDropDownToolTipSelect a valueSelecteer een waarde

IREPORTVIEWERMESSAGES3

PROPERTY NAMEORIGINAL ENGLISH VALUEDUTCH TRANSLATION
CalendarLoadingLoading...Verwerken...
CancelLinkTextCancelAnnuleer

Additional notes

Summary

Microsoft ReportViewer Control can be localized by implementing the three IReportViewerMessages interfaces. However, the documentation of these interfaces lack the original string values, which in turn makes it difficult to provide a proper translation. In this blog, a complete listing of these values and a Dutch implementation of IReportViewerMessages are presented.

© 2022 Martin Devillers. All rights reserved

GitHubStackOverflowLinkedIn