Browse Source

Translate react strings.

Split language detectors to be web/native dependent. Take care of strings that contain html.
j8
damencho 8 years ago
parent
commit
c361e1e31a

+ 15
- 1
lang/main.json View File

40
     },
40
     },
41
     "welcomepage":{
41
     "welcomepage":{
42
         "go": "GO",
42
         "go": "GO",
43
+        "join": "JOIN",
43
         "roomname": "Enter room name",
44
         "roomname": "Enter room name",
45
+        "roomnamePlaceHolder": "room name",
44
         "disable": "Don't show this page again",
46
         "disable": "Don't show this page again",
45
         "feature1": {
47
         "feature1": {
46
             "title": "Simple to use",
48
             "title": "Simple to use",
73
         "feature8": {
75
         "feature8": {
74
             "title": "Usage statistics",
76
             "title": "Usage statistics",
75
             "content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems."
77
             "content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems."
76
-        }
78
+        },
79
+        "privacy": "Privacy",
80
+        "sendFeedback": "Send feedback",
81
+        "terms": "Terms"
77
     },
82
     },
78
     "startupoverlay": {
83
     "startupoverlay": {
79
         "policyText": " ",
84
         "policyText": " ",
110
         "profile": "Edit your profile",
115
         "profile": "Edit your profile",
111
         "raiseHand": "Raise / Lower your hand"
116
         "raiseHand": "Raise / Lower your hand"
112
     },
117
     },
118
+    "unsupportedPage": {
119
+        "onlySupportedBy": "This application is currently only supported by",
120
+        "download": "DOWNLOAD",
121
+        "joinConversation": "Join the conversation",
122
+        "startConference": "Start a conference",
123
+        "joinConversationMobile": "You need <strong>__app__</strong> to join a conversation on your mobile",
124
+        "downloadApp": "Download the App",
125
+        "availableApp": "or if you already have it<br /><strong>then</strong>"
126
+    },
113
     "bottomtoolbar": {
127
     "bottomtoolbar": {
114
         "chat": "Open / close chat",
128
         "chat": "Open / close chat",
115
         "filmstrip": "Show / hide videos",
129
         "filmstrip": "Show / hide videos",

+ 2
- 1
package.json View File

21
     "autosize": "^1.18.13",
21
     "autosize": "^1.18.13",
22
     "bootstrap": "3.1.1",
22
     "bootstrap": "3.1.1",
23
     "i18next": "7.0.0",
23
     "i18next": "7.0.0",
24
-    "i18next-browser-languagedetector": "*",
24
+    "i18next-browser-languagedetector": "1.0.1",
25
     "i18next-xhr-backend": "1.3.0",
25
     "i18next-xhr-backend": "1.3.0",
26
     "jitsi-meet-logger": "jitsi/jitsi-meet-logger",
26
     "jitsi-meet-logger": "jitsi/jitsi-meet-logger",
27
     "jquery": "~2.1.1",
27
     "jquery": "~2.1.1",
40
     "react-native-background-timer": "1.0.0",
40
     "react-native-background-timer": "1.0.0",
41
     "react-native-immersive": "0.0.4",
41
     "react-native-immersive": "0.0.4",
42
     "react-native-keep-awake": "^2.0.2",
42
     "react-native-keep-awake": "^2.0.2",
43
+    "react-native-locale-detector": "1.0.1 ",
43
     "react-native-prompt": "^1.0.0",
44
     "react-native-prompt": "^1.0.0",
44
     "react-native-vector-icons": "^4.0.0",
45
     "react-native-vector-icons": "^4.0.0",
45
     "react-native-webrtc": "jitsi/react-native-webrtc",
46
     "react-native-webrtc": "jitsi/react-native-webrtc",

+ 7
- 2
react/features/base/react/components/Watermarks.web.js View File

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
+import { translate } from '../../translation';
4
 
5
 
5
 declare var APP: Object;
6
 declare var APP: Object;
6
 declare var interfaceConfig: Object;
7
 declare var interfaceConfig: Object;
18
  * A Web Component which renders watermarks such as Jits, brand, powered by,
19
  * A Web Component which renders watermarks such as Jits, brand, powered by,
19
  * etc.
20
  * etc.
20
  */
21
  */
21
-export class Watermarks extends Component {
22
+class WatermarksComponent extends Component {
22
     state = {
23
     state = {
23
         brandWatermarkLink: String,
24
         brandWatermarkLink: String,
24
         jitsiWatermarkLink: String,
25
         jitsiWatermarkLink: String,
139
      */
140
      */
140
     _renderPoweredBy() {
141
     _renderPoweredBy() {
141
         if (this.state.showPoweredBy) {
142
         if (this.state.showPoweredBy) {
143
+            const { t } = this.props;
144
+
142
             return (
145
             return (
143
                 <a
146
                 <a
144
                     className = 'poweredby'
147
                     className = 'poweredby'
145
                     href = 'http://jitsi.org'
148
                     href = 'http://jitsi.org'
146
                     target = '_new'>
149
                     target = '_new'>
147
-                    <span data-i18n = 'poweredby' /> jitsi.org
150
+                    <span>{t('poweredby')} jitsi.org</span>
148
                 </a>
151
                 </a>
149
             );
152
             );
150
         }
153
         }
152
         return null;
155
         return null;
153
     }
156
     }
154
 }
157
 }
158
+
159
+export const Watermarks = translate(WatermarksComponent);

+ 1
- 1
react/features/base/translation/ConfigLanguageDetector.js View File

12
     /**
12
     /**
13
      * The actual lookup.
13
      * The actual lookup.
14
      *
14
      *
15
-     * @returns {string} the default language if any.
15
+     * @returns {string} The default language if any.
16
      */
16
      */
17
     lookup() {
17
     lookup() {
18
         return config.defaultLanguage;
18
         return config.defaultLanguage;

+ 11
- 0
react/features/base/translation/LanguageDetector.native.js View File

1
+import locale from 'react-native-locale-detector';
2
+
3
+/**
4
+ * A language detector that uses native locale.
5
+ */
6
+export default {
7
+    init: Function.prototype,
8
+    type: 'languageDetector',
9
+    detect: () => locale,
10
+    cacheUserLanguage: Function.prototype
11
+};

+ 34
- 0
react/features/base/translation/LanguageDetector.web.js View File

1
+/* global interfaceConfig */
2
+import Browser from 'i18next-browser-languagedetector';
3
+import ConfigLanguageDetector from './ConfigLanguageDetector';
4
+
5
+/**
6
+ * List of detectors to use in their order.
7
+ *
8
+ * @type {[*]}
9
+ */
10
+const detectors = [ 'querystring', 'localStorage', 'configLanguageDetector' ];
11
+
12
+/**
13
+ * Allow i18n to detect the system language from the browser.
14
+ */
15
+if (interfaceConfig.LANG_DETECTION) {
16
+    detectors.push('navigator');
17
+}
18
+
19
+/**
20
+ * The language detectors.
21
+ */
22
+const browser = new Browser(null, {
23
+    order: detectors,
24
+    lookupQuerystring: 'lang',
25
+    lookupLocalStorage: 'language',
26
+    caches: [ 'localStorage' ]
27
+});
28
+
29
+/**
30
+ * adds a language detector that just checks the config
31
+ */
32
+browser.addDetector(ConfigLanguageDetector);
33
+
34
+export default browser;

+ 5
- 31
react/features/base/translation/Translation.js View File

4
 import { DEFAULT_LANG, languages } from './constants';
4
 import { DEFAULT_LANG, languages } from './constants';
5
 import languagesR from '../../../../lang/languages.json';
5
 import languagesR from '../../../../lang/languages.json';
6
 import mainR from '../../../../lang/main.json';
6
 import mainR from '../../../../lang/main.json';
7
-import Browser from 'i18next-browser-languagedetector';
8
-import ConfigLanguageDetector from './ConfigLanguageDetector';
7
+
8
+import LanguageDetector from './LanguageDetector';
9
 
9
 
10
 /**
10
 /**
11
  * Default options to initialize i18next.
11
  * Default options to initialize i18next.
26
     fallbackOnNull: true,
26
     fallbackOnNull: true,
27
     fallbackOnEmpty: true,
27
     fallbackOnEmpty: true,
28
     useDataAttrOptions: true,
28
     useDataAttrOptions: true,
29
-    app: interfaceConfig.APP_NAME
29
+    app: typeof interfaceConfig === 'undefined'
30
+        ? 'Jitsi Meet' : interfaceConfig.APP_NAME
30
 };
31
 };
31
 
32
 
32
-/**
33
- * List of detectors to use in their order.
34
- *
35
- * @type {[*]}
36
- */
37
-const detectors = [ 'querystring', 'localStorage', 'configLanguageDetector' ];
38
-
39
-/**
40
- * Allow i18n to detect the system language from the browser.
41
- */
42
-if (interfaceConfig.LANG_DETECTION) {
43
-    detectors.push('navigator');
44
-}
45
-
46
-/**
47
- * The language detectors.
48
- */
49
-const browser = new Browser(null, {
50
-    order: detectors,
51
-    lookupQuerystring: 'lang',
52
-    lookupLocalStorage: 'language',
53
-    caches: [ 'localStorage' ]
54
-});
55
-
56
-// adds a language detector that just checks the config
57
-browser.addDetector(ConfigLanguageDetector);
58
-
59
 i18n.use(XHR)
33
 i18n.use(XHR)
60
-    .use(browser)
34
+    .use(LanguageDetector)
61
     .use({
35
     .use({
62
         type: 'postProcessor',
36
         type: 'postProcessor',
63
         name: 'resolveAppName',
37
         name: 'resolveAppName',

+ 22
- 2
react/features/base/translation/functions.js View File

1
 import { translate as reactTranslate } from 'react-i18next';
1
 import { translate as reactTranslate } from 'react-i18next';
2
+import React from 'react';
2
 
3
 
3
 /**
4
 /**
4
  * Wrap a translatable component.
5
  * Wrap a translatable component.
5
  *
6
  *
6
- * @param {Component} component - the component to wrap
7
- * @returns {Component} the wrapped component.
7
+ * @param {Component} component - The component to wrap.
8
+ * @returns {Component} The wrapped component.
8
  */
9
  */
9
 export function translate(component) {
10
 export function translate(component) {
10
     // use the default list of namespaces
11
     // use the default list of namespaces
11
     return reactTranslate([ 'main', 'languages' ], { wait: true })(component);
12
     return reactTranslate([ 'main', 'languages' ], { wait: true })(component);
12
 }
13
 }
14
+
15
+/**
16
+ * Translates key and prepares data to be passed to dangerouslySetInnerHTML.
17
+ * Used when translation text contains html.
18
+ *
19
+ * @param {func} t - Translate function.
20
+ * @param {string} key - The key to translate.
21
+ * @param {Array} options - Optional options.
22
+ * @returns {XML} A span using dangerouslySetInnerHTML to insert html text.
23
+ */
24
+export function translateToHTML(t, key, options = {}) {
25
+    /* eslint-disable react/no-danger */
26
+    return (
27
+        <span
28
+            dangerouslySetInnerHTML = {{ __html: t(key, options) }} />
29
+    );
30
+
31
+    /* eslint-enable react/no-danger */
32
+}

+ 0
- 3
react/features/conference/components/Conference.web.js View File

51
         APP.UI.registerListeners();
51
         APP.UI.registerListeners();
52
         APP.UI.bindEvents();
52
         APP.UI.bindEvents();
53
 
53
 
54
-        // XXX Temporary solution until we add React translation.
55
-        APP.translation.translateElement($('#videoconference_page'));
56
-
57
         this.props.dispatch(connect());
54
         this.props.dispatch(connect());
58
     }
55
     }
59
 
56
 

+ 9
- 4
react/features/conference/components/PasswordRequiredPrompt.native.js View File

4
 
4
 
5
 import { setPassword } from '../../base/conference';
5
 import { setPassword } from '../../base/conference';
6
 
6
 
7
+import { translate } from '../../base/translation';
8
+
7
 /**
9
 /**
8
  * Implements a React Component which prompts the user when a password is
10
  * Implements a React Component which prompts the user when a password is
9
  * required to join a conference.
11
  * required to join a conference.
21
          * @type {JitsiConference}
23
          * @type {JitsiConference}
22
          */
24
          */
23
         conference: React.PropTypes.object,
25
         conference: React.PropTypes.object,
24
-        dispatch: React.PropTypes.func
26
+        dispatch: React.PropTypes.func,
27
+        t: React.PropTypes.func
25
     }
28
     }
26
 
29
 
27
     /**
30
     /**
45
      * @returns {ReactElement}
48
      * @returns {ReactElement}
46
      */
49
      */
47
     render() {
50
     render() {
51
+        const { t } = this.props;
52
+
48
         return (
53
         return (
49
             <Prompt
54
             <Prompt
50
                 onCancel = { this._onCancel }
55
                 onCancel = { this._onCancel }
51
                 onSubmit = { this._onSubmit }
56
                 onSubmit = { this._onSubmit }
52
-                placeholder = 'Password'
53
-                title = 'Password required'
57
+                placeholder = { t('dialog.passwordLabel') }
58
+                title = { t('dialog.passwordRequired') }
54
                 visible = { true } />
59
                 visible = { true } />
55
         );
60
         );
56
     }
61
     }
84
     }
89
     }
85
 }
90
 }
86
 
91
 
87
-export default connect()(PasswordRequiredPrompt);
92
+export default translate(connect()(PasswordRequiredPrompt));

+ 1
- 12
react/features/overlay/components/AbstractOverlay.js View File

1
-/* global $, APP */
1
+/* global APP */
2
 
2
 
3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 
4
 
30
         };
30
         };
31
     }
31
     }
32
 
32
 
33
-    /**
34
-     * This method is executed when comonent is mounted.
35
-     *
36
-     * @inheritdoc
37
-     * @returns {void}
38
-     */
39
-    componentDidMount() {
40
-        // XXX Temporary solution until we add React translation.
41
-        APP.translation.translateElement($('#overlay'));
42
-    }
43
-
44
     /**
33
     /**
45
      * Implements React's {@link Component#render()}.
34
      * Implements React's {@link Component#render()}.
46
      *
35
      *

+ 16
- 7
react/features/overlay/components/PageReloadOverlay.js View File

5
 import AbstractOverlay from './AbstractOverlay';
5
 import AbstractOverlay from './AbstractOverlay';
6
 import ReloadTimer from './ReloadTimer';
6
 import ReloadTimer from './ReloadTimer';
7
 
7
 
8
+import { translate } from '../../base/translation';
9
+
8
 declare var APP: Object;
10
 declare var APP: Object;
9
 
11
 
10
 const logger = require('jitsi-meet-logger').getLogger(__filename);
12
 const logger = require('jitsi-meet-logger').getLogger(__filename);
14
  * conference is reloaded. Shows a warning message and counts down towards the
16
  * conference is reloaded. Shows a warning message and counts down towards the
15
  * reload.
17
  * reload.
16
  */
18
  */
17
-export default class PageReloadOverlay extends AbstractOverlay {
19
+class PageReloadOverlay extends AbstractOverlay {
18
     /**
20
     /**
19
      * PageReloadOverlay component's property types.
21
      * PageReloadOverlay component's property types.
20
      *
22
      *
134
         if (this.props.isNetworkFailure) {
136
         if (this.props.isNetworkFailure) {
135
             const className
137
             const className
136
                 = 'button-control button-control_primary button-control_center';
138
                 = 'button-control button-control_primary button-control_center';
139
+            const { t } = this.props;
137
 
140
 
138
             /* eslint-disable react/jsx-handler-names */
141
             /* eslint-disable react/jsx-handler-names */
139
 
142
 
140
             return (
143
             return (
141
                 <button
144
                 <button
142
                     className = { className }
145
                     className = { className }
143
-                    data-i18n = 'dialog.reconnectNow'
144
                     id = 'reconnectNow'
146
                     id = 'reconnectNow'
145
-                    onClick = { this._reconnectNow } />
147
+                    onClick = { this._reconnectNow }>
148
+                    { t('dialog.reconnectNow') }
149
+                </button>
146
             );
150
             );
147
 
151
 
148
 
152
 
161
      * @protected
165
      * @protected
162
      */
166
      */
163
     _renderOverlayContent() {
167
     _renderOverlayContent() {
168
+        const { t } = this.props;
164
 
169
 
165
         /* eslint-disable react/jsx-handler-names */
170
         /* eslint-disable react/jsx-handler-names */
166
 
171
 
167
         return (
172
         return (
168
             <div className = 'inlay'>
173
             <div className = 'inlay'>
169
                 <span
174
                 <span
170
-                    className = 'reload_overlay_title'
171
-                    data-i18n = { this.state.title } />
175
+                    className = 'reload_overlay_title'>
176
+                    { t(this.state.title) }
177
+                </span>
172
                 <span
178
                 <span
173
-                    className = 'reload_overlay_text'
174
-                    data-i18n = { this.state.message } />
179
+                    className = 'reload_overlay_text'>
180
+                    { t(this.state.message) }
181
+                </span>
175
                 <ReloadTimer
182
                 <ReloadTimer
176
                     end = { 0 }
183
                     end = { 0 }
177
                     interval = { 1 }
184
                     interval = { 1 }
185
         /* eslint-enable react/jsx-handler-names */
192
         /* eslint-enable react/jsx-handler-names */
186
     }
193
     }
187
 }
194
 }
195
+
196
+export default translate(PageReloadOverlay);

+ 19
- 3
react/features/overlay/components/ReloadTimer.js View File

1
 import React, { Component } from 'react';
1
 import React, { Component } from 'react';
2
 
2
 
3
+import { translate } from '../../base/translation';
4
+
3
 declare var AJS: Object;
5
 declare var AJS: Object;
4
 
6
 
5
 /**
7
 /**
8
  * seconds until the current value reaches props.end. Also displays progress
10
  * seconds until the current value reaches props.end. Also displays progress
9
  * bar.
11
  * bar.
10
  */
12
  */
11
-export default class ReloadTimer extends Component {
13
+class ReloadTimer extends Component {
12
     /**
14
     /**
13
      * ReloadTimer component's property types.
15
      * ReloadTimer component's property types.
14
      *
16
      *
52
          * @public
54
          * @public
53
          * @type {number}
55
          * @type {number}
54
          */
56
          */
55
-        step: React.PropTypes.number
57
+        step: React.PropTypes.number,
58
+
59
+        /**
60
+         * The function used to translate strings.
61
+         *
62
+         * @public
63
+         * @type {func}
64
+         */
65
+        t: React.PropTypes.func
56
     }
66
     }
57
 
67
 
58
     /**
68
     /**
132
      * @public
142
      * @public
133
      */
143
      */
134
     render() {
144
     render() {
145
+        const { t } = this.props;
146
+
135
         return (
147
         return (
136
             <div>
148
             <div>
137
                 <div
149
                 <div
143
                     {
155
                     {
144
                         this.state.current
156
                         this.state.current
145
                     }
157
                     }
146
-                    <span data-i18n = 'dialog.conferenceReloadTimeLeft' />
158
+                    <span>
159
+                        { t('dialog.conferenceReloadTimeLeft') }
160
+                    </span>
147
                 </span>
161
                 </span>
148
             </div>
162
             </div>
149
         );
163
         );
150
     }
164
     }
151
 }
165
 }
166
+
167
+export default translate(ReloadTimer);

+ 12
- 5
react/features/overlay/components/SuspendedOverlay.js View File

2
 
2
 
3
 import AbstractOverlay from './AbstractOverlay';
3
 import AbstractOverlay from './AbstractOverlay';
4
 
4
 
5
+import { translate } from '../../base/translation';
6
+
5
 /**
7
 /**
6
  * Implements a React Component for suspended overlay. Shown when a suspend is
8
  * Implements a React Component for suspended overlay. Shown when a suspend is
7
  * detected.
9
  * detected.
8
  */
10
  */
9
-export default class SuspendedOverlay extends AbstractOverlay {
11
+class SuspendedOverlay extends AbstractOverlay {
10
     /**
12
     /**
11
      * Constructs overlay body with the message and a button to rejoin.
13
      * Constructs overlay body with the message and a button to rejoin.
12
      *
14
      *
16
      */
18
      */
17
     _renderOverlayContent() {
19
     _renderOverlayContent() {
18
         const btnClass = 'inlay__button button-control button-control_primary';
20
         const btnClass = 'inlay__button button-control button-control_primary';
21
+        const { t } = this.props;
19
 
22
 
20
         /* eslint-disable react/jsx-handler-names */
23
         /* eslint-disable react/jsx-handler-names */
21
 
24
 
24
                 <span className = 'inlay__icon icon-microphone' />
27
                 <span className = 'inlay__icon icon-microphone' />
25
                 <span className = 'inlay__icon icon-camera' />
28
                 <span className = 'inlay__icon icon-camera' />
26
                 <h3
29
                 <h3
27
-                    className = 'inlay__title'
28
-                    data-i18n = 'suspendedoverlay.title' />
30
+                    className = 'inlay__title'>
31
+                    { t('suspendedoverlay.title') }
32
+                </h3>
29
                 <button
33
                 <button
30
                     className = { btnClass }
34
                     className = { btnClass }
31
-                    data-i18n = 'suspendedoverlay.rejoinKeyTitle'
32
-                    onClick = { this._reconnectNow } />
35
+                    onClick = { this._reconnectNow }>
36
+                    { t('suspendedoverlay.rejoinKeyTitle') }
37
+                </button>
33
             </div>
38
             </div>
34
         );
39
         );
35
 
40
 
36
         /* eslint-enable react/jsx-handler-names */
41
         /* eslint-enable react/jsx-handler-names */
37
     }
42
     }
38
 }
43
 }
44
+
45
+export default translate(SuspendedOverlay);

+ 17
- 13
react/features/overlay/components/UserMediaPermissionsOverlay.js View File

4
 
4
 
5
 import AbstractOverlay from './AbstractOverlay';
5
 import AbstractOverlay from './AbstractOverlay';
6
 
6
 
7
+import { translate, translateToHTML } from '../../base/translation';
8
+
7
 /**
9
 /**
8
  * Implements a React Component for overlay with guidance how to proceed with
10
  * Implements a React Component for overlay with guidance how to proceed with
9
  * gUM prompt.
11
  * gUM prompt.
10
  */
12
  */
11
-export default class UserMediaPermissionsOverlay extends AbstractOverlay {
13
+class UserMediaPermissionsOverlay extends AbstractOverlay {
12
     /**
14
     /**
13
      * UserMediaPermissionsOverlay component's property types.
15
      * UserMediaPermissionsOverlay component's property types.
14
      *
16
      *
54
      * @protected
56
      * @protected
55
      */
57
      */
56
     _renderOverlayContent() {
58
     _renderOverlayContent() {
57
-        const textKey = `userMedia.${this.props.browser}GrantPermissions`;
59
+        const { t } = this.props;
58
 
60
 
59
         return (
61
         return (
60
             <div>
62
             <div>
61
                 <div className = 'inlay'>
63
                 <div className = 'inlay'>
62
                     <span className = 'inlay__icon icon-microphone' />
64
                     <span className = 'inlay__icon icon-microphone' />
63
                     <span className = 'inlay__icon icon-camera' />
65
                     <span className = 'inlay__icon icon-camera' />
64
-                    <h3
65
-                        className = 'inlay__title'
66
-                        data-i18n = 'startupoverlay.title'
67
-                        data-i18n-options
68
-                            = '{"postProcess": "resolveAppName"}' />
69
-                    <span
70
-                        className = 'inlay__text'
71
-                        data-i18n = { `[html]${textKey}` } />
66
+                    <h3 className = 'inlay__title'>
67
+                        { t('startupoverlay.title',
68
+                            { postProcess: 'resolveAppName' }) }
69
+                    </h3>
70
+                    <span className = 'inlay__text'>
71
+                        { translateToHTML(t,
72
+                            `userMedia.${this.props.browser}GrantPermissions`)}
73
+                    </span>
72
                 </div>
74
                 </div>
73
                 <div className = 'policy overlay__policy'>
75
                 <div className = 'policy overlay__policy'>
74
-                    <p
75
-                        className = 'policy__text'
76
-                        data-i18n = '[html]startupoverlay.policyText' />
76
+                    <p className = 'policy__text'>
77
+                        { t('startupoverlay.policyText') }
78
+                    </p>
77
                     {
79
                     {
78
                         this._renderPolicyLogo()
80
                         this._renderPolicyLogo()
79
                     }
81
                     }
102
         return null;
104
         return null;
103
     }
105
     }
104
 }
106
 }
107
+
108
+export default translate(UserMediaPermissionsOverlay);

+ 9
- 4
react/features/room-lock/components/RoomLockPrompt.native.js View File

4
 
4
 
5
 import { endRoomLockRequest } from '../actions';
5
 import { endRoomLockRequest } from '../actions';
6
 
6
 
7
+import { translate } from '../../base/translation';
8
+
7
 /**
9
 /**
8
  * Implements a React Component which prompts the user for a password to lock  a
10
  * Implements a React Component which prompts the user for a password to lock  a
9
  * conference/room.
11
  * conference/room.
21
          * @type {JitsiConference}
23
          * @type {JitsiConference}
22
          */
24
          */
23
         conference: React.PropTypes.object,
25
         conference: React.PropTypes.object,
24
-        dispatch: React.PropTypes.func
26
+        dispatch: React.PropTypes.func,
27
+        t: React.PropTypes.func
25
     }
28
     }
26
 
29
 
27
     /**
30
     /**
45
      * @returns {ReactElement}
48
      * @returns {ReactElement}
46
      */
49
      */
47
     render() {
50
     render() {
51
+        const { t } = this.props;
52
+
48
         return (
53
         return (
49
             <Prompt
54
             <Prompt
50
                 onCancel = { this._onCancel }
55
                 onCancel = { this._onCancel }
51
                 onSubmit = { this._onSubmit }
56
                 onSubmit = { this._onSubmit }
52
-                placeholder = 'Password'
53
-                title = 'Lock / Unlock room'
57
+                placeholder = { t('dialog.passwordLabel') }
58
+                title = { t('toolbar.lock') }
54
                 visible = { true } />
59
                 visible = { true } />
55
         );
60
         );
56
     }
61
     }
80
     }
85
     }
81
 }
86
 }
82
 
87
 
83
-export default connect()(RoomLockPrompt);
88
+export default translate(connect()(RoomLockPrompt));

+ 12
- 1
react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js View File

3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 
4
 
5
 import { Platform } from '../../base/react';
5
 import { Platform } from '../../base/react';
6
+import { translate } from '../../base/translation';
6
 
7
 
7
 import { CHROME, FIREFOX, IE, SAFARI } from './browserLinks';
8
 import { CHROME, FIREFOX, IE, SAFARI } from './browserLinks';
8
 import HideNotificationBarStyle from './HideNotificationBarStyle';
9
 import HideNotificationBarStyle from './HideNotificationBarStyle';
20
  *
21
  *
21
  * @class UnsupportedDesktopBrowser
22
  * @class UnsupportedDesktopBrowser
22
  */
23
  */
23
-export default class UnsupportedDesktopBrowser extends Component {
24
+class UnsupportedDesktopBrowser extends Component {
25
+    /**
26
+     * UnsupportedDesktopBrowser component's property types.
27
+     *
28
+     * @static
29
+     */
30
+    static propTypes = {
31
+        t: React.PropTypes.func
32
+    }
33
+
24
     /**
34
     /**
25
      * Renders the component.
35
      * Renders the component.
26
      *
36
      *
87
     }
97
     }
88
 }
98
 }
89
 
99
 
100
+export default translate(UnsupportedDesktopBrowser);

+ 13
- 12
react/features/unsupported-browser/components/UnsupportedMobileBrowser.js View File

4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
 import { Platform } from '../../base/react';
6
 import { Platform } from '../../base/react';
7
+import { translate, translateToHTML } from '../../base/translation';
7
 
8
 
8
 import HideNotificationBarStyle from './HideNotificationBarStyle';
9
 import HideNotificationBarStyle from './HideNotificationBarStyle';
9
 
10
 
39
          * @private
40
          * @private
40
          * @type {string}
41
          * @type {string}
41
          */
42
          */
42
-        _room: React.PropTypes.string
43
+        _room: React.PropTypes.string,
44
+        t: React.PropTypes.func
43
     }
45
     }
44
 
46
 
45
     /**
47
     /**
50
      */
52
      */
51
     componentWillMount() {
53
     componentWillMount() {
52
         const joinText
54
         const joinText
53
-            = this.props._room ? 'Join the conversation' : 'Start a conference';
55
+            = this.props._room ? 'unsupportedPage.joinConversation'
56
+                : 'unsupportedPage.startConference';
54
 
57
 
55
         // If the user installed the app while this Component was displayed
58
         // If the user installed the app while this Component was displayed
56
         // (e.g. the user clicked the Download the App button), then we would
59
         // (e.g. the user clicked the Download the App button), then we would
74
     render() {
77
     render() {
75
         const ns = 'unsupported-mobile-browser';
78
         const ns = 'unsupported-mobile-browser';
76
         const downloadButtonClassName = `${ns}__button ${ns}__button_primary`;
79
         const downloadButtonClassName = `${ns}__button ${ns}__button_primary`;
80
+        const { t } = this.props;
77
 
81
 
78
         return (
82
         return (
79
             <div className = { ns }>
83
             <div className = { ns }>
82
                         className = { `${ns}__logo` }
86
                         className = { `${ns}__logo` }
83
                         src = 'images/logo-blue.svg' />
87
                         src = 'images/logo-blue.svg' />
84
                     <p className = { `${ns}__text` }>
88
                     <p className = { `${ns}__text` }>
85
-                        You need <strong>Jitsi Meet</strong> to join a
86
-                        conversation on mobile
89
+                        { translateToHTML(t,
90
+                            'unsupportedPage.joinConversationMobile',
91
+                            { postProcess: 'resolveAppName' }) }
87
                     </p>
92
                     </p>
88
                     <a href = { _URLS[Platform.OS] }>
93
                     <a href = { _URLS[Platform.OS] }>
89
                         <button className = { downloadButtonClassName }>
94
                         <button className = { downloadButtonClassName }>
90
-                            Download the App
95
+                            { t('unsupportedPage.downloadApp') }
91
                         </button>
96
                         </button>
92
                     </a>
97
                     </a>
93
                     <p className = { `${ns}__text ${ns}__text_small` }>
98
                     <p className = { `${ns}__text ${ns}__text_small` }>
94
-                        or if you already have it
95
-                        <br />
96
-                        <strong>then</strong>
99
+                        { translateToHTML(t, 'unsupportedPage.availableApp') }
97
                     </p>
100
                     </p>
98
                     <a href = { this.state.joinURL }>
101
                     <a href = { this.state.joinURL }>
99
                         <button className = { `${ns}__button` }>
102
                         <button className = { `${ns}__button` }>
100
-                            {
101
-                                this.state.joinText
102
-                            }
103
+                            { t(this.state.joinText) }
103
                         </button>
104
                         </button>
104
                     </a>
105
                     </a>
105
                 </div>
106
                 </div>
133
     };
134
     };
134
 }
135
 }
135
 
136
 
136
-export default connect(_mapStateToProps)(UnsupportedMobileBrowser);
137
+export default translate(connect(_mapStateToProps)(UnsupportedMobileBrowser));

+ 17
- 7
react/features/welcome/components/WelcomePage.native.js View File

8
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
8
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
9
 import { styles } from './styles';
9
 import { styles } from './styles';
10
 
10
 
11
+import { translate } from '../../base/translation';
12
+
11
 /**
13
 /**
12
  * The URL at which the privacy policy is available to the user.
14
  * The URL at which the privacy policy is available to the user.
13
  */
15
  */
62
      * @returns {ReactElement}
64
      * @returns {ReactElement}
63
      */
65
      */
64
     _renderLegalese() {
66
     _renderLegalese() {
67
+        const { t } = this.props;
68
+
65
         return (
69
         return (
66
             <View style = { styles.legaleseContainer }>
70
             <View style = { styles.legaleseContainer }>
67
                 <Link
71
                 <Link
68
                     style = { styles.legaleseItem }
72
                     style = { styles.legaleseItem }
69
                     url = { TERMS_URL }>
73
                     url = { TERMS_URL }>
70
-                    Terms
74
+                    { t('welcomepage.terms') }
71
                 </Link>
75
                 </Link>
72
                 <Link
76
                 <Link
73
                     style = { styles.legaleseItem }
77
                     style = { styles.legaleseItem }
74
                     url = { PRIVACY_URL }>
78
                     url = { PRIVACY_URL }>
75
-                    Privacy
79
+                    { t('welcomepage.privacy') }
76
                 </Link>
80
                 </Link>
77
                 <Link
81
                 <Link
78
                     style = { styles.legaleseItem }
82
                     style = { styles.legaleseItem }
79
                     url = { SEND_FEEDBACK_URL }>
83
                     url = { SEND_FEEDBACK_URL }>
80
-                    Send feedback
84
+                    { t('welcomepage.sendFeedback') }
81
                 </Link>
85
                 </Link>
82
             </View>
86
             </View>
83
         );
87
         );
93
      * @returns {ReactElement}
97
      * @returns {ReactElement}
94
      */
98
      */
95
     _renderLocalVideoOverlay() {
99
     _renderLocalVideoOverlay() {
100
+        const { t } = this.props;
101
+
96
         return (
102
         return (
97
             <View style = { styles.localVideoOverlay }>
103
             <View style = { styles.localVideoOverlay }>
98
                 <View style = { styles.roomContainer }>
104
                 <View style = { styles.roomContainer }>
99
-                    <Text style = { styles.title }>Enter room name</Text>
105
+                    <Text style = { styles.title }>
106
+                        { t('welcomepage.roomname') }
107
+                    </Text>
100
                     <TextInput
108
                     <TextInput
101
                         accessibilityLabel = { 'Input room name.' }
109
                         accessibilityLabel = { 'Input room name.' }
102
                         autoCapitalize = 'none'
110
                         autoCapitalize = 'none'
104
                         autoCorrect = { false }
112
                         autoCorrect = { false }
105
                         autoFocus = { false }
113
                         autoFocus = { false }
106
                         onChangeText = { this._onRoomChange }
114
                         onChangeText = { this._onRoomChange }
107
-                        placeholder = 'room name'
115
+                        placeholder = { t('welcomepage.roomnamePlaceHolder') }
108
                         style = { styles.textInput }
116
                         style = { styles.textInput }
109
                         underlineColorAndroid = 'transparent'
117
                         underlineColorAndroid = 'transparent'
110
                         value = { this.state.room } />
118
                         value = { this.state.room } />
114
                         onPress = { this._onJoin }
122
                         onPress = { this._onJoin }
115
                         style = { styles.button }
123
                         style = { styles.button }
116
                         underlayColor = { ColorPalette.white }>
124
                         underlayColor = { ColorPalette.white }>
117
-                        <Text style = { styles.buttonText }>JOIN</Text>
125
+                        <Text style = { styles.buttonText }>
126
+                            { t('welcomepage.join') }
127
+                        </Text>
118
                     </TouchableHighlight>
128
                     </TouchableHighlight>
119
                 </View>
129
                 </View>
120
                 {
130
                 {
125
     }
135
     }
126
 }
136
 }
127
 
137
 
128
-export default connect(_mapStateToProps)(WelcomePage);
138
+export default translate(connect(_mapStateToProps)(WelcomePage));

+ 20
- 16
react/features/welcome/components/WelcomePage.web.js View File

1
-/* global $, APP, interfaceConfig */
1
+/* global APP, interfaceConfig */
2
 
2
 
3
 import React from 'react';
3
 import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
7
 
7
 
8
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
8
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
9
 
9
 
10
+import { translate } from '../../base/translation';
11
+
10
 /* eslint-disable require-jsdoc */
12
 /* eslint-disable require-jsdoc */
11
 
13
 
12
 /**
14
 /**
51
         if (this.state.generateRoomnames) {
53
         if (this.state.generateRoomnames) {
52
             this._updateRoomname();
54
             this._updateRoomname();
53
         }
55
         }
54
-
55
-        // XXX Temporary solution until we add React translation.
56
-        APP.translation.translateElement($('#welcome_page'));
57
     }
56
     }
58
 
57
 
59
     /**
58
     /**
142
      * @returns {ReactElement}
141
      * @returns {ReactElement}
143
      */
142
      */
144
     _renderFeature(index) {
143
     _renderFeature(index) {
144
+        const { t } = this.props;
145
+
145
         return (
146
         return (
146
             <div
147
             <div
147
                 className = 'feature_holder'
148
                 className = 'feature_holder'
148
                 key = { index } >
149
                 key = { index } >
149
                 <div
150
                 <div
150
-                    className = 'feature_icon'
151
-                    data-i18n = { `welcomepage.feature${index}.title` } />
151
+                    className = 'feature_icon'>
152
+                    { t(`welcomepage.feature${index}.title`) }
153
+                </div>
152
                 <div
154
                 <div
153
-                    className = 'feature_description'
154
-                    data-i18n = { `welcomepage.feature${index}.content` }
155
-                    data-i18n-options = { JSON.stringify({
156
-                        postProcess: 'resolveAppName'
157
-                    }) } />
155
+                    className = 'feature_description'>
156
+                    { t(`welcomepage.feature${index}.content`,
157
+                        { postProcess: 'resolveAppName' }) }
158
+                </div>
158
             </div>
159
             </div>
159
         );
160
         );
160
     }
161
     }
196
     _renderHeader() {
197
     _renderHeader() {
197
 
198
 
198
 /* eslint-enable require-jsdoc */
199
 /* eslint-enable require-jsdoc */
200
+        const { t } = this.props;
199
 
201
 
200
         return (
202
         return (
201
             <div id = 'welcome_page_header'>
203
             <div id = 'welcome_page_header'>
229
 
231
 
230
                             <button
232
                             <button
231
                                 className = 'enter-room__button'
233
                                 className = 'enter-room__button'
232
-                                data-i18n = 'welcomepage.go'
233
                                 id = 'enter_room_button'
234
                                 id = 'enter_room_button'
234
                                 onClick = { this._onJoin }
235
                                 onClick = { this._onJoin }
235
-                                type = 'button' />
236
+                                type = 'button'>
237
+                                { t('welcomepage.go') }
238
+                            </button>
236
                         </div>
239
                         </div>
237
                     </div>
240
                     </div>
238
                 </div>
241
                 </div>
245
                     type = 'checkbox' />
248
                     type = 'checkbox' />
246
                 <label
249
                 <label
247
                     className = 'disable_welcome_position'
250
                     className = 'disable_welcome_position'
248
-                    data-i18n = 'welcomepage.disable'
249
-                    htmlFor = 'disable_welcome' />
251
+                    htmlFor = 'disable_welcome'>
252
+                    { t('welcomepage.disable') }
253
+                </label>
250
                 <div id = 'header_text' />
254
                 <div id = 'header_text' />
251
             </div>
255
             </div>
252
         );
256
         );
274
     }
278
     }
275
 }
279
 }
276
 
280
 
277
-export default connect(_mapStateToProps)(WelcomePage);
281
+export default translate(connect(_mapStateToProps)(WelcomePage));

Loading…
Cancel
Save