Skip to content

Commit 71b4b57

Browse files
author
p
committed
myanmar_word_segmenter
0 parents  commit 71b4b57

7 files changed

Lines changed: 640 additions & 0 deletions

File tree

.gitignore

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
myanmar-words/
2+
MYWORDS.json
3+
4+
# Logs
5+
logs
6+
*.log
7+
npm-debug.log*
8+
yarn-debug.log*
9+
yarn-error.log*
10+
lerna-debug.log*
11+
.pnpm-debug.log*
12+
13+
# Diagnostic reports (https://nodejs.org/api/report.html)
14+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15+
16+
# Runtime data
17+
pids
18+
*.pid
19+
*.seed
20+
*.pid.lock
21+
22+
# Directory for instrumented libs generated by jscoverage/JSCover
23+
lib-cov
24+
25+
# Coverage directory used by tools like istanbul
26+
coverage
27+
*.lcov
28+
29+
# nyc test coverage
30+
.nyc_output
31+
32+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33+
.grunt
34+
35+
# Bower dependency directory (https://bower.io/)
36+
bower_components
37+
38+
# node-waf configuration
39+
.lock-wscript
40+
41+
# Compiled binary addons (https://nodejs.org/api/addons.html)
42+
build/Release
43+
44+
# Dependency directories
45+
node_modules/
46+
jspm_packages/
47+
48+
# Snowpack dependency directory (https://snowpack.dev/)
49+
web_modules/
50+
51+
# TypeScript cache
52+
*.tsbuildinfo
53+
54+
# Optional npm cache directory
55+
.npm
56+
57+
# Optional eslint cache
58+
.eslintcache
59+
60+
# Optional stylelint cache
61+
.stylelintcache
62+
63+
# Microbundle cache
64+
.rpt2_cache/
65+
.rts2_cache_cjs/
66+
.rts2_cache_es/
67+
.rts2_cache_umd/
68+
69+
# Optional REPL history
70+
.node_repl_history
71+
72+
# Output of 'npm pack'
73+
*.tgz
74+
75+
# Yarn Integrity file
76+
.yarn-integrity
77+
78+
# dotenv environment variable files
79+
.env
80+
.env.development.local
81+
.env.test.local
82+
.env.production.local
83+
.env.local
84+
85+
# parcel-bundler cache (https://parceljs.org/)
86+
.cache
87+
.parcel-cache
88+
89+
# Next.js build output
90+
.next
91+
out
92+
93+
# Nuxt.js build / generate output
94+
.nuxt
95+
dist
96+
97+
# Gatsby files
98+
.cache/
99+
# Comment in the public line in if your project uses Gatsby and not Next.js
100+
# https://nextjs.org/blog/next-9-1#public-directory-support
101+
# public
102+
103+
# vuepress build output
104+
.vuepress/dist
105+
106+
# vuepress v2.x temp and cache directory
107+
.temp
108+
.cache
109+
110+
# Docusaurus cache and generated files
111+
.docusaurus
112+
113+
# Serverless directories
114+
.serverless/
115+
116+
# FuseBox cache
117+
.fusebox/
118+
119+
# DynamoDB Local files
120+
.dynamodb/
121+
122+
# TernJS port file
123+
.tern-port
124+
125+
# Stores VSCode versions used for testing VSCode extensions
126+
.vscode-test
127+
128+
# yarn v2
129+
.yarn/cache
130+
.yarn/unplugged
131+
.yarn/build-state.yml
132+
.yarn/install-state.gz
133+
.pnp.*

MyWordSegmenter.js

Lines changed: 120 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# myanmar_word_segmenter
2+
3+
In Myanmar sentences, words are often written without spaces between them. This can make it inconvenient for beginners to find them in dictionaries.
4+
5+
This script utilizes a simplistic approach by using a known Myanmar word list to split long words or phrases into smaller individual words, making it easier to locate them in the dictionary.
6+
7+
## Usage
8+
9+
The [MyWordSegmenter.js](./MyWordSegmenter.js) is **standalone** code file that can be used directly in a browser or as a NodeJs module. Check [example.js](./example.js) file.
10+
11+
```javascript
12+
13+
const MyWordSegmenter = require("./MyWordSegmenter");
14+
const mSegmenter = new MyWordSegmenter();
15+
16+
const imagineTestWord =
17+
"123ညီအစ်ကိုမသိတသိအချိန်ညီအစ်ကိုမသိတသိအချိန်Singaporeမသေကောင်းမပျောက်ကောင်းမှုန်မှုန်မွှားမွှား";
18+
19+
console.log("\nTest input", imagineTestWord);
20+
console.log("Test output", mSegmenter.word_segment(imagineTestWord));
21+
22+
Expected output:
23+
[
24+
'123',
25+
'ညီအစ်ကိုမသိတသိအချိန်',
26+
'ညီအစ်ကိုမသိတသိအချိန်',
27+
'Singapore',
28+
'မသေကောင်းမပျောက်ကောင်း',
29+
'မှုန်မှုန်မွှားမွှား'
30+
]
31+
32+
```
33+
34+
## Attributions
35+
36+
- Myanmar word list is from [myanmartools/myanmar-words](https://github.com/myanmartools/myanmar-words.git) - The MIT License Copyright (c) Myanmar Linguistics team.
37+
38+
- syllable_segment method is adapted from [swanhtet1992/ReSegment](https://github.com/swanhtet1992/ReSegment/blob/master/Javascript/resegment.js)
39+
40+
- Many code/text snippets are generated via [ChatGPT](https://chat.openai.com/chat)
41+
42+
---
43+
44+
## Developer notes
45+
46+
### 1. Download or update the myanmar-word list
47+
48+
Clone or download the [myanmar-words](https://github.com/myanmartools/myanmar-words) repository to the same directory with `prepareWordList.js` file. The relative path to `prepareWordList.js` should be `myanmar-words/json-files`.
49+
50+
```bash
51+
git clone --depth=1 https://github.com/myanmartools/myanmar-words.git
52+
53+
# then run
54+
node prepareWordList.js
55+
```
56+
57+
It will use `dev_standalone_template` to generate `MYWORDS.json` and the standalone code file `MyWordSegmenter.js`.
58+
59+
## 2. Miscellaneous
60+
61+
- **Combine with dictionaries**
62+
63+
It is feasible to integrate Myanmar dictionaries within MyWordSegmenter.js to develop a basic Myanmar Popup dictionary browser extension for Myanmar learners.
64+
65+
- **Work with other languages**
66+
67+
The `theSegmenter` method in `MyWordSegmenter.js` can be easily adapted to work with many other languages with minor modifications.
68+
69+
Try yourself with the Pāḷi language.

dev_NodeJs_MyWordSegmenter.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Simple Word Segmenter
3+
* https://github.com/vpnry/myanmar_word_segmenter
4+
* File: dev_NodeJs_MyWordSegmenter.js
5+
*/
6+
7+
const fs = require("fs");
8+
const path = require("path");
9+
const myWordListJsonFile = "MYWORDS.json";
10+
11+
class MyWordSegmenter {
12+
/**
13+
* The MY_SYLLABLE_REGEX and method syllable_segment are adapted from
14+
* https://github.com/swanhtet1992/ReSegment/blob/master/Javascript/resegment.js
15+
*/
16+
17+
constructor(wordListJsonFile = myWordListJsonFile) {
18+
this.wordListJsonFile = wordListJsonFile;
19+
this.puncSpecialChars = "…‘’!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + "။၊";
20+
this.puncRegex = new RegExp(
21+
`^[${this.puncSpecialChars}]+|[${this.puncSpecialChars}]+$`,
22+
"g"
23+
);
24+
25+
// The regular expression for syllable segmentation
26+
this.MY_SYLLABLE_REGEX =
27+
/(?:(?<!\u1039)([\u1000-\u102A\u103F\u104A-\u104F]|[\u1040-\u1049]+|[^\u1000-\u104F]+)(?![\u103E\u103B]?[\u1039\u103A\u1037]))/g;
28+
29+
// Load all the words from the file into a set
30+
this.MYWORDS = this.loadWords();
31+
}
32+
33+
// Method to load all the words from the file
34+
loadWords() {
35+
const mergedFilePath = path.join(__dirname, this.wordListJsonFile);
36+
const mergedData = JSON.parse(fs.readFileSync(mergedFilePath, "utf8"));
37+
const MYWORDS = new Set(mergedData);
38+
console.log(`Words in ${this.wordListJsonFile}: ${MYWORDS.size}`);
39+
return MYWORDS;
40+
}
41+
42+
syllable_segment(text) {
43+
/**
44+
* @author Chan Mrate Ko Ko
45+
*/
46+
var outArray = text.replace(this.MY_SYLLABLE_REGEX, "𝕊$1").split("𝕊");
47+
if (outArray.length > 0) {
48+
outArray.shift();
49+
}
50+
return outArray;
51+
}
52+
53+
word_segment(word) {
54+
if (this.MYWORDS.has(word)) {
55+
return [word];
56+
}
57+
return this.theSegmenter(word);
58+
}
59+
60+
word_segment_sentence(sentence) {
61+
let words = sentence.split(/\s+/);
62+
let segmentedWords = [];
63+
64+
for (let word of words) {
65+
segmentedWords.push(...this.word_segment(word));
66+
}
67+
68+
return segmentedWords;
69+
}
70+
71+
theSegmenter(text) {
72+
text = text.trim();
73+
let syllables = this.syllable_segment(text);
74+
75+
// filter punctuation, whitespace
76+
syllables = syllables
77+
.filter(
78+
(element) => element.replace(this.puncRegex, "").trim().length > 0
79+
)
80+
.map((element) => element.trim());
81+
82+
// Split the word into its constituent syllables and look them up in the loaded words set
83+
let wordList = [];
84+
let i = 0;
85+
while (i < syllables.length) {
86+
for (let j = syllables.length; j > i; j--) {
87+
let word = syllables.slice(i, j).join("");
88+
if (this.MYWORDS.has(word)) {
89+
wordList.push(word);
90+
i = j;
91+
break;
92+
} else {
93+
if (j === i + 1) {
94+
let s = syllables[i];
95+
wordList.push(s);
96+
i++;
97+
}
98+
}
99+
}
100+
}
101+
return wordList;
102+
}
103+
}
104+
105+
module.exports = MyWordSegmenter;

0 commit comments

Comments
 (0)