Первичная реализация нового переключателя языков
@ -29,6 +29,7 @@
|
|||||||
"babel-polyfill": "^6.3.14",
|
"babel-polyfill": "^6.3.14",
|
||||||
"classnames": "^2.1.3",
|
"classnames": "^2.1.3",
|
||||||
"debounce": "^1.0.0",
|
"debounce": "^1.0.0",
|
||||||
|
"flag-icon-css": "^2.8.0",
|
||||||
"intl": "^1.2.2",
|
"intl": "^1.2.2",
|
||||||
"intl-format-cache": "^2.0.4",
|
"intl-format-cache": "^2.0.4",
|
||||||
"intl-messageformat": "^2.1.0",
|
"intl-messageformat": "^2.1.0",
|
||||||
|
@ -26,6 +26,24 @@ const LOCALES_MAP = {
|
|||||||
lt: 'lt-LT',
|
lt: 'lt-LT',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Некоторые языки, после выгрузки из OneSky, имеют не очень информативные названия,
|
||||||
|
* так что здесь можно указать точные имена для результирующего файла index.json
|
||||||
|
*/
|
||||||
|
const ORIGINAL_NAMES_MAP = {
|
||||||
|
en: 'English, UK',
|
||||||
|
pt: 'Português do Brasil',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Некоторые языки, после выгрузки из OneSky, имеют не очень точные английские названия,
|
||||||
|
* так что здесь можно указать точные имена для результирующего файла index.json
|
||||||
|
*/
|
||||||
|
const ENGLISH_NAMES_MAP = {
|
||||||
|
en: 'English, UK',
|
||||||
|
pt: 'Portuguese, Brazilian',
|
||||||
|
};
|
||||||
|
|
||||||
// https://ely-translates.oneskyapp.com/admin/site/settings
|
// https://ely-translates.oneskyapp.com/admin/site/settings
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
apiKey: '5MaW9TYp0S3qdJgkZ5QLgEIDeabkFDzB',
|
apiKey: '5MaW9TYp0S3qdJgkZ5QLgEIDeabkFDzB',
|
||||||
@ -83,6 +101,17 @@ function sortByKeys(object) {
|
|||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Убирает значение в скобках.
|
||||||
|
* Например: "French (France)" => "French".
|
||||||
|
*
|
||||||
|
* @param {string} value
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function trimValueInBrackets(value) {
|
||||||
|
return value.match(/^([^\(]+)/)[0].trim();
|
||||||
|
}
|
||||||
|
|
||||||
async function pullReadyLanguages() {
|
async function pullReadyLanguages() {
|
||||||
const languages = JSON.parse(await onesky.getLanguages({...defaultOptions}));
|
const languages = JSON.parse(await onesky.getLanguages({...defaultOptions}));
|
||||||
return languages.data
|
return languages.data
|
||||||
@ -112,7 +141,8 @@ async function pull() {
|
|||||||
const mapFileContent = {};
|
const mapFileContent = {};
|
||||||
langs.map((elem) => {
|
langs.map((elem) => {
|
||||||
mapFileContent[elem.locale] = {
|
mapFileContent[elem.locale] = {
|
||||||
name: elem.local_name.match(/^([^\(]+)/)[0].trim(), // Обрезаем значения в скобках
|
name: ORIGINAL_NAMES_MAP[elem.locale] || trimValueInBrackets(elem.local_name),
|
||||||
|
englishName: ENGLISH_NAMES_MAP[elem.locale] || trimValueInBrackets(elem.english_name),
|
||||||
progress: parseFloat(elem.translation_progress),
|
progress: parseFloat(elem.translation_progress),
|
||||||
isReleased: elem.is_ready_to_publish,
|
isReleased: elem.is_ready_to_publish,
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,6 @@ import React, { Component } from 'react';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
|
||||||
import { LangMenu } from 'components/langMenu';
|
|
||||||
|
|
||||||
import styles from './footerMenu.scss';
|
import styles from './footerMenu.scss';
|
||||||
import messages from './footerMenu.intl.json';
|
import messages from './footerMenu.intl.json';
|
||||||
|
|
||||||
@ -13,7 +11,8 @@ class FooterMenu extends Component {
|
|||||||
static displayName = 'FooterMenu';
|
static displayName = 'FooterMenu';
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
createPopup: PropTypes.func.isRequired
|
createContactPopup: PropTypes.func.isRequired,
|
||||||
|
createLanguageSwitcherPopup: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -27,23 +26,35 @@ class FooterMenu extends Component {
|
|||||||
<Message {...messages.contactUs} />
|
<Message {...messages.contactUs} />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<LangMenu />
|
<div className={styles.langTriggerContainer}>
|
||||||
|
<a href="#" className={styles.langTrigger} onClick={this.onLanguageSwitcher}>
|
||||||
|
<span className={styles.langTriggerIcon} />
|
||||||
|
<Message {...messages.siteLanguage} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onContact = (event) => {
|
onContact = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.createPopup();
|
this.props.createContactPopup();
|
||||||
|
};
|
||||||
|
|
||||||
|
onLanguageSwitcher = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.props.createLanguageSwitcherPopup();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ContactForm from 'components/contact/ContactForm';
|
import ContactForm from 'components/contact/ContactForm';
|
||||||
|
import LanguageSwitcher from 'components/languageSwitcher/LanguageSwitcher';
|
||||||
import { create as createPopup } from 'components/ui/popup/actions';
|
import { create as createPopup } from 'components/ui/popup/actions';
|
||||||
|
|
||||||
// mark this component, as not pure, because it is stateless,
|
// mark this component, as not pure, because it is stateless,
|
||||||
// but should be re-rendered, if current lang was changed
|
// but should be re-rendered, if current lang was changed
|
||||||
export default connect(null, {
|
export default connect(null, {
|
||||||
createPopup: () => createPopup(ContactForm)
|
createContactPopup: () => createPopup(ContactForm),
|
||||||
|
createLanguageSwitcherPopup: () => createPopup(LanguageSwitcher),
|
||||||
}, null, {pure: false})(FooterMenu);
|
}, null, {pure: false})(FooterMenu);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"contactUs": "Contact Us"
|
"contactUs": "Contact Us",
|
||||||
|
"siteLanguage": "Site language"
|
||||||
}
|
}
|
||||||
|
@ -8,3 +8,16 @@
|
|||||||
border-bottom-color: #666;
|
border-bottom-color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.langTriggerContainer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.langTriggerIcon {
|
||||||
|
composes: globe from 'components/ui/icons.scss';
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
bottom: 1px;
|
||||||
|
font-size: 11px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
|
||||||
|
|
||||||
import styles from './langMenu.scss';
|
|
||||||
import messages from './langMenu.intl.json';
|
|
||||||
|
|
||||||
import LANGS from 'i18n/index.json';
|
|
||||||
|
|
||||||
class LangMenu extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
showCurrentLang: PropTypes.bool,
|
|
||||||
toggleRef: PropTypes.func,
|
|
||||||
userLang: PropTypes.string,
|
|
||||||
changeLang: PropTypes.func
|
|
||||||
};
|
|
||||||
static defaultProps = {
|
|
||||||
toggleRef: () => {},
|
|
||||||
showCurrentLang: false
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
isActive: false
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('click', this.onBodyClick);
|
|
||||||
this.props.toggleRef(this.toggle.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('click', this.onBodyClick);
|
|
||||||
this.props.toggleRef(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {userLang: userLocale, showCurrentLang} = this.props;
|
|
||||||
const {isActive} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames(styles.container, {
|
|
||||||
[styles.withCurrentLang]: showCurrentLang
|
|
||||||
})}>
|
|
||||||
<div className={styles.menuContainer}>
|
|
||||||
<ul className={classNames(styles.menu, {
|
|
||||||
[styles.menuActive]: isActive
|
|
||||||
})}>
|
|
||||||
{Object.keys(LANGS).map((locale) => (
|
|
||||||
<li className={classNames(styles.menuItem, {
|
|
||||||
[styles.activeMenuItem]: locale === userLocale
|
|
||||||
})} onClick={this.onChangeLang(locale)} key={locale}
|
|
||||||
>
|
|
||||||
{this.renderLangLabel(locale, LANGS[locale])}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
<li className={styles.improveTranslatesLink}>
|
|
||||||
<a href="https://ely-translates.oneskyapp.com/collaboration/project?id=201323" target="_blank">
|
|
||||||
<Message {...messages.improveTranslations} />
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.triggerContainer} onClick={this.onToggle}>
|
|
||||||
<a className={styles.trigger} href="#">
|
|
||||||
{showCurrentLang
|
|
||||||
? this.renderLangLabel(userLocale, LANGS[userLocale]) : (
|
|
||||||
<span>
|
|
||||||
<span className={styles.triggerIcon} />
|
|
||||||
{' '}
|
|
||||||
<Message {...messages.siteLanguage} />
|
|
||||||
{' '}
|
|
||||||
<span className={isActive ? styles.triggerArrowBottom : styles.triggerArrowTop} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLangLabel(locale, localeData) {
|
|
||||||
const {name, progress, isReleased} = localeData;
|
|
||||||
let progressLabel;
|
|
||||||
|
|
||||||
if (progress !== 100) {
|
|
||||||
progressLabel = (
|
|
||||||
<span className={styles.langTranslateUnfinished}>
|
|
||||||
{`(${progress}%)`}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (!isReleased) {
|
|
||||||
progressLabel = (
|
|
||||||
<span className={styles.langTranslateUnreviewed}>
|
|
||||||
{'*'}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<span className={styles.langIco} style={{
|
|
||||||
backgroundImage: `url('${require(`icons/flag_${locale}.svg`)}')`
|
|
||||||
}} />
|
|
||||||
{this.formatLocaleName(locale) || name}
|
|
||||||
{progressLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
formatLocaleName(locale) {
|
|
||||||
switch (locale) {
|
|
||||||
case 'pt':
|
|
||||||
return 'Português (Br)';
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeLang(lang) {
|
|
||||||
return (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.props.changeLang(lang);
|
|
||||||
this.setState({
|
|
||||||
isActive: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onBodyClick = (event) => {
|
|
||||||
if (this.state.isActive) {
|
|
||||||
const el = ReactDOM.findDOMNode(this);
|
|
||||||
|
|
||||||
if (!el.contains(event.target) && el !== event.taget) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// add a small delay for the case someone have alredy called toggle
|
|
||||||
setTimeout(() => this.state.isActive && this.toggle(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onToggle = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.toggle();
|
|
||||||
};
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
// add small delay to skip click event on body
|
|
||||||
setTimeout(() => this.setState({
|
|
||||||
isActive: !this.state.isActive
|
|
||||||
}), 0);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { changeLang } from 'components/user/actions';
|
|
||||||
|
|
||||||
export default connect((state) => ({
|
|
||||||
userLang: state.user.lang
|
|
||||||
}), {
|
|
||||||
changeLang
|
|
||||||
})(LangMenu);
|
|
@ -1,5 +0,0 @@
|
|||||||
import LangMenu from './LangMenu';
|
|
||||||
|
|
||||||
export {
|
|
||||||
LangMenu
|
|
||||||
};
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"siteLanguage": "Site language",
|
|
||||||
"improveTranslations": "Improve translations"
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
@import "~components/ui/colors.scss";
|
|
||||||
@import "~components/ui/fonts.scss";
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuContainer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
|
|
||||||
perspective: 600px;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
pointer-events: auto;
|
|
||||||
background: #fff;
|
|
||||||
border: 5px solid #ddd8ce;
|
|
||||||
border-left: 0;
|
|
||||||
border-right: 0;
|
|
||||||
text-align: left;
|
|
||||||
box-shadow: 0 0 1px rgba(#000, .2);
|
|
||||||
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: .3s cubic-bezier(0.075, 0.82, 0.165, 1); // easeOutCirc
|
|
||||||
transform: translateY(10px) rotateX(-17deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuActive {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) rotateX(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.withCurrentLang {
|
|
||||||
.triggerContainer {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuContainer {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuItem {
|
|
||||||
position: relative;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 13px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: .2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $whiteButtonLight;
|
|
||||||
color: #262626;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activeMenuItem {
|
|
||||||
background: lighter($green)!important;
|
|
||||||
color: #fff!important;
|
|
||||||
font-weight: $font-weight-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.langIco {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 5px;
|
|
||||||
width: 20px;
|
|
||||||
height: 10px;
|
|
||||||
box-shadow: 0 0 1px rgba(#000, .2);
|
|
||||||
|
|
||||||
background: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.langTranslateProgress {
|
|
||||||
font-size: 9px;
|
|
||||||
position: relative;
|
|
||||||
font-family: $font-family-base;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.langTranslateUnfinished {
|
|
||||||
composes: langTranslateProgress;
|
|
||||||
|
|
||||||
top: -1px;
|
|
||||||
padding-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.langTranslateUnreviewed {
|
|
||||||
composes: langTranslateProgress;
|
|
||||||
|
|
||||||
top: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.improveTranslatesLink {
|
|
||||||
font-size: 11px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 5px 10px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #9a9a9a;
|
|
||||||
border-bottom-color: #9a9a9a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger {
|
|
||||||
color: #666;
|
|
||||||
border-bottom: 1px dotted #666;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: .25s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-bottom-color: #777;
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.triggerContainer {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.triggerIcon {
|
|
||||||
composes: globe from 'components/ui/icons.scss';
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
bottom: 1px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.triggerArrow {
|
|
||||||
font-size: 8px;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.triggerArrowTop {
|
|
||||||
composes: triggerArrow;
|
|
||||||
composes: arrowTop from 'components/ui/icons.scss';
|
|
||||||
}
|
|
||||||
|
|
||||||
.triggerArrowBottom {
|
|
||||||
composes: triggerArrow;
|
|
||||||
composes: arrowBottom from 'components/ui/icons.scss';
|
|
||||||
|
|
||||||
// Докручиваем лишний раз, чтобы иконка крутилась по часовой стрелке
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
197
src/components/languageSwitcher/LanguageSwitcher.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
|
||||||
|
import { requireLocaleFlag } from 'functions';
|
||||||
|
import LANGS from 'i18n/index.json';
|
||||||
|
|
||||||
|
import formStyles from 'components/ui/form/form.scss';
|
||||||
|
import popupStyles from 'components/ui/popup/popup.scss';
|
||||||
|
import icons from 'components/ui/icons.scss';
|
||||||
|
import styles from './languageSwitcher.scss';
|
||||||
|
import messages from './languageSwitcher.intl.json';
|
||||||
|
|
||||||
|
class LanguageSwitcher extends Component {
|
||||||
|
static displayName = 'LanguageSwitcher';
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
userLang: PropTypes.string,
|
||||||
|
changeLang: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
items: [],
|
||||||
|
filter: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onClose() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({items: LANGS});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {userLang, onClose} = this.props;
|
||||||
|
const {items} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.languageSwitcher}>
|
||||||
|
<div className={popupStyles.popup}>
|
||||||
|
<div className={popupStyles.header}>
|
||||||
|
<h2 className={popupStyles.headerTitle}>
|
||||||
|
<Message {...messages.siteLanguage} />
|
||||||
|
</h2>
|
||||||
|
<span className={classNames(icons.close, popupStyles.close)} onClick={onClose} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.languageSwitcherBody}>
|
||||||
|
<div className={styles.searchBox}>
|
||||||
|
<input
|
||||||
|
className={classNames(
|
||||||
|
formStyles.lightTextField,
|
||||||
|
formStyles.greenTextField
|
||||||
|
)}
|
||||||
|
placeholder={
|
||||||
|
<Message {...messages.startTyping}>
|
||||||
|
{(placeholder) => (
|
||||||
|
{placeholder}
|
||||||
|
)}
|
||||||
|
</Message>
|
||||||
|
}
|
||||||
|
onChange={this.onFilterUpdate()}
|
||||||
|
onKeyPress={this.onFilterKeyPress()}
|
||||||
|
/>
|
||||||
|
<span className={styles.searchIcon} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.languagesList}>
|
||||||
|
{Object.keys(items).map((locale) => (
|
||||||
|
<li className={classNames(styles.languageItem, {
|
||||||
|
[styles.activeLanguageItem]: locale === userLang
|
||||||
|
})} onClick={this.onChangeLang(locale)} key={locale}
|
||||||
|
>
|
||||||
|
{this.renderLanguageItem(locale, items[locale])}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.improveTranslates}>
|
||||||
|
<div className={styles.improveTranslatesIcon} />
|
||||||
|
<div className={styles.improveTranslatesContent}>
|
||||||
|
<div className={styles.improveTranslatesTitle}>
|
||||||
|
<Message {...messages.improveTranslates} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.improveTranslatesText}>
|
||||||
|
<Message {...messages.improveTranslatesDescription} values={{
|
||||||
|
articleLink: (
|
||||||
|
<a href="#">
|
||||||
|
<Message {...messages.improveTranslatesArticleLink} />
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLanguageItem(locale, localeData) {
|
||||||
|
const {name, progress, isReleased} = localeData;
|
||||||
|
let progressLabel;
|
||||||
|
if (progress !== 100) {
|
||||||
|
progressLabel = (
|
||||||
|
<Message {...messages.translationProgress} values={{
|
||||||
|
progress: `${progress}%`,
|
||||||
|
}} />
|
||||||
|
);
|
||||||
|
} else if (!isReleased) {
|
||||||
|
progressLabel = (
|
||||||
|
<Message {...messages.mayBeInaccurate} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.languageFlex}>
|
||||||
|
<div className={styles.languageIco} style={{
|
||||||
|
backgroundImage: `url('${requireLocaleFlag(locale)}')`
|
||||||
|
}} />
|
||||||
|
<div className={styles.languageCaptions}>
|
||||||
|
<div className={styles.languageName}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
<div className={styles.languageSubName}>
|
||||||
|
{localeData.englishName} {progressLabel ? '| ' : ''} {progressLabel}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className={styles.languageCircle} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeLang(lang) {
|
||||||
|
return (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.changeLang(lang);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLang(lang) {
|
||||||
|
this.props.changeLang(lang);
|
||||||
|
setTimeout(this.props.onClose, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterUpdate() {
|
||||||
|
return (event) => {
|
||||||
|
const value = event.target.value.trim().toLowerCase();
|
||||||
|
let items = LANGS;
|
||||||
|
if (value.length !== 0) {
|
||||||
|
items = Object.keys(items).reduce((prev, next) => {
|
||||||
|
if (items[next].englishName.toLowerCase().search(value) !== -1
|
||||||
|
|| items[next].name.toLowerCase().search(value) !== -1
|
||||||
|
) {
|
||||||
|
prev[next] = items[next];
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
items,
|
||||||
|
filter: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterKeyPress() {
|
||||||
|
return (event) => {
|
||||||
|
if (event.key !== 'Enter' || this.state.filter === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locales = Object.keys(this.state.items);
|
||||||
|
if (locales.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeLang(locales[0]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { changeLang } from 'components/user/actions';
|
||||||
|
|
||||||
|
export default connect((state) => ({
|
||||||
|
userLang: state.user.lang
|
||||||
|
}), {
|
||||||
|
changeLang
|
||||||
|
})(LanguageSwitcher);
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"siteLanguage": "Site language",
|
||||||
|
"startTyping": "Start typing…",
|
||||||
|
"translationProgress": "{progress} translated",
|
||||||
|
"mayBeInaccurate": "May be inaccurate",
|
||||||
|
"improveTranslates": "Improve Ely.by translation",
|
||||||
|
"improveTranslatesDescription": "Ely.by’s localization is a community effort. If you want to see Ely.by translated into another language, we'd love your help. To apply read {articleLink}.",
|
||||||
|
"improveTranslatesArticleLink": "this article"
|
||||||
|
}
|
162
src/components/languageSwitcher/languageSwitcher.scss
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
@import '~components/ui/colors.scss';
|
||||||
|
@import '~components/ui/fonts.scss';
|
||||||
|
@import '~components/ui/popup/popup.scss';
|
||||||
|
|
||||||
|
.languageSwitcher {
|
||||||
|
composes: popupWrapper from 'components/ui/popup/popup.scss';
|
||||||
|
|
||||||
|
@include popupBounding(400px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageSwitcherBody {
|
||||||
|
composes: body from 'components/ui/popup/popup.scss';
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 550px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBox {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchIcon {
|
||||||
|
composes: search from 'components/ui/icons.scss';
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
right: 12px;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #EDEBE5;
|
||||||
|
pointer-events: none; // Иконка чисто декоративная, так что клик должен проходить сквозь неё
|
||||||
|
}
|
||||||
|
|
||||||
|
$languageListBorderStyle: 1px solid #eee;
|
||||||
|
|
||||||
|
.languagesList {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-top: $languageListBorderStyle;
|
||||||
|
border-bottom: $languageListBorderStyle;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageItem {
|
||||||
|
padding: 10px;
|
||||||
|
border-top: $languageListBorderStyle;
|
||||||
|
font-family: $font-family-title;
|
||||||
|
transition: .25s;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $whiteButtonLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageFlex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageIco {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 7px;
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
box-shadow: 0 0 1px rgba(#000, .2);
|
||||||
|
|
||||||
|
background: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageCaptions {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageName {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageSubName {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Реализация радио кнопки. Когда у нас будет нормальный компонент радио кнопок, нужно будет перейти на него
|
||||||
|
.languageCircle {
|
||||||
|
composes: checkmark from 'components/ui/icons.scss';
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
right: -32px;
|
||||||
|
|
||||||
|
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
border: 2px solid #DCD8CD;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
transition: .5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageItem:hover & {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activeLanguageItem & {
|
||||||
|
border-color: $green;
|
||||||
|
background: $green;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.improveTranslates {
|
||||||
|
border: 1px solid #DEDEDE;
|
||||||
|
background: #F3F1ED;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.improveTranslatesIcon {
|
||||||
|
composes: translate from 'components/ui/icons.scss';
|
||||||
|
|
||||||
|
color: lighter($blue);
|
||||||
|
font-size: 22px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.improveTranslatesContent {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.improveTranslatesTitle {
|
||||||
|
font-family: $font-family-title;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.improveTranslatesText {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #9A9A9A;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { FormattedMessage as Message, FormattedRelative as Relative } from 'react-intl';
|
import { FormattedMessage as Message, FormattedRelative as Relative } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
|
|
||||||
|
import { requireLocaleFlag } from 'functions';
|
||||||
|
import LANGS from 'i18n/index.json';
|
||||||
|
|
||||||
import { userShape } from 'components/user/User';
|
import { userShape } from 'components/user/User';
|
||||||
import { LangMenu } from 'components/langMenu';
|
|
||||||
|
|
||||||
import ProfileField from './ProfileField';
|
import ProfileField from './ProfileField';
|
||||||
import styles from './profile.scss';
|
import styles from './profile.scss';
|
||||||
@ -17,7 +20,8 @@ import RulesPage from 'pages/rules/RulesPage';
|
|||||||
class Profile extends Component {
|
class Profile extends Component {
|
||||||
static displayName = 'Profile';
|
static displayName = 'Profile';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
user: userShape
|
user: userShape,
|
||||||
|
createLanguageSwitcherPopup: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -86,7 +90,14 @@ class Profile extends Component {
|
|||||||
|
|
||||||
<ProfileField
|
<ProfileField
|
||||||
label={<Message {...messages.siteLanguage} />}
|
label={<Message {...messages.siteLanguage} />}
|
||||||
value={<LangMenu showCurrentLang />}
|
value={
|
||||||
|
<span className={styles.language} onClick={this.onLanguageSwitcher.bind(this)}>
|
||||||
|
<span className={styles.languageIcon} style={{
|
||||||
|
backgroundImage: `url('${requireLocaleFlag(user.lang)}')`
|
||||||
|
}} />
|
||||||
|
{LANGS[user.lang].name}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProfileField
|
<ProfileField
|
||||||
@ -118,6 +129,10 @@ class Profile extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLanguageSwitcher() {
|
||||||
|
this.props.createLanguageSwitcherPopup();
|
||||||
|
}
|
||||||
|
|
||||||
handleUUIDMouseOver() {
|
handleUUIDMouseOver() {
|
||||||
try {
|
try {
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
@ -136,7 +151,11 @@ class Profile extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import LanguageSwitcher from 'components/languageSwitcher/LanguageSwitcher';
|
||||||
|
import { create as createPopup } from 'components/ui/popup/actions';
|
||||||
|
|
||||||
export default connect((state) => ({
|
export default connect((state) => ({
|
||||||
user: state.user
|
user: state.user,
|
||||||
}))(Profile);
|
}), {
|
||||||
|
createLanguageSwitcherPopup: () => createPopup(LanguageSwitcher),
|
||||||
|
})(Profile);
|
||||||
|
@ -68,6 +68,28 @@ $formColumnWidth: 416px;
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.language {
|
||||||
|
color: #666;
|
||||||
|
border-bottom: 1px dotted #666;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: .25s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageIcon {
|
||||||
|
$height: 13px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 4px;
|
||||||
|
height: $height;
|
||||||
|
width: $height * 4 / 3;
|
||||||
|
box-shadow: 0 0 1px rgba(#000, .2);
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
.uuidValue {
|
.uuidValue {
|
||||||
composes: paramName;
|
composes: paramName;
|
||||||
composes: paramValue;
|
composes: paramValue;
|
||||||
|
@ -49,11 +49,6 @@
|
|||||||
|
|
||||||
.textField {
|
.textField {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -79,3 +79,24 @@ export function getJwtPayload(jwt: string): Object {
|
|||||||
throw new Error('Can not decode jwt token');
|
throw new Error('Can not decode jwt token');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const localeToCountryCode = {
|
||||||
|
en: 'gb',
|
||||||
|
be: 'by',
|
||||||
|
pt: 'br',
|
||||||
|
uk: 'ua',
|
||||||
|
vi: 'vn',
|
||||||
|
sl: 'si',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает для указанной локали её флаг с учётом всех нюансов загрузки флага
|
||||||
|
* и подбора соответствующего локали флага.
|
||||||
|
*
|
||||||
|
* @param {string} locale
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
export function requireLocaleFlag(locale: string) {
|
||||||
|
return require(`flag-icon-css/flags/4x3/${localeToCountryCode[locale] || locale}.svg`);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,61 +1,73 @@
|
|||||||
{
|
{
|
||||||
"be": {
|
"be": {
|
||||||
"name": "Беларуская",
|
"name": "Беларуская",
|
||||||
|
"englishName": "Belarusian",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"en": {
|
"en": {
|
||||||
"name": "English",
|
"name": "English, UK",
|
||||||
|
"englishName": "English, UK",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
"name": "Français",
|
"name": "Français",
|
||||||
|
"englishName": "French",
|
||||||
"progress": 89,
|
"progress": 89,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"name": "Bahasa Indonesia",
|
"name": "Bahasa Indonesia",
|
||||||
|
"englishName": "Indonesian",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"lt": {
|
"lt": {
|
||||||
"name": "Lietuvių",
|
"name": "Lietuvių",
|
||||||
|
"englishName": "Lithuanian",
|
||||||
"progress": 96.9,
|
"progress": 96.9,
|
||||||
"isReleased": false
|
"isReleased": false
|
||||||
},
|
},
|
||||||
"pl": {
|
"pl": {
|
||||||
"name": "Polski",
|
"name": "Polski",
|
||||||
|
"englishName": "Polish",
|
||||||
"progress": 85.5,
|
"progress": 85.5,
|
||||||
"isReleased": false
|
"isReleased": false
|
||||||
},
|
},
|
||||||
"pt": {
|
"pt": {
|
||||||
"name": "Português",
|
"name": "Português do Brasil",
|
||||||
|
"englishName": "Portuguese, Brazilian",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"ro": {
|
"ro": {
|
||||||
"name": "Română",
|
"name": "Română",
|
||||||
|
"englishName": "Romanian",
|
||||||
"progress": 85.5,
|
"progress": 85.5,
|
||||||
"isReleased": false
|
"isReleased": false
|
||||||
},
|
},
|
||||||
"ru": {
|
"ru": {
|
||||||
"name": "Русский",
|
"name": "Русский",
|
||||||
|
"englishName": "Russian",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"sl": {
|
"sl": {
|
||||||
"name": "Slovenščina",
|
"name": "Slovenščina",
|
||||||
|
"englishName": "Slovenian",
|
||||||
"progress": 86,
|
"progress": 86,
|
||||||
"isReleased": false
|
"isReleased": false
|
||||||
},
|
},
|
||||||
"uk": {
|
"uk": {
|
||||||
"name": "Українська",
|
"name": "Українська",
|
||||||
|
"englishName": "Ukrainian",
|
||||||
"progress": 89,
|
"progress": 89,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
},
|
},
|
||||||
"vi": {
|
"vi": {
|
||||||
"name": "Tiếng Việt",
|
"name": "Tiếng Việt",
|
||||||
|
"englishName": "Vietnamese",
|
||||||
"progress": 96.9,
|
"progress": 96.9,
|
||||||
"isReleased": true
|
"isReleased": true
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="900" height="450" viewBox="0 0 1098 549">
|
|
||||||
<title>Flag of Belarus</title>
|
|
||||||
<rect fill="#C8313E" width="1098" height="549"/>
|
|
||||||
<rect y="366" fill="#4AA657" width="1098" height="183"/>
|
|
||||||
<rect fill="#FFF" width="122" height="549"/>
|
|
||||||
<g id="h">
|
|
||||||
<g id="q" fill="#C8313E" fill-rule="evenodd" transform="scale(5.304347826,9)">
|
|
||||||
<path d="M4,0h3v1h1v1h1v1h1v1h1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h1v-1h1v-1h1v-1h1zM5,2h1v1h1v1h1v1h-1v1h-1v1h-1v-1h-1v-1h-1v-1h1v-1h1zM5,4h1v1h-1zM0,1h1v1h-1zM0,7h1v1h-1zM11,0h0.6v2h-.6zM11,7h.6v2h-.6zM2,9h1v1h1v1h1v1h-1v1h-1v1h-1v-1h-1v-1h-1v-1h1v-1h1zM2,11h1v1h-1zM8,9h1v1h1v1h1v1h-1v1h-1v1h-1v-1h-1v-1h-1v-1h1v-1h1zM8,11h1v1h-1zM0,15h1v1h-1zM11,14h.6v2h-.6z"/>
|
|
||||||
<path d="M0,18h1v-1h1v-1h1v-1h1v-1h1v-1h1v1h1v1h1v1h1v1h1v1h1v1h.6v4h-.6v1h-1v1h-1v1h-1v1h-1v1h-1v2.6h-2v-0.6h-1v-1h-1v-1h-1v-1h-1 v-3h1v1h1v1h1v1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h-1v-1h-1v-1h-3v1h2v1h-1v1h-1v1h-1v-1h-1v-1h-1v-1h-1zM0,22h1v1h-1zM11,25h.6v1h-.6zM9,27h1v1h1v1h.6v1.6h-.6v-.6h-1v-1h-1zM7,30h1v.6h-1z"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#q" transform="translate(122,0) scale(-1,1)"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#h" transform="translate(0,549) scale(1,-1)"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,10 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="1200" height="600">
|
|
||||||
<clipPath id="t">
|
|
||||||
<path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
|
|
||||||
</clipPath>
|
|
||||||
<path d="M0,0 v30 h60 v-30 z" fill="#00247d"/>
|
|
||||||
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
|
|
||||||
<path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#cf142b" stroke-width="4"/>
|
|
||||||
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
|
|
||||||
<path d="M30,0 v30 M0,15 h60" stroke="#cf142b" stroke-width="6"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 522 B |
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 12 6">
|
|
||||||
<rect fill="#002395" width="4" height="6" x="0" />
|
|
||||||
<rect fill="#FFFFFF" width="4" height="6" x="4" />
|
|
||||||
<rect fill="#ED2939" width="4" height="6" x="8" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 297 B |
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 4 2">
|
|
||||||
<rect fill="#FFF" width="4" height="2"/>
|
|
||||||
<rect fill="#CE1126" width="4" height="1"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 224 B |
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 12 6">
|
|
||||||
<rect fill="#ffb300" width="12" height="2" y="0" />
|
|
||||||
<rect fill="#007308" width="12" height="2" y="2" />
|
|
||||||
<rect fill="#bf0000" width="12" height="2" y="4" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 300 B |
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 4 2">
|
|
||||||
<rect fill="#fff" width="4" height="1" />
|
|
||||||
<rect fill="#dc143c" width="4" height="1" y="1"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 231 B |
@ -1,90 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1200" height="600" viewBox="-2100 -1470 4200 2940">
|
|
||||||
<defs>
|
|
||||||
<path id="D" d="M -31.5,0 h 33 a 30 30 0 0 0 30,-30 v -10 a 30 30 0 0 0 -30,-30 H -31.5 z M -18.5,-13 h 19 a 19 19 0 0 0 19,-19 v -6 a 19 19 0 0 0 -19,-19 H -18.5 z" fill-rule="evenodd"/>
|
|
||||||
<path id="E" d="M 0,0 h 63 v -13 H 12 v -18 h 40 v -12 h -40 v -14 H 60 v -13 H 0 z" transform="translate(-31.5)"/>
|
|
||||||
<path id="e" d="M -26.25,0 h 52.5 v -12 h -40.5 v -16 h 33 v -12 h -33 v -11 H 25 v -12 H -26.25 z"/>
|
|
||||||
<g id="G">
|
|
||||||
<clipPath id="gcut">
|
|
||||||
<path d="M -31.5,0 v -70 h 63 v 70 z M 0,-47 v 12 h 31.5 v -12 z"/>
|
|
||||||
</clipPath>
|
|
||||||
<use xlink:href="#O" clip-path="url(#gcut)"/>
|
|
||||||
<rect x="5" y="-35" width="26.5" height="10"/>
|
|
||||||
<rect x="21.5" y="-35" width="10" height="35"/>
|
|
||||||
</g>
|
|
||||||
<path id="M" d="M -31.5,0 h 12 v -48 l 14,48 h 11 l 14,-48 V 0 h 12 V -70 h -17.5 l -14,48 l -14,-48 H -31.5 z"/>
|
|
||||||
<path id="O" d="M 0,0 a 31.5 35 0 0 0 0,-70 a 31.5 35 0 0 0 0,70 M 0,-13 a 18.5 22 0 0 0 0,-44 a 18.5 22 0 0 0 0,44" fill-rule="evenodd"/>
|
|
||||||
<path id="P" d="M -31.5,0 h 13 v -26 h 28 a 22 22 0 0 0 0,-44 h -40 z M -18.5,-39 h 27 a 9 9 0 0 0 0,-18 h -27 z" fill-rule="evenodd"/>
|
|
||||||
<g id="R">
|
|
||||||
<use xlink:href="#P"/>
|
|
||||||
<path d="M 28,0 c 0,-10 0,-32 -15,-32 h -19 c 22,0 22,22 22,32"/>
|
|
||||||
</g>
|
|
||||||
<path id="S" d="M -15.75,-22 C -15.75,-15 -9,-11.5 1,-11.5 C 11,-11.5 15.74,-14.75 15.75,-19.25 C 15.75,-33.5 -31,-24.5 -30.75,-49.5 C -30.5,-71 -6,-70 3,-70 C 12,-70 29,-66 28.75,-48.75 L 13.5,-48.75 C 13.5,-56.25 6.5,-59 -1.5,-59 C -9.25,-59 -14.75,-57.75 -14.75,-50.5 C -15,-38.75 31.5,-46.5 31.5,-21.75 C 31.5,-3.5 13.5,0 0,0 C -11.5,0 -31.55,-4.5 -31.5,-22 z"/>
|
|
||||||
<g id="star" fill="#fff">
|
|
||||||
<g id="c">
|
|
||||||
<path id="t" d="M 0,-1 v 1 h .5" transform="rotate(18 0,-1)"/>
|
|
||||||
<use xlink:href="#t" transform="scale(-1,1)"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#c" transform="rotate(72)"/>
|
|
||||||
<use xlink:href="#c" transform="rotate(-72)"/>
|
|
||||||
<use xlink:href="#c" transform="rotate(144)"/>
|
|
||||||
<use xlink:href="#c" transform="rotate(-144)"/>
|
|
||||||
</g>
|
|
||||||
<use id="star1" xlink:href="#star" transform="scale(31.5)"/>
|
|
||||||
<use id="star2" xlink:href="#star" transform="scale(26.25)"/>
|
|
||||||
<use id="star3" xlink:href="#star" transform="scale(21)"/>
|
|
||||||
<use id="star4" xlink:href="#star" transform="scale(15)"/>
|
|
||||||
<use id="star5" xlink:href="#star" transform="scale(10.5)"/>
|
|
||||||
</defs>
|
|
||||||
<rect x="-100%" y="-50%" width="200%" height="100%" fill="#009b3a"/>
|
|
||||||
<path d="M -1743,0 0,1113 1743,0 0,-1113 z" fill="#fedf00"/>
|
|
||||||
<circle r="735" fill="#002776"/>
|
|
||||||
<clipPath id="band">
|
|
||||||
<circle r="735"/>
|
|
||||||
</clipPath>
|
|
||||||
<path d="M -2205,1470 a 1785 1785 0 0 1 3570,0 h -105 a 1680 1680 0 1 0 -3360,0 z" clip-path="url(#band)" fill="#fff"/>
|
|
||||||
<g fill="#009b3a" transform="translate(-420,1470)">
|
|
||||||
<use xlink:href="#O" y="-1697.5" transform="rotate(-7)"/>
|
|
||||||
<use xlink:href="#R" y="-1697.5" transform="rotate(-4)"/>
|
|
||||||
<use xlink:href="#D" y="-1697.5" transform="rotate(-1)"/>
|
|
||||||
<use xlink:href="#E" y="-1697.5" transform="rotate(2)"/>
|
|
||||||
<use xlink:href="#M" y="-1697.5" transform="rotate(5)"/>
|
|
||||||
<use xlink:href="#e" y="-1697.5" transform="rotate(9.75)"/>
|
|
||||||
<use xlink:href="#P" y="-1697.5" transform="rotate(14.5)"/>
|
|
||||||
<use xlink:href="#R" y="-1697.5" transform="rotate(17.5)"/>
|
|
||||||
<use xlink:href="#O" y="-1697.5" transform="rotate(20.5)"/>
|
|
||||||
<use xlink:href="#G" y="-1697.5" transform="rotate(23.5)"/>
|
|
||||||
<use xlink:href="#R" y="-1697.5" transform="rotate(26.5)"/>
|
|
||||||
<use xlink:href="#E" y="-1697.5" transform="rotate(29.5)"/>
|
|
||||||
<use xlink:href="#S" y="-1697.5" transform="rotate(32.5)"/>
|
|
||||||
<use xlink:href="#S" y="-1697.5" transform="rotate(35.5)"/>
|
|
||||||
<use xlink:href="#O" y="-1697.5" transform="rotate(38.5)"/>
|
|
||||||
</g>
|
|
||||||
<use id="αCMi" xlink:href="#star1" x="-600" y="-132"/>
|
|
||||||
<use id="αCMa" xlink:href="#star1" x="-535" y="177"/>
|
|
||||||
<use id="βCMa" xlink:href="#star2" x="-625" y="243"/>
|
|
||||||
<use id="γCMa" xlink:href="#star4" x="-463" y="132"/>
|
|
||||||
<use id="δCMa" xlink:href="#star2" x="-382" y="250"/>
|
|
||||||
<use id="εCMa" xlink:href="#star3" x="-404" y="323"/>
|
|
||||||
<use id="αVir" xlink:href="#star1" x="228" y="-228"/>
|
|
||||||
<use id="αSco" xlink:href="#star1" x="515" y="258"/>
|
|
||||||
<use id="βSco" xlink:href="#star3" x="617" y="265"/>
|
|
||||||
<use id="εSco" xlink:href="#star2" x="545" y="323"/>
|
|
||||||
<use id="θSco" xlink:href="#star2" x="368" y="477"/>
|
|
||||||
<use id="ιSco" xlink:href="#star3" x="367" y="551"/>
|
|
||||||
<use id="κSco" xlink:href="#star3" x="441" y="419"/>
|
|
||||||
<use id="λSco" xlink:href="#star2" x="500" y="382"/>
|
|
||||||
<use id="μSco" xlink:href="#star3" x="365" y="405"/>
|
|
||||||
<use id="αHya" xlink:href="#star2" x="-280" y="30"/>
|
|
||||||
<use id="γHya" xlink:href="#star3" x="200" y="-37"/>
|
|
||||||
<use id="αCru" xlink:href="#star1" y="330"/>
|
|
||||||
<use id="βCru" xlink:href="#star2" x="85" y="184"/>
|
|
||||||
<use id="γCru" xlink:href="#star2" y="118"/>
|
|
||||||
<use id="δCru" xlink:href="#star3" x="-74" y="184"/>
|
|
||||||
<use id="εCru" xlink:href="#star4" x="-37" y="235"/>
|
|
||||||
<use id="αTrA" xlink:href="#star2" x="220" y="495"/>
|
|
||||||
<use id="βTrA" xlink:href="#star3" x="283" y="430"/>
|
|
||||||
<use id="γTrA" xlink:href="#star3" x="162" y="412"/>
|
|
||||||
<use id="αCar" xlink:href="#star1" x="-295" y="390"/>
|
|
||||||
<use id="σOct" xlink:href="#star5" y="575"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.1 KiB |
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 12 6">
|
|
||||||
<rect fill="#002B7F" width="4" height="6" x="0" />
|
|
||||||
<rect fill="#FCD116" width="4" height="6" x="4" />
|
|
||||||
<rect fill="#CE1126" width="4" height="6" x="8" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 297 B |
@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" width="1200" height="600"><rect fill="#fff" width="12" height="3"/><rect fill="#d52b1e" y="3" width="12" height="3"/><rect fill="#0039a6" y="2" width="12" height="2"/></svg>
|
|
Before Width: | Height: | Size: 271 B |
@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12 6">
|
|
||||||
<rect width="12" fill="#ed1c24" height="6"/>
|
|
||||||
<rect width="12" fill="#005da4" height="4"/>
|
|
||||||
<rect width="12" fill="#fff" height="2"/>
|
|
||||||
<g transform="translate(2.2238 1) scale(.12937)">
|
|
||||||
<svg width="12" viewBox="-120 -190.223125 240 309.188274" height="15.459"><!-- R0 = 15*sqrt(349) ~ 280.223125 Py= 15*sqrt(285)-90 ~ 163.229145242 -->
|
|
||||||
<path d="m110.26-19.478l9.74-143.75a280.22 280.22 0 0 0 -240 0l9.74 143.75a155.61 155.61 0 0 0 110.26 138.45 155.61 155.61 0 0 0 110.26 -138.45" fill="#005da4"/>
|
|
||||||
<!-- 30*sqrt(81/4+1) , 30*sqrt(19)-30 -->
|
|
||||||
<path d="m-90 0a138.29 138.29 0 0 0 90 100.77 138.29 138.29 0 0 0 90 -100.77l-45-60-18 24-27-54-27 54-18-24-45 60" fill="#fff"/>
|
|
||||||
<g id="wave" fill="#005da4" transform="scale(5) translate(0 5.1962)">
|
|
||||||
<path d="m-17.196-2.1962a6 6 0 0 0 8.1962 2.1962 6 6 0 0 1 6 0 6 6 0 0 0 6 0 6 6 0 0 1 6 0 6 6 0 0 0 8.1962 -2.1962v1.732a6 6 0 0 1 -8.1962 2.1962 6 6 0 0 0 -6 0 6 6 0 0 1 -6 0 6 6 0 0 0 -6 0 6 6 0 0 1 -8.1962 -2.1962z"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#wave" transform="translate(0 17.321)"/>
|
|
||||||
<g id="s" transform="translate(0,-120) scale(2.25)">
|
|
||||||
<path stroke-width=".2" d="m0-5l1 3.2679 3.3301-0.7679-2.3301 2.5 2.3301 2.5-3.3301-0.7679-1 3.2679-1-3.2679-3.3301 0.7679 2.3301-2.5-2.3301-2.5 3.3301 0.7679z" fill="#fd0"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#s" transform="translate(-33.75,-45)"/>
|
|
||||||
<use xlink:href="#s" transform="translate(33.75,-45)"/>
|
|
||||||
<path d="m-111.58-167.05l9.96 146.99a146.95 146.95 0 0 0 101.62 129.95 146.95 146.95 0 0 0 101.62 -129.95l9.96-146.99a280.22 280.22 0 0 0 8.42 3.82l-9.74 143.75a155.61 155.61 0 0 1 -110.26 138.45 155.61 155.61 0 0 1 -110.26 -138.45l-9.74-143.75a280.22 280.22 0 0 0 8.42 -3.82" fill="#ed1c24"/>
|
|
||||||
</svg>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.9 KiB |
@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600">
|
|
||||||
<rect width="1200" height="600" fill="#005BBB"/>
|
|
||||||
<rect width="1200" height="300" y="300" fill="#FFD500"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 180 B |
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1200" height="600" viewBox="-15 -10 30 20">
|
|
||||||
<rect fill="#DA251d" x="-20" y="-15" width="40" height="30"/>
|
|
||||||
<g id="g" transform="translate(0,-6)">
|
|
||||||
<polyline id="g1" fill="#FF0" points="0,0 0,6 4,6" transform="rotate(18)"/>
|
|
||||||
<use xlink:href="#g1" transform="scale(-1,1)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g2" transform="rotate(72)">
|
|
||||||
<use xlink:href="#g" />
|
|
||||||
<use xlink:href="#g" transform="rotate(72)"/>
|
|
||||||
</g>
|
|
||||||
<use xlink:href="#g2" transform="scale(-1,1)"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 568 B |
9
src/icons/webfont/search.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<svg version="1.2" baseProfile="tiny" id="Слой_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="17px" height="19px" viewBox="0 0 17 19" xml:space="preserve">
|
||||||
|
<path fill="#FFFFFF" d="M6.909,3.659c-2.346,0-4.25,1.903-4.25,4.25c0,2.347,1.903,4.248,4.25,4.248c2.347,0,4.249-1.9,4.249-4.248
|
||||||
|
C11.157,5.562,9.254,3.659,6.909,3.659z M16.831,16.617l-1.214,1.215c-0.222,0.223-0.586,0.223-0.809,0l-2.429-2.428
|
||||||
|
c-0.224-0.223-0.224-0.588,0-0.813l0.067-0.064l-1.144-1.141c-0.029-0.029-0.044-0.066-0.066-0.098
|
||||||
|
c-1.185,0.953-2.689,1.527-4.329,1.527C3.093,14.816,0,11.725,0,7.909S3.093,1,6.909,1s6.909,3.092,6.909,6.909
|
||||||
|
c0,1.641-0.573,3.144-1.528,4.328c0.031,0.021,0.068,0.039,0.1,0.068l1.14,1.141l0.066-0.066c0.223-0.223,0.587-0.223,0.811,0
|
||||||
|
l2.428,2.43C17.056,16.029,17.056,16.395,16.831,16.617z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 875 B |
5
src/icons/webfont/translate.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<title>translate</title>
|
||||||
|
<path d="M15.891 17.016h3.234l-1.641-4.359zM18.516 9.984l4.5 12h-2.016l-1.125-3h-4.734l-1.125 3h-2.016l4.5-12h2.016zM12.891 15.047l-0.797 2.063-3.094-3.094-5.016 4.969-1.406-1.406 5.109-5.016c-1.266-1.406-2.25-2.906-3-4.547h2.016c0.609 1.172 1.359 2.297 2.297 3.328 1.453-1.594 2.531-3.422 3.188-5.344h-11.203v-2.016h7.031v-1.969h1.969v1.969h7.031v2.016h-2.953c-0.75 2.344-1.969 4.594-3.703 6.516l-0.047 0.047z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 583 B |