PHPackages                             tinusg/syllable-counter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. tinusg/syllable-counter

ActiveLibrary

tinusg/syllable-counter
=======================

Counts and hyphenates the syllables of Dutch (and Dutch-spelled international) names and words

v1.0.0(today)02↑2900%MITPHPPHP ^8.2

Since Jul 28Pushed todayCompare

[ Source](https://github.com/tinusg/syllable-counter)[ Packagist](https://packagist.org/packages/tinusg/syllable-counter)[ Docs](https://github.com/tinusg/syllable-counter)[ RSS](/packages/tinusg-syllable-counter/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (4)Versions (2)Used By (0)

Syllable Counter
================

[](#syllable-counter)

Counts and hyphenates the syllables of Dutch — and Dutch-spelled international — names and words, without a dictionary, a pattern file or an API call. Pure spelling rules, one small class, no runtime dependencies beyond `illuminate/support` for the service provider.

```
$counter->count('Sophia');      // 3
$counter->hyphenate('Sophia');  // "So-phi-a"
$counter->split('Sophia');      // ['So', 'phi', 'a']
```

Installation
------------

[](#installation)

```
composer require tinusg/syllable-counter
```

The service provider is auto-discovered. Publishing the config is optional:

```
php artisan vendor:publish --tag=syllable-counter-config
```

Usage
-----

[](#usage)

Resolve it from the container (recommended — it picks up your config) or just new it up:

```
use TinusG\SyllableCounter\SyllableCounter;

class NameController
{
    public function show(SyllableCounter $syllables, string $name)
    {
        return view('names.show', [
            'syllableCount' => $syllables->count($name),
            'hyphenated' => $syllables->hyphenate($name),
        ]);
    }
}
```

MethodReturnsExample`count(string $value, ?bool $muteFinalE = null): int`Number of syllables, never less than 1`count('Olivia')` → `4``split(string $value, ?bool $muteFinalE = null): array`The syllables, casing and accents intact`split('Sophia')` → `['So', 'phi', 'a']``hyphenate(string $value, string $separator = '-', ?bool $muteFinalE = null): string`The hyphenated word`hyphenate('Olivia')` → `'O-li-vi-a'``count()` runs through `split()`, so counting and hyphenating can never disagree.

Spaces and hyphens are word boundaries; every word contributes its own syllables:

```
$counter->count('Anne-Marie');      // 4
$counter->split('Anne-Marie');      // ['An', 'ne', 'Ma', 'rie']
```

How it works
------------

[](#how-it-works)

The whole thing rests on one rule: **a syllable has exactly one vowel nucleus.** So finding the nuclei and dividing the consonants between them gives you both the count and the hyphenation.

### 1. Normalise, but remember the accents

[](#1-normalise-but-remember-the-accents)

The word is lower-cased and diacritics are reduced to their base letter (`é` → `e`, `ç` → `c`). Positions that carried a **diaeresis or an accent are flagged**, because those mark a vowel that is pronounced on its own. Without that flag `Chloë` and `Chloé` would both read as the digraph `oe` and come out as one syllable; with it they correctly split into `Chlo-ë` and `Chlo-é`.

### 2. Find the vowel nuclei

[](#2-find-the-vowel-nuclei)

Adjacent vowels are collected into runs, and each run is cut into nuclei with a greedy match on known vowel groups:

- **Trigraphs** — `aai`, `ooi`, `oei`, `eeu`, `ieu`, `eau` → one nucleus.
- **Digraphs** — `aa`, `ee`, `oo`, `uu`, `ie`, `ei`, `ui`, `ou`, `au`, `oe`, `eu`, `ae`, `ai`, `ay`, `ey`, `oy`, `uy`, `ij`, `oi` → one nucleus. So `Thijs`, `Fleur` and `Loes` are one syllable, and `Marie` is two.
- **Anything else adjacent is a hiatus** — two nuclei. That is what makes `So-phi-a`, `Le-o`, `Mat-te-o` and `An-to-ni-o` come out right; naive vowel-group counters collapse these into one.
- A flagged vowel (step 1) always breaks a group open, so `Zoë` never becomes one nucleus.

Two letters get special treatment:

- **`y` is a glide, not a vowel, when a vowel follows it** — `Ya-ra` and `Ma-ya` (2), but `Ly-di-a` (3), where the `y` is the nucleus.
- **`u` after `q` belongs to the /kw/ consonant** — `Quin-ten` (2), `Mo-nique` (2).

### 3. Divide the consonants: maximal onset

[](#3-divide-the-consonants-maximal-onset)

For the consonant cluster between two nuclei, as much of it as possible moves to the *following* syllable, as long as the part that moves is a valid Dutch onset (`br`, `chr`, `kn`, `zw`, `th`, …). A single consonant is always a valid onset, so V-CV is the default:

```
So-phie      ph is a valid onset, so it moves along
An-dre-a     dr is valid → An-dre-a, not And-re-a
I-sa-bel-la  ll is not an onset → the split falls in the middle
Chris-ti-aan st is not an onset → only t moves

```

### 4. The final e

[](#4-the-final-e)

Whether a final `e` is pronounced is not a spelling question — `Céline` (mute) and `Eline` (pronounced) share the same `-ine` ending — so it cannot be derived from the letters. The package handles this in two layers:

- **The safe heuristic.** After a `c` or `qu` the Dutch schwa-e simply does not occur, so there the `e` is mute: `Flo-rence` (2), `A-lice` (2), `Grace` (1). Wider rules (`-ne`, `-lle`) are deliberately *not* applied, because they would break `Han-ne`, `Jel-le` and `E-li-ne`.
- **Your own knowledge wins.** Pass `$muteFinalE` when you know the answer for that specific name — from a database column, an editor, a lookup, an LLM:

```
$counter->count('Céline', muteFinalE: true);   // 2 → Cé-line
$counter->count('Eline', muteFinalE: false);   // 3 → E-li-ne
$counter->count('Hatice', muteFinalE: false);  // 3 → Ha-ti-ce, overriding the -ce rule
```

`null` (the default) means "no knowledge, use the heuristic". The flag applies to the last word only, so `hyphenate('Marie-Alice', '-', true)` gives `Ma-rie-A-lice`.

### 5. Exceptions, last

[](#5-exceptions-last)

A handful of names break every rule Dutch spelling has — `James` is one syllable, `Ga-bri-el` splits where the digraph rule says it should not, Turkish `-ce` names pronounce their final e. Those live in a small exception list that is checked before the rules run.

An exception stores the syllables in lower case; the original is then cut at those same lengths, so **capitals and accents survive**: `GABRIEL` → `GA-BRI-EL`. If the exception does not match the length of the input, it is ignored and the normal rules apply.

Add your own in `config/syllable-counter.php` (an entry overrides a built-in one with the same key):

```
return [
    'exceptions' => [
        'ilse' => ['il', 'se'],
    ],
];
```

Or pass them straight to the constructor:

```
new SyllableCounter(['ilse' => ['il', 'se']]);
```

### What it does not do

[](#what-it-does-not-do)

It produces **orthographic hyphenation** (`So-phi-a`), not a phonetic transcription, and it is tuned for **names**. The rules are Dutch, so an English word with an English-only spelling pattern can come out wrong. It is a rule set, not an oracle — when you need a specific word to be exact, add an exception.

Testing
-------

[](#testing)

```
composer test
```

The suite is a large table of real names — Dutch, Turkish, Arabic, French, Latin, English — with their expected counts and hyphenations. If you find a name that comes out wrong, a pull request with that name added to the table is the most useful thing you can send.

Thanks
------

[](#thanks)

This package was extracted from the code that powers two Dutch sites, and it exists because they needed it:

- **[Naampedia](https://www.naampedia.nl)** — baby names, meanings, origins and popularity. The syllable rules here were written for its name pages and tested against tens of thousands of real first names.
- **[Puzzelpedia](https://www.puzzelpedia.nl)** — a puzzle dictionary and solving aid, where counting syllables and splitting words is daily business.

If this package is useful to you, a link back to [naampedia.nl](https://www.naampedia.nl) and [puzzelpedia.nl](https://www.puzzelpedia.nl) — in your README, your credits page or wherever you list what you build on — would be very much appreciated. That is the whole price.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2152919?v=4)[tinusg](/maintainers/tinusg)[@tinusg](https://github.com/tinusg)

---

Top Contributors

[![tinusg](https://avatars.githubusercontent.com/u/2152919?v=4)](https://github.com/tinusg "tinusg (1 commits)")

---

Tags

laravelphpsyllablesyllable-countsyllableslaravelhyphenationnamessyllableslinguisticsDutch

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/tinusg-syllable-counter/health.svg)

```
[![Health](https://phpackages.com/badges/tinusg-syllable-counter/health.svg)](https://phpackages.com/packages/tinusg-syllable-counter)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M348](/packages/psalm-plugin-laravel)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
