Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge upstream changes up to 7f866daf637e9303c7b503e0ed29ef8802fcb4c0 #2950

Merged
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ GEM
httplog (1.7.0)
rack (>= 2.0)
rainbow (>= 2.0.0)
i18n (1.14.6)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
i18n-tasks (1.0.14)
activesupport (>= 4.0.2)
Expand Down Expand Up @@ -426,7 +426,7 @@ GEM
net-smtp (0.5.0)
net-protocol
nio4r (2.7.4)
nokogiri (1.18.1)
nokogiri (1.18.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oj (3.16.9)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class LanguageDropdownMenu extends PureComponent {

static propTypes = {
value: PropTypes.string.isRequired,
guess: PropTypes.string,
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
Expand Down Expand Up @@ -81,14 +82,17 @@ class LanguageDropdownMenu extends PureComponent {
};

search () {
const { languages, value, frequentlyUsedLanguages } = this.props;
const { languages, value, frequentlyUsedLanguages, guess } = this.props;
const { searchValue } = this.state;

if (searchValue === '') {
return [...languages].sort((a, b) => {
// Push current selection to the top of the list

if (a[0] === value) {
if (guess && a[0] === guess) { // Push guessed language higher than current selection
return -1;
} else if (guess && b[0] === guess) {
return 1;
} else if (a[0] === value) { // Push current selection to the top of the list
return -1;
} else if (b[0] === value) {
return 1;
Expand Down Expand Up @@ -238,6 +242,7 @@ class LanguageDropdown extends PureComponent {
static propTypes = {
value: PropTypes.string,
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string),
guess: PropTypes.string,
intl: PropTypes.object.isRequired,
onChange: PropTypes.func,
};
Expand Down Expand Up @@ -281,7 +286,7 @@ class LanguageDropdown extends PureComponent {
};

render () {
const { value, intl, frequentlyUsedLanguages } = this.props;
const { value, guess, intl, frequentlyUsedLanguages } = this.props;
const { open, placement } = this.state;
const current = preloadedLanguages.find(lang => lang[0] === value) ?? [];

Expand All @@ -294,7 +299,7 @@ class LanguageDropdown extends PureComponent {
onClick={this.handleToggle}
onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown}
className={classNames('dropdown-button', { active: open })}
className={classNames('dropdown-button', { active: open, warning: guess !== '' && guess !== value })}
>
<Icon icon={TranslateIcon} />
<span className='dropdown-button__label'>{current[2] ?? value}</span>
Expand All @@ -306,6 +311,7 @@ class LanguageDropdown extends PureComponent {
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
<LanguageDropdownMenu
value={value}
guess={guess}
frequentlyUsedLanguages={frequentlyUsedLanguages}
onClose={this.handleClose}
onChange={this.handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createSelector } from '@reduxjs/toolkit';
import { Map as ImmutableMap } from 'immutable';
import { connect } from 'react-redux';

import lande from 'lande';
import { debounce } from 'lodash';

import { changeComposeLanguage } from 'flavours/glitch/actions/compose';

Expand All @@ -16,9 +18,80 @@ const getFrequentlyUsedLanguages = createSelector([
.toArray()
));

const ISO_639_MAP = {
afr: 'af', // Afrikaans
ara: 'ar', // Arabic
aze: 'az', // Azerbaijani
bel: 'be', // Belarusian
ben: 'bn', // Bengali
bul: 'bg', // Bulgarian
cat: 'ca', // Catalan
ces: 'cs', // Czech
ckb: 'ku', // Kurdish
cmn: 'zh', // Mandarin
dan: 'da', // Danish
deu: 'de', // German
ell: 'el', // Greek
eng: 'en', // English
est: 'et', // Estonian
eus: 'eu', // Basque
fin: 'fi', // Finnish
fra: 'fr', // French
hau: 'ha', // Hausa
heb: 'he', // Hebrew
hin: 'hi', // Hindi
hrv: 'hr', // Croatian
hun: 'hu', // Hungarian
hye: 'hy', // Armenian
ind: 'id', // Indonesian
isl: 'is', // Icelandic
ita: 'it', // Italian
jpn: 'ja', // Japanese
kat: 'ka', // Georgian
kaz: 'kk', // Kazakh
kor: 'ko', // Korean
lit: 'lt', // Lithuanian
mar: 'mr', // Marathi
mkd: 'mk', // Macedonian
nld: 'nl', // Dutch
nob: 'no', // Norwegian
pes: 'fa', // Persian
pol: 'pl', // Polish
por: 'pt', // Portuguese
ron: 'ro', // Romanian
run: 'rn', // Rundi
rus: 'ru', // Russian
slk: 'sk', // Slovak
spa: 'es', // Spanish
srp: 'sr', // Serbian
swe: 'sv', // Swedish
tgl: 'tl', // Tagalog
tur: 'tr', // Turkish
ukr: 'uk', // Ukrainian
vie: 'vi', // Vietnamese
};

const debouncedLande = debounce((text) => lande(text), 500, { trailing: true });

const detectedLanguage = createSelector([
state => state.getIn(['compose', 'text']),
], text => {
if (text.length > 20) {
const guesses = debouncedLande(text);
const [lang, confidence] = guesses[0];

if (confidence > 0.8) {
return ISO_639_MAP[lang];
}
}

return '';
});

const mapStateToProps = state => ({
frequentlyUsedLanguages: getFrequentlyUsedLanguages(state),
value: state.getIn(['compose', 'language']),
guess: detectedLanguage(state),
});

const mapDispatchToProps = dispatch => ({
Expand Down
10 changes: 10 additions & 0 deletions app/javascript/flavours/glitch/styles/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,16 @@ body > [data-popper-placement] {
border-color: $ui-highlight-color;
color: $primary-text-color;
}

&.warning {
border-color: var(--goldenrod-2);
color: var(--goldenrod-2);

&.active {
background-color: var(--goldenrod-2);
color: var(--indigo-1);
}
}
}

.character-counter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class LanguageDropdownMenu extends PureComponent {

static propTypes = {
value: PropTypes.string.isRequired,
guess: PropTypes.string,
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
Expand Down Expand Up @@ -81,14 +82,17 @@ class LanguageDropdownMenu extends PureComponent {
};

search () {
const { languages, value, frequentlyUsedLanguages } = this.props;
const { languages, value, frequentlyUsedLanguages, guess } = this.props;
const { searchValue } = this.state;

if (searchValue === '') {
return [...languages].sort((a, b) => {
// Push current selection to the top of the list

if (a[0] === value) {
if (guess && a[0] === guess) { // Push guessed language higher than current selection
return -1;
} else if (guess && b[0] === guess) {
return 1;
} else if (a[0] === value) { // Push current selection to the top of the list
return -1;
} else if (b[0] === value) {
return 1;
Expand Down Expand Up @@ -238,6 +242,7 @@ class LanguageDropdown extends PureComponent {
static propTypes = {
value: PropTypes.string,
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string),
guess: PropTypes.string,
intl: PropTypes.object.isRequired,
onChange: PropTypes.func,
};
Expand Down Expand Up @@ -281,7 +286,7 @@ class LanguageDropdown extends PureComponent {
};

render () {
const { value, intl, frequentlyUsedLanguages } = this.props;
const { value, guess, intl, frequentlyUsedLanguages } = this.props;
const { open, placement } = this.state;
const current = preloadedLanguages.find(lang => lang[0] === value) ?? [];

Expand All @@ -294,7 +299,7 @@ class LanguageDropdown extends PureComponent {
onClick={this.handleToggle}
onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown}
className={classNames('dropdown-button', { active: open })}
className={classNames('dropdown-button', { active: open, warning: guess !== '' && guess !== value })}
>
<Icon icon={TranslateIcon} />
<span className='dropdown-button__label'>{current[2] ?? value}</span>
Expand All @@ -306,6 +311,7 @@ class LanguageDropdown extends PureComponent {
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
<LanguageDropdownMenu
value={value}
guess={guess}
frequentlyUsedLanguages={frequentlyUsedLanguages}
onClose={this.handleClose}
onChange={this.handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createSelector } from '@reduxjs/toolkit';
import { Map as ImmutableMap } from 'immutable';
import { connect } from 'react-redux';

import lande from 'lande';
import { debounce } from 'lodash';

import { changeComposeLanguage } from 'mastodon/actions/compose';

Expand All @@ -16,9 +18,80 @@ const getFrequentlyUsedLanguages = createSelector([
.toArray()
));

const ISO_639_MAP = {
afr: 'af', // Afrikaans
ara: 'ar', // Arabic
aze: 'az', // Azerbaijani
bel: 'be', // Belarusian
ben: 'bn', // Bengali
bul: 'bg', // Bulgarian
cat: 'ca', // Catalan
ces: 'cs', // Czech
ckb: 'ku', // Kurdish
cmn: 'zh', // Mandarin
dan: 'da', // Danish
deu: 'de', // German
ell: 'el', // Greek
eng: 'en', // English
est: 'et', // Estonian
eus: 'eu', // Basque
fin: 'fi', // Finnish
fra: 'fr', // French
hau: 'ha', // Hausa
heb: 'he', // Hebrew
hin: 'hi', // Hindi
hrv: 'hr', // Croatian
hun: 'hu', // Hungarian
hye: 'hy', // Armenian
ind: 'id', // Indonesian
isl: 'is', // Icelandic
ita: 'it', // Italian
jpn: 'ja', // Japanese
kat: 'ka', // Georgian
kaz: 'kk', // Kazakh
kor: 'ko', // Korean
lit: 'lt', // Lithuanian
mar: 'mr', // Marathi
mkd: 'mk', // Macedonian
nld: 'nl', // Dutch
nob: 'no', // Norwegian
pes: 'fa', // Persian
pol: 'pl', // Polish
por: 'pt', // Portuguese
ron: 'ro', // Romanian
run: 'rn', // Rundi
rus: 'ru', // Russian
slk: 'sk', // Slovak
spa: 'es', // Spanish
srp: 'sr', // Serbian
swe: 'sv', // Swedish
tgl: 'tl', // Tagalog
tur: 'tr', // Turkish
ukr: 'uk', // Ukrainian
vie: 'vi', // Vietnamese
};

const debouncedLande = debounce((text) => lande(text), 500, { trailing: true });

const detectedLanguage = createSelector([
state => state.getIn(['compose', 'text']),
], text => {
if (text.length > 20) {
const guesses = debouncedLande(text);
const [lang, confidence] = guesses[0];

if (confidence > 0.8) {
return ISO_639_MAP[lang];
}
}

return '';
});

const mapStateToProps = state => ({
frequentlyUsedLanguages: getFrequentlyUsedLanguages(state),
value: state.getIn(['compose', 'language']),
guess: detectedLanguage(state),
});

const mapDispatchToProps = dispatch => ({
Expand Down
Loading
Loading