From 29c18487d7f622e429555d109863c6fd4a4e6cb7 Mon Sep 17 00:00:00 2001 From: Ezra Lazuardy Date: Tue, 4 Jun 2024 08:43:38 +0700 Subject: [PATCH] feat: update --- .github/workflows/build.yml | 2 + .mocharc.json | 8 + README.md | 81 +- bun.lockb | Bin 0 -> 128560 bytes eslint.config.mjs | 15 +- package.json | 45 +- src/core.ts | 1710 ++++++++++++++++++++++++++++++++++- src/env.ts | 11 - src/global.ts | 32 + src/types.ts | 29 +- src/utils.ts | 120 +++ tests/core.test.ts | 23 + tsconfig.json | 7 +- 13 files changed, 1936 insertions(+), 147 deletions(-) create mode 100644 .mocharc.json create mode 100755 bun.lockb delete mode 100644 src/env.ts create mode 100644 src/global.ts create mode 100644 src/utils.ts create mode 100644 tests/core.test.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 15155d0..f688e36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,5 +18,7 @@ jobs: run: npm ci --legacy-peer-deps - name: "🔍 Performing Lint" run: npm run lint + - name: "🧪 Running Tests" + run: npm run test - name: "🔨 Building Package" run: npm run build diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 0000000..6e5d176 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,8 @@ +{ + "loader": "ts-node/esm", + "require": ["ts-node/register", "chai/register-expect.js"], + "extensions": ["ts", "tsx"], + "spec": ["tests/**/*.test.*"], + "watch-files": ["src"], + "allowImportingTsExtensions": true +} diff --git a/README.md b/README.md index 27af27d..adcbaa2 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,63 @@
- Build - CodeQL Analysis - NPM License - NPM Unpacked Size - NPM Unpacked Size - NPM Type Definitions + Build + CodeQL Analysis + NPM License + NPM Unpacked Size + NPM Unpacked Size + NPM Type Definitions
-## 💎 gem +## ꦲ aksara -A more fun way to interact with Gemini. +Translate a text into Javanese Script in one line of code. ```typescript -await ask("am i deserve better?"); +translate("aksara jawa"); // ꦲꦏ꧀ꦱꦫ​ꦗꦮ ``` -Gem is a simple package that allows you to interact with the Gemini API in a more human-friendly way. +Aksara is a simple package that helps you to translate your text into Javanese Script or also known as _Aksara Jawa_, which is one of the most beautiful scripts in the world. -It's like talking to a friend, but in a more professional way. It's simplify the process of sending requests to the Gemini API, so you can focus more on the conversation itself. It's even more simpler than other API wrappers. +The translated text will be formatted as [Unicode](https://home.unicode.org) string of Javanese Script characters that can be used in any text editor or web browser, without any specific font requirement. -> 🚧 This package is still under heavy development. Beware of breaking changes. +It's also typescript compatible, so you can use it in any javascript or typescript project. + +> Reference: [Wikipedia](https://en.wikipedia.org/wiki/Javanese_script)
### 🚀 Getting Started -It's only require 3 simple steps to get started. +It's only require 2 simple steps to get started. Install the package ```bash -npm install @ezralazuardy/gem -``` - -Set the environment variables - -```bash -GEMINI_API_KEY="" +npm install @ezralazuardy/aksara ``` -Ask the Gemini. +And use it! ```typescript -import { ask } from "@ezralazuardy/gem"; +import { translate } from "@ezralazuardy/aksara"; -const response = await ask("what is the weather today?"); - -console.log(response); +console.log(translate("aksara jawa")); // ꦲꦏ꧀ꦱꦫ​ꦗꦮ ``` -Yup, just like that! Simple, right? - For advanced usage, please read the [documentation](#-documentation) below.
-### ⚙️ Using Specific Model - -By default, the model used is `gemini-pro`. You can change it by setting the `GEMINI_MODEL_ID` environment variable. - -```bash -GEMINI_MODEL_ID="gemini-pro" -``` - -Please beware of the pricing and capabilities of each model. - -As of `v0.0.1`, the following models are available: - -- `gemini-pro` (default) -- `gemini-1.5-pro-latest` +### 🔄 Revert Translation -> Plase refer to these references: -> [Gemini API Models](https://ai.google.dev/gemini-api/docs/models/gemini), [Gemini API Pricing](https://ai.google.dev/gemini-api/pricing). +If you want to revert the translation, you can just use the `translate` function again. -
- -### 🚥 Roadmap +```typescript +import { translate } from "@ezralazuardy/aksara"; -The following features are planned to be implemented in the future. +console.log(translate("ꦲꦏ꧀ꦱꦫ​ꦗꦮ")); // aksara jawa +``` -| Supported | Method | Version | Usage | -| --------- | ---------------- | --------- | -------------------------------------------------------- | -| 🟢 | `ask` | `v0.0.1+` | Generates text for a given prompt. | -| 🔴 | `askByStream` | `-` | Streams a text generations. | -| 🔴 | `object` | `-` | Generates a typed, structured object for a given prompt. | -| 🔴 | `objectByStream` | `-` | Streams a typed, structured object for a given prompt. | +No need to use other function. Simple, right?
@@ -92,4 +65,4 @@ The following features are planned to be implemented in the future. A proper documentation is still in progress 🥲 -> Written in [Typescript](https://www.typescriptlang.org). Heavily inspired by [Vercel AI](https://sdk.vercel.ai) and [Gemini](https://gemini.google.com). +> Written in [Typescript](https://www.typescriptlang.org). Heavily inspired by [@bennylin](https://github.com/bennylin). diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..96f03235bfd95d7fbd2090b27fadd0ddca4e56fd GIT binary patch literal 128560 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", "license": "MIT", "bugs": { - "url": "https://github.com/ezralazuardy/gem/issues" + "url": "https://github.com/ezralazuardy/aksara/issues" }, - "homepage": "https://github.com/ezralazuardy/gem#readme", + "homepage": "https://github.com/ezralazuardy/aksara#readme", "publishConfig": { "access": "public" }, "dependencies": { - "@ai-sdk/google": "^0.0.14", - "ai": "^3.1.12", - "dotenv": "^16.4.5", - "openai": "^4.47.1", - "react": "^18.3.1", - "typescript": "^5.4.5", - "zod": "^3.23.8" + "typescript": "^5.4.5" }, "devDependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@eslint/compat": "^1.0.1", - "@eslint/js": "^9.3.0", - "@types/json-schema": "^7.0.15", + "@eslint/js": "^9.4.0", + "@types/chai": "^4.3.16", + "@types/mocha": "^10.0.6", "@types/node": "^20.12.12", - "@types/react": "^18.3.2", - "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.10.0", "@typescript-eslint/parser": "^7.10.0", "@typescript-eslint/type-utils": "^7.10.0", "@typescript-eslint/utils": "^7.10.0", - "eslint": "^8.56.0", + "chai": "^4.4.1", + "eslint": "9.x", "eslint-plugin-react": "^7.34.1", "globals": "^15.3.0", - "typescript-eslint": "^7.10.0" + "mocha": "^10.4.0", + "ts-node": "^10.9.2", + "typescript-eslint": "^7.12.0" } } diff --git a/src/core.ts b/src/core.ts index 56d1bf2..d9981b2 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,32 +1,1698 @@ -import { google } from "@ai-sdk/google"; -import { generateText } from "ai"; -import { TextResponse } from "./types"; -import "./env"; +import type { + Character, + Consonant, + Digit, + Matra, + Shift, + SpecialSound, + TranslateConfiguration, +} from "./types"; +import { + trim, + find, + isDigit, + isPunct, + isVowel, + isConsonant, + isHR, + isCJ, + formatShift, + removeInvisibleChars, +} from "./utils"; +import "./global"; /** - * The model ID to use for generating text. + * Default configuration */ -const modelID = `models/${process.env.GOOGLE_GENERATIVE_AI_MODEL ?? "gemini-pro"}`; +const defaultConfig = { + typeMode: true, + withSpace: true, + withMurda: false, +} as TranslateConfiguration; /** - * The model to use for generating text. + * The map of Latin to Javanese Script */ -const model = google(modelID); +const javaneseToLatin: Character = { + "ꦀ": "", //? -- archaic + "ꦁ": "ng", //cecak + "ꦂ": "r", //layar + "ꦃ": "h", //wignyan + ꦄ: "A", //swara-A + ꦅ: "I", //I-Kawi -- archaic + ꦆ: "I", //I + ꦇ: "Ii", //Ii -- archaic + ꦈ: "U", //U + ꦉ: "rê", //pa cêrêk + ꦊ: "lê", //nga lêlêt + ꦋ: "lêu", //nga lêlêt Raswadi -- archaic + ꦌ: "E", //E + ꦍ: "Ai", //Ai + ꦎ: "O", //O + + ꦏ: "ka", + ꦐ: "qa", //Ka Sasak + ꦑ: "kʰa", //Murda + ꦒ: "ga", + ꦓ: "gʰa", //Murda + ꦔ: "nga", //ṅa + ꦕ: "ca", + ꦖ: "cʰa", //Murda + ꦗ: "ja", + ꦘ: "Nya", //Ja Sasak, Nya Murda + ꦙ: "jʰa", //Ja Mahaprana + ꦚ: "nya", //ña + ꦛ: "tha", //'ṭa', + ꦜ: "ṭʰa", //Murda + ꦝ: "dha", //'ḍa', + ꦞ: "ḍʰa", //Murda + ꦟ: "ṇa", //Murda + ꦠ: "ta", + ꦡ: "ṭa", //Murda + ꦢ: "da", + ꦣ: "ḍa", //Murda + ꦤ: "na", + ꦥ: "pa", + ꦦ: "pʰa", //Murda + ꦧ: "ba", + ꦨ: "bʰa", //Murda + ꦩ: "ma", + ꦪ: "ya", + ꦫ: "ra", + ꦬ: "Ra", //Ra Agung + ꦭ: "la", + ꦮ: "wa", + ꦯ: "śa", //Murda + ꦰ: "ṣa", //Sa Mahaprana + ꦱ: "sa", + ꦲ: "ha", //could also be "a" or any sandhangan swara + + "꦳": "​", //cecak telu -- diganti zero-width joiner (tmp) + "ꦺꦴ": "o", //taling tarung + "ꦴ": "a", + "ꦶ": "i", + "ꦷ": "ii", + "ꦸ": "u", + "ꦹ": "uu", + "ꦺ": "e", + "ꦻ": "ai", + "ꦼ": "ê", + "ꦽ": "rê", + "ꦾ": "ya", + "ꦿ": "ra", + + "꧀": "​", //pangkon -- diganti zero-width joiner (tmp) + + "꧁": "—", + "꧂": "—", + "꧃": "–", + "꧄": "–", + "꧅": "–", + "꧆": "", + "꧇": "​", //titik dua -- diganti zero-width joiner (tmp) + "꧈": ",", + "꧉": ".", + "꧊": "qqq", + "꧋": "–", + "꧌": "–", + "꧍": "–", + ꧏ: "²", + "꧐": "0", + "꧑": "1", + "꧒": "2", + "꧓": "3", + "꧔": "4", + "꧕": "5", + "꧖": "6", + "꧗": "7", + "꧘": "8", + "꧙": "9", + "꧞": "—", + "꧟": "—", + "​": " ", //zero-width space +}; /** - * Generate text based on the prompt. - * @param {string} prompt The props for the ask function. - * @returns {Promise} The response from the generateText function. + * Check the matra (sandhangan swara). + * If the character is a vowel, return the matra (sandhangan swara). + * @param {string} str + * @param {TranslateConfiguration} config */ -export const ask = async (prompt: string): Promise => { - // generate the text - const result = await generateText({ prompt, model }); +function getMatra(str: string, config: TranslateConfiguration) { + let i = 0; - return { - model: modelID, - text: result.text, - finishReason: result.finishReason, - usage: result.usage, - warnings: result.warnings, - } as TextResponse; -}; + if (str.length < 1) { + return "꧀"; + } + + while (str[i] === "h") { + i++; + if (i >= str.length) { + break; + } + } + + if (i < str.length) str = str.substring(i); + + const matramap1: Matra = { + ā: "ꦴ", + â: "ꦴ", + e: "ꦺ", + è: "ꦺ", + é: "ꦺ", + i: "ꦶ", + ī: "ꦷ", + o: "ꦺꦴ", + u: "ꦸ", + ū: "ꦹ", + x: "ꦼ", + ě: "ꦼ", + ĕ: "ꦼ", + ê: "ꦼ", + ō: "ꦼꦴ", + ô: "", + A: "ꦄ", + E: "ꦌ", + È: "ꦌ", + É: "ꦌ", + I: "ꦆ", + U: "ꦈ", + O: "ꦎ", + X: "ꦄꦼ", + Ě: "ꦄꦼ", + Ê: "ꦄꦼ", + ṛ: "ꦽ", + aa: "ꦴ", + ai: "ꦻ", + au: "ꦻꦴ", + ii: "ꦷ", + uu: "ꦹ", + }; + + const matramap2: Matra = { + ā: "ꦴ", + â: "ꦴ", + e: "ꦼ", + è: "ꦺ", + é: "ꦺ", + i: "ꦶ", + ī: "ꦷ", + u: "ꦸ", + ū: "ꦹ", + o: "ꦺꦴ", + x: "ꦼ", + ě: "ꦼ", + ĕ: "ꦼ", + ê: "ꦼ", + ô: "", + ō: "ꦼꦴ", + A: "ꦄ", + E: "ꦄꦼ", + È: "ꦌ", + É: "ꦌ", + I: "ꦆ", + U: "ꦈ", + O: "ꦎ", + X: "ꦄꦼ", + Ě: "ꦄꦼ", + Ê: "ꦄꦼ", + ṛ: "ꦽ", + aa: "ꦴ", + ai: "ꦻ", + au: "ꦻꦴ", + ii: "ꦷ", + uu: "ꦹ", + }; + + const matramap: Matra = config.typeMode ? matramap1 : matramap2; + + if (matramap[str] !== undefined) { + return matramap[str]; + } + + return ""; +} + +/** + * Get the consonant. + * If the character is a consonant, return the consonant character. + * @param {string} str + * @param {TranslateConfiguration} config + * + * TOC: + * 1. ends with 'h' -- th: thr, thl, thw, thy; dh: dhr, dhl, dhw, dhy; hy,hh, rh, kh, gh, ch, jh, ṭh, th: thr, thl; dh: dhr, dhl; hy,hh, rh, kh, gh, ch, jh, ṭh, ḍh, ph, bh, sh, h + * 2. ends with 'g' -- ng: ngr, ngy, nggr, nggl, nggw, nggy, ngg, ngng, ngl, njr, ngw; rg, hg, gg, g + * 3. ends with 'y' -- ny: nyr, nyl; ry, dhy, thy, y + * 4. ends with 'r', panjingan 'l'/'w' -- hr, rr, nggr; ll, rl, hl; rw, hw, ngw + * 5. ends with 'c', and 'j' -- nc: ncr, ncl; rc; nj: njr, njl; rj; + * 6. ends with 'ñ' -- jñ: jny + * + * @TODO: still case sensitive, e.g. "RR" is still incorrect. + */ +function getShift(str: string, config: TranslateConfiguration): Shift { + str = str.toLowerCase(); + + if (str.indexOf("th") === 0) { + // suku kata diawali 'th' + if (str.indexOf("thr") === 0) { + // cakra + return formatShift({ CoreSound: "ꦛꦿ", len: 3 }); + } else if (str.indexOf("thl") === 0) { + //thl + return formatShift({ CoreSound: "ꦛ꧀ꦭ", len: 3 }); + } else if (str.indexOf("thy") === 0) { + //thy -- ... + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦛꦾ", + len: 3, + }); + } else if (str.indexOf("thw") === 0) { + //thw -- ... + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦛ꧀ꦮ", + len: 3, + }); + } else { + return formatShift({ CoreSound: "ꦛ", len: 2 }); + } + } else if (str.indexOf("dh") === 0) { + // suku kata diawali 'dh' + if (str.indexOf("dhr") === 0) { + // cakra + return formatShift({ CoreSound: "ꦝꦿ", len: 3 }); + } else if (str.indexOf("dhl") === 0) { + //dhl + return formatShift({ CoreSound: "ꦝ꧀ꦭ", len: 3 }); + } else if (str.indexOf("dhy") === 0) { + //dhy -- dhyaksa + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦝꦾ", + len: 3, + }); + } else if (str.indexOf("dhw") === 0) { + //dhw -- dhwani + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦝ꧀ꦮ", + len: 3, + }); + } else { + return formatShift({ CoreSound: "ꦝ", len: 2 }); + } + } else if (str.indexOf("hy") === 0) { + //hyang + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦲꦾ", + len: 2, + }); + } else if (str.indexOf("hh") === 0) { + //hh + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦃꦲ", + len: 2, + }); + } else if (str.indexOf("rh") === 0) { + //rh (kata berakhiran r diikuti kata berawalan h + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦂꦲ", + len: 2, + }); + } else if (str.indexOf("kh") === 0) { + //kh (aksara murda) + return formatShift({ CoreSound: "ꦑ", len: 2 }); + } else if (str.indexOf("gh") === 0) { + //gh (aksara murda) + return formatShift({ CoreSound: "ꦓ", len: 2 }); + } else if (str.indexOf("ch") === 0) { + //ch (aksara murda) + return formatShift({ CoreSound: "ꦖ", len: 2 }); + } else if (str.indexOf("jh") === 0) { + //jh (aksara murda) + return formatShift({ CoreSound: "ꦙ", len: 2 }); + } else if (str.indexOf("ṭh") === 0) { + //ṭh (aksara murda) + return formatShift({ CoreSound: "ꦜ", len: 2 }); + } else if (str.indexOf("ḍh") === 0) { + //ḍh (aksara murda) + return formatShift({ CoreSound: "ꦞ", len: 2 }); + } else if (str.indexOf("ph") === 0) { + //ph (aksara murda) + return formatShift({ CoreSound: "ꦦ", len: 2 }); + } else if (str.indexOf("bh") === 0) { + //bh (aksara murda) + return formatShift({ CoreSound: "ꦨ", len: 2 }); + } else if (str.indexOf("sh") === 0) { + //sh (aksara murda) + return formatShift({ CoreSound: "ꦯ", len: 2 }); + } else if (str.indexOf("h") === 1) { + //h + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦲ", + len: 2, + }); + } else if (str.indexOf("h") > 1) { + //suku kata memiliki konsonan 'h' yang tidak di awal suku + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + //nga + if (str.indexOf("ng") === 0) { + //suku kata diawali 'ng' + if (str.indexOf("ngr") === 0) { + //cakra + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔꦿ", + len: 3, + }); + } else if (str.indexOf("ngy") === 0) { + //pengkal + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔꦾ", + len: 3, + }); + } else if (str.indexOf("nggr") === 0) { + //nggronjal + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦒꦿ", + len: 4, + }); + } else if (str.indexOf("nggl") === 0) { + //nggl- + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦒ꧀ꦭ", + len: 4, + }); + } else if (str.indexOf("nggw") === 0) { + //nggw-, munggwing + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦒ꧀ꦮ", + len: 4, + }); + } else if (str.indexOf("nggy") === 0) { + //nggy-, anggyat + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦒꦾ", + len: 4, + }); + } else if (str.indexOf("ngg") === 0) { + //ngg + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦒ", + len: 3, + }); /* + } else if (str.indexOf("ngng") === 0) { //ngng + return formatShift({ CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦔ", len: 4 });*/ + } else if (str.indexOf("ngl") === 0) { + //ngl, e.g. ngluwari + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦭ", + len: 3, + }); + } else if (str.indexOf("ngw") === 0) { + //ngw, e.g. ngwiru + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦔ꧀ꦮ", + len: 3, + }); + } else { + return formatShift({ CoreSound: "ꦁ", len: 2 }); + } + } else if (str.indexOf("rg") === 0) { + //'rg', e.g. amarga + return formatShift({ CoreSound: "ꦂꦒ", len: 2 }); + } else if (str.indexOf("hg") === 0) { + //'hg', e.g. dahgene + return formatShift({ CoreSound: "ꦃꦒ", len: 2 }); + } else if (str.indexOf("gg") === 0) { + //'gg', e.g. root word ends with 'g' with suffix starts with vocal + return formatShift({ CoreSound: "ꦒ꧀ꦒ", len: 2 }); + } else if (str.indexOf("g") === 1) { + //g + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦒ", + len: 2, + }); + } else if (str.indexOf("g") > 1) { + //suku kata memiliki konsonan 'g' yang tidak di awal suku + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + if (str.indexOf("jñ") === 0) { + //suku kata diawali 'jñ' + return formatShift({ CoreSound: "ꦘ", len: 2 }); + } + + if (str.indexOf("jny") === 0) { + //suku kata diawali 'jñ' + return formatShift({ CoreSound: "ꦘ", len: 3 }); // still not working, 22 Jan 19 + } + + //nya + if (str.indexOf("ny") === 0) { + //suku kata diawali 'ny' + if (str.indexOf("nyr") === 0) { + //cakra + return formatShift({ CoreSound: "ꦚꦿ", len: 3 }); + } else if (str.indexOf("nyl") === 0) { + //nyl, e.g. nylonong + return formatShift({ CoreSound: "ꦚ꧀ꦭ", len: 3 }); + } else { + return formatShift({ CoreSound: "ꦚ", len: 2 }); + } + } else if (str.indexOf("ry") === 0) { + //'ry', e.g. Suryati, Wiryadi + if (str.indexOf("ryy") === 0) { + return formatShift({ CoreSound: "ꦂꦪꦾ", len: 3 }); + } else { + return formatShift({ CoreSound: "ꦂꦪ", len: 2 }); + } + } else if (str.indexOf("yy") === 0) { + //'yy', e.g. Duryyodhana (Jawa Kuno) + return formatShift({ CoreSound: "ꦪꦾ", len: 2 }); + } else if (str.indexOf("qy") === 0) { + //qy -- only pengkal + return formatShift({ CoreSound: "ꦾ", len: 1 }); + } else if (str.indexOf("y") === 1) { + //pengkal + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦾ", + len: 2, + }); + } else if (str.indexOf("y") > 1) { + //suku kata memiliki konsonan 'y' yang tidak di awal suku + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound += resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + if (str.indexOf("hr") === 0) { + //hr- + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦃꦿ", + len: 2, + }); + } else if (str.indexOf("rr") === 0) { + //rr -- no cakra + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦂꦫ", + len: 2, + }); + } else if (str.indexOf("wr") === 0) { + //wr -- panjingan + cakra + return formatShift({ CoreSound: "" + "ꦮꦿ", len: 2 }); + } else if (str.indexOf("qr") === 0) { + //qr -- only cakra + return formatShift({ CoreSound: "ꦿ", len: 1 }); + } else if (str.indexOf("r") === 1) { + //cakra + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦿ", + len: 2, + }); + } else if (str.indexOf("r") > 1) { + //suku kata memiliki konsonan 'r' yang tidak di awal suku + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound += resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + // panjingan -l + if (str.indexOf("ll") === 0) { + //ll + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦭ꧀ꦭ", + len: 2, + }); + } else if (str.indexOf("rl") === 0) { + //rl (kata berakhiran r diikuti kata berawalan l + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦂꦭ", + len: 2, + }); + } else if (str.indexOf("hl") === 0) { + //hl + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦃꦭ", + len: 2, + }); + } else if (str.indexOf("ql") === 0) { + //only panjingan + return formatShift({ CoreSound: "꧀ꦭ", len: 2 }); + } else if (str.indexOf("l") === 1) { + //l + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦭ", + len: 2, + }); + } else if (str.indexOf("l") > 1) { + //suku kata memiliki konsonan 'l' yang tidak di awal suku//panjingan + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + // panjingan -w + if (str.indexOf("rw") === 0) { + //rw + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦂꦮ", + len: 2, + }); //error untuk 'rwi', 'rwab' + } else if (str.indexOf("hw") === 0) { + //hw + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦃꦮ", + len: 2, + }); //ꦲ꧀ꦮ + } else if (str.indexOf("qw") === 0) { + //only panjingan + return formatShift({ CoreSound: "꧀ꦮ", len: 2 }); + } else if (str.indexOf("w") === 1) { + //w + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦮ", + len: 2, + }); + } else if (str.indexOf("w") > 1) { + //suku kata memiliki konsonan 'w' yang tidak di awal suku//panjingan + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + if (str.indexOf("nc") === 0) { + //nc + if (str.indexOf("ncr") === 0) { + //ncr -- kencrung + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦕꦿ", + len: 3, + }); + } else if (str.indexOf("ncl") === 0) { + //ncl -- kinclong + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦕ꧀ꦭ", + len: 3, + }); + } else { + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦕ", + len: 2, + }); + } + } else if (str.indexOf("rc") === 0) { + //rc -- arca + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦂꦕ", + len: 2, + }); + } else if (str.indexOf("c") === 1) { + //c + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦕ", + len: 2, + }); + } else if (str.indexOf("c") > 1) { + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + if (str.indexOf("nj") === 0) { + //nj + if (str.indexOf("njr") === 0) { + //njr -- anjrah + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦗꦿ", + len: 3, + }); + } else if (str.indexOf("njl") === 0) { + //njl -- anjlog + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦗ꧀ꦭ", + len: 3, + }); + } else { + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "ꦚ꧀ꦗ", + len: 2, + }); + } + } else if (str.indexOf("rj") === 0) { + //'rj' + return formatShift({ CoreSound: "ꦂꦗ", len: 2 }); + } else if (str.indexOf("j") === 1) { + //j + return formatShift({ + CoreSound: "" + getCoreSound(str[0], config).CoreSound + "꧀ꦗ", + len: 2, + }); + } else if (str.indexOf("j") > 1) { + let sound = ""; + let len = 0; + for (let index = 0; index < str.length; index++) { + const c = str[index]; + if (!isVowel(c)) { + sound = sound + resolveCharacterSound(c, config); + len++; + } else { + break; + } + } + return formatShift({ CoreSound: sound, len: len }); + } + + return formatShift({ CoreSound: null, len: 1 }); +} + +/** + * Get the core sound of a character. + * Return 'aksara nglegana' or 'aksara istimewa' (f/v/z/pangkon). + * @param {string} str + * @param {TranslateConfiguration} config + */ +function getCoreSound(str: string, config: TranslateConfiguration): Shift { + const consonantMap1: Consonant = { + A: "ꦄ", //A + B: "ꦧ", //B + C: "ꦕ", //C + D: "ꦢ", //D + E: "ꦌ", //E + F: "ꦥ꦳", //F + G: "ꦒ", //G + H: "ꦲ", //H + I: "ꦆ", //I + J: "ꦗ", //J + K: "ꦏ", //K + L: "ꦭ", //L + M: "ꦩ", //M + N: "ꦤ", //N + O: "ꦎ", //O + P: "ꦥ", //P + Q: "꧀", //Q + R: "ꦂ", //R + S: "ꦱ", //S + T: "ꦠ", //T + U: "ꦈ", //U + V: "ꦮ꦳", //V + W: "ꦮ", //W + X: "ꦼ", //X + Y: "ꦪ", //Y + Z: "ꦗ꦳", //Z + a: "ꦲ", //a + b: "ꦧ", //b + c: "ꦕ", //c + d: "ꦢ", //d + e: "ꦲꦺ", //e + f: "ꦥ꦳", //f + g: "ꦒ", //g + h: "ꦃ", //h + i: "ꦲꦶ", //i + j: "ꦗ", //j + k: "ꦏ", //k + l: "ꦭ", //l + m: "ꦩ", //m + n: "ꦤ", //n + o: "ꦲꦺꦴ", //o + p: "ꦥ", //p + q: "꧀", //q + r: "ꦂ", //r + s: "ꦱ", //s + ś: "ꦯ", //ś + t: "ꦠ", //t + u: "ꦲꦸ", //u + v: "ꦮ꦳", //v + w: "ꦮ", //w + x: "ꦲꦼ", //x + y: "ꦪ", //y + z: "ꦗ꦳", //z + È: "ꦌ", //È + É: "ꦌ", //É + Ê: "ꦄꦼ", //Ê + Ě: "ꦄꦼ", //Ě + è: "ꦲꦺ", //è + é: "ꦲꦺ", //é + ê: "ꦲꦼ", //ê + ě: "ꦲꦼ", //ě + ô: "ꦲ", //ô + ñ: "ꦚ", //enye + ṇ: "ꦟ", + ḍ: "ꦝ", + ṭ: "ꦛ", + ṣ: "ꦰ", + ṛ: "ꦽ", + }; + + const consonantMap2: Consonant = { + A: "ꦄ", //A + B: "ꦨ", //B + C: "ꦖ", //C + D: "ꦣ", //D + E: "ꦌ", //E + F: "ꦦ꦳", //F + G: "ꦓ", //G + H: "ꦲ꦳", //H + I: "ꦆ", //I + J: "ꦙ", //J + K: "ꦑ", //K + L: "ꦭ", //L + M: "ꦩ", //M + N: "ꦟ", //N + O: "ꦎ", //O + P: "ꦦ", //P + Q: "꧀", //Q + R: "ꦬ", //R + ś: "ꦯ", //ś + S: "ꦯ", //S + T: "ꦡ", //T + U: "ꦈ", //U + V: "ꦮ꦳", //V + W: "ꦮ", //W + X: "ꦼ", //X + Y: "ꦪ", //Y + Z: "ꦗ꦳", //Z + a: "ꦄ", //a + b: "ꦧ", //b + c: "ꦕ", //c + d: "ꦢ", //d + e: "ꦌ", //e + f: "ꦥ꦳", //f + g: "ꦒ", //g + h: "ꦃ", //h + i: "ꦆ", //i + j: "ꦗ", //j + k: "ꦏ", //k + l: "ꦭ", //l + m: "ꦩ", //m + n: "ꦤ", //n + o: "ꦎ", //o + p: "ꦥ", //p + q: "꧀", //q + r: "ꦂ", //r + s: "ꦱ", //s + t: "ꦠ", //t + u: "ꦈ", //u + v: "ꦮ꦳", //v + w: "ꦮ", //w + x: "ꦼ", //x + ĕ: "ꦼ", //ĕ + ě: "ꦼ", //ě + ê: "ꦼ", //ê + ū: "ꦹ", //ū + y: "ꦪ", //y + z: "ꦗ꦳", //z + È: "ꦌ", //È + É: "ꦌ", //É + Ê: "ꦄꦼ", //Ê + Ě: "ꦄꦼ", //Ě + è: "ꦌ", //è + é: "ꦌ", //é + ṇ: "ꦟ", + ḍ: "ꦝ", + ṭ: "ꦛ", + ṣ: "ꦰ", + ṛ: "ꦽ", + }; + + const consonantMap: Consonant = config.withMurda + ? consonantMap2 + : consonantMap1; + const h_shift: Shift = getShift(str, config); + let core = str; + + if (h_shift.CoreSound === null) { + if (consonantMap[str.charAt(0)]) core = consonantMap[str.charAt(0)]; + return formatShift({ CoreSound: core, len: 1 }); + } + + return h_shift; +} + +/** + * Get the special sound of a character. + * @param {string} str + */ +function getSpecialSound(str: string): string | null { + const specialsoundMap: SpecialSound = { + f: "ꦥ꦳꧀", + v: "ꦮ꦳꧀", + z: "ꦗ꦳꧀", + ś: "ꦯ", + q: "꧀" /*pangkon*/, + }; + + if (specialsoundMap[str] !== undefined) { + return specialsoundMap[str]; + } + + return null; +} + +/** + * Resolve the core sound of a character. + * Return punctuation, digits, vowels, or nglegana + pangkon. + * @param {string} c + * @param {TranslateConfiguration} config + */ +function resolveCharacterSound( + c: string, + config: TranslateConfiguration, +): string { + const str = "" + c; + if (isDigit(c)) { + return "" + ("꧇" + parseInt(c)); + } else if (isHR(str[0])) { + return "" + getCoreSound(str, config).CoreSound; //layar dan wignyan + } else if (isCJ(str[1])) { + return "" + getCoreSound(str, config).CoreSound + "꧀"; //anuswara + } else if (isConsonant(str[0])) { + return "" + getCoreSound(str, config).CoreSound + "꧀"; + } else { + return "" + getCoreSound(str, config).CoreSound; + } +} + +/** + * Translate each syllable. + * @param {string} str + * @param {TranslateConfiguration} config + * @param {boolean} vowelPrev + */ +function getSound( + str: string, + config: TranslateConfiguration, + vowelPrev: boolean, +) { + str = trim(str); + + if (str === null || str === "") return ""; + + const specialSound = getSpecialSound(str); + + if (specialSound !== null && str.length === 1) return specialSound; + + if (str.length === 1) return resolveCharacterSound(str[0], config); + + const core_sound: Shift = getCoreSound(str, config); + let konsonan = ""; + let matra = ""; + + // aeiou (suku, wulu, pepet, taling, taling tarung, dll.) + if (core_sound.len >= 1) + matra = getMatra(str.substring(core_sound.len), config); + + if (str.indexOf("nggr") === 0) { + //nggr- + if (vowelPrev) + konsonan = "ꦁꦒꦿ"; //nggr-, e.g. panggrahita + else konsonan = "ꦔ꧀ꦒꦿ"; //nggr-, i.e. nggronjal + } else if (str.indexOf("nggl") === 0) { + //nggl- + konsonan = "ꦔ꧀ꦒ꧀ꦭ"; + } else if (str.indexOf("nggw") === 0) { + //nggw- + konsonan = "ꦔ꧀ꦒ꧀ꦮ"; + } else if (str.indexOf("nggy") === 0) { + //nggy- + konsonan = "ꦔ꧀ꦒꦾ"; + } else if (str.indexOf("ngg") === 0) { + //ngg- + if (vowelPrev) + konsonan = "ꦁꦒ"; //ngg-, e.g. tunggal + else konsonan = "ꦔ꧀ꦒ"; //ngg-, i.e. nggambar + } else if (str.indexOf("ngl") === 0) { + //ngl- + konsonan = "ꦔ꧀ꦭ"; + } else if (str.indexOf("ngw") === 0) { + //ngw- + konsonan = "ꦔ꧀ꦮ"; + } else if (str.indexOf("ncl") === 0) { + //ncl- + konsonan = "ꦚ꧀ꦕ꧀ꦭ"; + } else if (str.indexOf("ncr") === 0) { + //ncr- + konsonan = "ꦚ꧀ꦕꦿ"; + } else if (str.indexOf("njl") === 0) { + //njl- + konsonan = "ꦚ꧀ꦗ꧀ꦭ"; + } else if (str.indexOf("njr") === 0) { + //njr- + konsonan = "ꦚ꧀ꦗꦿ"; + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦕ꧀ꦭ") { + // -ncl- + konsonan = "ꦚ꧀ꦕ꧀ꦭ"; //-ncl- + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦕꦿ") { + // -ncr- + konsonan = "ꦚ꧀ꦕꦿ"; //-ncr-*/ + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦕ") { + // -nc- + konsonan = "ꦚ꧀ꦕ"; //-nyc-/* + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦗ꧀ꦭ") { + // -njl- + konsonan = "ꦚ꧀ꦗ꧀ꦭ"; //-njl- + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦗꦿ") { + // -njr- + konsonan = "ꦚ꧀ꦗꦿ"; //-njr-*/ + } else if (core_sound.CoreSound === "ꦤꦚ꧀ꦗ") { + // -nj- + konsonan = "ꦚ꧀ꦗ"; //-nyj- + } else if (core_sound.CoreSound === "ꦢꦝ꧀ꦮ") { + // -dhw- + konsonan = "ꦝ꧀ꦮ"; //-dhw- + } else if (core_sound.CoreSound === "ꦢꦝꦾ") { + // -dhy- + konsonan = "ꦝꦾ"; //-dhy- + } else if (core_sound.CoreSound === "ꦠꦛ꧀ꦮ") { + // -thw- + konsonan = "ꦛ꧀ꦮ"; //-dhw- + } else if (core_sound.CoreSound === "ꦠꦛꦾ") { + // -thy- + konsonan = "ꦛꦾ"; //-dhy- + } else if (find(core_sound.CoreSound, "ꦾ") && matra === "꧀") { + // pengkal + konsonan = core_sound.CoreSound; + matra = ""; //-y- + } else if (find(core_sound.CoreSound, "ꦿ") && matra === "꧀") { + // cakra + konsonan = core_sound.CoreSound; + matra = ""; //-r- + } else if (find(core_sound.CoreSound, "ꦿ") && matra === "ꦼ") { + // cakra keret + if ( + (str[0] === "n" && str[1] === "y") || + ((str[0] === "t" || str[0] === "d") && str[1] === "h") + ) { + konsonan = getCoreSound(str[0] + str[1], config).CoreSound + "ꦽ"; + matra = ""; //nyrê-, thrê-, dhrê- + } else if (str[0] === "n" && str[1] === "g") { + if (str[2] === "g") konsonan = "ꦔ꧀ꦒꦽ"; + else konsonan = "ꦔꦽ"; + matra = ""; //nggrê-/ngrê- + } else { + konsonan = getCoreSound(str[0], config).CoreSound + "ꦽ"; + matra = ""; //-rê- + } + } else if (find(core_sound.CoreSound, "ꦭ") && matra === "ꦼ") { + // nga lelet + if ( + (str[0] === "n" && str[1] === "y") || + ((str[0] === "t" || str[0] === "d") && str[1] === "h") + ) { + konsonan = getCoreSound(str[0] + str[1], config).CoreSound + "꧀ꦭꦼ"; + matra = ""; //nylê-, thlê-, dhlê- + } else if (str[0] === "n" && str[1] === "g") { + if (str[2] === "g") konsonan = "ꦔ꧀ꦒ꧀ꦭꦼ"; + else konsonan = "ꦔ꧀ꦭꦼ"; + matra = ""; //ngglê-/nglê- + } else if (str[0] === "l") { + konsonan = "ꦊ"; + matra = ""; //-lê- + } else { + konsonan = getCoreSound(str[0], config).CoreSound + "꧀ꦭꦼ"; + matra = ""; //-lê- + } + } else if ( + core_sound.CoreSound === "ꦛꦿ" || + core_sound.CoreSound === "ꦝꦿ" || + core_sound.CoreSound === "ꦔꦿ" || + core_sound.CoreSound === "ꦚꦿ" + ) { + // i.e. nyruput + konsonan = core_sound.CoreSound; + if (matra === "꧀") matra = ""; + } else if (core_sound.CoreSound === "ꦭꦭ꧀ꦭ") { + // -ll- + konsonan = "ꦭ꧀ꦭ"; //double -l- + } else if (core_sound.CoreSound === "ꦂꦂꦫ") { + // -rr- + konsonan = "ꦂꦫ"; //double -r- + } else if (core_sound.CoreSound === "ꦂꦂꦲ") { + // -rh- + konsonan = "ꦂꦲ"; //-rh- + } else if (core_sound.CoreSound === "ꦂꦂꦭ") { + // -rl- + konsonan = "ꦂꦭ"; //-rl- + } else if (core_sound.CoreSound === "ꦂꦂꦮ") { + // -rw- + if (vowelPrev) + konsonan = "ꦂꦮ"; //-rw- -- arwana + else konsonan = "ꦫ꧀ꦮ"; //rw- -- rwa/rwi/rwab + } else if (core_sound.CoreSound === "ꦂꦂꦕ") { + // -rc- + konsonan = "ꦂꦕ"; //-rc- + } else if (core_sound.CoreSound === "ꦃꦃꦲ") { + // -hh- + konsonan = "ꦃꦲ"; //double -h- + } else if (core_sound.CoreSound === "ꦃꦃꦭ") { + // -hl- + if (vowelPrev) + konsonan = "ꦃꦭ"; //-hl- + else konsonan = "ꦲ꧀ꦭ"; //hlam + } else if (core_sound.CoreSound === "ꦃꦃꦮ") { + // -hw- + if (vowelPrev) + konsonan = "ꦃꦮ"; //-hw- + else konsonan = "ꦲ꧀ꦮ"; //hwab,hwan + } else if (core_sound.CoreSound === "ꦃꦲꦾ") { + // -hy- + if (vowelPrev) + konsonan = "ꦃꦪ"; //sembahyang + else konsonan = "ꦲꦾ"; //hyang/* + } else if (core_sound.CoreSound === "ꦃꦃꦽ") { + // hrx- + konsonan = "ꦲꦿ"; //hrx- + } else if (core_sound.CoreSound === "ꦃꦃꦿ") { + // hr- + if (matra === "ꦼ") + konsonan = "ꦲꦽ"; //hr- + else konsonan = "ꦲꦿ"; //hr- + } else if (core_sound.CoreSound === "ꦃꦲꦿ") { + // hr- + if (matra === "ꦼ") + konsonan = "ꦲꦽ"; //hr- + else konsonan = "ꦲꦿ"; //hr- + } else if (core_sound.CoreSound === "ꦃ" && matra === "꧀") { + // wignyan - 12 April + konsonan = "ꦲ"; //ha + } else if (core_sound.CoreSound === "ꦃ" && matra !== "꧀") { + // wignyan + konsonan = "ꦲ"; //ha + } else if (core_sound.CoreSound === "ꦂ" && matra === "ꦼ") { + // pa cerek + konsonan = "ꦉ"; + matra = ""; //rê + } else if (core_sound.CoreSound === "ꦂ" && matra !== "꧀") { + // layar + konsonan = "ꦫ"; //ra + } else if (core_sound.CoreSound === "ꦁ" && matra !== "꧀") { + // cecak + konsonan = "ꦔ"; //nga + } else if (core_sound.CoreSound === "ꦁ" && matra === "꧀") { + // cecak + konsonan = "ꦁ"; + matra = ""; //cecak + } else { + konsonan = core_sound.CoreSound; + } + return "" + konsonan + matra; +} + +/** + * Check if a text contains Javanese Script. + * @param {string} text + * @returns {boolean} + */ +export function hasAksara(text: string): boolean { + const aksaras = Object.keys(javaneseToLatin); + for (let i = 0; i < aksaras.length; i++) + if (text.includes(aksaras[i])) return true; + return false; +} + +/** + * Translate Latin to Javanese Script. + * @param {string} latin + * @param {TranslateConfiguration} config + * @returns {string} + */ +export function translate( + latin: string, + config: TranslateConfiguration | null = null, +): string { + if (hasAksara(latin)) return translateAksara(latin); + + let i = 0; + let pi = 0; // offset + let vowelFlag = false; + let angkaFlag = false; + let cecakFlag = false; + let vowelPrev = false; + let str = latin; + let ret = ""; + const angka: Digit = { + "0": "꧐", + "1": "꧑", + "2": "꧒", + "3": "꧓", + "4": "꧔", + "5": "꧕", + "6": "꧖", + "7": "꧗", + "8": "꧘", + "9": "꧙", + }; + + str = trim(str); + + if (!config) config = defaultConfig; + + while (i < str.length) { + if (i > 0 && isVowel(str[i]) && isVowel(str[i - 1])) { + //deal with words that start with multiple vocals + if ( + (str[i - 1] === "a" && str[i] === "a") || + (str[i - 1] === "i" && str[i] === "i") || + (str[i - 1] === "u" && str[i] === "u") || + (str[i - 1] === "a" && str[i] === "i") || + (str[i - 1] === "a" && str[i] === "u") + ) { + //specials + if (i > 1 && !isConsonant(str[i - 2])) { + //for example if starts with 'ai-' + str = str.substring(0, i) + "h" + str.substring(i, str.length); + } + //else, do nothing, look in matramap table + } else if ( + (str[i - 1] === "e" || str[i - 1] === "è" || str[i - 1] === "é") && + (str[i] === "a" || str[i] === "o") + ) { + //-y- + str = str.substring(0, i) + "y" + str.substring(i, str.length); + } else if ( + str[i - 1] === "i" && + (str[i] === "a" || + str[i] === "e" || + str[i] === "è" || + str[i] === "é" || + str[i] === "o" || + str[i] === "u") + ) { + //-y- + str = str.substring(0, i) + "y" + str.substring(i, str.length); + } else if ( + str[i - 1] === "o" && + (str[i] === "a" || str[i] === "e" || str[i] === "è" || str[i] === "é") + ) { + //-w- + str = str.substring(0, i) + "w" + str.substring(i, str.length); + } else if ( + str[i - 1] === "u" && + (str[i] === "a" || + str[i] === "e" || + str[i] === "è" || + str[i] === "é" || + str[i] === "i" || + str[i] === "o") + ) { + //-y- + str = str.substring(0, i) + "w" + str.substring(i, str.length); + } else { + str = str.substring(0, i) + "h" + str.substring(i, str.length); + } + } + if ( + (str[i] === "h" && vowelFlag) || + (!isVowel(str[i]) && i > 0) || + str[i] === " " || + isPunct(str[i]) || + isDigit(str[i]) || + i - pi > 5 + ) { + if (!isDigit(str[i]) && angkaFlag) { + //turn off the flag + ret += "꧇​"; // with zws + angkaFlag = false; + } + if (pi < i) { + if ( + cecakFlag && + getSound(str.substring(pi, i), config, vowelPrev) === "ꦁ" + ) { + cecakFlag = false; + ret += "ꦔ꧀ꦔ"; + } else if ( + !cecakFlag && + getSound(str.substring(pi, i), config, vowelPrev) === "ꦁ" + ) { + cecakFlag = true; + ret += "ꦁ"; + } else { + cecakFlag = false; + ret += getSound(str.substring(pi, i), config, vowelPrev); + } + } + if (str[i] === " ") { + ret += config.withSpace ? " " : ""; + } + if (isPunct(str[i])) { + if (str[i] === ".") { + ret += "꧉​"; //titik+zero-width space + pi = i + 1; + } else if (str[i] === ",") { + ret += "꧈​"; //koma+zero-width space + pi = i + 1; + } else if (str[i] === "|") { + ret += "꧋"; + pi = i + 1; + } else if (str[i] === "(") { + ret += "꧌"; + pi = i + 1; + } else if (str[i] === ")") { + ret += "꧍​"; + pi = i + 1; // with zws + } else if (str[i] === "-") { + //tanda hubung + ret += "​"; + pi = i + 1; + } else if ( + str[i] === "?" || + str[i] === "!" || + str[i] === '"' || + str[i] === "'" + ) { + //tanda tanya/seru/petik + ret += "​"; //zero-width space + pi = i + 1; + } else { + ret += str[i]; + pi = i + 1; + } + } else if (isDigit(str[i])) { + if (!angkaFlag) ret += "꧇"; + ret += angka[str[i]]; + angkaFlag = true; + pi = i + 1; + } else { + pi = i; + } + vowelFlag = false; + } else if (isVowel(str[i]) && str[i] !== "h") { + if (!isDigit(str[i]) && angkaFlag) { + //turn off the flag + ret += "꧇​"; //with zws + angkaFlag = false; + } + vowelFlag = true; + } + if (pi > 0 && isVowel(str[pi - 1])) { + //ngg + vowelPrev = true; + } else vowelPrev = false; + i++; + } + + if (pi < i) { + ret += getSound(str.substring(pi, i), config, vowelPrev); + } + return trim(ret); +} + +/** + * Translate Aksara Jawa to Latin. + * @param {stirng} aksara + * @returns string + */ +export function translateAksara(aksara: string): string { + const regexp_file = javaneseToLatin; + const str = aksara; + let trans: string = str; + for (let i = 0, j = 0; i < str.length; i++) { + if (!regexp_file[str[i]]) { + //not Aksara Jawa + trans = trans.replaceChar1(j, str[i]); + j++; + } else { + if (i > 0 && str[i] == "ꦫ" && str[i - 1] == "ꦂ") { + trans = trans.replaceChar1(j, "a"); + j++; + } else if (i > 0 && str[i] == "ꦔ" && str[i - 1] == "ꦁ") { + //double ngng + trans = trans.replaceChar1(j, "a"); + j++; + } else if ( + str[i] == "ꦴ" || + str[i] == "ꦶ" || + str[i] == "ꦸ" || + str[i] == "ꦺ" || + str[i] == "ꦼ" + ) { + if (i > 2 && str[i - 1] == "ꦲ" && str[i - 2] == "ꦲ") { + //-hah- + if (str[i] == "ꦴ") trans = trans.replaceChar3(j, "ā"); + else if (str[i] == "ꦶ") trans = trans.replaceChar3(j, "ai"); + else if (str[i] == "ꦸ") trans = trans.replaceChar3(j, "au"); + else if (str[i] == "ꦺ") trans = trans.replaceChar3(j, "ae"); + else if (str[i] == "ꦼ") trans = trans.replaceChar3(j, "aě"); + } else if (i > 2 && str[i - 1] == "ꦲ") { + //-h- + if (str[i] == "ꦴ") trans = trans.replaceChar2(j, "ā"); + else if (str[i] == "ꦶ") trans = trans.replaceChar2(j, "i"); + else if (str[i] == "ꦸ") trans = trans.replaceChar2(j, "u"); + else if (str[i] == "ꦺ") trans = trans.replaceChar2(j, "e"); + else if (str[i] == "ꦼ") trans = trans.replaceChar2(j, "ě"); + } /**/ else if (i > 0 && str[i] == "ꦴ" && str[i - 1] == "ꦺ") { + //-o //2 aksara -> 1 huruf + trans = trans.replaceChar2(j, "o"); + } else if (i > 0 && str[i] == "ꦴ" && str[i - 1] == "ꦻ") { + //-au //2 aksara -> 2 huruf + trans = trans.replaceChar3(j, "au"); + } else if (str[i] == "ꦴ") { + //-aa + trans = trans.replaceChar1(j, "aa"); + j++; + } else if ( + i > 0 && + (str[i] == "ꦶ" || str[i] == "ꦸ" || str[i] == "ꦺ" || str[i] == "ꦼ") && + (str[i - 1] == "ꦄ" || + str[i - 1] == "ꦌ" || + str[i - 1] == "ꦆ" || + str[i - 1] == "ꦎ" || + str[i - 1] == "ꦈ") + ) { + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j++; + } else { + trans = trans.replaceChar2(j, regexp_file[str[i]]); + } + } else if ( + str[i] == "ꦽ" || + str[i] == "ꦾ" || + str[i] == "ꦿ" || + str[i] == "ꦷ" || + str[i] == "ꦹ" || + str[i] == "ꦻ" || + str[i] == "ꦇ" || + str[i] == "ꦍ" + ) { + //1 aksara -> 2 huruf + trans = trans.replaceChar2(j, regexp_file[str[i]]); + j++; + } else if (str[i] == "꦳") { + //2 aksara -> 2 huruf + if (i > 0 && str[i - 1] == "ꦗ") { + if (i > 1 && str[i - 2] == "꧊") { + trans = trans.replaceChar3(j, "Za"); + } else { + trans = trans.replaceChar3(j, "za"); + } + } else if (i > 0 && str[i - 1] == "ꦥ") { + if (i > 1 && str[i - 2] == "꧊") { + trans = trans.replaceChar3(j, "Fa"); + } else { + trans = trans.replaceChar3(j, "fa"); + } + } else if (i > 0 && str[i - 1] == "ꦮ") { + if (i > 1 && str[i - 2] == "꧊") { + trans = trans.replaceChar3(j, "Va"); + } else { + trans = trans.replaceChar3(j, "va"); + } + } // catatan, "va" biasanya ditulis sama dengan "fa" (dengan pa+cecak telu), variannya adalah wa+cecak telu. + else { + trans = trans.replaceChar2(j, regexp_file[str[i]]); + } + } else if (str[i] == "꧀") { + trans = trans.replaceChar2(j, regexp_file[str[i]]); + } else if ( + i > 1 && + str[i] == "ꦕ" && + str[i - 1] == "꧀" && + str[i - 2] == "ꦚ" + ) { + //nyj & nyc + trans = trans.replaceChar2(j - 2, "nc"); + } else if ( + i > 1 && + str[i] == "ꦗ" && + str[i - 1] == "꧀" && + str[i - 2] == "ꦚ" + ) { + //nyj & nyc + trans = trans.replaceChar2(j - 2, "nj"); + } else if ( + str[i] == "ꦏ" || + str[i] == "ꦐ" || + str[i] == "ꦑ" || + str[i] == "ꦒ" || + str[i] == "ꦓ" || + str[i] == "ꦕ" || + str[i] == "ꦖ" || + str[i] == "ꦗ" || + str[i] == "ꦙ" || + str[i] == "ꦟ" || + str[i] == "ꦠ" || + str[i] == "ꦡ" || + str[i] == "ꦢ" || + str[i] == "ꦣ" || + str[i] == "ꦤ" || + str[i] == "ꦥ" || + str[i] == "ꦦ" || + str[i] == "ꦧ" || + str[i] == "ꦨ" || + str[i] == "ꦩ" || + str[i] == "ꦪ" || + str[i] == "ꦫ" || + str[i] == "ꦬ" || + str[i] == "ꦭ" || + str[i] == "ꦮ" || + str[i] == "ꦯ" || + str[i] == "ꦱ" || + str[i] == "ꦉ" || + str[i] == "ꦊ" || + str[i] == "ꦁ" || + str[i] == "ꦲ" + ) { + if (i > 0 && str[i - 1] == "꧊") { + if (str[i] == "ꦐ") { + trans = trans.replaceChar1(j, "Qa"); + j += 2; + } else if (str[i] == "ꦧ" || str[i] == "ꦨ") { + trans = trans.replaceChar1(j, "Ba"); + j += 2; + } else if (str[i] == "ꦕ" || str[i] == "ꦖ") { + trans = trans.replaceChar1(j, "Ca"); + j += 2; + } else if (str[i] == "ꦢ" || str[i] == "ꦣ") { + trans = trans.replaceChar1(j, "Da"); + j += 2; + } else if (str[i] == "ꦒ" || str[i] == "ꦓ") { + trans = trans.replaceChar1(j, "Ga"); + j += 2; + } else if (str[i] == "ꦲ") { + if ( + i > 0 && + (str[i - 1] == "ꦼ" || + str[i - 1] == "ꦺ" || + str[i - 1] == "ꦶ" || + str[i - 1] == "ꦴ" || + str[i - 1] == "ꦸ" || + str[i - 1] == "ꦄ" || + str[i - 1] == "ꦌ" || + str[i - 1] == "ꦆ" || + str[i - 1] == "ꦎ" || + str[i - 1] == "ꦈ" || + str[i - 1] == "ꦿ" || + str[i - 1] == "ꦾ" || + str[i - 1] == "ꦽ") + ) { + trans = trans.replaceChar1(j, "h" + regexp_file[str[i]]); + j += 2; + } + if (i > 0 && str[i - 1] == "꧊") { + trans = trans.replaceChar1(j, "H" + regexp_file[str[i]]); + j += 2; + } else { + trans = trans.replaceChar1(j, "@" + regexp_file[str[i]]); + j += 2; + } + } else if (str[i] == "ꦗ" || str[i] == "ꦙ") { + trans = trans.replaceChar1(j, "Ja"); + j += 2; + } else if (str[i] == "ꦏ" || str[i] == "ꦑ") { + trans = trans.replaceChar1(j, "Ka"); + j += 2; + } else if (str[i] == "ꦭ") { + trans = trans.replaceChar1(j, "La"); + j += 2; + } else if (str[i] == "ꦩ") { + trans = trans.replaceChar1(j, "Ma"); + j += 2; + } else if (str[i] == "ꦤ" || str[i] == "ꦟ") { + trans = trans.replaceChar1(j, "Na"); + j += 2; + } else if (str[i] == "ꦥ" || str[i] == "ꦦ") { + trans = trans.replaceChar1(j, "Pa"); + j += 2; + } else if (str[i] == "ꦫ" || str[i] == "ꦬ") { + trans = trans.replaceChar1(j, "Ra"); + j += 2; + } else if (str[i] == "ꦱ" || str[i] == "ꦯ") { + trans = trans.replaceChar1(j, "Sa"); + j += 2; + } else if (str[i] == "ꦠ" || str[i] == "ꦡ") { + trans = trans.replaceChar1(j, "Ta"); + j += 2; + } else if (str[i] == "ꦮ") { + trans = trans.replaceChar1(j, "Wa"); + j += 2; + } else if (str[i] == "ꦪ") { + trans = trans.replaceChar1(j, "Ya"); + j += 2; + } else { + trans.replaceChar1(j, regexp_file[str[i]]); + j += 3; + } + } else if ( + str[i] == "ꦑ" || + str[i] == "ꦓ" || + str[i] == "ꦖ" || + str[i] == "ꦙ" || + str[i] == "ꦡ" || + str[i] == "ꦣ" || + str[i] == "ꦦ" || + str[i] == "ꦨ" || + str[i] == "ꦯ" + ) { + //bha, cha, dha, dll. + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j += 3; + } else if ( + str[i] == "ꦲ" && + (i == 0 || + [ + " ", + "​", + "꧀", + "꦳", + "ꦴ", + "ꦶ", + "ꦷ", + "ꦸ", + "ꦹ", + "ꦺ", + "ꦻ", + "ꦼ", + "ꦽ", + "ꦾ", + "ꦿ", + ].indexOf(str[i - 1]) >= 0) + ) { + //ha, preceeded by space or zws or open vowel + trans = trans.replaceChar1(j, "_a"); + j += 2; + } else { + //ba, ca, da, dll. + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j += 2; + } + } else if (str[i] == "ꦰ") { + //ṣa + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j += 2; + } else if ( + str[i] == "ꦔ" || + str[i] == "ꦘ" || + str[i] == "ꦚ" || + str[i] == "ꦛ" || + str[i] == "ꦜ" || + str[i] == "ꦝ" || + str[i] == "ꦞ" || + str[i] == "ꦋ" + ) { + if (i > 0 && str[i - 1] == "꧊") { + if (str[i] == "ꦔ") { + trans = trans.replaceChar1(j, "Nga"); + j += 3; + } else if (str[i] == "ꦚ" || str[i] == "ꦘ") { + trans = trans.replaceChar1(j, "Nya"); + j += 3; + } else if (str[i] == "ꦛ" || str[i] == "ꦜ") { + trans = trans.replaceChar1(j, "Tha"); + j += 3; + } else if (str[i] == "ꦝ" || str[i] == "ꦞ") { + trans = trans.replaceChar1(j, "Dha"); + j += 3; + } else { + trans.replaceChar1(j, regexp_file[str[i]]); + j += 3; + } + } else { + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j += 3; + } + } else if (str[i] == "꧊") { + // penanda nama diri -- made up for Latin back-compat + trans = trans.replaceChar1(j, ""); + } else if (str[i] == " ") { + trans = trans.replaceChar1(j, " "); + j++; + } else { + trans = trans.replaceChar1(j, regexp_file[str[i]]); + j++; + } + } + } + + let sentence = trans.split(" "); + sentence = sentence.map((word) => + removeInvisibleChars(word.trim().replace("_", "")), + ); + + return sentence.join(" "); +} diff --git a/src/env.ts b/src/env.ts deleted file mode 100644 index 7165f56..0000000 --- a/src/env.ts +++ /dev/null @@ -1,11 +0,0 @@ -import "dotenv/config"; - -// set the default gemini api key -if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) { - process.env.GOOGLE_GENERATIVE_AI_API_KEY = process.env.GEMINI_API_KEY ?? ""; -} - -// set the default gemini model -if (!process.env.GOOGLE_GENERATIVE_AI_MODEL) { - process.env.GOOGLE_GENERATIVE_AI_MODEL = process.env.GEMINI_MODEL ?? ""; -} diff --git a/src/global.ts b/src/global.ts new file mode 100644 index 0000000..0958028 --- /dev/null +++ b/src/global.ts @@ -0,0 +1,32 @@ +declare global { + interface String { + capitalize(): string; + replaceChar1(index: number, character: string): string; + replaceChar2(index: number, character: string): string; + replaceChar3(index: number, character: string): string; + } +} + +if (!String.prototype.capitalize) { + String.prototype.capitalize = function () { + return this.charAt(0).toUpperCase() + this.slice(1); + }; +} + +if (!String.prototype.replaceChar1) { + String.prototype.replaceChar1 = function (index: number, character: string) { + return this.substring(0, 0 + index) + character; + }; +} + +if (!String.prototype.replaceChar2) { + String.prototype.replaceChar2 = function (index: number, character: string) { + return this.substring(0, 0 + index - 1) + character; + }; +} + +if (!String.prototype.replaceChar3) { + String.prototype.replaceChar3 = function (index: number, character: string) { + return this.substring(0, 0 + index - 2) + character; + }; +} diff --git a/src/types.ts b/src/types.ts index f6d2e16..7e7ae71 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,26 +1,17 @@ -import { FinishReason, CallWarning } from "ai"; +export type Shift = { CoreSound: string; len: number }; -// the response from the generateText function. -export type TextResponse = { - // the model ID. - model: string; +export type Character = { [key: string]: string }; - // the generated text. - text: string; +export type Consonant = Character; - // the reason why the generation finished. - readonly finishReason: FinishReason; +export type Matra = Character; - // the token usage of the generated text. - readonly usage: TokenUsage; +export type SpecialSound = Character; - // the warnings that occurred during the generation. - warnings: CallWarning[] | undefined; -}; +export type Digit = Character; -// the token usage of the generated text. -export type TokenUsage = { - promptTokens: number; - completionTokens: number; - totalTokens: number; +export type TranslateConfiguration = { + typeMode: boolean; + withMurda: boolean; + withSpace: boolean; }; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..202e356 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,120 @@ +import { Shift } from "./types"; + +/** + * Trim the string. + * @param {str} str + * @returns {string} + */ +export function trim(str: string): string { + str = str || ""; + return str.replace(/^\s*|\s*$/g, "").replace(/\s+/g, " "); +} + +/** + * Find the character in the string. + * @param {string} str + * @param {string} toFind + * @returns {boolean} + */ +export function find(str: string, toFind: string): boolean { + for (let i = 0; i < str.length; i++) if (str[i] === toFind) return true; + return false; +} + +/** + * Check if the character is a number digit. + * @param {string} a + * @returns {boolean} + */ +export function isDigit(a: string): boolean { + const str = "0123456789"; + return find(str, a); +} + +/** + * Check if the character is a punctuation. + * @param {string} a + * @returns {boolean} + */ +export function isPunct(a: string): boolean { + const str = ',.> { + it("hasAksara should return true if aksara exist", () => { + expect(hasAksara(aksara)).to.be.true; + }); + + it("should translate latin to javanese script correctly", () => { + expect(translate(latin)).to.equal(aksara); + }); + + it("should translate javanese script to latin correctly", () => { + expect(translate(aksara)).to.equal(latin); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index c53bb0c..1b8829e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,22 +2,21 @@ "compilerOptions": { "declaration": true, "esModuleInterop": true, - "jsx": "react", "lib": ["esnext", "dom"], "module": "NodeNext", "moduleResolution": "nodenext", + "outDir": "./dist", + "target": "ES2019", "noFallthroughCasesInSwitch": true, "noImplicitReturns": false, "noUnusedLocals": true, "noUnusedParameters": true, "strictBindCallApply": true, - "outDir": "./dist", "strict": true, - "target": "ES2019", "noEmitOnError": true, "downlevelIteration": true, "incremental": true }, - "include": ["./src/**/*", "env.d.ts"], + "include": ["./src/**/*", "./tests/**/*", "env.d.ts"], "exclude": ["./**/dist", "examples", "node_modules"] }