gs-tokenizer 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/README.cn.md +262 -0
- package/README.ja.md +262 -0
- package/README.ko.md +262 -0
- package/README.md +262 -0
- package/lib/core.cjs +1 -0
- package/lib/core.d.ts +230 -0
- package/lib/core.js +1 -0
- package/lib/index.cjs +1 -0
- package/lib/index.d.ts +116 -0
- package/lib/index.js +1 -0
- package/lib/lexicon.cjs +1 -0
- package/lib/lexicon.d.ts +221 -0
- package/lib/lexicon.js +1 -0
- package/package.json +51 -0
package/README.ko.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# gs-tokenizer
|
|
2
|
+
|
|
3
|
+
영어, 중국어, 일본어, 한국어 등 다국어를 지원하는 강력하고 가벼운 다국어 토크나이저 라이브러리입니다.
|
|
4
|
+
|
|
5
|
+
## 문서
|
|
6
|
+
|
|
7
|
+
- [English README](README.md)
|
|
8
|
+
- [中文 README](README.cn.md)
|
|
9
|
+
- [日本語 README](README.ja.md)
|
|
10
|
+
- [한국어 README](README.ko.md)
|
|
11
|
+
|
|
12
|
+
## 기능
|
|
13
|
+
|
|
14
|
+
- **언어 지원**: 영어, 중국어, 일본어, 한국어
|
|
15
|
+
- **지능형 토큰화**:
|
|
16
|
+
- 영어: 단어 경계 기반 토큰화
|
|
17
|
+
- CJK(중국어, 일본어, 한국어): 브라우저의 Intl.Segmenter를 사용한 자연스러운 단어 분할
|
|
18
|
+
- 날짜: 날짜 패턴에 대한 특별 처리
|
|
19
|
+
- **사용자 정의 사전**: 우선순위와 이름이 있는 사용자 정의 단어 추가 지원
|
|
20
|
+
- **자동 언어 감지**: 입력 텍스트의 언어를 자동으로 감지
|
|
21
|
+
- **다양한 출력 형식**: 상세한 토큰 정보 또는 단어 목록만 가져오기
|
|
22
|
+
- **가벼움**: 최소한의 종속성, 브라우저 환경에 최적화
|
|
23
|
+
- **빠른 사용 API**: 쉽게 통합할 수 있는 편리한 정적 메서드
|
|
24
|
+
|
|
25
|
+
## 설치
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add gs-tokenizer
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 대체 설치 방법
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install gs-tokenizer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 사용 방법
|
|
38
|
+
|
|
39
|
+
### 기본 사용법
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import { MultilingualTokenizer, createTokenizer } from 'gs-tokenizer';
|
|
43
|
+
|
|
44
|
+
// 토크나이저 인스턴스 생성
|
|
45
|
+
const tokenizer = new MultilingualTokenizer();
|
|
46
|
+
// 또는 팩토리 함수 사용
|
|
47
|
+
// const tokenizer = createTokenizer();
|
|
48
|
+
|
|
49
|
+
// 텍스트 토큰화
|
|
50
|
+
const text = 'Hello world! 나는 북경 천안문을 좋아합니다.';
|
|
51
|
+
const tokens = tokenizer.tokenize(text);
|
|
52
|
+
console.log(tokens);
|
|
53
|
+
|
|
54
|
+
// 단어 토큰만 가져오기
|
|
55
|
+
const words = tokenizer.tokenizeToText(text);
|
|
56
|
+
console.log(words);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 빠른 사용 (권장)
|
|
60
|
+
|
|
61
|
+
quick 모듈은 쉽게 통합할 수 있는 편리한 정적 메서드를 제공합니다:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { tokenize, tokenizeToText, addCustomDictionary } from 'gs-tokenizer';
|
|
65
|
+
|
|
66
|
+
// 인스턴스 생성 없이 직접 토큰화
|
|
67
|
+
const text = 'Hello world! 나는 북경 천안문을 좋아합니다.';
|
|
68
|
+
const tokens = tokenize(text);
|
|
69
|
+
const words = tokenizeToText(text);
|
|
70
|
+
console.log(words);
|
|
71
|
+
|
|
72
|
+
// 사용자 정의 사전 추가
|
|
73
|
+
addCustomDictionary(['인공지능', '기술'], 'zh', 10, 'tech');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 고급 사용법
|
|
77
|
+
|
|
78
|
+
#### 빠른 모듈로 사용자 정의 사전 로드
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
import { tokenize, addCustomDictionary } from 'gs-tokenizer';
|
|
82
|
+
|
|
83
|
+
// 다른 언어에 대한 여러 사용자 정의 사전 로드
|
|
84
|
+
addCustomDictionary(['인공지능', '머신러닝'], 'zh', 10, 'tech');
|
|
85
|
+
addCustomDictionary(['Web3', 'Blockchain'], 'en', 10, 'crypto');
|
|
86
|
+
addCustomDictionary(['인공지능'], 'ko', 10, 'tech-ko');
|
|
87
|
+
|
|
88
|
+
// 사용자 정의 사전을 적용하여 토큰화
|
|
89
|
+
const text = '인공지능과 Web3는 미래의 중요한 기술입니다. 인공지능도 중요합니다.';
|
|
90
|
+
const tokens = tokenize(text);
|
|
91
|
+
console.log(tokens.filter(token => token.src === 'tech'));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### 기본 사전 사용 안함
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { MultilingualTokenizer } from 'gs-tokenizer';
|
|
98
|
+
|
|
99
|
+
// 기본 사전을 사용하지 않는 토크나이저 생성
|
|
100
|
+
const tokenizer = new MultilingualTokenizer({
|
|
101
|
+
customDictionaries: {
|
|
102
|
+
'ko': [{ priority: 10, data: new Set(['사용자정의단어']), name: 'custom', lang: 'ko' }]
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 사용자 정의 사전만 사용하여 토큰화
|
|
107
|
+
const text = '이것은 사용자정의단어의 예입니다.';
|
|
108
|
+
const tokens = tokenizer.tokenize(text, 'ko');
|
|
109
|
+
console.log(tokens);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 사용자 정의 사전
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
const tokenizer = new MultilingualTokenizer();
|
|
116
|
+
|
|
117
|
+
// 언어, 우선순위, 이름을 지정하여 사용자 정의 단어 추가
|
|
118
|
+
okenizer.addCustomDictionary(['인공지능', '기술'], 'zh', 10, 'tech');
|
|
119
|
+
okenizer.addCustomDictionary(['Python', 'JavaScript'], 'en', 5, 'programming');
|
|
120
|
+
|
|
121
|
+
const text = '나는 인공지능 기술과 Python 프로그래밍을 좋아합니다';
|
|
122
|
+
const tokens = tokenizer.tokenize(text);
|
|
123
|
+
const words = tokenizer.tokenizeToText(text);
|
|
124
|
+
console.log(words); // '인공지능', 'Python'이 포함되어야 함
|
|
125
|
+
|
|
126
|
+
// 사용자 정의 단어 삭제
|
|
127
|
+
okenizer.removeCustomWord('Python', 'en', 'programming');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 고급 옵션
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const tokenizer = createTokenizer({
|
|
134
|
+
defaultLanguage: 'ko',
|
|
135
|
+
customDictionaries: {
|
|
136
|
+
'ko': [{
|
|
137
|
+
priority: 10,
|
|
138
|
+
data: new Set(['사용자정의단어']),
|
|
139
|
+
name: 'custom',
|
|
140
|
+
lang: 'ko'
|
|
141
|
+
}]
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// 지정된 언어로 토큰화
|
|
146
|
+
const text = '나는 북경 천안문을 좋아합니다';
|
|
147
|
+
const tokens = tokenizer.tokenize(text, 'zh');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API 참조
|
|
151
|
+
|
|
152
|
+
### `MultilingualTokenizer`
|
|
153
|
+
|
|
154
|
+
다국어 텍스트 처리를 담당하는 주요 토크나이저 클래스입니다.
|
|
155
|
+
|
|
156
|
+
#### 생성자
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { MultilingualTokenizer, TokenizerOptions } from 'gs-tokenizer';
|
|
160
|
+
|
|
161
|
+
new MultilingualTokenizer(options?: TokenizerOptions)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**옵션**:
|
|
165
|
+
- `customDictionaries`: Record<string, LexiconEntry[]> - 각 언어에 대한 사용자 정의 사전
|
|
166
|
+
- `defaultLanguage`: string - 기본 언어 코드 (기본값: 'en')
|
|
167
|
+
|
|
168
|
+
#### 메서드
|
|
169
|
+
|
|
170
|
+
| 메서드 | 설명 |
|
|
171
|
+
|------|------|
|
|
172
|
+
| `tokenize(text: string, language?: string): Token[]` | 입력 텍스트를 토큰화하고 상세한 토큰 정보를 반환합니다 |
|
|
173
|
+
| `tokenizeToText(text: string, language?: string): string[]` | 입력 텍스트를 토큰화하고 단어 목록만 반환합니다 |
|
|
174
|
+
| `addCustomDictionary(words: string[], language: string, priority: number, name: string): void` | 토크나이저에 사용자 정의 단어를 추가합니다 |
|
|
175
|
+
| `removeCustomWord(word: string, language?: string, lexiconName?: string): void` | 토크나이저에서 사용자 정의 단어를 제거합니다 |
|
|
176
|
+
|
|
177
|
+
### `createTokenizer(options?: TokenizerOptions): MultilingualTokenizer`
|
|
178
|
+
|
|
179
|
+
선택적 구성으로 새로운 MultilingualTokenizer 인스턴스를 생성하는 팩토리 함수입니다.
|
|
180
|
+
|
|
181
|
+
### 빠른 사용 API
|
|
182
|
+
|
|
183
|
+
quick 모듈은 편리한 정적 메서드를 제공합니다:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { Token } from 'gs-tokenizer';
|
|
187
|
+
|
|
188
|
+
// 텍스트 토큰화
|
|
189
|
+
function tokenize(text: string, language?: string): Token[];
|
|
190
|
+
|
|
191
|
+
// 텍스트만 토큰화
|
|
192
|
+
function tokenizeToText(text: string, language?: string): string[];
|
|
193
|
+
|
|
194
|
+
// 사용자 정의 사전 추가
|
|
195
|
+
function addCustomDictionary(words: string[], language: string, priority: number, name: string): void;
|
|
196
|
+
|
|
197
|
+
// 사용자 정의 단어 제거
|
|
198
|
+
function removeCustomWord(word: string, language?: string, lexiconName?: string): void;
|
|
199
|
+
|
|
200
|
+
// 사전 로딩의 기본 언어 설정
|
|
201
|
+
function setDefaultLanguages(languages: string[]): void;
|
|
202
|
+
|
|
203
|
+
// 사전 로딩의 기본 타입 설정
|
|
204
|
+
function setDefaultTypes(types: string[]): void;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 타입
|
|
208
|
+
|
|
209
|
+
#### `Token` 인터페이스
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
interface Token {
|
|
213
|
+
txt: string; // 토큰 텍스트 내용
|
|
214
|
+
type: 'word' | 'punctuation' | 'space' | 'other' | 'emoji' | 'date';
|
|
215
|
+
lang?: string; // 언어 코드
|
|
216
|
+
src?: string; // 소스 (예: 사용자 정의 사전 이름)
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### `TokenizerOptions` 인터페이스
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { LexiconEntry } from 'gs-tokenizer';
|
|
224
|
+
|
|
225
|
+
interface TokenizerOptions {
|
|
226
|
+
customDictionaries?: Record<string, LexiconEntry[]>;
|
|
227
|
+
granularity?: 'word' | 'grapheme' | 'sentence';
|
|
228
|
+
defaultLanguage?: string;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 브라우저 호환성
|
|
233
|
+
|
|
234
|
+
- Chrome/Edge: 87+
|
|
235
|
+
- Firefox: 86+
|
|
236
|
+
- Safari: 14.1+
|
|
237
|
+
|
|
238
|
+
참고: CJK 언어에 `Intl.Segmenter`를 사용하므로 최신 브라우저 지원이 필요합니다.
|
|
239
|
+
|
|
240
|
+
## 개발
|
|
241
|
+
|
|
242
|
+
### 빌드
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm run build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 테스트 실행
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npm run test # 모든 테스트 실행
|
|
252
|
+
npm run test:base # 기본 테스트 실행
|
|
253
|
+
npm run test:english # 영어 특정 테스트 실행
|
|
254
|
+
npm run test:cjk # CJK 특정 테스트 실행
|
|
255
|
+
npm run test:mixed # 혼합 언어 테스트 실행
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## 라이선스
|
|
259
|
+
|
|
260
|
+
MIT
|
|
261
|
+
|
|
262
|
+
[GitHub Repository](https://github.com/grain-sand/gs-tokenizer)
|
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# gs-tokenizer
|
|
2
|
+
|
|
3
|
+
A powerful and lightweight multilingual tokenizer library that provides natural language processing capabilities for multiple languages including English, Chinese, Japanese, and Korean.
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
- [English README](README.md)
|
|
8
|
+
- [中文 README](README.cn.md)
|
|
9
|
+
- [日本語 README](README.ja.md)
|
|
10
|
+
- [한국어 README](README.ko.md)
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Language Support**: English, Chinese, Japanese, Korean
|
|
15
|
+
- **Intelligent Tokenization**:
|
|
16
|
+
- English: Word boundary-based tokenization
|
|
17
|
+
- CJK (Chinese, Japanese, Korean): Natural word segmentation using browser's Intl.Segmenter
|
|
18
|
+
- Date: Special handling for date patterns
|
|
19
|
+
- **Custom Dictionary**: Support for adding custom words with priority and name
|
|
20
|
+
- **Auto Language Detection**: Automatically detects the language of input text
|
|
21
|
+
- **Multiple Output Formats**: Get detailed token information or just word lists
|
|
22
|
+
- **Lightweight**: Minimal dependencies, designed for browser environments
|
|
23
|
+
- **Quick Use API**: Convenient static methods for easy integration
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add gs-tokenizer
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Alternative Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install gs-tokenizer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Usage
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import { MultilingualTokenizer, createTokenizer } from 'gs-tokenizer';
|
|
43
|
+
|
|
44
|
+
// Create tokenizer instance
|
|
45
|
+
const tokenizer = new MultilingualTokenizer();
|
|
46
|
+
// or using factory function
|
|
47
|
+
// const tokenizer = createTokenizer();
|
|
48
|
+
|
|
49
|
+
// Tokenize text
|
|
50
|
+
const text = 'Hello world! 我爱北京天安门。';
|
|
51
|
+
const tokens = tokenizer.tokenize(text);
|
|
52
|
+
console.log(tokens);
|
|
53
|
+
|
|
54
|
+
// Get only word tokens
|
|
55
|
+
const words = tokenizer.tokenizeToText(text);
|
|
56
|
+
console.log(words);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Quick Use (Recommended)
|
|
60
|
+
|
|
61
|
+
The quick module provides convenient static methods for easy integration:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { tokenize, tokenizeToText, addCustomDictionary } from 'gs-tokenizer';
|
|
65
|
+
|
|
66
|
+
// Direct tokenization without creating an instance
|
|
67
|
+
const text = 'Hello world! 我爱北京天安门。';
|
|
68
|
+
const tokens = tokenize(text);
|
|
69
|
+
const words = tokenizeToText(text);
|
|
70
|
+
console.log(words);
|
|
71
|
+
|
|
72
|
+
// Add custom dictionary
|
|
73
|
+
addCustomDictionary(['人工智能', '技术'], 'zh', 10, 'tech');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Advanced Usage
|
|
77
|
+
|
|
78
|
+
#### Load Custom Dictionary with Quick Module
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
import { tokenize, addCustomDictionary } from 'gs-multilingual-tokenizer';
|
|
82
|
+
|
|
83
|
+
// Load multiple custom dictionaries for different languages
|
|
84
|
+
addCustomDictionary(['人工智能', '机器学习'], 'zh', 10, 'tech');
|
|
85
|
+
addCustomDictionary(['Web3', 'Blockchain'], 'en', 10, 'crypto');
|
|
86
|
+
addCustomDictionary(['アーティフィシャル・インテリジェンス'], 'ja', 10, 'tech-ja');
|
|
87
|
+
|
|
88
|
+
// Tokenize with custom dictionaries applied
|
|
89
|
+
const text = '人工智能和Web3是未来的重要技术。アーティフィシャル・インテリジェンスも重要です。';
|
|
90
|
+
const tokens = tokenize(text);
|
|
91
|
+
console.log(tokens.filter(token => token.src === 'tech'));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Without Built-in Lexicon
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { MultilingualTokenizer } from 'gs-tokenizer';
|
|
98
|
+
|
|
99
|
+
// Create tokenizer without using built-in lexicon
|
|
100
|
+
const tokenizer = new MultilingualTokenizer({
|
|
101
|
+
customDictionaries: {
|
|
102
|
+
'zh': [{ priority: 10, data: new Set(['自定义词']), name: 'custom', lang: 'zh' }]
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Tokenize using only custom dictionary
|
|
107
|
+
const text = '这是一个自定义词的示例。';
|
|
108
|
+
const tokens = tokenizer.tokenize(text, 'zh');
|
|
109
|
+
console.log(tokens);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Custom Dictionary
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
const tokenizer = new MultilingualTokenizer();
|
|
116
|
+
|
|
117
|
+
// Add custom words with language, priority, and name
|
|
118
|
+
tokenizer.addCustomDictionary(['人工智能', '技术'], 'zh', 10, 'tech');
|
|
119
|
+
tokenizer.addCustomDictionary(['Python', 'JavaScript'], 'en', 5, 'programming');
|
|
120
|
+
|
|
121
|
+
const text = '我爱人工智能技术和Python编程';
|
|
122
|
+
const tokens = tokenizer.tokenize(text);
|
|
123
|
+
const words = tokenizer.tokenizeToText(text);
|
|
124
|
+
console.log(words); // Should include '人工智能', 'Python'
|
|
125
|
+
|
|
126
|
+
// Remove custom word
|
|
127
|
+
tokenizer.removeCustomWord('Python', 'en', 'programming');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Advanced Options
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const tokenizer = createTokenizer({
|
|
134
|
+
defaultLanguage: 'en',
|
|
135
|
+
customDictionaries: {
|
|
136
|
+
'zh': [{
|
|
137
|
+
priority: 10,
|
|
138
|
+
data: new Set(['自定义词']),
|
|
139
|
+
name: 'custom',
|
|
140
|
+
lang: 'zh'
|
|
141
|
+
}]
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Tokenize with specified language
|
|
146
|
+
const text = '我爱北京天安门';
|
|
147
|
+
const tokens = tokenizer.tokenize(text, 'zh');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Reference
|
|
151
|
+
|
|
152
|
+
### `MultilingualTokenizer`
|
|
153
|
+
|
|
154
|
+
Main tokenizer class that handles multilingual text processing.
|
|
155
|
+
|
|
156
|
+
#### Constructor
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { MultilingualTokenizer, TokenizerOptions } from 'gs-tokenizer';
|
|
160
|
+
|
|
161
|
+
new MultilingualTokenizer(options?: TokenizerOptions)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Options**:
|
|
165
|
+
- `customDictionaries`: Record<string, LexiconEntry[]> - Custom dictionaries for each language
|
|
166
|
+
- `defaultLanguage`: string - Default language code (default: 'en')
|
|
167
|
+
|
|
168
|
+
#### Methods
|
|
169
|
+
|
|
170
|
+
| Method | Description |
|
|
171
|
+
|--------|-------------|
|
|
172
|
+
| `tokenize(text: string, language?: string): Token[]` | Tokenizes the input text and returns detailed token information |
|
|
173
|
+
| `tokenizeToText(text: string, language?: string): string[]` | Tokenizes the input text and returns only word tokens |
|
|
174
|
+
| `addCustomDictionary(words: string[], language: string, priority: number, name: string): void` | Adds custom words to the tokenizer |
|
|
175
|
+
| `removeCustomWord(word: string, language?: string, lexiconName?: string): void` | Removes a custom word from the tokenizer |
|
|
176
|
+
|
|
177
|
+
### `createTokenizer(options?: TokenizerOptions): MultilingualTokenizer`
|
|
178
|
+
|
|
179
|
+
Factory function to create a new MultilingualTokenizer instance with optional configuration.
|
|
180
|
+
|
|
181
|
+
### Quick Use API
|
|
182
|
+
|
|
183
|
+
The quick module provides convenient static methods:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { Token } from 'gs-tokenizer';
|
|
187
|
+
|
|
188
|
+
// Tokenize text
|
|
189
|
+
function tokenize(text: string, language?: string): Token[];
|
|
190
|
+
|
|
191
|
+
// Tokenize to text only
|
|
192
|
+
function tokenizeToText(text: string, language?: string): string[];
|
|
193
|
+
|
|
194
|
+
// Add custom dictionary
|
|
195
|
+
function addCustomDictionary(words: string[], language: string, priority: number, name: string): void;
|
|
196
|
+
|
|
197
|
+
// Remove custom word
|
|
198
|
+
function removeCustomWord(word: string, language?: string, lexiconName?: string): void;
|
|
199
|
+
|
|
200
|
+
// Set default languages for lexicon loading
|
|
201
|
+
function setDefaultLanguages(languages: string[]): void;
|
|
202
|
+
|
|
203
|
+
// Set default types for lexicon loading
|
|
204
|
+
function setDefaultTypes(types: string[]): void;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Types
|
|
208
|
+
|
|
209
|
+
#### `Token` Interface
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
interface Token {
|
|
213
|
+
txt: string; // Token text content
|
|
214
|
+
type: 'word' | 'punctuation' | 'space' | 'other' | 'emoji' | 'date';
|
|
215
|
+
lang?: string; // Language code
|
|
216
|
+
src?: string; // Source (e.g., custom dictionary name)
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### `TokenizerOptions` Interface
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { LexiconEntry } from 'gs-tokenizer';
|
|
224
|
+
|
|
225
|
+
interface TokenizerOptions {
|
|
226
|
+
customDictionaries?: Record<string, LexiconEntry[]>;
|
|
227
|
+
granularity?: 'word' | 'grapheme' | 'sentence';
|
|
228
|
+
defaultLanguage?: string;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Browser Compatibility
|
|
233
|
+
|
|
234
|
+
- Chrome/Edge: 87+
|
|
235
|
+
- Firefox: 86+
|
|
236
|
+
- Safari: 14.1+
|
|
237
|
+
|
|
238
|
+
Note: Uses `Intl.Segmenter` for CJK languages, which requires modern browser support.
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
### Build
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm run build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Run Tests
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npm run test # Run all tests
|
|
252
|
+
npm run test:base # Run base tests
|
|
253
|
+
npm run test:english # Run English-specific tests
|
|
254
|
+
npm run test:cjk # Run CJK-specific tests
|
|
255
|
+
npm run test:mixed # Run mixed language tests
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
261
|
+
|
|
262
|
+
[GitHub Repository](https://github.com/grain-sand/gs-tokenizer)
|
package/lib/core.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t{customDictionaries;constructor(t={}){this.customDictionaries=t}detectLanguage(t){return/[a-zA-Z]/.test(t)&&!/[一-鿿]/.test(t)?"en":""}tokenize(t,e){const s=[],n=t.split(/\b/);for(const t of n)t&&(t.match(/^\s+$/)?s.push({txt:t,type:"space",lang:e,src:""}):/^\p{Emoji}+$/u.test(t)&&!/[0-9]/.test(t)?s.push({txt:t,type:"emoji",lang:e,src:""}):t.match(/^[^a-zA-Z0-9]+$/)?s.push({txt:t,type:"punctuation",lang:e,src:""}):t.match(/^[a-zA-Z0-9]+$/)?s.push({txt:t,type:"word",lang:e,src:""}):s.push({txt:t,type:"other",lang:e,src:""}));return this.tagNameTokens(s,e)}tagNameTokens(t,e){const s=[];let n=0;for(;n<t.length;){if(n<t.length&&"word"===t[n].type){const i=t[n].txt;if(this.customDictionaries[e]){let t=!1;for(const r of this.customDictionaries[e].sort((t,e)=>e.priority-t.priority))if(r.data.has(i)){s.push({txt:i,type:"word",lang:e,src:r.name}),n++,t=!0;break}if(t)continue}}s.push({txt:t[n].txt,type:t[n].type,lang:e,src:t[n].src||""}),n++}return s}}class e{segmenters;customDictionaries;constructor(t={}){this.segmenters=new Map,this.customDictionaries=t}detectLanguage(t){return/[\u4e00-\u9fff]/.test(t)?"zh":/[\u3040-\u309f\u30a0-\u30ff]/.test(t)?"ja":/[\uac00-\ud7af]/.test(t)?"ko":""}tokenize(t,e){const s=[],n=this.getSegmenter(e);for(const i of n.segment(t)){const{segment:t,isWordLike:n}=i;t.match(/^\s+$/)?s.push({txt:t,type:"space",lang:e,src:""}):/^\p{Emoji}+$/u.test(t)&&!/[0-9]/.test(t)?s.push({txt:t,type:"emoji",lang:e,src:""}):t.match(/^[^\p{L}\p{N}]+$/u)?s.push({txt:t,type:"punctuation",lang:e,src:""}):n?s.push({txt:t,type:"word",lang:e,src:""}):s.push({txt:t,type:"other",lang:e,src:""})}return this.applyCustomDictionary(s,e)}getSegmenter(t,e="word"){const s=`${t}-${e}`;return this.segmenters.has(s)||this.segmenters.set(s,new Intl.Segmenter(t,{granularity:e})),this.segmenters.get(s)}applyCustomDictionary(t,e){const s=this.customDictionaries[e]||[];let n=t;if(s.length>0){const t=[];let i=0;for(;i<n.length;){let r=!1;for(let o=Math.min(5,n.length-i);o>=1;o--){if(o>1&&n.slice(i,i+o).some(t=>"word"!==t.type))continue;const a=n.slice(i,i+o).map(t=>t.txt).join("");for(const n of s.sort((t,e)=>e.priority-t.priority))if(n.data.has(a)){t.push({txt:a,type:"word",lang:e,src:""}),i+=o,r=!0;break}if(r)break}r||(t.push({...n[i],src:""}),i++)}n=t}return n}}class s{comprehensiveDatePattern=/\d{8}|\d{4}[-/.]\d{2}[-/.]\d{2}|\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}|(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+\d{1,2}(?:,\s+\d{4})?|\d{4}年\d{1,2}月(?:\d{1,2}日)?|\d{1,2}月\d{1,2}日(?:\d{4}年)?/gi;detectLanguage(t){return""}tokenize(t,e="zh"){const s=[];let n=0;const i=[];for(const e of t.matchAll(this.comprehensiveDatePattern))void 0!==e.index&&e[0]&&i.push({text:e[0],index:e.index});i.sort((t,e)=>t.index-e.index);const r=[];let o=null;for(const e of i){const s=e.index+e.text.length;o?e.index<=o.end?o={text:t.slice(o.index,Math.max(o.end,s)),index:o.index,end:Math.max(o.end,s)}:(r.push({text:o.text,index:o.index}),o={...e,end:s}):o={...e,end:s}}o&&r.push({text:o.text,index:o.index});for(const i of r){if(i.index>n){const r=t.slice(n,i.index);s.push({txt:r,type:"other",lang:e,src:""})}this.isValidDate(i.text)?s.push({txt:i.text,type:"date",lang:e,src:""}):s.push({txt:i.text,type:"other",lang:e,src:""}),n=i.index+i.text.length}if(n<t.length){const i=t.slice(n);s.push({txt:i,type:"other",lang:e,src:""})}return s}isValidDate(t){let e,s,n;if(/^\d{8}$/.test(t))e=parseInt(t.slice(0,4)),s=parseInt(t.slice(4,6)),n=parseInt(t.slice(6,8));else if(/^\d{4}[-.]\d{2}[-.]\d{2}$/.test(t)){const i=t.split(/[-.]/).map(Number);e=i[0],s=i[1],n=i[2]}else if(/^\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}$/.test(t)||/^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(t)){const i=t.split(/[-/.]/).map(Number);i[0]>=1e3?(e=i[0],s=i[1],n=i[2]):i[2]>31?(s=i[0],n=i[1],e=i[2]):(s=i[0],n=i[1],e=i[2]<50?2e3+i[2]:1900+i[2])}else if(/^(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+(\d{1,2})(?:,\s+(\d{4}))?$/i.test(t)){const i=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],r=t.match(/^(\w+)\s+(\d{1,2})(?:,\s+(\d{4}))?$/i);if(!r)return!1;s=i.findIndex(t=>t.toLowerCase()===r[1].substring(0,3).toLowerCase())+1,n=parseInt(r[2]),e=r[3]?parseInt(r[3]):(new Date).getFullYear()}else{if(!/^(\d{4})?年?(\d{1,2})月(\d{1,2})日?(\d{4})?$/.test(t))return!1;{const i=t.match(/^(\d{4})?年?(\d{1,2})月(\d{1,2})日?(\d{4})?$/);if(!i)return!1;e=parseInt(i[1]||i[4]||(new Date).getFullYear().toString()),s=parseInt(i[2]),n=parseInt(i[3])}}return this.isValidDateComponents(e,s,n)}isValidDateComponents(t,e,s){if(e<1||e>12||s<1||s>31)return!1;if(2===e){if(s>(t%4==0&&t%100!=0||t%400==0?29:28))return!1}else if(s>[31,28,31,30,31,30,31,31,30,31,30,31][e-1])return!1;return!0}}class n{static detectLanguage(t){return/[\u3040-\u309f\u30a0-\u30ff]/.test(t)?"ja":/[\uac00-\ud7af]/.test(t)?"ko":/[\u4e00-\u9fff]/.test(t)?"zh":"en"}}class i{detectLanguage(t){return"en"}tokenize(t,e){const s=[];let n=0;const i=/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g,r=/(?:https?:\/\/)?(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?|(?:localhost|127\.0\.0\.1)|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?::\d{1,5})?(?:\/[^\s]*)?/g,o=[];let a;for(;null!==(a=i.exec(t));)o.push({index:a.index,endIndex:a.index+a[0].length,type:"ip",text:a[0]});for(;null!==(a=r.exec(t));)o.push({index:a.index,endIndex:a.index+a[0].length,type:"url",text:a[0]});o.sort((t,e)=>t.index-e.index);const c=[];for(let t=0;t<o.length;t++){let e=!1;for(let s=0;s<o.length;s++)if(t!==s){const n=o[t].index===o[s].index&&o[t].endIndex===o[s].endIndex,i=o[t].index>=o[s].index&&o[t].endIndex<=o[s].endIndex;if(n){if("ip"===o[s].type){e=!0;break}}else if(i){e=!0;break}}e||c.push(o[t])}for(const i of c)i.index>n&&s.push({txt:t.substring(n,i.index),type:"other",lang:e}),s.push({txt:i.text,type:i.type,lang:"en"}),n=i.endIndex;return n<t.length&&s.push({txt:t.substring(n),type:"other",lang:e}),s}}class r{tokenizers;customDictionaries;defaultLanguage;constructor(n={}){this.customDictionaries=n.customDictionaries||{},this.defaultLanguage=n.defaultLanguage||"en",this.tokenizers=[new s,new i,new t,new e(this.customDictionaries)]}addCustomDictionary(t,e,s,n){const i=e||this.defaultLanguage;this.customDictionaries[i]||(this.customDictionaries[i]=[]);const r=this.customDictionaries[i].findIndex(t=>t.name===n&&t.lang===i&&t.priority===s);if(r>=0){const e=this.customDictionaries[i][r];t.forEach(t=>e.data.add(t))}else this.customDictionaries[i].push({priority:s,data:new Set(t),name:n,lang:i})}removeCustomWord(t,e,s){if(e){if(this.customDictionaries[e])if(s){const n=this.customDictionaries[e].find(t=>t.name===s);n&&n.data.delete(t)}else this.customDictionaries[e].forEach(e=>{e.data.delete(t)})}else Object.values(this.customDictionaries).forEach(e=>{e.forEach(e=>{e.data.has(t)&&e.data.delete(t)})})}tokenize(r,o){const a=o||n.detectLanguage(r),c=this.tokenizers.find(t=>t instanceof s);if(!c)return[];const u=c.tokenize(r,a),d=[],l=this.tokenizers.find(t=>t instanceof i);if(!l)return d;for(const s of u)if("date"===s.type)d.push(s);else{const n=l.tokenize(s.txt,a);for(const s of n)if("url"===s.type||"ip"===s.type)d.push(s);else{let n=[];if("en"===a){const e=this.tokenizers.find(e=>e instanceof t);e&&(n=e.tokenize(s.txt,a))}else if(["zh","ja","ko"].includes(a)){const t=this.tokenizers.find(t=>t instanceof e);t&&(n=t.tokenize(s.txt,a))}else{const t=this.tokenizers.find(t=>t instanceof e);t&&(n=t.tokenize(s.txt,a))}n.length>0?d.push(...n):d.push(s)}}return d}tokenizeText(t,e){const s=this.tokenize(t,e?.language),n=["punctuation","space","other",...e?.excludeTypes||[]];return s.filter(t=>!(e?.includeTypes&&e.includeTypes.length>0&&!e.includeTypes.includes(t.type))&&!n.includes(t.type)).map(t=>t.txt)}}exports.CJKTokenizer=e,exports.DateTokenizer=s,exports.EnglishTokenizer=t,exports.LanguageDetector=n,exports.MultilingualTokenizer=r,exports.createTokenizer=function(t){return new r(t)};
|