mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-18 02:08:16 +00:00
Compare commits
17 Commits
6ed91eca05
...
2daf773d99
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2daf773d99 | ||
![]() |
97ce80b222 | ||
![]() |
e3ec61ab10 | ||
![]() |
8bde7fe8b9 | ||
![]() |
796bcc701d | ||
![]() |
9c9442ab84 | ||
![]() |
25b0143a12 | ||
![]() |
8f2834d02a | ||
![]() |
d59b6a7e12 | ||
![]() |
4122b4f26b | ||
![]() |
cdc956bba3 | ||
![]() |
bac259debf | ||
![]() |
3a232d9626 | ||
![]() |
3e1e6762aa | ||
![]() |
0733590c3b | ||
![]() |
c50cf3b544 | ||
![]() |
429ab8d210 |
|
@ -15,7 +15,6 @@ 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';
|
||||
|
@ -310,7 +309,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
>
|
||||
{intl.formatMessage(
|
||||
this.props.isEditing ?
|
||||
messages.saveChanges :
|
||||
messages.saveChanges :
|
||||
(this.props.isInReply ? messages.reply : messages.publish)
|
||||
)}
|
||||
</Button>
|
||||
|
|
|
@ -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 (
|
||||
|
|
5
app/javascript/mastodon/features/compose/util/language_detection.d.ts
vendored
Normal file
5
app/javascript/mastodon/features/compose/util/language_detection.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
export declare const debouncedGuess: (
|
||||
text: string,
|
||||
) => Promise<string | undefined>;
|
||||
|
||||
export declare const countLetters: (text: string) => number;
|
|
@ -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
|
||||
globalThis.LanguageDetector.create();
|
||||
}
|
||||
module = await import('./language_detection_with_languagedetector');
|
||||
} else {
|
||||
module = await import('./language_detection_with_lande');
|
||||
}
|
||||
const debouncedGuess = module.debouncedGuess;
|
||||
|
||||
export { debouncedGuess, countLetters, ISO_639_MAP };
|
||||
|
|
|
@ -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 };
|
|
@ -0,0 +1,41 @@
|
|||
import { debounce } from 'lodash';
|
||||
|
||||
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, '');
|
||||
|
||||
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 '';
|
||||
};
|
||||
|
||||
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 };
|
Loading…
Reference in New Issue
Block a user