nkbmusic/lib/core/search_normalize.dart
metatroncubeswdev 178d2d00ab Nanga Kathai: Flutter app, self-hosted backend and song tools
- Flutter app (Android/iOS): home, explore, albums, search, player with
  equalizer, mini player, sign up/sign in with email codes, profile,
  encrypted offline downloads, About page
- server/: one-command deploy of self-hosted Supabase behind HestiaCP/nginx
  (or Caddy), migrations, backups, email templates
- supabase/migrations: schema, row-level security, profiles
- tools/: song scanner and importer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:39:26 -04:00

40 lines
1.3 KiB
Dart

/// 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<String?> fields) {
final words = foldForSearch(query).split(' ').where((w) => w.isNotEmpty).toList();
if (words.isEmpty) return false;
final haystack = fields.whereType<String>().map(foldForSearch).join(' ');
return words.every(haystack.contains);
}