diff --git a/.terserrc b/.terserrc new file mode 100644 index 0000000..9cbf56d --- /dev/null +++ b/.terserrc @@ -0,0 +1,7 @@ +{ + "mangle": { + "properties": { + "regex": "^CAPITAL|^SMALL|^PUNCT|^DIA" + } + } +} diff --git a/README.md b/README.md index 153f24f..032e3f4 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,9 @@ fromType: KeyType, settings: Preset | MixedPreset | IConversionOptions = {} ``` -The **`fromType`** parameter can be set to `BETA_CODE | GREEK | TRANSLITERATION` (e.g. `KeyType.GREEK`). +**`fromType`** can be set to `BETA_CODE | TLG_BETA_CODE | GREEK | TRANSLITERATION` (e.g. `KeyType.GREEK`). -The **`settings`** parameter can be filled with: +**`settings`** can be filled with: 1. a `Preset`; 2. a user-defined `IConversionOptions` object; 3. a preset mixed with user-defined conversion options (`[Preset, IConversionOptions]`). @@ -98,6 +98,7 @@ The available presets are: | Preset | Description | | ------ | ----------- | | [**`MODERN_BC`**](https://github.com/antoineboquet/greek-conversion/wiki#Modern-beta-code) | `greek-conversion`'s own modernized style | +| [**`TLG`**](https://github.com/antoineboquet/greek-conversion/wiki#TLG) | Thesaurus Linguae Graecae | 2. **For transliteration:** @@ -105,35 +106,48 @@ The available presets are: | ------ | ----------- | | [**`ALA_LC`**](https://github.com/antoineboquet/greek-conversion/wiki#ALA-LC) | American Library Association – Library of Congress | | [**`BNF`**](https://github.com/antoineboquet/greek-conversion/wiki#BNF) | Bibliothèque nationale de France | +| [**`ISO`**](https://github.com/antoineboquet/greek-conversion/wiki#iso-843-1997) | ISO 843 (1997) — type 1 (transliteration) | | [**`SBL`**](https://github.com/antoineboquet/greek-conversion/wiki#SBL) | Society of Biblical Literature | ### Conversion options The **`IConversionOptions`** interface provides the following controls over the conversion process: + ```ts -removeDiacritics?: boolean, // remove diacritics, except those that represent letters +removeDiacritics?: boolean, // remove diacritics, except those that represent letters + +removeExtraWhitespace?: boolean, // remove potential extra whitespace -removeExtraWhitespace?: boolean, // remove potential extra whitespace +betaCodeStyle?: { + useTLGStyle?: boolean // use the Thesaurus Linguae Graecae style. e.g. 'Ἄϊδι' → '*)/AI+DI' +}, -setGreekStyle?: { - disableBetaVariant?: boolean, // disable the typographic variant 'ϐ' [U+03D0] - useLunateSigma?: boolean // use the lunate sigma rather than the regular form +greekStyle?: { + disableBetaVariant?: boolean, // disable the typographic variant 'ϐ' [U+03D0] + useGreekQuestionMark?: boolean, // use greek question marks ';' [U+037E] rather than regular semicolons + useLunateSigma?: boolean // use lunate sigmas 'ϲ, Ϲ' rather than regular sigmas }, -setTransliterationStyle?: { - useCxOverMacron?: boolean, // use a circumflex rather than a macron for 'η', 'ω', etc - xi_ks?: boolean, // transliterate 'ξ' as 'ks' (defaults to: 'x') - rho_rh?: boolean, // transliterate 'ρ' as 'rh' even if it doesn't have a rough breathing - chi_kh?: boolean, // transliterate 'χ' as 'kh' (defaults to: 'ch') - upsilon_y?: boolean, // transliterate 'υ' as 'y' (defaults to: 'u') - lunatesigma_s?: boolean // transliterate 'ϲ' [U+03F2] as 's' (defaults to: 'c') +transliterationStyle?: { + setCoronisStyle?: Coronis, // set Coronis enum to PSILI | APOSTOPHE | NO (defaults to: PSILI) + useCxOverMacron?: boolean, // use a circumflex rather than a macron for 'η', 'ω', etc + beta_v?: boolean, // transliterate 'β' as 'v' (defaults to: 'b') + eta_i?: boolean, // transliterate 'η' as 'ī' (defaults to: 'ē') + xi_ks?: boolean, // transliterate 'ξ' as 'ks' (defaults to: 'x') + rho_rh?: boolean, // transliterate 'ρ' as 'rh' even if it doesn't have a rough breathing + phi_f?: boolean, // transliterate 'φ' as 'f' (defaults to: 'ph') + chi_kh?: boolean, // transliterate 'χ' as 'kh' (defaults to: 'ch') + upsilon_y?: boolean, // transliterate 'υ' as 'y' (defaults to: 'u') + lunatesigma_s?: boolean // transliterate 'ϲ' [U+03F2] as 's' (defaults to: 'c') }, -useAdditionalChars?: // extend the default mapping with additional chars - AdditionalChar[] | // (use AdditionalChar.ALL to enable the whole set) +additionalChars?: // extend the default mapping with additional chars + AdditionalChar[] | // (use AdditionalChar.ALL to enable the whole set) AdditionalChar ``` +A more detailed description of these conversion options is available on this [page](https://github.com/antoineboquet/greek-conversion/wiki#conversion-options). + ### Examples #### Basic examples @@ -141,6 +155,7 @@ useAdditionalChars?: // extend the default mapping with additional c ```ts toBetaCode('ανθρωπος', KeyType.GREEK) // anqrwpos toGreek('A)/i+da', KeyType.BETA_CODE) // Ἄϊδα +toGreek('*)/AI+DA', KeyType.TLG_BETA_CODE) // Ἄϊδα toTransliteration('ἄϋλος', KeyType.GREEK, { removeDiacritics: true }) // aulos ``` @@ -164,7 +179,7 @@ toTransliteration('ἀΰπνους νύκτας ἴαυον', KeyType.GREEK, [ ```ts const style = { - setGreekStyle: { + greekStyle: { useLunateSigma: true } } @@ -177,7 +192,7 @@ toGreek('ICHTHUS ZŌNTŌN', KeyType.TRANSLITERATION, style) // ἸΧΘΥϹ ΖΩ ```ts const style = { - setTransliterationStyle: { + transliterationStyle: { useCxOverMacron: true, chi_kh: true } @@ -187,6 +202,19 @@ toTransliteration('τέχνη', KeyType.GREEK) // téchnē toTransliteration('τέχνη', KeyType.GREEK, style) // tékhnê ``` +#### Self conversion (reflect settings) + +```ts +toBetaCode('O(pli/ths', KeyType.BETA_CODE, Preset.TLG) // *(OPLI/THS +toBetaCode('*(OPLI/THS', KeyType.TLG_BETA_CODE) // O(pli/ths + +const grStyle = { greekStyle: { useLunateSigma: true } } +toGreek('ἅγιος', KeyType.GREEK, grStyle) // ἅγιοϲ + +const trStyle = { transliterationStyle: { lunatesigma_s: true } } +toTransliteration('Cōkrátēc', KeyType.TRANSLITERATION, trStyle) // Sōkrátēs +``` + ## OOP style ### Summary diff --git a/examples/playground.html b/examples/playground.html index f4b84cb..107ab45 100644 --- a/examples/playground.html +++ b/examples/playground.html @@ -11,7 +11,7 @@ main { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1.5rem; margin-bottom: 1.5rem; } @@ -24,7 +24,7 @@ #options-area { margin-top: 1.5rem; - padding: 0.75rem; + padding: .75rem; background: var(--accent-bg); border-radius: var(--standard-border-radius); } @@ -33,10 +33,10 @@ z-index: 999; position: absolute; margin-left: 5em; - padding: 0.75em; - font-size: 0.75em; + padding: .75em; + font-size: .75em; box-shadow: rgba(50, 50, 93, 0.25) 0px 30px 60px -12px, - rgba(0, 0, 0, 0.3) 0px 18px 36px -18px; + rgba(0, 0, 0, .3) 0px 18px 36px -18px; } label:has(input[type='checkbox']:disabled) { @@ -52,12 +52,26 @@ } fieldset { - padding: 0.5rem; + padding: .125em .5em; + } + + fieldset:last-child { + margin-bottom: 0; + } + + legend { + font-variant: small-caps; + font-size: .75em; + font-weight: bold; } fieldset label { display: inline-block; - margin: 0.25rem 0.75rem; + margin: .25rem .75rem; + } + + fieldset select { + margin: auto; } @@ -70,7 +84,7 @@

Playground example

-
+
Playground example Input type:
- +
Common options
@@ -267,18 +281,50 @@

Playground example

Disable beta variant + + + +
+ Beta code style + +
+
Transliteration style + + + + + + + +
-
Conversion results will be displayed here...
+

 
diff --git a/examples/searchBar.html b/examples/searchBar.html index 6f1422f..e6aa8ec 100644 --- a/examples/searchBar.html +++ b/examples/searchBar.html @@ -241,7 +241,7 @@

Search bar example

const conversionOptions = { removeDiacritics: true, - setTransliterationStyle: { + transliterationStyle: { useCxOverMacron: true } }; diff --git a/examples/textTransliteration.html b/examples/textTransliteration.html index 6fa4cc7..fb21c64 100644 --- a/examples/textTransliteration.html +++ b/examples/textTransliteration.html @@ -65,6 +65,7 @@

Text transliteration example

+ diff --git a/package-lock.json b/package-lock.json index 739ee0e..ab6d0c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "greek-conversion", - "version": "0.12.3", + "version": "0.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "greek-conversion", - "version": "0.12.3", + "version": "0.13.0", "license": "agpl-3.0", "devDependencies": { "@parcel/packager-ts": "^2.11.0", @@ -15,6 +15,9 @@ "jest": "^29.7.0", "parcel": "^2.11.0", "ts-jest": "^29.1.2" + }, + "engines": { + "node": ">= 18" } }, "node_modules/@ampproject/remapping": { @@ -2813,9 +2816,9 @@ } }, "node_modules/@swc/core": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.4.tgz", - "integrity": "sha512-P88AHGWM8xPY3Tjj5360V6vqKCS5UfsyffPJVnr7BKSr45rlG4/pjEGGmFYQjg6ztgPyrGLYz1jSyzajTqTVIA==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.6.tgz", + "integrity": "sha512-A7iK9+1qzTCIuc3IYcS8gPHCm9bZVKUJrfNnwveZYyo6OFp3jLno4WOM2yBy5uqedgYATEiWgBYHKq37KrU6IA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -2830,16 +2833,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.4", - "@swc/core-darwin-x64": "1.4.4", - "@swc/core-linux-arm-gnueabihf": "1.4.4", - "@swc/core-linux-arm64-gnu": "1.4.4", - "@swc/core-linux-arm64-musl": "1.4.4", - "@swc/core-linux-x64-gnu": "1.4.4", - "@swc/core-linux-x64-musl": "1.4.4", - "@swc/core-win32-arm64-msvc": "1.4.4", - "@swc/core-win32-ia32-msvc": "1.4.4", - "@swc/core-win32-x64-msvc": "1.4.4" + "@swc/core-darwin-arm64": "1.4.6", + "@swc/core-darwin-x64": "1.4.6", + "@swc/core-linux-arm-gnueabihf": "1.4.6", + "@swc/core-linux-arm64-gnu": "1.4.6", + "@swc/core-linux-arm64-musl": "1.4.6", + "@swc/core-linux-x64-gnu": "1.4.6", + "@swc/core-linux-x64-musl": "1.4.6", + "@swc/core-win32-arm64-msvc": "1.4.6", + "@swc/core-win32-ia32-msvc": "1.4.6", + "@swc/core-win32-x64-msvc": "1.4.6" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -2851,9 +2854,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.4.tgz", - "integrity": "sha512-goSHS8yvDgha93RHIV2Vn50neYasqbc4K1g/nKOV6T8kiKVv4w/rmqNJu9Aa0mPGVJtjcr0NvX6bBwE0T4HIzg==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.6.tgz", + "integrity": "sha512-bpggpx/BfLFyy48aUKq1PsNUxb7J6CINlpAUk0V4yXfmGnpZH80Gp1pM3GkFDQyCfq7L7IpjPrIjWQwCrL4hYw==", "cpu": [ "arm64" ], @@ -2867,9 +2870,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.4.tgz", - "integrity": "sha512-PLfgL355qsl5c5kUPsFGITgVXoaqjp9sCd0Y5Z5uN7RtSOvwIX28e23eCxj02dOr7OBr8sq6qBlEMDV03T24Iw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.6.tgz", + "integrity": "sha512-vJn+/ZuBTg+vtNkcmgZdH6FQpa0hFVdnB9bAeqYwKkyqP15zaPe6jfC+qL2y/cIeC7ASvHXEKrnCZgBLxfVQ9w==", "cpu": [ "x64" ], @@ -2883,9 +2886,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.4.tgz", - "integrity": "sha512-BVEZVOGnaZvEcHm//KyYzhte46vdF67wLVtmQEXPAlrkRgZ3b/JSySeLXqeocAcOANWb1/SPHlEmPK5azP+JvQ==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.6.tgz", + "integrity": "sha512-hEmYcB/9XBAl02MtuVHszhNjQpjBzhk/NFulnU33tBMbNZpy2TN5yTsitezMq090QXdDz8sKIALApDyg07ZR8g==", "cpu": [ "arm" ], @@ -2899,9 +2902,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.4.tgz", - "integrity": "sha512-ZbOJfVbCjVMKdfvvJDOTpa3tGqU6tfxng1CDjA62RUcqa7sRbovrjSiw6mq5/4EoOF4zK8CtPIG+TlxKPapnuw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.6.tgz", + "integrity": "sha512-/UCYIVoGpm2YVvGHZM2QOA3dexa28BjcpLAIYnoCbgH5f7ulDhE8FAIO/9pasj+kixDBsdqewHfsNXFYlgGJjQ==", "cpu": [ "arm64" ], @@ -2915,9 +2918,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.4.tgz", - "integrity": "sha512-+Gjo1W4tY/4kgEe5h22iuCWkpKcPMccXwYaSLNvgBCBQADB0zKFfF0lNf7y6U+81NFEjhRsdwXMsRGZtgTpUrg==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.6.tgz", + "integrity": "sha512-LGQsKJ8MA9zZ8xHCkbGkcPSmpkZL2O7drvwsGKynyCttHhpwVjj9lguhD4DWU3+FWIsjvho5Vu0Ggei8OYi/Lw==", "cpu": [ "arm64" ], @@ -2931,9 +2934,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.4.tgz", - "integrity": "sha512-PR/VbGm0LEkhpm5qClovZWhE/jYoQSyIeyPh8XY39uUI1u2yEfuz5UCW2sJJIWOvNiAfu7+TjW+9H/I7zBBDJA==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.6.tgz", + "integrity": "sha512-10JL2nLIreMQDKvq2TECnQe5fCuoqBHu1yW8aChqgHUyg9d7gfZX/kppUsuimqcgRBnS0AjTDAA+JF6UsG/2Yg==", "cpu": [ "x64" ], @@ -2947,9 +2950,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.4.tgz", - "integrity": "sha512-poT9zub4CyVcH1cxwGdrGiZD3urfOaYs/Kd52ve3ymPPeQZq7qQwKqAB/9NxoSiJDaSzJv5OwTEfgaBYCyw0iw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.6.tgz", + "integrity": "sha512-EGyjFVzVY6Do89x8sfah7I3cuP4MwtwzmA6OlfD/KASqfCFf5eIaEBMbajgR41bVfMV7lK72lwAIea5xEyq1AQ==", "cpu": [ "x64" ], @@ -2963,9 +2966,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.4.tgz", - "integrity": "sha512-29V5/fBd6XXFb7J/ri9ZeSS/GTqXfSWa3BiE0zTNbASpQbEXf+YPYiAtO6c1HqNyQobKB9ni+w7sa8KkAGhHXw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.6.tgz", + "integrity": "sha512-gfW9AuXvwSyK07Vb8Y8E9m2oJZk21WqcD+X4BZhkbKB0TCZK0zk1j/HpS2UFlr1JB2zPKPpSWLU3ll0GEHRG2A==", "cpu": [ "arm64" ], @@ -2979,9 +2982,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.4.tgz", - "integrity": "sha512-2lKEGEjpBOq0z4Nk0tFP9wxVwxgz7FonmjCkzJ95GBb5KNvMrgQQrGNGX6L0hoBo/a1kE752I6V5pOaMyIq5xQ==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.6.tgz", + "integrity": "sha512-ZuQm81FhhvNVYtVb9GfZ+Du6e7fZlkisWvuCeBeRiyseNt1tcrQ8J3V67jD2nxje8CVXrwG3oUIbPcybv2rxfQ==", "cpu": [ "ia32" ], @@ -2995,9 +2998,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.4.tgz", - "integrity": "sha512-xuN0oJhAewga8jNJkT5Wx25RPVnIEMZCYf4irqA5jiK6GckOdcXB8jvEJhggOxnJSW8RDsAtY5q+zw5kNkU+eA==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.6.tgz", + "integrity": "sha512-UagPb7w5V0uzWSjrXwOavGa7s9iv3wrVdEgWy+/inm0OwY4lj3zpK9qDnMWAwYLuFwkI3UG4Q3dH8wD+CUUcjw==", "cpu": [ "x64" ], @@ -3125,9 +3128,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "20.11.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz", + "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -3458,9 +3461,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001594", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz", - "integrity": "sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==", + "version": "1.0.30001596", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz", + "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==", "dev": true, "funding": [ { @@ -3986,9 +3989,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.692", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz", - "integrity": "sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==", + "version": "1.4.699", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", + "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==", "dev": true }, "node_modules/emittery": { @@ -6526,9 +6529,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "peer": true, "bin": { diff --git a/package.json b/package.json index 68b0f2b..2c3a9cc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "greek-conversion", "author": "Antoine Boquet", "license": "agpl-3.0", - "version": "0.12.3", + "version": "0.13.0", "description": "A small library to convert a polytonic greek string from/into various representations.", "keywords": [ "greek", @@ -24,19 +24,35 @@ "type": "git", "url": "git+https://github.com/antoineboquet/greek-conversion.git" }, + "type": "module", + "exports": { + ".": { + "import": "./dist/greekConversion.min.js", + "require": "./dist/greekConversion.min.cjs" + } + }, "source": "./src/index.ts", "types": "./dist/greekConversion.d.ts", - "main": "./dist/greekConversion.cjs.js", - "module": "./dist/greekConversion.esm.js", - "unpkg": "./dist/greekConversion.min.js", + "main": "./dist/greekConversion.min.js", + "commonjs": "./dist/greekConversion.min.cjs", "targets": { - "unpkg": { + "main": { + "context": "browser", "isLibrary": true, "optimize": true, "outputFormat": "esmodule" + }, + "commonjs": { + "context": "node", + "isLibrary": true, + "optimize": true, + "outputFormat": "commonjs" } }, "browserslist": "defaults", + "engines": { + "node": ">= 18" + }, "files": [ "/dist" ], diff --git a/src/Mapping.ts b/src/Mapping.ts index 2045d37..40a937b 100644 --- a/src/Mapping.ts +++ b/src/Mapping.ts @@ -4,7 +4,7 @@ import { IMappingProperty, ITransliterationStyle } from './interfaces'; -import { sanitizeRegExpString } from './utils'; +import { normalizeGreek, sanitizeRegExpString } from './utils'; export const GRAVE_ACCENT = '\u0300'; export const ACUTE_ACCENT = '\u0301'; @@ -21,6 +21,7 @@ export const CEDILLA = '\u0327'; export const IOTA_SUBSCRIPT = '\u0345'; export const ANO_TELEIA = '\u0387'; export const MIDDLE_DOT = '\u00B7'; +export const RIGHT_SINGLE_QUOTATION_MARK = '\u2019'; export const GREEK_QUESTION_MARK = '\u037E'; export const GREEK_BETA_SYMBOL = '\u03D0'; export const CAPITAL_LUNATE_SIGMA = '\u03F9'; @@ -130,409 +131,459 @@ const ADDITIONAL_CHARS_VALUES = (): { }*/ }); -export class Mapping { - private CAPITAL_ALPHA: IMappingProperty = { +const CAPITAL_LETTERS = (): { + [k in string]: IMappingProperty; +} => ({ + CAPITAL_ALPHA: { gr: 'Α', bc: 'A', tr: 'A' - }; - private CAPITAL_BETA: IMappingProperty = { + }, + CAPITAL_BETA: { gr: 'Β', bc: 'B', tr: 'B' - }; - private CAPITAL_GAMMA: IMappingProperty = { + }, + CAPITAL_GAMMA: { gr: 'Γ', bc: 'G', tr: 'G' - }; - private CAPITAL_DELTA: IMappingProperty = { + }, + CAPITAL_DELTA: { gr: 'Δ', bc: 'D', tr: 'D' - }; - private CAPITAL_EPSILON: IMappingProperty = { + }, + CAPITAL_EPSILON: { gr: 'Ε', bc: 'E', tr: 'E' - }; - private CAPITAL_ZETA: IMappingProperty = { + }, + CAPITAL_ZETA: { gr: 'Ζ', bc: 'Z', tr: 'Z' - }; - private CAPITAL_ETA: IMappingProperty = { + }, + CAPITAL_ETA: { gr: 'Η', bc: 'H', tr: 'Ē' - }; - private CAPITAL_THETA: IMappingProperty = { + }, + CAPITAL_THETA: { gr: 'Θ', bc: 'Q', tr: 'Th' - }; - private CAPITAL_IOTA: IMappingProperty = { + }, + CAPITAL_IOTA: { gr: 'Ι', bc: 'I', tr: 'I' - }; - private CAPITAL_KAPPA: IMappingProperty = { + }, + CAPITAL_KAPPA: { gr: 'Κ', bc: 'K', tr: 'K' - }; - private CAPITAL_LAMBDA: IMappingProperty = { + }, + CAPITAL_LAMBDA: { gr: 'Λ', bc: 'L', tr: 'L' - }; - private CAPITAL_MU: IMappingProperty = { + }, + CAPITAL_MU: { gr: 'Μ', bc: 'M', tr: 'M' - }; - private CAPITAL_NU: IMappingProperty = { + }, + CAPITAL_NU: { gr: 'Ν', bc: 'N', tr: 'N' - }; - private CAPITAL_XI: IMappingProperty = { + }, + CAPITAL_XI: { gr: 'Ξ', bc: 'C', tr: 'X' - }; - private CAPITAL_OMICRON: IMappingProperty = { + }, + CAPITAL_OMICRON: { gr: 'Ο', bc: 'O', tr: 'O' - }; - private CAPITAL_PI: IMappingProperty = { + }, + CAPITAL_PI: { gr: 'Π', bc: 'P', tr: 'P' - }; - private CAPITAL_RHO: IMappingProperty = { + }, + CAPITAL_RHO: { gr: 'Ρ', bc: 'R', tr: 'R' - }; - private CAPITAL_SIGMA: IMappingProperty = { + }, + CAPITAL_SIGMA: { gr: 'Σ', bc: 'S', tr: 'S' - }; - private CAPITAL_TAU: IMappingProperty = { + }, + CAPITAL_TAU: { gr: 'Τ', bc: 'T', tr: 'T' - }; - private CAPITAL_UPSILON: IMappingProperty = { + }, + CAPITAL_UPSILON: { gr: 'Υ', bc: 'U', tr: 'U' - }; - private CAPITAL_ALT_UPSILON: IMappingProperty; - private CAPITAL_PHI: IMappingProperty = { + }, + CAPITAL_PHI: { gr: 'Φ', bc: 'F', tr: 'Ph' - }; - private CAPITAL_CHI: IMappingProperty = { + }, + CAPITAL_CHI: { gr: 'Χ', bc: 'X', tr: 'Ch' - }; - private CAPITAL_PSI: IMappingProperty = { + }, + CAPITAL_PSI: { gr: 'Ψ', bc: 'Y', tr: 'Ps' - }; - private CAPITAL_OMEGA: IMappingProperty = { + }, + CAPITAL_OMEGA: { gr: 'Ω', bc: 'W', tr: 'Ō' - }; - private CAPITAL_DIGAMMA: IMappingProperty; - private CAPITAL_YOT: IMappingProperty; - private CAPITAL_LUNATE_SIGMA: IMappingProperty; - private CAPITAL_STIGMA: IMappingProperty; - private CAPITAL_KOPPA: IMappingProperty; - private CAPITAL_ARCHAIC_KOPPA: IMappingProperty; - private CAPITAL_SAMPI: IMappingProperty; - private CAPITAL_SAN: IMappingProperty; - private SMALL_ALPHA: IMappingProperty = { + }, + CAPITAL_ALT_UPSILON: {} as IMappingProperty, + CAPITAL_DIGAMMA: {} as IMappingProperty, + CAPITAL_YOT: {} as IMappingProperty, + CAPITAL_LUNATE_SIGMA: {} as IMappingProperty, + CAPITAL_STIGMA: {} as IMappingProperty, + CAPITAL_KOPPA: {} as IMappingProperty, + CAPITAL_ARCHAIC_KOPPA: {} as IMappingProperty, + CAPITAL_SAMPI: {} as IMappingProperty, + CAPITAL_SAN: {} as IMappingProperty +}); + +const SMALL_LETTERS = (): { + [k in string]: IMappingProperty; +} => ({ + SMALL_ALPHA: { gr: 'α', bc: 'a', tr: 'a' - }; - private SMALL_BETA: IMappingProperty = { + }, + SMALL_BETA: { gr: 'β', bc: 'b', tr: 'b' - }; - private SMALL_GAMMA: IMappingProperty = { + }, + SMALL_GAMMA: { gr: 'γ', bc: 'g', tr: 'g' - }; - private SMALL_DELTA: IMappingProperty = { + }, + SMALL_DELTA: { gr: 'δ', bc: 'd', tr: 'd' - }; - private SMALL_EPSILON: IMappingProperty = { + }, + SMALL_EPSILON: { gr: 'ε', bc: 'e', tr: 'e' - }; - private SMALL_ZETA: IMappingProperty = { + }, + SMALL_ZETA: { gr: 'ζ', bc: 'z', tr: 'z' - }; - private SMALL_ETA: IMappingProperty = { + }, + SMALL_ETA: { gr: 'η', bc: 'h', tr: 'ē' - }; - private SMALL_THETA: IMappingProperty = { + }, + SMALL_THETA: { gr: 'θ', bc: 'q', tr: 'th' - }; - private SMALL_IOTA: IMappingProperty = { + }, + SMALL_IOTA: { gr: 'ι', bc: 'i', tr: 'i' - }; - private SMALL_KAPPA: IMappingProperty = { + }, + SMALL_KAPPA: { gr: 'κ', bc: 'k', tr: 'k' - }; - private SMALL_LAMBDA: IMappingProperty = { + }, + SMALL_LAMBDA: { gr: 'λ', bc: 'l', tr: 'l' - }; - private SMALL_MU: IMappingProperty = { + }, + SMALL_MU: { gr: 'μ', bc: 'm', tr: 'm' - }; - private SMALL_NU: IMappingProperty = { + }, + SMALL_NU: { gr: 'ν', bc: 'n', tr: 'n' - }; - private SMALL_XI: IMappingProperty = { + }, + SMALL_XI: { gr: 'ξ', bc: 'c', tr: 'x' - }; - private SMALL_OMICRON: IMappingProperty = { + }, + SMALL_OMICRON: { gr: 'ο', bc: 'o', tr: 'o' - }; - private SMALL_PI: IMappingProperty = { + }, + SMALL_PI: { gr: 'π', bc: 'p', tr: 'p' - }; - private SMALL_RHO: IMappingProperty = { + }, + SMALL_RHO: { gr: 'ρ', bc: 'r', tr: 'r' - }; - private SMALL_SIGMA: IMappingProperty = { + }, + SMALL_SIGMA: { gr: 'σ', bc: 's', tr: 's' - }; - private SMALL_TAU: IMappingProperty = { + }, + SMALL_TAU: { gr: 'τ', bc: 't', tr: 't' - }; - private SMALL_UPSILON: IMappingProperty = { + }, + SMALL_UPSILON: { gr: 'υ', bc: 'u', tr: 'u' - }; - private SMALL_ALT_UPSILON: IMappingProperty; - private SMALL_PHI: IMappingProperty = { + }, + SMALL_PHI: { gr: 'φ', bc: 'f', tr: 'ph' - }; - private SMALL_CHI: IMappingProperty = { + }, + SMALL_CHI: { gr: 'χ', bc: 'x', tr: 'ch' - }; - private SMALL_PSI: IMappingProperty = { + }, + SMALL_PSI: { gr: 'ψ', bc: 'y', tr: 'ps' - }; - private SMALL_OMEGA: IMappingProperty = { + }, + SMALL_OMEGA: { gr: 'ω', bc: 'w', tr: 'ō' - }; - private SMALL_DIGAMMA: IMappingProperty; - private SMALL_YOT: IMappingProperty; - private SMALL_LUNATE_SIGMA: IMappingProperty; - private SMALL_STIGMA: IMappingProperty; - private SMALL_KOPPA: IMappingProperty; - private SMALL_ARCHAIC_KOPPA: IMappingProperty; - private SMALL_SAMPI: IMappingProperty; - private SMALL_SAN: IMappingProperty; - private QUESTION_MARK: IMappingProperty = { + }, + SMALL_ALT_UPSILON: {} as IMappingProperty, + SMALL_DIGAMMA: {} as IMappingProperty, + SMALL_YOT: {} as IMappingProperty, + SMALL_LUNATE_SIGMA: {} as IMappingProperty, + SMALL_STIGMA: {} as IMappingProperty, + SMALL_KOPPA: {} as IMappingProperty, + SMALL_ARCHAIC_KOPPA: {} as IMappingProperty, + SMALL_SAMPI: {} as IMappingProperty, + SMALL_SAN: {} as IMappingProperty +}); + +const PUNCTUATION = (): { + [k in string]: IMappingProperty; +} => ({ + PUNCT_QUESTION_MARK: { gr: GREEK_QUESTION_MARK, bc: ';', tr: '?' - }; - private ANO_TELEIA: IMappingProperty = { + }, + PUNCT_ANO_TELEIA: { gr: ANO_TELEIA, bc: ':', tr: ';' - }; - private DIACRITICS = { - SMOOTH_BREATHING: { - gr: SMOOTH_BREATHING, - bc: ')', - tr: SMOOTH_BREATHING - } as IMappingProperty, - ROUGH_BREATHING: { - gr: ROUGH_BREATHING, - bc: '(', - tr: undefined - } as IMappingProperty, - ACCUTE_ACCENT: { - gr: ACUTE_ACCENT, - bc: '/', - tr: ACUTE_ACCENT - } as IMappingProperty, - GRAVE_ACCENT: { - gr: GRAVE_ACCENT, - bc: '\\', - tr: GRAVE_ACCENT - } as IMappingProperty, - MACRON: { - gr: MACRON, - bc: '%26', - tr: MACRON - } as IMappingProperty, - BREVE: { - gr: BREVE, - bc: '%27', - tr: BREVE - } as IMappingProperty, - TILDE: { - gr: GREEK_TILDE, - bc: '=', - tr: LATIN_TILDE - } as IMappingProperty, - DIAERESIS: { - gr: DIAERESIS, - bc: '+', - tr: DIAERESIS - } as IMappingProperty, - IOTA_SUBSCRIPT: { - gr: IOTA_SUBSCRIPT, - bc: '|', - tr: CEDILLA - } as IMappingProperty, - DOT_BELOW: { - gr: DOT_BELOW, - bc: '?', - tr: DOT_BELOW - } as IMappingProperty - }; + } +}); - #isUpperCase: boolean; - //#betaCodeStyle: IBetaCodeStyle; - #removeDiacritics: boolean; - #transliterationStyle: ITransliterationStyle; - #useAdditionalChars: AdditionalChar[] | AdditionalChar; +const DIACRITICS = (): { + [k in string]: IMappingProperty; +} => ({ + DIA_SMOOTH_BREATHING: { + gr: SMOOTH_BREATHING, + bc: ')', + tr: SMOOTH_BREATHING + }, + DIA_ROUGH_BREATHING: { + gr: ROUGH_BREATHING, + bc: '(', + tr: undefined + }, + DIA_ACCUTE_ACCENT: { + gr: ACUTE_ACCENT, + bc: '/', + tr: ACUTE_ACCENT + }, + DIA_GRAVE_ACCENT: { + gr: GRAVE_ACCENT, + bc: '\\', + tr: GRAVE_ACCENT + }, + DIA_MACRON: { + gr: MACRON, + bc: '%26', + tr: MACRON + }, + DIA_BREVE: { + gr: BREVE, + bc: '%27', + tr: BREVE + }, + DIA_TILDE: { + gr: GREEK_TILDE, + bc: '=', + tr: LATIN_TILDE + }, + DIA_DIAERESIS: { + gr: DIAERESIS, + bc: '+', + tr: DIAERESIS + }, + DIA_IOTA_SUBSCRIPT: { + gr: IOTA_SUBSCRIPT, + bc: '|', + tr: CEDILLA + }, + DIA_DOT_BELOW: { + gr: DOT_BELOW, + bc: '?', + tr: DOT_BELOW + } +}); + +export class Mapping { + #capitalLetters = CAPITAL_LETTERS(); + #smallLetters = SMALL_LETTERS(); + #punctuation = PUNCTUATION(); + #diacritics = DIACRITICS(); + + #isUpperCase: boolean = false; + #removeDiacritics: boolean = false; + #transliterationStyle: ITransliterationStyle = {}; + #additionalChars: AdditionalChar[] | AdditionalChar = []; constructor(options?: IInternalConversionOptions) { if (!options) return; - this.#isUpperCase = options?.isUpperCase; - //this.#betaCodeStyle = options?.setBetaCodeStyle; - this.#removeDiacritics = options?.removeDiacritics; - this.#transliterationStyle = options?.setTransliterationStyle; - this.#useAdditionalChars = options?.useAdditionalChars; + this.#isUpperCase = Boolean(options?.isUpperCase); + this.#removeDiacritics = Boolean(options?.removeDiacritics); + this.#transliterationStyle = options?.transliterationStyle ?? {}; + this.#additionalChars = options?.additionalChars ?? []; - if (this.#useAdditionalChars) { + if (this.#additionalChars) { for (const [k, v] of Object.entries(ADDITIONAL_CHARS_VALUES())) { if ( - this.#useAdditionalChars === (AdditionalChar.ALL as number) || - this.#useAdditionalChars === (Number(k) as AdditionalChar) || - (Array.isArray(this.#useAdditionalChars) && - this.#useAdditionalChars.includes(Number(k) as AdditionalChar)) + this.#additionalChars === (AdditionalChar.ALL as number) || + this.#additionalChars === (Number(k) as AdditionalChar) || + (Array.isArray(this.#additionalChars) && + this.#additionalChars.includes(Number(k) as AdditionalChar)) ) { - for (const [char, props] of Object.entries(v)) { - this[char] = props; - } + const keys = Object.keys(v); + if (keys[0]) this.#capitalLetters[keys[0]] = v[keys[0]]; + if (keys[1]) this.#smallLetters[keys[1]] = v[keys[1]]; } } } - const { useCxOverMacron, xi_ks, chi_kh, upsilon_y, lunatesigma_s } = - this.#transliterationStyle ?? {}; - - if (useCxOverMacron) { - this.CAPITAL_ETA.tr = 'Ê'; - this.SMALL_ETA.tr = 'ê'; - - this.CAPITAL_OMEGA.tr = 'Ô'; - this.SMALL_OMEGA.tr = 'ô'; - - if (this.CAPITAL_STIGMA?.tr) { - this.CAPITAL_STIGMA.tr = 'Ĉ'; - this.SMALL_STIGMA.tr = 'ĉ'; - } + const { + useCxOverMacron, + beta_v, + eta_i, + xi_ks, + phi_f, + chi_kh, + upsilon_y, + lunatesigma_s + } = this.#transliterationStyle ?? {}; + + if (beta_v) { + this.#capitalLetters.CAPITAL_BETA.tr = 'V'; + this.#smallLetters.SMALL_BETA.tr = 'v'; + } - if (this.CAPITAL_SAMPI?.tr) { - this.CAPITAL_SAMPI.tr = 'Ŝ'; - this.SMALL_SAMPI.tr = 'ŝ'; - } + if (eta_i) { + this.#capitalLetters.CAPITAL_ETA.tr = 'Ī'; + this.#smallLetters.SMALL_ETA.tr = 'ī'; } if (xi_ks) { - this.CAPITAL_XI.tr = 'Ks'; - this.SMALL_XI.tr = 'ks'; + this.#capitalLetters.CAPITAL_XI.tr = 'Ks'; + this.#smallLetters.SMALL_XI.tr = 'ks'; + } + + if (phi_f) { + this.#capitalLetters.CAPITAL_PHI.tr = 'F'; + this.#smallLetters.SMALL_PHI.tr = 'f'; } if (chi_kh) { - this.CAPITAL_CHI.tr = 'Kh'; - this.SMALL_CHI.tr = 'kh'; + this.#capitalLetters.CAPITAL_CHI.tr = 'Kh'; + this.#smallLetters.SMALL_CHI.tr = 'kh'; } if (upsilon_y) { - this.CAPITAL_UPSILON.tr = 'Y'; - this.SMALL_UPSILON.tr = 'y'; + this.#capitalLetters.CAPITAL_UPSILON.tr = 'Y'; + this.#smallLetters.SMALL_UPSILON.tr = 'y'; } if (lunatesigma_s) { // The lunate sigma might not have been activated using the - // `useAdditionalChars` option. So, we need to check if its property exists. - if (this.CAPITAL_LUNATE_SIGMA?.tr) this.CAPITAL_LUNATE_SIGMA.tr = 'S'; - if (this.SMALL_LUNATE_SIGMA?.tr) this.SMALL_LUNATE_SIGMA.tr = 's'; + // `additionalChars` option. So, we need to check if its property exists. + if (this.#capitalLetters.CAPITAL_LUNATE_SIGMA?.tr) + this.#capitalLetters.CAPITAL_LUNATE_SIGMA.tr = 'S'; + if (this.#smallLetters.SMALL_LUNATE_SIGMA?.tr) + this.#smallLetters.SMALL_LUNATE_SIGMA.tr = 's'; + + if (!this.#capitalLetters.CAPITAL_LUNATE_SIGMA?.tr) { + console.warn('You must enable `AdditionalChar.LUNATE_SIGMA` for the option `transliterationStyle.lunatesigma_s` to take effect.'); // prettier-ignore + } + } + + if (useCxOverMacron) { + if (eta_i) { + this.#capitalLetters.CAPITAL_ETA.tr = 'Î'; + this.#smallLetters.SMALL_ETA.tr = 'î'; + } else { + this.#capitalLetters.CAPITAL_ETA.tr = 'Ê'; + this.#smallLetters.SMALL_ETA.tr = 'ê'; + } + + this.#capitalLetters.CAPITAL_OMEGA.tr = 'Ô'; + this.#smallLetters.SMALL_OMEGA.tr = 'ô'; + + if (this.#capitalLetters.CAPITAL_STIGMA?.tr) { + this.#capitalLetters.CAPITAL_STIGMA.tr = 'Ĉ'; + this.#smallLetters.SMALL_STIGMA.tr = 'ĉ'; + } - if (!this.CAPITAL_LUNATE_SIGMA?.tr) { - console.warn('You must enable `AdditionalChar.LUNATE_SIGMA` for the option `setTransliterationStyle.lunatesigma_s` to take effect.'); // prettier-ignore + if (this.#capitalLetters.CAPITAL_SAMPI?.tr) { + this.#capitalLetters.CAPITAL_SAMPI.tr = 'Ŝ'; + this.#smallLetters.SMALL_SAMPI.tr = 'ŝ'; } } if (this.#isUpperCase) { - for (const [k, v] of Object.entries(this)) { - if (k.startsWith('CAPITAL') && v.tr?.length > 1 /* Th, Ph, etc */) { - this[k].tr = v.tr.toUpperCase(); + for (const [k, v] of Object.entries(this.#capitalLetters)) { + if (v.tr?.length > 1 /* Th, Ph, etc */) { + this.#capitalLetters[k].tr = v.tr.toUpperCase(); } } } @@ -544,35 +595,32 @@ export class Mapping { apply(fromStr: string, fromType: KeyType, toType: KeyType): string { fromStr = fromStr.normalize('NFD'); - if ( - fromType === KeyType.TRANSLITERATION && - toType !== KeyType.TRANSLITERATION - ) { + if (fromType === KeyType.TRANSLITERATION && toType !== fromType) { fromStr = this.#trJoinSpecialChars(fromStr); // Add the alternate upsilon form (y/u) to the mapping. if (this.#transliterationStyle?.upsilon_y) { - this.CAPITAL_ALT_UPSILON = { - bc: this.CAPITAL_UPSILON.bc, - gr: this.CAPITAL_UPSILON.gr, + this.#capitalLetters.CAPITAL_ALT_UPSILON = { + bc: this.#capitalLetters.CAPITAL_UPSILON.bc, + gr: this.#capitalLetters.CAPITAL_UPSILON.gr, tr: 'U' }; - this.SMALL_ALT_UPSILON = { - bc: this.SMALL_UPSILON.bc, - gr: this.SMALL_UPSILON.gr, + this.#smallLetters.SMALL_ALT_UPSILON = { + bc: this.#smallLetters.SMALL_UPSILON.bc, + gr: this.#smallLetters.SMALL_UPSILON.gr, tr: 'u' }; } // `lunatesigma_s` is destructive: convert back all sigmas using the regular form. if (this.#transliterationStyle?.lunatesigma_s) { - this.CAPITAL_LUNATE_SIGMA.tr = undefined; - this.SMALL_LUNATE_SIGMA.tr = undefined; + this.#capitalLetters.CAPITAL_LUNATE_SIGMA.tr = undefined; + this.#smallLetters.SMALL_LUNATE_SIGMA.tr = undefined; } } if (fromType === KeyType.GREEK) { - fromStr = Mapping.#grBypassUnicodeEquivalences(fromStr); + fromStr = normalizeGreek(fromStr, true, true); } const mappingProps = this.#getPropsMapOrderByLengthDesc(fromType, toType); @@ -604,7 +652,7 @@ export class Mapping { // Nullish subsequent array indices if necessary. if (lval.length > 1) { for (let i = 1; i < lval.length; i++) { - conversionArr[matches.index + i] = null; + conversionArr[matches.index + i] = ''; } } } @@ -661,47 +709,32 @@ export class Mapping { * * @param fromType - The left value of the resulting `Map` * @param toType - The right value of the resulting `Map` - * @param removeDiacritics - If `true`, exclude the `DIACRITICS` property + * @param removeDiacritics - If `true`, exclude `this.#diacritics` */ #getPropsMapOrderByLengthDesc( fromType: KeyType, toType: KeyType ): Map { - let fromProp: string; - let toProp: string; + let fromProp, toProp: string; - switch (fromType) { - case KeyType.BETA_CODE: - fromProp = 'bc'; - break; - case KeyType.GREEK: - fromProp = 'gr'; - break; - case KeyType.TRANSLITERATION: - fromProp = 'tr'; - break; - } + if (fromType === KeyType.BETA_CODE) fromProp = 'bc'; + else if (fromType === KeyType.GREEK) fromProp = 'gr'; + else fromProp = 'tr'; - switch (toType) { - case KeyType.BETA_CODE: - toProp = 'bc'; - break; - case KeyType.GREEK: - toProp = 'gr'; - break; - case KeyType.TRANSLITERATION: - toProp = 'tr'; - break; - } + if (toType === KeyType.BETA_CODE) toProp = 'bc'; + else if (toType === KeyType.GREEK) toProp = 'gr'; + else toProp = 'tr'; let chars = []; - for (const [k, v] of Object.entries(this)) { - if (this.#isUpperCase && k.startsWith('SMALL')) continue; + const props = Object.assign( + this.#capitalLetters, + !this.#isUpperCase ? this.#smallLetters : {}, + this.#punctuation + ); - if (v[fromProp] !== undefined && v[toProp] !== undefined) { - chars.push([v[fromProp], v[toProp]]); - } + for (const [k, v] of Object.entries(props)) { + if (v[fromProp] && v[toProp]) chars.push([v[fromProp], v[toProp]]); } const sortedChars = chars.sort((a, b) => { @@ -711,10 +744,8 @@ export class Mapping { if (!this.#removeDiacritics) { let diacritics = []; - for (const [k, v] of Object.entries(this.DIACRITICS)) { - if (v[fromProp] !== undefined && v[toProp] !== undefined) { - diacritics.push([v[fromProp], v[toProp]]); - } + for (const [k, v] of Object.entries(this.#diacritics)) { + if (v[fromProp] && v[toProp]) diacritics.push([v[fromProp], v[toProp]]); } return new Map([...sortedChars, ...diacritics]); @@ -723,18 +754,6 @@ export class Mapping { return new Map(sortedChars); } - /** - * Returns a string for which the wrong Unicode canonical equivalences - * have been replaced by the right Unicode points. - * - * @param NFDGreekStr - Expects an `NFD` normalized greek string. - */ - static #grBypassUnicodeEquivalences(NFDGreekStr: string): string { - return NFDGreekStr.replace(new RegExp(LATIN_TILDE, 'g'), GREEK_TILDE) - .replace(new RegExp(MIDDLE_DOT, 'g'), ANO_TELEIA) - .replace(new RegExp(';', 'g'), GREEK_QUESTION_MARK); - } - /** * Returns a string for which some diacritical marks have been joined back * to their letter as they should not be treated separately (e. g. when @@ -744,10 +763,9 @@ export class Mapping { */ #trJoinSpecialChars(NFDTransliteratedStr: string): string { // Join back below dots to archaic koppas. - // @fixme: this does not work with adjacent small & capital archaic koppa. - if (this.CAPITAL_ARCHAIC_KOPPA?.tr) { + if (this.#capitalLetters.CAPITAL_ARCHAIC_KOPPA?.tr) { NFDTransliteratedStr = NFDTransliteratedStr.replace( - new RegExp(`${this.CAPITAL_ARCHAIC_KOPPA.tr.normalize('NFD')}`, 'gi'), + new RegExp(`${this.#capitalLetters.CAPITAL_ARCHAIC_KOPPA.tr.normalize('NFD')}`, 'gi'), // prettier-ignore (match) => match.normalize() ); } @@ -762,32 +780,23 @@ export class Mapping { } /** - * Returns an array containing the transliterated mapped chars tied - * to a circumflex or a macron, depnding on the context. + * Returns an array containing the transliterated chars that carry + * a circumflex or a macron (depnding on the context). * * @remarks - * (1) Letters are returned without their diacritical sign. - * (2) The current implementation is semi-static as it doesn't check - * the actual mapped chars. + * Letters are returned without their diacritical sign. */ trLettersWithCxOrMacron(): string[] { - let letters = [ - this.CAPITAL_ETA, - this.SMALL_ETA, - this.CAPITAL_OMEGA, - this.SMALL_OMEGA - ]; - - if (this.CAPITAL_STIGMA?.tr) { - letters.push(this.CAPITAL_STIGMA, this.SMALL_STIGMA); - } + const letters = Object.assign(this.#capitalLetters, this.#smallLetters); - if (this.CAPITAL_SAMPI?.tr) { - letters.push(this.CAPITAL_SAMPI, this.SMALL_SAMPI); + const found = []; + for (const [k, v] of Object.entries(letters)) { + const el = v?.tr ? v.tr.normalize('NFD') : ''; + if (/\u0302|\u0304/.test(el)) { + found.push(el); + } } - return letters.map((letter) => - letter.tr.normalize('NFD').charAt(0).normalize() - ); + return found.map((el) => el.charAt(0).normalize()); } } diff --git a/src/enums.ts b/src/enums.ts index 7810fb1..63fa8e8 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -1,3 +1,5 @@ +import { RIGHT_SINGLE_QUOTATION_MARK, SMOOTH_BREATHING } from './Mapping'; + export enum AdditionalChar { ALL = 1, DIGAMMA, @@ -10,16 +12,24 @@ export enum AdditionalChar { //SAN } +export enum Coronis { + PSILI = SMOOTH_BREATHING, + APOSTROPHE = RIGHT_SINGLE_QUOTATION_MARK, + NO = '' +} + export enum KeyType { GREEK, BETA_CODE, + TLG_BETA_CODE, TRANSLITERATION } export enum Preset { ALA_LC, BNF, + ISO, MODERN_BC, - SBL - //TLG + SBL, + TLG } diff --git a/src/index.ts b/src/index.ts index 88da48d..eb52a25 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ * @license AGPL-3.0 */ -import { AdditionalChar, KeyType, Preset } from './enums'; +import { AdditionalChar, Coronis, KeyType, Preset } from './enums'; import { GreekString } from './GreekString'; import { toBetaCode } from './toBetaCode'; import { toGreek } from './toGreek'; @@ -15,7 +15,7 @@ import { removeGreekVariants } from './utils'; -export { AdditionalChar, KeyType, Preset } from './enums'; +export { AdditionalChar, Coronis, KeyType, Preset } from './enums'; export { GreekString } from './GreekString'; export { toBetaCode } from './toBetaCode'; export { toGreek } from './toGreek'; @@ -28,6 +28,7 @@ export { export default { AdditionalChar, + Coronis, KeyType, Preset, GreekString, diff --git a/src/interfaces.ts b/src/interfaces.ts index a2ae3d2..bea6bfa 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,12 +1,12 @@ -import { AdditionalChar, Preset } from './enums'; +import { AdditionalChar, Coronis, Preset } from './enums'; export interface IConversionOptions { removeDiacritics?: boolean; removeExtraWhitespace?: boolean; - //setBetaCodeStyle?: IBetaCodeStyle; - setGreekStyle?: IGreekStyle; - setTransliterationStyle?: ITransliterationStyle; - useAdditionalChars?: AdditionalChar[] | AdditionalChar; + betaCodeStyle?: IBetaCodeStyle; + greekStyle?: IGreekStyle; + transliterationStyle?: ITransliterationStyle; + additionalChars?: AdditionalChar[] | AdditionalChar; } export interface IInternalConversionOptions extends IConversionOptions { @@ -15,13 +15,13 @@ export interface IInternalConversionOptions extends IConversionOptions { export type MixedPreset = [Preset, IConversionOptions]; -// v0.13 -/*export interface IBetaCodeStyle { +export interface IBetaCodeStyle { useTLGStyle?: boolean; -}*/ +} export interface IGreekStyle { disableBetaVariant?: boolean; + useGreekQuestionMark?: boolean; useLunateSigma?: boolean; } @@ -32,10 +32,17 @@ export interface IMappingProperty { } export interface ITransliterationStyle { + setCoronisStyle?: Coronis; useCxOverMacron?: boolean; + beta_v?: boolean; + eta_i?: boolean; xi_ks?: boolean; rho_rh?: boolean; + phi_f?: boolean; chi_kh?: boolean; - upsilon_y?: boolean; + upsilon_y?: boolean | Preset.ISO; // (¹) lunatesigma_s?: boolean; } + +// ¹ Preset.ISO: only preserve 'au', 'eu', 'ou'. +// Note that this is undoubtedly poorly designed. diff --git a/src/presets.ts b/src/presets.ts index be6a969..fc4e681 100644 --- a/src/presets.ts +++ b/src/presets.ts @@ -1,23 +1,28 @@ -import { AdditionalChar, Preset } from './enums'; +import { AdditionalChar, Coronis, Preset } from './enums'; import { IConversionOptions, MixedPreset } from './interfaces'; const ALA_LC_OPTIONS = (): IConversionOptions => ({ removeDiacritics: true, - useAdditionalChars: [ - AdditionalChar.DIGAMMA, - AdditionalChar.ARCHAIC_KOPPA, - AdditionalChar.LUNATE_SIGMA - ], - setTransliterationStyle: { + transliterationStyle: { rho_rh: true, upsilon_y: true, lunatesigma_s: true - } + }, + additionalChars: [ + AdditionalChar.DIGAMMA, + AdditionalChar.ARCHAIC_KOPPA, + AdditionalChar.LUNATE_SIGMA + ] }); const BNF_OPTIONS = (): IConversionOptions => ({ - removeDiacritics: false, - useAdditionalChars: [ + greekStyle: { + useGreekQuestionMark: true + }, + transliterationStyle: { + upsilon_y: Preset.ISO + }, + additionalChars: [ AdditionalChar.DIGAMMA, AdditionalChar.YOT, AdditionalChar.LUNATE_SIGMA, @@ -27,25 +32,39 @@ const BNF_OPTIONS = (): IConversionOptions => ({ ] }); +const ISO_OPTIONS = (): IConversionOptions => ({ + transliterationStyle: { + setCoronisStyle: Coronis.APOSTROPHE, + beta_v: true, + eta_i: true, + phi_f: true, + upsilon_y: Preset.ISO, + lunatesigma_s: true + }, + additionalChars: [ + AdditionalChar.DIGAMMA, + AdditionalChar.YOT, + AdditionalChar.LUNATE_SIGMA + ] +}); + const MODERN_BC_OPTIONS = (): IConversionOptions => ({ - removeDiacritics: false, - useAdditionalChars: AdditionalChar.ALL + additionalChars: AdditionalChar.ALL }); const SBL_OPTIONS = (): IConversionOptions => ({ removeDiacritics: true, - setTransliterationStyle: { + transliterationStyle: { rho_rh: true, upsilon_y: true } }); const TLG_OPTIONS = (): IConversionOptions => ({ - removeDiacritics: false, - useAdditionalChars: AdditionalChar.ALL - /*setBetaCodeStyle: { + betaCodeStyle: { useTLGStyle: true - }*/ + }, + additionalChars: AdditionalChar.ALL }); export function applyPreset(preset: Preset | MixedPreset): IConversionOptions { @@ -65,6 +84,10 @@ export function applyPreset(preset: Preset | MixedPreset): IConversionOptions { options = BNF_OPTIONS(); break; + case Preset.ISO: + options = ISO_OPTIONS(); + break; + case Preset.MODERN_BC: options = MODERN_BC_OPTIONS(); break; @@ -73,10 +96,9 @@ export function applyPreset(preset: Preset | MixedPreset): IConversionOptions { options = SBL_OPTIONS(); break; - // v0.13 - /*case Preset.TLG: + case Preset.TLG: options = TLG_OPTIONS(); - break;*/ + break; default: throw new RangeError(`Preset '${preset}' is not implemented.`); @@ -90,7 +112,7 @@ export function applyPreset(preset: Preset | MixedPreset): IConversionOptions { } function mergeOptions(target: IConversionOptions, source: IConversionOptions) { - const isObject = (obj) => obj && typeof obj === 'object'; + const isObject = (obj: object) => obj && typeof obj === 'object'; for (const key in source) { if (Array.isArray(target[key]) && Array.isArray(source[key])) { diff --git a/src/toBetaCode.ts b/src/toBetaCode.ts index 2483951..54e6b67 100644 --- a/src/toBetaCode.ts +++ b/src/toBetaCode.ts @@ -1,9 +1,12 @@ -import { KeyType, Preset } from './enums'; +import { Coronis, KeyType, Preset } from './enums'; import { IConversionOptions, MixedPreset } from './interfaces'; -import { Mapping } from './Mapping'; +import { Mapping, SMOOTH_BREATHING } from './Mapping'; import { applyUppercaseChars, + bcReorderDiacritics, + fromTLG, handleOptions, + toTLG, removeDiacritics as utilRmDiacritics, removeExtraWhitespace as utilRmExtraWhitespace, removeGreekVariants as utilRmGreekVariants @@ -16,42 +19,59 @@ export function toBetaCode( declaredMapping?: Mapping ): string { const options = handleOptions(str, fromType, settings); - const { removeDiacritics, removeExtraWhitespace } = options; + const { + removeDiacritics, + removeExtraWhitespace, + betaCodeStyle, + transliterationStyle + } = options; const mapping = declaredMapping ?? new Mapping(options); switch (fromType) { case KeyType.BETA_CODE: + case KeyType.TLG_BETA_CODE: if (removeDiacritics) str = utilRmDiacritics(str, KeyType.BETA_CODE); str = mapping.apply(str, KeyType.BETA_CODE, KeyType.BETA_CODE); - str = reorderDiacritics(str); break; case KeyType.GREEK: - if (removeDiacritics) str = utilRmDiacritics(str, KeyType.GREEK); + if (removeDiacritics) str = utilRmDiacritics(str, fromType); str = utilRmGreekVariants(str); - str = mapping.apply(str, KeyType.GREEK, KeyType.BETA_CODE); - str = reorderDiacritics(str); + str = mapping.apply(str, fromType, KeyType.BETA_CODE); break; case KeyType.TRANSLITERATION: str = applyUppercaseChars(str); + if (transliterationStyle?.setCoronisStyle === Coronis.APOSTROPHE) { + str = str.replace( + new RegExp(`(?<=\\S)${Coronis.APOSTROPHE}(?=\\S)`, 'gu'), + SMOOTH_BREATHING + ); + } + // Flag transliterated rough breathings. str = str.replace(/(?<=\p{P}|\s|^|r{1,2})h/gimu, '$'); - str = mapping.apply(str, KeyType.TRANSLITERATION, KeyType.BETA_CODE); + str = mapping.apply(str, fromType, KeyType.BETA_CODE); if (removeDiacritics) { - str = utilRmDiacritics(str, KeyType.TRANSLITERATION); + str = utilRmDiacritics(str, fromType); str = str.replace(/\$/gi, ''); } else { str = trConvertFlaggedBreathings(str); } - - str = reorderDiacritics(str); break; } + str = bcReorderDiacritics(str); + + if (fromType === KeyType.TLG_BETA_CODE) { + if (!betaCodeStyle?.useTLGStyle) str = fromTLG(str); + } else { + if (betaCodeStyle?.useTLGStyle) str = toTLG(str); + } + if (removeExtraWhitespace) str = utilRmExtraWhitespace(str); return str.normalize(); @@ -101,15 +121,3 @@ function trConvertFlaggedBreathings(str: string): string { .replace(/\$/g, '') .normalize(); } - -/** - * Returns a beta code string with a correct diacritics order. - * - * @privateRemarks - * This function should reorder all the diacritics defined for the beta code - * representation. Currently, it only reorders breathings/accents in relation - * to the iota subscript. - */ -function reorderDiacritics(betaCodeStr: string): string { - return betaCodeStr.replace(/(\|)([()\\/+=]+)/g, '$2$1'); -} diff --git a/src/toGreek.ts b/src/toGreek.ts index 7ffdfcf..e3552f7 100644 --- a/src/toGreek.ts +++ b/src/toGreek.ts @@ -1,4 +1,4 @@ -import { KeyType, Preset } from './enums'; +import { Coronis, KeyType, Preset } from './enums'; import { IConversionOptions, IInternalConversionOptions, @@ -8,6 +8,8 @@ import { Mapping, ROUGH_BREATHING, SMOOTH_BREATHING } from './Mapping'; import { applyGreekVariants, applyUppercaseChars, + bcReorderDiacritics, + fromTLG, handleOptions, normalizeGreek, removeDiacritics as utilRmDiacritics, @@ -22,27 +24,50 @@ export function toGreek( declaredMapping?: Mapping ): string { const options = handleOptions(str, fromType, settings); - const { removeDiacritics, removeExtraWhitespace, setGreekStyle } = options; + const { + removeDiacritics, + removeExtraWhitespace, + greekStyle, + transliterationStyle + } = options; const mapping = declaredMapping ?? new Mapping(options); + if (fromType === KeyType.TLG_BETA_CODE) { + str = fromTLG(str); + fromType = KeyType.BETA_CODE; + } + switch (fromType) { case KeyType.BETA_CODE: - if (removeDiacritics) str = utilRmDiacritics(str, KeyType.BETA_CODE); - str = mapping.apply(str, KeyType.BETA_CODE, KeyType.GREEK); + if (removeDiacritics) str = utilRmDiacritics(str, fromType); + else str = bcReorderDiacritics(str); + + str = mapping.apply(str, fromType, KeyType.GREEK); + break; case KeyType.GREEK: - if (removeDiacritics) str = utilRmDiacritics(str, KeyType.GREEK); + if (removeDiacritics) str = utilRmDiacritics(str, fromType); str = utilRmGreekVariants(str); - str = mapping.apply(str, KeyType.GREEK, KeyType.GREEK); + str = mapping.apply(str, fromType, fromType); break; case KeyType.TRANSLITERATION: str = applyUppercaseChars(str); - str = mapping.apply(str, KeyType.TRANSLITERATION, KeyType.GREEK); + str = mapping.apply(str, fromType, KeyType.GREEK); + + if (transliterationStyle?.setCoronisStyle === Coronis.APOSTROPHE) { + str = str + .normalize('NFD') + .replace( + new RegExp(`(?<=\\S)${Coronis.APOSTROPHE}(?=\\S)`, 'gu'), + SMOOTH_BREATHING + ) + .normalize(); + } if (removeDiacritics) { - str = utilRmDiacritics(str, KeyType.TRANSLITERATION); + str = utilRmDiacritics(str, fromType); str = str.replace(/h/gi, ''); } else { str = trConvertBreathings(str, options); @@ -50,10 +75,10 @@ export function toGreek( break; } - str = applyGreekVariants(str, setGreekStyle); + str = applyGreekVariants(str, greekStyle); if (removeExtraWhitespace) str = utilRmExtraWhitespace(str); - return normalizeGreek(str); + return normalizeGreek(str, greekStyle?.useGreekQuestionMark); } /** diff --git a/src/toTransliteration.ts b/src/toTransliteration.ts index 46d41db..14e9713 100644 --- a/src/toTransliteration.ts +++ b/src/toTransliteration.ts @@ -1,4 +1,4 @@ -import { KeyType, Preset } from './enums'; +import { Coronis, KeyType, Preset } from './enums'; import { IConversionOptions, IInternalConversionOptions, @@ -13,8 +13,9 @@ import { SMOOTH_BREATHING } from './Mapping'; import { + bcReorderDiacritics, + fromTLG, handleOptions, - normalizeGreek, removeDiacritics as utilRmDiacritics, removeExtraWhitespace as utilRmExtraWhitespace, removeGreekVariants as utilRmGreekVariants @@ -27,64 +28,94 @@ export function toTransliteration( declaredMapping?: Mapping ): string { const options = handleOptions(str, fromType, settings); - const { removeDiacritics, removeExtraWhitespace, setTransliterationStyle } = + const { removeDiacritics, removeExtraWhitespace, transliterationStyle } = options; + const { + setCoronisStyle, + useCxOverMacron, + beta_v, + eta_i, + xi_ks, + phi_f, + chi_kh, + rho_rh, + upsilon_y, + lunatesigma_s + } = transliterationStyle ?? {}; const mapping = declaredMapping ?? new Mapping(options); - if (setTransliterationStyle?.upsilon_y) str = flagDiaereses(str, fromType); + if (upsilon_y) str = flagDiaereses(str, fromType); + + if (fromType === KeyType.TLG_BETA_CODE) { + str = fromTLG(str); + fromType = KeyType.BETA_CODE; + } switch (fromType) { case KeyType.BETA_CODE: + str = bcReorderDiacritics(str); str = bcFlagRoughBreathings(str, options); - if (removeDiacritics) str = utilRmDiacritics(str, KeyType.BETA_CODE); - str = mapping.apply(str, KeyType.BETA_CODE, KeyType.TRANSLITERATION); + if (removeDiacritics) str = utilRmDiacritics(str, fromType); + str = mapping.apply(str, fromType, KeyType.TRANSLITERATION); str = bcConvertBreathings(str, options); break; case KeyType.GREEK: str = grConvertBreathings(str, options); - if (removeDiacritics) str = utilRmDiacritics(str, KeyType.GREEK); + if (removeDiacritics) str = utilRmDiacritics(str, fromType); str = utilRmGreekVariants(str); - str = normalizeGreek(str); - str = mapping.apply(str, KeyType.GREEK, KeyType.TRANSLITERATION); + str = mapping.apply(str, fromType, KeyType.TRANSLITERATION); break; case KeyType.TRANSLITERATION: - const { - useCxOverMacron, - xi_ks, - chi_kh, - rho_rh, - upsilon_y, - lunatesigma_s - } = setTransliterationStyle ?? {}; - if (useCxOverMacron) { const re = new RegExp(`([${mapping.trLettersWithCxOrMacron()}])(${MACRON})`, 'g'); // prettier-ignore str = str.normalize('NFD').replace(re, `$1${CIRCUMFLEX}`).normalize(); } + if (beta_v) { + str = str.replace(/b/gi, (m) => (m.toUpperCase() === m ? 'V' : 'v')); + } + + if (eta_i) { + str = str + .normalize('NFD') + .replace(/(e)(\p{M}+)/giu, (m, $1, $2) => { + if ((useCxOverMacron && /\u0302/.test(m)) || /\u0304/.test(m)) { + return $1.toUpperCase() === $1 ? 'I' + $2 : 'i' + $2; + } + return m; + }) + .normalize(); + } + if (xi_ks) { - str = str.replace(/x/gi, (match) => { + str = str.replace(/x/gi, (m) => { if (options.isUpperCase) return 'KS'; - else return match.charAt(0).toUpperCase() === match ? 'Ks' : 'ks'; + else return m.charAt(0).toUpperCase() === m.charAt(0) ? 'Ks' : 'ks'; }); } + if (phi_f) { + str = str.replace(/ph/gi, (m) => + m.charAt(0).toUpperCase() === m.charAt(0) ? 'F' : 'f' + ); + } + if (chi_kh) { - str = str.replace(/ch/gi, (match) => { + str = str.replace(/ch/gi, (m) => { if (options.isUpperCase) return 'KH'; - else return match.charAt(0).toUpperCase() === match ? 'Kh' : 'kh'; + else return m.charAt(0).toUpperCase() === m.charAt(0) ? 'Kh' : 'kh'; }); } if (rho_rh) { str = str - .replace(/(? - match.toUpperCase() === match ? match + 'H' : match + 'h' + .replace(/(? + m.toUpperCase() === m ? m + 'H' : m + 'h' ) - .replace(/(?<=\p{P}|\s|^)r/gimu, (match) => - options.isUpperCase ? match + 'H' : match + 'h' + .replace(/(?<=\p{P}|\s|^)r/gimu, (m) => + options.isUpperCase ? m + 'H' : m + 'h' ); } @@ -97,35 +128,32 @@ export function toTransliteration( } if (lunatesigma_s) { - str = str.replace(/c(?!h)/gi, (match) => - match.toUpperCase() === match ? 'S' : 's' + str = str.replace(/c(?!h)/gi, (m) => + m.toUpperCase() === m ? 'S' : 's' ); } if (removeDiacritics) { str = utilRmDiacritics( str, - KeyType.TRANSLITERATION, + fromType, mapping.trLettersWithCxOrMacron(), useCxOverMacron ); } - str = mapping.apply( - str, - KeyType.TRANSLITERATION, - KeyType.TRANSLITERATION - ); + str = mapping.apply(str, fromType, fromType); break; } - if (setTransliterationStyle?.upsilon_y) { - str = applyUpsilonDiphthongs(str); - str = str.replace(/@/gm, ''); + if (upsilon_y) { + str = applyUpsilonDiphthongs(str, options, mapping).replace(/@/gm, ''); } if (removeExtraWhitespace) str = utilRmExtraWhitespace(str); + str = trApplyCoronis(str, setCoronisStyle); + return str.normalize(); } @@ -133,25 +161,41 @@ export function toTransliteration( * Returns a transliterated string with correct upsilon forms. * * @remarks - * This applies to the `transliterationStyle.upsilon_y` option. + * This applies to the `transliterationStyle.upsilon_y` option. If its + * value has been set to `Preset.ISO`, diphthongs 'au', 'eu', 'ou' only + * will be preserved. * * @privateRemarks - * (1) Upsilon diphthongs are: 'au', 'eu', 'ēu', 'ou', 'ui', 'ōu'. + * (1) The expected input form of the upsilon is 'y'. + * (2) Upsilon diphthongs to preserve are: 'au', 'eu', 'ēu', 'ou', 'ui', 'ōu'. * (2) The given string's diaereses should have been flagged as '@' using * the `flagDiaereses()` function. */ -function applyUpsilonDiphthongs(transliteratedStr: string): string { - const vowels = 'aeēioyō'; - // `vowelsGroup` includes the upsilon ('y'), the diaeresis flag '@'. - const reUpsilonDiphthongs = new RegExp(`([${vowels}\\p{M}@]{2,})`, 'gimu'); // prettier-ignore +function applyUpsilonDiphthongs( + transliteratedStr: string, + options: IInternalConversionOptions, + mapping: Mapping +): string { + const { transliterationStyle } = options; + const reUpsilonDiphthongs = new RegExp(`([aeēioyō\\p{M}@]{2,})`, 'gimu'); return transliteratedStr .normalize('NFD') - .replace(reUpsilonDiphthongs, (match, vowelsGroup) => { + .replace(reUpsilonDiphthongs, (m, vowelsGroup) => { if (!/y/i.test(vowelsGroup)) return vowelsGroup; if (/* flagged diaeresis */ /@/.test(vowelsGroup)) return vowelsGroup; if (vowelsGroup.normalize().length === 1) return vowelsGroup; + if (transliterationStyle?.upsilon_y === Preset.ISO) { + const unaccentedGroup = utilRmDiacritics( + vowelsGroup, + KeyType.TRANSLITERATION, + mapping.trLettersWithCxOrMacron(), + transliterationStyle?.useCxOverMacron + ); + if (!/ay|ey|oy/i.test(unaccentedGroup)) return vowelsGroup; + } + return vowelsGroup.replace(/Y/, 'U').replace(/y/, 'u'); }) .normalize(); @@ -163,7 +207,7 @@ function applyUpsilonDiphthongs(transliteratedStr: string): string { * @remarks * This function does: * 1. convert flagged rough breathings; - * 2. enforce rough breathings on rhos if `setTransliterationStyle.rho_rh` is enabled; + * 2. enforce rough breathings on rhos if `transliterationStyle.rho_rh` is enabled; * 3. remove initial smooth breathings (while keeping coronides); * 4. remove potential smooth breathings on rhos. * 5. transliterate the remaining smooth breathings. @@ -176,21 +220,20 @@ function bcConvertBreathings( transliteratedStr: string, options: IInternalConversionOptions ): string { - const { isUpperCase, setTransliterationStyle } = options; - const rho_rh = setTransliterationStyle?.rho_rh; + const { isUpperCase, transliterationStyle } = options; transliteratedStr = transliteratedStr .replace(/\$\$/g, 'H') .replace(/\$/g, 'h'); - if (rho_rh) { + if (transliterationStyle?.rho_rh) { transliteratedStr = transliteratedStr - .replace(new RegExp(`(r${SMOOTH_BREATHING}?r)(?!h)`, 'gi'), (match) => - match.toUpperCase() === match ? 'RRH' : 'rrh' + .replace(new RegExp(`(r${SMOOTH_BREATHING}?r)(?!h)`, 'gi'), (m) => + m.toUpperCase() === m ? 'RRH' : 'rrh' ) - .replace(/(?<=\p{P}|\\s|^)(r)(?!h)/gimu, (match) => - // @fixme(v0.13): case should be checked against the current word. - isUpperCase ? match + 'H' : match + 'h' + .replace(/(?<=\p{P}|\\s|^)(r)(?!h)/gimu, (m) => + // @fixme(v0.14): case should be checked against the current word. + isUpperCase ? m + 'H' : m + 'h' ); } @@ -219,8 +262,8 @@ function bcFlagRoughBreathings( const { isUpperCase } = options; return betaCodeStr - .replace(/([aehiouw]{1,2})\(/gi, (match, vowelsGroup) => { - // @fixme(v0.13): case should be checked against the current word too. + .replace(/([aehiouw]{1,2})\(/gi, (m, vowelsGroup) => { + // @fixme(v0.14): case should be checked against the current word too. if (isUpperCase) return '$$' + vowelsGroup; else { return vowelsGroup.charAt(0).toUpperCase() === vowelsGroup.charAt(0) @@ -228,7 +271,7 @@ function bcFlagRoughBreathings( : '$' + vowelsGroup; } }) - .replace(/(r{1,2})\(/gi, (match, rho) => + .replace(/(r{1,2})\(/gi, (m, rho) => isUpperCase ? rho + '$$' : rho + '$' ); } @@ -266,8 +309,8 @@ function grConvertBreathings( greekStr: string, options: IInternalConversionOptions ): string { - const { isUpperCase, setTransliterationStyle } = options; - const rho_rh = setTransliterationStyle?.rho_rh; + const { isUpperCase, transliterationStyle } = options; + const { rho_rh } = transliterationStyle ?? {}; const reInitialSmooth = new RegExp(`(?<=\\p{P}|\\s|^)([αεηιουω]{1,2})(${SMOOTH_BREATHING})`, 'gimu'); // prettier-ignore const reInitialRough = new RegExp(`([αεηιοωυ]{1,2})(${ROUGH_BREATHING})`, 'gi'); // prettier-ignore @@ -275,14 +318,11 @@ function grConvertBreathings( const reInitialRho = new RegExp(`(ρ)${ROUGH_BREATHING}`, 'gi'); const reInitialRhoLazy = new RegExp(`(?<=\\p{P}|\\s|^)(ρ)${ROUGH_BREATHING}?`, 'gimu'); // prettier-ignore - // Change the coronis form after `reInitialSmooth`, - // by replacing the remaining smooth breathings (e. g. - // `.replace(new RegExp(SMOOTH_BREATHING, 'g'), CORONIS)`). return greekStr .normalize('NFD') .replace(reInitialSmooth, '$1') - .replace(reInitialRough, (match, vowelsGroup) => { - // @fixme(v0.13): case should be checked against the current word too. + .replace(reInitialRough, (m, vowelsGroup) => { + // @fixme(v0.14): case should be checked against the current word too. if (isUpperCase) return 'H' + vowelsGroup; else { return vowelsGroup.charAt(0).toUpperCase() === vowelsGroup.charAt(0) @@ -290,11 +330,53 @@ function grConvertBreathings( : 'h' + vowelsGroup; } }) - .replace(reDoubleRhoLazy, (match, doubleRho) => + .replace(reDoubleRhoLazy, (m, doubleRho) => doubleRho.toUpperCase() === doubleRho ? 'RRH' : 'rrh' ) - .replace(rho_rh ? reInitialRhoLazy : reInitialRho, (match, initialRho) => + .replace(rho_rh ? reInitialRhoLazy : reInitialRho, (m, initialRho) => isUpperCase ? initialRho + 'H' : initialRho + 'h' ) .normalize(); } + +/** + * Returns a transliterated string with converted coronides, + * following the given `coronisStyle`. + */ +function trApplyCoronis( + transliteratedStr: string, + coronisStyle?: Coronis +): string { + transliteratedStr = transliteratedStr + .normalize('NFD') + .replace( + new RegExp(`(?<=\\S)${Coronis.APOSTROPHE}(?=\\S)`, 'gu'), + SMOOTH_BREATHING + ); + + switch (coronisStyle) { + case Coronis.APOSTROPHE: + transliteratedStr = transliteratedStr.replace( + new RegExp(`(${SMOOTH_BREATHING})(\\p{M}*)`, 'gu'), + (m, $1, $2) => $2 + Coronis.APOSTROPHE + ); + break; + + case Coronis.NO: + transliteratedStr = transliteratedStr.replace( + new RegExp(SMOOTH_BREATHING, 'g'), + '' + ); + break; + + case Coronis.PSILI: + default: + transliteratedStr = transliteratedStr.replace( + new RegExp(`(\\p{M}*)(${SMOOTH_BREATHING})`, 'gu'), + (m, $1, $2) => $2 + $1 + ); + break; + } + + return transliteratedStr.normalize(); +} diff --git a/src/utils.ts b/src/utils.ts index 9b59ee3..d3c8cd4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -65,6 +65,55 @@ export function applyUppercaseChars(transliteratedStr: string): string { }); } +/** + * Takes a TLG beta code string and returns a beta code string following + * the `greek-conversion` convention. + */ +export function fromTLG(betaCodeStr: string): string { + return betaCodeStr + .toLowerCase() + .replace( + /(\*)([\(\)\\\/\+=\|\?]*)([a-z])/g, + (m, $1, $2, $3) => $3.toUpperCase() + $2 + ); +} + +/** + * Takes a beta code string following the `greek-conversion` convention + * and returns a TLG beta code string. + * + * @remarks + * The iota subscript must remain after its base letter. + */ +export function toTLG(betaCodeStr: string): string { + return betaCodeStr + .replace(/([a-z])([\(\)\\\/\+=\?]*)/gi, (m, $1, $2) => { + if ($1.toUpperCase() === $1) return '*' + $2 + $1; + else return m; + }) + .toUpperCase(); +} + +/** + * Returns a beta code string with a correct diacritics order. + * + * @remarks + * The correct order seems to be: (1) breathings; (2) diaereses; (3) accents; + * (4) iota subscript; (5) dot below. + */ +export function bcReorderDiacritics(betaCodeStr: string): string { + return betaCodeStr.replace( + /([\(\)\\\/\+=\|\?]{2,})/gi, + (match, diacritics) => { + const order: string[] = [')', '(', '+', '/', '\\', '=', '|', '?']; + return diacritics + .split('') + .sort((a: string, b: string) => order.indexOf(a) - order.indexOf(b)) + .join(''); + } + ); +} + /** * Returns an `IInternalConversionOptions` from a (mixed) preset or * a plain `IConversionOptions` object submitted by an end user. @@ -79,6 +128,9 @@ export function handleOptions( settings = applyPreset(settings); } + // Determining the case of a TLG string involves converting it. + if (fromType === KeyType.TLG_BETA_CODE) str = fromTLG(str); + return { isUpperCase: isUpperCase(str, fromType), ...settings @@ -114,13 +166,24 @@ export function isUpperCase(str: string, type: KeyType): boolean { * (2) Due to the poor Unicode canonical equivalences, any subsequent * normalization may break the replacements made by this function. */ -export function normalizeGreek(greekStr: string): string { - return greekStr - .normalize('NFD') - .replace(new RegExp(LATIN_TILDE, 'g'), GREEK_TILDE) - .normalize() - .replace(new RegExp(MIDDLE_DOT, 'g'), ANO_TELEIA) - .replace(new RegExp(';', 'g'), GREEK_QUESTION_MARK); +export function normalizeGreek( + greekStr: string, + useGreekQuestionMark: boolean = false, + skipUnicodeNormalization: boolean = false +): string { + if (!skipUnicodeNormalization) greekStr = greekStr.normalize('NFD'); + + greekStr = greekStr.replace(new RegExp(LATIN_TILDE, 'g'), GREEK_TILDE); + + if (!skipUnicodeNormalization) greekStr = greekStr.normalize(); + + greekStr = greekStr.replace(new RegExp(MIDDLE_DOT, 'g'), ANO_TELEIA); + + if (useGreekQuestionMark) { + greekStr = greekStr.replace(new RegExp(';', 'g'), GREEK_QUESTION_MARK); + } + + return greekStr; } /** @@ -195,6 +258,6 @@ export function removeExtraWhitespace(str: string): string { return str.replace(/(\s)+/g, '$1').trim(); } -export function sanitizeRegExpString(str): string { +export function sanitizeRegExpString(str: string): string { return str.replace(/[#-.]|[[-^]|[?|{}]/g, '\\$&'); } diff --git a/tests/GreekString.test.ts b/tests/GreekString.test.ts index 2632879..215511d 100644 --- a/tests/GreekString.test.ts +++ b/tests/GreekString.test.ts @@ -75,7 +75,7 @@ describe('GreekString', () => { test('From gr: Enabling/Disabling beta variant', () => { const gs1 = new GreekString('βάρβαρος', KeyType.GREEK) const gs2 = new GreekString('βάρβαρος', KeyType.GREEK, { - setGreekStyle: { + greekStyle: { disableBetaVariant: true } }) @@ -107,7 +107,7 @@ describe('GreekString', () => { expect(gs2.transliteration).toBe('sphínx, tunchánō') const gs3 = new GreekString('σφίγξ, τυγχάνω', KeyType.GREEK, { - setTransliterationStyle: { + transliterationStyle: { xi_ks: true, chi_kh: true } @@ -135,7 +135,7 @@ describe('GreekString', () => { expect(gs2.transliteration).toBe('sphínx, tunchánō') const gs3 = new GreekString('sphínks, tunkhánō', KeyType.TRANSLITERATION, { - setTransliterationStyle: { + transliterationStyle: { xi_ks: true, chi_kh: true } @@ -149,7 +149,7 @@ describe('GreekString', () => { test('From tr: using circumflex on long vowels', () => { const gs = new GreekString('ánthrôpos', KeyType.TRANSLITERATION, { - setTransliterationStyle: { + transliterationStyle: { useCxOverMacron: true } }) @@ -182,7 +182,7 @@ describe('GreekString', () => { expect(gs1.transliteration).toBe('ánthrōpos') const trStyleGs2: IConversionOptions = { - setTransliterationStyle: { + transliterationStyle: { lunatesigma_s: false } } @@ -194,7 +194,7 @@ describe('GreekString', () => { expect(gs2.transliteration).toBe('anthrōpos') const trStyleGs3: IConversionOptions = { - useAdditionalChars: undefined + additionalChars: undefined } const gs3 = new GreekString('a)/nqrwpos3', KeyType.BETA_CODE, [Preset.ALA_LC, trStyleGs3]) @@ -204,7 +204,7 @@ describe('GreekString', () => { expect(gs3.transliteration).toBe('anthrōpos3') const trStyleGs4: IConversionOptions = { - useAdditionalChars: AdditionalChar.DIGAMMA + additionalChars: AdditionalChar.DIGAMMA } const gs4 = new GreekString('a)/nqrwpos3', KeyType.BETA_CODE, [Preset.ALA_LC, trStyleGs4]) @@ -214,7 +214,7 @@ describe('GreekString', () => { expect(gs4.transliteration).toBe('anthrōpos3') const trStyleGs5: IConversionOptions = { - useAdditionalChars: AdditionalChar.LUNATE_SIGMA, + additionalChars: AdditionalChar.LUNATE_SIGMA, removeExtraWhitespace: true } const gs5 = new GreekString('a)/nqrwpos3', KeyType.BETA_CODE, [Preset.ALA_LC, trStyleGs5]) @@ -227,7 +227,7 @@ describe('GreekString', () => { test('Testing upsilon_y', () => { const options = { - setTransliterationStyle: { + transliterationStyle: { upsilon_y: true } } @@ -238,10 +238,10 @@ describe('GreekString', () => { test('Testing lunatesigma_s w/ additional chars enabled', () => { const options = { - setTransliterationStyle: { + transliterationStyle: { lunatesigma_s: true }, - useAdditionalChars: AdditionalChar.ALL + additionalChars: AdditionalChar.ALL } const gs = new GreekString('purós, ouranós, aülos', KeyType.TRANSLITERATION, options) diff --git a/tests/toBetaCode.test.ts b/tests/toBetaCode.test.ts index e353451..60319ad 100644 --- a/tests/toBetaCode.test.ts +++ b/tests/toBetaCode.test.ts @@ -1,4 +1,4 @@ -import { AdditionalChar, KeyType, toBetaCode } from '../src/index' +import { AdditionalChar, Coronis, KeyType, Preset, toBetaCode } from '../src/index' /* * Special characters: @@ -14,6 +14,9 @@ const aristotle = { } describe('From greek to beta code', () => { + + // Basic conversion + test.each` str | expected ${'ἄνθρωπος'} | ${'a)/nqrwpos'} @@ -31,6 +34,8 @@ describe('From greek to beta code', () => { ${aristotle.gr} | ${aristotle.bc} `('Basic conversion', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK)).toBe(expected) }) + // Removing diacritics + test.each` str | expected ${'ανθρωπος'} | ${'anqrwpos'} @@ -48,10 +53,28 @@ describe('From greek to beta code', () => { ${aristotle.gr} | ${aristotle.bcNoAcc} `('Removing diacritics', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK, { removeDiacritics: true })).toBe(expected) }) + // Testing useTLGStyle / TLG preset + + test.each` + str | expected + ${'ἄνθρωπος'} | ${'A)/NQRWPOS'} + ${'Ὁπλίτης'} | ${'*(OPLI/THS'} + ${'Ἄϊδα'} | ${'*)/AI+DA'} + ${'ΠΟΙῌ͂'} | ${'*P*O*I*=H|'} + ${'ῬΌΔΟΣ'} | ${'*(R*/O*D*O*S'} + `('Testing useTLGStyle / TLG preset', ({ str, expected }) => { + expect(toBetaCode(str, KeyType.GREEK, { betaCodeStyle: { useTLGStyle: true } })).toBe(expected) + expect(toBetaCode(str, KeyType.GREEK, Preset.TLG)).toBe(expected) + }) + + // Disabling beta variant + test('Disabling beta variant', () => { - expect(toBetaCode('βάρβαρος', KeyType.GREEK, { setGreekStyle: { disableBetaVariant: true } })).toBe('ba/rbaros') + expect(toBetaCode('βάρβαρος', KeyType.GREEK, { greekStyle: { disableBetaVariant: true } })).toBe('ba/rbaros') }) + // Testing rho rules + test.each` str | expected ${'Ρόδος'} | ${'Ro/dos'} @@ -61,18 +84,24 @@ describe('From greek to beta code', () => { ${'μάρμαρος'} | ${'ma/rmaros'} `('Testing rho rules', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK)).toBe(expected) }) + // Applying xi_ks / chi_kh + test.each` str | expected ${'Ξενοφῶν'} | ${'Cenofw=n'} ${'χορηγέω'} | ${'xorhge/w'} - `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + + // Using additional letters test('Using additional letters', () => { - expect(toBetaCode('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ', KeyType.GREEK, { useAdditionalChars: AdditionalChar.ALL })).toBe('vVjJs3S3#2*#2#1*#1#3*#3#5*#5') - expect(toBetaCode('ϝϜ\u03F2\u03F9', KeyType.GREEK, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('vVs3S3') - expect(toBetaCode('\u03F3\u037F\u03DB\u03DAϟϞϡϠ', KeyType.GREEK, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('\u03F3\u037F\u03DB\u03DAϟϞϡϠ') + expect(toBetaCode('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ', KeyType.GREEK, { additionalChars: AdditionalChar.ALL })).toBe('vVjJs3S3#2*#2#1*#1#3*#3#5*#5') + expect(toBetaCode('ϝϜ\u03F2\u03F9', KeyType.GREEK, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('vVs3S3') + expect(toBetaCode('\u03F3\u037F\u03DB\u03DAϟϞϡϠ', KeyType.GREEK, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('\u03F3\u037F\u03DB\u03DAϟϞϡϠ') }) + // Testing uppercase writing + test.each` str | expected ${'ΒΆΡΒΑΡΟΣ'} | ${'BA/RBAROS'} @@ -83,18 +112,35 @@ describe('From greek to beta code', () => { ${'ὙΙΌΣ'} | ${'U(IO/S'} `('Testing uppercase writing', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK)).toBe(expected) }) + // Testing whitespace behavior + test('Testing whitespace behavior', () => { expect(toBetaCode('αἴξ κριός', KeyType.GREEK)).toBe('ai)/c krio/s') expect(toBetaCode('αἴξ κριός', KeyType.GREEK, { removeExtraWhitespace: true })).toBe('ai)/c krio/s') }) + // Testing correctness with various word separators + test('Testing correctness with various word separators', () => { expect(toBetaCode('Ρόδος\nΡόδος\tΡόδος Ρόδος', KeyType.GREEK)).toBe('Ro/dos\nRo/dos\tRo/dos Ro/dos') expect(toBetaCode('Ῥόδος\nῬόδος\tῬόδος Ῥόδος', KeyType.GREEK)).toBe('R(o/dos\nR(o/dos\tR(o/dos R(o/dos') }) + + // Testing diacritics order + + test.each` + str | expected + ${'ἀΰλως'} | ${'a)u+/lws'} + ${'ᾖ'} | ${'h)=|'} + ${'ᾧ'} | ${'w(=|'} + `('Testing diacritics order', ({ str, expected }) => { expect(toBetaCode(str, KeyType.GREEK)).toBe(expected) }) + }) describe('From transliteration to beta code', () => { + + // Basic conversion + test.each` str | expected ${'ánthrōpos'} | ${'a)/nqrwpos'} @@ -116,6 +162,8 @@ describe('From transliteration to beta code', () => { ${aristotle.tr} | ${aristotle.bc} `('Basic conversion', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Removing diacritics + test.each` str | expected ${'ánthrōpos'} | ${'anqrwpos'} @@ -137,12 +185,29 @@ describe('From transliteration to beta code', () => { ${aristotle.tr} | ${aristotle.bcNoAcc} `('Removing diacritics', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { removeDiacritics: true })).toBe(expected) }) - // @fixme(v0.13) + // Testing useTLGStyle / TLG preset + + test.each` + str | expected + ${'ánthrōpos'} | ${'A)/NQRWPOS'} + ${'Hoplítēs'} | ${'*(OPLI/THS'} + ${'Áïda'} | ${'*)/AI+DA'} + ${'POIȨ̄̃'} | ${'*P*O*I*=H|'} + ${'RHÓDOS'} | ${'*(R*/O*D*O*S'} + `('Testing useTLGStyle / TLG preset', ({ str, expected }) => { + expect(toBetaCode(str, KeyType.TRANSLITERATION, { betaCodeStyle: { useTLGStyle: true } })).toBe(expected) + expect(toBetaCode(str, KeyType.TRANSLITERATION, Preset.TLG)).toBe(expected) + }) + + // Testing the combining dot below + test('Testing the combining dot below', () => { - expect(toBetaCode('Pátroḳlos', KeyType.TRANSLITERATION, { useAdditionalChars: AdditionalChar.ALL })).toBe('Pa/tro#3los') - expect(toBetaCode('Pátroḳlos', KeyType.TRANSLITERATION, { removeDiacritics: true, useAdditionalChars: AdditionalChar.ALL })).toBe('Patro#3los') + expect(toBetaCode('Pátroḳlos', KeyType.TRANSLITERATION, { additionalChars: AdditionalChar.ALL })).toBe('Pa/tro#3los') + expect(toBetaCode('Pátroḳlos', KeyType.TRANSLITERATION, { removeDiacritics: true, additionalChars: AdditionalChar.ALL })).toBe('Patro#3los') }) + // Testing breathings placement rules + test.each` str | expected ${'Ēṓs'} | ${'H)w/s'} @@ -152,11 +217,41 @@ describe('From transliteration to beta code', () => { ${'huḯdion'} | ${'u(i+/dion'} `('Testing breathings placement rules', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Testing coronides + test.each` - str | expected - ${'ka̓́n'} | ${'ka)/n'} - ${'tau̓tó'} | ${'tau)to/'} - `('Testing coronides', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + str | expected + ${'ka̓gṓ'} | ${'ka)gw/'} + ${'ka̓́n'} | ${'ka)/n'} + ${'ka’gṓ'} | ${'ka’gw/'} + ${'ká’n'} | ${'ka/’n'} + `('Testing coronides', ({ str, expected }) => expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected)) + + // Testing coronides, using coronis style + + test.each` + str | expected + ${'ka̓gṓ'} | ${'ka)gw/'} + ${'ka̓́n'} | ${'ka)/n'} + ${'ká’n'} | ${'ka/’n'} + `('Testing coronides, using coronis style (PSILI)', ({ str, expected }) => expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.PSILI } })).toBe(expected)) + + test.each` + str | expected + ${'ka’gṓ'} | ${'ka)gw/'} + ${'ká’n'} | ${'ka)/n'} + ${'ka̓́n'} | ${'ka)/n'} + `('Testing coronides, using coronis style (APOSTROPHE)', ({ str, expected }) => expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.APOSTROPHE } })).toBe(expected)) + + test.each` + str | expected + ${'ka̓gṓ'} | ${'ka)gw/'} + ${'ka’gṓ'} | ${'ka’gw/'} + ${'ka̓́n'} | ${'ka)/n'} + ${'ká’n'} | ${'ka/’n'} + `('Testing coronides, using coronis style (NO)', ({ str, expected }) => expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.NO } })).toBe(expected)) + + // Testing rho rules test.each` str | expected @@ -167,18 +262,54 @@ describe('From transliteration to beta code', () => { ${'mármaros'} | ${'ma/rmaros'} `('Testing rho rules', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Using circumflex on long vowels + test.each` str | expected ${'ánthrôpos'} | ${'a)/nqrwpos'} ${'Hoplítês'} | ${'O(pli/ths'} ${'Xenophỗn'} | ${'Cenofw=n'} - `('Using circumflex on long vowels', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { useCxOverMacron: true } })).toBe(expected) }) + `('Using circumflex on long vowels', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true } })).toBe(expected) }) + + // Applying beta_v + + test('Applying beta_v', () => { + expect(toBetaCode('várvaros', KeyType.TRANSLITERATION, { transliterationStyle: { beta_v: true } })) + .toBe('ba/rbaros') + }) + + // Applying eta_i + + test('Applying eta_i', () => { + expect(toBetaCode('hīdonī́', KeyType.TRANSLITERATION, { transliterationStyle: { eta_i: true } })) + .toBe('h(donh/') + }) + + // Applying eta_i, using circumflex + + test('Applying eta_i, using circumflex', () => { + expect(toBetaCode('hîdonî́', KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true, eta_i: true } })) + .toBe('h(donh/') + }) + + // Applying phi_f + + test.each` + str | expected + ${'fantasía'} | ${'fantasi/a'} + ${'Fainṓ'} | ${'Fainw/'} + ${'FILOSOFIA'} | ${'FILOSOFIA'} + `('Applying phi_f', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { phi_f: true } })).toBe(expected) }) + + // Applying xi_ks / chi_kh test.each` str | expected ${'Ksenophȭn'} | ${'Cenofw=n'} ${'khorēgéō'} | ${'xorhge/w'} - `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + + // Applying upsilon_y test.each` str | expected @@ -189,15 +320,19 @@ describe('From transliteration to beta code', () => { ${'hýdōr'} | ${'u(/dwr'} ${'Hýbla'} | ${'U(/bla'} ${'ý hỹ'} | ${'u)/ u(='} - `('Applying upsilon_y', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { upsilon_y: true } })).toBe(expected) }) + `('Applying upsilon_y', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION, { transliterationStyle: { upsilon_y: true } })).toBe(expected) }) + + // Using additional letters test('Using additional letters', () => { - expect(toBetaCode('wWjJcCc̄C̄qQḳḲs̄S̄', KeyType.TRANSLITERATION, { useAdditionalChars: AdditionalChar.ALL })).toBe('vVjJs3S3#2*#2#1*#1#3*#3#5*#5') - expect(toBetaCode('wWcC', KeyType.TRANSLITERATION, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('vVs3S3') - expect(toBetaCode('qQḳḲs̄S̄', KeyType.TRANSLITERATION, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('qQk?K?s%26S%26') + expect(toBetaCode('wWjJcCc̄C̄qQḳḲs̄S̄', KeyType.TRANSLITERATION, { additionalChars: AdditionalChar.ALL })).toBe('vVjJs3S3#2*#2#1*#1#3*#3#5*#5') + expect(toBetaCode('wWcC', KeyType.TRANSLITERATION, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('vVs3S3') + expect(toBetaCode('qQḳḲs̄S̄', KeyType.TRANSLITERATION, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('qQk?K?s%26S%26') }) - // @fixme(v0.13): check if rough breathings diphthongs rules must be overridden when converting from + // Testing uppercase writing + + // @fixme(v0.13.1): check if rough breathings diphthongs rules must be overridden when converting from // transliteration to beta code, as 'HUIÓS' -> 'UI(O/S' but 'ὙΙΌΣ' produces 'U(IO/S' (greek to beta code). test.each` str | expected @@ -209,8 +344,54 @@ describe('From transliteration to beta code', () => { ${'HUIÓS'} | ${'UI(O/S'} `('Testing uppercase writing', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Testing whitespace behavior + test('Testing whitespace behavior', () => { expect(toBetaCode('aíx kriós', KeyType.TRANSLITERATION)).toBe('ai)/c krio/s') expect(toBetaCode('aíx kriós', KeyType.TRANSLITERATION, { removeExtraWhitespace: true })).toBe('ai)/c krio/s') }) + + // Testing diacritics order + + test.each` + str | expected + ${'aǘlōs'} | ${'a)u+/lws'} + ${'ȩ̄̃'} | ${'h)=|'} + ${'hō̧̃'} | ${'w(=|'} + `('Testing diacritics order', ({ str, expected }) => { expect(toBetaCode(str, KeyType.TRANSLITERATION)).toBe(expected) }) + +}) + +describe('Self conversion', () => { + + // Testing useTLGStyle / TLG preset + + test.each` + str | expected + ${'a)/nqrwpos'} | ${'A)/NQRWPOS'} + ${'O(pli/ths'} | ${'*(OPLI/THS'} + ${'A)/i+da'} | ${'*)/AI+DA'} + ${'POIH=|'} | ${'*P*O*I*=H|'} + ${'R(O/DOS'} | ${'*(R*/O*D*O*S'} + `('Testing useTLGStyle / TLG preset', ({ str, expected }) => { + expect(toBetaCode(str, KeyType.BETA_CODE, { betaCodeStyle: { useTLGStyle: true } })).toBe(expected) + expect(toBetaCode(str, KeyType.BETA_CODE, Preset.TLG)).toBe(expected) + }) + + // Testing TLG beta code input + + test('Testing TLG beta code input', () => { + expect(toBetaCode('*(OPLI/THS', KeyType.TLG_BETA_CODE)).toBe('O(pli/ths') + expect(toBetaCode('*(OPLI/THS', KeyType.TLG_BETA_CODE, Preset.TLG)).toBe('*(OPLI/THS') + }) + + // Testing diacritics order + + test.each` + str | expected + ${'a)u/+lws'} | ${'a)u+/lws'} + ${'h|)='} | ${'h)=|'} + ${'w|(='} | ${'w(=|'} + `('Testing diacritics order', ({ str, expected }) => { expect(toBetaCode(str, KeyType.BETA_CODE)).toBe(expected) }) + }) diff --git a/tests/toGreek.test.ts b/tests/toGreek.test.ts index 4570e3a..6e9abda 100644 --- a/tests/toGreek.test.ts +++ b/tests/toGreek.test.ts @@ -1,9 +1,10 @@ -import { AdditionalChar, KeyType, toGreek } from '../src/index' +import { AdditionalChar, Coronis, KeyType, Preset, toGreek } from '../src/index' /* * Special characters: * - \u03D0 = Greek Beta Symbol * - \u03F2 = Greek Lunate Sigma Symbol + * - \u037E = Greek Question Mark */ const aristotle = { @@ -20,10 +21,13 @@ const thucydides = { const plato = { tr: 'Chalepón gé se elénxai, ō̃ Sṓkrates; all\' ouchì ka̓̀n paĩs se elénxeien hóti ouk alēthē̃ légeis?', trCx: 'Chalepón gé se elénxai, ỗ Sốkrates; all\' ouchì ka̓̀n paĩs se elénxeien hóti ouk alêthễ légeis?', - gr: 'Χαλεπόν γέ σε ἐλέγξαι, ὦ Σώκρατες· ἀλλ\' οὐχὶ κἂν παῖς σε ἐλέγξειεν ὅτι οὐκ ἀληθῆ λέγεις\u037E' + gr: 'Χαλεπόν γέ σε ἐλέγξαι, ὦ Σώκρατες· ἀλλ\' οὐχὶ κἂν παῖς σε ἐλέγξειεν ὅτι οὐκ ἀληθῆ λέγεις;' } describe('From beta code to greek', () => { + + // Basic conversion + test.each` str | expected ${'a)/nqrwpos'} | ${'ἄνθρωπος'} @@ -39,6 +43,8 @@ describe('From beta code to greek', () => { ${aristotle.bc} | ${aristotle.gr} `('Basic conversion', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE)).toBe(expected) }) + // Removing diacritics + test.each` str | expected ${'a)/nqrwpos'} | ${'ανθρωπος'} @@ -54,28 +60,42 @@ describe('From beta code to greek', () => { ${aristotle.bc} | ${aristotle.grNoAcc} `('Removing diacritics', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE, { removeDiacritics: true })).toBe(expected) }) - // v0.13 - /*test.each` - str | expected - ${')/ANQRWPOS'} | ${'ἄνθρωπος'} - ${'POIH|='} | ${'ποιῇ'} - ${')/*AI+DA'} | ${'Ἄϊδα'} - ${'BA/RBAROS'} | ${'βάρ\u03D0αρος'} - ${'(*OPLI/THS'} | ${'Ὁπλίτης'} - ${'(*Opli/ths'} | ${'Ὁπλίτης'} - ${'NOI='} | ${'vοῖ'} - ${'(/AGIOS3'} | ${'ἅγιοσ3'} - `('Testing TLG preset', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE, Preset.TLG)).toBe(expected) })*/ + // Testing useTLGStyle / TLG preset + + test.each` + str | expected + ${'A)/NQRWPOS'} | ${'ἄνθρωπος'} + ${'A)/nqrwpos'} | ${'ἄνθρωπος'} + ${'a)/nqrwpos'} | ${'ἄνθρωπος'} + ${'*(OPLI/THS'} | ${'Ὁπλίτης'} + ${'*(Opli/ths'} | ${'Ὁπλίτης'} + ${'*(opli/ths'} | ${'Ὁπλίτης'} + ${'*)/AI+DA'} | ${'Ἄϊδα'} + ${'*)/ai+da'} | ${'Ἄϊδα'} + ${'*P*O*I*=H|'} | ${'ΠΟΙῌ͂'} + ${'*p*o*i*=|h'} | ${'ΠΟΙῌ͂'} + ${'*(R*/O*D*O*S'} | ${'ῬΌΔΟΣ'} + ${'*(r*/o*d*o*s'} | ${'ῬΌΔΟΣ'} + `('Testing useTLGStyle / TLG preset', ({ str, expected }) => { + expect(toGreek(str, KeyType.TLG_BETA_CODE)).toBe(expected) + expect(toGreek(str, KeyType.TLG_BETA_CODE)).toBe(expected) + }) + + // Disabling beta variant test('Disabling beta variant', () => { - expect(toGreek('ba/rbaros', KeyType.BETA_CODE, { setGreekStyle: { disableBetaVariant: true } })).toBe('βάρβαρος') + expect(toGreek('ba/rbaros', KeyType.BETA_CODE, { greekStyle: { disableBetaVariant: true } })).toBe('βάρβαρος') }) + // Using lunate sigma + test('Using lunate sigma', () => { expect(toGreek('I)hsou=s Xristo\\s Qeou= Ui(o\\s Swth/r', KeyType.BETA_CODE)).toBe('Ἰησοῦς Χριστὸς Θεοῦ Υἱὸς Σωτήρ') - expect(toGreek('I)hsou=s Xristo\\s Qeou= Ui(o\\s Swth/r', KeyType.BETA_CODE, { setGreekStyle: { useLunateSigma: true } })).toBe('Ἰη\u03F2οῦ\u03F2 Χρι\u03F2τὸ\u03F2 Θεοῦ Υἱὸ\u03F2 \u03F9ωτήρ') + expect(toGreek('I)hsou=s Xristo\\s Qeou= Ui(o\\s Swth/r', KeyType.BETA_CODE, { greekStyle: { useLunateSigma: true } })).toBe('Ἰη\u03F2οῦ\u03F2 Χρι\u03F2τὸ\u03F2 Θεοῦ Υἱὸ\u03F2 \u03F9ωτήρ') }) + // Testing rho rules + test.each` str | expected ${'Ro/dos'} | ${'Ρόδος'} @@ -85,13 +105,16 @@ describe('From beta code to greek', () => { ${'ma/rmaros'} | ${'μάρμαρος'} `('Testing rho rules', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE)).toBe(expected) }) + // Using additional letters + test('Using additional letters', () => { - expect(toGreek('vVjJs3S3#2*#2#1*#1#3*#3#5*#5', KeyType.BETA_CODE, { useAdditionalChars: AdditionalChar.ALL })).toBe('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ') - expect(toGreek('vVs3S3', KeyType.BETA_CODE, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('ϝϜ\u03F2\u03F9') - expect(toGreek('#1*#1#3*#3#5*#5', KeyType.BETA_CODE, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('#1*#1#3*#3#5*#5') + expect(toGreek('vVjJs3S3#2*#2#1*#1#3*#3#5*#5', KeyType.BETA_CODE, { additionalChars: AdditionalChar.ALL })).toBe('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ') + expect(toGreek('vVs3S3', KeyType.BETA_CODE, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('ϝϜ\u03F2\u03F9') + expect(toGreek('#1*#1#3*#3#5*#5', KeyType.BETA_CODE, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('#1*#1#3*#3#5*#5') }) - // @fixme(v0.13): + // Testing uppercase writing + test.each` str | expected ${'BA/RBAROS'} | ${'ΒΆΡΒΑΡΟΣ'} @@ -103,14 +126,16 @@ describe('From beta code to greek', () => { ${'UI(O/S'} | ${'ὙΙΌΣ'} `('Testing uppercase writing', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE)).toBe(expected) }) + // Testing whitespace behavior + test('Testing whitespace behavior', () => { expect(toGreek('ai)/c krio/s', KeyType.BETA_CODE)).toBe('αἴξ κριός') expect(toGreek('ai)/c krio/s', KeyType.BETA_CODE, { removeExtraWhitespace: true })).toBe('αἴξ κριός') }) - // v0.13 - // Broken orders: `w|=(`, `w=(|`, `w=|(`. - /*test.each` + // Testing various diacritics order + + test.each` str | expected ${'w(|='} | ${'ᾧ'} ${'w(=|'} | ${'ᾧ'} @@ -118,10 +143,14 @@ describe('From beta code to greek', () => { ${'w|=('} | ${'ᾧ'} ${'w=(|'} | ${'ᾧ'} ${'w=|('} | ${'ᾧ'} - `('Applying various diacritics order', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE)).toBe(expected) })*/ + `('Testing various diacritics order', ({ str, expected }) => { expect(toGreek(str, KeyType.BETA_CODE)).toBe(expected) }) + }) describe('From transliteration to greek', () => { + + // Basic conversion + test.each` str | expected ${'ánthrōpos'} | ${'ἄνθρωπος'} @@ -143,6 +172,8 @@ describe('From transliteration to greek', () => { ${plato.tr} | ${plato.gr} `('Basic conversion', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Removing diacritics + test.each` str | expected ${'ánthrōpos'} | ${'ανθρωπος'} @@ -164,6 +195,8 @@ describe('From transliteration to greek', () => { ${thucydides.trNoAcc} | ${thucydides.grNoAcc} `('Removing diacritics', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { removeDiacritics: true })).toBe(expected) }) + // Testing breathings placement rules + test.each` str | expected ${'Ēṓs'} | ${'Ἠώς'} @@ -173,11 +206,41 @@ describe('From transliteration to greek', () => { ${'huḯdion'} | ${'ὑΐδιον'} `('Testing breathings placement rules', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Testing coronides + test.each` - str | expected - ${'ka̓́n'} | ${'κἄν'} - ${'tau̓tó'} | ${'ταὐτό'} - `('Testing coronides', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + str | expected + ${'ka̓gṓ'} | ${'κἀγώ'} + ${'ka̓́n'} | ${'κἄν'} + ${'ka’gṓ'} | ${'κα’γώ'} + ${'ká’n'} | ${'κά’ν'} + `('Testing coronides', ({ str, expected }) => expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected)) + + // Testing coronides, using coronis style + + test.each` + str | expected + ${'ka̓gṓ'} | ${'κἀγώ'} + ${'ka̓́n'} | ${'κἄν'} + ${'ká’n'} | ${'κά’ν'} + `('Testing coronides, using coronis style (PSILI)', ({ str, expected }) => expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.PSILI } })).toBe(expected)) + + test.each` + str | expected + ${'ka’gṓ'} | ${'κἀγώ'} + ${'ká’n'} | ${'κἄν'} + ${'ka̓́n'} | ${'κἄν'} + `('Testing coronides, using coronis style (APOSTROPHE)', ({ str, expected }) => expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.APOSTROPHE } })).toBe(expected)) + + test.each` + str | expected + ${'ka̓gṓ'} | ${'κἀγώ'} + ${'ka’gṓ'} | ${'κα’γώ'} + ${'ka̓́n'} | ${'κἄν'} + ${'ká’n'} | ${'κά’ν'} + `('Testing coronides, using coronis style (NO)', ({ str, expected }) => expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.NO } })).toBe(expected)) + + // Testing gamma nasals test.each` str | expected @@ -188,28 +251,38 @@ describe('From transliteration to greek', () => { ${'tunchánō'} | ${'τυγχάνω'} `('Testing gamma nasals', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Testing gamma nasals with xi_ks / chi_kh enabled + test.each` str | expected ${'sphínks'} | ${'σφίγξ'} ${'tunkhánō'} | ${'τυγχάνω'} - `('Testing gamma nasals with xi_ks / chi_kh enabled', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Testing gamma nasals with xi_ks / chi_kh enabled', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + + // Disabling beta variant test('Disabling beta variant', () => { - expect(toGreek('bárbaros', KeyType.TRANSLITERATION, { setGreekStyle: { disableBetaVariant: true } })).toBe('βάρβαρος') + expect(toGreek('bárbaros', KeyType.TRANSLITERATION, { greekStyle: { disableBetaVariant: true } })).toBe('βάρβαρος') }) + // Using lunate sigma + test('Using lunate sigma', () => { expect(toGreek('Iēsoũs Christòs Theoũ Huiòs Sōtḗr', KeyType.TRANSLITERATION)).toBe('Ἰησοῦς Χριστὸς Θεοῦ Υἱὸς Σωτήρ') - expect(toGreek('Iēsoũs Christòs Theoũ Huiòs Sōtḗr', KeyType.TRANSLITERATION, { setGreekStyle: { useLunateSigma: true } })).toBe('Ἰη\u03F2οῦ\u03F2 Χρι\u03F2τὸ\u03F2 Θεοῦ Υἱὸ\u03F2 \u03F9ωτήρ') + expect(toGreek('Iēsoũs Christòs Theoũ Huiòs Sōtḗr', KeyType.TRANSLITERATION, { greekStyle: { useLunateSigma: true } })).toBe('Ἰη\u03F2οῦ\u03F2 Χρι\u03F2τὸ\u03F2 Θεοῦ Υἱὸ\u03F2 \u03F9ωτήρ') }) + // Using circumflex on long vowels + test.each` str | expected ${'ánthrôpos'} | ${'ἄνθρωπος'} ${'Hoplítês'} | ${'Ὁπλίτης'} ${'Xenophỗn'} | ${'Ξενοφῶν'} ${plato.trCx} | ${plato.gr} - `('Using circumflex on long vowels', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { useCxOverMacron: true } })).toBe(expected) }) + `('Using circumflex on long vowels', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true } })).toBe(expected) }) + + // Testing rho rules test.each` str | expected @@ -220,11 +293,44 @@ describe('From transliteration to greek', () => { ${'mármaros'} | ${'μάρμαρος'} `('Testing rho rules', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Applying beta_v + test('Applying beta_v', () => { + expect(toGreek('várvaros', KeyType.TRANSLITERATION, { transliterationStyle: { beta_v: true } })) + .toBe('βάρ\u03D0αρος') + }) + + // Applying eta_i + + test('Applying eta_i', () => { + expect(toGreek('hīdonī́', KeyType.TRANSLITERATION, { transliterationStyle: { eta_i: true } })) + .toBe('ἡδονή') + }) + + // Applying eta_i, using circumflex + + test('Applying eta_i', () => { + expect(toGreek('hîdonî́', KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true, eta_i: true } })) + .toBe('ἡδονή') + }) + + // Applying phi_f + + test.each` + str | expected + ${'fantasía'} | ${'φαντασία'} + ${'Fainṓ'} | ${'Φαινώ'} + ${'FILOSOFIA'} | ${'ΦΙΛΟΣΟΦΙΑ'} + `('Applying phi_f', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { phi_f: true } })).toBe(expected) }) + + // Applying xi_ks / chi_kh + test.each` str | expected ${'Ksenophȭn'} | ${'Ξενοφῶν'} ${'khorēgéō'} | ${'χορηγέω'} - `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + + // Applying upsilon_y test.each` str | expected @@ -235,14 +341,18 @@ describe('From transliteration to greek', () => { ${'hýdōr'} | ${'ὕδωρ'} ${'Hýbla'} | ${'Ὕϐλα'} ${'ý hỹ'} | ${'ὔ ὗ'} - `('Applying upsilon_y', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { upsilon_y: true } })).toBe(expected) }) + `('Applying upsilon_y', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION, { transliterationStyle: { upsilon_y: true } })).toBe(expected) }) + + // Using additional letters test('Using additional letters', () => { - expect(toGreek('wWjJcCc̄C̄qQḳḲs̄S̄', KeyType.TRANSLITERATION, { useAdditionalChars: AdditionalChar.ALL })).toBe('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ') - expect(toGreek('wWcC', KeyType.TRANSLITERATION, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('ϝϜ\u03F2\u03F9') - expect(toGreek('qQḳḲs̄S̄', KeyType.TRANSLITERATION, { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('qQκ̣Κ̣σ̄Σ̄') + expect(toGreek('wWjJcCc̄C̄qQḳḲs̄S̄', KeyType.TRANSLITERATION, { additionalChars: AdditionalChar.ALL })).toBe('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ') + expect(toGreek('wWcC', KeyType.TRANSLITERATION, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('ϝϜ\u03F2\u03F9') + expect(toGreek('qQḳḲs̄S̄', KeyType.TRANSLITERATION, { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA] })).toBe('qQκ̣Κ̣σ̄Σ̄') }) + // Testing uppercase writing + test.each` str | expected ${'BÁRBAROS'} | ${'ΒΆΡΒΑΡΟΣ'} @@ -253,25 +363,59 @@ describe('From transliteration to greek', () => { ${'HUIÓS'} | ${'ὙΙΌΣ'} `('Testing uppercase writing', ({ str, expected }) => { expect(toGreek(str, KeyType.TRANSLITERATION)).toBe(expected) }) + // Testing whitespace behavior + test('Testing whitespace behavior', () => { expect(toGreek('aíx kriós', KeyType.TRANSLITERATION)).toBe('αἴξ κριός') expect(toGreek('aíx kriós', KeyType.TRANSLITERATION, { removeExtraWhitespace: true })).toBe('αἴξ κριός') }) + // Testing correctness with various word separators + test('Testing correctness with various word separators', () => { expect(toGreek('Ródos\nRódos\tRódos Ródos Ródos.', KeyType.TRANSLITERATION)).toBe('Ρόδος\nΡόδος\tΡόδος Ρόδος Ρόδος.') expect(toGreek('Rhódos\nRhódos\tRhódos Rhódos Rhódos.', KeyType.TRANSLITERATION)).toBe('Ῥόδος\nῬόδος\tῬόδος Ῥόδος Ῥόδος.') }) + }) describe('Self conversion', () => { + + // Disabling beta variant + test('Disabling beta variant', () => { const options = { - setGreekStyle: { + greekStyle: { disableBetaVariant: true } } expect(toGreek('βάρ\u03D0αρος', KeyType.GREEK)).toBe('βάρ\u03D0αρος') expect(toGreek('βάρ\u03D0αρος', KeyType.GREEK, options)).toBe('βάρβαρος') }) + + // Using greek question mark + + test('Using greek question mark', () => { + const options = { + greekStyle: { + useGreekQuestionMark: true + } + } + expect(toGreek('πῶς;', KeyType.GREEK)).toBe('πῶς;') + expect(toGreek('πῶς\u037E', KeyType.GREEK)).toBe('πῶς;') + expect(toGreek('πῶς;', KeyType.GREEK, options)).toBe('πῶς\u037E') + expect(toGreek('πῶς\u037E', KeyType.GREEK, options)).toBe('πῶς\u037E') + }) + + // Using lunate sigma + + test('Using lunate sigma', () => { + const options = { + greekStyle: { + useLunateSigma: true + } + } + expect(toGreek('ἅγιος', KeyType.GREEK, options)).toBe('ἅγιο\u03F2') + }) + }) diff --git a/tests/toTransliteration.test.ts b/tests/toTransliteration.test.ts index f445458..eddd923 100644 --- a/tests/toTransliteration.test.ts +++ b/tests/toTransliteration.test.ts @@ -1,4 +1,4 @@ -import { AdditionalChar, KeyType, Preset, toTransliteration } from '../src/index' +import { AdditionalChar, Coronis, KeyType, Preset, toTransliteration } from '../src/index' /* * Special characters: @@ -60,6 +60,56 @@ describe('From beta code to transliteration', () => { ${aristotle.bc} | ${aristotle.trNoAcc} `('Removing diacritics', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { removeDiacritics: true })).toBe(expected)) + // Testing useTLGStyle / TLG preset + + test.each` + str | expected + ${'A)/NQRWPOS'} | ${'ánthrōpos'} + ${'A)/nqrwpos'} | ${'ánthrōpos'} + ${'a)/nqrwpos'} | ${'ánthrōpos'} + ${'*(OPLI/THS'} | ${'Hoplítēs'} + ${'*(Opli/ths'} | ${'Hoplítēs'} + ${'*(opli/ths'} | ${'Hoplítēs'} + ${'*)/AI+DA'} | ${'Áïda'} + ${'*)/ai+da'} | ${'Áïda'} + ${'*P*O*I*=H|'} | ${'POIȨ̄̃'} + ${'*p*o*i*=|h'} | ${'POIȨ̄̃'} + ${'*(R*/O*D*O*S'} | ${'RHÓDOS'} + ${'*(r*/o*d*o*s'} | ${'RHÓDOS'} + `('Testing useTLGStyle / TLG preset', ({ str, expected }) => { + expect(toTransliteration(str, KeyType.TLG_BETA_CODE)).toBe(expected) + expect(toTransliteration(str, KeyType.TLG_BETA_CODE)).toBe(expected) + }) + + // Testing coronides + + test.each` + str | expected + ${'ka)gw/'} | ${'ka̓gṓ'} + ${'ka)/n'} | ${'ka̓́n'} + ${'tau)to/'} | ${'tau̓tó'} + `('Testing coronides', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE)).toBe(expected)) + + // Testing coronides, using coronis style + + test.each` + str | expected + ${'ka)gw/'} | ${'ka̓gṓ'} + ${'ka)/n'} | ${'ka̓́n'} + `('Testing coronides, using coronis style (PSILI)', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { setCoronisStyle: Coronis.PSILI } })).toBe(expected)) + + test.each` + str | expected + ${'ka)gw/'} | ${'ka’gṓ'} + ${'ka)/n'} | ${'ká’n'} + `('Testing coronides, using coronis style (APOSTROPHE)', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { setCoronisStyle: Coronis.APOSTROPHE } })).toBe(expected)) + + test.each` + str | expected + ${'ka)gw/'} | ${'kagṓ'} + ${'ka)/n'} | ${'kán'} + `('Testing coronides, using coronis style (NO)', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { setCoronisStyle: Coronis.NO } })).toBe(expected)) + // Testing rho rules test.each` @@ -84,7 +134,37 @@ describe('From beta code to transliteration', () => { ${'POLU/RRIZOS'} | ${'POLÚRRHIZOS'} ${'polu/r)r(izos'} | ${'polúrrhizos'} ${'ma/rmaros'} | ${'mármaros'} - `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { setTransliterationStyle: { rho_rh: true } })).toBe(expected)) + `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { rho_rh: true } })).toBe(expected)) + + // Applying beta_v + + test('Applying beta_v', () => { + expect(toTransliteration('ba/rbaros', KeyType.BETA_CODE, { transliterationStyle: { beta_v: true } })) + .toBe('várvaros') + }) + + // Applying eta_i + + test('Applying eta_i', () => { + expect(toTransliteration('h(donh/', KeyType.BETA_CODE, { transliterationStyle: { eta_i: true } })) + .toBe('hīdonī́') + }) + + // Applying eta_i, using circumflex + + test('Applying eta_i', () => { + expect(toTransliteration('h(donh/', KeyType.BETA_CODE, { transliterationStyle: { useCxOverMacron: true, eta_i: true } })) + .toBe('hîdonî́') + }) + + // Applying phi_f + + test.each` + str | expected + ${'fantasi/a'} | ${'fantasía'} + ${'Fainw/'} | ${'Fainṓ'} + ${'FILOSOFIA'} | ${'FILOSOFIA'} + `('Applying phi_f', ({ str, expected }) => { expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { phi_f: true } })).toBe(expected) }) // Applying upsilon_y @@ -97,13 +177,28 @@ describe('From beta code to transliteration', () => { ${'u(/dwr'} | ${'hýdōr'} ${'U(/bla'} | ${'Hýbla'} ${'u)/ u(='} | ${'ý hỹ'} - `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { setTransliterationStyle: { upsilon_y: true } })).toBe(expected)) + `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { upsilon_y: true } })).toBe(expected)) + + // Applying upsilon_y, preserving diphthongs au/eu/ou only + + test.each` + str | expected + ${'SOUIDAS'} | ${'SOUIDAS'} + ${'mauli/s'} | ${'maulís'} + ${'pneu=ma'} | ${'pneũma'} + ${'hu)/dwn'} | ${'ēýdōn'} + ${'pou='} | ${'poũ'} + ${'mui/agros'} | ${'myíagros'} + ${'wuto/s'} | ${'ōytós'} + `('Applying upsilon_y, preserving diphthongs au/eu/ou only', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE, { transliterationStyle: { upsilon_y: Preset.ISO } })).toBe(expected)) + + // Using additional letters test('Using additional letters', () => { - const enableAll = { useAdditionalChars: AdditionalChar.ALL } + const enableAll = { additionalChars: AdditionalChar.ALL } expect(toTransliteration('vVjJs3S3#2*#2#1*#1#3*#3#5*#5', KeyType.BETA_CODE, enableAll)).toBe('wWjJcCc̄C̄qQḳḲs̄S̄') - const enableDigammaAndLunateSigma = { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA]} + const enableDigammaAndLunateSigma = { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA]} expect(toTransliteration('vVs3S3', KeyType.BETA_CODE, enableDigammaAndLunateSigma)).toBe('wWcC') expect(toTransliteration('#1*#1#3*#3#5*#5', KeyType.GREEK, enableDigammaAndLunateSigma)).toBe('#1*#1#3*#3#5*#5') }) @@ -127,18 +222,17 @@ describe('From beta code to transliteration', () => { expect(toTransliteration('ai)/c krio/s', KeyType.BETA_CODE, { removeExtraWhitespace: true })).toBe('aíx kriós') }) - // Applying various diacritics order + // Testing various diacritics order - // v0.13 - broken orders: `w|=(`, `w=(|`, `w=|(`. - /*test.each` + test.each` str | expected - ${'w(|='} | ${'ᾧ'} - ${'w(=|'} | ${'ᾧ'} - ${'w|(='} | ${'ᾧ'} - ${'w|=('} | ${'ᾧ'} - ${'w=(|'} | ${'ᾧ'} - ${'w=|('} | ${'ᾧ'} - `('Applying various diacritics order', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE)).toBe(expected))*/ + ${'w(|='} | ${'hō̧̃'} + ${'w(=|'} | ${'hō̧̃'} + ${'w|(='} | ${'hō̧̃'} + ${'w|=('} | ${'hō̧̃'} + ${'w=(|'} | ${'hō̧̃'} + ${'w=|('} | ${'hō̧̃'} + `('Testing various diacritics order', ({ str, expected }) => expect(toTransliteration(str, KeyType.BETA_CODE)).toBe(expected)) }) @@ -202,11 +296,32 @@ describe('From greek to transliteration', () => { // Testing coronides test.each` - str | expected - ${'κἄν'} | ${'ka̓́n'} - ${'ταὐτό'} | ${'tau̓tó'} + str | expected + ${'κἀγώ'} | ${'ka̓gṓ'} + ${'κἄν'} | ${'ka̓́n'} + ${'κηὖ'} | ${'kēu̓̃'} `('Testing coronides', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK)).toBe(expected)) + // Testing coronides, using coronis style + + test.each` + str | expected + ${'κἀγώ'} | ${'ka̓gṓ'} + ${'κἄν'} | ${'ka̓́n'} + `('Testing coronides, using coronis style (PSILI)', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { setCoronisStyle: Coronis.PSILI } })).toBe(expected)) + + test.each` + str | expected + ${'κἀγώ'} | ${'ka’gṓ'} + ${'κἄν'} | ${'ká’n'} + `('Testing coronides, using coronis style (APOSTROPHE)', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { setCoronisStyle: Coronis.APOSTROPHE } })).toBe(expected)) + + test.each` + str | expected + ${'κἀγώ'} | ${'kagṓ'} + ${'κἄν'} | ${'kán'} + `('Testing coronides, using coronis style (NO)', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { setCoronisStyle: Coronis.NO } })).toBe(expected)) + // Testing gamma nasals test.each` @@ -224,12 +339,12 @@ describe('From greek to transliteration', () => { str | expected ${'σφίγξ'} | ${'sphínks'} ${'τυγχάνω'} | ${'tunkhánō'} - `('Testing gamma nasals with xi_ks / chi_kh enabled', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected)) + `('Testing gamma nasals with xi_ks / chi_kh enabled', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected)) // Disabling beta variant test('Disabling beta variant', () => { - expect(toTransliteration('βάρβαρος', KeyType.GREEK, { setGreekStyle: { disableBetaVariant: true } })).toBe('bárbaros') + expect(toTransliteration('βάρβαρος', KeyType.GREEK, { greekStyle: { disableBetaVariant: true } })).toBe('bárbaros') }) // Testing rho rules @@ -256,7 +371,7 @@ describe('From greek to transliteration', () => { ${'ΠΟΛΎΡΡΙΖΟΣ'} | ${'POLÚRRHIZOS'} ${'πολύῤῥιζος'} | ${'polúrrhizos'} ${'μάρμαρος'} | ${'mármaros'} - `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { setTransliterationStyle: { rho_rh: true } })).toBe(expected)) + `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { rho_rh: true } })).toBe(expected)) // Using circumflex on long vowels @@ -266,19 +381,51 @@ describe('From greek to transliteration', () => { ${'Ὁπλίτης'} | ${'Hoplítês'} ${'Ξενοφῶν'} | ${'Xenophỗn'} ${plato.gr} | ${plato.trCrx} - `('Using circumflex on long vowels', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { setTransliterationStyle: { useCxOverMacron: true } })).toBe(expected)) + `('Using circumflex on long vowels', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { useCxOverMacron: true } })).toBe(expected)) + + // Applying beta_v + + test('Applying beta_v', () => { + expect(toTransliteration('βάρ\u03D0αρος', KeyType.GREEK, { transliterationStyle: { beta_v: true } })) + .toBe('várvaros') + }) + + // Applying eta_i + + test('Applying eta_i', () => { + expect(toTransliteration('ἡδονή', KeyType.GREEK, { transliterationStyle: { eta_i: true } })) + .toBe('hīdonī́') + }) + + // Applying eta_i, using circumflex + + test('Applying eta_i', () => { + expect(toTransliteration('ἡδονή', KeyType.GREEK, { transliterationStyle: { useCxOverMacron: true, eta_i: true } })) + .toBe('hîdonî́') + }) + + // Applying phi_f + + test.each` + str | expected + ${'φαντασία'} | ${'fantasía'} + ${'Φαινώ'} | ${'Fainṓ'} + ${'ΦΙΛΟΣΟΦΙΑ'} | ${'FILOSOFIA'} + `('Applying phi_f', ({ str, expected }) => { expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { phi_f: true } })).toBe(expected) }) // Applying xi_ks / chi_kh test.each` str | expected + ${'ΞΕΝΟΦΩΝ'} | ${'KSENOPHŌN'} ${'Ξενοφῶν'} | ${'Ksenophō̃n'} + ${'ΧΟΡΗΓΕΩ'} | ${'KHORĒGEŌ'} ${'χορηγέω'} | ${'khorēgéō'} ${'σφίγξ'} | ${'sphínks'} ${'μελαγχολία'} | ${'melankholía'} ${'σφίνξ'} | ${'sphínks'} ${'μελανχολία'} | ${'melankholía'} - `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toTransliteration(str, KeyType.GREEK, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) // Applying upsilon_y @@ -292,15 +439,28 @@ describe('From greek to transliteration', () => { ${'ὕδωρ'} | ${'hýdōr'} ${'Ὕϐλα'} | ${'Hýbla'} ${'ὔ ὗ'} | ${'ý hỹ'} - `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { setTransliterationStyle: { upsilon_y: true } })).toBe(expected)) + `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { upsilon_y: true } })).toBe(expected)) + + // Applying upsilon_y, preserving diphthongs au/eu/ou only + + test.each` + str | expected + ${'ΣΟΥΙΔΑΣ'} | ${'SOUIDAS'} + ${'μαυλίς'} | ${'maulís'} + ${'πνεῦμα'} | ${'pneũma'} + ${'ηὔδων'} | ${'ēýdōn'} + ${'ποῦ'} | ${'poũ'} + ${'μυίαγρος'} | ${'myíagros'} + ${'ωὐτός'} | ${'ōytós'} + `('Applying upsilon_y, preserving diphthongs au/eu/ou only', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, { transliterationStyle: { upsilon_y: Preset.ISO } })).toBe(expected)) // Using additional letters test('Using additional letters', () => { - const enableAll = { useAdditionalChars: AdditionalChar.ALL } + const enableAll = { additionalChars: AdditionalChar.ALL } expect(toTransliteration('ϝϜ\u03F3\u037F\u03F2\u03F9\u03DB\u03DAϟϞϙϘϡϠ', KeyType.GREEK, enableAll)).toBe('wWjJcCc̄C̄qQḳḲs̄S̄') - const enableDigammaAndLunateSigma = { useAdditionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA]} + const enableDigammaAndLunateSigma = { additionalChars: [AdditionalChar.DIGAMMA, AdditionalChar.LUNATE_SIGMA]} expect(toTransliteration('ϝϜ\u03F2\u03F9', KeyType.GREEK, enableDigammaAndLunateSigma)).toBe('wWcC') expect(toTransliteration('\u03F3\u037F\u03DB\u03DAϟϞϡϠ', KeyType.GREEK, enableDigammaAndLunateSigma)).toBe('\u03F3\u037F\u03DB\u03DAϟϞϡϠ') }) @@ -331,7 +491,7 @@ describe('From greek to transliteration', () => { expect(toTransliteration('Ῥόδος\nῬόδος\tῬόδος Ῥόδος', KeyType.GREEK)).toBe('Rhódos\nRhódos\tRhódos Rhódos') }) - // Applying preset ALA_LC (the following sentences are given by the ALA-LC romanization table). + // Applying preset ALA_LC (the following sentences are given by the ALA-LC romanization table) test.each` str | expected @@ -348,7 +508,7 @@ describe('From greek to transliteration', () => { ${'ὑϊκὸν πάσχειν'} | ${'hyikon paschein'} ${'εἶπε πρὸς τὸν ἄνδρα τὸν ἑωυτῆς'} | ${'eipe pros ton andra ton heōutēs'} ${'τί τοῦδ’ ἂν εὕρημ’ ηὗρον εὐτυχέστερον;'} | ${'ti toud’ an heurēm’ hēuron eutychesteron?'} - ${'Τοῦ Κατὰ πασῶν αἱρέσεων ἐλέγχου βιβλίον αʹ'} | ${'Tou Kata pasōn haireseōn elenchou biblion 1'} + ${'Τοῦ Κατὰ πασῶν αἱρέσεων ἐλέγχου βιβλίον αʹ'} | ${'Tou Kata pasōn haireseōn elenchou biblion 1'} ${'καλὸν κἀγαθόν'} | ${'kalon kagathon'} ${'ᾤχοντο θοἰμάτιον λαβόντες μου'} | ${'ōchonto thoimation labontes mou'} ${'Περὶ ἰλίγγων'} | ${'Peri ilingōn'} @@ -360,10 +520,55 @@ describe('From greek to transliteration', () => { ${'Πάτροϙλος'} | ${'Patroḳlos'} `('Applying preset ALA_LC', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, Preset.ALA_LC)).toBe(expected)) + + // Applying preset ISO (-> ISO 843 [1997]) + + test.each` + str | expected + ${'Ἡσιόδου τοῦ Ἀσκραίου Ἔργα καὶ ἡμέραι'} | ${'Hīsiódou toũ Askraíou Érga kaì hīmérai'} + ${'Ἡ τοῦ Ὁμήρου Ἰλιάς'} | ${'Hī toũ Homī́rou Iliás'} + ${'Φίληβος ἢ Περὶ ἡδονῆς'} | ${'Fílīvos ī̀ Perì hīdonī̃s'} + ${'Ἀγνώστῳ θεῷ'} | ${'Agnṓstō̧ theō̧̃'} + ${'κεῖται παρ’ Ἅιδῃ'} | ${'keĩtai par’ Háidī̧'} + `('Applying preset ISO (-> ISO 843 [1997])', ({ str, expected }) => expect(toTransliteration(str, KeyType.GREEK, Preset.ISO)).toBe(expected)) + }) describe('Self conversion', () => { + // Testing coronides + + test.each` + str | expected + ${'ka̓gṓ'} | ${'ka̓gṓ'} + ${'ka̓́n'} | ${'ka̓́n'} + ${'ka’gṓ'} | ${'ka̓gṓ'} + ${'ká’n'} | ${'ka̓́n'} + `('Testing coronides', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION)).toBe(expected)) + + // Testing coronides, using coronis style + + test.each` + str | expected + ${'ka̓gṓ'} | ${'ka̓gṓ'} + ${'ka’gṓ'} | ${'ka̓gṓ'} + ${'ká’n'} | ${'ka̓́n'} + `('Testing coronides, using coronis style (PSILI)', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.PSILI } })).toBe(expected)) + + test.each` + str | expected + ${'ka̓gṓ'} | ${'ka’gṓ'} + ${'ka’gṓ'} | ${'ka’gṓ'} + ${'ka̓́n'} | ${'ká’n'} + `('Testing coronides, using coronis style (APOSTROPHE)', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.APOSTROPHE } })).toBe(expected)) + + test.each` + str | expected + ${'ka̓gṓ'} | ${'kagṓ'} + ${'ka’gṓ'} | ${'kagṓ'} + ${'ká’n'} | ${'kán'} + `('Testing coronides, using coronis style (NO)', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { setCoronisStyle: Coronis.NO } })).toBe(expected)) + // Testing rho rules, applying rho_rh test.each` @@ -372,7 +577,7 @@ describe('Self conversion', () => { ${'RÓDOS'} | ${'RHÓDOS'} ${'polúrrizos'} | ${'polúrrhizos'} ${'POLÚRRIZOS'} | ${'POLÚRRHIZOS'} - `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { rho_rh: true } })).toBe(expected)) + `('Testing rho rules, applying rho_rh', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { rho_rh: true } })).toBe(expected)) // Using circumflex on long vowels @@ -381,17 +586,51 @@ describe('Self conversion', () => { ${'ánthrōpos'} | ${'ánthrôpos'} ${'chorēgéō'} | ${'chorêgéô'} ${'Xenophō̃n'} | ${'Xenophỗn'} - `('Using circumflex on long vowels', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { useCxOverMacron: true } })).toBe(expected)) + `('Using circumflex on long vowels', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true } })).toBe(expected)) + + // Applying beta_v + + test('Applying beta_v', () => { + expect(toTransliteration('bárbaros', KeyType.TRANSLITERATION, { transliterationStyle: { beta_v: true } })) + .toBe('várvaros') + }) + + // Applying eta_i + + test('Applying eta_i', () => { + expect(toTransliteration('hēdonḗ', KeyType.TRANSLITERATION, { transliterationStyle: { eta_i: true } })) + .toBe('hīdonī́') + }) + + // Applying eta_i, using circumflex + + test('Applying eta_i', () => { + expect(toTransliteration('hêdonế', KeyType.TRANSLITERATION, { transliterationStyle: { useCxOverMacron: true, eta_i: true } })) + .toBe('hîdonî́') + }) + + // Applying phi_f + + test.each` + str | expected + ${'phantasía'} | ${'fantasía'} + ${'Phainṓ'} | ${'Fainṓ'} + ${'PHILOSOFIA'} | ${'FILOSOFIA'} + `('Applying phi_f', ({ str, expected }) => { expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { phi_f: true } })).toBe(expected) }) // Applying xi_ks / chi_kh test.each` str | expected + ${'XENOPHŌN'} | ${'KSENOPHŌN'} ${'Xenophō̃n'} | ${'Ksenophō̃n'} - ${'Chorēgéō'} | ${'khorēgéō'} + ${'xenophō̃n'} | ${'ksenophō̃n'} + ${'CHORĒGEŌ'} | ${'KHORĒGEŌ'} + ${'Chorēgéō'} | ${'Khorēgéō'} + ${'chorēgéō'} | ${'khorēgéō'} ${'sphínx'} | ${'sphínks'} ${'melancholía'} | ${'melankholía'} - `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toTransliteration(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) + `('Applying xi_ks / chi_kh', ({ str, expected }) => { expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { xi_ks: true, chi_kh: true } })).toBe(expected) }) // Applying upsilon_y @@ -413,7 +652,20 @@ describe('Self conversion', () => { ${'Hýbla'} | ${'Hýbla'} ${'ú hũ'} | ${'ý hỹ'} ${'ý hỹ'} | ${'ý hỹ'} - `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { upsilon_y: true } })).toBe(expected)) + `('Applying upsilon_y', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { upsilon_y: true } })).toBe(expected)) + + // Applying upsilon_y, preserving diphthongs au/eu/ou only + + test.each` + str | expected + ${'SOYIDAS'} | ${'SOUIDAS'} + ${'maylís'} | ${'maulís'} + ${'pneỹma'} | ${'pneũma'} + ${'ēúdōn'} | ${'ēýdōn'} + ${'poỹ'} | ${'poũ'} + ${'muíagros'} | ${'myíagros'} + ${'ōutós'} | ${'ōytós'} + `('Applying upsilon_y, preserving diphthongs au/eu/ou only', ({ str, expected }) => expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { upsilon_y: Preset.ISO } })).toBe(expected)) // Applying lunatesigma_s @@ -422,6 +674,6 @@ describe('Self conversion', () => { ${'hagioc'} | ${'hagios'} ${'Cōkrátēc'} | ${'Sōkrátēs'} ${'ICHTUC'} | ${'ICHTUS'} - `('Applying lunatesigma_s', ({ str, expected }) => { expect(toTransliteration(str, KeyType.TRANSLITERATION, { setTransliterationStyle: { lunatesigma_s: true } })).toBe(expected) }) + `('Applying lunatesigma_s', ({ str, expected }) => { expect(toTransliteration(str, KeyType.TRANSLITERATION, { transliterationStyle: { lunatesigma_s: true } })).toBe(expected) }) }) diff --git a/tsconfig.json b/tsconfig.json index 2fe8638..f51a4e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "useDefineForClassFields": true, + "esModuleInterop": true, "isolatedModules": true, "target": "ES2018" }