Selaa lähdekoodia

Translate react strings.

Split language detectors to be web/native dependent. Take care of strings that contain html.
j8
damencho 8 vuotta sitten
vanhempi
commit
c361e1e31a

+ 15
- 1
lang/main.json Näytä tiedosto

@@ -40,7 +40,9 @@
40 40
     },
41 41
     "welcomepage":{
42 42
         "go": "GO",
43
+        "join": "JOIN",
43 44
         "roomname": "Enter room name",
45
+        "roomnamePlaceHolder": "room name",
44 46
         "disable": "Don't show this page again",
45 47
         "feature1": {
46 48
             "title": "Simple to use",
@@ -73,7 +75,10 @@
73 75
         "feature8": {
74 76
             "title": "Usage statistics",
75 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 83
     "startupoverlay": {
79 84
         "policyText": " ",
@@ -110,6 +115,15 @@
110 115
         "profile": "Edit your profile",
111 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 127
     "bottomtoolbar": {
114 128
         "chat": "Open / close chat",
115 129
         "filmstrip": "Show / hide videos",

+ 2
- 1
package.json Näytä tiedosto

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

+ 7
- 2
react/features/base/react/components/Watermarks.web.js Näytä tiedosto

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

+ 1
- 1
react/features/base/translation/ConfigLanguageDetector.js Näytä tiedosto

@@ -12,7 +12,7 @@ export default {
12 12
     /**
13 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 17
     lookup() {
18 18
         return config.defaultLanguage;

+ 11
- 0
react/features/base/translation/LanguageDetector.native.js Näytä tiedosto

@@ -0,0 +1,11 @@
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 Näytä tiedosto

@@ -0,0 +1,34 @@
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 Näytä tiedosto

@@ -4,8 +4,8 @@ import XHR from 'i18next-xhr-backend';
4 4
 import { DEFAULT_LANG, languages } from './constants';
5 5
 import languagesR from '../../../../lang/languages.json';
6 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 11
  * Default options to initialize i18next.
@@ -26,38 +26,12 @@ const defaultOptions = {
26 26
     fallbackOnNull: true,
27 27
     fallbackOnEmpty: true,
28 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 33
 i18n.use(XHR)
60
-    .use(browser)
34
+    .use(LanguageDetector)
61 35
     .use({
62 36
         type: 'postProcessor',
63 37
         name: 'resolveAppName',

+ 22
- 2
react/features/base/translation/functions.js Näytä tiedosto

@@ -1,12 +1,32 @@
1 1
 import { translate as reactTranslate } from 'react-i18next';
2
+import React from 'react';
2 3
 
3 4
 /**
4 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 10
 export function translate(component) {
10 11
     // use the default list of namespaces
11 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 Näytä tiedosto

@@ -51,9 +51,6 @@ class Conference extends Component {
51 51
         APP.UI.registerListeners();
52 52
         APP.UI.bindEvents();
53 53
 
54
-        // XXX Temporary solution until we add React translation.
55
-        APP.translation.translateElement($('#videoconference_page'));
56
-
57 54
         this.props.dispatch(connect());
58 55
     }
59 56
 

+ 9
- 4
react/features/conference/components/PasswordRequiredPrompt.native.js Näytä tiedosto

@@ -4,6 +4,8 @@ import { connect } from 'react-redux';
4 4
 
5 5
 import { setPassword } from '../../base/conference';
6 6
 
7
+import { translate } from '../../base/translation';
8
+
7 9
 /**
8 10
  * Implements a React Component which prompts the user when a password is
9 11
  * required to join a conference.
@@ -21,7 +23,8 @@ class PasswordRequiredPrompt extends Component {
21 23
          * @type {JitsiConference}
22 24
          */
23 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,12 +48,14 @@ class PasswordRequiredPrompt extends Component {
45 48
      * @returns {ReactElement}
46 49
      */
47 50
     render() {
51
+        const { t } = this.props;
52
+
48 53
         return (
49 54
             <Prompt
50 55
                 onCancel = { this._onCancel }
51 56
                 onSubmit = { this._onSubmit }
52
-                placeholder = 'Password'
53
-                title = 'Password required'
57
+                placeholder = { t('dialog.passwordLabel') }
58
+                title = { t('dialog.passwordRequired') }
54 59
                 visible = { true } />
55 60
         );
56 61
     }
@@ -84,4 +89,4 @@ class PasswordRequiredPrompt extends Component {
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 Näytä tiedosto

@@ -1,4 +1,4 @@
1
-/* global $, APP */
1
+/* global APP */
2 2
 
3 3
 import React, { Component } from 'react';
4 4
 
@@ -30,17 +30,6 @@ export default class AbstractOverlay extends Component {
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 34
      * Implements React's {@link Component#render()}.
46 35
      *

+ 16
- 7
react/features/overlay/components/PageReloadOverlay.js Näytä tiedosto

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

@@ -1,5 +1,7 @@
1 1
 import React, { Component } from 'react';
2 2
 
3
+import { translate } from '../../base/translation';
4
+
3 5
 declare var AJS: Object;
4 6
 
5 7
 /**
@@ -8,7 +10,7 @@ declare var AJS: Object;
8 10
  * seconds until the current value reaches props.end. Also displays progress
9 11
  * bar.
10 12
  */
11
-export default class ReloadTimer extends Component {
13
+class ReloadTimer extends Component {
12 14
     /**
13 15
      * ReloadTimer component's property types.
14 16
      *
@@ -52,7 +54,15 @@ export default class ReloadTimer extends Component {
52 54
          * @public
53 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,6 +142,8 @@ export default class ReloadTimer extends Component {
132 142
      * @public
133 143
      */
134 144
     render() {
145
+        const { t } = this.props;
146
+
135 147
         return (
136 148
             <div>
137 149
                 <div
@@ -143,9 +155,13 @@ export default class ReloadTimer extends Component {
143 155
                     {
144 156
                         this.state.current
145 157
                     }
146
-                    <span data-i18n = 'dialog.conferenceReloadTimeLeft' />
158
+                    <span>
159
+                        { t('dialog.conferenceReloadTimeLeft') }
160
+                    </span>
147 161
                 </span>
148 162
             </div>
149 163
         );
150 164
     }
151 165
 }
166
+
167
+export default translate(ReloadTimer);

+ 12
- 5
react/features/overlay/components/SuspendedOverlay.js Näytä tiedosto

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

@@ -4,11 +4,13 @@ import React from 'react';
4 4
 
5 5
 import AbstractOverlay from './AbstractOverlay';
6 6
 
7
+import { translate, translateToHTML } from '../../base/translation';
8
+
7 9
 /**
8 10
  * Implements a React Component for overlay with guidance how to proceed with
9 11
  * gUM prompt.
10 12
  */
11
-export default class UserMediaPermissionsOverlay extends AbstractOverlay {
13
+class UserMediaPermissionsOverlay extends AbstractOverlay {
12 14
     /**
13 15
      * UserMediaPermissionsOverlay component's property types.
14 16
      *
@@ -54,26 +56,26 @@ export default class UserMediaPermissionsOverlay extends AbstractOverlay {
54 56
      * @protected
55 57
      */
56 58
     _renderOverlayContent() {
57
-        const textKey = `userMedia.${this.props.browser}GrantPermissions`;
59
+        const { t } = this.props;
58 60
 
59 61
         return (
60 62
             <div>
61 63
                 <div className = 'inlay'>
62 64
                     <span className = 'inlay__icon icon-microphone' />
63 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 74
                 </div>
73 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 80
                         this._renderPolicyLogo()
79 81
                     }
@@ -102,3 +104,5 @@ export default class UserMediaPermissionsOverlay extends AbstractOverlay {
102 104
         return null;
103 105
     }
104 106
 }
107
+
108
+export default translate(UserMediaPermissionsOverlay);

+ 9
- 4
react/features/room-lock/components/RoomLockPrompt.native.js Näytä tiedosto

@@ -4,6 +4,8 @@ import { connect } from 'react-redux';
4 4
 
5 5
 import { endRoomLockRequest } from '../actions';
6 6
 
7
+import { translate } from '../../base/translation';
8
+
7 9
 /**
8 10
  * Implements a React Component which prompts the user for a password to lock  a
9 11
  * conference/room.
@@ -21,7 +23,8 @@ class RoomLockPrompt extends Component {
21 23
          * @type {JitsiConference}
22 24
          */
23 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,12 +48,14 @@ class RoomLockPrompt extends Component {
45 48
      * @returns {ReactElement}
46 49
      */
47 50
     render() {
51
+        const { t } = this.props;
52
+
48 53
         return (
49 54
             <Prompt
50 55
                 onCancel = { this._onCancel }
51 56
                 onSubmit = { this._onSubmit }
52
-                placeholder = 'Password'
53
-                title = 'Lock / Unlock room'
57
+                placeholder = { t('dialog.passwordLabel') }
58
+                title = { t('toolbar.lock') }
54 59
                 visible = { true } />
55 60
         );
56 61
     }
@@ -80,4 +85,4 @@ class RoomLockPrompt extends Component {
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 Näytä tiedosto

@@ -3,6 +3,7 @@
3 3
 import React, { Component } from 'react';
4 4
 
5 5
 import { Platform } from '../../base/react';
6
+import { translate } from '../../base/translation';
6 7
 
7 8
 import { CHROME, FIREFOX, IE, SAFARI } from './browserLinks';
8 9
 import HideNotificationBarStyle from './HideNotificationBarStyle';
@@ -20,7 +21,16 @@ const _NS = 'unsupported-desktop-browser';
20 21
  *
21 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 35
      * Renders the component.
26 36
      *
@@ -87,3 +97,4 @@ export default class UnsupportedDesktopBrowser extends Component {
87 97
     }
88 98
 }
89 99
 
100
+export default translate(UnsupportedDesktopBrowser);

+ 13
- 12
react/features/unsupported-browser/components/UnsupportedMobileBrowser.js Näytä tiedosto

@@ -4,6 +4,7 @@ import React, { Component } from 'react';
4 4
 import { connect } from 'react-redux';
5 5
 
6 6
 import { Platform } from '../../base/react';
7
+import { translate, translateToHTML } from '../../base/translation';
7 8
 
8 9
 import HideNotificationBarStyle from './HideNotificationBarStyle';
9 10
 
@@ -39,7 +40,8 @@ class UnsupportedMobileBrowser extends Component {
39 40
          * @private
40 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,7 +52,8 @@ class UnsupportedMobileBrowser extends Component {
50 52
      */
51 53
     componentWillMount() {
52 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 58
         // If the user installed the app while this Component was displayed
56 59
         // (e.g. the user clicked the Download the App button), then we would
@@ -74,6 +77,7 @@ class UnsupportedMobileBrowser extends Component {
74 77
     render() {
75 78
         const ns = 'unsupported-mobile-browser';
76 79
         const downloadButtonClassName = `${ns}__button ${ns}__button_primary`;
80
+        const { t } = this.props;
77 81
 
78 82
         return (
79 83
             <div className = { ns }>
@@ -82,24 +86,21 @@ class UnsupportedMobileBrowser extends Component {
82 86
                         className = { `${ns}__logo` }
83 87
                         src = 'images/logo-blue.svg' />
84 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 92
                     </p>
88 93
                     <a href = { _URLS[Platform.OS] }>
89 94
                         <button className = { downloadButtonClassName }>
90
-                            Download the App
95
+                            { t('unsupportedPage.downloadApp') }
91 96
                         </button>
92 97
                     </a>
93 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 100
                     </p>
98 101
                     <a href = { this.state.joinURL }>
99 102
                         <button className = { `${ns}__button` }>
100
-                            {
101
-                                this.state.joinText
102
-                            }
103
+                            { t(this.state.joinText) }
103 104
                         </button>
104 105
                     </a>
105 106
                 </div>
@@ -133,4 +134,4 @@ function _mapStateToProps(state) {
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 Näytä tiedosto

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

@@ -1,4 +1,4 @@
1
-/* global $, APP, interfaceConfig */
1
+/* global APP, interfaceConfig */
2 2
 
3 3
 import React from 'react';
4 4
 import { connect } from 'react-redux';
@@ -7,6 +7,8 @@ import { Watermarks } from '../../base/react';
7 7
 
8 8
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
9 9
 
10
+import { translate } from '../../base/translation';
11
+
10 12
 /* eslint-disable require-jsdoc */
11 13
 
12 14
 /**
@@ -51,9 +53,6 @@ class WelcomePage extends AbstractWelcomePage {
51 53
         if (this.state.generateRoomnames) {
52 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,19 +141,21 @@ class WelcomePage extends AbstractWelcomePage {
142 141
      * @returns {ReactElement}
143 142
      */
144 143
     _renderFeature(index) {
144
+        const { t } = this.props;
145
+
145 146
         return (
146 147
             <div
147 148
                 className = 'feature_holder'
148 149
                 key = { index } >
149 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 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 159
             </div>
159 160
         );
160 161
     }
@@ -196,6 +197,7 @@ class WelcomePage extends AbstractWelcomePage {
196 197
     _renderHeader() {
197 198
 
198 199
 /* eslint-enable require-jsdoc */
200
+        const { t } = this.props;
199 201
 
200 202
         return (
201 203
             <div id = 'welcome_page_header'>
@@ -229,10 +231,11 @@ class WelcomePage extends AbstractWelcomePage {
229 231
 
230 232
                             <button
231 233
                                 className = 'enter-room__button'
232
-                                data-i18n = 'welcomepage.go'
233 234
                                 id = 'enter_room_button'
234 235
                                 onClick = { this._onJoin }
235
-                                type = 'button' />
236
+                                type = 'button'>
237
+                                { t('welcomepage.go') }
238
+                            </button>
236 239
                         </div>
237 240
                     </div>
238 241
                 </div>
@@ -245,8 +248,9 @@ class WelcomePage extends AbstractWelcomePage {
245 248
                     type = 'checkbox' />
246 249
                 <label
247 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 254
                 <div id = 'header_text' />
251 255
             </div>
252 256
         );
@@ -274,4 +278,4 @@ class WelcomePage extends AbstractWelcomePage {
274 278
     }
275 279
 }
276 280
 
277
-export default connect(_mapStateToProps)(WelcomePage);
281
+export default translate(connect(_mapStateToProps)(WelcomePage));

Loading…
Peruuta
Tallenna