trtext 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fikret İmamoğlu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # trtext
2
+
3
+ Turkish-correct string operations for JavaScript and TypeScript. Zero
4
+ dependencies, no ICU data required, pure functions only.
5
+
6
+ ## The problem
7
+
8
+ `String.prototype.toLowerCase()` is deliberately locale-independent, so it is
9
+ wrong for Turkish:
10
+
11
+ ```js
12
+ "I".toLowerCase() // "i" — should be "ı"
13
+ "İ".toLowerCase() // "i̇" — an "i" plus a combining dot above, length 2
14
+ "i".toUpperCase() // "I" — should be "İ"
15
+ ```
16
+
17
+ This is the [Turkish locale
18
+ bug](https://mattryall.net/blog/the-infamous-turkish-locale-bug), and it keeps
19
+ resurfacing: it has hit the [Kotlin
20
+ compiler](https://news.ycombinator.com/item?id=45559767),
21
+ [Gradle](https://github.com/gradle/gradle/issues/1506) and
22
+ [Vaadin](https://github.com/vaadin/flow/issues/7705), among many others. The
23
+ usual symptom is an identifier, class name, file extension or user login that
24
+ silently stops matching on a Turkish machine.
25
+
26
+ `toLocaleLowerCase("tr")` fixes the first line above, but it depends on the
27
+ host's ICU build (absent in some minimal Node builds, React Native and older
28
+ embedded runtimes) and it still leaves the combining dot behind on the second
29
+ line. This package is explicit and produces identical results everywhere.
30
+
31
+ ## Install
32
+
33
+ ```sh
34
+ npm install trtext
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ```js
40
+ import {
41
+ toLowerCaseTr,
42
+ toUpperCaseTr,
43
+ foldTr,
44
+ slugTr,
45
+ sortTr,
46
+ capitalizeTr,
47
+ } from "trtext"
48
+
49
+ toLowerCaseTr("İZMİR") // "izmir"
50
+ toLowerCaseTr("ISTANBUL") // "ıstanbul"
51
+ toUpperCaseTr("ığdır") // "IĞDIR"
52
+ capitalizeTr("istanbul") // "İstanbul"
53
+
54
+ foldTr(" ÇAĞRI Merkezi ") // "cagri merkezi"
55
+ slugTr("Şanlıurfa'da Güneş") // "sanliurfada-gunes"
56
+
57
+ sortTr(["Üsküdar", "Zonguldak", "Çorum", "Iğdır"])
58
+ // ["Çorum", "Iğdır", "Üsküdar", "Zonguldak"]
59
+ ```
60
+
61
+ ### Case-insensitive search that actually works
62
+
63
+ The common bug is a user typing `istanbul` and not matching a stored
64
+ `İSTANBUL`. Fold both sides to one comparison key:
65
+
66
+ ```js
67
+ const matches = cities.filter((city) => foldTr(city).includes(foldTr(query)))
68
+ ```
69
+
70
+ ## API
71
+
72
+ | Function | Description |
73
+ | --- | --- |
74
+ | `toLowerCaseTr(s)` | Lowercase with Turkish rules. `I` → `ı`, `İ` → `i`, and `I` + combining dot above → `i`. |
75
+ | `toUpperCaseTr(s)` | Uppercase with Turkish rules. `i` → `İ`, `ı` → `I`. |
76
+ | `capitalizeTr(s)` | First letter up, the rest down. |
77
+ | `titleCaseTr(s)` | Capitalize every word, preserving the original spacing. |
78
+ | `asciifyTr(s)` | Transliterate `çğıöşüâîû` to ASCII, preserving case. |
79
+ | `foldTr(s)` | Turkish lowercase + diacritics stripped + whitespace collapsed. A comparison key for search and deduplication. |
80
+ | `slugTr(s, opts?)` | URL slug. Options: `separator` (default `"-"`), `preserveTurkish` (default `false`). |
81
+ | `compareTr(a, b)` | Comparator for `Array.prototype.sort`, using Turkish alphabet order. |
82
+ | `sortTr(strings)` | New sorted array; the input is not mutated. |
83
+ | `TURKISH_ALPHABET` | The 29 letters in collation order. |
84
+
85
+ Collation follows the Turkish alphabet, where `ç` follows `c`, `ğ` follows
86
+ `g`, `ı` precedes `i`, `ö` follows `o`, `ş` follows `s` and `ü` follows `u`.
87
+ The test suite asserts that `sortTr` produces exactly the same order as
88
+ `Intl.Collator("tr")` on runtimes that ship Turkish collation data, and skips
89
+ rather than fails on runtimes that do not.
90
+
91
+ Characters outside the Turkish alphabet (digits, punctuation, foreign letters)
92
+ sort after it in code point order, so the ordering is always deterministic.
93
+
94
+ ## Related work
95
+
96
+ - [`Intl.Collator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator)
97
+ and `toLocaleLowerCase("tr")` — use these when you can rely on ICU being
98
+ present and you do not mind the combining-dot result.
99
+ - [`slugify`](https://www.npmjs.com/package/slugify) — a general-purpose
100
+ slugger with a much wider character map, but it lowercases with the default
101
+ locale, so dotted and dotless `i` get mangled.
102
+ - [`turkish-deasciifier`](https://www.npmjs.com/package/turkish-deasciifier) —
103
+ solves the opposite problem: guessing the missing diacritics in text typed on
104
+ an ASCII keyboard.
105
+
106
+ ## Development
107
+
108
+ ```sh
109
+ npm install
110
+ npm test # builds, then runs the suite on the compiled output
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT
116
+
117
+ ---
118
+
119
+ <details>
120
+ <summary>🇹🇷 Türkçe</summary>
121
+
122
+ ### Neden gerekli
123
+
124
+ JavaScript'in `toLowerCase()` fonksiyonu bilinçli olarak yerel ayardan
125
+ bağımsızdır, bu yüzden Türkçede hatalı çalışır: `"I".toLowerCase()` sonucu
126
+ `"ı"` yerine `"i"` verir, `"İ".toLowerCase()` ise temiz bir `"i"` yerine
127
+ üzerinde birleşen nokta taşıyan iki karakterlik bir dize döndürür.
128
+ `toLocaleLowerCase("tr")` ilk sorunu çözer ama çalıştığı ortamdaki ICU verisine
129
+ bağlıdır ve ikinci sorunu çözmez.
130
+
131
+ En sık görülen sonuç: kullanıcı `istanbul` yazıyor, veritabanındaki `İSTANBUL`
132
+ kaydı bulunamıyor. `foldTr` her iki tarafı tek bir karşılaştırma anahtarına
133
+ indirger ve sorun ortadan kalkar.
134
+
135
+ ### Kurulum ve kullanım
136
+
137
+ ```sh
138
+ npm install trtext
139
+ ```
140
+
141
+ ```js
142
+ import { toLowerCaseTr, slugTr, sortTr } from "trtext"
143
+
144
+ toLowerCaseTr("İZMİR") // "izmir"
145
+ slugTr("Şanlıurfa'da Güneş") // "sanliurfada-gunes"
146
+ sortTr(["Üsküdar", "Çorum"]) // ["Çorum", "Üsküdar"]
147
+ ```
148
+
149
+ Sıralama Türk alfabesi düzenini izler: `ç` harfi `c`'den, `ğ` harfi `g`'den,
150
+ `ö` harfi `o`'dan, `ş` harfi `s`'den ve `ü` harfi `u`'dan sonra gelir; `ı`
151
+ harfi `i`'den önce gelir. Testler, Türkçe sıralama verisi bulunan ortamlarda
152
+ sonucun `Intl.Collator("tr")` ile birebir aynı olduğunu doğrular.
153
+
154
+ Tüm fonksiyonların listesi için yukarıdaki API tablosuna bakın. Katkılar ve
155
+ hata bildirimleri memnuniyetle karşılanır.
156
+
157
+ </details>
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Turkish-correct text utilities. Zero dependencies.
3
+ *
4
+ * Why this exists: JavaScript's `toLowerCase()` / `toUpperCase()` are
5
+ * locale-independent, so `"I".toLowerCase()` returns `"i"` instead of the
6
+ * Turkish `"ı"`, and `"İ".toLowerCase()` returns `"i\u0307"` (an `i` plus a
7
+ * combining dot above) rather than a clean `"i"`. `toLocaleLowerCase("tr")`
8
+ * fixes the first case but still depends on the host ICU build and still
9
+ * leaves the combining mark behind in the second. These helpers are explicit
10
+ * and deterministic on every runtime.
11
+ */
12
+ /**
13
+ * Lowercase a string using Turkish rules.
14
+ *
15
+ * - `I` -> `ı`
16
+ * - `İ` (U+0130) -> `i`
17
+ * - `I` followed by a combining dot above -> `i`
18
+ *
19
+ * @example
20
+ * toLowerCaseTr("ISTANBUL") // "ıstanbul"
21
+ * toLowerCaseTr("İZMİR") // "izmir"
22
+ */
23
+ export declare function toLowerCaseTr(input: string): string;
24
+ /**
25
+ * Uppercase a string using Turkish rules.
26
+ *
27
+ * - `i` -> `İ`
28
+ * - `ı` -> `I`
29
+ *
30
+ * @example
31
+ * toUpperCaseTr("iğne") // "İĞNE"
32
+ */
33
+ export declare function toUpperCaseTr(input: string): string;
34
+ /**
35
+ * Replace Turkish-specific letters with their closest ASCII equivalents,
36
+ * preserving letter case. Characters outside the Turkish set are untouched.
37
+ *
38
+ * @example
39
+ * asciifyTr("Çiğdem Şahin") // "Cigdem Sahin"
40
+ */
41
+ export declare function asciifyTr(input: string): string;
42
+ /**
43
+ * Normalize a string for case-insensitive, diacritic-insensitive comparison
44
+ * and search. Applies Turkish lowercasing, then strips Turkish diacritics,
45
+ * then collapses whitespace.
46
+ *
47
+ * Both `"İSTANBUL"` and `"istanbul"` fold to `"istanbul"`, so user input
48
+ * matches stored data regardless of how either was capitalized.
49
+ *
50
+ * @example
51
+ * foldTr(" ÇAĞRI Merkezi ") // "cagri merkezi"
52
+ */
53
+ export declare function foldTr(input: string): string;
54
+ export type SlugOptions = {
55
+ /** Separator between words. Defaults to `"-"`. */
56
+ separator?: string;
57
+ /**
58
+ * Keep Turkish letters instead of transliterating them to ASCII.
59
+ * Defaults to `false`, which is what URLs almost always want.
60
+ */
61
+ preserveTurkish?: boolean;
62
+ };
63
+ /**
64
+ * Build a URL-safe slug from Turkish text.
65
+ *
66
+ * Unlike generic slug libraries, this lowercases with Turkish rules first, so
67
+ * dotted and dotless `i` survive the round trip instead of being mangled by
68
+ * the default locale.
69
+ *
70
+ * @example
71
+ * slugTr("Şanlıurfa'da Güneş") // "sanliurfada-gunes"
72
+ */
73
+ export declare function slugTr(input: string, options?: SlugOptions): string;
74
+ /** The Turkish alphabet in collation order. */
75
+ export declare const TURKISH_ALPHABET: readonly ["a", "b", "c", "ç", "d", "e", "f", "g", "ğ", "h", "ı", "i", "j", "k", "l", "m", "n", "o", "ö", "p", "r", "s", "ş", "t", "u", "ü", "v", "y", "z"];
76
+ /**
77
+ * Compare two strings using Turkish alphabet order, where `ç` follows `c`,
78
+ * `ğ` follows `g`, `ı` precedes `i`, `ö` follows `o`, `ş` follows `s` and `ü`
79
+ * follows `u`.
80
+ *
81
+ * Returns a negative number, zero, or a positive number, so it can be passed
82
+ * straight to `Array.prototype.sort`.
83
+ *
84
+ * @example
85
+ * ["çilek", "cam", "ıspanak", "incir"].sort(compareTr)
86
+ * // ["cam", "çilek", "ıspanak", "incir"]
87
+ */
88
+ export declare function compareTr(a: string, b: string): number;
89
+ /**
90
+ * Sort an array of strings in Turkish alphabet order. Returns a new array and
91
+ * leaves the input untouched.
92
+ */
93
+ export declare function sortTr(values: ReadonlyArray<string>): Array<string>;
94
+ /**
95
+ * Uppercase the first letter of a string with Turkish rules and lowercase the
96
+ * rest.
97
+ *
98
+ * @example
99
+ * capitalizeTr("istanbul") // "İstanbul"
100
+ */
101
+ export declare function capitalizeTr(input: string): string;
102
+ /**
103
+ * Capitalize every whitespace-separated word with Turkish rules, preserving
104
+ * the original spacing.
105
+ *
106
+ * @example
107
+ * titleCaseTr("ışık ılgın") // "Işık Ilgın"
108
+ */
109
+ export declare function titleCaseTr(input: string): string;
110
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAenD;AAuBD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,MAAM,MAAM,WAAW,GAAG;IACzB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAevE;AAED,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,4JA8BnB,CAAA;AAcV;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAStD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Turkish-correct text utilities. Zero dependencies.
3
+ *
4
+ * Why this exists: JavaScript's `toLowerCase()` / `toUpperCase()` are
5
+ * locale-independent, so `"I".toLowerCase()` returns `"i"` instead of the
6
+ * Turkish `"ı"`, and `"İ".toLowerCase()` returns `"i\u0307"` (an `i` plus a
7
+ * combining dot above) rather than a clean `"i"`. `toLocaleLowerCase("tr")`
8
+ * fixes the first case but still depends on the host ICU build and still
9
+ * leaves the combining mark behind in the second. These helpers are explicit
10
+ * and deterministic on every runtime.
11
+ */
12
+ const COMBINING_DOT_ABOVE = "\u0307";
13
+ /**
14
+ * Lowercase a string using Turkish rules.
15
+ *
16
+ * - `I` -> `ı`
17
+ * - `İ` (U+0130) -> `i`
18
+ * - `I` followed by a combining dot above -> `i`
19
+ *
20
+ * @example
21
+ * toLowerCaseTr("ISTANBUL") // "ıstanbul"
22
+ * toLowerCaseTr("İZMİR") // "izmir"
23
+ */
24
+ export function toLowerCaseTr(input) {
25
+ if (input.length === 0)
26
+ return input;
27
+ let out = "";
28
+ for (let i = 0; i < input.length; i++) {
29
+ const ch = input[i];
30
+ if (ch === "I") {
31
+ if (input[i + 1] === COMBINING_DOT_ABOVE) {
32
+ out += "i";
33
+ i++;
34
+ }
35
+ else {
36
+ out += "ı";
37
+ }
38
+ continue;
39
+ }
40
+ if (ch === "\u0130") {
41
+ out += "i";
42
+ continue;
43
+ }
44
+ out += ch.toLowerCase();
45
+ }
46
+ return out;
47
+ }
48
+ /**
49
+ * Uppercase a string using Turkish rules.
50
+ *
51
+ * - `i` -> `İ`
52
+ * - `ı` -> `I`
53
+ *
54
+ * @example
55
+ * toUpperCaseTr("iğne") // "İĞNE"
56
+ */
57
+ export function toUpperCaseTr(input) {
58
+ if (input.length === 0)
59
+ return input;
60
+ let out = "";
61
+ for (const ch of input) {
62
+ if (ch === "i") {
63
+ out += "\u0130";
64
+ continue;
65
+ }
66
+ if (ch === "ı") {
67
+ out += "I";
68
+ continue;
69
+ }
70
+ out += ch.toUpperCase();
71
+ }
72
+ return out;
73
+ }
74
+ const ASCII_MAP = {
75
+ ç: "c",
76
+ ğ: "g",
77
+ ı: "i",
78
+ ö: "o",
79
+ ş: "s",
80
+ ü: "u",
81
+ "â": "a",
82
+ "î": "i",
83
+ "û": "u",
84
+ Ç: "C",
85
+ Ğ: "G",
86
+ Ö: "O",
87
+ Ş: "S",
88
+ Ü: "U",
89
+ "\u0130": "I",
90
+ "Â": "A",
91
+ "Î": "I",
92
+ "Û": "U",
93
+ };
94
+ /**
95
+ * Replace Turkish-specific letters with their closest ASCII equivalents,
96
+ * preserving letter case. Characters outside the Turkish set are untouched.
97
+ *
98
+ * @example
99
+ * asciifyTr("Çiğdem Şahin") // "Cigdem Sahin"
100
+ */
101
+ export function asciifyTr(input) {
102
+ let out = "";
103
+ for (const ch of input) {
104
+ out += ASCII_MAP[ch] ?? ch;
105
+ }
106
+ return out;
107
+ }
108
+ /**
109
+ * Normalize a string for case-insensitive, diacritic-insensitive comparison
110
+ * and search. Applies Turkish lowercasing, then strips Turkish diacritics,
111
+ * then collapses whitespace.
112
+ *
113
+ * Both `"İSTANBUL"` and `"istanbul"` fold to `"istanbul"`, so user input
114
+ * matches stored data regardless of how either was capitalized.
115
+ *
116
+ * @example
117
+ * foldTr(" ÇAĞRI Merkezi ") // "cagri merkezi"
118
+ */
119
+ export function foldTr(input) {
120
+ return asciifyTr(toLowerCaseTr(input)).replace(/\s+/g, " ").trim();
121
+ }
122
+ /**
123
+ * Build a URL-safe slug from Turkish text.
124
+ *
125
+ * Unlike generic slug libraries, this lowercases with Turkish rules first, so
126
+ * dotted and dotless `i` survive the round trip instead of being mangled by
127
+ * the default locale.
128
+ *
129
+ * @example
130
+ * slugTr("Şanlıurfa'da Güneş") // "sanliurfada-gunes"
131
+ */
132
+ export function slugTr(input, options = {}) {
133
+ const separator = options.separator ?? "-";
134
+ const lowered = toLowerCaseTr(input);
135
+ const base = options.preserveTurkish ? lowered : asciifyTr(lowered);
136
+ const allowed = options.preserveTurkish
137
+ ? /[^a-z0-9çğıöşü]+/g
138
+ : /[^a-z0-9]+/g;
139
+ const slug = base
140
+ .normalize("NFC")
141
+ .replace(/['’`]/g, "")
142
+ .replace(allowed, separator);
143
+ const escaped = separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
144
+ return slug
145
+ .replace(new RegExp(`${escaped}{2,}`, "g"), separator)
146
+ .replace(new RegExp(`^${escaped}|${escaped}$`, "g"), "");
147
+ }
148
+ /** The Turkish alphabet in collation order. */
149
+ export const TURKISH_ALPHABET = [
150
+ "a",
151
+ "b",
152
+ "c",
153
+ "ç",
154
+ "d",
155
+ "e",
156
+ "f",
157
+ "g",
158
+ "ğ",
159
+ "h",
160
+ "ı",
161
+ "i",
162
+ "j",
163
+ "k",
164
+ "l",
165
+ "m",
166
+ "n",
167
+ "o",
168
+ "ö",
169
+ "p",
170
+ "r",
171
+ "s",
172
+ "ş",
173
+ "t",
174
+ "u",
175
+ "ü",
176
+ "v",
177
+ "y",
178
+ "z",
179
+ ];
180
+ const COLLATION_RANK = new Map(TURKISH_ALPHABET.map((letter, index) => [letter, index]));
181
+ function rankOf(ch) {
182
+ const rank = COLLATION_RANK.get(ch);
183
+ if (rank !== undefined)
184
+ return rank;
185
+ // Unknown characters (digits, punctuation, foreign letters) sort after the
186
+ // Turkish alphabet, in code point order, so ordering stays deterministic.
187
+ return TURKISH_ALPHABET.length + (ch.codePointAt(0) ?? 0);
188
+ }
189
+ /**
190
+ * Compare two strings using Turkish alphabet order, where `ç` follows `c`,
191
+ * `ğ` follows `g`, `ı` precedes `i`, `ö` follows `o`, `ş` follows `s` and `ü`
192
+ * follows `u`.
193
+ *
194
+ * Returns a negative number, zero, or a positive number, so it can be passed
195
+ * straight to `Array.prototype.sort`.
196
+ *
197
+ * @example
198
+ * ["çilek", "cam", "ıspanak", "incir"].sort(compareTr)
199
+ * // ["cam", "çilek", "ıspanak", "incir"]
200
+ */
201
+ export function compareTr(a, b) {
202
+ const left = [...toLowerCaseTr(a.normalize("NFC"))];
203
+ const right = [...toLowerCaseTr(b.normalize("NFC"))];
204
+ const length = Math.min(left.length, right.length);
205
+ for (let i = 0; i < length; i++) {
206
+ const diff = rankOf(left[i]) - rankOf(right[i]);
207
+ if (diff !== 0)
208
+ return diff;
209
+ }
210
+ return left.length - right.length;
211
+ }
212
+ /**
213
+ * Sort an array of strings in Turkish alphabet order. Returns a new array and
214
+ * leaves the input untouched.
215
+ */
216
+ export function sortTr(values) {
217
+ return [...values].sort(compareTr);
218
+ }
219
+ /**
220
+ * Uppercase the first letter of a string with Turkish rules and lowercase the
221
+ * rest.
222
+ *
223
+ * @example
224
+ * capitalizeTr("istanbul") // "İstanbul"
225
+ */
226
+ export function capitalizeTr(input) {
227
+ if (input.length === 0)
228
+ return input;
229
+ const chars = [...input];
230
+ const first = chars[0];
231
+ return toUpperCaseTr(first) + toLowerCaseTr(chars.slice(1).join(""));
232
+ }
233
+ /**
234
+ * Capitalize every whitespace-separated word with Turkish rules, preserving
235
+ * the original spacing.
236
+ *
237
+ * @example
238
+ * titleCaseTr("ışık ılgın") // "Işık Ilgın"
239
+ */
240
+ export function titleCaseTr(input) {
241
+ return input.replace(/\S+/g, (word) => capitalizeTr(word));
242
+ }
243
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,mBAAmB,GAAG,QAAQ,CAAA;AAEpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACpC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAW,CAAA;QAC7B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChB,IAAI,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,mBAAmB,EAAE,CAAC;gBAC1C,GAAG,IAAI,GAAG,CAAA;gBACV,CAAC,EAAE,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACP,GAAG,IAAI,GAAG,CAAA;YACX,CAAC;YACD,SAAQ;QACT,CAAC;QACD,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YACrB,GAAG,IAAI,GAAG,CAAA;YACV,SAAQ;QACT,CAAC;QACD,GAAG,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACxB,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACpC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChB,GAAG,IAAI,QAAQ,CAAA;YACf,SAAQ;QACT,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChB,GAAG,IAAI,GAAG,CAAA;YACV,SAAQ;QACT,CAAC;QACD,GAAG,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACxB,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC;AAED,MAAM,SAAS,GAA2B;IACzC,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,QAAQ,EAAE,GAAG;IACb,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;CACR,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACtC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACxB,GAAG,IAAI,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;IAC3B,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa;IACnC,OAAO,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AACnE,CAAC;AAYD;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,UAAuB,EAAE;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAA;IAC1C,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACnE,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe;QACtC,CAAC,CAAC,mBAAmB;QACrB,CAAC,CAAC,aAAa,CAAA;IAChB,MAAM,IAAI,GAAG,IAAI;SACf,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAC7B,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;IAChE,OAAO,IAAI;SACT,OAAO,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC;SACrD,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,OAAO,IAAI,OAAO,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;AAC1D,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;CACM,CAAA;AAEV,MAAM,cAAc,GAAG,IAAI,GAAG,CAC7B,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CACxD,CAAA;AAED,SAAS,MAAM,CAAC,EAAU;IACzB,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACnC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACnC,2EAA2E;IAC3E,0EAA0E;IAC1E,OAAO,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,CAAS;IAC7C,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACnD,MAAM,KAAK,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAA;QACnE,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,MAA6B;IACnD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACpC,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;IACxB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAW,CAAA;IAChC,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;AACrE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;AAC3D,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "trtext",
3
+ "version": "0.1.0",
4
+ "description": "Turkish-correct lowercase, uppercase, folding, slugs and alphabet sorting. Zero dependencies, no ICU required.",
5
+ "keywords": [
6
+ "turkish",
7
+ "turkce",
8
+ "locale",
9
+ "tolowercase",
10
+ "touppercase",
11
+ "dotless-i",
12
+ "slug",
13
+ "slugify",
14
+ "collation",
15
+ "normalize",
16
+ "diacritics",
17
+ "i18n"
18
+ ],
19
+ "license": "MIT",
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ }
26
+ },
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "files": ["dist", "README.md", "LICENSE"],
30
+ "sideEffects": false,
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc -p tsconfig.json",
36
+ "test": "npm run build && node --test test/*.test.mjs",
37
+ "prepublishOnly": "npm run test"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.6.0"
41
+ }
42
+ }