Packages

Split Russian words into syllables (фонетическое деление слова на слоги)

Current section

Files

Jump to
syllabify lib syllabify.ex
Raw

lib/syllabify.ex

defmodule Syllabify do
@moduledoc """
Split Russian words into syllables
"""
@doc """
Split word into syllables
## Examples
iex> Syllabify.syllabify("Вдохновение")
["Вдо", "хно", "ве", "ни", "е"]
"""
def syllabify(word) do
letters =
word
|> String.graphemes()
|> Enum.reverse()
total = Enum.reduce(letters, 0, fn l, total -> if vowel?(l), do: total + 1, else: total end)
letters
|> traverse([], [], false, "", total)
|> Enum.map(&Enum.join/1)
end
@vowels ~w(а е ё и о у ы э ю я А Е Ё И О У Ы Э Ю Я)
@voiced ~w(м р л н М Р Л Н)
@consonant ~w(б в г д ж з к п с т ф х ц ч ш щ Б В Г Д Ж З К П С Т Ф Х Ц Ч Ш Щ)
defp vowel?(letter), do: Enum.member?(@vowels, letter)
defp voiced?(letter), do: Enum.member?(@voiced, letter)
defp non_letter?(letter),
do: !vowel?(letter) && !voiced?(letter) && !Enum.member?(@consonant, letter)
defp traverse([], syllables, syllable, _, _, _), do: [syllable | syllables]
defp traverse([letter | letters], syllables, syllable, voweled, prev_letter, total) do
cond do
voweled && vowel?(letter) ->
# check if this is a last syllable
voweled = length(syllables) < total - 2
traverse(letters, [syllable | syllables], [letter], voweled, letter, total)
voweled && non_letter?(letter) ->
traverse(letters, [syllable | syllables], [letter], false, letter, total)
voweled && letter == prev_letter ->
syllable = [letter | syllable]
traverse(letters, [syllable | syllables], [], false, letter, total)
voweled && voiced?(letter) && !vowel?(prev_letter) ->
traverse(letters, [syllable | syllables], [letter], false, letter, total)
true ->
traverse(
letters,
syllables,
[letter | syllable],
voweled || vowel?(letter),
letter,
total
)
end
end
end