more components updated and translated
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
// Flattens messages extracted via the React Intl Babel plugin. An error will be
|
||||
// 1.Flattens messages extracted via the React Intl Babel plugin. An error will be
|
||||
// thrown if there are messages with the same `id`. The result is a file containing
|
||||
// `id: message` pairs in "en" locale.
|
||||
|
||||
//
|
||||
// 2. Takes all translations and groups them into a single file.
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var globSync = require('glob').sync;
|
||||
@@ -9,7 +10,10 @@ var mkdirpSync = require('mkdirp').sync;
|
||||
|
||||
const MESSAGES_PATTERN = './src/i18n/src/**/*.json';
|
||||
const LANG_DIR = './src/i18n/';
|
||||
const LANG_PATTERN = './src/i18n/*.json';
|
||||
const FINAL_FILE = './src/messages.json';
|
||||
|
||||
// Read message files generated by react-intl-babel-plugin.
|
||||
let messages = globSync(MESSAGES_PATTERN)
|
||||
.map((filename) => fs.readFileSync(filename, 'utf8'))
|
||||
.map((file) => JSON.parse(file))
|
||||
@@ -24,6 +28,22 @@ let messages = globSync(MESSAGES_PATTERN)
|
||||
return collection;
|
||||
}, {});
|
||||
|
||||
// Write English translation.
|
||||
let filename = path.join(LANG_DIR, 'en.json');
|
||||
mkdirpSync(path.dirname(filename));
|
||||
fs.writeFileSync(filename, JSON.stringify(messages, null, 2));
|
||||
|
||||
// Read all translations.
|
||||
messages = globSync(LANG_PATTERN)
|
||||
.map((filename) => {
|
||||
const parts = filename.split('/');
|
||||
const locale = parts[parts.length - 1].split('.json')[0];
|
||||
return { [locale]: JSON.parse(fs.readFileSync(filename, 'utf8')) };
|
||||
})
|
||||
.reduce((collection, descriptors) => {
|
||||
return { ...collection, ...descriptors };
|
||||
}, {});
|
||||
|
||||
// Write file with all translations combined.
|
||||
mkdirpSync(path.dirname(FINAL_FILE));
|
||||
fs.writeFileSync(FINAL_FILE, JSON.stringify(messages, null, 2));
|
||||
|
||||
+8
-3
@@ -2,12 +2,12 @@
|
||||
"contacts_not_found": "You have no chats<br />¯\\\\_(ツ)_/¯",
|
||||
"full_name_prompt": "Full name, e.g. John Doe",
|
||||
"email_prompt": "Email, e.g. jdoe@example.com",
|
||||
"sign_up_button": "Sign up",
|
||||
"button_sign_up": "Sign up",
|
||||
"login_prompt": "Login",
|
||||
"password_prompt": "Password",
|
||||
"stay_logged_in": "Stay logged in",
|
||||
"forgot_password_link": "Forgot password?",
|
||||
"sign_in_button": "Sign in",
|
||||
"button_sign_in": "Sign in",
|
||||
"label_client": "Client:",
|
||||
"label_server": "Server:",
|
||||
"online_now": "online now",
|
||||
@@ -15,6 +15,9 @@
|
||||
"title_not_found": "Not found",
|
||||
"unnamed_topic": "Unnamed",
|
||||
"messages_not_readable": "no access to messages",
|
||||
"tabtitle_find_user": "find",
|
||||
"tabtitle_new_group": "new group",
|
||||
"tabtitle_group_by_id": "by id",
|
||||
"label_server_to_use": "Server to use:",
|
||||
"label_wire_transport": "Wire transport:",
|
||||
"button_update": "Update",
|
||||
@@ -24,5 +27,7 @@
|
||||
"sidepanel_title_edit_account": "Edit Account",
|
||||
"sidepanel_title_newtpk": "Start New Chat",
|
||||
"sidepanel_title_cred": "Confirm Credentials",
|
||||
"sidepanel_title_reset": "Reset Password"
|
||||
"sidepanel_title_reset": "Reset Password",
|
||||
"tags_not_found": "No tags defined. Add some.",
|
||||
"tags_editor_no_tags": "Add some tags"
|
||||
}
|
||||
+5
-2
@@ -2,12 +2,12 @@
|
||||
"contacts_not_found": "Чатов нет<br />¯\\\\_(ツ)_/¯",
|
||||
"full_name_prompt": "Полное имя, напр. Иван Петров",
|
||||
"email_prompt": "Email, напр. ivan@example.com",
|
||||
"sign_up_button": "Создать счет",
|
||||
"button_sign_up": "Создать счет",
|
||||
"login_prompt": "Логин",
|
||||
"password_prompt": "Пароль",
|
||||
"stay_logged_in": "Запомнить",
|
||||
"forgot_password_link": "Напомнить пароль",
|
||||
"sign_in_button": "Войти",
|
||||
"button_sign_in": "Войти",
|
||||
"label_client": "Клиент:",
|
||||
"label_server": "Сервер:",
|
||||
"online_now": "онлайн",
|
||||
@@ -15,6 +15,9 @@
|
||||
"title_not_found": "Не найден",
|
||||
"unnamed_topic": "Без названия",
|
||||
"messages_not_readable": "нет доступа к сообщениям",
|
||||
"tabtitle_find_user": "найти",
|
||||
"tabtitle_new_group": "создать",
|
||||
"tabtitle_group_by_id": "по id",
|
||||
"label_server_to_use": "Использовать сервер:",
|
||||
"label_wire_transport": "Соединение:",
|
||||
"button_update": "Применить",
|
||||
|
||||
+10
-3
@@ -6,8 +6,7 @@ import ReactDOM from 'react-dom';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import ruData from 'react-intl/locale-data/ru';
|
||||
addLocaleData(ruData);
|
||||
import en from './i18n/en.json';
|
||||
import ru from './i18n/ru.json';
|
||||
import allMessages from './messages.json';
|
||||
|
||||
import TinodeWeb from './views/tinode-web.jsx';
|
||||
|
||||
@@ -19,8 +18,16 @@ const language = (params && params.hl) ||
|
||||
navigator.language ||
|
||||
navigator.userLanguage;
|
||||
|
||||
const baseLanguage = language.toLowerCase().split(/[-_]+/)[0];
|
||||
|
||||
// Try full locale, try locale without region code, fallback to 'en'
|
||||
const messages =
|
||||
allMessages[language] ||
|
||||
allMessages[baseLanguage] ||
|
||||
allMessages.en;
|
||||
|
||||
ReactDOM.render(
|
||||
<IntlProvider locale={language} messages={en} >
|
||||
<IntlProvider locale={language} messages={messages} >
|
||||
<TinodeWeb />
|
||||
</IntlProvider>,
|
||||
document.getElementById('mountPoint')
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
// Odds and ends
|
||||
|
||||
// Make shortcut icon appear with a green dot + show unread count in title.
|
||||
function updateFavicon(count) {
|
||||
export function updateFavicon(count) {
|
||||
var oldIcon = document.getElementById('shortcut-icon');
|
||||
if (oldIcon) {
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
@@ -15,7 +17,7 @@ function updateFavicon(count) {
|
||||
}
|
||||
|
||||
// Create VCard which represents topic 'public' info
|
||||
function vcard(fn, imageDataUrl) {
|
||||
export function vcard(fn, imageDataUrl) {
|
||||
var card = null;
|
||||
|
||||
if ((fn && fn.trim()) || imageDataUrl) {
|
||||
@@ -35,7 +37,7 @@ function vcard(fn, imageDataUrl) {
|
||||
}
|
||||
|
||||
// Deep-shallow compare two arrays: shallow compare each element.
|
||||
function arrayEqual(a, b) {
|
||||
export function arrayEqual(a, b) {
|
||||
// Compare lengths first.
|
||||
if (a.length != b.length) {
|
||||
return false;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/* ContactsView holds all contacts-related stuff */
|
||||
import React from 'react';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import ContactList from '../widgets/contact-list.jsx';
|
||||
|
||||
import { updateFavicon } from '../lib/utils.js';
|
||||
|
||||
// Panel titles for translation.
|
||||
const messages = defineMessages({
|
||||
'contacts_not_found': {
|
||||
@@ -13,7 +15,7 @@ const messages = defineMessages({
|
||||
}
|
||||
});
|
||||
|
||||
export default class ContactsView extends React.Component {
|
||||
class ContactsView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -37,7 +39,7 @@ export default class ContactsView extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const {formatHTMLMessage} = this.props.intl;
|
||||
return (
|
||||
<ContactList
|
||||
connected={this.props.connected}
|
||||
@@ -52,3 +54,5 @@ export default class ContactsView extends React.Component {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default injectIntl(ContactsView);
|
||||
|
||||
@@ -7,7 +7,7 @@ import CheckBox from '../widgets/checkbox.jsx';
|
||||
import VisiblePassword from '../widgets/visible-password.jsx';
|
||||
|
||||
import LocalStorageUtil from '../lib/local-storage.js';
|
||||
import vcard from '../lib/utils.js';
|
||||
import { vcard } from '../lib/utils.js';
|
||||
|
||||
export default class CreateAccountView extends React.PureComponent {
|
||||
constructor(props) {
|
||||
@@ -115,7 +115,7 @@ export default class CreateAccountView extends React.PureComponent {
|
||||
</div>
|
||||
<div className="dialog-buttons">
|
||||
<button className={submitClasses} type="submit">
|
||||
<FormattedMessage id="sign_up_button" defaultMessage="Sign up"
|
||||
<FormattedMessage id="button_sign_up" defaultMessage="Sign up"
|
||||
description="Create account button [Sign Up]" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +85,7 @@ export default class LoginView extends React.Component {
|
||||
</div>
|
||||
<div className="dialog-buttons">
|
||||
<button className={submitClasses} type="submit">
|
||||
<FormattedMessage id="sign_in_button" defaultMessage="Sign in"
|
||||
<FormattedMessage id="button_sign_in" defaultMessage="Sign in"
|
||||
description="Button [Sign In]" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/* Create new topic and invite users or send an invite */
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ContactList from '../widgets/contact-list.jsx';
|
||||
import NewTopicById from '../widgets/new-topic-by-id.jsx';
|
||||
import NewTopicGroup from '../widgets/new-topic-group.jsx';
|
||||
import SearchContacts from '../widgets/search-contacts.jsx';
|
||||
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
|
||||
export default class NewTopicView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -34,24 +37,24 @@ export default class NewTopicView extends React.Component {
|
||||
|
||||
handleTabClick(e) {
|
||||
e.preventDefault();
|
||||
navigateTo(addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));
|
||||
HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));
|
||||
this.setState({tabSelected: e.currentTarget.dataset.id});
|
||||
}
|
||||
|
||||
handleContactSelected(sel) {
|
||||
if (this.state.tabSelected === 'p2p') {
|
||||
navigateTo(removeUrlParam(window.location.hash, 'tab'));
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
|
||||
this.props.onCreateTopic(sel, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
handleNewGroupSubmit(name, dataUrl, priv, tags) {
|
||||
navigateTo(removeUrlParam(window.location.hash, 'tab'));
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
|
||||
this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags);
|
||||
}
|
||||
|
||||
handleGroupByID(topicName) {
|
||||
navigateTo(removeUrlParam(window.location.hash, 'tab'));
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
|
||||
this.props.onCreateTopic(topicName);
|
||||
}
|
||||
|
||||
@@ -60,13 +63,22 @@ export default class NewTopicView extends React.Component {
|
||||
<div className="flex-column">
|
||||
<ul className="tabbar">
|
||||
<li className={this.state.tabSelected === "p2p" ? "active" : null}>
|
||||
<a href="javascript:;" data-id="p2p" onClick={this.handleTabClick}>find</a>
|
||||
<a href="javascript:;" data-id="p2p" onClick={this.handleTabClick}>
|
||||
<FormattedMessage id="tabtitle_find_user" defaultMessage="find"
|
||||
description="Tab title Find" />
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.state.tabSelected === "grp" ? "active" : null}>
|
||||
<a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>new group</a>
|
||||
<a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>
|
||||
<FormattedMessage id="tabtitle_new_group" defaultMessage="new group"
|
||||
description="Tab title New Group" />
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.state.tabSelected === "byid" ? "active" : null}>
|
||||
<a href="javascript:;" data-id="byid" onClick={this.handleTabClick}>by id</a>
|
||||
<a href="javascript:;" data-id="byid" onClick={this.handleTabClick}>
|
||||
<FormattedMessage id="tabtitle_group_by_id" defaultMessage="by id"
|
||||
description="Tab title Find topic by ID" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{this.state.tabSelected === 'grp' ?
|
||||
|
||||
@@ -13,6 +13,7 @@ import MessagesView from './messages-view.jsx';
|
||||
import SidepanelView from './sidepanel-view.jsx';
|
||||
|
||||
import { API_KEY, APP_NAME, MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { isLocalHost, isSecureConnection } from '../lib/host-name.js';
|
||||
import LocalStorageUtil from '../lib/local-storage.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from 'react';
|
||||
|
||||
import Contact from './contact.jsx';
|
||||
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
/* ContactList: component for showing a list of contacts,
|
||||
* such as a list of group members in a group chat */
|
||||
export default class ContactList extends React.Component {
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from 'react';
|
||||
|
||||
import Tinode from 'tinode-sdk';
|
||||
|
||||
import { stringHash } from '../lib/strformat.js';
|
||||
|
||||
export default class LetterTile extends React.PureComponent {
|
||||
render() {
|
||||
let avatar;
|
||||
|
||||
+27
-15
@@ -1,8 +1,11 @@
|
||||
// TagManager: edit topic or user tags.
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ChipInput from './chip-input.jsx';
|
||||
|
||||
import { arrayEqual } from '../lib/utils.js';
|
||||
|
||||
export default class TagManager extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -10,8 +13,7 @@ export default class TagManager extends React.Component {
|
||||
this.state = {
|
||||
tags: this.props.tags,
|
||||
tagInput: '',
|
||||
activated: this.props.activated,
|
||||
noTagsMessage: "Add some tags"
|
||||
activated: this.props.activated
|
||||
};
|
||||
|
||||
this.handleShowTagManager = this.handleShowTagManager.bind(this);
|
||||
@@ -99,7 +101,9 @@ export default class TagManager extends React.Component {
|
||||
tags.push(<span className="badge" key={tags.length}>{tag}</span>);
|
||||
});
|
||||
if (tags.length == 0) {
|
||||
tags = <i>No tags defined. Add some.</i>;
|
||||
tags = (<i>
|
||||
<FormattedMessage id="tags_not_found" defaultMessage="No tags defined. Add some." description="" />
|
||||
</i>);
|
||||
}
|
||||
}
|
||||
return (
|
||||
@@ -109,26 +113,34 @@ export default class TagManager extends React.Component {
|
||||
</div>
|
||||
{this.state.activated ?
|
||||
<div>
|
||||
<ChipInput
|
||||
chips={tags}
|
||||
avatarDisabled={true}
|
||||
prompt={this.state.noTagsMessage}
|
||||
onEnter={this.handleAddTag}
|
||||
onFocusLost={this.handleAddTag}
|
||||
onCancel={this.handleCancel}
|
||||
onChipRemoved={this.handleRemoveTag}
|
||||
filterFunc={this.handleTagInput} />
|
||||
<FormattedMessage id="tags_editor_no_tags" defaultMessage="Add some tags"
|
||||
description="Tag editor prompt when no tags are found.">{
|
||||
(add_tags_prompt) => <ChipInput
|
||||
chips={tags}
|
||||
avatarDisabled={true}
|
||||
prompt={add_tags_prompt}
|
||||
onEnter={this.handleAddTag}
|
||||
onFocusLost={this.handleAddTag}
|
||||
onCancel={this.handleCancel}
|
||||
onChipRemoved={this.handleRemoveTag}
|
||||
filterFunc={this.handleTagInput} />
|
||||
}</FormattedMessage>
|
||||
{this.props.onSubmit || this.props.onCancel ?
|
||||
<div id="tag-manager-buttons" className="panel-form-row">
|
||||
<button className="blue" onClick={this.handleSubmit}>OK</button>
|
||||
<button className="white" onClick={this.handleCancel}>Cancel</button>
|
||||
<button className="blue" onClick={this.handleSubmit}>
|
||||
<FormattedMessage id="button_ok" defautMessage="OK" description="Confirmation button [OK]" />
|
||||
</button>
|
||||
<button className="white" onClick={this.handleCancel}>
|
||||
<FormattedMessage id="button_cancel" defautMessage="Cancel" description="Rejection button [Cancel]" />
|
||||
</button>
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<a href="javascript:;" className="flat-button" onClick={this.handleShowTagManager}>
|
||||
<i className="material-icons">edit</i> Manage tags
|
||||
<i className="material-icons">edit</i> <FormattedMessage id="title_manage_tags" defautMessage="Manage tags"
|
||||
description="Section title in TagManager" />
|
||||
</a>
|
||||
<span>{tags}</span>
|
||||
</div>
|
||||
|
||||
+36
-34
File diff suppressed because one or more lines are too long
+6
-6
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user