Match multiple locales for spellchecker

This commit is contained in:
Jamie Kyle 2023-03-31 15:36:14 -07:00 committed by GitHub
parent 2edc47118e
commit a4055cec40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 31 deletions

View file

@ -9,6 +9,10 @@ Signal Desktop makes use of the following open source projects.
License: MIT
## @formatjs/intl-localematcher
License: MIT
## @indutny/frameless-titlebar
MIT License

View file

@ -800,7 +800,11 @@ async function createWindow() {
}
mainWindowCreated = true;
setupSpellChecker(mainWindow, getResolvedMessagesLocale());
setupSpellChecker(
mainWindow,
getPreferredSystemLocales(),
getResolvedMessagesLocale().i18n
);
if (!startInTray && windowConfig && windowConfig.maximized) {
mainWindow.maximize();
}

View file

@ -3,44 +3,62 @@
import type { BrowserWindow } from 'electron';
import { Menu, clipboard, nativeImage } from 'electron';
import { uniq } from 'lodash';
import { fileURLToPath } from 'url';
import * as LocaleMatcher from '@formatjs/intl-localematcher';
import { maybeParseUrl } from '../ts/util/url';
import type { LocaleType } from './locale';
import type { MenuListType } from '../ts/types/menu';
import type { LocalizerType } from '../ts/types/Util';
export function getLanguages(
userLocale: string,
availableLocales: ReadonlyArray<string>
preferredSystemLocales: ReadonlyArray<string>,
availableLocales: ReadonlyArray<string>,
defaultLocale: string
): Array<string> {
// First attempt to find the exact locale
const candidateLocales = uniq([userLocale, userLocale]).filter(l =>
availableLocales.includes(l)
);
if (candidateLocales.length > 0) {
return candidateLocales;
const matchedLocales = [];
preferredSystemLocales.forEach(preferredSystemLocale => {
if (preferredSystemLocale === defaultLocale) {
matchedLocales.push(defaultLocale);
return;
}
const matchedLocale = LocaleMatcher.match(
[preferredSystemLocale],
availableLocales as Array<string>, // bad types
defaultLocale,
{ algorithm: 'best fit' }
);
if (matchedLocale !== defaultLocale) {
matchedLocales.push(matchedLocale);
}
});
if (matchedLocales.length === 0) {
matchedLocales.push(defaultLocale);
}
// If no languages were found then return all locales that start with the base
const baseLocale = userLocale.split('-')[0];
return uniq(availableLocales.filter(l => l.startsWith(baseLocale)));
return matchedLocales;
}
export const setup = (
browserWindow: BrowserWindow,
{ name: userLocale, i18n }: LocaleType
preferredSystemLocales: ReadonlyArray<string>,
i18n: LocalizerType
): void => {
const { session } = browserWindow.webContents;
const availableLocales = session.availableSpellCheckerLanguages;
const languages = getLanguages(userLocale, availableLocales);
console.log(`spellcheck: user locale: ${userLocale}`);
const languages = getLanguages(
preferredSystemLocales,
availableLocales,
'en'
);
console.log('spellcheck: user locales:', preferredSystemLocales);
console.log(
'spellcheck: available spellchecker languages: ',
'spellcheck: available spellchecker languages:',
availableLocales
);
console.log('spellcheck: setting languages to: ', languages);
console.log('spellcheck: setting languages to:', languages);
session.setSpellCheckerLanguages(languages);
browserWindow.webContents.on('context-menu', (_event, params) => {

View file

@ -80,6 +80,7 @@
},
"dependencies": {
"@formatjs/fast-memoize": "1.2.6",
"@formatjs/intl-localematcher": "0.2.32",
"@indutny/frameless-titlebar": "2.3.5",
"@indutny/sneequals": "4.0.0",
"@popperjs/core": "2.11.6",

View file

@ -8,34 +8,48 @@ import { getLanguages } from '../../../app/spell_check';
describe('SpellCheck', () => {
describe('getLanguages', () => {
it('works with locale and base available', () => {
assert.deepEqual(getLanguages('en-US', ['en-US', 'en-CA', 'en']), [
assert.deepEqual(getLanguages(['en-US'], ['en-US', 'en'], 'en'), [
'en-US',
]);
});
it('works with neither locale nor base available', () => {
assert.deepEqual(getLanguages('en-US', ['en-NZ', 'en-CA']), [
'en-NZ',
'en-CA',
it('uses icu likely subtags rules to match languages', () => {
assert.deepEqual(getLanguages(['fa-FR'], ['fa-IR'], 'en'), ['fa-IR']);
assert.deepEqual(getLanguages(['zh'], ['zh-Hans-CN'], 'en'), [
'zh-Hans-CN',
]);
assert.deepEqual(
getLanguages(['zh-HK'], ['zh-Hans-CN', 'zh-Hant-HK'], 'en'),
['zh-Hant-HK']
);
});
it('matches multiple locales', () => {
assert.deepEqual(
getLanguages(['fr-FR', 'es'], ['fr', 'es-ES', 'en-US'], 'en'),
['fr', 'es-ES']
);
});
it('works with only base locale available', () => {
assert.deepEqual(getLanguages('en-US', ['en', 'en-CA']), ['en', 'en-CA']);
assert.deepEqual(getLanguages(['en-US'], ['en'], 'en'), ['en']);
});
it('works with only full locale available', () => {
assert.deepEqual(getLanguages('en-US', ['en-CA', 'en-US']), ['en-US']);
assert.deepEqual(getLanguages(['en-US'], ['en-CA', 'en-US'], 'en'), [
'en-US',
]);
});
it('works with base provided and base available', () => {
assert.deepEqual(getLanguages('en', ['en-CA', 'en-US', 'en']), ['en']);
assert.deepEqual(getLanguages(['en'], ['en-CA', 'en-US', 'en'], 'en'), [
'en',
]);
});
it('works with base provided and base not available', () => {
assert.deepEqual(getLanguages('en', ['en-CA', 'en-US']), [
'en-CA',
'en-US',
it('falls back to default', () => {
assert.deepEqual(getLanguages(['fa-IR'], ['es-ES', 'fr-FR'], 'en'), [
'en',
]);
});
});