Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / guides / localization / README.md
1 # Localization in Ext JS
2 ______________________________________________
3
4 Creating an application that works is one thing; creating an application that works for your users is something very different. Communicating with users in a language that they understand and with conventions that they're used to is vital.
5
6 Imagine this scenario, you hand your phone to a friend in good faith but when they return it, everything's in Japanese. Frustrated, you try to remember which combination of buttons leads you to the Settings menu so you can change it back, navigating through, you realize that menus slide in the opposite direction, maybe even the color scheme is different. You start to realize just how important language and cultural conventions are and how disorienting it is when faced with a localization setting that wasn't meant for you. Now imagine your users, wanting to use your Ext JS application but feeling the same confusion and unsure of what's being asked of them.
7
8 To fix this, we go through a process known as 'localization' (sometimes called l10n). A large part of localization is translation and, thankfully, Ext JS makes it easy to localize your application.
9
10 ## Ext's Localization Files
11
12 In the root directory of your copy of Ext JS there is a folder called `locale`. This contains common examples (e.g. day names) in 45 languages ranging from Indonesian to Macedonian. You can inspect the contents of each to see exactly what they contain. Here's an excerpt from the Spanish localization file:
13
14     if (Ext.toolbar.Paging){
15         Ext.apply(Ext.PagingToolbar.prototype, {
16             beforePageText : "Página",
17             afterPageText  : "de {0}",
18             firstText      : "Primera página",
19             prevText       : "Página anterior",
20             nextText       : "Página siguiente",
21             lastText       : "Última página",
22             refreshText    : "Actualizar",
23             displayMsg     : "Mostrando {0} - {1} de {2}",
24             emptyMsg       : "Sin datos para mostrar"
25         });
26     }
27
28 Note: The `�` are character entity references which render as special characters, e.g. `á` shows á.
29
30 You can see that it checks to see if a {@link Ext.view.BoundList#pagingToolbar Paging toolbar} is in use, and if it is, applies the Spanish strings to each area text is shown. If you have custom text areas you, can append them here as well with the appropriate translations. You'll also notice that it is setting these properties to the Paging Toolbar's prototype. The upshot of this is that every new Paging Toolbar that is created will inherit these translated properties.
31
32 ## Utilizing Localization
33
34 There are two ways you could implement localization in your application: statically or dynamically. We're going to look at how to do it dynamically so users can choose which language they're most familiar with. First, we're going to create a Combobox where users will select their language and secondly, we'll deduce the language from the URL so if a user visits http://yoursite.com/?lang=es the Spanish version of your Ext application is used.
35
36 Set up a basic HTML page with links to Ext JS's necessary parts and our localized application's languages.js and app.js files.
37
38     <!DOCTYPE html>
39     <html>
40     <head>
41         <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
42         <title>Localization example</title>
43         <!-- Ext Library Files -->
44         <link rel="stylesheet" type="text/css" href="ext/resources/css/ext-all.css">
45         <script src="ext/ext-all-debug.js"></script>
46         <!-- App Scripts -->
47         <script src="languages.js"></script>
48         <script src="app.js"></script>
49     </head>
50     <body>
51         <div id="languages"></div>
52         <div id="datefield"></div>
53     </body>
54     </html>
55
56 We have two separate JavaScript files: the first will be a list of all the languages that Ext JS comes with, the second will be the application itself. We've also set up two `div` tags, the first will contain the combobox for users to select their language and the second, `datefield`, will have a date picker.
57
58 Now create a file called `languages.js`. In this we'll store the languages in an array with two values, the language code and the name of the language like so:
59
60     Ext.namespace('Ext.local');
61
62     Ext.local.languages = [
63         ['af', 'Afrikaans'],
64         ['bg', 'Bulgarian'],
65         ['ca', 'Catalonian'],
66         ['cs', 'Czech'],
67         ['da', 'Danish'],
68         ['de', 'German'],
69         ['el_GR', 'Greek'],
70         ['en_GB', 'English (UK)'],
71         ['en', 'English'],
72         ['es', 'Spanish/Latin American'],
73         ['fa', 'Farsi (Persian)'],
74         ['fi', 'Finnish'],
75         ['fr_CA', 'French (Canadian)'],
76         ['fr', 'French (France)'],
77         ['gr', 'Greek (Old Version)'],
78         ['he', 'Hebrew'],
79         ['hr', 'Croatian'],
80         ['hu', 'Hungarian'],
81         ['id', 'Indonesian'],
82         ['it', 'Italian'],
83         ['ja', 'Japanese'],
84         ['ko', 'Korean'],
85         ['lt', 'Lithuanian'],
86         ['lv', 'Latvian'],
87         ['mk', 'Macedonian'],
88         ['nl', 'Dutch'],
89         ['no_NB', 'Norwegian Bokmål'],
90         ['no_NN', 'Norwegian Nynorsk'],
91         ['pl', 'Polish'],
92         ['pt_BR', 'Portuguese/Brazil'],
93         ['pt_PT', 'Portuguese/Portugal'],
94         ['ro', 'Romanian'],
95         ['ru', 'Russian'],
96         ['sk', 'Slovak'],
97         ['sl', 'Slovenian'],
98         ['sr_RS', 'Serbian Cyrillic'],
99         ['sr', 'Serbian Latin'],
100         ['sv_SE', 'Swedish'],
101         ['th', 'Thai'],
102         ['tr', 'Turkish'],
103         ['ukr', 'Ukrainian'],
104         ['vn', 'Vietnamese'],
105         ['zh_CN', 'Simplified Chinese'],
106         ['zh_TW', 'Traditional Chinese']
107     ];
108
109 This is all the languages file will consist of but will serve as a useful reference for our Ext JS application.
110
111 Next, we'll start building the application itself. Using the module pattern, we will have four methods: `init`, `onSuccess`, `onFailure` and `setup`.
112
113     Ext.Loader.setConfig({enabled: true});
114     Ext.Loader.setPath('Ext.ux', 'ext/examples/ux/');
115     Ext.require([
116         'Ext.data.*',
117         'Ext.tip.QuickTipManager',
118         'Ext.form.*',
119         'Ext.ux.data.PagingMemoryProxy',
120         'Ext.grid.Panel'
121     ]);
122
123     Ext.onReady(function() {
124
125         MultiLangDemo = (function() {
126             return {
127                 init: function() {
128
129                 },
130                 onSuccess: function() {
131
132                 },
133                 onFailure: function() {
134
135                 },
136                 setup: function() {
137
138                 }
139             };
140         })();
141
142         MultiLangDemo.init();
143     });
144
145 To create the {@link Ext.form.field.ComboBox combobox} that will contain all the possible language selections we first need to create an {@link Ext.data.ArrayStore array store} in the `init` function like so.
146
147     var store = Ext.create('Ext.data.ArrayStore', {
148         fields: ['code', 'language'],
149         data  : Ext.local.languages //from languages.js
150     });
151
152 This is a very simple store that contains the two fields that correspond to the two values for each record in the `languages.js` file. As we gave it a namespace, we can refer to it as `Ext.local.languages`. You can type this in your browser's console to see what it consists of.
153
154 Now create the combobox itself, again, within the `init` function:
155
156     var combo = Ext.create('Ext.form.field.ComboBox', {
157         renderTo: 'languages',
158         store: store,
159         displayField: 'language',
160         queryMode: 'local',
161         emptyText: 'Select a language...',
162         hideLabel: true,
163         listeners: {
164             select: {
165                 fn: function(cb, records) {
166                     var record = records[0];
167                     window.location.search = Ext.urlEncode({"lang":record.get("code")});
168                 },
169                 scope: this
170             }
171         }
172     });
173
174 If you refresh your browser, you should see a combobox that, when clicked, shows a list of languages bundled with Ext JS. When one of these languages is selected, the page refreshes and appends `?lang=da` (if you chose Danish) to the URL. We'll use this information to display the desired language to the user.
175
176 {@img combobox.png}
177
178 After the creation of the combobox, we're going to check to see if any language has been previously selected and act accordingly by checking the URL with Ext's {@link Ext#urlDecode urlDecode} function.
179
180     var params = Ext.urlDecode(window.location.search.substring(1));
181
182     if (params.lang) {
183         var url = Ext.util.Format.format('ext/locale/ext-lang-{0}.js', params.lang);
184
185         Ext.Ajax.request({
186             url: url,
187             success: this.onSuccess,
188             failure: this.onFailure,
189             scope: this
190         });
191
192         // check if there's really a language with passed code
193         var record = store.findRecord('code', params.lang, null, null, null, true);
194         // if language was found in store, assign it as current value in combobox
195
196         if (record) {
197             combo.setValue(record.data.language);
198         }
199     } else {
200         // no language found, default to english
201         this.setup();
202     }
203
204     Ext.tip.QuickTipManager.init();
205
206 Note: We're loading the files with an AJAX request, so the files will have to be uploaded to a server otherwise they'll fail to load due to browser security measures.
207
208 Here you can see why we have the `onSuccess` and `onFailure` methods. If a language file fails to load then the user must be notified instead of failing silently. First, we'll deal with failed files to make it obvious if debugging is needed; the idea is that if a user types in a nonexistent language code, or for some reason the language has been removed, an alert will be displayed so the user won't be surprised that the application is still in English.
209
210     onFailure: function() {
211         Ext.Msg.alert('Failure', 'Failed to load locale file.');
212         this.setup();
213     },
214
215 {@img onfailure.png}
216
217 The `onSuccess` method is similar. We evaluate the locale file and then setup the demo knowing that the file has been loaded:
218
219     onSuccess: function(response) {
220         eval(response.responseText);
221         this.setup();
222     },
223
224 The AJAX call that we made returns a few parameters. We use JavaScript's `eval` function on `responseText`. `responseText` is the entirety of the locale file that we loaded and `eval` parses all of the JavaScript contained in the string that is `responseText`, that is, applying all of the translated text and thus localizing the application.
225
226 However, there's nothing in `setup()` to look at yet so we'll move onto this method next. We're going to start by creating a {@link Ext.menu.DatePicker date picker} that will change based on the chosen language.
227
228     setup: function() {
229         Ext.create('Ext.FormPanel', {
230             renderTo: 'datefield',
231             frame: true,
232             title: 'Date picker',
233             width: 380,
234             defaultType: 'datefield',
235             items: [{
236                 fieldLabel: 'Date',
237                 name: 'date'
238             }]
239         });
240     }
241
242 Now, if you click on the calendar icon you'll see the month in the specified language as well as the first letter of each day.
243
244 {@img datepicker.png}
245
246 To show more of Ext JS's localization features we'll now create an e-mail field and a month browser. Inside the setup method, write the following:
247
248     Ext.create('Ext.FormPanel', {
249         renderTo: 'emailfield',
250         labelWidth: 100,
251         frame: true,
252         title: 'E-mail Field',
253         width: 380,
254         defaults: {
255             msgTarget: 'side',
256             width: 340
257         },
258         defaultType: 'textfield',
259         items: [{
260             fieldlabel: 'Email',
261             name: 'email',
262             vtype: 'email'
263         }]
264     });
265
266     var monthArray = Ext.Array.map(Ext.Date.monthNames, function (e) { return [e]; });
267     var ds = Ext.create('Ext.data.Store', {
268          fields: ['month'],
269          remoteSort: true,
270          pageSize: 6,
271          proxy: {
272              type: 'pagingmemory',
273              data: monthArray,
274              reader: {
275                  type: 'array'
276              }
277          }
278      });
279
280     Ext.create('Ext.grid.Panel', {
281         renderTo: 'grid',
282         width: 380,
283         height: 203,
284         title:'Month Browser',
285         columns:[{
286             text: 'Month of the year',
287             dataIndex: 'month',
288             width: 240
289         }],
290         store: ds,
291         bbar: Ext.create('Ext.toolbar.Paging', {
292             pageSize: 6,
293             store: ds,
294             displayInfo: true
295         })
296     });
297     // trigger the data store load
298     ds.load();
299
300 Remember that `renderTo` corresponds to an `id` on an HTML tag so add those to our index file, too.
301
302 Notice that when typing in fields, a warning icon is displayed that, when hovered over, reveals context-specific information in the native language as a tooltip.
303
304 {@img tooltip.png}
305
306 An excellent example of what localization means beyond translation can be seen by selecting Polish and seeing how the order of the date field changes from DD-MM-YYYY to YYYY-MM-DD. Another is selecting Finnish and seeing how instead of dashes (-), periods (.) are used to separate day from month from year and the months are not capitalized. It's details like this that Ext takes care for you with it's comprehensive locale files.
307
308 ## Conclusion
309
310 In this tutorial we have looked at how to load different locale files included with Ext JS by using AJAX requests that reload the application in the desired language along with subtle cultural conventions.
311
312 Your users will benefit from a more native experience and appreciate the extra lengths that you've gone to to ensure a better experience.