Ignore Arabic short vowels and shadda in chat autocomplete

Arabic catalog strings carry optional marks ("تتبّع"), but people type
without them, so "تتبع" found no suggestion. Fold the marks out of both
the draft and the candidates before comparing.
This commit is contained in:
German Khokhlov committed 2026-11-04 23:35:25 +04:00
1 parent cd7c5d5a17
commit 06c84f92d3
2 files changed
+25 -6

No files matched your search

+6 -6
View File
@@ -22,6 +22,10 @@ export function bindChatPreferences({ ui, getDraft, setDraft }) {
msg('Reset to globe view'),
msg('Show earthquakes'),
].map((text) => t(text));
// Arabic short vowels and shadda are optional in writing: typing "تتبع"
// still finds "تتبّع".
const fold = (text) =>
text.toLowerCase().replace(/[\u064B-\u065F\u0670]/g, '');
try {
const stored = JSON.parse(localStorage.getItem(key));
if (typeof stored?.quick === 'boolean') quick = stored.quick;
@@ -31,18 +35,14 @@ export function bindChatPreferences({ ui, getDraft, setDraft }) {
function render() {
const draft = getDraft();
if (historyIndex >= 0 && draft !== recent[historyIndex]) resetHistory();
const query = draft.trim().toLowerCase();
const query = fold(draft.trim());
const groups = [];
if (query) {
const candidates = [...recent, ...(quick ? quickCommands() : [])];
groups.push({
label: t('Suggestions'),
items: [...new Set(candidates)]
.filter(
(text) =>
text.toLowerCase().includes(query) &&
text.toLowerCase() !== query,
)
.filter((text) => fold(text).includes(query) && fold(text) !== query)
.slice(0, 4),
});
} else {
+19
View File
@@ -1,5 +1,6 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { setLocale } from '../i18n/index.js';
import { bindChatPreferences } from './chatPreferences.js';
import { createTestVoiceUi } from '../testSupport/voiceUi.mjs';
@@ -99,3 +100,21 @@ test('editing a recalled message ends recall, and sending resets it', (t) => {
press('ArrowUp');
assert.equal(field.value, 'Show live flights now');
});
test('autocomplete ignores Arabic short vowels and shadda', (t) => {
setLocale('ar');
t.after(() => setLocale('en'));
const ui = createTestVoiceUi();
let draft = '';
const prefs = bindChatPreferences({ ui, getDraft: () => draft });
t.after(() => prefs.destroy());
const suggested = (text) => {
draft = text;
prefs.render();
return ui.state.chat.suggestions.flatMap((group) => group.items);
};
// The quick command is written "تتبّع", with a shadda.
assert.deepEqual(suggested('تتبع'), ['اعرض تتبّع الرحلات المباشر']);
prefs.remember('انتقل إلى طوكيو');
assert.deepEqual(suggested('طُوكيو'), ['انتقل إلى طوكيو']);
});