From 466a57e6b1d3a780e035615dcc57f345a75a3fb1 Mon Sep 17 00:00:00 2001
From: Diego Mello <diegolmello@gmail.com>
Date: Fri, 1 Jun 2018 14:38:13 -0300
Subject: [PATCH] I18n (#312)

---
 android/app/build.gradle                      |   1 +
 .../reactnative/CustomTabsAndroid.java        |   0
 .../reactnative/CustomTabsHelper.java         |   0
 .../reactnative/MainActivity.java             |   0
 .../reactnative/MainApplication.java          |   4 +-
 .../reactnative/RocketChatNativePackage.java  |   0
 android/settings.gradle                       |   2 +
 app/containers/Banner.js                      |  69 ------
 app/containers/MessageActions.js              |  37 +--
 app/containers/MessageBox/Recording.js        |   9 +-
 app/containers/MessageBox/index.js            |  28 +--
 app/containers/MessageErrorActions.js         |   5 +-
 app/containers/Sidebar.js                     |   9 +-
 app/containers/Typing.js                      |   3 +-
 app/containers/message/ReactionsModal.js      |  11 +-
 app/containers/message/index.js               |  29 +--
 app/containers/routes/AuthRoutes.js           |  55 ++---
 app/containers/routes/PublicRoutes.js         |  11 +-
 app/i18n/index.js                             |  10 +
 app/i18n/locales/en.js                        | 216 ++++++++++++++++++
 .../helpers/mergeSubscriptionsRooms.js        |   5 +-
 app/presentation/RoomItem.js                  |  46 +---
 app/sagas/messages.js                         |   2 +-
 app/views/CreateChannelView.js                |  22 +-
 app/views/ForgotPasswordView.js               |  14 +-
 app/views/ListServerView.js                   |   3 +-
 app/views/LoginSignupView.js                  |  11 +-
 app/views/LoginView.js                        |  23 +-
 app/views/MentionedMessagesView/index.js      |   3 +-
 app/views/NewServerView.js                    |  13 +-
 app/views/PinnedMessagesView/index.js         |   7 +-
 app/views/PrivacyPolicyView.js                |   6 +-
 app/views/RegisterView.js                     |  39 ++--
 app/views/RoomActionsView/index.js            |  41 ++--
 app/views/RoomFilesView/index.js              |   3 +-
 app/views/RoomInfoEditView/index.js           |  69 +++---
 app/views/RoomInfoView/index.js               |  17 +-
 app/views/RoomMembersView/index.js            |  15 +-
 app/views/RoomView/Header/index.js            |  20 +-
 app/views/RoomView/UnreadSeparator.js         |   3 +-
 app/views/RoomView/banner.js                  |  22 --
 app/views/RoomView/index.js                   |  25 +-
 app/views/RoomsListView/Header/index.js       |  26 ++-
 app/views/RoomsListView/index.js              |   5 +-
 app/views/SearchMessagesView/index.js         |   7 +-
 app/views/SelectedUsersView.js                |   5 +-
 app/views/SnippetedMessagesView/index.js      |   3 +-
 app/views/StarredMessagesView/index.js        |   7 +-
 app/views/TermsServiceView.js                 |   6 +-
 e2e/07-room.spec.js                           |  40 ++--
 ios/RocketChatRN.xcodeproj/project.pbxproj    | 103 ++++++++-
 package-lock.json                             |  13 ++
 package.json                                  |   1 +
 53 files changed, 679 insertions(+), 445 deletions(-)
 rename android/app/src/main/java/chat/{rocketchat => rocket}/reactnative/CustomTabsAndroid.java (100%)
 rename android/app/src/main/java/chat/{rocketchat => rocket}/reactnative/CustomTabsHelper.java (100%)
 rename android/app/src/main/java/chat/{rocketchat => rocket}/reactnative/MainActivity.java (100%)
 rename android/app/src/main/java/chat/{rocketchat => rocket}/reactnative/MainApplication.java (95%)
 rename android/app/src/main/java/chat/{rocketchat => rocket}/reactnative/RocketChatNativePackage.java (100%)
 delete mode 100644 app/containers/Banner.js
 create mode 100644 app/i18n/index.js
 create mode 100644 app/i18n/locales/en.js
 delete mode 100644 app/views/RoomView/banner.js

diff --git a/android/app/build.gradle b/android/app/build.gradle
index 8179f8603..8c3ef1d6d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -172,6 +172,7 @@ repositories {
 }
 
 dependencies {
+    compile project(':react-native-i18n')
     compile project(':react-native-fabric')
     compile project(':react-native-audio')
     compile project(":reactnativekeyboardinput")
diff --git a/android/app/src/main/java/chat/rocketchat/reactnative/CustomTabsAndroid.java b/android/app/src/main/java/chat/rocket/reactnative/CustomTabsAndroid.java
similarity index 100%
rename from android/app/src/main/java/chat/rocketchat/reactnative/CustomTabsAndroid.java
rename to android/app/src/main/java/chat/rocket/reactnative/CustomTabsAndroid.java
diff --git a/android/app/src/main/java/chat/rocketchat/reactnative/CustomTabsHelper.java b/android/app/src/main/java/chat/rocket/reactnative/CustomTabsHelper.java
similarity index 100%
rename from android/app/src/main/java/chat/rocketchat/reactnative/CustomTabsHelper.java
rename to android/app/src/main/java/chat/rocket/reactnative/CustomTabsHelper.java
diff --git a/android/app/src/main/java/chat/rocketchat/reactnative/MainActivity.java b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
similarity index 100%
rename from android/app/src/main/java/chat/rocketchat/reactnative/MainActivity.java
rename to android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
diff --git a/android/app/src/main/java/chat/rocketchat/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
similarity index 95%
rename from android/app/src/main/java/chat/rocketchat/reactnative/MainApplication.java
rename to android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
index 7883ed1b2..c6fbea41c 100644
--- a/android/app/src/main/java/chat/rocketchat/reactnative/MainApplication.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
@@ -20,6 +20,7 @@ import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
 import com.rnim.rn.audio.ReactNativeAudioPackage;
 import com.smixx.fabric.FabricPackage;
 import com.dylanvann.fastimage.FastImageViewPackage;
+import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
 
 import java.util.Arrays;
 import java.util.List;
@@ -51,7 +52,8 @@ public class MainApplication extends Application implements ReactApplication {
         new KeyboardInputPackage(MainApplication.this),
         new RocketChatNativePackage(),
         new FabricPackage(),
-        new FastImageViewPackage()
+        new FastImageViewPackage(),
+        new RNI18nPackage()
       );
     }
   };
diff --git a/android/app/src/main/java/chat/rocketchat/reactnative/RocketChatNativePackage.java b/android/app/src/main/java/chat/rocket/reactnative/RocketChatNativePackage.java
similarity index 100%
rename from android/app/src/main/java/chat/rocketchat/reactnative/RocketChatNativePackage.java
rename to android/app/src/main/java/chat/rocket/reactnative/RocketChatNativePackage.java
diff --git a/android/settings.gradle b/android/settings.gradle
index 1259f2ba9..ffb155c5c 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1,4 +1,6 @@
 rootProject.name = 'RocketChatRN'
+include ':react-native-i18n'
+project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android')
 include ':react-native-fast-image'
 project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
 include ':react-native-fabric'
diff --git a/app/containers/Banner.js b/app/containers/Banner.js
deleted file mode 100644
index ae7b75d1f..000000000
--- a/app/containers/Banner.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { StyleSheet, View, Text } from 'react-native';
-import PropTypes from 'prop-types';
-import React from 'react';
-
-import { connect } from 'react-redux';
-
-const styles = StyleSheet.create({
-	bannerContainer: {
-		backgroundColor: '#ddd'
-	},
-	bannerText: {
-		textAlign: 'center',
-		margin: 5
-	}
-});
-
-@connect(state => ({
-	connecting: state.meteor.connecting,
-	authenticating: state.login.isFetching,
-	offline: !state.meteor.connected,
-	logged: !!state.login.token
-}))
-
-export default class Banner extends React.PureComponent {
-	static propTypes = {
-		connecting: PropTypes.bool,
-		authenticating: PropTypes.bool,
-		offline: PropTypes.bool
-	}
-	render() {
-		const {
-			connecting, authenticating, offline, logged
-		} = this.props;
-
-		if (offline) {
-			return (
-				<View style={[styles.bannerContainer, { backgroundColor: 'red' }]}>
-					<Text style={[styles.bannerText, { color: '#a00' }]}>offline...</Text>
-				</View>
-			);
-		}
-
-		if (connecting) {
-			return (
-				<View style={[styles.bannerContainer, { backgroundColor: '#0d0' }]}>
-					<Text style={[styles.bannerText, { color: '#fff' }]}>Connecting...</Text>
-				</View>
-			);
-		}
-
-		if (authenticating) {
-			return (
-				<View style={[styles.bannerContainer, { backgroundColor: 'orange' }]}>
-					<Text style={[styles.bannerText, { color: '#a00' }]}>Authenticating...</Text>
-				</View>
-			);
-		}
-
-		if (logged) {
-			return this.props.children;
-		}
-
-		return (
-			<View style={[styles.bannerContainer, { backgroundColor: 'orange' }]}>
-				<Text style={[styles.bannerText, { color: '#a00' }]}>Not logged...</Text>
-			</View>
-		);
-	}
-}
diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js
index d126600df..ee63c6ad9 100644
--- a/app/containers/MessageActions.js
+++ b/app/containers/MessageActions.js
@@ -18,6 +18,7 @@ import {
 } from '../actions/messages';
 import { showToast } from '../utils/info';
 import RocketChat from '../lib/rocketchat';
+import I18n from '../i18n';
 
 @connect(
 	state => ({
@@ -86,50 +87,50 @@ export default class MessageActions extends React.Component {
 		if (nextProps.showActions !== this.props.showActions && nextProps.showActions) {
 			const { actionMessage } = nextProps;
 			// Cancel
-			this.options = ['Cancel'];
+			this.options = [I18n.t('Cancel')];
 			this.CANCEL_INDEX = 0;
 			// Reply
 			if (!this.isRoomReadOnly()) {
-				this.options.push('Reply');
+				this.options.push(I18n.t('Reply'));
 				this.REPLY_INDEX = this.options.length - 1;
 			}
 			// Edit
 			if (this.allowEdit(nextProps)) {
-				this.options.push('Edit');
+				this.options.push(I18n.t('Edit'));
 				this.EDIT_INDEX = this.options.length - 1;
 			}
 			// Permalink
-			this.options.push('Copy Permalink');
+			this.options.push(I18n.t('Copy_Permalink'));
 			this.PERMALINK_INDEX = this.options.length - 1;
 			// Copy
-			this.options.push('Copy Message');
+			this.options.push(I18n.t('Copy_Message'));
 			this.COPY_INDEX = this.options.length - 1;
 			// Share
-			this.options.push('Share Message');
+			this.options.push(I18n.t('Share_Message'));
 			this.SHARE_INDEX = this.options.length - 1;
 			// Quote
 			if (!this.isRoomReadOnly()) {
-				this.options.push('Quote');
+				this.options.push(I18n.t('Quote'));
 				this.QUOTE_INDEX = this.options.length - 1;
 			}
 			// Star
 			if (this.props.Message_AllowStarring) {
-				this.options.push(actionMessage.starred ? 'Unstar' : 'Star');
+				this.options.push(I18n.t(actionMessage.starred ? 'Unstar' : 'Star'));
 				this.STAR_INDEX = this.options.length - 1;
 			}
 			// Pin
 			if (this.props.Message_AllowPinning) {
-				this.options.push(actionMessage.pinned ? 'Unpin' : 'Pin');
+				this.options.push(I18n.t(actionMessage.pinned ? 'Unpin' : 'Pin'));
 				this.PIN_INDEX = this.options.length - 1;
 			}
 			// Reaction
 			if (!this.isRoomReadOnly() || this.canReactWhenReadOnly()) {
-				this.options.push('Add Reaction');
+				this.options.push(I18n.t('Add_Reaction'));
 				this.REACTION_INDEX = this.options.length - 1;
 			}
 			// Delete
 			if (this.allowDelete(nextProps)) {
-				this.options.push('Delete');
+				this.options.push(I18n.t('Delete'));
 				this.DELETE_INDEX = this.options.length - 1;
 			}
 			setTimeout(() => {
@@ -141,7 +142,7 @@ export default class MessageActions extends React.Component {
 			if (this.state.copyPermalink) {
 				this.setState({ copyPermalink: false });
 				await Clipboard.setString(nextProps.permalink);
-				showToast('Permalink copied to clipboard!');
+				showToast(I18n.t('Permalink_copied_to_clipboard'));
 				this.props.permalinkClear();
 			// quote
 			} else if (this.state.quote) {
@@ -234,15 +235,15 @@ export default class MessageActions extends React.Component {
 
 	handleDelete() {
 		Alert.alert(
-			'Are you sure?',
-			'You will not be able to recover this message!',
+			I18n.t('Are_you_sure_question_mark'),
+			I18n.t('You_will_not_be_able_to_recover_this_message'),
 			[
 				{
-					text: 'Cancel',
+					text: I18n.t('Cancel'),
 					style: 'cancel'
 				},
 				{
-					text: 'Yes, delete it!',
+					text: I18n.t('Yes_action_it', { action: 'delete' }),
 					style: 'destructive',
 					onPress: () => this.props.deleteRequest(this.props.actionMessage)
 				}
@@ -258,7 +259,7 @@ export default class MessageActions extends React.Component {
 
 	handleCopy = async() => {
 		await Clipboard.setString(this.props.actionMessage.msg);
-		showToast('Copied to clipboard!');
+		showToast(I18n.t('Copied_to_clipboard'));
 	}
 
 	handleShare = async() => {
@@ -336,7 +337,7 @@ export default class MessageActions extends React.Component {
 		return (
 			<ActionSheet
 				ref={o => this.ActionSheet = o}
-				title='Messages actions'
+				title={I18n.t('Message_actions')}
 				testID='message-actions'
 				options={this.options}
 				cancelButtonIndex={this.CANCEL_INDEX}
diff --git a/app/containers/MessageBox/Recording.js b/app/containers/MessageBox/Recording.js
index 713564a19..5396fb671 100644
--- a/app/containers/MessageBox/Recording.js
+++ b/app/containers/MessageBox/Recording.js
@@ -4,6 +4,7 @@ import { View, SafeAreaView, Platform, PermissionsAndroid, Text } from 'react-na
 import { AudioRecorder, AudioUtils } from 'react-native-audio';
 import Icon from 'react-native-vector-icons/MaterialIcons';
 import styles from './styles';
+import I18n from '../../i18n';
 
 export const _formatTime = function(seconds) {
 	let minutes = Math.floor(seconds / 60);
@@ -24,8 +25,8 @@ export default class extends React.PureComponent {
 		}
 
 		const rationale = {
-			title: 'Microphone Permission',
-			message: 'Rocket Chat needs access to your microphone so you can send audio message.'
+			title: I18n.t('Microphone_Permission'),
+			message: I18n.t('Microphone_Permission_Message')
 		};
 
 		const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, rationale);
@@ -118,7 +119,7 @@ export default class extends React.PureComponent {
 						style={[styles.actionButtons, { color: 'red' }]}
 						name='clear'
 						key='clear'
-						accessibilityLabel='Cancel recording'
+						accessibilityLabel={I18n.t('Cancel_recording')}
 						accessibilityTraits='button'
 						onPress={this.cancelAudioMessage}
 					/>
@@ -127,7 +128,7 @@ export default class extends React.PureComponent {
 						style={[styles.actionButtons, { color: 'green' }]}
 						name='check'
 						key='check'
-						accessibilityLabel='Finish recording'
+						accessibilityLabel={I18n.t('Finish_recording')}
 						accessibilityTraits='button'
 						onPress={this.finishAudioMessage}
 					/>
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index 113ce2dbe..fea1c26b6 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -19,6 +19,7 @@ import { emojis } from '../../emojis';
 import Recording from './Recording';
 import './EmojiKeyboard';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 const MENTIONS_TRACKING_TYPE_USERS = '@';
 const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
@@ -107,7 +108,7 @@ export default class MessageBox extends React.PureComponent {
 			return (<Icon
 				style={styles.actionButtons}
 				name='close'
-				accessibilityLabel='Cancel editing'
+				accessibilityLabel={I18n.t('Cancel_editing')}
 				accessibilityTraits='button'
 				onPress={() => this.editCancel()}
 				testID='messagebox-cancel-editing'
@@ -116,14 +117,14 @@ export default class MessageBox extends React.PureComponent {
 		return !this.state.showEmojiKeyboard ? (<Icon
 			style={styles.actionButtons}
 			onPress={() => this.openEmoji()}
-			accessibilityLabel='Open emoji selector'
+			accessibilityLabel={I18n.t('Open_emoji_selector')}
 			accessibilityTraits='button'
 			name='mood'
 			testID='messagebox-open-emoji'
 		/>) : (<Icon
 			onPress={() => this.closeEmoji()}
 			style={styles.actionButtons}
-			accessibilityLabel='Close emoji selector'
+			accessibilityLabel={I18n.t('Close_emoji_selector')}
 			accessibilityTraits='button'
 			name='keyboard'
 			testID='messagebox-close-emoji'
@@ -137,7 +138,7 @@ export default class MessageBox extends React.PureComponent {
 				style={[styles.actionButtons, { color: '#1D74F5' }]}
 				name='send'
 				key='sendIcon'
-				accessibilityLabel='Send message'
+				accessibilityLabel={I18n.t('Send message')}
 				accessibilityTraits='button'
 				onPress={() => this.submit(this.state.text)}
 				testID='messagebox-send-message'
@@ -148,7 +149,7 @@ export default class MessageBox extends React.PureComponent {
 			style={[styles.actionButtons, { color: '#1D74F5', paddingHorizontal: 10 }]}
 			name='mic'
 			key='micIcon'
-			accessibilityLabel='Send audio message'
+			accessibilityLabel={I18n.t('Send audio message')}
 			accessibilityTraits='button'
 			onPress={() => this.recordAudioMessage()}
 			testID='messagebox-send-audio'
@@ -157,7 +158,7 @@ export default class MessageBox extends React.PureComponent {
 			style={[styles.actionButtons, { color: '#2F343D', fontSize: 16 }]}
 			name='plus'
 			key='fileIcon'
-			accessibilityLabel='Message actions'
+			accessibilityLabel={I18n.t('Message actions')}
 			accessibilityTraits='button'
 			onPress={() => this.addFile()}
 			testID='messagebox-actions'
@@ -169,18 +170,13 @@ export default class MessageBox extends React.PureComponent {
 		const options = {
 			maxHeight: 1960,
 			maxWidth: 1960,
-			quality: 0.8,
-			customButtons: [{
-				name: 'import', title: 'Import File From'
-			}]
+			quality: 0.8
 		};
 		ImagePicker.showImagePicker(options, (response) => {
 			if (response.didCancel) {
 				console.warn('User cancelled image picker');
 			} else if (response.error) {
 				log('ImagePicker Error', response.error);
-			} else if (response.customButton) {
-				console.warn('User tapped custom button: ', response.customButton);
 			} else {
 				const fileInfo = {
 					name: response.fileName,
@@ -250,10 +246,10 @@ export default class MessageBox extends React.PureComponent {
 
 	_getFixedMentions(keyword) {
 		if ('all'.indexOf(keyword) !== -1) {
-			this.users = [{ _id: -1, username: 'all', desc: 'all' }, ...this.users];
+			this.users = [{ _id: -1, username: 'all' }, ...this.users];
 		}
 		if ('here'.indexOf(keyword) !== -1) {
-			this.users = [{ _id: -2, username: 'here', desc: 'active users' }, ...this.users];
+			this.users = [{ _id: -2, username: 'here' }, ...this.users];
 		}
 	}
 
@@ -419,7 +415,7 @@ export default class MessageBox extends React.PureComponent {
 			onPress={() => this._onPressMention(item)}
 		>
 			<Text style={styles.fixedMentionAvatar}>{item.username}</Text>
-			<Text>Notify {item.desc} in this room</Text>
+			<Text>{item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}</Text>
 		</TouchableOpacity>
 	)
 	renderMentionEmoji = (item) => {
@@ -507,7 +503,7 @@ export default class MessageBox extends React.PureComponent {
 						returnKeyType='default'
 						keyboardType='twitter'
 						blurOnSubmit={false}
-						placeholder='New Message'
+						placeholder={I18n.t('New_Message')}
 						onChangeText={text => this.onChangeText(text)}
 						value={this.state.text}
 						underlineColorAndroid='transparent'
diff --git a/app/containers/MessageErrorActions.js b/app/containers/MessageErrorActions.js
index a65c016d7..4a8a9eeb5 100644
--- a/app/containers/MessageErrorActions.js
+++ b/app/containers/MessageErrorActions.js
@@ -7,6 +7,7 @@ import { errorActionsHide } from '../actions/messages';
 import RocketChat from '../lib/rocketchat';
 import database from '../lib/realm';
 import protectedFunction from '../lib/methods/helpers/protectedFunction';
+import I18n from '../i18n';
 
 @connect(
 	state => ({
@@ -27,7 +28,7 @@ export default class MessageErrorActions extends React.Component {
 	constructor(props) {
 		super(props);
 		this.handleActionPress = this.handleActionPress.bind(this);
-		this.options = ['Cancel', 'Delete', 'Resend'];
+		this.options = [I18n.t('Cancel'), I18n.t('Delete'), I18n.t('Resend')];
 		this.CANCEL_INDEX = 0;
 		this.DELETE_INDEX = 1;
 		this.RESEND_INDEX = 2;
@@ -66,7 +67,7 @@ export default class MessageErrorActions extends React.Component {
 		return (
 			<ActionSheet
 				ref={o => this.ActionSheet = o}
-				title='Messages actions'
+				title={I18n.t('Message_actions')}
 				options={this.options}
 				cancelButtonIndex={this.CANCEL_INDEX}
 				destructiveButtonIndex={this.DELETE_INDEX}
diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js
index df34b7ff1..655338416 100644
--- a/app/containers/Sidebar.js
+++ b/app/containers/Sidebar.js
@@ -7,6 +7,7 @@ import { DrawerActions } from 'react-navigation';
 import database from '../lib/realm';
 import { setServer } from '../actions/server';
 import { logout } from '../actions/login';
+import I18n from '../i18n';
 
 const styles = StyleSheet.create({
 	scrollView: {
@@ -115,9 +116,7 @@ export default class Sidebar extends Component {
 						testID='sidebar-logout'
 					>
 						<View style={styles.serverItem}>
-							<Text>
-								Logout
-							</Text>
+							<Text>{I18n.t('Logout')}</Text>
 						</View>
 					</TouchableHighlight>
 					<TouchableHighlight
@@ -128,9 +127,7 @@ export default class Sidebar extends Component {
 						testID='sidebar-add-server'
 					>
 						<View style={styles.serverItem}>
-							<Text>
-								Add Server
-							</Text>
+							<Text>{I18n.t('Add_Server')}</Text>
 						</View>
 					</TouchableHighlight>
 				</View>
diff --git a/app/containers/Typing.js b/app/containers/Typing.js
index eb667093b..162d462f0 100644
--- a/app/containers/Typing.js
+++ b/app/containers/Typing.js
@@ -2,6 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { View, StyleSheet, Text, Keyboard, LayoutAnimation } from 'react-native';
 import { connect } from 'react-redux';
+import I18n from '../i18n';
 
 const styles = StyleSheet.create({
 	typing: {
@@ -31,7 +32,7 @@ export default class Typing extends React.Component {
 	}
 	get usersTyping() {
 		const users = this.props.usersTyping.filter(_username => this.props.username !== _username);
-		return users.length ? `${ users.join(' ,') } ${ users.length > 1 ? 'are' : 'is' } typing` : '';
+		return users.length ? `${ users.join(' ,') } ${ users.length > 1 ? I18n.t('are_typing') : I18n.t('is_typing') }` : '';
 	}
 	render() {
 		const { usersTyping } = this;
diff --git a/app/containers/message/ReactionsModal.js b/app/containers/message/ReactionsModal.js
index 8f3f5620f..cde604260 100644
--- a/app/containers/message/ReactionsModal.js
+++ b/app/containers/message/ReactionsModal.js
@@ -5,6 +5,7 @@ import Modal from 'react-native-modal';
 import Icon from 'react-native-vector-icons/MaterialIcons';
 import { connect } from 'react-redux';
 import Emoji from './Emoji';
+import I18n from '../../i18n';
 
 const styles = StyleSheet.create({
 	titleContainer: {
@@ -68,11 +69,11 @@ export default class ReactionsModal extends React.PureComponent {
 	renderItem = (item) => {
 		const count = item.usernames.length;
 		let usernames = item.usernames.slice(0, 3)
-			.map(username => (username.value === this.props.user.username ? 'you' : username.value)).join(', ');
+			.map(username => (username.value === this.props.user.username ? I18n.t('you') : username.value)).join(', ');
 		if (count > 3) {
-			usernames = `${ usernames } and more ${ count - 3 }`;
+			usernames = `${ usernames } ${ I18n.t('and_more') } ${ count - 3 }`;
 		} else {
-			usernames = usernames.replace(/,(?=[^,]*$)/, ' and');
+			usernames = usernames.replace(/,(?=[^,]*$)/, ` ${ I18n.t('and') }`);
 		}
 		return (
 			<View style={styles.itemContainer}>
@@ -86,7 +87,7 @@ export default class ReactionsModal extends React.PureComponent {
 				</View>
 				<View style={styles.peopleItemContainer}>
 					<Text style={styles.reactCount}>
-						{count === 1 ? '1 person' : `${ count } people`} reacted
+						{count === 1 ? I18n.t('1_person_reacted') : I18n.t('N_people_reacted', { n: count })}
 					</Text>
 					<Text style={styles.peopleReacted}>{ usernames }</Text>
 				</View>
@@ -113,7 +114,7 @@ export default class ReactionsModal extends React.PureComponent {
 							size={20}
 							onPress={onClose}
 						/>
-						<Text style={styles.title}>Reactions</Text>
+						<Text style={styles.title}>{I18n.t('Reactions')}</Text>
 					</View>
 				</TouchableWithoutFeedback>
 				<View style={styles.listContainer}>
diff --git a/app/containers/message/index.js b/app/containers/message/index.js
index 3aac53d5f..54c31aa49 100644
--- a/app/containers/message/index.js
+++ b/app/containers/message/index.js
@@ -21,6 +21,7 @@ import styles from './styles';
 import { actionsShow, errorActionsShow, toggleReactionPicker, replyBroadcast } from '../../actions/messages';
 import messagesStatus from '../../constants/messagesStatus';
 import Touch from '../../utils/touch';
+import I18n from '../../i18n';
 
 const SYSTEM_MESSAGES = [
 	'r',
@@ -44,35 +45,35 @@ const getInfoMessage = ({
 	t, role, msg, u
 }) => {
 	if (t === 'rm') {
-		return 'Message removed';
+		return I18n.t('Message_removed');
 	} else if (t === 'uj') {
-		return 'Has joined the channel.';
+		return I18n.t('Has_joined_the_channel');
 	} else if (t === 'r') {
-		return `Room name changed to: ${ msg } by ${ u.username }`;
+		return I18n.t('Room_name_changed', { name: msg, userBy: u.username });
 	} else if (t === 'message_pinned') {
-		return 'Message pinned';
+		return I18n.t('Message_pinned');
 	} else if (t === 'ul') {
-		return 'Has left the channel.';
+		return I18n.t('Has_left_the_channel');
 	} else if (t === 'ru') {
-		return `User ${ msg } removed by ${ u.username }`;
+		return I18n.t('User_removed_by', { userRemoved: msg, userBy: u.username });
 	} else if (t === 'au') {
-		return `User ${ msg } added by ${ u.username }`;
+		return I18n.t('User_added_by', { userAdded: msg, userBy: u.username });
 	} else if (t === 'user-muted') {
-		return `User ${ msg } muted by ${ u.username }`;
+		return I18n.t('User_muted_by', { userMuted: msg, userBy: u.username });
 	} else if (t === 'user-unmuted') {
-		return `User ${ msg } unmuted by ${ u.username }`;
+		return I18n.t('User_unmuted_by', { userUnmuted: msg, userBy: u.username });
 	} else if (t === 'subscription-role-added') {
 		return `${ msg } was set ${ role } by ${ u.username }`;
 	} else if (t === 'subscription-role-removed') {
 		return `${ msg } is no longer ${ role } by ${ u.username }`;
 	} else if (t === 'room_changed_description') {
-		return `Room description changed to: ${ msg } by ${ u.username }`;
+		return I18n.t('Room_changed_description', { description: msg, userBy: u.username });
 	} else if (t === 'room_changed_announcement') {
-		return `Room announcement changed to: ${ msg } by ${ u.username }`;
+		return I18n.t('Room_changed_announcement', { announcement: msg, userBy: u.username });
 	} else if (t === 'room_changed_topic') {
-		return `Room topic changed to: ${ msg } by ${ u.username }`;
+		return I18n.t('Room_changed_topic', { topic: msg, userBy: u.username });
 	} else if (t === 'room_changed_privacy') {
-		return `Room type changed to: ${ msg } by ${ u.username }`;
+		return I18n.t('Room_changed_privacy', { type: msg, userBy: u.username });
 	}
 	return '';
 };
@@ -334,7 +335,7 @@ export default class Message extends React.Component {
 		} = this.props;
 		const username = item.alias || item.u.username;
 		const isEditing = message._id === item._id && editing;
-		const accessibilityLabel = `Message from ${ username } at ${ moment(item.ts).format(this.timeFormat) }, ${ this.props.item.msg }`;
+		const accessibilityLabel = I18n.t('Message_accessibility', { user: username, time: moment(item.ts).format(this.timeFormat), message: this.props.item.msg });
 
 		return (
 			<Touch
diff --git a/app/containers/routes/AuthRoutes.js b/app/containers/routes/AuthRoutes.js
index c38082c59..cd46d1b48 100644
--- a/app/containers/routes/AuthRoutes.js
+++ b/app/containers/routes/AuthRoutes.js
@@ -17,6 +17,9 @@ import RoomFilesView from '../../views/RoomFilesView';
 import RoomMembersView from '../../views/RoomMembersView';
 import RoomInfoView from '../../views/RoomInfoView';
 import RoomInfoEditView from '../../views/RoomInfoEditView';
+import I18n from '../../i18n';
+
+const headerTintColor = '#292E35';
 
 const AuthRoutes = createStackNavigator(
 	{
@@ -29,92 +32,92 @@ const AuthRoutes = createStackNavigator(
 		CreateChannel: {
 			screen: CreateChannelView,
 			navigationOptions: {
-				title: 'Create Channel',
-				headerTintColor: '#292E35'
+				title: I18n.t('Create_Channel'),
+				headerTintColor
 			}
 		},
 		SelectedUsers: {
 			screen: SelectedUsersView,
 			navigationOptions: {
-				title: 'Select Users',
-				headerTintColor: '#292E35'
+				title: I18n.t('Select_Users'),
+				headerTintColor
 			}
 		},
 		AddServer: {
 			screen: NewServerView,
 			navigationOptions: {
-				title: 'New server',
-				headerTintColor: '#292E35'
+				title: I18n.t('New_Server'),
+				headerTintColor
 			}
 		},
 		RoomActions: {
 			screen: RoomActionsView,
 			navigationOptions: {
-				title: 'Actions',
-				headerTintColor: '#292E35'
+				title: I18n.t('Actions'),
+				headerTintColor
 			}
 		},
 		StarredMessages: {
 			screen: StarredMessagesView,
 			navigationOptions: {
-				title: 'Starred Messages',
-				headerTintColor: '#292E35'
+				title: I18n.t('Starred_Messages'),
+				headerTintColor
 			}
 		},
 		PinnedMessages: {
 			screen: PinnedMessagesView,
 			navigationOptions: {
-				title: 'Pinned Messages',
-				headerTintColor: '#292E35'
+				title: I18n.t('Pinned_Messages'),
+				headerTintColor
 			}
 		},
 		MentionedMessages: {
 			screen: MentionedMessagesView,
 			navigationOptions: {
-				title: 'Mentioned Messages',
-				headerTintColor: '#292E35'
+				title: I18n.t('Mentioned_Messages'),
+				headerTintColor
 			}
 		},
 		SnippetedMessages: {
 			screen: SnippetedMessagesView,
 			navigationOptions: {
-				title: 'Snippet Messages',
-				headerTintColor: '#292E35'
+				title: I18n.t('Snippet_Messages'),
+				headerTintColor
 			}
 		},
 		SearchMessages: {
 			screen: SearchMessagesView,
 			navigationOptions: {
-				title: 'Search Messages',
-				headerTintColor: '#292E35'
+				title: I18n.t('Search_Messages'),
+				headerTintColor
 			}
 		},
 		RoomFiles: {
 			screen: RoomFilesView,
 			navigationOptions: {
-				title: 'Room Files',
-				headerTintColor: '#292E35'
+				title: I18n.t('Room_Files'),
+				headerTintColor
 			}
 		},
 		RoomMembers: {
 			screen: RoomMembersView,
 			navigationOptions: {
-				title: 'Room Members',
-				headerTintColor: '#292E35'
+				title: I18n.t('Room_Members'),
+				headerTintColor
 			}
 		},
 		RoomInfo: {
 			screen: RoomInfoView,
 			navigationOptions: {
-				title: 'Room Info',
-				headerTintColor: '#292E35'
+				title: I18n.t('Room_Info'),
+				headerTintColor
 			}
 		},
 		RoomInfoEdit: {
 			screen: RoomInfoEditView,
 			navigationOptions: {
-				title: 'Room Info Edit',
-				headerTintColor: '#292E35'
+				title: I18n.t('Room_Info_Edit'),
+				headerTintColor
 			}
 		}
 	},
diff --git a/app/containers/routes/PublicRoutes.js b/app/containers/routes/PublicRoutes.js
index 99fe3a314..bff89edfc 100644
--- a/app/containers/routes/PublicRoutes.js
+++ b/app/containers/routes/PublicRoutes.js
@@ -13,6 +13,7 @@ import TermsServiceView from '../../views/TermsServiceView';
 import PrivacyPolicyView from '../../views/PrivacyPolicyView';
 import ForgotPasswordView from '../../views/ForgotPasswordView';
 import database from '../../lib/realm';
+import I18n from '../../i18n';
 
 const hasServers = () => {
 	const db = database.databases.serversDB.objects('servers');
@@ -24,12 +25,12 @@ const ServerStack = createStackNavigator({
 		screen: ListServerView,
 		navigationOptions({ navigation }) {
 			return {
-				title: 'Servers',
+				title: I18n.t('Servers'),
 				headerRight: (
 					<TouchableOpacity
 						onPress={() => navigation.navigate({ key: 'AddServer', routeName: 'AddServer' })}
 						style={{ width: 50, alignItems: 'center' }}
-						accessibilityLabel='Add server'
+						accessibilityLabel={I18n.t('Add_Server')}
 						accessibilityTraits='button'
 					>
 						<Icon name='plus' size={16} />
@@ -65,7 +66,7 @@ const LoginStack = createStackNavigator({
 	ForgotPassword: {
 		screen: ForgotPasswordView,
 		navigationOptions: {
-			title: 'Forgot my password',
+			title: I18n.t('Forgot_my_password'),
 			headerTintColor: '#292E35'
 		}
 	}
@@ -83,14 +84,14 @@ const RegisterStack = createStackNavigator({
 	TermsService: {
 		screen: TermsServiceView,
 		navigationOptions: {
-			title: 'Terms of service',
+			title: I18n.t('Terms_of_Service'),
 			headerTintColor: '#292E35'
 		}
 	},
 	PrivacyPolicy: {
 		screen: PrivacyPolicyView,
 		navigationOptions: {
-			title: 'Privacy policy',
+			title: I18n.t('Privacy_Policy'),
 			headerTintColor: '#292E35'
 		}
 	}
diff --git a/app/i18n/index.js b/app/i18n/index.js
new file mode 100644
index 000000000..b130a36bd
--- /dev/null
+++ b/app/i18n/index.js
@@ -0,0 +1,10 @@
+import I18n from 'react-native-i18n';
+import en from './locales/en';
+
+I18n.fallbacks = true;
+
+I18n.translations = {
+	en
+};
+
+export default I18n;
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
new file mode 100644
index 000000000..e2cf96ecb
--- /dev/null
+++ b/app/i18n/locales/en.js
@@ -0,0 +1,216 @@
+export default {
+	'1_online_member': '1 online member',
+	'1_person_reacted': '1 person reacted',
+	Actions: 'Actions',
+	Add_Reaction: 'Add Reaction',
+	Add_Server: 'Add Server',
+	Add_user: 'Add user',
+	Alert: 'Alert',
+	alert: 'alert',
+	alerts: 'alerts',
+	All_users_in_the_channel_can_write_new_messages: 'All users in the channel can write new messages',
+	All: 'All',
+	Allow_Reactions: 'Allow Reactions',
+	and_more: 'and more',
+	and: 'and',
+	announcement: 'announcement',
+	Announcement: 'Announcement',
+	ARCHIVE: 'ARCHIVE',
+	archive: 'archive',
+	are_typing: 'are typing',
+	Are_you_sure_question_mark: 'Are you sure?',
+	Are_you_sure_you_want_to_leave_the_room: 'Are you sure you want to leave the room {{room}}?',
+	Authenticating: 'Authenticating',
+	Away: 'Away',
+	Block_user: 'Block user',
+	Broadcast_channel_Description: 'Only authorized users can write new messages, but the other users will be able to reply',
+	Broadcast_Channel: 'Broadcast Channel',
+	Busy: 'Busy',
+	By_proceeding_you_are_agreeing: 'By proceeding you are agreeing to our',
+	Cancel_editing: 'Cancel editing',
+	Cancel_recording: 'Cancel recording',
+	Cancel: 'Cancel',
+	Channel_Name: 'Channel Name',
+	Close_emoji_selector: 'Close emoji selector',
+	Code: 'Code',
+	Colaborative: 'Colaborative',
+	Connect: 'Connect',
+	Connected_to: 'Connected to',
+	Connecting: 'Connecting',
+	Copied_to_clipboard: 'Copied to clipboard!',
+	Copy_Message: 'Copy Message',
+	Copy_Permalink: 'Copy Permalink',
+	Create_account: 'Create account',
+	Create_Channel: 'Create Channel',
+	Create: 'Create',
+	Delete_Room_Warning: 'Deleting a room will delete all messages posted within the room. This cannot be undone.',
+	delete: 'delete',
+	Delete: 'Delete',
+	DELETE: 'DELETE',
+	description: 'description',
+	Description: 'Description',
+	Disable_notifications: 'Disable notifications',
+	Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
+	edit: 'edit',
+	Edit: 'Edit',
+	Email_or_password_field_is_empty: 'Email or password field is empty',
+	Email: 'Email',
+	Enable_notifications: 'Enable notifications',
+	Everyone_can_access_this_channel: 'Everyone can access this channel',
+	Files: 'Files',
+	Finish_recording: 'Finish recording',
+	Forgot_my_password: 'Forgot my password',
+	Forgot_password_If_this_email_is_registered: 'If this email is registered, we\'ll send instructions on how to reset your password. If you do not receive an email shortly, please come back and try again.',
+	Forgot_password: 'Forgot password',
+	Has_joined_the_channel: 'Has joined the channel',
+	Has_left_the_channel: 'Has left the channel',
+	I_have_an_account: 'I have an account',
+	Invisible: 'Invisible',
+	is_a_valid_RocketChat_instance: 'is a valid Rocket.Chat instance',
+	is_not_a_valid_RocketChat_instance: 'is not a valid Rocket.Chat instance',
+	is_typing: 'is typing',
+	Just_invited_people_can_access_this_channel: 'Just invited people can access this channel',
+	last_message: 'last message',
+	Leave_channel: 'Leave channel',
+	leave: 'leave',
+	Loading_messages_ellipsis: 'Loading messages...',
+	Login: 'Login',
+	Logout: 'Logout',
+	Members: 'Members',
+	Mentioned_Messages: 'Mentioned Messages',
+	mentioned: 'mentioned',
+	Mentions: 'Mentions',
+	Message_accessibility: 'Message from {{user}} at {{time}}: {{message}}',
+	Message_actions: 'Message actions',
+	Message_pinned: 'Message pinned',
+	Message_removed: 'Message removed',
+	Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
+	Microphone_Permission: 'Microphone Permission',
+	Mute: 'Mute',
+	muted: 'muted',
+	My_servers: 'My servers',
+	N_online_members: '{{n}} online members',
+	N_person_reacted: '{{n}} people reacted',
+	Name: 'Name',
+	New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
+	New_Message: 'New Message',
+	New_Server: 'New Server',
+	No_files: 'No files',
+	No_mentioned_messages: 'No mentioned messages',
+	No_pinned_messages: 'No pinned messages',
+	No_snippeted_messages: 'No snippeted messages',
+	No_starred_messages: 'No starred messages',
+	No_announcement_provided: 'No announcement provided.',
+	No_description_provided: 'No description provided.',
+	No_topic_provided: 'No topic provided.',
+	No_Message: 'No Message',
+	No_Reactions: 'No Reactions',
+	Not_logged: 'Not logged',
+	Nothing_to_save: 'Nothing to save!',
+	Notify_active_in_this_room: 'Notify active users in this room',
+	Notify_all_in_this_room: 'Notify all in this room',
+	Offline: 'Offline',
+	Online: 'Online',
+	Only_authorized_users_can_write_new_messages: 'Only authorized users can write new messages',
+	Open_emoji_selector: 'Open emoji selector',
+	Or_continue_using_social_accounts: 'Or continue using social accounts',
+	Password: 'Password',
+	Permalink_copied_to_clipboard: 'Permalink copied to clipboard!',
+	Pin: 'Pin',
+	Pinned_Messages: 'Pinned Messages',
+	pinned: 'pinned',
+	Pinned: 'Pinned',
+	Privacy_Policy: ' Privacy Policy',
+	Private_Channel: 'Private Channel',
+	Private: 'Private',
+	Public_Channel: 'Public Channel',
+	Public: 'Public',
+	Quote: 'Quote',
+	Reactions_are_disabled: 'Reactions are disabled',
+	Reactions_are_enabled: 'Reactions are enabled',
+	Reactions: 'Reactions',
+	Read_Only_Channel: 'Read Only Channel',
+	Read_Only: 'Read Only',
+	Register: 'Register',
+	Repeat_Password: 'Repeat Password',
+	Reply: 'Reply',
+	Resend: 'Resend',
+	Reset_password: 'Reset password',
+	RESET: 'RESET',
+	Roles: 'Roles',
+	Room_actions: 'Room actions',
+	Room_changed_announcement: 'Room announcement changed to: {{announcement}} by {{userBy}}',
+	Room_changed_description: 'Room description changed to: {{description}} by {{userBy}}',
+	Room_changed_privacy: 'Room type changed to: {{type}} by {{userBy}}',
+	Room_changed_topic: 'Room topic changed to: {{topic}} by {{userBy}}',
+	Room_Files: 'Room Files',
+	Room_Info_Edit: 'Room Info Edit',
+	Room_Info: 'Room Info',
+	Room_Members: 'Room Members',
+	Room_name_changed: 'Room name changed to: {{name}} by {{userBy}}',
+	SAVE: 'SAVE',
+	Search_Messages: 'Search Messages',
+	Search: 'Search',
+	Select_Users: 'Select Users',
+	Send_audio_message: 'Send audio message',
+	Send_message: 'Send message',
+	Servers: 'Servers',
+	Settings_succesfully_changed: 'Settings succesfully changed!',
+	Share_Message: 'Share Message',
+	Share: 'Share',
+	Sign_in_your_server: 'Sign in your server',
+	Sign_Up: 'Sign Up',
+	Snippet_Messages: 'Snippet Messages',
+	snippeted: 'snippeted',
+	Snippets: 'Snippets',
+	Some_field_is_invalid_or_empty: 'Some field is invalid or empty',
+	Star_room: 'Star room',
+	Star: 'Star',
+	Starred_Messages: 'Starred Messages',
+	starred: 'starred',
+	Starred: 'Starred',
+	Start_of_conversation: 'Start of conversation',
+	Submit: 'Submit',
+	tap_to_change_status: 'tap to change status',
+	Tap_to_view_servers_list: 'Tap to view servers list',
+	Terms_of_Service: ' Terms of Service ',
+	There_was_an_error_while_saving_settings: 'There was an error while saving settings!',
+	This_room_is_blocked: 'This room is blocked',
+	This_room_is_read_only: 'This room is read only',
+	Timezone: 'Timezone',
+	topic: 'topic',
+	Topic: 'Topic',
+	Type_the_channel_name_here: 'Type the channel name here',
+	unarchive: 'unarchive',
+	UNARCHIVE: 'UNARCHIVE',
+	Unblock_user: 'Unblock user',
+	Unmute: 'Unmute',
+	unmuted: 'unmuted',
+	Unpin: 'Unpin',
+	unread_messages: 'unread messages',
+	Unstar: 'Unstar',
+	User_added_by: 'User {{userAdded}} added by {{userBy}}',
+	User_has_been_key: 'User has been {{key}}!',
+	User_is_no_longer_role_by_: '{{user}} is no longer {{role}} by {{userBy}}',
+	User_muted_by: 'User {{userMuted}} muted by {{userBy}}',
+	User_removed_by: 'User {{userRemoved}} removed by {{userBy}}',
+	User_unmuted_by: 'User {{userUnmuted}} unmuted by {{userBy}}',
+	User_was_set_role_by_: '{{user}} was set {{role}} by {{userBy}}',
+	Username_is_empty: 'Username is empty',
+	Username: 'Username',
+	Validating: 'Validating',
+	Video_call: 'Video call',
+	Voice_call: 'Voice call',
+	Welcome_title_pt_1: 'Prepare to take off with',
+	Welcome_title_pt_2: 'the ultimate chat platform',
+	Yes_action_it: 'Yes, {{action}} it!',
+	Yesterday: 'Yesterday',
+	You_are_in_preview_mode: 'You are in preview mode',
+	You_are_offline: 'You are offline',
+	You_can_search_using_RegExp_eg: 'You can search using RegExp. e.g. `/^text$/i`',
+	You_colon: 'You: ',
+	you_were_mentioned: 'you were mentioned',
+	You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!',
+	you: 'you',
+	Your_server: 'Your server'
+};
diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
index e5d4340d4..371ddaf3d 100644
--- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js
+++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
@@ -2,7 +2,6 @@ import normalizeMessage from './normalizeMessage';
 // TODO: delete and update
 
 export const merge = (subscription, room) => {
-	subscription.muted = [];
 	if (room) {
 		if (room.rid) {
 			subscription.rid = room.rid;
@@ -19,7 +18,9 @@ export const merge = (subscription, room) => {
 		subscription.broadcast = room.broadcast;
 
 		if (room.muted && room.muted.length) {
-			subscription.muted = room.muted.filter(user => user).map(user => ({ value: user }));
+			subscription.muted = room.muted.map(user => ({ value: user }));
+		} else {
+			subscription.muted = [];
 		}
 	}
 	if (subscription.roles && subscription.roles.length) {
diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js
index f2401d698..243d4e3be 100644
--- a/app/presentation/RoomItem.js
+++ b/app/presentation/RoomItem.js
@@ -4,13 +4,13 @@ import PropTypes from 'prop-types';
 import { View, Text, StyleSheet, ViewPropTypes } from 'react-native';
 import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
 import { connect } from 'react-redux';
-// import SimpleMarkdown from 'simple-markdown';
 
 import Avatar from '../containers/Avatar';
 import Status from '../containers/status';
 import Touch from '../utils/touch/index'; //eslint-disable-line
 import Markdown from '../containers/message/Markdown';
 import RoomTypeIcon from '../containers/RoomTypeIcon';
+import I18n from '../i18n';
 
 const styles = StyleSheet.create({
 	container: {
@@ -98,36 +98,6 @@ const styles = StyleSheet.create({
 		marginTop: 3
 	}
 });
-// const markdownStyle = { block: { marginBottom: 0, flexWrap: 'wrap', flexDirection: 'row' } };
-
-// const parseInline = (parse, content, state) => {
-// 	const isCurrentlyInline = state.inline || false;
-// 	state.inline = true;
-// 	const result = parse(content, state);
-// 	state.inline = isCurrentlyInline;
-// 	return result;
-// };
-// const parseCaptureInline = (capture, parse, state) => ({ content: parseInline(parse, capture[1], state) });
-// const customRules = {
-// 	strong: {
-// 		order: -4,
-// 		match: SimpleMarkdown.inlineRegex(/^\*\*([\s\S]+?)\*\*(?!\*)/),
-// 		parse: parseCaptureInline,
-// 		react: (node, output, state) => ({
-// 			type: 'strong',
-// 			key: state.key,
-// 			props: {
-// 				children: output(node.content, state)
-// 			}
-// 		})
-// 	},
-// 	text: {
-// 		order: -3,
-// 		match: SimpleMarkdown.inlineRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]|\n\n| {2,}\n|\w+:\S|$)/),
-// 		parse: capture => ({ content: capture[0] }),
-// 		react: node => node.content
-// 	}
-// };
 
 const renderNumber = (unread, userMentions) => {
 	if (!unread || unread <= 0) {
@@ -207,13 +177,13 @@ export default class RoomItem extends React.Component {
 			return '';
 		}
 		if (!lastMessage) {
-			return 'No Message';
+			return I18n.t('No_Message');
 		}
 
 		let prefix = '';
 
 		if (lastMessage.u.username === this.props.user.username) {
-			prefix = 'You: ';
+			prefix = I18n.t('You_colon');
 		}	else if (type !== 'd') {
 			prefix = `${ lastMessage.u.username }: `;
 		}
@@ -234,7 +204,7 @@ export default class RoomItem extends React.Component {
 	}
 
 	formatDate = date => moment(date).calendar(null, {
-		lastDay: '[Yesterday]',
+		lastDay: `[${ I18n.t('Yesterday') }]`,
 		sameDay: 'h:mm A',
 		lastWeek: 'dddd',
 		sameElse: 'MMM D'
@@ -249,17 +219,17 @@ export default class RoomItem extends React.Component {
 
 		let accessibilityLabel = name;
 		if (unread === 1) {
-			accessibilityLabel += `, ${ unread } alert`;
+			accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`;
 		} else if (unread > 1) {
-			accessibilityLabel += `, ${ unread } alerts`;
+			accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`;
 		}
 
 		if (userMentions > 0) {
-			accessibilityLabel += ', you were mentioned';
+			accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`;
 		}
 
 		if (date) {
-			accessibilityLabel += `, last message ${ date }`;
+			accessibilityLabel += `, ${ I18n.t('last_message') } ${ date }`;
 		}
 
 		return (
diff --git a/app/sagas/messages.js b/app/sagas/messages.js
index 0585e833b..0dff63c69 100644
--- a/app/sagas/messages.js
+++ b/app/sagas/messages.js
@@ -98,7 +98,7 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
 		}
 		yield delay(100);
 		const server = yield select(state => state.server.server);
-		const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id })`;
+		const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id }) `;
 		yield put(setInput({ msg }));
 	} catch (e) {
 		log('handleReplyBroadcast', e);
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index ed221fe3e..bad0fcfad 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -11,6 +11,7 @@ import styles from './Styles';
 import KeyboardView from '../presentation/KeyboardView';
 import scrollPersistTaps from '../utils/scrollPersistTaps';
 import Button from '../containers/Button';
+import I18n from '../i18n';
 
 @connect(
 	state => ({
@@ -22,9 +23,6 @@ import Button from '../containers/Button';
 	})
 )
 export default class CreateChannelView extends LoggedView {
-	static navigationOptions = () => ({
-		title: 'Create a New Channel'
-	});
 	static propTypes = {
 		create: PropTypes.func.isRequired,
 		createChannel: PropTypes.object.isRequired,
@@ -99,8 +97,8 @@ export default class CreateChannelView extends LoggedView {
 		return this.renderSwitch({
 			id: 'type',
 			value: type,
-			label: type ? 'Private Channel' : 'Public Channel',
-			description: type ? 'Just invited people can access this channel' : 'Everyone can access this channel',
+			label: type ? I18n.t('Private_Channel') : I18n.t('Public_Channel'),
+			description: type ? I18n.t('Just_invited_people_can_access_this_channel') : I18n.t('Everyone_can_access_this_channel'),
 			onValueChange: value => this.setState({ type: value })
 		});
 	}
@@ -110,8 +108,8 @@ export default class CreateChannelView extends LoggedView {
 		return this.renderSwitch({
 			id: 'readonly',
 			value: readOnly,
-			label: 'Read Only Channel',
-			description: readOnly ? 'Only authorized users can write new messages' : 'All users in the channel can write new messages',
+			label: I18n.t('Read_Only_Channel'),
+			description: readOnly ? I18n.t('Only_authorized_users_can_write_new_messages') : I18n.t('All_users_in_the_channel_can_write_new_messages'),
 			onValueChange: value => this.setState({ readOnly: value }),
 			disabled: broadcast
 		});
@@ -122,8 +120,8 @@ export default class CreateChannelView extends LoggedView {
 		return this.renderSwitch({
 			id: 'broadcast',
 			value: broadcast,
-			label: 'Broadcast Channel',
-			description: 'Only authorized users can write new messages, but the other users will be able to reply',
+			label: I18n.t('Broadcast_Channel'),
+			description: I18n.t('Broadcast_channel_Description'),
 			onValueChange: (value) => {
 				this.setState({
 					broadcast: value,
@@ -142,10 +140,10 @@ export default class CreateChannelView extends LoggedView {
 				<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
 					<SafeAreaView testID='create-channel-view'>
 						<RCTextInput
-							label='Channel Name'
+							label={I18n.t('Channel_Name')}
 							value={this.state.channelName}
 							onChangeText={channelName => this.setState({ channelName })}
-							placeholder='Type the channel name here'
+							placeholder={I18n.t('Type_the_channel_name_here')}
 							returnKeyType='done'
 							autoFocus
 							testID='create-channel-name'
@@ -156,7 +154,7 @@ export default class CreateChannelView extends LoggedView {
 						{this.renderBroadcast()}
 						<View style={styles.alignItemsFlexStart}>
 							<Button
-								title='Create'
+								title={I18n.t('Create')}
 								type='primary'
 								onPress={this.submit}
 								disabled={this.state.channelName.length === 0 || this.props.createChannel.isFetching}
diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js
index b702ea258..09d317095 100644
--- a/app/views/ForgotPasswordView.js
+++ b/app/views/ForgotPasswordView.js
@@ -12,6 +12,7 @@ import Loading from '../containers/Loading';
 import styles from './Styles';
 import { showErrorAlert } from '../utils/info';
 import scrollPersistTaps from '../utils/scrollPersistTaps';
+import I18n from '../i18n';
 
 @connect(state => ({
 	login: state.login
@@ -45,12 +46,7 @@ export default class ForgotPasswordView extends LoggedView {
 		if (login.success) {
 			this.props.navigation.goBack();
 			setTimeout(() => {
-				showErrorAlert(
-					'If this email is registered, ' +
-					'we\'ll send instructions on how to reset your password. ' +
-					'If you do not receive an email shortly, please come back and try again.',
-					'Alert'
-				);
+				showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
 			});
 		}
 	}
@@ -85,8 +81,8 @@ export default class ForgotPasswordView extends LoggedView {
 							<View style={styles.formContainer}>
 								<TextInput
 									inputStyle={this.state.invalidEmail ? { borderColor: 'red' } : {}}
-									label='Email'
-									placeholder='Email'
+									label={I18n.t('Email')}
+									placeholder={I18n.t('Email')}
 									keyboardType='email-address'
 									returnKeyType='next'
 									onChangeText={email => this.validate(email)}
@@ -96,7 +92,7 @@ export default class ForgotPasswordView extends LoggedView {
 
 								<View style={styles.alignItemsFlexStart}>
 									<Button
-										title='Reset password'
+										title={I18n.t('Reset_password')}
 										type='primary'
 										onPress={this.resetPassword}
 										testID='forgot-password-view-submit'
diff --git a/app/views/ListServerView.js b/app/views/ListServerView.js
index 8dd905838..aee2ea6e6 100644
--- a/app/views/ListServerView.js
+++ b/app/views/ListServerView.js
@@ -12,6 +12,7 @@ import { setServer } from '../actions/server';
 import database from '../lib/realm';
 import Fade from '../animations/fade';
 import Touch from '../utils/touch';
+import I18n from '../i18n';
 
 const styles = StyleSheet.create({
 	view: {
@@ -141,7 +142,7 @@ class ListServerView extends LoggedView {
 
 	getState = () => {
 		const sections = [{
-			title: 'My servers',
+			title: I18n.t('My_servers'),
 			data: this.data
 		}];
 		//
diff --git a/app/views/LoginSignupView.js b/app/views/LoginSignupView.js
index fab993011..2cd391dcc 100644
--- a/app/views/LoginSignupView.js
+++ b/app/views/LoginSignupView.js
@@ -15,6 +15,7 @@ import scrollPersistTaps from '../utils/scrollPersistTaps';
 import random from '../utils/random';
 import Button from '../containers/Button';
 import Loading from '../containers/Loading';
+import I18n from '../i18n';
 
 const userAgentAndroid = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1';
 const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
@@ -205,7 +206,7 @@ export default class LoginSignupView extends LoggedView {
 		return (
 			<View style={styles.servicesContainer}>
 				<Text style={styles.servicesTitle}>
-					Or continue using Social accounts
+					{I18n.t('Or_continue_using_social_accounts')}
 				</Text>
 				<View style={sharedStyles.loginOAuthButtons} key='services'>
 					{this.props.Accounts_OAuth_Facebook && this.props.services.facebook &&
@@ -284,20 +285,20 @@ export default class LoginSignupView extends LoggedView {
 								style={sharedStyles.loginLogo}
 								resizeMode='center'
 							/>
-							<Text style={[sharedStyles.loginText, styles.header, { color: '#81848A' }]}>Prepare to take off with</Text>
-							<Text style={[sharedStyles.loginText, styles.header]}>the ultimate chat platform</Text>
+							<Text style={[sharedStyles.loginText, styles.header, { color: '#81848A' }]}>{I18n.t('Welcome_title_pt_1')}</Text>
+							<Text style={[sharedStyles.loginText, styles.header]}>{I18n.t('Welcome_title_pt_2')}</Text>
 							<Image
 								style={styles.planetImage}
 								source={require('../static/images/planet.png')}
 							/>
 							<Button
-								title='I have an account'
+								title={I18n.t('I_have_an_account')}
 								type='primary'
 								onPress={() => this.props.navigation.navigate({ key: 'Login', routeName: 'Login' })}
 								testID='welcome-view-login'
 							/>
 							<Button
-								title='Create account'
+								title={I18n.t('Create_account')}
 								type='secondary'
 								onPress={() => this.props.navigation.navigate({ key: 'Register', routeName: 'Register' })}
 								testID='welcome-view-register'
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index 0ee6de0d1..25b29853d 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -15,6 +15,7 @@ import scrollPersistTaps from '../utils/scrollPersistTaps';
 import { showToast } from '../utils/info';
 import { COLOR_BUTTON_PRIMARY } from '../constants/colors';
 import LoggedView from './View';
+import I18n from '../i18n';
 
 @connect(state => ({
 	server: state.server.server,
@@ -44,7 +45,7 @@ export default class LoginView extends LoggedView {
 	submit = async() => {
 		const {	username, password, code } = this.state;
 		if (username.trim() === '' || password.trim() === '') {
-			showToast('Email or password field is empty');
+			showToast(I18n.t('Email_or_password_field_is_empty'));
 			return;
 		}
 		Keyboard.dismiss();
@@ -62,9 +63,9 @@ export default class LoginView extends LoggedView {
 			return (
 				<TextInput
 					inputRef={ref => this.codeInput = ref}
-					label='Code'
+					label={I18n.t('Code')}
 					onChangeText={code => this.setState({ code })}
-					placeholder='Code'
+					placeholder={I18n.t('Code')}
 					keyboardType='numeric'
 					returnKeyType='done'
 					autoCapitalize='none'
@@ -87,8 +88,8 @@ export default class LoginView extends LoggedView {
 						<CloseModalButton navigation={this.props.navigation} />
 						<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
 						<TextInput
-							label='Username'
-							placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Username'}
+							label={I18n.t('Username')}
+							placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || I18n.t('Username')}
 							keyboardType='email-address'
 							returnKeyType='next'
 							iconLeft='at'
@@ -99,8 +100,8 @@ export default class LoginView extends LoggedView {
 
 						<TextInput
 							inputRef={(e) => { this.password = e; }}
-							label='Password'
-							placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'}
+							label={I18n.t('Password')}
+							placeholder={this.props.Accounts_PasswordPlaceholder || I18n.t('Password')}
 							returnKeyType='done'
 							iconLeft='key-variant'
 							secureTextEntry
@@ -113,7 +114,7 @@ export default class LoginView extends LoggedView {
 
 						<View style={styles.alignItemsFlexStart}>
 							<Button
-								title='Login'
+								title={I18n.t('Login')}
 								type='primary'
 								onPress={this.submit}
 								testID='login-view-submit'
@@ -122,15 +123,15 @@ export default class LoginView extends LoggedView {
 								style={[styles.loginText, { marginTop: 10 }]}
 								testID='login-view-register'
 								onPress={() => this.props.navigation.navigate('Register')}
-							>New in Rocket.Chat? &nbsp;
-								<Text style={{ color: COLOR_BUTTON_PRIMARY }}>Sign Up
+							>{I18n.t('New_in_RocketChat_question_mark')} &nbsp;
+								<Text style={{ color: COLOR_BUTTON_PRIMARY }}>{I18n.t('Sign_Up')}
 								</Text>
 							</Text>
 							<Text
 								style={[styles.loginText, { marginTop: 20, fontSize: 13 }]}
 								onPress={() => this.props.navigation.navigate('ForgotPassword')}
 								testID='login-view-forgot-password'
-							>Forgot password
+							>{I18n.t('Forgot_password')}
 							</Text>
 						</View>
 
diff --git a/app/views/MentionedMessagesView/index.js b/app/views/MentionedMessagesView/index.js
index b6add2799..226153cb1 100644
--- a/app/views/MentionedMessagesView/index.js
+++ b/app/views/MentionedMessagesView/index.js
@@ -8,6 +8,7 @@ import { openMentionedMessages, closeMentionedMessages } from '../../actions/men
 import styles from './styles';
 import Message from '../../containers/message';
 import RCActivityIndicator from '../../containers/ActivityIndicator';
+import I18n from '../../i18n';
 
 @connect(
 	state => ({
@@ -74,7 +75,7 @@ export default class MentionedMessagesView extends LoggedView {
 
 	renderEmpty = () => (
 		<View style={styles.listEmptyContainer} testID='mentioned-messages-view'>
-			<Text>No mentioned messages</Text>
+			<Text>{I18n.t('No_mentioned_messages')}</Text>
 		</View>
 	)
 
diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js
index 28562495c..2b3906fd5 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView.js
@@ -11,6 +11,7 @@ import Button from '../containers/Button';
 import TextInput from '../containers/TextInput';
 import Loading from '../containers/Loading';
 import LoggedView from './View';
+import I18n from '../i18n';
 
 @connect(state => ({
 	validInstance: !state.server.failure && !state.server.connecting,
@@ -81,7 +82,7 @@ export default class NewServerView extends LoggedView {
 		if (this.props.validating) {
 			return (
 				<Text style={[styles.validateText, styles.validatingText]}>
-					Validating {this.state.text || 'open'} ...
+					{I18n.t('Validating')} {this.state.text || 'open'} ...
 				</Text>
 			);
 		}
@@ -89,13 +90,13 @@ export default class NewServerView extends LoggedView {
 		if (this.props.validInstance) {
 			return (
 				<Text style={[styles.validateText, styles.validText]}>
-					{this.state.url} is a valid Rocket.Chat instance
+					{this.state.url} {I18n.t('is_a_valid_RocketChat_instance')}
 				</Text>
 			);
 		}
 		return (
 			<Text style={[styles.validateText, styles.invalidText]}>
-				{this.state.url} is not a valid Rocket.Chat instance
+				{this.state.url} {I18n.t('is_not_a_valid_RocketChat_instance')}
 			</Text>
 		);
 	}
@@ -109,11 +110,11 @@ export default class NewServerView extends LoggedView {
 			>
 				<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
 					<SafeAreaView testID='new-server-view'>
-						<Text style={[styles.loginText, styles.loginTitle]}>Sign in your server</Text>
+						<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_in_your_server')}</Text>
 						<TextInput
 							inputRef={e => this.input = e}
 							containerStyle={{ marginBottom: 5 }}
-							label='Your server'
+							label={I18n.t('Your_server')}
 							placeholder={this.state.defaultServer}
 							returnKeyType='done'
 							onChangeText={this.onChangeText}
@@ -123,7 +124,7 @@ export default class NewServerView extends LoggedView {
 						{this.renderValidation()}
 						<View style={[styles.alignItemsFlexStart, { marginTop: 20 }]}>
 							<Button
-								title='Connect'
+								title={I18n.t('Connect')}
 								type='primary'
 								onPress={this.submit}
 								disabled={!validInstance}
diff --git a/app/views/PinnedMessagesView/index.js b/app/views/PinnedMessagesView/index.js
index 5a96b380f..107be23b0 100644
--- a/app/views/PinnedMessagesView/index.js
+++ b/app/views/PinnedMessagesView/index.js
@@ -10,10 +10,11 @@ import styles from './styles';
 import Message from '../../containers/message';
 import { togglePinRequest } from '../../actions/messages';
 import RCActivityIndicator from '../../containers/ActivityIndicator';
+import I18n from '../../i18n';
 
 const PIN_INDEX = 0;
 const CANCEL_INDEX = 1;
-const options = ['Unpin', 'Cancel'];
+const options = [I18n.t('Unpin'), I18n.t('Cancel')];
 
 @connect(
 	state => ({
@@ -98,7 +99,7 @@ export default class PinnedMessagesView extends LoggedView {
 
 	renderEmpty = () => (
 		<View style={styles.listEmptyContainer} testID='pinned-messages-view'>
-			<Text>No pinned messages</Text>
+			<Text>{I18n.t('No_pinned_messages')}</Text>
 		</View>
 	)
 
@@ -138,7 +139,7 @@ export default class PinnedMessagesView extends LoggedView {
 				<ActionSheet
 					key='pinned-messages-view-action-sheet'
 					ref={o => this.actionSheet = o}
-					title='Actions'
+					title={I18n.t('Actions')}
 					options={options}
 					cancelButtonIndex={CANCEL_INDEX}
 					onPress={this.handleActionPress}
diff --git a/app/views/PrivacyPolicyView.js b/app/views/PrivacyPolicyView.js
index e4f96abc1..8a78a70b3 100644
--- a/app/views/PrivacyPolicyView.js
+++ b/app/views/PrivacyPolicyView.js
@@ -3,15 +3,11 @@ import PropTypes from 'prop-types';
 import { WebView } from 'react-native';
 import { connect } from 'react-redux';
 
-class PrivacyPolicyView extends React.Component {
+class PrivacyPolicyView extends React.PureComponent {
 	static propTypes = {
 		privacyPolicy: PropTypes.string
 	}
 
-	static navigationOptions = () => ({
-		title: 'Terms of service'
-	});
-
 	render() {
 		return (
 			<WebView source={{ html: this.props.privacyPolicy }} />
diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js
index 364572fe4..caeb72613 100644
--- a/app/views/RegisterView.js
+++ b/app/views/RegisterView.js
@@ -13,6 +13,7 @@ import { showToast } from '../utils/info';
 import CloseModalButton from '../containers/CloseModalButton';
 import scrollPersistTaps from '../utils/scrollPersistTaps';
 import LoggedView from './View';
+import I18n from '../i18n';
 
 @connect(state => ({
 	server: state.server.server,
@@ -65,7 +66,7 @@ export default class RegisterView extends LoggedView {
 			name, email, password, code
 		} = this.state;
 		if (!this.valid()) {
-			showToast('Some field is invalid or empty');
+			showToast(I18n.t('Some_field_is_invalid_or_empty'));
 			return;
 		}
 
@@ -78,7 +79,7 @@ export default class RegisterView extends LoggedView {
 	usernameSubmit = () => {
 		const { username } = this.state;
 		if (!username) {
-			showToast('Username is empty');
+			showToast(I18n.t('Username_is_empty'));
 			return;
 		}
 
@@ -102,8 +103,8 @@ export default class RegisterView extends LoggedView {
 			<View>
 				<TextInput
 					inputRef={(e) => { this.name = e; }}
-					label={this.props.Accounts_NamePlaceholder || 'Name'}
-					placeholder={this.props.Accounts_NamePlaceholder || 'Name'}
+					label={this.props.Accounts_NamePlaceholder || I18n.t('Name')}
+					placeholder={this.props.Accounts_NamePlaceholder || I18n.t('Name')}
 					returnKeyType='next'
 					iconLeft='account'
 					onChangeText={name => this.setState({ name })}
@@ -112,8 +113,8 @@ export default class RegisterView extends LoggedView {
 				/>
 				<TextInput
 					inputRef={(e) => { this.email = e; }}
-					label={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email'}
-					placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email'}
+					label={this.props.Accounts_EmailOrUsernamePlaceholder || I18n.t('Email')}
+					placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || I18n.t('Email')}
 					returnKeyType='next'
 					keyboardType='email-address'
 					iconLeft='email'
@@ -124,8 +125,8 @@ export default class RegisterView extends LoggedView {
 				/>
 				<TextInput
 					inputRef={(e) => { this.password = e; }}
-					label={this.props.Accounts_PasswordPlaceholder || 'Password'}
-					placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'}
+					label={this.props.Accounts_PasswordPlaceholder || I18n.t('Password')}
+					placeholder={this.props.Accounts_PasswordPlaceholder || I18n.t('Password')}
 					returnKeyType='next'
 					iconLeft='key-variant'
 					secureTextEntry
@@ -140,8 +141,8 @@ export default class RegisterView extends LoggedView {
 						this.state.confirmPassword &&
 						this.state.confirmPassword !== this.state.password ? { borderColor: 'red' } : {}
 					}
-					label={this.props.Accounts_RepeatPasswordPlaceholder || 'Repeat Password'}
-					placeholder={this.props.Accounts_RepeatPasswordPlaceholder || 'Repeat Password'}
+					label={this.props.Accounts_RepeatPasswordPlaceholder || I18n.t('Repeat_Password')}
+					placeholder={this.props.Accounts_RepeatPasswordPlaceholder || I18n.t('Repeat_Password')}
 					returnKeyType='done'
 					iconLeft='key-variant'
 					secureTextEntry
@@ -152,13 +153,13 @@ export default class RegisterView extends LoggedView {
 
 				<View style={styles.alignItemsFlexStart}>
 					<Text style={styles.loginTermsText}>
-						By proceeding you are agreeing to our
-						<Text style={styles.link} onPress={this.termsService}> Terms of Service </Text>
-						and
-						<Text style={styles.link} onPress={this.privacyPolicy}> Privacy Policy</Text>
+						{I18n.t('By_proceeding_you_are_agreeing')}
+						<Text style={styles.link} onPress={this.termsService}>{I18n.t('Terms_of_Service')}</Text>
+						{I18n.t('and')}
+						<Text style={styles.link} onPress={this.privacyPolicy}>{I18n.t('Privacy_Policy')}</Text>
 					</Text>
 					<Button
-						title='Register'
+						title={I18n.t('Register')}
 						type='primary'
 						onPress={this.submit}
 						testID='register-view-submit'
@@ -176,8 +177,8 @@ export default class RegisterView extends LoggedView {
 			<View>
 				<TextInput
 					inputRef={(e) => { this.username = e; }}
-					label={this.props.Accounts_UsernamePlaceholder || 'Username'}
-					placeholder={this.props.Accounts_UsernamePlaceholder || 'Username'}
+					label={this.props.Accounts_UsernamePlaceholder || I18n.t('Username')}
+					placeholder={this.props.Accounts_UsernamePlaceholder || I18n.t('Username')}
 					returnKeyType='done'
 					iconLeft='at'
 					onChangeText={username => this.setState({ username })}
@@ -187,7 +188,7 @@ export default class RegisterView extends LoggedView {
 
 				<View style={styles.alignItemsFlexStart}>
 					<Button
-						title='Register'
+						title={I18n.t('Register')}
 						type='primary'
 						onPress={this.usernameSubmit}
 						testID='register-view-submit-username'
@@ -203,7 +204,7 @@ export default class RegisterView extends LoggedView {
 				<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
 					<SafeAreaView testID='register-view'>
 						<CloseModalButton navigation={this.props.navigation} />
-						<Text style={[styles.loginText, styles.loginTitle]}>Sign Up</Text>
+						<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
 						{this._renderRegister()}
 						{this._renderUsername()}
 						{this.props.login.failure &&
diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js
index 02b31881d..52aef0c93 100644
--- a/app/views/RoomActionsView/index.js
+++ b/app/views/RoomActionsView/index.js
@@ -17,6 +17,7 @@ import { leaveRoom } from '../../actions/room';
 import { setLoading } from '../../actions/selectedUsers';
 import log from '../../utils/log';
 import RoomTypeIcon from '../../containers/RoomTypeIcon';
+import I18n from '../../i18n';
 
 const renderSeparator = () => <View style={styles.separator} />;
 const getRoomTitle = room => (room.t === 'd' ? <Text>{room.fname}</Text> : <Text><RoomTypeIcon type={room.t} />&nbsp;{room.name}</Text>);
@@ -158,13 +159,13 @@ export default class RoomActionsView extends LoggedView {
 			data: [
 				{
 					icon: 'ios-call-outline',
-					name: 'Voice call',
+					name: I18n.t('Voice_call'),
 					disabled: true,
 					testID: 'room-actions-voice'
 				},
 				{
 					icon: 'ios-videocam-outline',
-					name: 'Video call',
+					name: I18n.t('Video_call'),
 					disabled: true,
 					testID: 'room-actions-video'
 				}
@@ -174,55 +175,55 @@ export default class RoomActionsView extends LoggedView {
 			data: [
 				{
 					icon: 'ios-attach',
-					name: 'Files',
+					name: I18n.t('Files'),
 					route: 'RoomFiles',
 					params: { rid },
 					testID: 'room-actions-files'
 				},
 				{
 					icon: 'ios-at-outline',
-					name: 'Mentions',
+					name: I18n.t('Mentions'),
 					route: 'MentionedMessages',
 					params: { rid },
 					testID: 'room-actions-mentioned'
 				},
 				{
 					icon: 'ios-star-outline',
-					name: 'Starred',
+					name: I18n.t('Starred'),
 					route: 'StarredMessages',
 					params: { rid },
 					testID: 'room-actions-starred'
 				},
 				{
 					icon: 'ios-search',
-					name: 'Search',
+					name: I18n.t('Search'),
 					route: 'SearchMessages',
 					params: { rid },
 					testID: 'room-actions-search'
 				},
 				{
 					icon: 'ios-share-outline',
-					name: 'Share',
+					name: I18n.t('Share'),
 					disabled: true,
 					testID: 'room-actions-share'
 				},
 				{
 					icon: 'ios-pin',
-					name: 'Pinned',
+					name: I18n.t('Pinned'),
 					route: 'PinnedMessages',
 					params: { rid },
 					testID: 'room-actions-pinned'
 				},
 				{
 					icon: 'ios-code',
-					name: 'Snippets',
+					name: I18n.t('Snippets'),
 					route: 'SnippetedMessages',
 					params: { rid },
 					testID: 'room-actions-snippeted'
 				},
 				{
 					icon: `ios-notifications${ notifications ? '' : '-off' }-outline`,
-					name: `${ notifications ? 'Enable' : 'Disable' } notifications`,
+					name: I18n.t(`${ notifications ? 'Enable' : 'Disable' }_notifications`),
 					event: () => this.toggleNotifications(),
 					testID: 'room-actions-notifications'
 				}
@@ -235,7 +236,7 @@ export default class RoomActionsView extends LoggedView {
 				data: [
 					{
 						icon: 'block',
-						name: `${ blocker ? 'Unblock' : 'Block' } user`,
+						name: I18n.t(`${ blocker ? 'Unblock' : 'Block' }_user`),
 						type: 'danger',
 						event: () => this.toggleBlockUser(),
 						testID: 'room-actions-block-user'
@@ -249,8 +250,10 @@ export default class RoomActionsView extends LoggedView {
 			if (this.canViewMembers) {
 				actions.push({
 					icon: 'ios-people',
-					name: 'Members',
-					description: (onlineMembers.length === 1 ? `${ onlineMembers.length } member` : `${ onlineMembers.length } members`),
+					name: I18n.t('Members'),
+					description: (onlineMembers.length === 1 ?
+						I18n.t('1_online_member') :
+						I18n.t('N_online_members', { n: onlineMembers.length })),
 					route: 'RoomMembers',
 					params: { rid, members: onlineMembers },
 					testID: 'room-actions-members'
@@ -260,7 +263,7 @@ export default class RoomActionsView extends LoggedView {
 			if (this.canAddUser) {
 				actions.push({
 					icon: 'ios-person-add',
-					name: 'Add user',
+					name: I18n.t('Add_user'),
 					route: 'SelectedUsers',
 					params: {
 						nextAction: async() => {
@@ -283,7 +286,7 @@ export default class RoomActionsView extends LoggedView {
 				data: [
 					{
 						icon: 'block',
-						name: 'Leave channel',
+						name: I18n.t('Leave_channel'),
 						type: 'danger',
 						event: () => this.leaveChannel(),
 						testID: 'room-actions-leave-channel'
@@ -308,15 +311,15 @@ export default class RoomActionsView extends LoggedView {
 	leaveChannel = () => {
 		const { room } = this.state;
 		Alert.alert(
-			'Are you sure?',
-			`Are you sure you want to leave the room ${ getRoomTitle(room) }?`,
+			I18n.t('Are_you_sure_question_mark'),
+			I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: room.t === 'd' ? room.fname : room.name }),
 			[
 				{
-					text: 'Cancel',
+					text: I18n.t('Cancel'),
 					style: 'cancel'
 				},
 				{
-					text: 'Yes, leave it!',
+					text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
 					style: 'destructive',
 					onPress: () => this.props.leaveRoom(room.rid)
 				}
diff --git a/app/views/RoomFilesView/index.js b/app/views/RoomFilesView/index.js
index 3061a32c0..8a4941cbd 100644
--- a/app/views/RoomFilesView/index.js
+++ b/app/views/RoomFilesView/index.js
@@ -8,6 +8,7 @@ import { openRoomFiles, closeRoomFiles } from '../../actions/roomFiles';
 import styles from './styles';
 import Message from '../../containers/message';
 import RCActivityIndicator from '../../containers/ActivityIndicator';
+import I18n from '../../i18n';
 
 @connect(
 	state => ({
@@ -74,7 +75,7 @@ export default class RoomFilesView extends LoggedView {
 
 	renderEmpty = () => (
 		<View style={styles.listEmptyContainer} testID='room-files-view'>
-			<Text>No files</Text>
+			<Text>{I18n.t('No_files')}</Text>
 		</View>
 	)
 
diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js
index cd98cae9a..95effd731 100644
--- a/app/views/RoomInfoEditView/index.js
+++ b/app/views/RoomInfoEditView/index.js
@@ -17,6 +17,7 @@ import Loading from '../../containers/Loading';
 import SwitchContainer from './SwitchContainer';
 import random from '../../utils/random';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 const PERMISSION_SET_READONLY = 'set-readonly';
 const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
@@ -133,7 +134,7 @@ export default class RoomInfoEditView extends LoggedView {
 		let error = false;
 
 		if (!this.formIsChanged()) {
-			showErrorAlert('Nothing to save!');
+			showErrorAlert(I18n.t('Nothing_to_save'));
 			return;
 		}
 
@@ -189,24 +190,24 @@ export default class RoomInfoEditView extends LoggedView {
 		await this.setState({ saving: false });
 		setTimeout(() => {
 			if (error) {
-				showErrorAlert('There was an error while saving settings!');
+				showErrorAlert(I18n.t('There_was_an_error_while_saving_settings'));
 			} else {
-				showToast('Settings succesfully changed!');
+				showToast(I18n.t('Settings_succesfully_changed'));
 			}
 		}, 100);
 	}
 
 	delete = () => {
 		Alert.alert(
-			'Are you sure?',
-			'Deleting a room will delete all messages posted within the room. This cannot be undone.',
+			I18n.t('Are_you_sure_question_mark'),
+			I18n.t('Delete_Room_Warning'),
 			[
 				{
-					text: 'Cancel',
+					text: I18n.t('Cancel'),
 					style: 'cancel'
 				},
 				{
-					text: 'Yes, delete it!',
+					text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
 					style: 'destructive',
 					onPress: () => this.props.eraseRoom(this.state.room.rid)
 				}
@@ -217,17 +218,17 @@ export default class RoomInfoEditView extends LoggedView {
 
 	toggleArchive = () => {
 		const { archived } = this.state.room;
-		const action = `${ archived ? 'un' : '' }archive`;
+		const action = I18n.t(`${ archived ? 'un' : '' }archive`);
 		Alert.alert(
-			'Are you sure?',
-			`Do you really want to ${ action } this room?`,
+			I18n.t('Are_you_sure_question_mark'),
+			I18n.t('Do_you_really_want_to_key_this_room_question_mark', { key: action }),
 			[
 				{
-					text: 'Cancel',
+					text: I18n.t('Cancel'),
 					style: 'cancel'
 				},
 				{
-					text: `Yes, ${ action } it!`,
+					text: I18n.t('Yes_action_it', { action }),
 					style: 'destructive',
 					onPress: () => {
 						try {
@@ -268,7 +269,7 @@ export default class RoomInfoEditView extends LoggedView {
 						<View style={sharedStyles.formContainer}>
 							<RCTextInput
 								inputRef={(e) => { this.name = e; }}
-								label='Name'
+								label={I18n.t('Name')}
 								value={name}
 								onChangeText={value => this.setState({ name: value })}
 								onSubmitEditing={() => { this.description.focus(); }}
@@ -277,7 +278,7 @@ export default class RoomInfoEditView extends LoggedView {
 							/>
 							<RCTextInput
 								inputRef={(e) => { this.description = e; }}
-								label='Description'
+								label={I18n.t('Description')}
 								value={description}
 								onChangeText={value => this.setState({ description: value })}
 								onSubmitEditing={() => { this.topic.focus(); }}
@@ -285,7 +286,7 @@ export default class RoomInfoEditView extends LoggedView {
 							/>
 							<RCTextInput
 								inputRef={(e) => { this.topic = e; }}
-								label='Topic'
+								label={I18n.t('Topic')}
 								value={topic}
 								onChangeText={value => this.setState({ topic: value })}
 								onSubmitEditing={() => { this.announcement.focus(); }}
@@ -293,7 +294,7 @@ export default class RoomInfoEditView extends LoggedView {
 							/>
 							<RCTextInput
 								inputRef={(e) => { this.announcement = e; }}
-								label='Announcement'
+								label={I18n.t('Announcement')}
 								value={announcement}
 								onChangeText={value => this.setState({ announcement: value })}
 								onSubmitEditing={() => { this.joinCode.focus(); }}
@@ -301,7 +302,7 @@ export default class RoomInfoEditView extends LoggedView {
 							/>
 							<RCTextInput
 								inputRef={(e) => { this.joinCode = e; }}
-								label='Password'
+								label={I18n.t('Password')}
 								value={joinCode}
 								onChangeText={value => this.setState({ joinCode: value })}
 								onSubmitEditing={this.submit}
@@ -310,19 +311,19 @@ export default class RoomInfoEditView extends LoggedView {
 							/>
 							<SwitchContainer
 								value={t}
-								leftLabelPrimary='Public'
-								leftLabelSecondary='Everyone can access this channel'
-								rightLabelPrimary='Private'
-								rightLabelSecondary='Just invited people can access this channel'
+								leftLabelPrimary={I18n.t('Public')}
+								leftLabelSecondary={I18n.t('Everyone_can_access_this_channel')}
+								rightLabelPrimary={I18n.t('Private')}
+								rightLabelSecondary={I18n.t('Just_invited_people_can_access_this_channel')}
 								onValueChange={value => this.setState({ t: value })}
 								testID='room-info-edit-view-t'
 							/>
 							<SwitchContainer
 								value={ro}
-								leftLabelPrimary='Colaborative'
-								leftLabelSecondary='All users in the channel can write new messages'
-								rightLabelPrimary='Read Only'
-								rightLabelSecondary='Only authorized users can write new messages'
+								leftLabelPrimary={I18n.t('Colaborative')}
+								leftLabelSecondary={I18n.t('All_users_in_the_channel_can_write_new_messages')}
+								rightLabelPrimary={I18n.t('Read_Only')}
+								rightLabelSecondary={I18n.t('Only_authorized_users_can_write_new_messages')}
 								onValueChange={value => this.setState({ ro: value })}
 								disabled={!this.permissions[PERMISSION_SET_READONLY] || room.broadcast}
 								testID='room-info-edit-view-ro'
@@ -330,10 +331,10 @@ export default class RoomInfoEditView extends LoggedView {
 							{ro && !room.broadcast &&
 								<SwitchContainer
 									value={reactWhenReadOnly}
-									leftLabelPrimary='No Reactions'
-									leftLabelSecondary='Reactions are disabled'
-									rightLabelPrimary='Allow Reactions'
-									rightLabelSecondary='Reactions are enabled'
+									leftLabelPrimary={I18n.t('No_Reactions')}
+									leftLabelSecondary={I18n.t('Reactions_are_disabled')}
+									rightLabelPrimary={I18n.t('Allow_Reactions')}
+									rightLabelSecondary={I18n.t('Reactions_are_enabled')}
 									onValueChange={value => this.setState({ reactWhenReadOnly: value })}
 									disabled={!this.permissions[PERMISSION_SET_REACT_WHEN_READONLY]}
 									testID='room-info-edit-view-react-when-ro'
@@ -341,7 +342,7 @@ export default class RoomInfoEditView extends LoggedView {
 							}
 							{room.broadcast &&
 								[
-									<Text style={styles.broadcast}>Broadcast channel</Text>,
+									<Text style={styles.broadcast}>{I18n.t('Broadcast_Channel')}</Text>,
 									<View style={styles.divider} />
 								]
 							}
@@ -351,7 +352,7 @@ export default class RoomInfoEditView extends LoggedView {
 								disabled={!this.formIsChanged()}
 								testID='room-info-edit-view-submit'
 							>
-								<Text style={sharedStyles.button} accessibilityTraits='button'>SAVE</Text>
+								<Text style={sharedStyles.button} accessibilityTraits='button'>{I18n.t('SAVE')}</Text>
 							</TouchableOpacity>
 							<View style={{ flexDirection: 'row' }}>
 								<TouchableOpacity
@@ -359,7 +360,7 @@ export default class RoomInfoEditView extends LoggedView {
 									onPress={this.reset}
 									testID='room-info-edit-view-reset'
 								>
-									<Text style={sharedStyles.button_inverted} accessibilityTraits='button'>RESET</Text>
+									<Text style={sharedStyles.button_inverted} accessibilityTraits='button'>{I18n.t('RESET')}</Text>
 								</TouchableOpacity>
 								<TouchableOpacity
 									style={[
@@ -373,7 +374,7 @@ export default class RoomInfoEditView extends LoggedView {
 									testID='room-info-edit-view-archive'
 								>
 									<Text style={[sharedStyles.button_inverted, styles.colorDanger]} accessibilityTraits='button'>
-										{ room.archived ? 'UNARCHIVE' : 'ARCHIVE' }
+										{ room.archived ? I18n.t('UNARCHIVE') : I18n.t('ARCHIVE') }
 									</Text>
 								</TouchableOpacity>
 							</View>
@@ -389,7 +390,7 @@ export default class RoomInfoEditView extends LoggedView {
 								disabled={!this.hasDeletePermission()}
 								testID='room-info-edit-view-delete'
 							>
-								<Text style={[sharedStyles.button_inverted, styles.colorDanger]} accessibilityTraits='button'>DELETE</Text>
+								<Text style={[sharedStyles.button_inverted, styles.colorDanger]} accessibilityTraits='button'>{I18n.t('DELETE')}</Text>
 							</TouchableOpacity>
 						</View>
 						<Loading visible={this.state.saving} />
diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js
index 369b4e3a1..68209a655 100644
--- a/app/views/RoomInfoView/index.js
+++ b/app/views/RoomInfoView/index.js
@@ -16,6 +16,7 @@ import Touch from '../../utils/touch';
 
 import log from '../../utils/log';
 import RoomTypeIcon from '../../containers/RoomTypeIcon';
+import I18n from '../../i18n';
 
 const PERMISSION_EDIT_ROOM = 'edit-room';
 
@@ -56,7 +57,7 @@ export default class RoomInfoView extends LoggedView {
 					onPress={() => navigation.navigate({ key: 'RoomInfoEdit', routeName: 'RoomInfoEdit', params: { rid: navigation.state.params.rid } })}
 					underlayColor='#ffffff'
 					activeOpacity={0.5}
-					accessibilityLabel='edit'
+					accessibilityLabel={I18n.t('edit')}
 					accessibilityTraits='button'
 					testID='room-info-view-edit-button'
 				>
@@ -132,14 +133,14 @@ export default class RoomInfoView extends LoggedView {
 		const [room] = this.rooms;
 		this.setState({ room });
 	}
-	// TODO: translate
+
 	renderItem = (key, room) => (
 		<View style={styles.item}>
-			<Text style={styles.itemLabel}>{camelize(key)}</Text>
+			<Text style={styles.itemLabel}>{I18n.t(camelize(key))}</Text>
 			<Text
 				style={[styles.itemContent, !room[key] && styles.itemContent__empty]}
 				testID={`room-info-view-${ key }`}
-			>{ room[key] ? room[key] : `No ${ key } provided.` }
+			>{ room[key] ? room[key] : I18n.t(`No_${ key }_provided`) }
 			</Text>
 		</View>
 	);
@@ -147,7 +148,7 @@ export default class RoomInfoView extends LoggedView {
 	renderRoles = () => (
 		this.state.roles.length > 0 &&
 		<View style={styles.item}>
-			<Text style={styles.itemLabel}>Roles</Text>
+			<Text style={styles.itemLabel}>{I18n.t('Roles')}</Text>
 			<View style={styles.rolesContainer}>
 				{this.state.roles.map(role => (
 					<View style={styles.roleBadge} key={role}>
@@ -168,7 +169,7 @@ export default class RoomInfoView extends LoggedView {
 			// TODO: translate
 			return (
 				<View style={styles.item}>
-					<Text style={styles.itemLabel}>Timezone</Text>
+					<Text style={styles.itemLabel}>{I18n.t('Timezone')}</Text>
 					<Text style={styles.itemContent}>{moment().utcOffset(utcOffset).format(this.props.Message_TimeFormat)} (UTC { utcOffset })</Text>
 				</View>
 			);
@@ -189,11 +190,11 @@ export default class RoomInfoView extends LoggedView {
 
 	renderBroadcast = () => (
 		<View style={styles.item}>
-			<Text style={styles.itemLabel}>Broadcast Channel</Text>
+			<Text style={styles.itemLabel}>{I18n.t('Broadcast_Channel')}</Text>
 			<Text
 				style={styles.itemContent}
 				testID='room-info-view-broadcast'
-			>Only authorized users can write new messages, but the other users will be able to reply
+			>{I18n.t('Broadcast_channel_Description')}
 			</Text>
 		</View>
 	)
diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js
index 8c6e9286e..68fc2d0cf 100644
--- a/app/views/RoomMembersView/index.js
+++ b/app/views/RoomMembersView/index.js
@@ -14,6 +14,7 @@ import { goRoom } from '../../containers/routes/NavigationService';
 import database from '../../lib/realm';
 import { showToast } from '../../utils/info';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 @connect(state => ({
 	user: state.login.user,
@@ -26,7 +27,7 @@ export default class MentionedMessagesView extends LoggedView {
 
 	static navigationOptions = ({ navigation }) => {
 		const params = navigation.state.params || {};
-		const label = params.allUsers ? 'All' : 'Online';
+		const label = params.allUsers ? I18n.t('All') : I18n.t('Online');
 		if (params.allUsers === undefined) {
 			return;
 		}
@@ -123,14 +124,14 @@ export default class MentionedMessagesView extends LoggedView {
 		if (!this.permissions['mute-user']) {
 			return;
 		}
-		this.actionSheetOptions = ['Cancel'];
+		this.actionSheetOptions = [I18n.t('Cancel')];
 		const { muted } = this.state.room;
 		const userIsMuted = !!muted.find(m => m.value === user.username);
 		user.muted = userIsMuted;
 		if (userIsMuted) {
-			this.actionSheetOptions.push('Unmute');
+			this.actionSheetOptions.push(I18n.t('Unmute'));
 		} else {
-			this.actionSheetOptions.push('Mute');
+			this.actionSheetOptions.push(I18n.t('Mute'));
 		}
 		this.setState({ userLongPressed: user });
 		Vibration.vibrate(50);
@@ -141,7 +142,7 @@ export default class MentionedMessagesView extends LoggedView {
 		const { rid, userLongPressed } = this.state;
 		try {
 			await RocketChat.toggleMuteUserInRoom(rid, userLongPressed.username, !userLongPressed.muted);
-			showToast(`User has been ${ userLongPressed.muted ? 'unmuted' : 'muted' }!`);
+			showToast(I18n.t('User_has_been_key', { key: userLongPressed.muted ? I18n.t('unmuted') : I18n.t('muted') }));
 		} catch (e) {
 			log('handleMute', e);
 		}
@@ -164,7 +165,7 @@ export default class MentionedMessagesView extends LoggedView {
 				style={styles.searchBox}
 				onChangeText={text => this.onSearchChangeText(text)}
 				returnKeyType='search'
-				placeholder='Search'
+				placeholder={I18n.t('Search')}
 				clearButtonMode='while-editing'
 				blurOnSubmit
 				autoCorrect={false}
@@ -209,7 +210,7 @@ export default class MentionedMessagesView extends LoggedView {
 				<ActionSheet
 					key='room-members-actionsheet'
 					ref={o => this.ActionSheet = o}
-					title='Actions'
+					title={I18n.t('Actions')}
 					options={this.actionSheetOptions}
 					cancelButtonIndex={this.CANCEL_INDEX}
 					onPress={this.handleActionPress}
diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js
index 1301a8571..275150aad 100644
--- a/app/views/RoomView/Header/index.js
+++ b/app/views/RoomView/Header/index.js
@@ -11,28 +11,28 @@ import Avatar from '../../../containers/Avatar';
 import { STATUS_COLORS } from '../../../constants/colors';
 import styles from './styles';
 import { closeRoom } from '../../../actions/room';
-
 import log from '../../../utils/log';
 import RoomTypeIcon from '../../../containers/RoomTypeIcon';
+import I18n from '../../../i18n';
 
 const title = (offline, connecting, authenticating, logged) => {
 	if (offline) {
-		return 'You are offline...';
+		return `${ I18n.t('You_are_offline') }...`;
 	}
 
 	if (connecting) {
-		return 'Connecting...';
+		return `${ I18n.t('Connecting') }...`;
 	}
 
 	if (authenticating) {
-		return 'Authenticating...';
+		return `${ I18n.t('Authenticating') }...`;
 	}
 
 	if (logged) {
 		return null;
 	}
 
-	return 'Not logged...';
+	return `${ I18n.t('Not_logged') }...`;
 };
 
 @connect(state => ({
@@ -87,7 +87,7 @@ export default class RoomHeaderView extends React.PureComponent {
 
 	getUserStatusLabel() {
 		const status = this.getUserStatus();
-		return status.charAt(0).toUpperCase() + status.slice(1);
+		return I18n.t(status.charAt(0).toUpperCase() + status.slice(1));
 	}
 
 	updateState = () => {
@@ -104,7 +104,7 @@ export default class RoomHeaderView extends React.PureComponent {
 			requestAnimationFrame(() => this.props.close());
 		}}
 		tintColor='#292E35'
-		title='Back'
+		title={I18n.t('Back')}
 		titleStyle={{ display: 'none' }}
 	/>);
 
@@ -124,7 +124,7 @@ export default class RoomHeaderView extends React.PureComponent {
 
 		let t = '';
 		if (!title(offline, connecting, authenticating, logged) && loading) {
-			t = 'Loading messages...';
+			t = I18n.t('Loading_messages_ellipsis');
 		} else if (this.isDirect()) {
 			t = this.getUserStatusLabel();
 		} else {
@@ -177,7 +177,7 @@ export default class RoomHeaderView extends React.PureComponent {
 						log('toggleFavorite', e);
 					}
 				}}
-				accessibilityLabel='Star room'
+				accessibilityLabel={I18n.t('Star_room')}
 				accessibilityTraits='button'
 				testID='room-view-header-star'
 			>
@@ -191,7 +191,7 @@ export default class RoomHeaderView extends React.PureComponent {
 			<TouchableOpacity
 				style={styles.headerButton}
 				onPress={() => this.props.navigation.navigate({ key: 'RoomActions', routeName: 'RoomActions', params: { rid: this.state.room.rid } })}
-				accessibilityLabel='Room actions'
+				accessibilityLabel={I18n.t('Room_actions')}
 				accessibilityTraits='button'
 				testID='room-view-header-actions'
 			>
diff --git a/app/views/RoomView/UnreadSeparator.js b/app/views/RoomView/UnreadSeparator.js
index 7892b1664..00b4b9958 100644
--- a/app/views/RoomView/UnreadSeparator.js
+++ b/app/views/RoomView/UnreadSeparator.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { View, StyleSheet, Text, LayoutAnimation } from 'react-native';
+import I18n from '../../i18n';
 
 const styles = StyleSheet.create({
 	firstUnread: {
@@ -30,7 +31,7 @@ export default class UnreadSeparator extends React.PureComponent {
 		return (
 			<View style={styles.firstUnread}>
 				<View style={styles.firstUnreadLine} />
-				<Text style={styles.firstUnreadBadge}>unread messages</Text>
+				<Text style={styles.firstUnreadBadge}>{I18n.t('unread_messages')}</Text>
 			</View>
 		);
 	}
diff --git a/app/views/RoomView/banner.js b/app/views/RoomView/banner.js
deleted file mode 100644
index bc5424ff0..000000000
--- a/app/views/RoomView/banner.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Text, View } from 'react-native';
-import { connect } from 'react-redux';
-import styles from './styles';
-
-@connect(state => ({
-	loading: state.messages.isFetching
-}), null)
-export default class Banner extends React.PureComponent {
-	static propTypes = {
-		loading: PropTypes.bool
-	};
-
-	render() {
-		return (this.props.loading ? (
-			<View style={styles.bannerContainer}>
-				<Text style={styles.bannerText}>Loading new messages...</Text>
-			</View>
-		) : null);
-	}
-}
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 5a7d3c741..928da4fb1 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -2,12 +2,12 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { Text, View, Button } from 'react-native';
 import { connect } from 'react-redux';
-import { bindActionCreators } from 'redux';
+// import { bindActionCreators } from 'redux';
 import equal from 'deep-equal';
 
 import LoggedView from '../View';
 import { List } from './ListView';
-import * as actions from '../../actions';
+// import * as actions from '../../actions';
 import { openRoom, setLastOpen } from '../../actions/room';
 import { editCancel, toggleReactionPicker, actionsShow } from '../../actions/messages';
 import database from '../../lib/realm';
@@ -21,17 +21,18 @@ import RoomsHeader from './Header';
 import ReactionPicker from './ReactionPicker';
 import styles from './styles';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 @connect(
 	state => ({
-		Site_Url: state.settings.Site_Url || state.server ? state.server.server : '',
-		Message_TimeFormat: state.settings.Message_TimeFormat,
+		// Site_Url: state.settings.Site_Url || state.server ? state.server.server : '',
+		// Message_TimeFormat: state.settings.Message_TimeFormat,
 		loading: state.messages.isFetching,
 		user: state.login.user,
 		actionMessage: state.messages.actionMessage
 	}),
 	dispatch => ({
-		actions: bindActionCreators(actions, dispatch),
+		// actions: bindActionCreators(actions, dispatch),
 		openRoom: room => dispatch(openRoom(room)),
 		editCancel: () => dispatch(editCancel()),
 		setLastOpen: date => dispatch(setLastOpen(date)),
@@ -48,8 +49,8 @@ export default class RoomView extends LoggedView {
 		editCancel: PropTypes.func,
 		rid: PropTypes.string,
 		name: PropTypes.string,
-		Site_Url: PropTypes.string,
-		Message_TimeFormat: PropTypes.string,
+		// Site_Url: PropTypes.string,
+		// Message_TimeFormat: PropTypes.string,
 		loading: PropTypes.bool,
 		actionMessage: PropTypes.object,
 		toggleReactionPicker: PropTypes.func.isRequired,
@@ -191,7 +192,7 @@ export default class RoomView extends LoggedView {
 		if (!this.state.joined) {
 			return (
 				<View>
-					<Text>You are in preview mode.</Text>
+					<Text>{I18n.t('You_are_in_preview_mode')}</Text>
 					<Button title='Join' onPress={this.joinRoom} />
 				</View>
 			);
@@ -199,14 +200,14 @@ export default class RoomView extends LoggedView {
 		if (this.state.room.archived || this.isReadOnly()) {
 			return (
 				<View style={styles.readOnly}>
-					<Text>This room is read only</Text>
+					<Text>{I18n.t('This_room_is_read_only')}</Text>
 				</View>
 			);
 		}
 		if (this.isBlocked()) {
 			return (
 				<View style={styles.blockedOrBlocker}>
-					<Text>This room is blocked</Text>
+					<Text>{I18n.t('This_room_is_blocked')}</Text>
 				</View>
 			);
 		}
@@ -215,9 +216,9 @@ export default class RoomView extends LoggedView {
 
 	renderHeader = () => {
 		if (this.state.end) {
-			return <Text style={styles.loadingMore}>Start of conversation</Text>;
+			return <Text style={styles.loadingMore}>{I18n.t('Start_of_conversation')}</Text>;
 		}
-		return <Text style={styles.loadingMore}>Loading more messages...</Text>;
+		return <Text style={styles.loadingMore}>{I18n.t('Loading_messages_ellipsis')}</Text>;
 	}
 	render() {
 		return (
diff --git a/app/views/RoomsListView/Header/index.js b/app/views/RoomsListView/Header/index.js
index 194540754..a225b7d70 100644
--- a/app/views/RoomsListView/Header/index.js
+++ b/app/views/RoomsListView/Header/index.js
@@ -14,25 +14,26 @@ import { STATUS_COLORS } from '../../../constants/colors';
 import { setSearch } from '../../../actions/rooms';
 import styles from './styles';
 import log from '../../../utils/log';
+import I18n from '../../../i18n';
 
 const title = (offline, connecting, authenticating, logged) => {
 	if (offline) {
-		return 'offline...';
+		return `${ I18n.t('Offline') }...`;
 	}
 
 	if (connecting) {
-		return 'Connecting...';
+		return `${ I18n.t('Connecting') }...`;
 	}
 
 	if (authenticating) {
-		return 'Authenticating...';
+		return `${ I18n.t('Authenticating') }...`;
 	}
 
 	if (logged) {
 		return null;
 	}
 
-	return 'Not logged...';
+	return `${ I18n.t('Not_logged') }...`;
 };
 
 @connect(state => ({
@@ -96,7 +97,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 
 	getUserStatusLabel() {
 		const status = this.getUserStatus();
-		return status.charAt(0).toUpperCase() + status.slice(1);
+		return I18n.t(status.charAt(0).toUpperCase() + status.slice(1));
 	}
 
 	showModal() {
@@ -124,7 +125,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 			<View
 				style={styles.left}
 				accessible
-				accessibilityLabel={`Connected to ${ this.props.baseUrl }. Tap to view servers list.`}
+				accessibilityLabel={`${ I18n.t('Connected_to') } ${ this.props.baseUrl }. ${ I18n.t('Tap_to_view_servers_list') }.`}
 				accessibilityTraits='button'
 				testID='rooms-list-view-sidebar'
 			>
@@ -156,7 +157,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 
 		const t = title(offline, connecting, authenticating, logged);
 
-		const accessibilityLabel = `${ user.username }, ${ this.getUserStatusLabel() }, tap to change status`;
+		const accessibilityLabel = `${ user.username }, ${ this.getUserStatusLabel() }, ${ I18n.t('tap_to_change_status') }`;
 		return (
 			<TouchableOpacity
 				style={styles.titleContainer}
@@ -190,7 +191,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 					<TouchableOpacity
 						style={styles.headerButton}
 						onPress={() => this.onPressSearchButton()}
-						accessibilityLabel='Search'
+						accessibilityLabel={I18n.t('Search')}
 						accessibilityTraits='button'
 					>
 						<Icon
@@ -204,7 +205,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 					<TouchableOpacity
 						style={styles.headerButton}
 						onPress={() => this.createChannel()}
-						accessibilityLabel='Create channel'
+						accessibilityLabel={I18n.t('Create_Channel')}
 						accessibilityTraits='button'
 						testID='rooms-list-view-create-channel'
 					>
@@ -223,6 +224,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 	renderModalButton = (status, text) => {
 		const statusStyle = [styles.status, { marginRight: 10, backgroundColor: STATUS_COLORS[status] }];
 		const textStyle = { flex: 1, fontWeight: this.props.user.status === status ? 'bold' : 'normal' };
+		const label = text || status;
 		return (
 			<TouchableOpacity
 				style={styles.modalButton}
@@ -231,7 +233,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 			>
 				<View style={statusStyle} />
 				<Text style={textStyle}>
-					{text || status.charAt(0).toUpperCase() + status.slice(1)}
+					{I18n.t(label.charAt(0).toUpperCase() + label.slice(1))}
 				</Text>
 			</TouchableOpacity>
 		);
@@ -252,7 +254,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 					style={styles.inputSearch}
 					onChangeText={text => this.onSearchChangeText(text)}
 					returnKeyType='search'
-					placeholder='Search'
+					placeholder={I18n.t('Search')}
 					clearButtonMode='while-editing'
 					blurOnSubmit
 					autoCorrect={false}
@@ -281,7 +283,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
 						{this.renderModalButton('online')}
 						{this.renderModalButton('busy')}
 						{this.renderModalButton('away')}
-						{this.renderModalButton('offline', 'Invisible')}
+						{this.renderModalButton('offline', 'invisible')}
 					</View>
 				</Modal>
 			</View>
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index d1c8726fc..929e0cfb9 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -14,6 +14,7 @@ import styles from './styles';
 import debounce from '../../utils/debounce';
 import LoggedView from '../View';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 @connect(state => ({
 	user: state.login.user,
@@ -166,7 +167,7 @@ export default class RoomsListView extends LoggedView {
 				style={styles.searchBox}
 				onChangeText={text => this.onSearchChangeText(text)}
 				returnKeyType='search'
-				placeholder='Search'
+				placeholder={I18n.t('Search')}
 				clearButtonMode='while-editing'
 				blurOnSubmit
 				autoCorrect={false}
@@ -213,7 +214,7 @@ export default class RoomsListView extends LoggedView {
 
 	renderCreateButtons = () => (
 		<ActionButton buttonColor='rgba(231,76,60,1)'>
-			<ActionButton.Item buttonColor='#9b59b6' title='Create Channel' onPress={() => { this.createChannel(); }} >
+			<ActionButton.Item buttonColor='#9b59b6' title={I18n.t('Create_Channel')} onPress={() => { this.createChannel(); }} >
 				<Icon name='md-chatbubbles' style={styles.actionButtonIcon} />
 			</ActionButton.Item>
 		</ActionButton>
diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js
index f7ca21767..4aa45da26 100644
--- a/app/views/SearchMessagesView/index.js
+++ b/app/views/SearchMessagesView/index.js
@@ -14,6 +14,7 @@ import buildMessage from '../../lib/methods/helpers/buildMessage';
 import Message from '../../containers/message';
 import scrollPersistTaps from '../../utils/scrollPersistTaps';
 import log from '../../utils/log';
+import I18n from '../../i18n';
 
 @connect(state => ({
 	user: state.login.user,
@@ -114,12 +115,12 @@ export default class SearchMessagesView extends LoggedView {
 				<View style={styles.searchContainer}>
 					<RCTextInput
 						inputRef={(e) => { this.name = e; }}
-						label='Search'
+						label={I18n.t('Search')}
 						onChangeText={this.onChangeSearch}
-						placeholder='Search Messages'
+						placeholder={I18n.t('Search_Messages')}
 						testID='search-message-view-input'
 					/>
-					<Markdown msg='You can search using RegExp. e.g. `/^text$/i`' />
+					<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} />
 					<View style={styles.divider} />
 				</View>
 				<FlatList
diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js
index 712fe67e0..d1582ccc8 100644
--- a/app/views/SelectedUsersView.js
+++ b/app/views/SelectedUsersView.js
@@ -12,6 +12,7 @@ import Avatar from '../containers/Avatar';
 import Loading from '../containers/Loading';
 import debounce from '../utils/debounce';
 import LoggedView from './View';
+import I18n from '../i18n';
 
 const styles = StyleSheet.create({
 	container: {
@@ -101,7 +102,7 @@ export default class SelectedUsersView extends LoggedView {
 							justifyContent: 'center'
 						}}
 						onPress={() => params.nextAction()}
-						accessibilityLabel='Submit'
+						accessibilityLabel={I18n.t('Submit')}
 						accessibilityTraits='button'
 						testID='selected-users-view-submit'
 					>
@@ -227,7 +228,7 @@ export default class SelectedUsersView extends LoggedView {
 				style={styles.searchBox}
 				onChangeText={text => this.onSearchChangeText(text)}
 				returnKeyType='search'
-				placeholder='Search'
+				placeholder={I18n.t('Search')}
 				clearButtonMode='while-editing'
 				blurOnSubmit
 				testID='select-users-view-search'
diff --git a/app/views/SnippetedMessagesView/index.js b/app/views/SnippetedMessagesView/index.js
index 9535e28c2..f1b240023 100644
--- a/app/views/SnippetedMessagesView/index.js
+++ b/app/views/SnippetedMessagesView/index.js
@@ -8,6 +8,7 @@ import { openSnippetedMessages, closeSnippetedMessages } from '../../actions/sni
 import styles from './styles';
 import Message from '../../containers/message';
 import RCActivityIndicator from '../../containers/ActivityIndicator';
+import I18n from '../../i18n';
 
 @connect(
 	state => ({
@@ -74,7 +75,7 @@ export default class SnippetedMessagesView extends LoggedView {
 
 	renderEmpty = () => (
 		<View style={styles.listEmptyContainer} testID='snippeted-messages-view'>
-			<Text>No snippeted messages</Text>
+			<Text>{I18n.t('No_snippeted_messages')}</Text>
 		</View>
 	)
 
diff --git a/app/views/StarredMessagesView/index.js b/app/views/StarredMessagesView/index.js
index f9df1e04d..6feed3c3b 100644
--- a/app/views/StarredMessagesView/index.js
+++ b/app/views/StarredMessagesView/index.js
@@ -10,10 +10,11 @@ import styles from './styles';
 import Message from '../../containers/message';
 import { toggleStarRequest } from '../../actions/messages';
 import RCActivityIndicator from '../../containers/ActivityIndicator';
+import I18n from '../../i18n';
 
 const STAR_INDEX = 0;
 const CANCEL_INDEX = 1;
-const options = ['Unstar', 'Cancel'];
+const options = [I18n.t('Unstar'), I18n.t('Cancel')];
 
 @connect(
 	state => ({
@@ -98,7 +99,7 @@ export default class StarredMessagesView extends LoggedView {
 
 	renderEmpty = () => (
 		<View style={styles.listEmptyContainer} testID='starred-messages-view'>
-			<Text>No starred messages</Text>
+			<Text>{I18n.t('No_starred_messages')}</Text>
 		</View>
 	)
 
@@ -138,7 +139,7 @@ export default class StarredMessagesView extends LoggedView {
 				<ActionSheet
 					key='starred-messages-view-action-sheet'
 					ref={o => this.actionSheet = o}
-					title='Actions'
+					title={I18n.t('Actions')}
 					options={options}
 					cancelButtonIndex={CANCEL_INDEX}
 					onPress={this.handleActionPress}
diff --git a/app/views/TermsServiceView.js b/app/views/TermsServiceView.js
index b56abca08..f27d30a41 100644
--- a/app/views/TermsServiceView.js
+++ b/app/views/TermsServiceView.js
@@ -3,15 +3,11 @@ import PropTypes from 'prop-types';
 import { WebView } from 'react-native';
 import { connect } from 'react-redux';
 
-class TermsServiceView extends React.Component {
+class TermsServiceView extends React.PureComponent {
 	static propTypes = {
 		termsService: PropTypes.string
 	}
 
-	static navigationOptions = () => ({
-		title: 'Terms of service'
-	});
-
 	render() {
 		return (
 			<WebView source={{ html: this.props.termsService }} />
diff --git a/e2e/07-room.spec.js b/e2e/07-room.spec.js
index e3cb3874d..70490aef9 100644
--- a/e2e/07-room.spec.js
+++ b/e2e/07-room.spec.js
@@ -178,16 +178,16 @@ describe('Room screen', () => {
 		describe('Message', async() => {
 			it('should show message actions', async() => {
 				await element(by.text(`${ data.random }message`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Cancel')).tap();
 				await waitFor(element(by.text('Cancel'))).toBeNotVisible().withTimeout(2000);
 			});
 
 			it('should copy permalink', async() => {
 				await element(by.text(`${ data.random }message`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Copy Permalink')).tap();
 				await expect(element(by.text('Permalink copied to clipboard!'))).toBeVisible();
 				await waitFor(element(by.text('Permalink copied to clipboard!'))).toBeNotVisible().withTimeout(5000);
@@ -197,8 +197,8 @@ describe('Room screen', () => {
 
 			it('should copy message', async() => {
 				await element(by.text(`${ data.random }message`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Copy Message')).tap();
 				await expect(element(by.text('Copied to clipboard!'))).toBeVisible();
 				await waitFor(element(by.text('Copied to clipboard!'))).toBeNotVisible().withTimeout(5000);
@@ -207,10 +207,10 @@ describe('Room screen', () => {
 
 			it('should star message', async() => {
 				await element(by.text(`${ data.random }message`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Star')).tap();
-				await waitFor(element(by.text('Messages actions'))).toBeNotVisible().withTimeout(5000);
+				await waitFor(element(by.text('Message actions'))).toBeNotVisible().withTimeout(5000);
 				await element(by.text(`${ data.random }message`)).longPress();
 				await waitFor(element(by.text('Unstar'))).toBeVisible().withTimeout(2000);
 				await expect(element(by.text('Unstar'))).toBeVisible();
@@ -220,8 +220,8 @@ describe('Room screen', () => {
 
 			it('should react to message', async() => {
 				await element(by.text(`${ data.random }message`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Add Reaction')).tap();
 				await waitFor(element(by.id('reaction-picker'))).toBeVisible().withTimeout(2000);
 				await expect(element(by.id('reaction-picker'))).toBeVisible();
@@ -254,8 +254,8 @@ describe('Room screen', () => {
 			it('should reply message', async() => {
 				await mockMessage('reply');
 				await element(by.text(`${ data.random }reply`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Reply')).tap();
 				await element(by.id('messagebox-input')).typeText('replied');
 				await element(by.id('messagebox-send-message')).tap();
@@ -265,8 +265,8 @@ describe('Room screen', () => {
 			it('should edit message', async() => {
 				await mockMessage('edit');
 				await element(by.text(`${ data.random }edit`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Edit')).tap();
 				await element(by.id('messagebox-input')).typeText('ed');
 				await element(by.id('messagebox-send-message')).tap();
@@ -277,8 +277,8 @@ describe('Room screen', () => {
 			it('should quote message', async() => {
 				await mockMessage('quote');
 				await element(by.text(`${ data.random }quote`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Quote')).tap();
 				await element(by.id('messagebox-input')).typeText(`${ data.random }quoted`);
 				await element(by.id('messagebox-send-message')).tap();
@@ -287,10 +287,10 @@ describe('Room screen', () => {
 
 			it('should pin message', async() => {
 				await element(by.text(`${ data.random }edited`)).longPress();
-				await waitFor(element(by.text('Messages actions'))).toBeVisible().withTimeout(5000);
-				await expect(element(by.text('Messages actions'))).toBeVisible();
+				await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
+				await expect(element(by.text('Message actions'))).toBeVisible();
 				await element(by.text('Pin')).tap();
-				await waitFor(element(by.text('Messages actions'))).toBeNotVisible().withTimeout(5000);
+				await waitFor(element(by.text('Message actions'))).toBeNotVisible().withTimeout(5000);
 				await waitFor(element(by.text(`${ data.random }edited`)).atIndex(1)).toBeVisible().withTimeout(60000);
 				await element(by.text(`${ data.random }edited`)).atIndex(0).longPress();
 				await waitFor(element(by.text('Unpin'))).toBeVisible().withTimeout(2000);
diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index 800e7e1a1..9ce790288 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -5,6 +5,7 @@
 	};
 	objectVersion = 46;
 	objects = {
+
 /* Begin PBXBuildFile section */
 		00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
 		00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@@ -12,6 +13,7 @@
 		00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
 		00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
 		00E356F31AD99517003FC87E /* RocketChatRNTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* RocketChatRNTests.m */; };
+		09CB5909C1E64707832358CE /* libRNI18n-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C01CD6D4653143EEB5100C3A /* libRNI18n-tvOS.a */; };
 		0C6E2DE448364EA896869ADF /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B37C79D9BD0742CE936B6982 /* libc++.tbd */; };
 		0DC38A29B0E54AF4AF96CB95 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2EADB1731B5E47D093292B59 /* MaterialCommunityIcons.ttf */; };
 		0F026E58B8A6427D9A204D89 /* libSplashScreen.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2607FA180F14E6584301101 /* libSplashScreen.a */; };
@@ -65,12 +67,13 @@
 		B8C682AD1FD8511E003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
 		B8C682AE1FD8511F003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
 		B8E79AF41F3CD167005B464F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB61A68108700A75B9A /* Info.plist */; };
+		BAB7DC22804246F3923A1833 /* libFastImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD2E2837F110483CA29EE0D4 /* libFastImage.a */; };
 		BED2B77AA660460E8BC9F8E0 /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */; };
 		C758F0BD5C3244E2BA073E61 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B696712EE2345A59F007A88 /* libRNImagePicker.a */; };
 		CBD0E0A35B174C4DBFED3B31 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E528DE3A405E43B4A37ABA68 /* Zocial.ttf */; };
 		D6408D9E4A864FF6BA986857 /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8A2DD67ADD954AD9873F45FC /* SimpleLineIcons.ttf */; };
 		EF736EF520A64AE8820E684A /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF26CC845883492D8AC8869B /* libRealmReact.a */; };
-		BAB7DC22804246F3923A1833 /* libFastImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD2E2837F110483CA29EE0D4 /* libFastImage.a */; };
+		F5BF54DC78E1411B8343933B /* libRNI18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 921481B47B50490CA761932E /* libRNI18n.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -326,6 +329,27 @@
 			remoteGlobalIDString = 39DF4FE71E00394E00F5B4B2;
 			remoteInfo = RCTCustomInputController;
 		};
+		7A770EC120BECDC7001AD51A /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 1845C223DA364898A8400573 /* FastImage.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = A287971D1DE0C0A60081BDFA;
+			remoteInfo = FastImage;
+		};
+		7A770EC520BECDC7001AD51A /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 134814201AA4EA6300B7C361;
+			remoteInfo = RNI18n;
+		};
+		7A770EC720BECDC7001AD51A /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 6476C4051EEAA69700B10F51;
+			remoteInfo = "RNI18n-tvOS";
+		};
 		7A7F5C981FCC982500024129 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = AD0379F2BCE84C968538CDAF /* RCTVideo.xcodeproj */;
@@ -490,10 +514,12 @@
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RocketChatRN/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = RocketChatRN/main.m; sourceTree = "<group>"; };
 		146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
+		1845C223DA364898A8400573 /* FastImage.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = FastImage.xcodeproj; path = "../node_modules/react-native-fast-image/ios/FastImage.xcodeproj"; sourceTree = "<group>"; };
 		1B0746E708284151B8AD1198 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = file; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
 		1D3BB00B9ABF44EA9BD71318 /* libSafariViewManager.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libSafariViewManager.a; sourceTree = "<group>"; };
 		20CE3E407E0D4D9E8C9885F2 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTVideo.a; sourceTree = "<group>"; };
 		22A8B76C8EBA443BB97CE82D /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
+		22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNI18n.xcodeproj; path = "../node_modules/react-native-i18n/ios/RNI18n.xcodeproj"; sourceTree = "<group>"; };
 		2D02E47B1E0B4A5D006451C7 /* RocketChatRN-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RocketChatRN-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		2D02E4901E0B4A5D006451C7 /* RocketChatRN-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RocketChatRN-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		2EADB1731B5E47D093292B59 /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
@@ -519,6 +545,7 @@
 		7AFB8035205AE63000D004E7 /* RCTToast.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTToast.xcodeproj; path = "../node_modules/@remobile/react-native-toast/ios/RCTToast.xcodeproj"; sourceTree = "<group>"; };
 		832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
 		8A2DD67ADD954AD9873F45FC /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
+		921481B47B50490CA761932E /* libRNI18n.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNI18n.a; sourceTree = "<group>"; };
 		9A1E1766CCB84C91A62BD5A6 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
 		A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
 		AD0379F2BCE84C968538CDAF /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTVideo.xcodeproj; path = "../node_modules/react-native-video/ios/RCTVideo.xcodeproj"; sourceTree = "<group>"; };
@@ -528,6 +555,7 @@
 		B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeyboardTrackingView.xcodeproj; path = "../node_modules/react-native-keyboard-tracking-view/lib/KeyboardTrackingView.xcodeproj"; sourceTree = "<group>"; };
 		B8C682611FD84CEF003A12C8 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = icomoon.ttf; path = ../resources/fonts/icomoon.ttf; sourceTree = "<group>"; };
 		BAAE4B947F5D44959F0A9D5A /* libRNZeroconf.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNZeroconf.a; sourceTree = "<group>"; };
+		C01CD6D4653143EEB5100C3A /* libRNI18n-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNI18n-tvOS.a"; sourceTree = "<group>"; };
 		C21010507E5B4B37BA0E4C9D /* RNAudio.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNAudio.xcodeproj; path = "../node_modules/react-native-audio/ios/RNAudio.xcodeproj"; sourceTree = "<group>"; };
 		C23AEF1D9EBE4A38A1A6B97B /* RNSVG.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSVG.xcodeproj; path = "../node_modules/react-native-svg/ios/RNSVG.xcodeproj"; sourceTree = "<group>"; };
 		DA50CE47374C4C35BE6D9D58 /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSVG.a; sourceTree = "<group>"; };
@@ -535,8 +563,7 @@
 		DF26CC845883492D8AC8869B /* libRealmReact.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRealmReact.a; sourceTree = "<group>"; };
 		E528DE3A405E43B4A37ABA68 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; };
 		F88C6541BD764BEEABB87272 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
-		1845C223DA364898A8400573 /* FastImage.xcodeproj */ = {isa = PBXFileReference; name = "FastImage.xcodeproj"; path = "../node_modules/react-native-fast-image/ios/FastImage.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
-		FD2E2837F110483CA29EE0D4 /* libFastImage.a */ = {isa = PBXFileReference; name = "libFastImage.a"; path = "libFastImage.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
+		FD2E2837F110483CA29EE0D4 /* libFastImage.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libFastImage.a; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -583,6 +610,7 @@
 				2C800DF680F8451599E80AF1 /* libSafariViewManager.a in Frameworks */,
 				74815BBCB91147C08C8F7B3D /* libRNAudio.a in Frameworks */,
 				BAB7DC22804246F3923A1833 /* libFastImage.a in Frameworks */,
+				F5BF54DC78E1411B8343933B /* libRNI18n.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -598,6 +626,7 @@
 				2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */,
 				2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */,
 				2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */,
+				09CB5909C1E64707832358CE /* libRNI18n-tvOS.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -788,6 +817,23 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		7A770EBC20BECDC7001AD51A /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				7A770EC620BECDC7001AD51A /* libRNI18n.a */,
+				7A770EC820BECDC7001AD51A /* libRNI18n-tvOS.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		7A770EBE20BECDC7001AD51A /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				7A770EC220BECDC7001AD51A /* libFastImage.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		7A7F5C831FCC982500024129 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -843,6 +889,7 @@
 				4019A5E1911B4C61944FBCEC /* SafariViewManager.xcodeproj */,
 				C21010507E5B4B37BA0E4C9D /* RNAudio.xcodeproj */,
 				1845C223DA364898A8400573 /* FastImage.xcodeproj */,
+				22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */,
 			);
 			name = Libraries;
 			sourceTree = "<group>";
@@ -948,6 +995,9 @@
 				B2607FA180F14E6584301101 /* libSplashScreen.a */,
 				1D3BB00B9ABF44EA9BD71318 /* libSafariViewManager.a */,
 				1142E3442BA94B19BCF52814 /* libRNAudio.a */,
+				FD2E2837F110483CA29EE0D4 /* libFastImage.a */,
+				921481B47B50490CA761932E /* libRNI18n.a */,
+				C01CD6D4653143EEB5100C3A /* libRNI18n-tvOS.a */,
 			);
 			name = "Recovered References";
 			sourceTree = "<group>";
@@ -1109,6 +1159,10 @@
 			productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
 			projectDirPath = "";
 			projectReferences = (
+				{
+					ProductGroup = 7A770EBE20BECDC7001AD51A /* Products */;
+					ProjectRef = 1845C223DA364898A8400573 /* FastImage.xcodeproj */;
+				},
 				{
 					ProductGroup = B8971BAD202A091D0000D245 /* Products */;
 					ProjectRef = B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */;
@@ -1185,6 +1239,10 @@
 					ProductGroup = B8E79A881F3CCC6C005B464F /* Products */;
 					ProjectRef = 4CD38E4891ED4601B7481448 /* RNFetchBlob.xcodeproj */;
 				},
+				{
+					ProductGroup = 7A770EBC20BECDC7001AD51A /* Products */;
+					ProjectRef = 22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */;
+				},
 				{
 					ProductGroup = 60B8375C1F3F6F4B00677E56 /* Products */;
 					ProjectRef = 4B38C7E37A8748E0BC665078 /* RNImagePicker.xcodeproj */;
@@ -1463,6 +1521,27 @@
 			remoteRef = 7A430E1D20238C02008F55BC /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		7A770EC220BECDC7001AD51A /* libFastImage.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libFastImage.a;
+			remoteRef = 7A770EC120BECDC7001AD51A /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		7A770EC620BECDC7001AD51A /* libRNI18n.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libRNI18n.a;
+			remoteRef = 7A770EC520BECDC7001AD51A /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		7A770EC820BECDC7001AD51A /* libRNI18n-tvOS.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = "libRNI18n-tvOS.a";
+			remoteRef = 7A770EC720BECDC7001AD51A /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		7A7F5C991FCC982500024129 /* libRCTVideo.a */ = {
 			isa = PBXReferenceProxy;
 			fileType = archive.ar;
@@ -1773,6 +1852,7 @@
 					"$(SRCROOT)/../node_modules/react-native-safari-view",
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = RocketChatRNTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1787,6 +1867,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -1818,6 +1900,7 @@
 					"$(SRCROOT)/../node_modules/react-native-safari-view",
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = RocketChatRNTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1832,6 +1915,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -1873,6 +1958,7 @@
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../../../react-native/React/**",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = RocketChatRN/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1919,6 +2005,7 @@
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../../../react-native/React/**",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = RocketChatRN/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1963,6 +2050,7 @@
 					"$(SRCROOT)/../node_modules/react-native-safari-view",
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = "RocketChatRN-tvOS/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1976,6 +2064,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -2017,6 +2107,7 @@
 					"$(SRCROOT)/../node_modules/react-native-safari-view",
 					"$(SRCROOT)/../node_modules/react-native-audio/ios",
 					"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
+					"$(SRCROOT)/../node_modules/react-native-i18n/ios",
 				);
 				INFOPLIST_FILE = "RocketChatRN-tvOS/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -2030,6 +2121,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -2067,6 +2160,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RocketChatRN-tvOSTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2100,6 +2195,8 @@
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
+					"\"$(SRCROOT)/$(TARGET_NAME)\"",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RocketChatRN-tvOSTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/package-lock.json b/package-lock.json
index 62ea43eeb..b8dbfd33f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8852,6 +8852,11 @@
       "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz",
       "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es="
     },
+    "i18n-js": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.0.3.tgz",
+      "integrity": "sha512-u144MQhV/8mz4Y5wP86SQAWMwS8gpe/JavIa9hugSI4WreezGgbhJPdk2Q60KcdIltKLiNefGtHNh1N8SSmQqQ=="
+    },
     "iconv-lite": {
       "version": "0.4.19",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
@@ -14806,6 +14811,14 @@
         "prop-types": "15.6.1"
       }
     },
+    "react-native-i18n": {
+      "version": "2.0.12",
+      "resolved": "https://registry.npmjs.org/react-native-i18n/-/react-native-i18n-2.0.12.tgz",
+      "integrity": "sha512-2eTUk7BVZP5knthCmVt6y7rePFwrxXl4ym2I20e91oTYJnKM22sAjQMnLhRCYZWC4ucRBbe2pUp63uxNdTkkQw==",
+      "requires": {
+        "i18n-js": "3.0.3"
+      }
+    },
     "react-native-image-picker": {
       "version": "0.26.10",
       "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.26.10.tgz",
diff --git a/package.json b/package.json
index 60b026184..8e6b4dda7 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
     "react-native-fabric": "^0.5.1",
     "react-native-fast-image": "^4.0.14",
     "react-native-fetch-blob": "^0.10.8",
+    "react-native-i18n": "^2.0.12",
     "react-native-image-picker": "^0.26.10",
     "react-native-keyboard-aware-scroll-view": "^0.5.0",
     "react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git",
-- 
GitLab