-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocalization.html
More file actions
389 lines (371 loc) · 21.4 KB
/
localization.html
File metadata and controls
389 lines (371 loc) · 21.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<link type="text/css" rel="stylesheet" href="bootstrap.min.css" />
<style type="text/css">
.auto-style1 {
background-color: #F5F5F5;
}
</style>
</head>
<body>
<div class="document-contents">
<h3 id="DocAppLangs">Application Languages</h3>
<p>An application has at least one language for user interface. Many
applications have more than one language. ASP.NET Boilerplate provides a
flexible localization system for an application.</p>
<p>The first thing is to declare which languages are supported. This is done
in
<strong>PreInitialize</strong> method of your
<a href="/Pages/Documents/Module-System">module</a> as shown below:</p>
<pre lang="cs">Configuration.Localization.Languages.Add(new LanguageInfo("en", "English", "famfamfam-flag-england", true));
Configuration.Localization.Languages.Add(new LanguageInfo("tr", "Türkçe", "famfamfam-flag-tr"));</pre>
<p>In server side, you can <a href="/Pages/Documents/Dependency-Injection">
inject</a> and use <strong>ILocalizationManager</strong>. In client side, you
can use <strong>abp.localization</strong> javascript API to get list of
all available languages and the current language. famfamfam-flag-england (and
tr) is just a CSS class, you can change it upon your needs. Then you can use it
on UI to show related flag.</p>
<p>ASP.NET Boilerplate <a href="/Templates">templates</a> uses this system to
show a <strong>language-switch</strong> combobox to the user. Try to create a
template and see source codes for more.</p>
<h3 id="DocLocalizationSources">Localization Sources</h3>
<p>Localization
texts can be stored in different sources.
Even you can use more than one source in same application (If you have more than
one <a href="/Pages/Documents/Module-System">module</a>, each module can define
a seperated localization source).<strong>
ILocalizationSource</strong> interface should be implemented for a localization
source. Then it is <strong>register</strong>ed to ASP.NET Boilerplate's
localization configuration. </p>
<p>Each localization source must have a <strong>unique source name</strong>.
There are pre-defined localization source types as defined below.</p>
<h4 id="DocLocalizeXml">XML Files</h4>
<p>Localization texts can be stored in XML files. Content of an XML file is
something like that:</p>
<pre lang="xml"><?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary <strong>culture="en"</strong>>
<texts>
<text name="TaskSystem" value="Task System" />
<text name="TaskList" value="Task List" />
<text name="NewTask" value="New Task" />
<text name="Xtasks" value="{0} tasks" />
<text name="CompletedTasks" value="Completed tasks" />
<text name="EmailWelcomeMessage">Hi,
Welcome to Simple Task System! This is a sample
email content.</text>
</texts>
</localizationDictionary></pre>
<p>XML file must be unicode (<strong>utf-8</strong>). <strong>culture="en"</strong> declares that this XML file
contains English texts. For text nodes; <strong>name</strong> attribute is
used to identify a text. You can use <strong>value </strong>attribute or
<strong>inner text </strong>(like the
last one) to set value of the localization text. We create a seperated XML file for
<strong>each language</strong> as shown below:</p>
<p>
<img alt="Localization files" height="89" src="/SiteFiles/Content/Documents/Resources/localization_files2.png" width="217" /></p>
<p><strong>SimpleTaskSystem</strong> is the <strong>source name</strong> here
and SimpleTaskSystem.xml defines the <strong>default language</strong>. When a text is requested, ASP.NET
Boilerplate gets the text from current language's XML file (finds current
language using
Thread.CurrentThread.<strong>CurrentUICulture</strong>). If it does not exists
in the current language, it gets the text from default language's XML file.</p>
<h5>Registering XML Localization Sources</h5>
<p>XML files can be stored in <strong>file system</strong> or can be <strong>
embedded</strong> into an assembly.</p>
<p>For <strong>file system</strong> stored XMLs, we can register an XML
localization source as shown below:</p>
<pre lang="cs">Configuration.Localization.Sources.Add(
new DictionaryBasedLocalizationSource(
"SimpleTaskSystem",
new <strong>XmlFileLocalizationDictionaryProvider</strong>(
HttpContext.Current.Server.MapPath("~/Localization/SimpleTaskSystem")
)
)
);</pre>
<p>This is done in <strong>PreInitialize</strong> event of a module (See
<a href="/Pages/Documents/Module-System">module system</a> for more info).
ASP.NET Boilerplate finds all XML files in given directory and registers the
localization source.</p>
<p>For <strong>embedded XML files</strong>, we should mark all
localization XML files as <strong>embedded resource</strong> (Select XML files, open properties
window (F4) and change Build Action as Embedded Resource). Then we can register
the localization source as shown below:</p>
<pre lang="cs">Configuration.Localization.Sources.Add(
new DictionaryBasedLocalizationSource(
"SimpleTaskSystem",
new <strong>XmlEmbeddedFileLocalizationDictionaryProvider</strong>(
Assembly.GetExecutingAssembly(),
"MyCompany.MyProject.Localization.Sources"
)
)
);</pre>
<p><strong>XmlEmbeddedFileLocalizationDictionaryProvider</strong> gets an
assembly containing XML files (GetExecutingAssembly simply refers to current
assembly) and a <strong>namespace</strong> of XML files (namespace is calculated
assembly name + folder hierarchy of XML files). </p>
<p>Note that: When adding language postfix to embedded XML files, <strong>do not</strong> use dot
notation like 'MySource.tr.xml', instead use dash like '<strong>MySource-tr.xml</strong>'
since dot notation causes problems when finding resources.</p>
<h4>JSON Files</h4>
<p>JSON files can be used to store texts for a localization source. A sample
JSON localization file is shown below:</p>
<pre lang="js">{
<strong>"culture": "en"</strong>,
"texts": {
"TaskSystem": "Task system",
"Xtasks": "{0} tasks"
}
}</pre>
<p>JSON files should be unicode (<strong>utf-8</strong>). <strong>culture:
"en"</strong> declares that this JSON file contains English texts. We create
a seperated JSON file for <strong>each language</strong> as shown below:</p>
<p>
<img alt="JSON localization files" height="64" src="/SiteFiles/Content/Documents/Resources/json-localization-source-files.png" width="212" /></p>
<p><strong>MySourceName </strong>is the <strong>source name</strong> here,
and MySourceName.json defines the <strong>default language</strong>. It's
similar to XML files.</p>
<h5>Registering JSON Localization Sources</h5>
<p>JSON files can be stored in <strong>file system</strong> or can be
<strong>embedded</strong> into an assembly.</p>
<p>File file system stored JSONs, we can register a JSON localization source
as shown below:</p>
<pre lang="cs">Configuration.Localization.Sources.Add(
new DictionaryBasedLocalizationSource(
"MySourceName",
new <strong>JsonFileLocalizationDictionaryProvider</strong>(
HttpContext.Current.Server.MapPath("~/Localization/MySourceName")
)
)
);</pre>
<p>This is done in <strong>PreInitialize</strong> event of a module (See
<a href="/Pages/Documents/Module-System">module system</a> for more info).
ASP.NET Boilerplate finds all JSON files in given directory and registers the
localization source.</p>
<p>For <strong>embedded JSON files</strong>, we should mark all
localization JSON files as <strong>embedded resource</strong> (Select JSON files, open properties
window (F4) and change Build Action as Embedded Resource). Then we can register
the localization source as shown below:</p>
<pre lang="cs"> Configuration.Localization.Sources.Add(
new DictionaryBasedLocalizationSource(
"MySourceName",
new <strong>JsonEmbeddedFileLocalizationDictionaryProvider</strong>(
Assembly.GetExecutingAssembly(),
"MyCompany.MyProject.Localization.Sources"
)
)
);</pre>
<p><strong>JsonEmbeddedFileLocalizationDictionaryProvider</strong> gets an
assembly containing JSON files (GetExecutingAssembly simply refers to current
assembly) and a <strong>namespace</strong> of JSON files (namespace is calculated
assembly name + folder hierarchy of JSON files). </p>
<p>Note that: When adding language postfix to embedded JSON files, <strong>do
not</strong> use dot
notation like 'MySource.tr.json', instead use dash like '<strong>MySource-tr.json</strong>'
since dot notation causes problems when finding resources. </p>
<h4 id="DocLocalizeResources">Resource Files</h4>
<p>Localization text can also be stored in .NET's resource files. We can create
a resource file for each language as shown below (Right click to the project,
choose add new item then find resources file).</p>
<p>
<img alt="Localization resource files" height="97" src="/SiteFiles/Content/Documents/Resources/resource_files.png" width="149" /></p>
<p><strong>MyTexts.resx</strong> contains the default language texts and MyTexts<strong>.tr</strong>.resx
contains texts for Turkish language. When we open MyTexts.resx, we can see all
texts:</p>
<p>
<img alt="Content of a resource file" height="174" src="/SiteFiles/Content/Documents/Resources/resource_files_content.png" width="466" /></p>
<p>In this case, ASP.NET Boilerplate uses .NET's built-in resource manager for
localization. You should configure a localization source for the resource:</p>
<pre lang="cs">Configuration.Localization.Sources.Add(
new ResourceFileLocalizationSource(
"MySource",
MyTexts.ResourceManager
));</pre>
<p><strong>Uniqe name</strong> of the source is <strong>MySource</strong> here. And
<strong>MyTexts.ResourceManager</strong> is a
reference to the resource manager to be used to get localized texts. This is done in <strong>
PreInitialize</strong> event of the module (See
<a href="/Pages/Documents/Module-System">module system</a> for more info).</p>
<h4 id="DocLocalizeCustom">Custom Source</h4>
<p>A custom localization source can be implemented to store texts in different
sources such as in a database. You can directly implement the <strong>
ILocalizationSource</strong> interface or you can use <strong>
DictionaryBasedLocalizationSource</strong>
class to make implementation easier (json and xml localization sources also use
it).</p>
<h3 id="DocGetText">Getting A Localized Text</h3>
<p>After creating a source and register it to the ASP.NET Boilerplate's
localization system, texts can be localized easily.</p>
<h4 id="DocInServer">In Server Side</h4>
<p>In server side, we can <a href="/Pages/Documents/Dependency-Injection">inject</a> <strong>ILocalizationManager</strong> and use
it's <strong>GetString</strong> method.</p>
<pre lang="cs">var s1 = _localizationManager.GetString("SimpleTaskSystem", "NewTask");</pre>
<p>GetString method gets the string from the localization source based on
<strong>current thread's UI culture</strong>. If not found, it fallbacks to
<strong>default language</strong>. If it's not defined anywhere then it returns
the <strong>given string</strong> by wrapping with <strong>[</strong> and
<strong>]</strong> by default (instead
of throwing Exception). This behavior is configurable (you can use
Configuration.Localization.ReturnGivenTextIfNotFound property in PreInitialize of your
<a href="/Pages/Documents/Module-System">module</a>).</p>
<p>To do not repeat source name always, you can first <strong>get the source</strong>,
then get a string from the source:</p>
<pre lang="cs">var source = _localizationManager.GetSource("SimpleTaskSystem");
var s<span lang="tr">1</span> = source.GetString("NewTask");</pre>
<p>This returns text in the current language. There are also overrides of
GetString to get text in <strong>different languages</strong> and <strong>formatted by arguments</strong>.</p>
<p>If we can not inject ILocalizationManager (maybe in a static context that can
not reach to the dependency injection), we can simply use <strong>
LocalizationHelper</strong> static class. But prefer injecting and using ILocalizationManager where
it's possible
since LocalizationHelper is static and statics are not well testable (for who
writes unit tests).</p>
<p>If you need to localization in an
<a href="/Pages/Documents/Application-Services#DocAppServiceClass"><strong>application
service</strong></a>, in an <strong>MVC Controller</strong>, in a <strong>Razor
View</strong> or in another class derived from <strong>AbpServiceBase</strong>, there are shortcut <strong>L</strong>
methods.</p>
<h5 id="DocInControllers">In MVC Controllers</h5>
<p>Localization texts are generally needed in MVC Controller and Views. There is
a shortcut for that. See the sample controller below:</p>
<pre lang="cs">public class HomeController : SimpleTaskSystemControllerBase
{
public ActionResult Index()
{
var helloWorldText = L("HelloWorld");
return View();
}
}</pre>
<p><strong>L</strong> method is used to localize a string. Surely, you must
supply a source name. It's done in SimpleTaskSystemControllerBase as shown
below:</p>
<pre lang="cs">public abstract class SimpleTaskSystemControllerBase : AbpController
{
protected SimpleTaskSystemControllerBase()
{
LocalizationSourceName = "SimpleTaskSystem";
}
}</pre>
<p>Notice that it is derived from <strong>AbpController</strong>. Thus, you can
easily localize texts with <strong>L</strong> method. </p>
<h5 id="DocInViews">In MVC Views</h5>
<p>Same <strong>L</strong> method also exists in
views:</p>
<pre lang="cs"><div>
<form id="NewTaskForm" role="form">
<div class="form-group">
<label for="TaskDescription"><strong>@L("TaskDescription")</strong></label>
<textarea id="TaskDescription" data-bind="value: task.description" class="form-control" rows="3" placeholder="<strong>@L("EnterDescriptionHere")</strong>" required></textarea>
</div>
<div class="form-group">
<label for="TaskAssignedPerson"><strong>@L("AssignTo")</strong></label>
<select id="TaskAssignedPerson" data-bind="options: people, optionsText: 'name', optionsValue: 'id', value: task.assignedPersonId, optionsCaption: '<strong>@L("SelectPerson")</strong>'" class="form-control"></select>
</div>
<button data-bind="click: saveTask" type="submit" class="btn btn-primary"><strong>@L("CreateTheTask")</strong></button>
</form>
</div></pre>
<p>To make this work, you should derive your views from a base class that sets
the source name:</p>
<pre lang="cs">public abstract class SimpleTaskSystemWebViewPageBase : SimpleTaskSystemWebViewPageBase<dynamic>
{
}
public abstract class SimpleTaskSystemWebViewPageBase<TModel> : AbpWebViewPage<TModel>
{
protected SimpleTaskSystemWebViewPageBase()
{
LocalizationSourceName = "SimpleTaskSystem";
}
}</pre>
<p>And set this view base class in web.config:</p>
<pre lang="cs"><pages pageBaseType="SimpleTaskSystem.Web.Views.SimpleTaskSystemWebViewPageBase"></pre>
<p>All these for controllers and views are ready when you create your solution
from one of the ASP.NET Boilerplate <a href="/Templates">templates</a>.</p>
<h4 id="DocInJavascript">In Javascript</h4>
<p>ASP.NET Boilerplate makes possible to use same localization texts in also
javascript code. First, you should be added dynamic ABP scripts to the page:</p>
<pre lang="xml"><script src="/Abp<span lang="tr">S<span class="auto-style1">cripts</span></span>/GetScripts" type="text/javascript"></script></pre>
<p>ASP.NET Boilerplate automatically generates needed javascript code to get
localized texts in the client side. Then you can easily get a localized text in
javascript as shown below:</p>
<pre lang="js">var s1 = abp.localization.localize('NewTask', 'SimpleTaskSystem');</pre>
<p>NewTask is the text name and SimpleTaskSystem is the source name here. To do
not repeat source name, you can first get the source then get the text:</p>
<pre lang="js">var source = abp.localization.getSource('SimpleTaskSystem');
var s<span lang="tr">1</span> = source('NewTask');</pre>
<h5>Format Arguments</h5>
<p>Localization method can also get additional format arguments. Example:</p>
<pre lang="js">abp.localization.localize('RoleDeleteWarningMessage', 'MySource', 'Admin');
//shortcut if source is got using getSource as shown above
source('RoleDeleteWarningMessage', 'Admin');</pre>
<p>if RoleDeleteWarningMessage = 'Role {0} will be deleted', then localized text
will be 'Role Admin will be deleted'.</p>
<h5>Default Localization Source</h5>
<p>You can set a default localization source and use
abp.localization.localize method without source name.</p>
<pre lang="js">abp.localization.defaultSourceName = 'SimpleTaskSystem';
var s1 = abp.localization.localize('NewTask');</pre>
<p>defaultSourceName is global and works for only one source at a time.</p>
<h3 id="DocExtending">Extending Localization Sources</h3>
<p>Assume that we use a module which defines it's own localization source. We
may need to change it's localized texts, add new texts or translate to other languages.
ASP.NET Boilerplate allows extending a localization source. It currently works
for XML and JSON files (Actually any localization source implements
IDictionaryBasedLocalizationSource interface).</p>
<p>ASP.NET Boilerplate also defines some localization sources. For instance,
<strong>Abp.Web</strong> nuget package defines a localization source named "<strong>AbpWeb</strong>"
as embedded XML files:</p>
<p>
<img alt="AbpWeb localization source files" height="147" src="/SiteFiles/Content/Documents/Resources/AbpWeb-localization-source.png" width="216" /></p>
<p> Default (English) XML file
is like below (only first two texts are shown):</p>
<pre lang="xml"><?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
<texts>
<text name="InternalServerError" value="An internal error occurred during your request!" />
<text name="ValidationError" value="Your request is not valid!" />
...
</texts>
</localizationDictionary></pre>
<p>To extend AbpWeb source, we can define XML files. Assume that we only want to
change <strong>InternalServerError</strong> text. We can define an XML file as shown below:</p>
<pre lang="xml"><?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
<texts>
<text name="InternalServerError" value="Sorry :( It seems there is a problem. Let us to solve it and please try again later." />
</texts>
</localizationDictionary></pre>
<p>Then we can register it on PreInitialize method of our module:</p>
<pre lang="cs"><strong>Configuration.Localization.Sources.Extensions.Add</strong>(
new LocalizationSourceExtensionInfo("<strong>AbpWeb</strong>",
new XmlFileLocalizationDictionaryProvider(
HttpContext.Current.Server.MapPath("<strong>~/Localization/AbpWebExtensions</strong>")
)
)
);</pre>
<p>We could use XmlEmbeddedFileLocalizationDictionaryProvider if want to create
embedded resource XML files (see Localization sources section). ASP.NET
Boilerplate overrides (merges) base localization source with our XML files. We
can also add new language files.</p>
<p><strong>Note</strong>: We can use JSON files to extend XML files, or vice
verse.</p>
<h3 id="DocConclusion">Best Practices</h3>
<p>XML files, JSON files and Resource files have own strengths and weaknesses.
We suggest to use XML or JSON files instead of Resource files, because;</p>
<ul>
<li>XML/JSON files are easy to edit, extend or port.</li>
<li>XML/JSON files requires string keys while getting localized texts,
instead of compile time properties like Resource files. This can be
considered as a weekness. But, it's easier to change source later. Even
we can move localization to a database without changing or code which
use localization (<strong>Module-zero</strong> implements it to create a <strong>
database based</strong> and <strong>per-tenant</strong> localization source. See
<a href="/Pages/Documents/Zero/Language-Management">documentation</a>.) </li>
</ul>
<p>If you use XML or JSON, it's
suggested to do not sort texts by name. Sort them by creation date. Thus, when
someone translates it to another language, he/she can easily see which texts are
added newly.</p>
</div>
</body>
</html>