greek-name-correction 2.2.1 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +411 -16
- package/package.json +12 -6
- package/src/cases.js +43 -50
- package/src/rulesParser.js +42 -2
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# GreekNameCorrection
|
|
2
2
|
|
|
3
|
-
A powerful, zero-dependency
|
|
3
|
+
A powerful, zero-dependency library for correcting, formatting, and validating Greek names with advanced features including transliteration, genitive conversion, and intelligent name processing. Works seamlessly in both Node.js and browser environments.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|

|
|
8
|
+

|
|
8
9
|
|
|
9
10
|
## Features
|
|
10
11
|
|
|
@@ -14,7 +15,7 @@ A powerful, zero-dependency Node.js library for correcting, formatting, and vali
|
|
|
14
15
|
📝 **Smart Formatting** - Proper capitalization and syntax
|
|
15
16
|
👔 **Title Support** - Handles Greek honorifics (Δρ., Καθ., etc.)
|
|
16
17
|
🎩 **Auto Title Addition** - Automatically adds general titles (Κ. for men, Κα for women)
|
|
17
|
-
🔀 **Case Conversion** - Genitive, vocative, and accusative
|
|
18
|
+
🔀 **Case Conversion** - Genitive, vocative (κλητική), and accusative (αιτιατική); distinct rules for names like Πρόδρομος → Πρόδρομο / Πρόδρομε
|
|
18
19
|
🎯 **Gender Detection** - Identifies gender from name endings
|
|
19
20
|
📊 **Statistics** - Comprehensive name analysis
|
|
20
21
|
🔍 **Diminutive Detection** - Recognizes nickname patterns
|
|
@@ -120,10 +121,14 @@ greek-name-correction -name "Μαρία Κωνσταντίνου" -detectGender
|
|
|
120
121
|
### Examples
|
|
121
122
|
|
|
122
123
|
```bash
|
|
123
|
-
# Vocative
|
|
124
|
-
greek-name-correction -name "
|
|
124
|
+
# Vocative (addressing): Πρόδρομος → Πρόδρομε
|
|
125
|
+
greek-name-correction -name "Πρόδρομος" -convertToCase vocative
|
|
126
|
+
|
|
127
|
+
# Accusative (direct object): Πρόδρομος → Πρόδρομο
|
|
128
|
+
greek-name-correction -name "Πρόδρομος" -convertToCase accusative
|
|
125
129
|
|
|
126
|
-
#
|
|
130
|
+
# Full name examples
|
|
131
|
+
greek-name-correction -name "Γιώργος Παπαδόπουλος" -convertToCase vocative
|
|
127
132
|
greek-name-correction -name "Δημήτρης Νικολάου" -convertToCase accusative
|
|
128
133
|
|
|
129
134
|
# Transliteration
|
|
@@ -216,7 +221,10 @@ GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
|
216
221
|
// }
|
|
217
222
|
```
|
|
218
223
|
|
|
219
|
-
### Vocative Case Conversion
|
|
224
|
+
### Vocative Case Conversion (Κλητική)
|
|
225
|
+
|
|
226
|
+
Use `convertToCase: 'vocative'` when **addressing** someone directly (e.g. «Γεια σου, …», «Αγαπητέ …»).
|
|
227
|
+
|
|
220
228
|
```javascript
|
|
221
229
|
// Convert to vocative case (for addressing someone)
|
|
222
230
|
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
@@ -224,6 +232,13 @@ GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
|
224
232
|
});
|
|
225
233
|
// → "Γιώργο Παπαδόπουλο"
|
|
226
234
|
|
|
235
|
+
// Longer first names often form vocative in -ε
|
|
236
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'vocative' });
|
|
237
|
+
// → "Στέφανε"
|
|
238
|
+
|
|
239
|
+
GreekNameCorrection('Πρόδρομος', { convertToCase: 'vocative' });
|
|
240
|
+
// → "Πρόδρομε"
|
|
241
|
+
|
|
227
242
|
// With preserveOriginal to get both forms
|
|
228
243
|
GreekNameCorrection('Γιάννης Αλεξίου', {
|
|
229
244
|
convertToCase: 'vocative',
|
|
@@ -241,7 +256,23 @@ GreekNameCorrection('Μαρία Κωνσταντίνου', {
|
|
|
241
256
|
// → "Μαρία Κωνσταντίνου"
|
|
242
257
|
```
|
|
243
258
|
|
|
244
|
-
|
|
259
|
+
**Masculine names ending in -ος (vocative)** depend on the name and position (first name vs surname):
|
|
260
|
+
|
|
261
|
+
| Pattern | Examples (nominative → vocative) |
|
|
262
|
+
|--------|-----------------------------------|
|
|
263
|
+
| Common short first names → **-ο** | Γιώργος → Γιώργο, Νίκος → Νίκο, Πέτρος → Πέτρο |
|
|
264
|
+
| Listed longer first names → **-ε** | Στέφανος → Στέφανε, Κωνσταντίνος → Κωνσταντίνε, Αλέξανδρος → Αλέξανδρε |
|
|
265
|
+
| Other longer first names (3+ syllables) → **-ε** | Πρόδρομος → Πρόδρομε |
|
|
266
|
+
| Paroxytone surnames → **-ο** | Παπαδόπουλος → Παπαδόπουλο |
|
|
267
|
+
| Oxytone surnames → **-ε** | Ξινός → Ξινέ |
|
|
268
|
+
| Oxytone first names | Often unchanged (e.g. Νικολός) |
|
|
269
|
+
|
|
270
|
+
Rules are loaded from `names_klitiki.md` in Node.js when available, with the same logic as hard-coded fallbacks in `src/cases.js` and `src/constants.js`.
|
|
271
|
+
|
|
272
|
+
### Accusative Case Conversion (Αιτιατική)
|
|
273
|
+
|
|
274
|
+
Use `convertToCase: 'accusative'` for **direct objects** (e.g. «Είδα τον …», «Καλώ την …»).
|
|
275
|
+
|
|
245
276
|
```javascript
|
|
246
277
|
// Convert to accusative case (for direct objects)
|
|
247
278
|
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
@@ -249,6 +280,13 @@ GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
|
249
280
|
});
|
|
250
281
|
// → "Γιώργο Παπαδόπουλο"
|
|
251
282
|
|
|
283
|
+
// All masculine -ος names → -ο in accusative (unlike vocative)
|
|
284
|
+
GreekNameCorrection('Πρόδρομος', { convertToCase: 'accusative' });
|
|
285
|
+
// → "Πρόδρομο"
|
|
286
|
+
|
|
287
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'accusative' });
|
|
288
|
+
// → "Στέφανο"
|
|
289
|
+
|
|
252
290
|
// With preserveOriginal to get both forms
|
|
253
291
|
GreekNameCorrection('Κώστας Παπαδάκης', {
|
|
254
292
|
convertToCase: 'accusative',
|
|
@@ -266,6 +304,39 @@ GreekNameCorrection('Μαρία Κωνσταντίνου', {
|
|
|
266
304
|
// → "Μαρία Κωνσταντίνου"
|
|
267
305
|
```
|
|
268
306
|
|
|
307
|
+
**Masculine accusative endings** (consistent; no stress-based split for -ος):
|
|
308
|
+
|
|
309
|
+
| Ending | Rule | Examples |
|
|
310
|
+
|--------|------|----------|
|
|
311
|
+
| **-ος** | Always **-ο** | Πρόδρομος → Πρόδρομο, Στέφανος → Στέφανο, Γιώργος → Γιώργο |
|
|
312
|
+
| **-ης** | **-η** | Δημήτρης → Δημήτρη, Γιάννης → Γιάννη |
|
|
313
|
+
| **-ας** | **-α** (or **-η** if name follows -ης pattern) | Κώστας → Κώστα, Θανάσης → Θανάση |
|
|
314
|
+
| **-ούς** | **-ού** | (rare in names) |
|
|
315
|
+
|
|
316
|
+
Rules are loaded from `names_aitiatiki.md` in Node.js when available.
|
|
317
|
+
|
|
318
|
+
### Vocative vs Accusative (do not confuse)
|
|
319
|
+
|
|
320
|
+
For the same nominative name, **vocative** and **accusative** can differ when the vocative uses **-ε**:
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
const name = 'Πρόδρομος';
|
|
324
|
+
|
|
325
|
+
GreekNameCorrection(name, { convertToCase: 'accusative' }); // → "Πρόδρομο" (τον Πρόδρομο)
|
|
326
|
+
GreekNameCorrection(name, { convertToCase: 'vocative' }); // → "Πρόδρομε" (direct address)
|
|
327
|
+
|
|
328
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'accusative' }); // → "Στέφανο"
|
|
329
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'vocative' }); // → "Στέφανε"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
| Name (ονομαστική) | Accusative (αιτιατική) | Vocative (κλητική) | Typical use |
|
|
333
|
+
|-------------------|------------------------|-------------------|-------------|
|
|
334
|
+
| Πρόδρομος | Πρόδρομο | Πρόδρομε | τον Πρόδρομο / «Πρόδρομε!» |
|
|
335
|
+
| Στέφανος | Στέφανο | Στέφανε | τον Στέφανο / «Στέφανε!» |
|
|
336
|
+
| Γιώργος | Γιώργο | Γιώργο | τον Γιώργο / «Γιώργο!» |
|
|
337
|
+
|
|
338
|
+
Use **vocative** for calling or greeting someone; use **accusative** for object forms after verbs or with **τον/την**.
|
|
339
|
+
|
|
269
340
|
### Title Handling
|
|
270
341
|
```javascript
|
|
271
342
|
GreekNameCorrection('δρ. γιώργος παπαδόπουλος', {
|
|
@@ -631,6 +702,295 @@ const { corrected, sortKey } = GreekNameCorrection(name, {
|
|
|
631
702
|
// [corrected, sortKey]
|
|
632
703
|
```
|
|
633
704
|
|
|
705
|
+
## Option Interactions and Processing Order
|
|
706
|
+
|
|
707
|
+
When using multiple options together, it's important to understand the processing order and how options interact with each other. This section explains the execution pipeline, option conflicts, and best practices.
|
|
708
|
+
|
|
709
|
+
### Processing Order
|
|
710
|
+
|
|
711
|
+
The library processes names in a specific order to ensure correct results. Here's the exact sequence:
|
|
712
|
+
|
|
713
|
+
```mermaid
|
|
714
|
+
flowchart TD
|
|
715
|
+
Start[Input Name] --> ExtractTitle[1. Extract Title<br/>handleTitles]
|
|
716
|
+
ExtractTitle --> Transliterate[2. Transliteration<br/>transliterate]
|
|
717
|
+
Transliterate --> RemoveSpaces[3. Remove Extra Spaces<br/>removeExtraSpaces]
|
|
718
|
+
RemoveSpaces --> Katharevousa[4. Katharevousa Conversion<br/>recognizeKatharevousa]
|
|
719
|
+
Katharevousa --> Suggest[5. Suggest Corrections<br/>suggestCorrections]
|
|
720
|
+
Suggest --> Normalize[6. Normalize Tonotics<br/>normalizeTonotics]
|
|
721
|
+
Normalize --> Diacritics[7. Handle Diacritics<br/>handleDiacritics]
|
|
722
|
+
Diacritics --> Capitalize[8. Capitalize & Split<br/>splitNames]
|
|
723
|
+
Capitalize --> AddAccents[9. Add Accents<br/>addAccents]
|
|
724
|
+
AddAccents --> Genitive[10. Convert to Genitive<br/>convertToGenitive<br/>stored separately]
|
|
725
|
+
Genitive --> DatabaseSafe[11. Database-Safe<br/>databaseSafe]
|
|
726
|
+
DatabaseSafe --> AddTitle[12. Add General Title<br/>addGeneralTitle]
|
|
727
|
+
AddTitle --> CaseConvert[13. Convert to Case<br/>convertToCase]
|
|
728
|
+
CaseConvert --> ReattachTitle[14. Re-attach Title]
|
|
729
|
+
ReattachTitle --> Metadata[15. Generate Metadata<br/>detectGender, detectDiminutive<br/>generateSortKey, statistics]
|
|
730
|
+
Metadata --> End[Output Result]
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
#### Detailed Processing Steps
|
|
734
|
+
|
|
735
|
+
1. **Title Extraction** (`handleTitles: true` by default)
|
|
736
|
+
- Extracts titles like "Δρ.", "Καθ.", "Κ." from the beginning of the name
|
|
737
|
+
- Title is stored separately and removed from processing
|
|
738
|
+
|
|
739
|
+
2. **Transliteration** (`transliterate`)
|
|
740
|
+
- Converts between Greek, Greeklish, and Latin scripts
|
|
741
|
+
- Applied early to ensure subsequent steps work on the correct script
|
|
742
|
+
|
|
743
|
+
3. **Space Normalization** (`removeExtraSpaces: true` by default)
|
|
744
|
+
- Removes extra whitespace and trims the name
|
|
745
|
+
|
|
746
|
+
4. **Katharevousa Recognition** (`recognizeKatharevousa`)
|
|
747
|
+
- Converts archaic Greek forms to modern Greek
|
|
748
|
+
|
|
749
|
+
5. **Suggestion Corrections** (`suggestCorrections`)
|
|
750
|
+
- Suggests corrections for common misspellings
|
|
751
|
+
- Applied before normalization to fix errors early
|
|
752
|
+
|
|
753
|
+
6. **Tonotic Normalization** (`normalizeTonotics: true` by default)
|
|
754
|
+
- Normalizes Greek accent marks to standard forms
|
|
755
|
+
|
|
756
|
+
7. **Diacritic Handling** (`handleDiacritics: true` by default)
|
|
757
|
+
- Properly handles Greek diacritics (diaeresis, etc.)
|
|
758
|
+
|
|
759
|
+
8. **Capitalization & Splitting** (`splitNames: true` by default)
|
|
760
|
+
- Splits name into parts and capitalizes each part
|
|
761
|
+
- Handles particles (του/της/των) if `handleParticles: true`
|
|
762
|
+
|
|
763
|
+
9. **Accent Addition** (`addAccents`)
|
|
764
|
+
- Adds accents to unaccented Greek names
|
|
765
|
+
- Uses comprehensive name dictionary with fallback rules
|
|
766
|
+
|
|
767
|
+
10. **Genitive Conversion** (`convertToGenitive`)
|
|
768
|
+
- Converts to genitive case
|
|
769
|
+
- **Stored separately** in result object, doesn't modify main processed name
|
|
770
|
+
|
|
771
|
+
11. **Database-Safe Conversion** (`databaseSafe`)
|
|
772
|
+
- Removes problematic characters for database storage
|
|
773
|
+
|
|
774
|
+
12. **General Title Addition** (`addGeneralTitle`)
|
|
775
|
+
- Adds "Κ." (for men) or "Κα" (for women) if no title exists
|
|
776
|
+
- Only adds if no title was extracted in step 1
|
|
777
|
+
|
|
778
|
+
13. **Case Conversion** (`convertToCase: 'vocative' | 'accusative'`)
|
|
779
|
+
- **Vocative (κλητική):** name-dependent **-ο** / **-ε** for masculine **-ος** (see [Vocative Case Conversion](#vocative-case-conversion-κλητική))
|
|
780
|
+
- **Accusative (αιτιατική):** masculine **-ος** always → **-ο**; **-ης** → **-η**; **-ας** → **-α** (see [Accusative Case Conversion](#accusative-case-conversion-αιτιατική))
|
|
781
|
+
- **Stored separately** in result object
|
|
782
|
+
- If `preserveOriginal: false`, returns case form directly
|
|
783
|
+
|
|
784
|
+
14. **Title Re-attachment**
|
|
785
|
+
- Re-attaches extracted or added title to the processed name
|
|
786
|
+
|
|
787
|
+
15. **Metadata Generation**
|
|
788
|
+
- `detectGender`: Detects gender from name endings
|
|
789
|
+
- `detectDiminutive`: Detects diminutive forms
|
|
790
|
+
- `generateSortKey`: Generates accent-free sort key
|
|
791
|
+
- `statistics`: Generates comprehensive name statistics
|
|
792
|
+
|
|
793
|
+
### Option Conflicts and Incompatibilities
|
|
794
|
+
|
|
795
|
+
Some options don't work well together or have special behaviors:
|
|
796
|
+
|
|
797
|
+
#### Conflicting Combinations
|
|
798
|
+
|
|
799
|
+
| Combination | Issue | Solution |
|
|
800
|
+
|------------|-------|----------|
|
|
801
|
+
| `transliterate: 'greek-to-latin'` + `addAccents` | Accents can't be added to Latin text | Use `addAccents` only with Greek text |
|
|
802
|
+
| `transliterate: 'greek-to-latin'` + `convertToCase` | Case conversion only works on Greek text | Case conversion requires Greek input |
|
|
803
|
+
| `transliterate: 'greek-to-greeklish'` + `convertToCase` | Case conversion only works on Greek text | Case conversion requires Greek input |
|
|
804
|
+
| `databaseSafe: true` + `addAccents` | Database-safe may remove characters needed for accents | Apply `addAccents` before `databaseSafe` (automatic) |
|
|
805
|
+
| `convertToCase` + `preserveOriginal: false` | Returns case form string, not main corrected name | Use `preserveOriginal: true` to get both forms |
|
|
806
|
+
|
|
807
|
+
#### Special Behaviors
|
|
808
|
+
|
|
809
|
+
**Case Conversion Return Value**
|
|
810
|
+
- When `convertToCase` is set and `preserveOriginal: false`, the function returns the case form string directly (not the nominative form)
|
|
811
|
+
- When `preserveOriginal: true`, both the corrected nominative and case forms are included in the result object
|
|
812
|
+
|
|
813
|
+
```javascript
|
|
814
|
+
// Returns case form directly
|
|
815
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
816
|
+
convertToCase: 'vocative'
|
|
817
|
+
});
|
|
818
|
+
// → "Γιώργο Παπαδόπουλο"
|
|
819
|
+
|
|
820
|
+
// Returns object with both forms
|
|
821
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
822
|
+
convertToCase: 'vocative',
|
|
823
|
+
preserveOriginal: true
|
|
824
|
+
});
|
|
825
|
+
// → {
|
|
826
|
+
// corrected: "Γιώργος Παπαδόπουλος",
|
|
827
|
+
// vocative: "Γιώργο Παπαδόπουλο",
|
|
828
|
+
// ...
|
|
829
|
+
// }
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
**Genitive and Case Conversion Together**
|
|
833
|
+
- Both `convertToGenitive` and `convertToCase` can be used together
|
|
834
|
+
- Genitive form is stored in `result.genitive`
|
|
835
|
+
- Case form is stored in `result.vocative` or `result.accusative`
|
|
836
|
+
- The main `corrected` field contains the nominative form
|
|
837
|
+
|
|
838
|
+
**General Title Addition**
|
|
839
|
+
- `addGeneralTitle` only adds a title if no title was extracted in step 1
|
|
840
|
+
- If a title already exists, it won't add another one
|
|
841
|
+
|
|
842
|
+
**Accent Addition Limitations**
|
|
843
|
+
- `addAccents` works best on Greek text
|
|
844
|
+
- May not work correctly after transliteration to non-Greek scripts
|
|
845
|
+
- Uses dictionary lookup first, then falls back to heuristic rules
|
|
846
|
+
|
|
847
|
+
### Best Practices
|
|
848
|
+
|
|
849
|
+
#### Recommended Combinations
|
|
850
|
+
|
|
851
|
+
**Basic Name Correction**
|
|
852
|
+
```javascript
|
|
853
|
+
GreekNameCorrection('γιώργος παπαδόπουλος', {
|
|
854
|
+
addAccents: true,
|
|
855
|
+
normalizeTonotics: true,
|
|
856
|
+
handleDiacritics: true
|
|
857
|
+
});
|
|
858
|
+
// Safe and effective for most use cases
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**Full Name Processing with Metadata**
|
|
862
|
+
```javascript
|
|
863
|
+
GreekNameCorrection('γιώργος παπαδόπουλος', {
|
|
864
|
+
preserveOriginal: true,
|
|
865
|
+
addAccents: true,
|
|
866
|
+
detectGender: true,
|
|
867
|
+
convertToGenitive: true,
|
|
868
|
+
generateSortKey: true
|
|
869
|
+
});
|
|
870
|
+
// Gets all information without conflicts
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
**Case Conversion with Full Details**
|
|
874
|
+
```javascript
|
|
875
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
876
|
+
preserveOriginal: true,
|
|
877
|
+
convertToCase: 'vocative',
|
|
878
|
+
convertToGenitive: true,
|
|
879
|
+
detectGender: true
|
|
880
|
+
});
|
|
881
|
+
// Returns object with nominative, vocative, and genitive forms
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
**Transliteration Workflow**
|
|
885
|
+
```javascript
|
|
886
|
+
// Step 1: Convert Greeklish to Greek
|
|
887
|
+
const greek = GreekNameCorrection('giorgos papadopoulos', {
|
|
888
|
+
transliterate: 'greeklish-to-greek',
|
|
889
|
+
addAccents: true
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// Step 2: Process the Greek name
|
|
893
|
+
const processed = GreekNameCorrection(greek, {
|
|
894
|
+
convertToCase: 'vocative',
|
|
895
|
+
detectGender: true
|
|
896
|
+
});
|
|
897
|
+
// Separate transliteration from case conversion
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
#### Problematic Combinations to Avoid
|
|
901
|
+
|
|
902
|
+
**Don't combine transliteration to non-Greek with case conversion:**
|
|
903
|
+
```javascript
|
|
904
|
+
// ❌ Won't work - case conversion needs Greek text
|
|
905
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
906
|
+
transliterate: 'greek-to-latin',
|
|
907
|
+
convertToCase: 'vocative'
|
|
908
|
+
});
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Don't use addAccents after transliteration to non-Greek:**
|
|
912
|
+
```javascript
|
|
913
|
+
// ❌ Won't work - accents can't be added to Latin
|
|
914
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
915
|
+
transliterate: 'greek-to-latin',
|
|
916
|
+
addAccents: true
|
|
917
|
+
});
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
**Do use preserveOriginal when you need multiple forms:**
|
|
921
|
+
```javascript
|
|
922
|
+
// ✅ Correct - gets all forms
|
|
923
|
+
GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
924
|
+
preserveOriginal: true,
|
|
925
|
+
convertToGenitive: true,
|
|
926
|
+
convertToCase: 'vocative'
|
|
927
|
+
});
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
### Examples of Option Combinations
|
|
931
|
+
|
|
932
|
+
#### Example 1: Safe Combination - All Features
|
|
933
|
+
```javascript
|
|
934
|
+
const result = GreekNameCorrection('dr giorgos tou papa', {
|
|
935
|
+
transliterate: 'greeklish-to-greek',
|
|
936
|
+
preserveOriginal: true,
|
|
937
|
+
handleTitles: true,
|
|
938
|
+
addGeneralTitle: true,
|
|
939
|
+
addAccents: true,
|
|
940
|
+
handleParticles: true,
|
|
941
|
+
suggestCorrections: true,
|
|
942
|
+
detectGender: true,
|
|
943
|
+
convertToGenitive: true,
|
|
944
|
+
convertToCase: 'vocative',
|
|
945
|
+
generateSortKey: true,
|
|
946
|
+
statistics: true,
|
|
947
|
+
detectDiminutive: true
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
// Result includes:
|
|
951
|
+
// - corrected: Main corrected name
|
|
952
|
+
// - genitive: Genitive form
|
|
953
|
+
// - vocative: Vocative form
|
|
954
|
+
// - gender: Detected gender
|
|
955
|
+
// - sortKey: Sort key
|
|
956
|
+
// - statistics: Name statistics
|
|
957
|
+
// - diminutive: Diminutive detection
|
|
958
|
+
// - title: Extracted title
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
#### Example 2: Database Integration
|
|
962
|
+
```javascript
|
|
963
|
+
const result = GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
964
|
+
preserveOriginal: true,
|
|
965
|
+
databaseSafe: true,
|
|
966
|
+
generateSortKey: true
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
// Safe for database storage
|
|
970
|
+
// result.corrected - safe for display
|
|
971
|
+
// result.sortKey - safe for sorting
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
#### Example 3: Case Conversion Only
|
|
975
|
+
```javascript
|
|
976
|
+
// Simple case conversion - returns string
|
|
977
|
+
const vocative = GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
978
|
+
convertToCase: 'vocative'
|
|
979
|
+
});
|
|
980
|
+
// → "Γιώργο Παπαδόπουλο"
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
#### Example 4: Problematic Combination
|
|
984
|
+
```javascript
|
|
985
|
+
// ⚠️ Warning: This won't work as expected
|
|
986
|
+
const result = GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
987
|
+
transliterate: 'greek-to-latin',
|
|
988
|
+
convertToCase: 'vocative', // Won't work on Latin text
|
|
989
|
+
addAccents: true // Won't work on Latin text
|
|
990
|
+
});
|
|
991
|
+
// Transliteration happens first, so case conversion and accent addition fail
|
|
992
|
+
```
|
|
993
|
+
|
|
634
994
|
## Use Cases
|
|
635
995
|
|
|
636
996
|
### 1. Form Validation & Correction
|
|
@@ -685,18 +1045,20 @@ const recipient = GreekNameCorrection(name, {
|
|
|
685
1045
|
console.log(`Προς: ${recipient.genitive}`);
|
|
686
1046
|
|
|
687
1047
|
// Use vocative case for addressing someone
|
|
688
|
-
const addressee = GreekNameCorrection('
|
|
1048
|
+
const addressee = GreekNameCorrection('Πρόδρομος', {
|
|
689
1049
|
convertToCase: 'vocative'
|
|
690
1050
|
});
|
|
1051
|
+
console.log(`Γεια σου, ${addressee}!`); // "Γεια σου, Πρόδρομε!"
|
|
691
1052
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
// Use accusative case for direct objects
|
|
695
|
-
const object = GreekNameCorrection('Δημήτρης Νικολάου', {
|
|
1053
|
+
// Use accusative case for direct objects (with article τον/την)
|
|
1054
|
+
const object = GreekNameCorrection('Πρόδρομος', {
|
|
696
1055
|
convertToCase: 'accusative'
|
|
697
1056
|
});
|
|
1057
|
+
console.log(`Είδα τον ${object}.`); // "Είδα τον Πρόδρομο."
|
|
698
1058
|
|
|
699
|
-
|
|
1059
|
+
// Same name, different case — do not use vocative where accusative is required
|
|
1060
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'accusative' }); // → "Στέφανο" (τον Στέφανο)
|
|
1061
|
+
GreekNameCorrection('Στέφανος', { convertToCase: 'vocative' }); // → "Στέφανε" (Στέφανε!)
|
|
700
1062
|
```
|
|
701
1063
|
|
|
702
1064
|
### 5. Gender-Based Processing
|
|
@@ -719,7 +1081,29 @@ console.log(`${pronoun} ${person.corrected}`);
|
|
|
719
1081
|
|
|
720
1082
|
## Browser Support
|
|
721
1083
|
|
|
722
|
-
|
|
1084
|
+
The library is fully compatible with browser environments! It automatically detects the runtime environment and works seamlessly in both Node.js and browsers.
|
|
1085
|
+
|
|
1086
|
+
### Features
|
|
1087
|
+
|
|
1088
|
+
- ✅ **Automatic Environment Detection** - No configuration needed
|
|
1089
|
+
- ✅ **Zero Browser Errors** - No `__dirname` or Node.js-specific globals required
|
|
1090
|
+
- ✅ **Full Feature Support** - All features work in browsers, including case conversion
|
|
1091
|
+
- ✅ **Smart Fallback** - Uses hard-coded rules in browsers, markdown files in Node.js when available
|
|
1092
|
+
|
|
1093
|
+
### Usage in Browser
|
|
1094
|
+
|
|
1095
|
+
```javascript
|
|
1096
|
+
// Works directly in browser (with bundler like Webpack, Browserify, or Vite)
|
|
1097
|
+
import GreekNameCorrection from 'greek-name-correction';
|
|
1098
|
+
|
|
1099
|
+
// All features work, including case conversion
|
|
1100
|
+
const result = GreekNameCorrection('Γιώργος Παπαδόπουλος', {
|
|
1101
|
+
convertToCase: 'vocative',
|
|
1102
|
+
detectGender: true
|
|
1103
|
+
});
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
The library automatically uses hard-coded rules for case conversion in browser environments, ensuring full functionality without file system access.
|
|
723
1107
|
|
|
724
1108
|
## TypeScript
|
|
725
1109
|
|
|
@@ -804,7 +1188,7 @@ The test suite covers:
|
|
|
804
1188
|
- Array processing
|
|
805
1189
|
- Object processing
|
|
806
1190
|
- All transliteration modes
|
|
807
|
-
- Case conversions (genitive, vocative, accusative)
|
|
1191
|
+
- Case conversions (genitive, vocative, accusative), including vocative vs accusative for names like Πρόδρομος
|
|
808
1192
|
- Title handling
|
|
809
1193
|
- Automatic general title addition
|
|
810
1194
|
- Accent addition
|
|
@@ -815,7 +1199,18 @@ The test suite covers:
|
|
|
815
1199
|
|
|
816
1200
|
## Changelog
|
|
817
1201
|
|
|
818
|
-
### Version 2.2.
|
|
1202
|
+
### Version 2.2.3 (Current)
|
|
1203
|
+
- 📖 **Case conversion documentation** - README documents vocative (κλητική) vs accusative (αιτιατική), including masculine **-ος** names where forms differ (e.g. Πρόδρομος → **Πρόδρομο** / **Πρόδρομε**; Στέφανος → **Στέφανο** / **Στέφανε**)
|
|
1204
|
+
- 🔧 **Vocative logic refactor** - Shared `resolveOsVocativeStyleEnding()` in `src/cases.js` for clearer **-ο** / **-ε** handling
|
|
1205
|
+
- ✅ **Accusative consistency** - Masculine **-ος** names always form accusative in **-ο**; vocative keeps separate name-dependent rules
|
|
1206
|
+
- ✅ **Tests** - Added coverage for Πρόδρομος accusative vs vocative forms
|
|
1207
|
+
- 📝 See [README_NOTES_2.2.3.md](./README_NOTES_2.2.3.md) for full release notes
|
|
1208
|
+
|
|
1209
|
+
### Version 2.2.2
|
|
1210
|
+
- 🌐 **Browser compatibility** - Environment detection and fallback to hard-coded rules when `__dirname` / file system is unavailable
|
|
1211
|
+
- 🔧 **Package configuration** - Updated repository URL format in package.json
|
|
1212
|
+
|
|
1213
|
+
### Version 2.2.1
|
|
819
1214
|
- 🔧 **Enhanced Accent Addition** - Improved `addAccents` feature with comprehensive name dictionary support. Now uses actual Greek name dictionaries from `generate_greek_names.js` for accurate accent placement on common names. Includes CLI support with `-addAccents` flag.
|
|
820
1215
|
- ✨ **Name Dictionary** - Built-in dictionary with 1,100+ Greek names (first names, surnames, compound surnames) for accurate accent placement
|
|
821
1216
|
- 🐛 **CLI Fix** - Added missing `-addAccents` flag to command-line interface
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "greek-name-correction",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "A zero-dependency Node.js library for correcting and formatting Greek names with transliteration, genitive conversion, and advanced features",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/sraftopo/greek-name-correction.git"
|
|
29
|
+
"url": "git+https://github.com/sraftopo/greek-name-correction.git"
|
|
30
30
|
},
|
|
31
31
|
"bugs": {
|
|
32
32
|
"url": "https://github.com/sraftopo/greek-name-correction/issues"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=12.0.0"
|
|
37
37
|
},
|
|
38
38
|
"bin": {
|
|
39
|
-
"greek-name-correction": "
|
|
39
|
+
"greek-name-correction": "bin/greek-name-correction.js"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"src/",
|
|
@@ -54,9 +54,15 @@
|
|
|
54
54
|
"collectCoverageFrom": [
|
|
55
55
|
"src/**/*.js"
|
|
56
56
|
],
|
|
57
|
+
"coverageDirectory": "coverage",
|
|
58
|
+
"coverageReporters": [
|
|
59
|
+
"text",
|
|
60
|
+
"lcov",
|
|
61
|
+
"json"
|
|
62
|
+
],
|
|
57
63
|
"coveragePathIgnorePatterns": [
|
|
58
|
-
"
|
|
59
|
-
"
|
|
64
|
+
"node_modules",
|
|
65
|
+
"test"
|
|
60
66
|
],
|
|
61
67
|
"coverageThreshold": {
|
|
62
68
|
"global": {
|
|
@@ -67,4 +73,4 @@
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
|
-
}
|
|
76
|
+
}
|
package/src/cases.js
CHANGED
|
@@ -163,6 +163,40 @@ function isDiminutivePattern(lowerPart) {
|
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// Resolve -ος ending as "ο", "ε", or null (unchanged) using vocative-style rules
|
|
167
|
+
function resolveOsVocativeStyleEnding(part, index, totalParts, parsedRules) {
|
|
168
|
+
const lowerPart = part.toLowerCase();
|
|
169
|
+
|
|
170
|
+
if (isDiminutivePattern(lowerPart)) {
|
|
171
|
+
return "ο";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const firstNamesInO =
|
|
175
|
+
parsedRules?.osEndings?.firstNames?.paroxytone?.inO ||
|
|
176
|
+
vocativeInO_FirstNames;
|
|
177
|
+
const firstNamesInE =
|
|
178
|
+
parsedRules?.osEndings?.firstNames?.paroxytone?.inE ||
|
|
179
|
+
vocativeInE_FirstNames;
|
|
180
|
+
const firstNamesUnchanged =
|
|
181
|
+
parsedRules?.osEndings?.firstNames?.oxytone?.unchanged || oxytoneNames;
|
|
182
|
+
|
|
183
|
+
if (firstNamesInO.includes(lowerPart)) {
|
|
184
|
+
return "ο";
|
|
185
|
+
}
|
|
186
|
+
if (firstNamesInE.includes(lowerPart)) {
|
|
187
|
+
return "ε";
|
|
188
|
+
}
|
|
189
|
+
if (isLikelySurname(part, index, totalParts)) {
|
|
190
|
+
return isOxytoneSurname(part) ? "ε" : "ο";
|
|
191
|
+
}
|
|
192
|
+
if (firstNamesUnchanged.includes(lowerPart) || isOxytone(part)) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const syllableCount = (part.match(/[αεηιουωάέήίόύώ]/gi) || []).length;
|
|
197
|
+
return syllableCount <= 2 ? "ο" : "ε";
|
|
198
|
+
}
|
|
199
|
+
|
|
166
200
|
// Convert to vocative case
|
|
167
201
|
// Converts all name parts (first name and last name), but skips particles
|
|
168
202
|
function convertToVocative(name, config) {
|
|
@@ -196,55 +230,14 @@ function convertToVocative(name, config) {
|
|
|
196
230
|
|
|
197
231
|
// Handle masculine names ending in -ος
|
|
198
232
|
if (lowerPart.endsWith("ος")) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const firstNamesInO = parsedRules?.osEndings?.firstNames?.paroxytone?.inO || vocativeInO_FirstNames;
|
|
208
|
-
const firstNamesInE = parsedRules?.osEndings?.firstNames?.paroxytone?.inE || vocativeInE_FirstNames;
|
|
209
|
-
const firstNamesUnchanged = parsedRules?.osEndings?.firstNames?.oxytone?.unchanged || oxytoneNames;
|
|
210
|
-
|
|
211
|
-
// Check explicit list of first names that form vocative in -ο
|
|
212
|
-
if (firstNamesInO.includes(lowerPart)) {
|
|
213
|
-
vocative = part.slice(0, -2) + "ο";
|
|
214
|
-
}
|
|
215
|
-
// Check explicit list of first names that form vocative in -ε
|
|
216
|
-
else if (firstNamesInE.includes(lowerPart)) {
|
|
217
|
-
vocative = part.slice(0, -2) + "ε";
|
|
218
|
-
}
|
|
219
|
-
// Check if it's likely a surname (by position or explicit list)
|
|
220
|
-
else if (isLikelySurname(part, index, totalParts)) {
|
|
221
|
-
// For surnames, check if oxytone
|
|
222
|
-
if (isOxytoneSurname(part)) {
|
|
223
|
-
// Oxytone surname: -ος → -ε
|
|
224
|
-
vocative = part.slice(0, -2) + "ε";
|
|
225
|
-
} else {
|
|
226
|
-
// Paroxytone surname: -ος → -ο
|
|
227
|
-
vocative = part.slice(0, -2) + "ο";
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
// It's a first name (not in explicit lists) - default behavior
|
|
231
|
-
else {
|
|
232
|
-
if (firstNamesUnchanged.includes(lowerPart) || isOxytone(part)) {
|
|
233
|
-
// Oxytone first name: remains unchanged
|
|
234
|
-
vocative = part;
|
|
235
|
-
} else {
|
|
236
|
-
// Paroxytone first name: default to -ο for common short names, -ε for longer names
|
|
237
|
-
// Default to -ο for disyllabic names, -ε for longer
|
|
238
|
-
const syllableCount = (part.match(/[αεηιουωάέήίόύώ]/gi) || []).length;
|
|
239
|
-
if (syllableCount <= 2) {
|
|
240
|
-
// Disyllabic: -ος → -ο
|
|
241
|
-
vocative = part.slice(0, -2) + "ο";
|
|
242
|
-
} else {
|
|
243
|
-
// Longer names: -ος → -ε
|
|
244
|
-
vocative = part.slice(0, -2) + "ε";
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
233
|
+
const ending = resolveOsVocativeStyleEnding(
|
|
234
|
+
part,
|
|
235
|
+
index,
|
|
236
|
+
totalParts,
|
|
237
|
+
parsedRules
|
|
238
|
+
);
|
|
239
|
+
if (ending) {
|
|
240
|
+
vocative = part.slice(0, -2) + ending;
|
|
248
241
|
}
|
|
249
242
|
}
|
|
250
243
|
// Handle masculine names ending in -ης
|
|
@@ -290,7 +283,7 @@ function convertToVocative(name, config) {
|
|
|
290
283
|
function convertToAccusative(name, config) {
|
|
291
284
|
// Try to load parsed rules from markdown, fallback to hard-coded rules
|
|
292
285
|
const parsedRules = getAccusativeRules();
|
|
293
|
-
|
|
286
|
+
|
|
294
287
|
const parts = name.split(/\s+/);
|
|
295
288
|
const processedParts = parts.map((part) => {
|
|
296
289
|
const lowerPart = part.toLowerCase();
|
package/src/rulesParser.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
// rulesParser.js
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Browser environment detection
|
|
5
|
+
// Check if Node.js globals are available
|
|
6
|
+
function isNodeEnvironment() {
|
|
7
|
+
return (
|
|
8
|
+
typeof __dirname !== "undefined" &&
|
|
9
|
+
typeof require !== "undefined" &&
|
|
10
|
+
typeof module !== "undefined"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Safely require Node.js modules only in Node.js environment
|
|
15
|
+
let fs, path;
|
|
16
|
+
if (isNodeEnvironment()) {
|
|
17
|
+
try {
|
|
18
|
+
fs = require("fs");
|
|
19
|
+
path = require("path");
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// If require fails, we're likely in a browser environment
|
|
22
|
+
fs = null;
|
|
23
|
+
path = null;
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
fs = null;
|
|
27
|
+
path = null;
|
|
28
|
+
}
|
|
6
29
|
|
|
7
30
|
/**
|
|
8
31
|
* Parse markdown file and extract Greek name case conversion rules
|
|
@@ -10,6 +33,11 @@ const path = require("path");
|
|
|
10
33
|
* @returns {Object} Parsed rules structure
|
|
11
34
|
*/
|
|
12
35
|
function parseMarkdownRules(filePath) {
|
|
36
|
+
// In browser environments, fs is not available
|
|
37
|
+
if (!fs || !path) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
13
41
|
try {
|
|
14
42
|
const content = fs.readFileSync(filePath, "utf8");
|
|
15
43
|
return parseMarkdownContent(content);
|
|
@@ -260,6 +288,12 @@ function extractEnding(name) {
|
|
|
260
288
|
* Parse vocative rules from names_klitiki.md
|
|
261
289
|
*/
|
|
262
290
|
function parseVocativeRules() {
|
|
291
|
+
// In browser environments, skip file reading and return null
|
|
292
|
+
// This will trigger fallback to hard-coded rules in cases.js
|
|
293
|
+
if (!isNodeEnvironment() || !path || typeof __dirname === "undefined") {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
263
297
|
const filePath = path.join(__dirname, "..", "names_klitiki.md");
|
|
264
298
|
const rules = parseMarkdownRules(filePath);
|
|
265
299
|
|
|
@@ -448,6 +482,12 @@ function extractNameListsFromExamples(structuredRules, examples) {
|
|
|
448
482
|
* Parse accusative rules from names_aitiatiki.md
|
|
449
483
|
*/
|
|
450
484
|
function parseAccusativeRules() {
|
|
485
|
+
// In browser environments, skip file reading and return null
|
|
486
|
+
// This will trigger fallback to hard-coded rules in cases.js
|
|
487
|
+
if (!isNodeEnvironment() || !path || typeof __dirname === "undefined") {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
451
491
|
const filePath = path.join(__dirname, "..", "names_aitiatiki.md");
|
|
452
492
|
const rules = parseMarkdownRules(filePath);
|
|
453
493
|
|