From 429ab8d21055aa25d8c7d92c140c7f1fd5db27ce Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Sat, 21 Jun 2025 13:19:04 +0200 Subject: [PATCH 1/9] Add language detection to compose widget --- .../compose/components/compose_form.jsx | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index 3611a74b4f7..1ee9bbb6920 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -11,6 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { length } from 'stringz'; import { missingAltTextModal } from 'mastodon/initial_state'; +import { changeComposeLanguage } from 'mastodon/actions/compose'; import AutosuggestInput from '../../../components/autosuggest_input'; import AutosuggestTextarea from '../../../components/autosuggest_textarea'; @@ -31,6 +32,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({ @@ -41,6 +44,28 @@ const messages = defineMessages({ reply: { id: 'compose_form.reply', defaultMessage: 'Reply' }, }); +const mapStateToProps = (state) => ({ + currentLanguage: state.compose.get('language'), +}); + +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, @@ -97,6 +122,30 @@ class ComposeForm extends ImmutablePureComponent { } }; + handleKeyUp = async (e) => { + if (!supportsLanguageDetector) { + return; + } + if (!languageDetector) { + languageDetector = await globalThis.LanguageDetector.create(); + } + const text = this.getFulltextForCharacterCounting(); + 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(''); }; @@ -274,6 +323,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} @@ -318,4 +368,4 @@ class ComposeForm extends ImmutablePureComponent { } -export default injectIntl(ComposeForm); +export default injectIntl(connect(mapStateToProps)(ComposeForm)); From c50cf3b5444d62b176eb7e708fe34562c38dd402 Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Sat, 21 Jun 2025 13:27:03 +0200 Subject: [PATCH 2/9] Use the UI language as default compose language --- .../mastodon/features/compose/components/compose_form.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index 1ee9bbb6920..b65b544607b 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -45,7 +45,7 @@ const messages = defineMessages({ }); const mapStateToProps = (state) => ({ - currentLanguage: state.compose.get('language'), + currentLanguage: state.meta.get('locale'), }); const languageDetectorInGlobalThis = 'LanguageDetector' in globalThis; @@ -129,9 +129,10 @@ class ComposeForm extends ImmutablePureComponent { if (!languageDetector) { languageDetector = await globalThis.LanguageDetector.create(); } - const text = this.getFulltextForCharacterCounting(); + const text = this.getFulltextForCharacterCounting().trim(); const currentLanguage = this.props.currentLanguage; if (!text || countLetters(text) <= 5) { + console.log('hier', currentLanguage) this.props.dispatch(changeComposeLanguage(currentLanguage)); return; } From 0733590c3b29e0aaef6aae96f67579e6ff4b26ab Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Sat, 21 Jun 2025 13:28:29 +0200 Subject: [PATCH 3/9] Remove log --- .../mastodon/features/compose/components/compose_form.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index b65b544607b..bb45cecb3a2 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -132,7 +132,6 @@ class ComposeForm extends ImmutablePureComponent { const text = this.getFulltextForCharacterCounting().trim(); const currentLanguage = this.props.currentLanguage; if (!text || countLetters(text) <= 5) { - console.log('hier', currentLanguage) this.props.dispatch(changeComposeLanguage(currentLanguage)); return; } From 3e1e6762aa7c35e0bb193d65f108a1f1733bcc16 Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Fri, 11 Jul 2025 08:37:44 +0200 Subject: [PATCH 4/9] Debounce language detection --- .../features/compose/components/compose_form.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index bb45cecb3a2..61c75211568 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -10,6 +10,8 @@ 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'; @@ -110,6 +112,7 @@ class ComposeForm extends ImmutablePureComponent { constructor(props) { super(props); this.textareaRef = createRef(null); + this.debouncedHandleKeyUp = debounce(this._handleKeyUp.bind(this), 500); } handleChange = (e) => { @@ -122,7 +125,11 @@ class ComposeForm extends ImmutablePureComponent { } }; - handleKeyUp = async (e) => { + handleKeyUp = (e) => { + this.debouncedHandleKeyUp(e); + } + + _handleKeyUp = async (e) => { if (!supportsLanguageDetector) { return; } @@ -211,6 +218,7 @@ class ComposeForm extends ImmutablePureComponent { componentWillUnmount () { if (this.timeout) clearTimeout(this.timeout); + this.debouncedHandleKeyUp.cancel(); } componentDidUpdate (prevProps) { From bac259debf419694f745d5ff41584051d9b20ea3 Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Fri, 11 Jul 2025 17:26:38 +0200 Subject: [PATCH 5/9] Restructure to use laude or LanguageDetector --- .../compose/components/compose_form.jsx | 46 +++-------------- .../compose/components/language_dropdown.tsx | 21 ++++++-- .../compose/util/language_detection.d.ts | 5 ++ .../compose/util/language_detection.js | 40 +++++++-------- ...anguage_detection_with_languagedetector.js | 50 +++++++++++++++++++ .../util/language_detection_with_laude.js | 38 ++++++++++++++ 6 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 app/javascript/mastodon/features/compose/util/language_detection.d.ts create mode 100644 app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js create mode 100644 app/javascript/mastodon/features/compose/util/language_detection_with_laude.js diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index 61c75211568..fcce6aec423 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -10,8 +10,6 @@ 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'; @@ -24,6 +22,7 @@ import PrivacyDropdownContainer from '../containers/privacy_dropdown_container'; import SpoilerButtonContainer from '../containers/spoiler_button_container'; import UploadButtonContainer from '../containers/upload_button_container'; import { countableText } from '../util/counter'; +import { debouncedGuess, countLetters } from '../util/language_detection'; import { CharacterCounter } from './character_counter'; import { EditIndicator } from './edit_indicator'; @@ -50,24 +49,6 @@ 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, @@ -112,7 +93,6 @@ class ComposeForm extends ImmutablePureComponent { constructor(props) { super(props); this.textareaRef = createRef(null); - this.debouncedHandleKeyUp = debounce(this._handleKeyUp.bind(this), 500); } handleChange = (e) => { @@ -125,30 +105,21 @@ class ComposeForm extends ImmutablePureComponent { } }; - handleKeyUp = (e) => { - this.debouncedHandleKeyUp(e); - } - - _handleKeyUp = async (e) => { - if (!supportsLanguageDetector) { - return; - } - if (!languageDetector) { - languageDetector = await globalThis.LanguageDetector.create(); - } + handleKeyUp = async (e) => { 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); + let detectedLanguage = await debouncedGuess(text); + if (!detectedLanguage) { + this.props.dispatch(changeComposeLanguage(currentLanguage)); + return; + } this.props.dispatch(changeComposeLanguage(detectedLanguage)); - } - catch { + } catch { this.props.dispatch(changeComposeLanguage(currentLanguage)); } } @@ -218,7 +189,6 @@ class ComposeForm extends ImmutablePureComponent { componentWillUnmount () { if (this.timeout) clearTimeout(this.timeout); - this.debouncedHandleKeyUp.cancel(); } componentDidUpdate (prevProps) { diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.tsx b/app/javascript/mastodon/features/compose/components/language_dropdown.tsx index d11891308f5..2fe73f34250 100644 --- a/app/javascript/mastodon/features/compose/components/language_dropdown.tsx +++ b/app/javascript/mastodon/features/compose/components/language_dropdown.tsx @@ -20,7 +20,7 @@ import { languages as preloadedLanguages } from 'mastodon/initial_state'; import type { RootState } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; -import { debouncedGuess } from '../util/language_detection'; +import { debouncedGuess, countLetters } from '../util/language_detection'; const messages = defineMessages({ changeLanguage: { @@ -375,12 +375,25 @@ export const LanguageDropdown: React.FC = () => { ); useEffect(() => { - if (text.length > 20) { - debouncedGuess(text, setGuess); + let canceled = false; + + if (countLetters(text) >= 5) { + debouncedGuess(text) + .then((lang) => { + if (!canceled) { + setGuess(lang ?? ''); + } + }) + .catch(() => { + setGuess(''); + }); } else { - debouncedGuess.cancel(); setGuess(''); } + + return () => { + canceled = true; + }; }, [text, setGuess]); return ( diff --git a/app/javascript/mastodon/features/compose/util/language_detection.d.ts b/app/javascript/mastodon/features/compose/util/language_detection.d.ts new file mode 100644 index 00000000000..289c0476b85 --- /dev/null +++ b/app/javascript/mastodon/features/compose/util/language_detection.d.ts @@ -0,0 +1,5 @@ +export declare const debouncedGuess: ( + text: string, +) => Promise; + +export declare const countLetters: (text: string) => number; diff --git a/app/javascript/mastodon/features/compose/util/language_detection.js b/app/javascript/mastodon/features/compose/util/language_detection.js index ed22a2bd9ca..531cb6f632b 100644 --- a/app/javascript/mastodon/features/compose/util/language_detection.js +++ b/app/javascript/mastodon/features/compose/util/language_detection.js @@ -1,7 +1,5 @@ -import lande from 'lande'; -import { debounce } from 'lodash'; - -import { urlRegex } from './url_regex'; +const languageDetectorInGlobalThis = 'LanguageDetector' in globalThis; +let languageDetectorSupportedAndReady = languageDetectorInGlobalThis && await globalThis.LanguageDetector.availability() === 'available'; const ISO_639_MAP = { afr: 'af', // Afrikaans @@ -56,21 +54,23 @@ const ISO_639_MAP = { vie: 'vi', // Vietnamese }; -const guessLanguage = (text) => { - text = text - .replace(urlRegex, '') - .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, ''); - - if (text.length > 20) { - const [lang, confidence] = lande(text)[0]; - - if (confidence > 0.8) - return ISO_639_MAP[lang]; - } - - return ''; +const countLetters = (text) => { + const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' }) + const letters = [...segmenter.segment(text)] + return letters.length }; -export const debouncedGuess = debounce((text, setGuess) => { - setGuess(guessLanguage(text)); -}, 500, { maxWait: 1500, leading: true, trailing: true }); +let module; +// If the API is supported, but the model not loaded yet… +if (languageDetectorInGlobalThis) { + if (!languageDetectorSupportedAndReady) { + // …trigger the model download + self.LanguageDetector.create(); + } + module = await import('./language_detection_with_languagedetector'); +} else { + module = await import('./language_detection_with_laude'); +} +const debouncedGuess = module.debouncedGuess; + +export { debouncedGuess, countLetters, ISO_639_MAP }; diff --git a/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js b/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js new file mode 100644 index 00000000000..3d98f236eb4 --- /dev/null +++ b/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js @@ -0,0 +1,50 @@ +import { debounce } from 'lodash'; + +import { countLetters } from './language_detection'; +import { urlRegex } from './url_regex'; + +const guessLanguage = async (text) => { + text = text + .replace(urlRegex, '') + .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, ''); + + if (countLetters(text) > 5) { + try { + const languageDetector = await self.LanguageDetector.create(); + let {detectedLanguage, confidence} = (await languageDetector.detect(text))[0]; + if (confidence > 0.8) { + detectedLanguage = detectedLanguage.split('-')[0]; + return detectedLanguage; + } + } catch { + return ''; + } + } + + return ''; +}; + +const debouncedGuess = (() => { + let resolver = null; + let rejecter = null; + + const debounced = debounce(async (text) => { + try { + const result = await guessLanguage(text); + if (resolver) { + resolver(result); + resolver = null; + } + } catch { + rejecter(''); + } + }, 500, { maxWait: 1500, leading: true, trailing: true }); + + return (text) => new Promise((resolve, reject) => { + resolver = resolve; + rejecter = reject; + debounced(text); + }); +})(); + +export { debouncedGuess }; diff --git a/app/javascript/mastodon/features/compose/util/language_detection_with_laude.js b/app/javascript/mastodon/features/compose/util/language_detection_with_laude.js new file mode 100644 index 00000000000..aa6e768a2c5 --- /dev/null +++ b/app/javascript/mastodon/features/compose/util/language_detection_with_laude.js @@ -0,0 +1,38 @@ +import lande from 'lande'; +import { debounce } from 'lodash'; + +import { countLetters, ISO_639_MAP } from './language_detection'; +import { urlRegex } from './url_regex'; + +const guessLanguage = (text) => { + text = text + .replace(urlRegex, '') + .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, ''); + + if (countLetters(text) > 20) { + const [lang, confidence] = lande(text)[0]; + if (confidence > 0.8) + return ISO_639_MAP[lang]; + } + + return ''; +}; + +const debouncedGuess = (() => { + let resolver = null; + + const debounced = debounce((text) => { + const result = guessLanguage(text); + if (resolver) { + resolver(result); + resolver = null; + } + }, 500, { maxWait: 1500, leading: true, trailing: true }); + + return (text) => new Promise((resolve) => { + resolver = resolve; + debounced(text); + }); +})(); + +export { debouncedGuess }; From d59b6a7e125b35a5be8591d1a465716b6c4aafad Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Fri, 11 Jul 2025 17:40:09 +0200 Subject: [PATCH 6/9] Log debug --- .../mastodon/features/compose/util/language_detection.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/mastodon/features/compose/util/language_detection.js b/app/javascript/mastodon/features/compose/util/language_detection.js index 531cb6f632b..3e25a41ab53 100644 --- a/app/javascript/mastodon/features/compose/util/language_detection.js +++ b/app/javascript/mastodon/features/compose/util/language_detection.js @@ -72,5 +72,6 @@ if (languageDetectorInGlobalThis) { module = await import('./language_detection_with_laude'); } const debouncedGuess = module.debouncedGuess; +console.log(debouncedGuess) export { debouncedGuess, countLetters, ISO_639_MAP }; From 25b0143a12e9c28366ff2b34cba1b0a2d30dcd44 Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Fri, 11 Jul 2025 19:14:25 +0200 Subject: [PATCH 7/9] Don't auto-change the language --- .../compose/components/compose_form.jsx | 29 +-------------- .../compose/util/language_detection.js | 3 +- ...anguage_detection_with_languagedetector.js | 37 +++++++------------ 3 files changed, 17 insertions(+), 52 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index c8a43787d29..b4b71f8310f 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -11,7 +11,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { length } from 'stringz'; 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'; @@ -46,10 +45,6 @@ const messages = defineMessages({ reply: { id: 'compose_form.reply', defaultMessage: 'Reply' }, }); -const mapStateToProps = (state) => ({ - currentLanguage: state.meta.get('locale'), -}); - class ComposeForm extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, @@ -106,25 +101,6 @@ class ComposeForm extends ImmutablePureComponent { } }; - handleKeyUp = async (e) => { - 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 debouncedGuess(text); - if (!detectedLanguage) { - this.props.dispatch(changeComposeLanguage(currentLanguage)); - return; - } - this.props.dispatch(changeComposeLanguage(detectedLanguage)); - } catch { - this.props.dispatch(changeComposeLanguage(currentLanguage)); - } - } - getFulltextForCharacterCounting = () => { return [this.props.spoiler? this.props.spoilerText: '', countableText(this.props.text)].join(''); }; @@ -301,7 +277,6 @@ 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} @@ -338,7 +313,7 @@ class ComposeForm extends ImmutablePureComponent { > {intl.formatMessage( this.props.isEditing ? - messages.saveChanges : + messages.saveChanges : (this.props.isInReply ? messages.reply : messages.publish) )} @@ -352,4 +327,4 @@ class ComposeForm extends ImmutablePureComponent { } -export default injectIntl(connect(mapStateToProps)(ComposeForm)); +export default injectIntl(ComposeForm); diff --git a/app/javascript/mastodon/features/compose/util/language_detection.js b/app/javascript/mastodon/features/compose/util/language_detection.js index 3e25a41ab53..79ca8db19ae 100644 --- a/app/javascript/mastodon/features/compose/util/language_detection.js +++ b/app/javascript/mastodon/features/compose/util/language_detection.js @@ -65,13 +65,12 @@ let module; if (languageDetectorInGlobalThis) { if (!languageDetectorSupportedAndReady) { // …trigger the model download - self.LanguageDetector.create(); + globalThis.LanguageDetector.create(); } module = await import('./language_detection_with_languagedetector'); } else { module = await import('./language_detection_with_laude'); } const debouncedGuess = module.debouncedGuess; -console.log(debouncedGuess) export { debouncedGuess, countLetters, ISO_639_MAP }; diff --git a/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js b/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js index 3d98f236eb4..de5df6e0921 100644 --- a/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js +++ b/app/javascript/mastodon/features/compose/util/language_detection_with_languagedetector.js @@ -1,6 +1,5 @@ import { debounce } from 'lodash'; -import { countLetters } from './language_detection'; import { urlRegex } from './url_regex'; const guessLanguage = async (text) => { @@ -8,17 +7,15 @@ const guessLanguage = async (text) => { .replace(urlRegex, '') .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, ''); - if (countLetters(text) > 5) { - try { - const languageDetector = await self.LanguageDetector.create(); - let {detectedLanguage, confidence} = (await languageDetector.detect(text))[0]; - if (confidence > 0.8) { - detectedLanguage = detectedLanguage.split('-')[0]; - return detectedLanguage; - } - } catch { - return ''; + try { + const languageDetector = await globalThis.LanguageDetector.create(); + let {detectedLanguage, confidence} = (await languageDetector.detect(text))[0]; + if (confidence > 0.8) { + detectedLanguage = detectedLanguage.split('-')[0]; + return detectedLanguage; } + } catch { + return ''; } return ''; @@ -26,23 +23,17 @@ const guessLanguage = async (text) => { const debouncedGuess = (() => { let resolver = null; - let rejecter = null; - const debounced = debounce(async (text) => { - try { - const result = await guessLanguage(text); - if (resolver) { - resolver(result); - resolver = null; - } - } catch { - rejecter(''); + const debounced = debounce((text) => { + const result = guessLanguage(text); + if (resolver) { + resolver(result); + resolver = null; } }, 500, { maxWait: 1500, leading: true, trailing: true }); - return (text) => new Promise((resolve, reject) => { + return (text) => new Promise((resolve) => { resolver = resolve; - rejecter = reject; debounced(text); }); })(); From 9c9442ab84659392a34b39f326c17f66b730a30f Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Fri, 11 Jul 2025 19:17:52 +0200 Subject: [PATCH 8/9] Remove unused imports --- .../mastodon/features/compose/components/compose_form.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index b4b71f8310f..a3ba3a49ad3 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -15,14 +15,12 @@ import { missingAltTextModal } from 'mastodon/initial_state'; import AutosuggestInput from 'mastodon/components/autosuggest_input'; import AutosuggestTextarea from 'mastodon/components/autosuggest_textarea'; import { Button } from 'mastodon/components/button'; -import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container'; import PollButtonContainer from '../containers/poll_button_container'; import PrivacyDropdownContainer from '../containers/privacy_dropdown_container'; import SpoilerButtonContainer from '../containers/spoiler_button_container'; import UploadButtonContainer from '../containers/upload_button_container'; import { countableText } from '../util/counter'; -import { debouncedGuess, countLetters } from '../util/language_detection'; import { CharacterCounter } from './character_counter'; import { EditIndicator } from './edit_indicator'; @@ -33,8 +31,6 @@ 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({ From 8bde7fe8b9b5b8150f75ee97b5bd34f462ed5c39 Mon Sep 17 00:00:00 2001 From: Thomas Steiner Date: Mon, 14 Jul 2025 13:34:41 +0200 Subject: [PATCH 9/9] Rename file, as it's lande, not laude --- .../mastodon/features/compose/util/language_detection.js | 2 +- ...detection_with_laude.js => language_detection_with_lande.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename app/javascript/mastodon/features/compose/util/{language_detection_with_laude.js => language_detection_with_lande.js} (100%) diff --git a/app/javascript/mastodon/features/compose/util/language_detection.js b/app/javascript/mastodon/features/compose/util/language_detection.js index 79ca8db19ae..b82b60fa37e 100644 --- a/app/javascript/mastodon/features/compose/util/language_detection.js +++ b/app/javascript/mastodon/features/compose/util/language_detection.js @@ -69,7 +69,7 @@ if (languageDetectorInGlobalThis) { } module = await import('./language_detection_with_languagedetector'); } else { - module = await import('./language_detection_with_laude'); + module = await import('./language_detection_with_lande'); } const debouncedGuess = module.debouncedGuess; diff --git a/app/javascript/mastodon/features/compose/util/language_detection_with_laude.js b/app/javascript/mastodon/features/compose/util/language_detection_with_lande.js similarity index 100% rename from app/javascript/mastodon/features/compose/util/language_detection_with_laude.js rename to app/javascript/mastodon/features/compose/util/language_detection_with_lande.js