Add warning when selected and detected language is different in web UI

This commit is contained in:
Eugen Rochko 2024-11-23 03:45:26 +01:00
parent 27e79da6b9
commit e3dc8200be
6 changed files with 105 additions and 3 deletions

View file

@ -238,6 +238,7 @@ class LanguageDropdown extends PureComponent {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string), frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string),
guess: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onChange: PropTypes.func, onChange: PropTypes.func,
}; };
@ -281,7 +282,7 @@ class LanguageDropdown extends PureComponent {
}; };
render () { render () {
const { value, intl, frequentlyUsedLanguages } = this.props; const { value, guess, intl, frequentlyUsedLanguages } = this.props;
const { open, placement } = this.state; const { open, placement } = this.state;
const current = preloadedLanguages.find(lang => lang[0] === value) ?? []; const current = preloadedLanguages.find(lang => lang[0] === value) ?? [];
@ -294,7 +295,7 @@ class LanguageDropdown extends PureComponent {
onClick={this.handleToggle} onClick={this.handleToggle}
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown} onKeyDown={this.handleButtonKeyDown}
className={classNames('dropdown-button', { active: open })} className={classNames('dropdown-button', { active: open, warning: guess !== '' && guess !== value })}
> >
<Icon icon={TranslateIcon} /> <Icon icon={TranslateIcon} />
<span className='dropdown-button__label'>{current[2] ?? value}</span> <span className='dropdown-button__label'>{current[2] ?? value}</span>

View file

@ -2,6 +2,8 @@ import { createSelector } from '@reduxjs/toolkit';
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap } from 'immutable';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import lande from 'lande';
import { debounce } from 'lodash';
import { changeComposeLanguage } from 'mastodon/actions/compose'; import { changeComposeLanguage } from 'mastodon/actions/compose';
@ -16,9 +18,80 @@ const getFrequentlyUsedLanguages = createSelector([
.toArray() .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 => ({ const mapStateToProps = state => ({
frequentlyUsedLanguages: getFrequentlyUsedLanguages(state), frequentlyUsedLanguages: getFrequentlyUsedLanguages(state),
value: state.getIn(['compose', 'language']), value: state.getIn(['compose', 'language']),
guess: detectedLanguage(state),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View file

@ -935,6 +935,16 @@ body > [data-popper-placement] {
border-color: $ui-highlight-color; border-color: $ui-highlight-color;
color: $primary-text-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 { .character-counter {

View file

@ -4,7 +4,7 @@ const { env, settings } = require('../configuration');
// Those modules contain modern ES code that need to be transpiled for Webpack to process it // Those modules contain modern ES code that need to be transpiled for Webpack to process it
const nodeModulesToProcess = [ const nodeModulesToProcess = [
'@reduxjs', 'fuzzysort' '@reduxjs', 'fuzzysort', 'toygrad'
]; ];
module.exports = { module.exports = {

View file

@ -82,6 +82,7 @@
"imports-loader": "^1.2.0", "imports-loader": "^1.2.0",
"intl-messageformat": "^10.3.5", "intl-messageformat": "^10.3.5",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lande": "^1.0.10",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mark-loader": "^0.1.6", "mark-loader": "^0.1.6",
"marky": "^1.2.5", "marky": "^1.2.5",

View file

@ -2930,6 +2930,7 @@ __metadata:
jest: "npm:^29.5.0" jest: "npm:^29.5.0"
jest-environment-jsdom: "npm:^29.5.0" jest-environment-jsdom: "npm:^29.5.0"
js-yaml: "npm:^4.1.0" js-yaml: "npm:^4.1.0"
lande: "npm:^1.0.10"
lint-staged: "npm:^15.0.0" lint-staged: "npm:^15.0.0"
lodash: "npm:^4.17.21" lodash: "npm:^4.17.21"
mark-loader: "npm:^0.1.6" mark-loader: "npm:^0.1.6"
@ -11322,6 +11323,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"lande@npm:^1.0.10":
version: 1.0.10
resolution: "lande@npm:1.0.10"
dependencies:
toygrad: "npm:^2.6.0"
checksum: 10c0/27300be5937b6b9e245a7ea7a8216a0dcf5286a3b7ae38886c10c5c75b83fbfa1a69cd6754ab26bb38c6bd18aa8a2dcb62dea873506accb245cf82084acfee71
languageName: node
linkType: hard
"language-subtag-registry@npm:^0.3.20": "language-subtag-registry@npm:^0.3.20":
version: 0.3.22 version: 0.3.22
resolution: "language-subtag-registry@npm:0.3.22" resolution: "language-subtag-registry@npm:0.3.22"
@ -17127,6 +17137,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"toygrad@npm:^2.6.0":
version: 2.6.0
resolution: "toygrad@npm:2.6.0"
checksum: 10c0/96e42ced87431e99cec7d9b446c7827fe7782c2fd82bb5fc8c4a0855679011d809f9967096a60b4c8ceca867a29f1aadd62af447bdb652cb6f7fee279ae743ed
languageName: node
linkType: hard
"tr46@npm:^1.0.1": "tr46@npm:^1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "tr46@npm:1.0.1" resolution: "tr46@npm:1.0.1"