Compare commits

...

6 Commits

Author SHA1 Message Date
Thomas Steiner
fabd675951
Merge 3a232d9626 into 94bceb8683 2025-07-11 15:51:20 +02:00
Thomas Steiner
3a232d9626
Merge branch 'main' into compose-language-detection 2025-07-11 12:47:47 +02:00
Thomas Steiner
3e1e6762aa Debounce language detection 2025-07-11 08:37:44 +02:00
Thomas Steiner
0733590c3b Remove log 2025-06-21 13:28:29 +02:00
Thomas Steiner
c50cf3b544 Use the UI language as default compose language 2025-06-21 13:27:03 +02:00
Thomas Steiner
429ab8d210 Add language detection to compose widget 2025-06-21 13:19:04 +02:00

View File

@ -10,7 +10,10 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz';
import debounce from 'lodash.debounce';
import { missingAltTextModal } from 'mastodon/initial_state';
import { changeComposeLanguage } from 'mastodon/actions/compose';
import AutosuggestInput from 'mastodon/components/autosuggest_input';
import AutosuggestTextarea from 'mastodon/components/autosuggest_textarea';
@ -32,6 +35,8 @@ import { ReplyIndicator } from './reply_indicator';
import { UploadForm } from './upload_form';
import { Warning } from './warning';
import { connect } from 'react-redux';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
const messages = defineMessages({
@ -42,6 +47,28 @@ const messages = defineMessages({
reply: { id: 'compose_form.reply', defaultMessage: 'Reply' },
});
const mapStateToProps = (state) => ({
currentLanguage: state.meta.get('locale'),
});
const languageDetectorInGlobalThis = 'LanguageDetector' in globalThis;
let supportsLanguageDetector = languageDetectorInGlobalThis && await globalThis.LanguageDetector.availability() === 'available';
let languageDetector;
// If the API is supported, but the model not loaded yet
if (languageDetectorInGlobalThis && !supportsLanguageDetector) {
// trigger the model download
LanguageDetector.create().then((_languageDetector) => {
supportsLanguageDetector = true
languageDetector = _languageDetector
})
}
function countLetters(text) {
const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' })
const letters = [...segmenter.segment(text)]
return letters.length
}
class ComposeForm extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
@ -86,6 +113,7 @@ class ComposeForm extends ImmutablePureComponent {
constructor(props) {
super(props);
this.textareaRef = createRef(null);
this.debouncedHandleKeyUp = debounce(this._handleKeyUp.bind(this), 500);
}
handleChange = (e) => {
@ -98,6 +126,34 @@ class ComposeForm extends ImmutablePureComponent {
}
};
handleKeyUp = (e) => {
this.debouncedHandleKeyUp(e);
}
_handleKeyUp = async (e) => {
if (!supportsLanguageDetector) {
return;
}
if (!languageDetector) {
languageDetector = await globalThis.LanguageDetector.create();
}
const text = this.getFulltextForCharacterCounting().trim();
const currentLanguage = this.props.currentLanguage;
if (!text || countLetters(text) <= 5) {
this.props.dispatch(changeComposeLanguage(currentLanguage));
return;
}
try {
let detectedLanguage = (await languageDetector.detect(text))[0].detectedLanguage
detectedLanguage = detectedLanguage === 'und' ? currentLanguage : detectedLanguage.substring(0, 2);
this.props.dispatch(changeComposeLanguage(detectedLanguage));
}
catch {
this.props.dispatch(changeComposeLanguage(currentLanguage));
}
}
getFulltextForCharacterCounting = () => {
return [this.props.spoiler? this.props.spoilerText: '', countableText(this.props.text)].join('');
};
@ -163,6 +219,7 @@ class ComposeForm extends ImmutablePureComponent {
componentWillUnmount () {
if (this.timeout) clearTimeout(this.timeout);
this.debouncedHandleKeyUp.cancel();
}
componentDidUpdate (prevProps) {
@ -274,6 +331,7 @@ class ComposeForm extends ImmutablePureComponent {
suggestions={this.props.suggestions}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
onKeyUp={this.handleKeyUp}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
@ -324,4 +382,4 @@ class ComposeForm extends ImmutablePureComponent {
}
export default injectIntl(ComposeForm);
export default injectIntl(connect(mapStateToProps)(ComposeForm));