ソースを参照

Implements basic multi language support.

master
hristoterezov 10年前
コミット
3032ea7684

+ 2
- 1
Makefile ファイルの表示

@@ -1,5 +1,5 @@
1 1
 BROWSERIFY = browserify
2
-GLOBAL_FLAGS = -e
2
+GLOBAL_FLAGS = -x jquery -e
3 3
 OUTPUT_DIR = .
4 4
 DEPLOY_DIR = libs
5 5
 
@@ -21,3 +21,4 @@ clean:
21 21
 
22 22
 deploy:
23 23
 	@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR)
24
+	

+ 3
- 0
app.js ファイルの表示

@@ -13,6 +13,7 @@ var APP =
13 13
         this.desktopsharing = require("./modules/desktopsharing/desktopsharing");
14 14
         this.xmpp = require("./modules/xmpp/xmpp");
15 15
         this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut");
16
+        this.translation = require("./modules/translation/translation");
16 17
     }
17 18
 };
18 19
 
@@ -34,6 +35,8 @@ $(document).ready(function () {
34 35
 
35 36
     APP.init();
36 37
 
38
+    APP.translation.init();
39
+
37 40
     if(APP.API.isEnabled())
38 41
         APP.API.init();
39 42
 

+ 5
- 1
css/settingsmenu.css ファイルの表示

@@ -3,7 +3,7 @@
3 3
     color: #00ccff;
4 4
 }
5 5
 
6
-#settingsmenu input {
6
+#settingsmenu input, select {
7 7
     margin-top: 10px;
8 8
     margin-left: 10%;
9 9
     width: 80%;
@@ -43,3 +43,7 @@
43 43
 #settingsmenu .icon-settings {
44 44
     padding: 34px;
45 45
 }
46
+
47
+#languages_selectbox{
48
+    height: 40px;
49
+}

+ 50
- 65
index.html ファイルの表示

@@ -19,7 +19,7 @@
19 19
     <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
20 20
     <script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
21 21
     <script src="interface_config.js?v=5"></script>
22
-    <script src="libs/app.bundle.js?v=6"></script>
22
+    <script src="libs/app.bundle.js?v=7"></script>
23 23
 
24 24
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
25 25
     <link rel="stylesheet" href="css/font.css?v=6"/>
@@ -52,76 +52,66 @@
52 52
             <a target="_new">
53 53
                 <div class="watermark rightwatermark"></div>
54 54
             </a>
55
-            <a class="poweredby" href="http://jitsi.org" target="_new" >powered by jitsi.org</a>
55
+            <a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
56 56
 
57 57
             <div id="enter_room_container">
58 58
                     <div id="enter_room_form" >
59 59
                         <div id="domain_name"></div>
60 60
                         <div id="enter_room">
61
-                            <input id="enter_room_field" type="text" autofocus placeholder="Enter room name" />
61
+                            <input id="enter_room_field" type="text" autofocus data-i18n="[placeholder]welcomepage.roomname" placeholder="Enter room name" />
62 62
                             <div class="icon-reload" id="reload_roomname"></div>
63
-                            <input id="enter_room_button" type="button" value="GO" />
63
+                            <input id="enter_room_button" type="button" data-i18n="[value]welcomepage.go" value="GO" />
64 64
 
65 65
                         </div>
66 66
                     </div>
67 67
             </div>
68 68
             <div id="brand_header"></div>
69 69
             <input type='checkbox' name='checkbox' id="disable_welcome"/>
70
-            <label for="disable_welcome" class="disable_welcome_position">Don't show this page next time I enter</label>
70
+            <label for="disable_welcome" class="disable_welcome_position" data-i18n="welcomepage.disable"></label>
71 71
             <div id="header_text"></div>
72 72
         </div>
73 73
         <div id="welcome_page_main">
74 74
             <div id="features">
75 75
                 <div class="feature_row">
76 76
                     <div class="feature_holder">
77
-                        <div class="feature_icon">Simple to use</div>
78
-                        <div class="feature_description">
79
-                            No downloads required. <span name="appName"></span> works directly within your browser. Simply share your conference URL with others to get started.
77
+                        <div class="feature_icon" data-i18n="welcomepage.feature1.title" ></div>
78
+                        <div class="feature_description" data-i18n="welcomepage.feature1.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
80 79
                         </div>
81 80
                     </div>
82 81
                     <div class="feature_holder">
83
-                        <div class="feature_icon">Low bandwidth</div>
84
-                        <div class="feature_description">
85
-                            Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less.
82
+                        <div class="feature_icon" data-i18n="welcomepage.feature2.title" ></div>
83
+                        <div class="feature_description" data-i18n="welcomepage.feature2.content">
86 84
                         </div>
87 85
                     </div>
88 86
                     <div class="feature_holder">
89
-                        <div class="feature_icon">Open source</div>
90
-                        <div class="feature_description">
91
-                            <span name="appName"></span> is licensed under MIT. You are free to download, use, modify, and share them as per these licenses.
87
+                        <div class="feature_icon" data-i18n="welcomepage.feature3.title" ></div>
88
+                        <div class="feature_description" data-i18n="welcomepage.feature3.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
92 89
                         </div>
93 90
                     </div>
94 91
                     <div class="feature_holder">
95
-                        <div class="feature_icon">Unlimited users</div>
96
-                        <div class="feature_description">
97
-                            There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors.
92
+                        <div class="feature_icon" data-i18n="welcomepage.feature4.title" ></div>
93
+                        <div class="feature_description" data-i18n="welcomepage.feature4.content">
98 94
                         </div>
99 95
                     </div>
100 96
                 </div>
101 97
                 <div class="feature_row">
102 98
                     <div class="feature_holder">
103
-                        <div class="feature_icon">Screen sharing</div>
104
-                        <div class="feature_description">
105
-                            It's easy to share your screen with others. <span name="appName"></span> is ideal for on-line presentations, lectures, and tech support sessions.
106
-                        </div>
99
+                            <div class="feature_icon" data-i18n="welcomepage.feature5.title" ></div>
100
+                            <div class="feature_description" data-i18n="welcomepage.feature5.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
101
+                            </div>
107 102
                     </div>
108 103
                     <div class="feature_holder">
109
-                        <div class="feature_icon">Secure rooms</div>
110
-                        <div class="feature_description">
111
-                            Need some privacy? <span name="appName"></span> conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions.
112
-                        </div>
104
+                            <div class="feature_icon" data-i18n="welcomepage.feature6.title" ></div>
105
+                            <div class="feature_description" data-i18n="welcomepage.feature6.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
106
+                            </div>
113 107
                     </div>
114 108
                     <div class="feature_holder">
115
-                        <div class="feature_icon">Shared notes</div>
116
-                        <div class="feature_description">
117
-                            <span name="appName"></span> features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more.
118
-                        </div>
109
+                            <div class="feature_icon" data-i18n="welcomepage.feature7.title" ></div>
110
+                            <div class="feature_description" data-i18n="welcomepage.feature7.content" data-i18n-options='{ "postProcess": "resolveAppName" }'></div>
119 111
                     </div>
120 112
                     <div class="feature_holder">
121
-                        <div class="feature_icon">Usage statistics</div>
122
-                        <div class="feature_description">
123
-                            Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems.
124
-                        </div>
113
+                            <div class="feature_icon" data-i18n="welcomepage.feature8.title" ></div>
114
+                            <div class="feature_description" data-i18n="welcomepage.feature8.content"></div>
125 115
                     </div>
126 116
                 </div>
127 117
             </div>
@@ -131,36 +121,36 @@
131 121
         <div style="position: relative;" id="header_container">
132 122
             <div id="header">
133 123
                 <span id="toolbar">
134
-                    <a class="button" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" content="Mute / Unmute">
124
+                    <a class="button" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute">
135 125
                         <i id="mute" class="icon-microphone"></i>
136 126
                     </a>
137 127
                     <div class="header_button_separator"></div>
138
-                    <a class="button" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" content="Start / stop camera">
128
+                    <a class="button" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera">
139 129
                         <i id="video" class="icon-camera"></i>
140 130
                     </a>
141 131
                     <span id="authentication" style="display: none">
142 132
                         <div class="header_button_separator"></div>
143
-                        <a class="button" id="toolbar_button_authentication" data-container="body" data-toggle="popover" data-placement="bottom" content="Authenticate">
133
+                        <a class="button" id="toolbar_button_authentication" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.authenticate" content="Authenticate">
144 134
                             <i id="authButton" class="icon-avatar"></i>
145 135
                         </a>
146 136
                     </span>
147 137
                     <span id="recording" style="display: none">
148 138
                         <div class="header_button_separator"></div>
149
-                        <a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" content="Record">
139
+                        <a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record">
150 140
                             <i id="recordButton" class="icon-recEnable"></i>
151 141
                         </a>
152 142
                     </span>
153 143
                     <div class="header_button_separator"></div>
154
-                    <a class="button" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" content="Lock / unlock room">
144
+                    <a class="button" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room">
155 145
                         <i id="lockIcon" class="icon-security"></i>
156 146
                     </a>
157 147
                     <div class="header_button_separator"></div>
158
-                    <a class="button" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" content="Invite others">
148
+                    <a class="button" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others">
159 149
                         <i class="icon-link"></i>
160 150
                     </a>
161 151
                     <div class="header_button_separator"></div>
162 152
                     <span class="toolbar_span">
163
-                        <a class="button" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" content="Open / close chat">
153
+                        <a class="button" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
164 154
                             <i id="chatButton" class="icon-chat">
165 155
                                 <span id="unreadMessages"></span>
166 156
                             </i>
@@ -168,38 +158,38 @@
168 158
                     </span>
169 159
                     <span id="prezi_button">
170 160
                         <div class="header_button_separator"></div>
171
-                        <a class="button" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" content="Share Prezi">
161
+                        <a class="button" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.prezi" content="Share Prezi">
172 162
                             <i class="icon-prezi"></i>
173 163
                         </a>
174 164
                     </span>
175 165
                     <span id="etherpadButton">
176 166
                         <div class="header_button_separator"></div>
177
-                        <a class="button" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document">
167
+                        <a class="button" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad">
178 168
                             <i class="icon-share-doc"></i>
179 169
                         </a>
180 170
                     </span>
181 171
                     <div class="header_button_separator"></div>
182 172
                     <span id="desktopsharing" style="display: none">
183
-                        <a class="button" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen">
173
+                        <a class="button" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen" data-i18n="[content]toolbar.sharescreen">
184 174
                             <i class="icon-share-desktop"></i>
185 175
                         </a>
186 176
                     </span>
187 177
                     <div class="header_button_separator"></div>
188
-                    <a class="button" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen">
178
+                    <a class="button" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen">
189 179
                         <i id="fullScreen" class="icon-full-screen"></i>
190 180
                     </a>
191 181
                     <span id="sipCallButton" style="display: none">
192 182
                         <div class="header_button_separator"></div>
193
-                        <a class="button" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number">
183
+                        <a class="button" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip">
194 184
                             <i class="icon-telephone"></i></a>
195 185
                     </span>
196 186
                     <div class="header_button_separator"></div>
197
-                    <a class="button" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" >
187
+                    <a class="button" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" data-i18n="[content]toolbar.Settings">
198 188
                         <i id="settingsButton" class="icon-settings"></i>
199 189
                     </a>
200 190
                     <div class="header_button_separator"></div>
201 191
                     <span id="hangup">
202
-                        <a class="button" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up">
192
+                        <a class="button" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up" data-i18n="[content]toolbar.hangup">
203 193
                             <i class="icon-hangup" style="color:#ff0000;font-size: 1.4em;"></i>
204 194
                         </a>
205 195
                     </span>
@@ -208,7 +198,7 @@
208 198
             <div id="subject"></div>
209 199
         </div>
210 200
         <div id="settings">
211
-          <h1>Connection Settings</h1>
201
+          <h1 data-i18n="connectionsettings"></h1>
212 202
           <form id="loginInfo">
213 203
             <label>JID: <input id="jid" type="text" name="jid" placeholder="me@example.com"/></label>
214 204
             <label>Password: <input id="password" type="password" name="password" placeholder="secret"/></label>
@@ -223,7 +213,7 @@
223 213
                 <div id="etherpad"></div>
224 214
                 <a target="_new"><div class="watermark leftwatermark"></div></a>
225 215
                 <a target="_new"><div class="watermark rightwatermark"></div></a>
226
-                <a class="poweredby" href="http://jitsi.org" target="_new" >powered by jitsi.org</a>
216
+                <a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
227 217
                 <div id="activeSpeaker">
228 218
                     <img id="activeSpeakerAvatar" src=""/>
229 219
                     <canvas id="activeSpeakerAudioLevel"></canvas>
@@ -238,18 +228,13 @@
238 228
                     </span>
239 229
                     <audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio>
240 230
                     <span class="focusindicator"></span>
241
-                    <!--<div class="connectionindicator">
242
-                        <span class="connection connection_empty"><i class="icon-connection"></i></span>
243
-                        <span class="connection connection_full"><i class="icon-connection"></i></span>
244
-                    </div>-->
245
-
246 231
                 </span>
247 232
                 <audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
248 233
                 <audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
249 234
             </div>
250 235
             <span id="bottomToolbar">
251 236
                 <span class="bottomToolbar_span">
252
-                    <a class="bottomToolbarButton" id="bottom_toolbar_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" content="Open / close chat">
237
+                    <a class="bottomToolbarButton" id="bottom_toolbar_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" data-i18n="[content]bottomtoolbar.chat" content="Open / close chat">
253 238
                         <i id="chatBottomButton" class="icon-chat-simple">
254 239
                             <span id="bottomUnreadMessages"></span>
255 240
                         </i>
@@ -257,7 +242,7 @@
257 242
                 </span>
258 243
                 <div class="bottom_button_separator"></div>
259 244
                 <span class="bottomToolbar_span">
260
-                    <a class="bottomToolbarButton" id="bottom_toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="top" id="contactlistpopover" content="Open / close contact list">
245
+                    <a class="bottomToolbarButton" id="bottom_toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="top" id="contactlistpopover"  data-i18n="[content]bottomtoolbar.contactlist" content="Open / close contact list">
261 246
                         <i id="contactListButton" class="icon-contactList">
262 247
                             <span id="numberOfParticipants"></span>
263 248
                         </i>
@@ -265,7 +250,7 @@
265 250
                 </span>
266 251
                 <div class="bottom_button_separator"></div>
267 252
                 <span class="bottomToolbar_span">
268
-                    <a class="bottomToolbarButton" id="bottom_toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" content="Show / hide film strip">
253
+                    <a class="bottomToolbarButton" id="bottom_toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" data-i18n="[content]bottomtoolbar.filmstrip" content="Show / hide film strip">
269 254
                         <i id="filmStripButton" class="icon-filmstrip"></i>
270 255
                     </a>
271 256
                 </span>
@@ -273,16 +258,16 @@
273 258
         </div>
274 259
         <div id="chatspace" class="right-panel">
275 260
             <div id="nickname">
276
-                Enter a nickname in the box below
261
+                <span data-i18n="chat.nickname.title"></span>
277 262
                 <form>
278
-                    <input type='text' id="nickinput" placeholder='Choose a nickname' autofocus>
263
+                    <input type='text' id="nickinput" data-i18n="[placeholder]chat.nickname.popover" autofocus>
279 264
                 </form>
280 265
             </div>
281 266
 
282 267
             <!--div><i class="fa fa-comments">&nbsp;</i><span class='nick'></span>:&nbsp;<span class='chattext'></span></div-->
283 268
             <div id="chatconversation"></div>
284 269
             <audio id="chatNotification" src="sounds/incomingMessage.wav" preload="auto"></audio>
285
-                <textarea id="usermsg" placeholder='Enter text...' autofocus></textarea>
270
+                <textarea id="usermsg" data-i18n="[placeholder]chat.messagebox" autofocus></textarea>
286 271
             <div id="smileysarea">
287 272
                 <div id="smileys" id="toggle_smileys">
288 273
                     <img src="images/smile.svg"/>
@@ -291,18 +276,18 @@
291 276
         </div>
292 277
         <div id="contactlist" class="right-panel">
293 278
             <ul>
294
-                <li class="title"><i class="icon-contact-list"></i> CONTACT LIST</li>
279
+                <li class="title"><i class="icon-contact-list"></i><span data-i18n="contactlist"></span></li>
295 280
             </ul>
296 281
         </div>
297 282
         <div id="settingsmenu" class="right-panel">
298
-            <div class="icon-settings"> SETTINGS</div>
283
+            <div class="icon-settings" data-i18n="settings.title"></div>
299 284
             <img id="avatar" src="https://www.gravatar.com/avatar/87291c37c25be69a072a4514931b1749?d=wavatar&size=30"/>
300 285
             <div class="arrow-up"></div>
301
-            <input type="text" id="setDisplayName" placeholder="Name">
286
+            <input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
302 287
             <input type="text" id="setEmail" placeholder="E-Mail">
303
-            <button id="updateSettings">Update</button>
288
+            <button id="updateSettings" data-i18n="settings.update"></button>
304 289
         </div>
305
-        <a id="downloadlog" onclick='dump(event.target);' data-container="body" data-toggle="popover" data-placement="right" data-content="Download logs" ><i class="fa fa-cloud-download"></i></a>
290
+        <a id="downloadlog" onclick='dump(event.target);' data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
306 291
     </div>
307 292
   </body>
308 293
 </html>

+ 4
- 0
lang/languages.json ファイルの表示

@@ -0,0 +1,4 @@
1
+{
2
+    "en": "English",
3
+    "bg": "Bulgarian"
4
+}

+ 120
- 0
lang/main.json ファイルの表示

@@ -0,0 +1,120 @@
1
+{
2
+    "contactlist": "CONTACT LIST",
3
+    "connectionsettings": "Connection Settings",
4
+    "poweredby": "powered by",
5
+    "downloadlogs": "Download logs",
6
+    "welcomepage":{
7
+        "go": "GO",
8
+        "roomname": "Enter room name",
9
+        "disable": "Don't show this page next time I enter",
10
+        "feature1": {
11
+            "title": "Simple to use",
12
+            "content": "No downloads required. __app__ works directly within your browser. Simply share your conference URL with others to get started."
13
+        },
14
+        "feature2": {
15
+            "title": "Low bandwidth",
16
+            "content": "Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less."
17
+        },
18
+        "feature3": {
19
+            "title": "Open source",
20
+            "content": "__app__ is licensed under MIT. You are free to download, use, modify, and share them as per these licenses."
21
+        },
22
+        "feature4": {
23
+            "title": "Unlimited users",
24
+            "content": "There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors."
25
+        },
26
+        "feature5": {
27
+            "title": "Screen sharing",
28
+            "content": "It's easy to share your screen with others. __app__ is ideal for on-line presentations, lectures, and tech support sessions."
29
+        },
30
+        "feature6": {
31
+            "title": "Secure rooms",
32
+            "content": "Need some privacy? __app__ conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions."
33
+        },
34
+        "feature7": {
35
+            "title": "Shared notes",
36
+            "content": "__app__ features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more."
37
+        },
38
+        "feature8": {
39
+            "title": "Usage statistics",
40
+            "content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems."
41
+        }
42
+    },
43
+    "toolbar": {
44
+        "mute": "Mute / Unmute",
45
+        "videomute": "Start / stop camera",
46
+        "authenticate": "Authenticate",
47
+        "record": "Record",
48
+        "lock": "Lock / unlock room",
49
+        "invite": "Invite others",
50
+        "chat": "Open / close chat",
51
+        "prezi": "Share Prezi",
52
+        "etherpad": "Shared document",
53
+        "sharescreen": "Share screen",
54
+        "fullscreen": "Enter / Exit Full Screen",
55
+        "sip": "Call SIP number",
56
+        "Settings": "Settings",
57
+        "hangup": "Hang Up"
58
+    },
59
+    "bottomtoolbar": {
60
+        "chat": "Open / close chat",
61
+        "filmstrip": "Open / close contact list",
62
+        "contactlist": "Show / hide film strip"
63
+    },
64
+    "chat":{
65
+        "nickname": {
66
+            "title": "Enter a nickname in the box below",
67
+            "popover": "Choose a nickname"
68
+        },
69
+        "messagebox": "Enter text..."
70
+    },
71
+    "settings":
72
+    {
73
+        "title": "SETTINGS",
74
+        "update": "Update",
75
+        "name": "Name"
76
+    },
77
+    "videothumbnail":
78
+    {
79
+        "editnickname": "Click to edit your<br/>display name",
80
+        "moderator": "The owner of<br/>this conference",
81
+        "videomute": "Participant has<br/>stopped the camera.",
82
+        "mute": "Participant is muted",
83
+        "kick": "Kick out",
84
+        "muted": "Muted",
85
+        "domute": "Mute"
86
+
87
+    },
88
+    "connectionindicator":
89
+    {
90
+        "bitrate": "Bitrate:",
91
+        "packetloss": "Packet loss: ",
92
+        "resolution": "Resolution:",
93
+        "less": "Show less",
94
+        "more": "Show more",
95
+        "address": "Address:",
96
+        "remoteports": "Remote ports:",
97
+        "localports": "Local ports:",
98
+        "remoteport": "Remote port:",
99
+        "localport": "Local port:",
100
+        "localaddress": "Local address: ",
101
+        "localaddresses": "Local addresses: ",
102
+        "remoteaddress": "Remote address: ",
103
+        "remoteaddresses": "Remote addresses: ",
104
+        "transport": "Transport: ",
105
+        "bandwidth": "Estimated bandwidth:",
106
+        "na": "Come back here for connection information once the conference starts"
107
+    },
108
+    "notify": {
109
+        "disconnected": "disconnected",
110
+        "moderator": "Moderator rights granted !",
111
+        "connected": "connected"
112
+
113
+    }
114
+
115
+
116
+
117
+
118
+
119
+
120
+}

+ 2607
- 298
libs/app.bundle.js
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 1
- 9
modules/UI/UI.js ファイルの表示

@@ -340,15 +340,7 @@ UI.start = function (init) {
340 340
         "newestOnTop": false
341 341
     };
342 342
 
343
-    $('#settingsmenu>input').keyup(function(event){
344
-        if(event.keyCode === 13) {//enter
345
-            SettingsMenu.update();
346
-        }
347
-    });
348
-
349
-    $("#updateSettings").click(function () {
350
-        SettingsMenu.update();
351
-    });
343
+    SettingsMenu.init();
352 344
 
353 345
 };
354 346
 

+ 34
- 0
modules/UI/side_pannels/settings/SettingsMenu.js ファイルの表示

@@ -1,10 +1,43 @@
1 1
 var Avatar = require("../../avatar/Avatar");
2 2
 var Settings = require("./Settings");
3 3
 var UIUtil = require("../../util/UIUtil");
4
+var languages = require("../../../../service/translation/languages");
5
+
6
+function generateLanguagesSelectBox()
7
+{
8
+    var currentLang = APP.translation.getCurrentLanguage();
9
+    var html = "<select id=\"languages_selectbox\">";
10
+    var langArray = languages.getLanguages();
11
+    for(var i = 0; i < langArray.length; i++)
12
+    {
13
+        var lang = langArray[i];
14
+        html += "<option ";
15
+        if(lang === currentLang)
16
+            html += "selected ";
17
+        html += "value=\"" + lang + "\" data-i18n='languages:" + lang + "'>";
18
+        html += "</option>";
19
+
20
+    }
21
+
22
+    return html + "</select>";
23
+}
4 24
 
5 25
 
6 26
 var SettingsMenu = {
7 27
 
28
+    init: function () {
29
+        $("#updateSettings").before(generateLanguagesSelectBox());
30
+        $('#settingsmenu>input').keyup(function(event){
31
+            if(event.keyCode === 13) {//enter
32
+                SettingsMenu.update();
33
+            }
34
+        });
35
+
36
+        $("#updateSettings").click(function () {
37
+            SettingsMenu.update();
38
+        });
39
+    },
40
+
8 41
     update: function() {
9 42
         var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
10 43
         var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
@@ -14,6 +47,7 @@ var SettingsMenu = {
14 47
             APP.xmpp.addToPresence("displayName", displayName, true);
15 48
         }
16 49
 
50
+        APP.translation.setLanguage($("#languages_selectbox").val());
17 51
 
18 52
         APP.xmpp.addToPresence("email", newEmail);
19 53
         var email = Settings.setEmail(newEmail);

+ 2
- 2
modules/UI/util/UIUtil.js ファイルの表示

@@ -69,8 +69,8 @@ module.exports = {
69 69
         context.putImageData(imgData, 0, 0);
70 70
     },
71 71
 
72
-    setTooltip: function (element, tooltipText, position) {
73
-        element.setAttribute("data-content", tooltipText);
72
+    setTooltip: function (element, key, position) {
73
+        element.setAttribute("data-i18n", "[data-content]" + key);
74 74
         element.setAttribute("data-toggle", "popover");
75 75
         element.setAttribute("data-placement", position);
76 76
         element.setAttribute("data-html", true);

+ 55
- 34
modules/UI/videolayout/ConnectionIndicator.js ファイルの表示

@@ -64,6 +64,8 @@ ConnectionIndicator.getStringFromArray = function (array) {
64 64
 ConnectionIndicator.prototype.generateText = function () {
65 65
     var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
66 66
 
67
+    var translate = APP.translation.translateString;
68
+
67 69
     if(this.bitrate === null)
68 70
     {
69 71
         downloadBitrate = "N/A";
@@ -145,22 +147,28 @@ ConnectionIndicator.prototype.generateText = function () {
145 147
 
146 148
     var result = "<table style='width:100%'>" +
147 149
         "<tr>" +
148
-        "<td><span class='jitsipopover_blue'>Bitrate:</span></td>" +
150
+        "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.bitrate'>" +
151
+        translate("connectionindicator.bitrate") + "</span></td>" +
149 152
         "<td><span class='jitsipopover_green'>&darr;</span>" +
150 153
         downloadBitrate + " <span class='jitsipopover_orange'>&uarr;</span>" +
151 154
         uploadBitrate + "</td>" +
152 155
         "</tr><tr>" +
153
-        "<td><span class='jitsipopover_blue'>Packet loss: </span></td>" +
156
+        "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.packetloss'>" +
157
+        translate("connectionindicator.packetloss") + "</span></td>" +
154 158
         "<td>" + packetLoss  + "</td>" +
155 159
         "</tr><tr>" +
156
-        "<td><span class='jitsipopover_blue'>Resolution:</span></td>" +
160
+        "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.resolution'>" +
161
+        translate("connectionindicator.resolution") + "</span></td>" +
157 162
         "<td>" + resolution + "</td></tr></table>";
158 163
 
159
-    if(this.videoContainer.id == "localVideoContainer")
164
+    if(this.videoContainer.id == "localVideoContainer") {
160 165
         result += "<div class=\"jitsipopover_showmore\" " +
161 166
             "onclick = \"APP.UI.connectionIndicatorShowMore('" +
162
-            this.videoContainer.id + "')\">" +
163
-            (this.showMoreValue? "Show less" : "Show More") + "</div><br />";
167
+            this.videoContainer.id + "')\"  data-i18n='connectionindicator." +
168
+                (this.showMoreValue ? "less" : "more") + "'>" +
169
+            translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
170
+            "</div><br />";
171
+    }
164 172
 
165 173
     if(this.showMoreValue)
166 174
     {
@@ -183,7 +191,9 @@ ConnectionIndicator.prototype.generateText = function () {
183 191
         if(!this.transport || this.transport.length === 0)
184 192
         {
185 193
             transport = "<tr>" +
186
-                "<td><span class='jitsipopover_blue'>Address:</span></td>" +
194
+                "<td><span class='jitsipopover_blue' " +
195
+                "data-i18n='connectionindicator.address'>" +
196
+                translate("connectionindicator.address") + "</span></td>" +
187 197
                 "<td> N/A</td></tr>";
188 198
         }
189 199
         else
@@ -218,41 +228,49 @@ ConnectionIndicator.prototype.generateText = function () {
218 228
                 }
219 229
 
220 230
             }
231
+
232
+            var local_address_key = "connectionindicator." +
233
+                (data.localIP.length > 1? "localaddresses" : "localaddress");
234
+            var remote_address_key = "connectionindicator." +
235
+                (data.remoteIP.length > 1? "remoteaddresses" : "remoteaddress");
221 236
             var localTransport =
222
-                "<tr><td><span class='jitsipopover_blue'>Local address" +
223
-                (data.localIP.length > 1? "es" : "") + ": </span></td><td> " +
237
+                "<tr><td><span class='jitsipopover_blue' data-i18n='" +
238
+                local_address_key +"'>" +
239
+                    translate(local_address_key) + "</span></td><td> " +
224 240
                 ConnectionIndicator.getStringFromArray(data.localIP) +
225 241
                 "</td></tr>";
226 242
             transport =
227
-                "<tr><td><span class='jitsipopover_blue'>Remote address"+
228
-                (data.remoteIP.length > 1? "es" : "") + ":</span></td><td> " +
243
+                "<tr><td><span class='jitsipopover_blue' data-i18n='" +
244
+                remote_address_key + "'>" +
245
+                    translate(remote_address_key) + "</span></td><td> " +
229 246
                 ConnectionIndicator.getStringFromArray(data.remoteIP) +
230 247
                 "</td></tr>";
248
+
249
+            var key_remote = "connectionindicator.",
250
+                key_local = "connectionindicator.";
251
+
231 252
             if(this.transport.length > 1)
232 253
             {
233
-                transport += "<tr>" +
234
-                    "<td>" +
235
-                    "<span class='jitsipopover_blue'>Remote ports:</span>" +
236
-                    "</td><td>";
237
-                localTransport += "<tr>" +
238
-                    "<td>" +
239
-                    "<span class='jitsipopover_blue'>Local ports:</span>" +
240
-                    "</td><td>";
254
+                key_remote += "remoteports";
255
+                key_local += "localports";
241 256
             }
242 257
             else
243 258
             {
244
-                transport +=
245
-                    "<tr>" +
246
-                    "<td>" +
247
-                    "<span class='jitsipopover_blue'>Remote port:</span>" +
248
-                    "</td><td>";
249
-                localTransport +=
250
-                    "<tr>" +
251
-                    "<td>" +
252
-                    "<span class='jitsipopover_blue'>Local port:</span>" +
253
-                    "</td><td>";
259
+                key_remote += "remoteport";
260
+                key_local += "localport";
254 261
             }
255 262
 
263
+            transport += "<tr>" +
264
+                "<td>" +
265
+                "<span class='jitsipopover_blue' data-i18n='" + key_remote +
266
+                "'>" +
267
+                translate(key_remote) + "</span></td><td>";
268
+            localTransport += "<tr>" +
269
+                "<td>" +
270
+                "<span class='jitsipopover_blue' data-i18n='" + key_local +
271
+                "'>" +
272
+                translate(key_local) + "</span></td><td>";
273
+
256 274
             transport +=
257 275
                 ConnectionIndicator.getStringFromArray(data.remotePort);
258 276
             localTransport +=
@@ -260,7 +278,8 @@ ConnectionIndicator.prototype.generateText = function () {
260 278
             transport += "</td></tr>";
261 279
             transport += localTransport + "</td></tr>";
262 280
             transport +="<tr>" +
263
-                "<td><span class='jitsipopover_blue'>Transport:</span></td>" +
281
+                "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.transport'>" +
282
+                translate("connectionindicator.transport") + "</span></td>" +
264 283
                 "<td>" + this.transport[0].type + "</td></tr>";
265 284
 
266 285
         }
@@ -268,7 +287,8 @@ ConnectionIndicator.prototype.generateText = function () {
268 287
         result += "<table  style='width:100%'>" +
269 288
             "<tr>" +
270 289
             "<td>" +
271
-            "<span class='jitsipopover_blue'>Estimated bandwidth:</span>" +
290
+            "<span class='jitsipopover_blue' data-i18n='connectionindicator.bandwidth'>" +
291
+            translate("connectionindicator.bandwidth") + "</span>" +
272 292
             "</td><td>" +
273 293
             "<span class='jitsipopover_green'>&darr;</span>" +
274 294
             downloadBandwidth +
@@ -313,8 +333,8 @@ ConnectionIndicator.prototype.create = function () {
313 333
     this.videoContainer.appendChild(this.connectionIndicatorContainer);
314 334
     this.popover = new JitsiPopover(
315 335
         $("#" + this.videoContainer.id + " > .connectionindicator"),
316
-        {content: "<div class=\"connection_info\">Come back here for " +
317
-            "connection information once the conference starts</div>",
336
+        {content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
337
+            APP.translation.translateString("connectionindicator.na") + "</div>",
318 338
             skin: "black"});
319 339
 
320 340
     this.emptyIcon = this.connectionIndicatorContainer.appendChild(
@@ -388,7 +408,8 @@ ConnectionIndicator.prototype.updateResolution = function (resolution) {
388 408
  */
389 409
 ConnectionIndicator.prototype.updatePopoverData = function () {
390 410
     this.popover.updateContent(
391
-            "<div class=\"connection_info\">" + this.generateText() + "</div>");
411
+        "<div class=\"connection_info\">" + this.generateText() + "</div>");
412
+    APP.translation.translateElement($(".connection_info"));
392 413
 };
393 414
 
394 415
 /**

+ 27
- 14
modules/UI/videolayout/VideoLayout.js ファイルの表示

@@ -235,6 +235,8 @@ function setDisplayName(videoSpanId, displayName) {
235 235
         } else {
236 236
             nameSpan.id = 'localDisplayName';
237 237
             $('#' + videoSpanId)[0].appendChild(editButton);
238
+            //translates popover of edit button
239
+            APP.translation.translateElement($("a.displayname"));
238 240
 
239 241
             var editableText = document.createElement('input');
240 242
             editableText.className = 'displayname';
@@ -345,14 +347,16 @@ function addRemoteVideoMenu(jid, parentElement) {
345 347
     var muteMenuItem = document.createElement('li');
346 348
     var muteLinkItem = document.createElement('a');
347 349
 
348
-    var mutedIndicator = "<i class='icon-mic-disabled'></i>";
350
+    var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
349 351
 
350 352
     if (!mutedAudios[jid]) {
351
-        muteLinkItem.innerHTML = mutedIndicator + 'Mute';
353
+        muteLinkItem.innerHTML = mutedIndicator +
354
+            " <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
352 355
         muteLinkItem.className = 'mutelink';
353 356
     }
354 357
     else {
355
-        muteLinkItem.innerHTML = mutedIndicator + ' Muted';
358
+        muteLinkItem.innerHTML = mutedIndicator +
359
+            " <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
356 360
         muteLinkItem.className = 'mutelink disabled';
357 361
     }
358 362
 
@@ -366,11 +370,13 @@ function addRemoteVideoMenu(jid, parentElement) {
366 370
         popupmenuElement.setAttribute('style', 'display:none;');
367 371
 
368 372
         if (isMute) {
369
-            this.innerHTML = mutedIndicator + ' Muted';
373
+            this.innerHTML = mutedIndicator +
374
+                " <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
370 375
             this.className = 'mutelink disabled';
371 376
         }
372 377
         else {
373
-            this.innerHTML = mutedIndicator + ' Mute';
378
+            this.innerHTML = mutedIndicator +
379
+                " <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
374 380
             this.className = 'mutelink';
375 381
         }
376 382
     };
@@ -378,11 +384,12 @@ function addRemoteVideoMenu(jid, parentElement) {
378 384
     muteMenuItem.appendChild(muteLinkItem);
379 385
     popupmenuElement.appendChild(muteMenuItem);
380 386
 
381
-    var ejectIndicator = "<i class='fa fa-eject'></i>";
387
+    var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
382 388
 
383 389
     var ejectMenuItem = document.createElement('li');
384 390
     var ejectLinkItem = document.createElement('a');
385
-    ejectLinkItem.innerHTML = ejectIndicator + ' Kick out';
391
+    var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'>&nbsp;</div>";
392
+    ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
386 393
     ejectLinkItem.onclick = function(){
387 394
         APP.xmpp.eject(jid);
388 395
         popupmenuElement.setAttribute('style', 'display:none;');
@@ -394,6 +401,7 @@ function addRemoteVideoMenu(jid, parentElement) {
394 401
     var paddingSpan = document.createElement('span');
395 402
     paddingSpan.className = 'popupmenuPadding';
396 403
     popupmenuElement.appendChild(paddingSpan);
404
+    APP.translation.translateElement($("#" + popupmenuElement.id + " > li > a > div"));
397 405
 }
398 406
 
399 407
 /**
@@ -468,7 +476,7 @@ function createEditDisplayNameButton() {
468 476
     var editButton = document.createElement('a');
469 477
     editButton.className = 'displayname';
470 478
     UIUtil.setTooltip(editButton,
471
-        'Click to edit your<br/>display name',
479
+        "videothumbnail.editnickname",
472 480
         "top");
473 481
     editButton.innerHTML = '<i class="fa fa-pencil"></i>';
474 482
 
@@ -487,7 +495,7 @@ function createModeratorIndicatorElement(parentElement) {
487 495
     parentElement.appendChild(moderatorIndicator);
488 496
 
489 497
     UIUtil.setTooltip(parentElement,
490
-        "The owner of<br/>this conference",
498
+        "videothumbnail.moderator",
491 499
         "top");
492 500
 }
493 501
 
@@ -1043,13 +1051,11 @@ var VideoLayout = (function (my) {
1043 1051
         container.id = spanId;
1044 1052
         container.className = 'videocontainer';
1045 1053
         var remotes = document.getElementById('remoteVideos');
1046
-
1054
+        remotes.appendChild(container);
1047 1055
         // If the peerJid is null then this video span couldn't be directly
1048 1056
         // associated with a participant (this could happen in the case of prezi).
1049 1057
         if (APP.xmpp.isModerator() && peerJid !== null)
1050 1058
             addRemoteVideoMenu(peerJid, container);
1051
-
1052
-        remotes.appendChild(container);
1053 1059
         AudioLevels.updateAudioLevelCanvas(peerJid, VideoLayout);
1054 1060
 
1055 1061
         return container;
@@ -1335,6 +1341,8 @@ var VideoLayout = (function (my) {
1335 1341
             if (indicatorSpan.children().length === 0)
1336 1342
             {
1337 1343
                 createModeratorIndicatorElement(indicatorSpan[0]);
1344
+                //translates text in focus indicator
1345
+                APP.translation.translateElement($('#localVideoContainer .focusindicator'));
1338 1346
             }
1339 1347
         }
1340 1348
 
@@ -1375,6 +1383,8 @@ var VideoLayout = (function (my) {
1375 1383
                     videoContainer.appendChild(indicatorSpan);
1376 1384
 
1377 1385
                     createModeratorIndicatorElement(indicatorSpan);
1386
+                    //translates text in focus indicators
1387
+                    APP.translation.translateElement($('#' + videoSpanId + ' .focusindicator'));
1378 1388
                 }
1379 1389
             } else if (isModerator) {
1380 1390
                 // We are moderator, but user is not - add menu
@@ -1408,9 +1418,11 @@ var VideoLayout = (function (my) {
1408 1418
                 var mutedIndicator = document.createElement('i');
1409 1419
                 mutedIndicator.className = 'icon-camera-disabled';
1410 1420
                 UIUtil.setTooltip(mutedIndicator,
1411
-                    "Participant has<br/>stopped the camera.",
1421
+                    "videothumbnail.videomute",
1412 1422
                     "top");
1413 1423
                 videoMutedSpan.appendChild(mutedIndicator);
1424
+                //translate texts for muted indicator
1425
+                APP.translation.translateElement($('#' + videoSpanId  + " > i"));
1414 1426
             }
1415 1427
 
1416 1428
             VideoLayout.updateMutePosition(videoSpanId);
@@ -1451,10 +1463,11 @@ var VideoLayout = (function (my) {
1451 1463
                 audioMutedSpan = document.createElement('span');
1452 1464
                 audioMutedSpan.className = 'audioMuted';
1453 1465
                 UIUtil.setTooltip(audioMutedSpan,
1454
-                    "Participant is muted",
1466
+                    "videothumbnail.mute",
1455 1467
                     "top");
1456 1468
 
1457 1469
                 $('#' + videoSpanId)[0].appendChild(audioMutedSpan);
1470
+                APP.translation.translateElement($('#' + videoSpanId + " > span"));
1458 1471
                 var mutedIndicator = document.createElement('i');
1459 1472
                 mutedIndicator.className = 'icon-mic-disabled';
1460 1473
                 audioMutedSpan.appendChild(mutedIndicator);

+ 0
- 2
modules/UI/welcome_page/WelcomePage.js ファイルの表示

@@ -37,8 +37,6 @@ function setupWelcomePage()
37 37
     $("#videoconference_page").hide();
38 38
     $("#domain_name").text(
39 39
             window.location.protocol + "//" + window.location.host + "/");
40
-    $("span[name='appName']").text(interfaceConfig.APP_NAME);
41
-
42 40
     if (interfaceConfig.SHOW_JITSI_WATERMARK) {
43 41
         var leftWatermarkDiv
44 42
             = $("#welcome_page_header div[class='watermark leftwatermark']");

+ 110
- 0
modules/translation/translation.js ファイルの表示

@@ -0,0 +1,110 @@
1
+var i18n = require("i18next-client");
2
+var languages = require("../../service/translation/languages");
3
+var DEFAULT_LANG = languages.EN;
4
+var initialized = false;
5
+var waitingForInit = [];
6
+
7
+i18n.addPostProcessor("resolveAppName", function(value, key, options) {
8
+    return value.replace("__app__", interfaceConfig.APP_NAME);
9
+});
10
+
11
+
12
+
13
+var defaultOptions = {
14
+    detectLngQS: "lang",
15
+    useCookie: false,
16
+    fallbackLng: DEFAULT_LANG,
17
+    load: "unspecific",
18
+    resGetPath: 'lang/__ns__-__lng__.json',
19
+    ns: {
20
+        namespaces: ['main', 'languages'],
21
+        defaultNs: 'main'
22
+    },
23
+    lngWhitelist : languages.getLanguages(),
24
+    fallbackOnNull: true,
25
+    useDataAttrOptions: true,
26
+    app: interfaceConfig.APP_NAME,
27
+    getAsync: true,
28
+    customLoad: function(lng, ns, options, done) {
29
+        var resPath = "lang/__ns__-__lng__.json";
30
+        if(lng === languages.EN)
31
+            resPath = "lang/__ns__.json";
32
+        var url = i18n.functions.applyReplacement(resPath, { lng: lng, ns: ns });
33
+        initialized = false;
34
+        i18n.functions.ajax({
35
+            url: url,
36
+            success: function(data, status, xhr) {
37
+                i18n.functions.log('loaded: ' + url);
38
+                done(null, data);
39
+            },
40
+            error : function(xhr, status, error) {
41
+                if ((status && status == 200) ||
42
+                    (xhr && xhr.status && xhr.status == 200)) {
43
+                    // file loaded but invalid json, stop waste time !
44
+                    i18n.functions.error('There is a typo in: ' + url);
45
+                } else if ((status && status == 404) ||
46
+                    (xhr && xhr.status && xhr.status == 404)) {
47
+                    i18n.functions.log('Does not exist: ' + url);
48
+                } else {
49
+                    var theStatus = status ? status :
50
+                        ((xhr && xhr.status) ? xhr.status : null);
51
+                    i18n.functions.log(theStatus + ' when loading ' + url);
52
+                }
53
+
54
+                done(error, {});
55
+            },
56
+            dataType: "json",
57
+            async : options.getAsync
58
+        });
59
+    }
60
+    //              options for caching
61
+//                useLocalStorage: true,
62
+//                localStorageExpirationTime: 86400000 // in ms, default 1 week
63
+};
64
+
65
+function initCompleted(t)
66
+{
67
+    initialized = true;
68
+    $("[data-i18n]").i18n();
69
+    for(var i = 0; i < waitingForInit.length; i++)
70
+    {
71
+        var obj = waitingForInit[i];
72
+        obj.callback(i18n.t(obj.key));
73
+    }
74
+    waitingForInit = [];
75
+}
76
+
77
+module.exports = {
78
+    init: function (lang) {
79
+        initialized = false;
80
+        var options = defaultOptions;
81
+        if(lang)
82
+            options.lng = lang;
83
+        i18n.init(options, initCompleted);
84
+    },
85
+    translateString: function (key, cb) {
86
+        if(!cb)
87
+            return i18n.t(key);
88
+
89
+        if(initialized)
90
+        {
91
+            cb(i18n.t(key));
92
+        }
93
+        else
94
+        {
95
+            waitingForInit.push({"callback": cb, "key": key});
96
+        }
97
+    },
98
+    setLanguage: function (lang) {
99
+        initialized = false;
100
+        if(!lang)
101
+            lang = DEFAULT_LANG;
102
+        i18n.setLng(lang, defaultOptions, initCompleted);
103
+    },
104
+    getCurrentLanguage: function () {
105
+        return i18n.lng();
106
+    },
107
+    translateElement: function (selector) {
108
+        selector.i18n();
109
+    }
110
+};

+ 2
- 1
package.json ファイルの表示

@@ -16,7 +16,8 @@
16 16
   "readmeFilename": "README.md",
17 17
   "dependencies": {
18 18
       "events": "*",
19
-      "pako": "*"
19
+      "pako": "*",
20
+      "i18next-client": "*"
20 21
   },
21 22
   "devDependencies": {
22 23
   },

+ 12
- 0
service/translation/languages.js ファイルの表示

@@ -0,0 +1,12 @@
1
+module.exports = {
2
+    getLanguages : function () {
3
+        var languages = [];
4
+        for(var lang in this)
5
+        {
6
+            if(typeof this[lang] === "string")
7
+                languages.push(this[lang]);
8
+        }
9
+        return languages;
10
+    },
11
+    EN: "en"
12
+}

読み込み中…
キャンセル
保存