/// Badaga has no fixed Latin spelling ("Madhuvae" / "Madhuve", "Hethai" / "Hettai" / "Hethe"). /// Both the query and the searchable text are folded to a common key before matching. /// Tamil-script text passes through unchanged (only lower-cased Latin is folded). String foldForSearch(String input) { var s = input.toLowerCase().replaceAll(RegExp(r"[^a-z0-9஀-௿ಀ-೿ ]"), ' '); const digraphs = { 'ae': 'e', 'ee': 'i', 'oo': 'u', 'aa': 'a', 'th': 't', 'dh': 'd', 'bh': 'b', 'ph': 'p', 'kh': 'k', 'gh': 'g', 'sh': 's', 'zh': 'l', 'w': 'v', }; digraphs.forEach((from, to) => s = s.replaceAll(from, to)); // Word-final "ai" and "e" are written interchangeably (Hethai / Hethe). s = s.replaceAllMapped(RegExp(r'ai\b'), (_) => 'e'); // Collapse doubled letters: "hettai" -> "hetai", "madhuvve" -> "madhuve". s = s.replaceAllMapped(RegExp(r'([a-z])\1+'), (m) => m[1]!); return s.replaceAll(RegExp(r'\s+'), ' ').trim(); } /// True when every word of [query] appears in at least one of [fields]. bool matchesSearch(String query, Iterable fields) { final words = foldForSearch(query).split(' ').where((w) => w.isNotEmpty).toList(); if (words.isEmpty) return false; final haystack = fields.whereType().map(foldForSearch).join(' '); return words.every(haystack.contains); }