mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 22:32:12 +03:00
fix(i18n): Turkish locale-aware search and sorting (#3115)
* feat(i18n): add Turkish locale-aware text helpers (search/sort) * test(i18n): cover null/whitespace edges + document compareTr/normalize contract * fix(i18n): route dashboard search through Turkish-safe matchesSearch * fix(i18n): sort user-visible lists with Turkish collation (compareTr) * fix(i18n): keep providerId tiebreaker as ASCII sort (technical id) * docs(i18n): document intentional lang=en in global-error boundary * chore(lint): guard against locale-unsafe toLowerCase().includes search * fix(i18n): migrate missed provider-name search + harden lint disable placement * fix(i18n): downgrade no-restricted-syntax to warn (incremental adoption) The rule errored on ~19 pre-existing toLowerCase().includes() call-sites in src/app accumulated since this PR's base. Keep it as a warning so the guard-rail guides future code without breaking the 0-errors lint gate (project policy: 0 errors, warnings tolerated). --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
66
src/shared/utils/turkishText.ts
Normal file
66
src/shared/utils/turkishText.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Türkçe-güvenli metin normalizasyonu, arama ve sıralama yardımcıları.
|
||||
*
|
||||
* JavaScript'in locale-bağımsız `toLowerCase()`'i Türkçe `İ/I/ı/i`
|
||||
* karakterlerini bozar: `"İ".toLowerCase()` → `"i̇"` (U+0069 U+0307
|
||||
* combining dot above). Bu, `toLowerCase().includes()` tabanlı aramada
|
||||
* Türkçe girdileri eşleşmez kılar.
|
||||
*
|
||||
* Saf fonksiyonlar — yan etki ve bağımlılık yok.
|
||||
*
|
||||
* @module shared/utils/turkishText
|
||||
*/
|
||||
|
||||
/** NFD ile katlanmayan Türkçe harfler için açık eşleme. */
|
||||
const TR_FOLD: Readonly<Record<string, string>> = {
|
||||
ı: "i",
|
||||
ş: "s",
|
||||
ğ: "g",
|
||||
ü: "u",
|
||||
ö: "o",
|
||||
ç: "c",
|
||||
};
|
||||
|
||||
/**
|
||||
* Aksan-duyarsız, Türkçe-güvenli arama anahtarı üretir.
|
||||
* `"İstanbul"` ve `"istanbul"` → `"istanbul"`; `"Şarj"` → `"sarj"`.
|
||||
* Baştaki ve sondaki boşluklar kırpılır.
|
||||
*/
|
||||
export function normalizeForSearch(input: string | null | undefined): string {
|
||||
if (!input) return "";
|
||||
let s = input.toLocaleLowerCase("tr"); // İ→i, I→ı (Türkçe-güvenli)
|
||||
s = s.replace(/[ışğüöç]/g, (ch) => TR_FOLD[ch] ?? ch); // açık Türkçe fold
|
||||
s = s.normalize("NFD").replace(/[̀-ͯ]/g, ""); // kalan aksanları sil
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* `text`, `query`'yi (aksan-duyarsız, Türkçe-güvenli) içeriyor mu?
|
||||
* Boş sorgu her zaman `true` döner.
|
||||
*/
|
||||
export function matchesSearch(
|
||||
text: string | null | undefined,
|
||||
query: string | null | undefined
|
||||
): boolean {
|
||||
const q = normalizeForSearch(query);
|
||||
if (!q) return true;
|
||||
return normalizeForSearch(text).includes(q);
|
||||
}
|
||||
|
||||
const trCollator = new Intl.Collator("tr", {
|
||||
sensitivity: "base",
|
||||
numeric: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* Kullanıcı-görünür listeler için Türkçe alfabe + sayısal-duyarlı
|
||||
* karşılaştırma. `Array.prototype.sort` ile doğrudan kullanılabilir.
|
||||
*
|
||||
* `sensitivity: "base"` kullanıldığından aksan/büyük-küçük harf varyantları
|
||||
* sıralama açısından eşit sayılır — örneğin `"Şehir"` ile `"şehir"` berabere
|
||||
* gelir. Bu karşılaştırıcı görsel liste sıralaması içindir, kesin
|
||||
* aksan-farklılaştırmalı sıralama için değil.
|
||||
*/
|
||||
export function compareTr(a: string | null | undefined, b: string | null | undefined): number {
|
||||
return trCollator.compare(a ?? "", b ?? "");
|
||||
}
|
||||
Reference in New Issue
Block a user