File Coverage

blib/lib/Lingua/ES/PhT.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Lingua::ES::PhT;
2              
3 1     1   25332 use 5.008008;
  1         5  
  1         39  
4 1     1   5 use strict;
  1         2  
  1         33  
5 1     1   5 use warnings;
  1         17  
  1         450  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14              
15              
16             our %EXPORT_TAGS = ( 'test' => [ qw(
17             transcribe
18             ) ] );
19              
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'test'} } );
21              
22             our @EXPORT = qw(
23            
24             );
25              
26             our $VERSION = '0.9';
27 1     1   2152 use Lingua::ES::Syllabify;
  0            
  0            
28             use String::Multibyte;
29              
30             sub _setStressOnSyllabe;
31             sub _setStressOnSyllabedWord;
32              
33              
34              
35             =item transcribe
36              
37             Phonetically transcribe the given piece of text.
38              
39             If text has several words, the returned list of phonemes has no idications
40             of word boundaries.
41              
42             =cut
43             sub transcribe($) {
44             my $text = shift;
45              
46             my @phonemes = ();
47              
48             my @words = split(/\s+/, $text);
49              
50             foreach my $word (@words) {
51             # Some replacement for phonemes depending on suprasyllabical information
52             $word =~ s/^r/R/;
53             $word =~ s/([bcdfghjklmnpqstvwxyz])r/$1R/g;
54             $word =~ s/^ps/s/;
55             $word =~ s/^gn/n/;
56              
57             $word =~ s/^x/s/g;
58              
59             my @syllabes =
60             _setStressOnSyllabedWord(
61             Lingua::ES::Syllabify::getSyllables($word));
62              
63             push(@phonemes, _transcribeSyllabe($_)) foreach @syllabes;
64             }
65              
66             return @phonemes;
67             }
68              
69             sub _transcribeSyllabe($) {
70             my $syllabe = shift;
71              
72             my @phonemesToReturn;
73              
74             my %symbolsWithDirectMappings = (
75             "a" => "a",
76             "á" => "a'",
77             "b" => "b",
78             "C" => "tS", # ch
79             "d" => "d",
80             "e" => "e",
81             "é" => "e'",
82             "f" => "f",
83             "G" => "g", # gu
84             "í" => "i'",
85             "j" => "x",
86             "k" => "k",
87             "l" => "l",
88             "L" => "L",
89             "m" => "m",
90             "n" => "n",
91             "ñ" => "J",
92             "o" => "o",
93             "ó" => "o'",
94             "p" => "p",
95             "q" => "k",
96             "Q" => "k", # qu
97             "r" => "r",
98             "R" => "rr", # rr
99             "s" => "s",
100             "t" => "t",
101             "ü" => "w",
102             "ú" => "u'",
103             "v" => "b",
104             "z" => "T"
105             );
106              
107             my $vowels_re = "i|í|u|ú|ü|a|á|e|é|o|ó";
108             my $consonants_re = "[bcdfghjklmnpqrstvwxyz]";
109              
110             # Some replacement for phonemes expanding several letters
111             $syllabe =~ s/rr/R/;
112             $syllabe =~ s/ll/L/;
113             $syllabe =~ s/ch/C/;
114             $syllabe =~ s/tx/C/;
115             $syllabe =~ s/qu(e|é|i|í)/Q$1/;
116             $syllabe =~ s/gu(e|é|i|í)/G$1/;
117              
118             # Some replacements
119             $syllabe =~ s/x/cs/;
120              
121             my $utf8 = new String::Multibyte('UTF8');
122             my @chars = $utf8->strsplit('', $syllabe);
123             my $cInd = 0;
124             while ($cInd < @chars) {
125             my $char = $chars[$cInd];
126             if ($symbolsWithDirectMappings{$char}) {
127             push(@phonemesToReturn, $symbolsWithDirectMappings{$char});
128             } else {
129             if ($char eq 'c') {
130             if ($cInd < $#chars && $chars[$cInd+1] =~ /e|é|i|í/) {
131             push(@phonemesToReturn, 'T');
132             } else {
133             push(@phonemesToReturn, 'k');
134             }
135             } elsif ($char eq 'g') {
136             if ($cInd < $#chars && $chars[$cInd+1] =~ /a|á|o|ó|ü/) {
137             push(@phonemesToReturn, 'g');
138             } else {
139             push(@phonemesToReturn, 'x');
140             }
141             } elsif ($char eq 'h') {
142             # 'h' has no sound in spanish
143             } elsif ($char eq 'i') {
144             if (($cInd == 0 || ($cInd == 1 && $chars[0] eq 'h')) &&
145             ($cInd < $#chars && $chars[$cInd+1] =~ /$vowels_re/)) {
146             push(@phonemesToReturn, 'L');
147             } elsif (($cInd > 0 && $chars[$cInd-1] =~ /$vowels_re/) ||
148             ($cInd < $#chars && $chars[$cInd+1] =~ /$vowels_re/)) {
149             push(@phonemesToReturn, 'j');
150             } else {
151             push(@phonemesToReturn, 'i');
152             }
153             } elsif ($char eq 'u') {
154             if (($cInd < $#chars && $chars[$cInd+1] =~ /$vowels_re/)) {
155             push(@phonemesToReturn, 'w');
156             } else {
157             push(@phonemesToReturn, 'u');
158             }
159             } elsif ($char eq 'w') {
160             push(@phonemesToReturn, 'g', 'u');
161             } elsif ($char eq 'y') {
162             if ($cInd == $#chars) {
163             push(@phonemesToReturn, 'i');
164             } else {
165             push(@phonemesToReturn, 'L');
166             }
167             } else {
168             warn "'$char' can not be translated. Ignoring it\n";
169             }
170             }
171             $cInd++;
172             }
173             return @phonemesToReturn;
174             }
175              
176             sub _setStressOnSyllabedWord($) {
177             my @syllabes = @_;
178             my $word = join("", @syllabes);
179             if ($word !~ /á|é|í|ó|ú/) {
180             if ($word =~ /.mente$/ && @syllabes > 2) {
181             my $maxIndex = $#syllabes - 2;
182             @syllabes[0..$maxIndex] = _setStressOnSyllabedWord(
183             @syllabes[0..$maxIndex]);
184             } elsif ($syllabes[-1] =~ /[aeiouns]$/ ) { # word is 'llana' or monosyllabic
185             if (@syllabes == 1) {
186             $syllabes[0] = _setStressOnSyllabe($syllabes[0]);
187             } else {
188             $syllabes[-2] = _setStressOnSyllabe($syllabes[-2]);
189             }
190             } else { # word is 'aguda'
191             $syllabes[-1] = _setStressOnSyllabe($syllabes[-1]);
192             }
193             }
194             return @syllabes;
195             }
196              
197             sub _setStressOnSyllabe($) {
198             my $syllabe = shift;
199              
200             my $strongVowels_re = "a|á|e|é|o|ó";
201             my $softVowels_re = "i|í|u|ú|ü";
202              
203             my $utf8 = new String::Multibyte('UTF8');
204             my @chars = $utf8->strsplit('', $syllabe);
205              
206             my @vowelsPositions;
207              
208             for (my $index = 0; $index < @chars; $index++) {
209             push(@vowelsPositions, $index) if ($chars[$index] =~ /[aeiou]/);
210             }
211              
212             return $syllabe unless @vowelsPositions;
213              
214             my $stressPosition;
215              
216             if (@vowelsPositions == 1) {
217             $stressPosition = $vowelsPositions[0];
218             } elsif (@vowelsPositions == 2) { #diphthong
219             if ($chars[$vowelsPositions[0]] =~ /$strongVowels_re/ &&
220             $chars[$vowelsPositions[1]] =~ /$softVowels_re/) {
221             $stressPosition = $vowelsPositions[0];
222             } elsif ($chars[$vowelsPositions[0]] =~ /$softVowels_re/) {
223             $stressPosition = $vowelsPositions[1];
224             } elsif ($chars[$vowelsPositions[0]] =~ /$strongVowels_re/ &&
225             $chars[$vowelsPositions[1]] =~ /$strongVowels_re/) {
226             $stressPosition = $vowelsPositions[1];
227             } else {
228             warn "Can not determine stressed vowel for '$syllabe'\n";
229             }
230             } else {
231             foreach (@vowelsPositions) {
232             if ($chars[$_] =~ /$strongVowels_re/) {
233             $stressPosition = $_;
234             last;
235             }
236             }
237             }
238              
239             $chars[$stressPosition] =~ s/a/á/;
240             $chars[$stressPosition] =~ s/e/é/;
241             $chars[$stressPosition] =~ s/i/í/;
242             $chars[$stressPosition] =~ s/o/ó/;
243             $chars[$stressPosition] =~ s/u/ú/;
244              
245             return join("", @chars);
246             }
247              
248             # Preloaded methods go here.
249             1;
250             __END__