tdk-api-wrapper 1.5.0 → 1.6.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/README.md +1 -1
- package/dist/{chunk-7KJHYRJZ.mjs → chunk-ZFWZXNGY.mjs} +389 -13
- package/dist/cli.js +394 -14
- package/dist/cli.mjs +6 -2
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +389 -13
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/cli.ts +6 -1
- package/src/morphology.ts +9 -0
- package/src/tdk.ts +441 -40
- package/test/morphology.test.js +11 -0
- package/test/proofread.test.js +28 -0
package/dist/index.js
CHANGED
|
@@ -584,6 +584,13 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
|
|
|
584
584
|
seen.add(apostropheStem);
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
|
+
const bareInfinitives = restoreInfinitive(normalized);
|
|
588
|
+
for (const inf of bareInfinitives) {
|
|
589
|
+
if (!seen.has(inf) && inf !== normalized) {
|
|
590
|
+
seen.add(inf);
|
|
591
|
+
candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
587
594
|
let frontier = [normalized];
|
|
588
595
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
589
596
|
const nextFrontier = [];
|
|
@@ -633,6 +640,210 @@ var path = __toESM(require("path"));
|
|
|
633
640
|
var os = __toESM(require("os"));
|
|
634
641
|
var https = __toESM(require("https"));
|
|
635
642
|
var tls = __toESM(require("tls"));
|
|
643
|
+
var COMMON_MISSPELLINGS = {
|
|
644
|
+
// -şey ile biten ve ayrı yazılması zorunlu söz öbekleri
|
|
645
|
+
her\u015Fey: "her \u015Fey",
|
|
646
|
+
hersey: "her \u015Fey",
|
|
647
|
+
bir\u015Fey: "bir \u015Fey",
|
|
648
|
+
birsey: "bir \u015Fey",
|
|
649
|
+
hi\u00E7bir\u015Fey: "hi\xE7bir \u015Fey",
|
|
650
|
+
hicbirsey: "hi\xE7bir \u015Fey",
|
|
651
|
+
\u00E7ok\u015Fey: "\xE7ok \u015Fey",
|
|
652
|
+
coksey: "\xE7ok \u015Fey",
|
|
653
|
+
\u015Feyler: "\u015Feyler",
|
|
654
|
+
seyler: "\u015Feyler",
|
|
655
|
+
herhangibir\u015Fey: "herhangi bir \u015Fey",
|
|
656
|
+
herhangibirsey: "herhangi bir \u015Fey",
|
|
657
|
+
// Sıkça birleşik yazılan ama ayrı yazılması gereken sözler
|
|
658
|
+
herg\u00FCn: "her g\xFCn",
|
|
659
|
+
hergun: "her g\xFCn",
|
|
660
|
+
herzaman: "her zaman",
|
|
661
|
+
heran: "her an",
|
|
662
|
+
heryer: "her yer",
|
|
663
|
+
herbiri: "her biri",
|
|
664
|
+
pek\u00E7ok: "pek \xE7ok",
|
|
665
|
+
pekcok: "pek \xE7ok",
|
|
666
|
+
pekaz: "pek az",
|
|
667
|
+
yada: "ya da",
|
|
668
|
+
tabiki: "tabii ki",
|
|
669
|
+
tabiiki: "tabii ki",
|
|
670
|
+
sa\u011Fol: "sa\u011F ol",
|
|
671
|
+
sagol: "sa\u011F ol",
|
|
672
|
+
sa\u011Folun: "sa\u011F olun",
|
|
673
|
+
sagolun: "sa\u011F olun",
|
|
674
|
+
ho\u015F\u00E7akal: "ho\u015F\xE7a kal",
|
|
675
|
+
hoscakal: "ho\u015F\xE7a kal",
|
|
676
|
+
ho\u015Fgeldin: "ho\u015F geldin",
|
|
677
|
+
hosgeldin: "ho\u015F geldin",
|
|
678
|
+
ho\u015Fgeldiniz: "ho\u015F geldiniz",
|
|
679
|
+
hosgeldiniz: "ho\u015F geldiniz",
|
|
680
|
+
ho\u015Fbulduk: "ho\u015F bulduk",
|
|
681
|
+
hosbulduk: "ho\u015F bulduk",
|
|
682
|
+
yan\u0131s\u0131ra: "yan\u0131 s\u0131ra",
|
|
683
|
+
yanisira: "yan\u0131 s\u0131ra",
|
|
684
|
+
pe\u015Fis\u0131ra: "pe\u015Fi s\u0131ra",
|
|
685
|
+
pesisira: "pe\u015Fi s\u0131ra",
|
|
686
|
+
ard\u0131s\u0131ra: "ard\u0131 s\u0131ra",
|
|
687
|
+
ardisira: "ard\u0131 s\u0131ra",
|
|
688
|
+
artarda: "art arda",
|
|
689
|
+
y\u00FCzy\u00FCze: "y\xFCz y\xFCze",
|
|
690
|
+
yuzyuze: "y\xFCz y\xFCze",
|
|
691
|
+
elele: "el ele",
|
|
692
|
+
g\u00F6zg\u00F6ze: "g\xF6z g\xF6ze",
|
|
693
|
+
ba\u015Fba\u015Fa: "ba\u015F ba\u015Fa",
|
|
694
|
+
basbasa: "ba\u015F ba\u015Fa",
|
|
695
|
+
yanyana: "yan yana",
|
|
696
|
+
i\u00E7i\u00E7e: "i\xE7 i\xE7e",
|
|
697
|
+
icice: "i\xE7 i\xE7e",
|
|
698
|
+
\u00FCst\u00FCste: "\xFCst \xFCste",
|
|
699
|
+
ustuste: "\xFCst \xFCste",
|
|
700
|
+
altalta: "alt alta",
|
|
701
|
+
\u00F6ns\u00F6z: "\xF6n s\xF6z",
|
|
702
|
+
onsoz: "\xF6n s\xF6z",
|
|
703
|
+
\u00F6nyarg\u0131: "\xF6n yarg\u0131",
|
|
704
|
+
onyargi: "\xF6n yarg\u0131",
|
|
705
|
+
farketmek: "fark etmek",
|
|
706
|
+
farketti: "fark etti",
|
|
707
|
+
farkettim: "fark ettim",
|
|
708
|
+
farkeder: "fark eder",
|
|
709
|
+
farketmez: "fark etmez",
|
|
710
|
+
terketmek: "terk etmek",
|
|
711
|
+
terketti: "terk etti",
|
|
712
|
+
ay\u0131rdetmek: "ay\u0131rt etmek",
|
|
713
|
+
ay\u0131rtetmek: "ay\u0131rt etmek",
|
|
714
|
+
arzetmek: "arz etmek",
|
|
715
|
+
arzederim: "arz ederim",
|
|
716
|
+
varolmak: "var olmak",
|
|
717
|
+
yokolmak: "yok olmak",
|
|
718
|
+
haketmek: "hak etmek",
|
|
719
|
+
haketti: "hak etti",
|
|
720
|
+
hakkaten: "hakikaten",
|
|
721
|
+
hi\u00E7kimse: "hi\xE7 kimse",
|
|
722
|
+
hickimse: "hi\xE7 kimse",
|
|
723
|
+
// Ünlü düşmesi yapılmaması gereken yer bildiren sözler (TDK Kural 15)
|
|
724
|
+
burda: "burada",
|
|
725
|
+
burdan: "buradan",
|
|
726
|
+
\u015Furda: "\u015Furada",
|
|
727
|
+
surda: "\u015Furada",
|
|
728
|
+
\u015Furdan: "\u015Furadan",
|
|
729
|
+
surdan: "\u015Furadan",
|
|
730
|
+
orda: "orada",
|
|
731
|
+
ordan: "oradan",
|
|
732
|
+
i\u00E7erde: "i\xE7eride",
|
|
733
|
+
icerde: "i\xE7eride",
|
|
734
|
+
i\u00E7erden: "i\xE7eriden",
|
|
735
|
+
icerden: "i\xE7eriden",
|
|
736
|
+
d\u0131\u015Farda: "d\u0131\u015Far\u0131da",
|
|
737
|
+
disarda: "d\u0131\u015Far\u0131da",
|
|
738
|
+
d\u0131\u015Fardan: "d\u0131\u015Far\u0131dan",
|
|
739
|
+
disardan: "d\u0131\u015Far\u0131dan",
|
|
740
|
+
yukarda: "yukar\u0131da",
|
|
741
|
+
yukardan: "yukar\u0131dan",
|
|
742
|
+
// Sıkça yanlış yazılan sözcükler
|
|
743
|
+
herkez: "herkes",
|
|
744
|
+
yanl\u0131z: "yaln\u0131z",
|
|
745
|
+
yaln\u0131\u015F: "yanl\u0131\u015F",
|
|
746
|
+
orjinal: "orijinal",
|
|
747
|
+
labaratuar: "laboratuvar",
|
|
748
|
+
laboratuar: "laboratuvar",
|
|
749
|
+
\u015F\u00F6f\u00F6r: "\u015Fof\xF6r",
|
|
750
|
+
sofor: "\u015Fof\xF6r",
|
|
751
|
+
egzos: "egzoz",
|
|
752
|
+
eksoz: "egzoz",
|
|
753
|
+
ekzoz: "egzoz",
|
|
754
|
+
kiprik: "kirpik",
|
|
755
|
+
kirbit: "kibrit",
|
|
756
|
+
klavuz: "k\u0131lavuz",
|
|
757
|
+
k\u0131ravat: "kravat",
|
|
758
|
+
s\u00FCpriz: "s\xFCrpriz",
|
|
759
|
+
supriz: "s\xFCrpriz",
|
|
760
|
+
raslant\u0131: "rastlant\u0131",
|
|
761
|
+
hastahane: "hastane",
|
|
762
|
+
pastahane: "pastane",
|
|
763
|
+
postahane: "postane",
|
|
764
|
+
eczahane: "eczane",
|
|
765
|
+
meyva: "meyve",
|
|
766
|
+
sarm\u0131sak: "sar\u0131msak",
|
|
767
|
+
dinazor: "dinozor",
|
|
768
|
+
pantalon: "pantolon",
|
|
769
|
+
tesbih: "tespih",
|
|
770
|
+
ah\u00E7\u0131: "a\u015F\xE7\u0131",
|
|
771
|
+
matba: "matbaa",
|
|
772
|
+
idda: "iddia",
|
|
773
|
+
iddaa: "iddia",
|
|
774
|
+
muhattap: "muhatap",
|
|
775
|
+
tra\u015F: "t\u0131ra\u015F",
|
|
776
|
+
karn\u0131bahar: "karnabahar",
|
|
777
|
+
kareografi: "koreografi",
|
|
778
|
+
poa\u00E7a: "po\u011Fa\xE7a",
|
|
779
|
+
poha\u00E7a: "po\u011Fa\xE7a",
|
|
780
|
+
\u015Farz: "\u015Farj",
|
|
781
|
+
sarj: "\u015Farj",
|
|
782
|
+
makina: "makine",
|
|
783
|
+
m\u00FCsade: "m\xFCsaade",
|
|
784
|
+
entellekt\u00FCel: "entelekt\xFCel",
|
|
785
|
+
inisiyatif: "inisiyatif",
|
|
786
|
+
insiyatif: "inisiyatif",
|
|
787
|
+
sezeryan: "sezaryen",
|
|
788
|
+
dok\u00FCman: "dok\xFCman",
|
|
789
|
+
d\u00F6k\u00FCman: "dok\xFCman",
|
|
790
|
+
erozyon: "erozyon",
|
|
791
|
+
erizyon: "erozyon",
|
|
792
|
+
anane: "anneanne",
|
|
793
|
+
babaanne: "babaanne"
|
|
794
|
+
};
|
|
795
|
+
var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
|
|
796
|
+
var KEYBOARD_ROWS = [
|
|
797
|
+
["qwertyu\u0131op\u011F\xFC", 0],
|
|
798
|
+
["asdfghjkl\u015Fi", 0.5],
|
|
799
|
+
["zxcvbnm\xF6\xE7", 1]
|
|
800
|
+
];
|
|
801
|
+
var KEYBOARD_COORDS = (() => {
|
|
802
|
+
const coords = {};
|
|
803
|
+
KEYBOARD_ROWS.forEach(([keys, offset], row) => {
|
|
804
|
+
[...keys].forEach((key, col) => {
|
|
805
|
+
coords[key] = [col + offset, row];
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
return coords;
|
|
809
|
+
})();
|
|
810
|
+
var DIACRITIC_SIBLINGS = {
|
|
811
|
+
\u0131: "i",
|
|
812
|
+
i: "\u0131",
|
|
813
|
+
\u00F6: "o",
|
|
814
|
+
o: "\xF6",
|
|
815
|
+
\u00FC: "u",
|
|
816
|
+
u: "\xFC",
|
|
817
|
+
\u015F: "s",
|
|
818
|
+
s: "\u015F",
|
|
819
|
+
\u00E7: "c",
|
|
820
|
+
c: "\xE7",
|
|
821
|
+
\u011F: "g",
|
|
822
|
+
g: "\u011F",
|
|
823
|
+
\u00E2: "a",
|
|
824
|
+
a: "\xE2"
|
|
825
|
+
};
|
|
826
|
+
var KEYBOARD_ROW_SUB_COST = 0.4;
|
|
827
|
+
var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
|
|
828
|
+
var DIACRITIC_SUB_COST = 0.3;
|
|
829
|
+
var TRANSPOSITION_COST = 0.8;
|
|
830
|
+
function keyboardSubCost(a, b) {
|
|
831
|
+
if (a === b)
|
|
832
|
+
return 0;
|
|
833
|
+
if (DIACRITIC_SIBLINGS[a] === b)
|
|
834
|
+
return DIACRITIC_SUB_COST;
|
|
835
|
+
const pa = KEYBOARD_COORDS[a];
|
|
836
|
+
const pb = KEYBOARD_COORDS[b];
|
|
837
|
+
if (!pa || !pb)
|
|
838
|
+
return 1;
|
|
839
|
+
const dx = Math.abs(pa[0] - pb[0]);
|
|
840
|
+
const dy = Math.abs(pa[1] - pb[1]);
|
|
841
|
+
if (dy === 0 && dx <= 1 + 1e-9)
|
|
842
|
+
return KEYBOARD_ROW_SUB_COST;
|
|
843
|
+
if (dy === 1 && dx <= 1 + 1e-9)
|
|
844
|
+
return KEYBOARD_DIAGONAL_SUB_COST;
|
|
845
|
+
return 1;
|
|
846
|
+
}
|
|
636
847
|
var TDK = class {
|
|
637
848
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
638
849
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
@@ -1196,25 +1407,45 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1196
1407
|
* Checks spelling and returns suggestions if wrong.
|
|
1197
1408
|
*/
|
|
1198
1409
|
static async checkSpelling(word) {
|
|
1410
|
+
if (!word || word.trim() === "") {
|
|
1411
|
+
return { isCorrect: false, word };
|
|
1412
|
+
}
|
|
1413
|
+
const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
|
|
1199
1414
|
const results = await this.getWord(word);
|
|
1200
1415
|
if (results.length > 0) {
|
|
1201
1416
|
return { isCorrect: true, word };
|
|
1202
1417
|
}
|
|
1418
|
+
if (COMMON_MISSPELLINGS[cleanWord]) {
|
|
1419
|
+
return { isCorrect: false, word, suggestion: COMMON_MISSPELLINGS[cleanWord] };
|
|
1420
|
+
}
|
|
1421
|
+
const seyMatch = cleanWord.match(/^(.+?)(?:şey|sey)([ıiuaeüodekmnl]+)?$/);
|
|
1422
|
+
if (seyMatch && !SEY_EXCEPTIONS.has(cleanWord)) {
|
|
1423
|
+
let prefix = seyMatch[1];
|
|
1424
|
+
const suffix = seyMatch[2] || "";
|
|
1425
|
+
if (prefix === "hicbir")
|
|
1426
|
+
prefix = "hi\xE7bir";
|
|
1427
|
+
if (prefix === "cok")
|
|
1428
|
+
prefix = "\xE7ok";
|
|
1429
|
+
return {
|
|
1430
|
+
isCorrect: false,
|
|
1431
|
+
word,
|
|
1432
|
+
suggestion: `${prefix} \u015Fey${suffix}`
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1203
1435
|
const daily = await this.getDailyContent();
|
|
1204
1436
|
if (daily) {
|
|
1205
|
-
const syydMatch = daily.syyd.find((s) => s.yanliskelime.toLocaleLowerCase("tr-TR") ===
|
|
1437
|
+
const syydMatch = daily.syyd.find((s) => s.yanliskelime.toLocaleLowerCase("tr-TR") === cleanWord);
|
|
1206
1438
|
if (syydMatch) {
|
|
1207
1439
|
return { isCorrect: false, word, suggestion: syydMatch.dogrukelime };
|
|
1208
1440
|
}
|
|
1209
|
-
const mixMatch = daily.karistirma.find((s) => s.yanlis.toLocaleLowerCase("tr-TR") ===
|
|
1441
|
+
const mixMatch = daily.karistirma.find((s) => s.yanlis.toLocaleLowerCase("tr-TR") === cleanWord);
|
|
1210
1442
|
if (mixMatch) {
|
|
1211
1443
|
return { isCorrect: false, word, suggestion: mixMatch.dogru };
|
|
1212
1444
|
}
|
|
1213
1445
|
}
|
|
1214
1446
|
const root = await this.findRoot(word);
|
|
1215
1447
|
if (root) {
|
|
1216
|
-
const
|
|
1217
|
-
const isInflected = root !== cleanWord2;
|
|
1448
|
+
const isInflected = root !== cleanWord;
|
|
1218
1449
|
return {
|
|
1219
1450
|
isCorrect: true,
|
|
1220
1451
|
word,
|
|
@@ -1225,24 +1456,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1225
1456
|
if (this.autocompleteCache.length === 0) {
|
|
1226
1457
|
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
1227
1458
|
}
|
|
1228
|
-
const
|
|
1459
|
+
for (const candidate of this.autocompleteCache) {
|
|
1460
|
+
if (candidate.includes(" ")) {
|
|
1461
|
+
const candidateNoSpace = candidate.replace(/\s+/g, "").toLocaleLowerCase("tr-TR");
|
|
1462
|
+
if (candidateNoSpace === cleanWord) {
|
|
1463
|
+
return { isCorrect: false, word, suggestion: candidate };
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1229
1467
|
let best = null;
|
|
1230
1468
|
for (const candidate of this.autocompleteCache) {
|
|
1231
1469
|
if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR"))
|
|
1232
1470
|
continue;
|
|
1233
1471
|
if (Math.abs(candidate.length - cleanWord.length) > 2)
|
|
1234
1472
|
continue;
|
|
1235
|
-
const
|
|
1236
|
-
if (
|
|
1473
|
+
const rawDist = this.damerauLevenshtein(cleanWord, candidate);
|
|
1474
|
+
if (rawDist === 0 || rawDist > 2)
|
|
1237
1475
|
continue;
|
|
1238
1476
|
const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
|
|
1239
1477
|
const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
|
|
1240
|
-
const
|
|
1478
|
+
const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
|
|
1479
|
+
const better = !best || score < best.score - 1e-9 || Math.abs(score - best.score) < 1e-9 && firstMismatch < best.firstMismatch || Math.abs(score - best.score) < 1e-9 && firstMismatch === best.firstMismatch && lengthMismatch < best.lengthMismatch;
|
|
1241
1480
|
if (better) {
|
|
1242
|
-
best = { candidate,
|
|
1481
|
+
best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
|
|
1243
1482
|
}
|
|
1244
1483
|
}
|
|
1245
|
-
if (best && best.
|
|
1484
|
+
if (best && best.rawDist <= 2 && (best.firstMismatch === 0 || best.rawDist <= 1)) {
|
|
1246
1485
|
return { isCorrect: false, word, suggestion: best.candidate };
|
|
1247
1486
|
}
|
|
1248
1487
|
return { isCorrect: false, word };
|
|
@@ -1771,6 +2010,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1771
2010
|
}
|
|
1772
2011
|
return dp[a.length][b.length];
|
|
1773
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
2015
|
+
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
2016
|
+
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
2017
|
+
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
2018
|
+
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
2019
|
+
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
2020
|
+
* integer distance still gates whether a suggestion is offered at all.
|
|
2021
|
+
*/
|
|
2022
|
+
static keyboardAwareDistance(a, b) {
|
|
2023
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
2024
|
+
for (let i = 0; i <= a.length; i++)
|
|
2025
|
+
dp[i][0] = i;
|
|
2026
|
+
for (let j = 0; j <= b.length; j++)
|
|
2027
|
+
dp[0][j] = j;
|
|
2028
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2029
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2030
|
+
const cost = keyboardSubCost(a[i - 1], b[j - 1]);
|
|
2031
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
2032
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
2033
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
return dp[a.length][b.length];
|
|
2038
|
+
}
|
|
1774
2039
|
/**
|
|
1775
2040
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
1776
2041
|
*/
|
|
@@ -1995,6 +2260,73 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1995
2260
|
"me\u011Ferki",
|
|
1996
2261
|
"illaki"
|
|
1997
2262
|
]);
|
|
2263
|
+
const PHRASE_MISTAKES = [
|
|
2264
|
+
{
|
|
2265
|
+
regex: /\bhiç\s+bir\b/gi,
|
|
2266
|
+
suggestion: "hi\xE7bir",
|
|
2267
|
+
message: "'hi\xE7bir' belgisiz s\u0131fat\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2268
|
+
type: "spelling"
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
regex: /\bbir\s+çok\b/gi,
|
|
2272
|
+
suggestion: "bir\xE7ok",
|
|
2273
|
+
message: "'bir\xE7ok' belgisiz s\u0131fat\u0131/zamiri biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2274
|
+
type: "spelling"
|
|
2275
|
+
},
|
|
2276
|
+
{
|
|
2277
|
+
regex: /\bbir\s+kaç\b/gi,
|
|
2278
|
+
suggestion: "birka\xE7",
|
|
2279
|
+
message: "'birka\xE7' belgisiz s\u0131fat\u0131/zamiri biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2280
|
+
type: "spelling"
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
regex: /\bbir\s+az\b/gi,
|
|
2284
|
+
suggestion: "biraz",
|
|
2285
|
+
message: "'biraz' s\xF6zc\xFC\u011F\xFC biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2286
|
+
type: "spelling"
|
|
2287
|
+
},
|
|
2288
|
+
{
|
|
2289
|
+
regex: /\bher\s+hangi\b/gi,
|
|
2290
|
+
suggestion: "herhangi",
|
|
2291
|
+
message: "'herhangi' s\xF6zc\xFC\u011F\xFC biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2292
|
+
type: "spelling"
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
regex: /\bgit\s+gide\b/gi,
|
|
2296
|
+
suggestion: "gitgide",
|
|
2297
|
+
message: "'gitgide' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2298
|
+
type: "spelling"
|
|
2299
|
+
},
|
|
2300
|
+
{
|
|
2301
|
+
regex: /\bbirden\s+bire\b/gi,
|
|
2302
|
+
suggestion: "birdenbire",
|
|
2303
|
+
message: "'birdenbire' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2304
|
+
type: "spelling"
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
regex: /\brast\s+gele\b/gi,
|
|
2308
|
+
suggestion: "rastgele",
|
|
2309
|
+
message: "'rastgele' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
|
|
2310
|
+
type: "spelling"
|
|
2311
|
+
}
|
|
2312
|
+
];
|
|
2313
|
+
const coveredRanges = [];
|
|
2314
|
+
for (const pm of PHRASE_MISTAKES) {
|
|
2315
|
+
let pmMatch;
|
|
2316
|
+
while ((pmMatch = pm.regex.exec(text)) !== null) {
|
|
2317
|
+
const start = pmMatch.index;
|
|
2318
|
+
const end = start + pmMatch[0].length;
|
|
2319
|
+
coveredRanges.push({ start, end });
|
|
2320
|
+
issues.push({
|
|
2321
|
+
type: pm.type,
|
|
2322
|
+
word: pmMatch[0],
|
|
2323
|
+
startIndex: start,
|
|
2324
|
+
endIndex: end,
|
|
2325
|
+
suggestion: pm.suggestion,
|
|
2326
|
+
message: pm.message
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
1998
2330
|
const tokenRegex = /[\p{L}0-9'’]+/gu;
|
|
1999
2331
|
let match;
|
|
2000
2332
|
while ((match = tokenRegex.exec(text)) !== null) {
|
|
@@ -2004,6 +2336,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
2004
2336
|
const lower = rawWord.toLocaleLowerCase("tr-TR");
|
|
2005
2337
|
if (/^\d+$/.test(lower))
|
|
2006
2338
|
continue;
|
|
2339
|
+
if (coveredRanges.some((r) => startIndex >= r.start && endIndex <= r.end))
|
|
2340
|
+
continue;
|
|
2007
2341
|
let flagged = false;
|
|
2008
2342
|
const questionMatch = lower.match(/^(.+?)(m[ıiuü](?:sin|sın|sun|sün|siniz|sınız|sunuz|sünüz|yiz|yız|yuz|yüz|m|k)?)$/);
|
|
2009
2343
|
if (questionMatch) {
|
|
@@ -2029,7 +2363,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
2029
2363
|
if (!SOMBAHCEMI.has(lower)) {
|
|
2030
2364
|
if (!await this.isHeadword(lower)) {
|
|
2031
2365
|
const root = await this.findRoot(base);
|
|
2032
|
-
const isVerb =
|
|
2366
|
+
const isVerb = (base === "demek" || base === "kald\u0131" || base === "yeter" || base === "bilmem" || VERB_CONJUGATION_REGEX.test(base)) && (root ? root.endsWith("mek") || root.endsWith("mak") : true);
|
|
2033
2367
|
if (isVerb) {
|
|
2034
2368
|
issues.push({
|
|
2035
2369
|
type: "conjunction_ki",
|
|
@@ -2049,7 +2383,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
2049
2383
|
const ending = lower.slice(-2);
|
|
2050
2384
|
if (!await this.isHeadword(lower)) {
|
|
2051
2385
|
const root = await this.findRoot(base);
|
|
2052
|
-
const isVerb =
|
|
2386
|
+
const isVerb = VERB_CONJUGATION_REGEX.test(base) && (root ? root.endsWith("mek") || root.endsWith("mak") : false);
|
|
2053
2387
|
if (isVerb) {
|
|
2054
2388
|
const correctEnding = ending.startsWith("t") ? ending === "te" ? "de" : "da" : ending;
|
|
2055
2389
|
issues.push({
|
|
@@ -2064,6 +2398,47 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
2064
2398
|
}
|
|
2065
2399
|
}
|
|
2066
2400
|
}
|
|
2401
|
+
const seyMatch = lower.match(/^(.+?)(?:şey|sey)([ıiuaeüodekmnl]+)?$/);
|
|
2402
|
+
if (!flagged && seyMatch && !SEY_EXCEPTIONS.has(lower)) {
|
|
2403
|
+
let prefix = seyMatch[1];
|
|
2404
|
+
const suffix = seyMatch[2] || "";
|
|
2405
|
+
if (prefix === "hicbir")
|
|
2406
|
+
prefix = "hi\xE7bir";
|
|
2407
|
+
if (prefix === "cok")
|
|
2408
|
+
prefix = "\xE7ok";
|
|
2409
|
+
issues.push({
|
|
2410
|
+
type: "spelling",
|
|
2411
|
+
word: rawWord,
|
|
2412
|
+
startIndex,
|
|
2413
|
+
endIndex,
|
|
2414
|
+
suggestion: `${prefix} \u015Fey${suffix}`,
|
|
2415
|
+
message: "'\u015Fey' s\xF6zc\xFC\u011F\xFC kendinden \xF6nceki kelimeden ayr\u0131 yaz\u0131lmal\u0131d\u0131r."
|
|
2416
|
+
});
|
|
2417
|
+
flagged = true;
|
|
2418
|
+
}
|
|
2419
|
+
if (!flagged && lower === "yada") {
|
|
2420
|
+
issues.push({
|
|
2421
|
+
type: "spelling",
|
|
2422
|
+
word: rawWord,
|
|
2423
|
+
startIndex,
|
|
2424
|
+
endIndex,
|
|
2425
|
+
suggestion: "ya da",
|
|
2426
|
+
message: "'ya da' ba\u011Flac\u0131 her zaman ayr\u0131 yaz\u0131l\u0131r."
|
|
2427
|
+
});
|
|
2428
|
+
flagged = true;
|
|
2429
|
+
}
|
|
2430
|
+
if (!flagged && (lower === "burda" || lower === "\u015Furda" || lower === "surda" || lower === "orda" || lower === "i\xE7erde" || lower === "icerde" || lower === "d\u0131\u015Farda" || lower === "disarda" || lower === "yukarda")) {
|
|
2431
|
+
const correct = COMMON_MISSPELLINGS[lower] || lower;
|
|
2432
|
+
issues.push({
|
|
2433
|
+
type: "spelling",
|
|
2434
|
+
word: rawWord,
|
|
2435
|
+
startIndex,
|
|
2436
|
+
endIndex,
|
|
2437
|
+
suggestion: correct,
|
|
2438
|
+
message: `'${rawWord}' s\xF6zc\xFC\u011F\xFCnde \xFCnl\xFC d\xFC\u015Fmesi yap\u0131lmaz.`
|
|
2439
|
+
});
|
|
2440
|
+
flagged = true;
|
|
2441
|
+
}
|
|
2067
2442
|
if (!flagged) {
|
|
2068
2443
|
const check = await this.checkSpelling(rawWord);
|
|
2069
2444
|
if (!check.isCorrect) {
|
|
@@ -2073,11 +2448,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
2073
2448
|
startIndex,
|
|
2074
2449
|
endIndex,
|
|
2075
2450
|
suggestion: check.suggestion,
|
|
2076
|
-
message: check.suggestion ? `'${rawWord}' yanl\u0131\u015F yaz\u0131lm\u0131\u015F olabilir
|
|
2451
|
+
message: check.suggestion ? `'${rawWord}' yanl\u0131\u015F yaz\u0131lm\u0131\u015F olabilir.` : `'${rawWord}' s\xF6zl\xFCkte bulunamad\u0131.`
|
|
2077
2452
|
});
|
|
2078
2453
|
}
|
|
2079
2454
|
}
|
|
2080
2455
|
}
|
|
2456
|
+
issues.sort((a, b) => a.startIndex - b.startIndex);
|
|
2081
2457
|
return {
|
|
2082
2458
|
text,
|
|
2083
2459
|
issues,
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -53,7 +53,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
53
53
|
let command = args[0];
|
|
54
54
|
let word = args.slice(1).join(" ");
|
|
55
55
|
|
|
56
|
-
if (command && !KNOWN_COMMANDS.has(command) && command !== "--help" && command !== "-h") {
|
|
56
|
+
if (command && !KNOWN_COMMANDS.has(command) && command !== "--help" && command !== "-h" && command !== "--version" && command !== "-v") {
|
|
57
57
|
word = args.join(" ");
|
|
58
58
|
command = "anlam";
|
|
59
59
|
}
|
|
@@ -153,6 +153,11 @@ async function run() {
|
|
|
153
153
|
process.exit(1);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
if (command === "--version" || command === "-v") {
|
|
157
|
+
console.log("tdk-api-wrapper v1.5.1");
|
|
158
|
+
process.exit(0);
|
|
159
|
+
}
|
|
160
|
+
|
|
156
161
|
if (command === "--help" || command === "-h") {
|
|
157
162
|
console.log("Kullanım: tdk [komut] <kelime> [--json]");
|
|
158
163
|
console.log(
|
package/src/morphology.ts
CHANGED
|
@@ -230,6 +230,15 @@ export function getStemCandidates(
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// Bare verb imperative candidates (e.g. "söyle" -> "söylemek", "oku" -> "okumak")
|
|
234
|
+
const bareInfinitives = restoreInfinitive(normalized);
|
|
235
|
+
for (const inf of bareInfinitives) {
|
|
236
|
+
if (!seen.has(inf) && inf !== normalized) {
|
|
237
|
+
seen.add(inf);
|
|
238
|
+
candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
233
242
|
let frontier = [normalized];
|
|
234
243
|
|
|
235
244
|
for (let depth = 0; depth < maxDepth; depth++) {
|