Dart Documentationphonetics

phonetics library

Dart Phonetics

@author Edwin Bühler

Properties

final isVowel #

final isVowel = (String aString, int pos) => vowels.contains(aString.substring(pos, (pos + 1)))

final Logger log #

final Logger log =  new Logger('SoundexLogger')..level = Level.FINER

final Logger logTest #

final Logger logTest =  new Logger('SoundexLogger.Tests')..level = Level.ALL

final Slavo_Germanic #

final Slavo_Germanic = (String aString) {
 return (aString.contains('WK')) || (aString.indexOf('CZ', 0) >= 0) || (aString.indexOf('WITZ', 0) >= 0);
}

final string_at #

final  string_at = (string, start, length, list) {
   if ((start < 0) || (start >= string.length)) {
     return false;
   }
   //if (string.length >= (start + length)) {
     for (var i=0, len = list.length; i < len; i++) {
       if (list[i] == string.substring(start, (start + length))) {
         return true;
       }
     }
   //}
   return false;
 }

const List<String> vowels #

const List<String> vowels = const ['A', 'E', 'I', 'O', 'U']

Functions

String asDoubleMetaphonePhonetic(String aString) #

Returns the "Double Metaphone" elements primary and secondary as String separated with "-" If both primary and secondary are equal, only the primary returns.

asMetaphonePhonetic('Dart'); // Returns 'TRT' asMetaphonePhonetic('This is the Basic thing'); // Returns '0SS0-TSST'

String asDoubleMetaphonePhonetic(String aString) {
 asDoubleMetaphonePhoneticList(aString);
 var primary = asDoubleMetaphonePhoneticList(aString)[0];
 var secondary = asDoubleMetaphonePhoneticList(aString)[1];
 if ((secondary.isEmpty) || (primary == secondary)) {
   return asDoubleMetaphonePhoneticList(aString)[0];
 } else {
   return primary +  '-' + secondary ;
 }
}

bool doubleMetaphoneMinimalCompare(String a, String b) #

Minimal Match Secondary Key (1) = Secondary Key (2)

bool doubleMetaphoneMinimalCompare(String a, String b) {
 var mA = asDoubleMetaphonePhoneticList(a);
 var mB = asDoubleMetaphonePhoneticList(b);
 if (mA[0] == mB[0]) {
   return true;
 } else if (mA[0] == mB[1]) {
   return true;
 } else if (mA[1] == mB[1]) {
   return true;
 } else if (mA[1] == mB[0]) {
   return true;
 }
 return false;
}

bool doubleMetaphoneNormalCompare(String a, String b) #

Normal Match Secondary Key (1) = Primary Key (2) Primary Key (1) = Secondary Key (2)

bool doubleMetaphoneNormalCompare(String a, String b) {
 var mA = asDoubleMetaphonePhoneticList(a);
 var mB = asDoubleMetaphonePhoneticList(b);
 if (mA[0] == mB[0]) {
   return true;
 } else if (mA[0] == mB[1]) {
   return true;
 } else if (mA[1] == mB[0]) {
   return true;
 }
 return false;
}

bool doubleMetaphoneStrongCompare(String a, String b) #

Strongest Match Primary Key (1) = Primary Key (2)

bool doubleMetaphoneStrongCompare(String a, String b) {
 var mA = asDoubleMetaphonePhoneticList(a);
 var mB = asDoubleMetaphonePhoneticList(b);
 if (mA[0] == mB[0]) {
   return true;
 }
 return false;
}

bool doubleMetaphoneFuzzyCompare(String a, String b) #

Returns the first element (primary) from asDoubleMetaphonePhoneticList as "Metaphone" String.

doubleMetaphoneFuzzyCompare('Smith', 'Schmidt'); // Returns true doubleMetaphoneFuzzyCompare('Smith', 'Schmitz'); // Returns false

bool doubleMetaphoneFuzzyCompare(String a, String b) {
 var mA = asDoubleMetaphonePhoneticList(a);
 var mB = asDoubleMetaphonePhoneticList(b);
 if (mA[0] == mB[0]) {
   return true;
 } else if (mA[0] == mB[1]) {
   return true;
 } else if (mA[1] == mB[1]) {
   return true;
 } else if (mA[1] == mB[0]) {

   return true;
 }
 return _fuzzyCompare(mA, mB);
}

List<String> asDoubleMetaphonePhoneticList(String aString) #

Calculates the "Double Metaphone" code for the given string. It's the phonetic code for the English language.

main() {
  print(asDoubleMetaphonePhonetic('Dart')); // Returns 'TRT'
}

Returns the "Double Metaphone" elements primary and secondary as List.

asMetaphonePhonetic('Dart'); // Returns 'TRT', 'TRT' asMetaphonePhonetic('This is the Basic thing'); // Returns '0SS0', 'TSST'

List<String> asDoubleMetaphonePhoneticList(String aString) {
 if (aString == null || aString.isEmpty) return ['',''];
 var startIndex =  0, skipCount = 0,  current =  0;
 var length   = aString.length;
 var last     = length - 1;
 var primary = '', secondary = '';
 var inputKey =  aString.toUpperCase() + "     ";
 var addPrimaryTranslation = (String tailString) => primary = primary + tailString;
 var addSecondaryTranslation = (String tailString) => secondary = secondary + tailString;

 if(inputKey.length > 1) {
   if (['GN', 'KN', 'PN', 'WR', 'PS'].contains(inputKey.substring(0, 2))) {
     current++;
   }
 }

 while ((primary.length < 4) || (secondary.length < 4)) {
   if (current >= aString.length) {
     break;
   }
   switch (inputKey.substring(current, (current + 1))) {
     case 'A':
     case 'E':
     case 'I':
     case 'O':
     case 'U':
     case 'Y':
       if (current == 0) {
         addPrimaryTranslation('A');
         addSecondaryTranslation('A');
       }
       current += 1;
       break;
     case 'B':
       addPrimaryTranslation('P');
       addSecondaryTranslation('P');
       if (inputKey.substring(current + 1, current + 2) == 'B')
         current += 2;
       else
         current += 1;
       break;
     case 'Ç':
       addPrimaryTranslation('S');
       addSecondaryTranslation('S');
       current += 1;
       break;
     case 'C':
         // various gremanic
         if ((current > 1)
             && !isVowel(inputKey, current - 2)
             && string_at(inputKey, current - 1, 3,
                       ["ACH"])
             && ((inputKey.substring(current + 2, current + 3) != 'I')
                 && ((inputKey.substring(current + 2, current + 3) != 'E')
                     || string_at(inputKey, current - 2, 6,
                               ["BACHER", "MACHER"])))) {

           addPrimaryTranslation('K');
           addSecondaryTranslation('K');
           current += 2;
           break;
         }
         //  'caesar'
         if ((current == 0)
             && string_at(inputKey, current, 6,
                        ["CAESAR"])) {
           addPrimaryTranslation('S');
           addSecondaryTranslation('S');
           current += 2;
           break;
         }
         // 'chianti'
         if (string_at(inputKey, current, 4,
                        ["CHIA"])) {
           addPrimaryTranslation('K');
           addSecondaryTranslation('K');
           current += 2;
           break;
         }

         if (string_at(inputKey, current, 2,
                        ["CH"])) {

           // find 'michael'
           if ((current > 0)
               && string_at(inputKey, current, 4,
                        ["CHAE"])) {
             addPrimaryTranslation('K');
             addSecondaryTranslation('X');
             current += 2;
             break;
           }

           // greek roots e.g. 'chemistry', 'chorus'
           if ((current == 0)
               && (string_at(inputKey, current + 1, 5,
                        ["HARAC", "HARIS"])
                   || string_at(inputKey, current + 1, 3,
                             ["HOR", "HYM", "HIA", "HEM"]))
               && !string_at(inputKey, 0, 5, ["CHORE"])) {
             addPrimaryTranslation('K');
             addSecondaryTranslation('K');
             current += 2;
             break;
           }

           // germanic, greek, or otherwise 'ch' for 'kh' sound
           if ((string_at(inputKey, 0, 4, ["VAN ", "VON "])
                || string_at(inputKey, 0, 3, ["SCH"]))
               // 'architect' but not 'arch', orchestra', 'orchid'
               || string_at(inputKey, current - 2, 6,
                        ["ORCHES", "ARCHIT", "ORCHID"])
               || string_at(inputKey, current + 2, 1,
                        ["T", "S"])
               || ((string_at(inputKey, current - 1, 1,
                        ["A","O","U","E"])
                    || (current == 0))
                   // e.g. 'wachtler', 'weschsler', but not 'tichner'
                   && string_at(inputKey, current + 2, 1,
                        ["L","R","N","M","B","H","F","V","W"," "]))) {
             addPrimaryTranslation('K');
             addSecondaryTranslation('K');
           } else {
             if (current > 0) {
               if (string_at(inputKey, 0, 2, ["MC"])) {
                 // e.g. 'McHugh'
                 addPrimaryTranslation('K');
                 addSecondaryTranslation('K');
               } else {
                 addPrimaryTranslation('X');
                 addSecondaryTranslation('K');
               }
             } else {
               addPrimaryTranslation('X');
               addSecondaryTranslation('X');
             }
           }
           current += 2;
           break;
         }

         // e.g. 'czerny'
         if (string_at(inputKey, current, 2, ["CZ"])
             && !string_at(inputKey, current -2, 4,
                        ["WICZ"])) {
           addPrimaryTranslation('S');
           addSecondaryTranslation('X');
           current += 2;
           break;
         }

         // e.g. 'focaccia'
         if (string_at(inputKey, current + 1, 3,
                    ["CIA"])) {
           addPrimaryTranslation('X');
           addSecondaryTranslation('X');
           current += 3;
           break;
         }

         // double 'C', but not McClellan'
         if (string_at(inputKey, current, 2, ["CC"])
             && !((current == 1)
                  && (inputKey.substring(0, 1) == 'M'))) {
           // 'bellocchio' but not 'bacchus'
           if (string_at(inputKey, current + 2, 1,
                      ["I","E","H"])
               && !string_at(inputKey, current + 2, 2,
                         ["HU"])) {
             // 'accident', 'accede', 'succeed'
             if (((current == 1)
                  && (inputKey.substring(current - 1, current) == 'A'))
                 || string_at(inputKey, current - 1, 5,
                           ["UCCEE", "UCCES"])) {
               primary   += "KS";
               addSecondaryTranslation('KS');
               // 'bacci', 'bertucci', other italian
             } else {
               primary   += "X";
               addSecondaryTranslation('X');
             }
             current += 3;
             break;
           } else {
             // Pierce's rule
             addPrimaryTranslation("K");
             addSecondaryTranslation('K');
             current += 2;
             break;
           }
         }

         if (string_at(inputKey, current, 2,
                    ["CK","CG","CQ"])) {
           addPrimaryTranslation("K");
           addSecondaryTranslation('K');
           current += 2;
           break;
         }

         if (string_at(inputKey, current, 2,
                    ["CI","CE","CY"])) {
           // italian vs. english
           if (string_at(inputKey, current, 3,
                      ["CIO","CIE","CIA"])) {
             addPrimaryTranslation("S");
             addSecondaryTranslation('X');
           } else {
             primary   += "S";
             addSecondaryTranslation('S');
           }
           current += 2;
           break;
         }

         // else
         addPrimaryTranslation("K");
         addSecondaryTranslation('K');

         // name sent in 'mac caffrey', 'mac gregor'
         if (string_at(inputKey, current + 1, 2,
                    [" C"," Q"," G"])) {
           current += 3;
         } else {
           if (string_at(inputKey, current + 1, 1,
                      ["C","K","Q"])
               && !string_at(inputKey, current + 1, 2,
                          ["CE","CI"])) {
             current += 2;
           } else {
             current += 1;
           }
         }
         break;

     case 'D':
       if (string_at(inputKey, current, 2,
           ["DG"])) {
         if (string_at(inputKey, current + 2, 1,
             ["I","E","Y"])) {
           // e.g. 'edge'
           addPrimaryTranslation("J");
           addSecondaryTranslation('J');
           current += 3;
           break;
         } else {
           // e.g. 'edgar'
           addPrimaryTranslation("TK");
           addSecondaryTranslation("TK");
           current += 2;
           break;
         }
       }

       if (string_at(inputKey, current, 2,
           ["DT","DD"])) {
         addPrimaryTranslation("T");
         addSecondaryTranslation('T');
         current += 2;
         break;
       }

       // else
       addPrimaryTranslation("T");
       addSecondaryTranslation('T');
       current += 1;
       break;

     case 'F':
       if (inputKey.substring(current + 1, current + 2) == 'F')
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("F");
       addSecondaryTranslation('F');
       break;

     case 'G':
       if (inputKey.substring(current + 1, current + 2) == 'H') {
         if ((current > 0)
             && !isVowel(inputKey, current - 1)) {
           addPrimaryTranslation("K");
           addSecondaryTranslation('K');
           current += 2;
           break;
         }

         if (current < 3) {
           // 'ghislane', 'ghiradelli'
           if (current == 0) {
             if (inputKey.substring(current + 2, current + 3) == 'I') {
               primary   += "J";
               addSecondaryTranslation('J');
             } else {
               addPrimaryTranslation("K");
               addSecondaryTranslation('K');
             }
             current += 2;
             break;
           }
         }

         // Parker's rule (with some further refinements) - e.g. 'hugh'
         if (((current > 1)
             && string_at(inputKey, current - 2, 1,
                 ["B","H","D"]))
                 // e.g. 'bough'
                 || ((current > 2)
                     &&  string_at(inputKey, current - 3, 1,
                         ["B","H","D"]))
                         // e.g. 'broughton'
                         || ((current > 3)
                             && string_at(inputKey, current - 4, 1,
                                 ["B","H"]))) {
           current += 2;
           break;
         } else {
           // e.g. 'laugh', 'McLaughlin', 'cough', 'gough', 'rough', 'tough'
           if ((current > 2)
               && (inputKey.substring(current - 1, current) == 'U')
               && string_at(inputKey, current - 3, 1,
                   ["C","G","L","R","T"])) {
             addPrimaryTranslation("F");
             addSecondaryTranslation('F');
           } else if ( (current > 0) && inputKey.substring(current - 1, current) != 'I') {
             addPrimaryTranslation("K");
             addSecondaryTranslation('K');
           }
           current += 2;
           break;
         }
       }

       if (inputKey.substring(current + 1, current + 2) == 'N') {
         if ((current == 1) && isVowel(inputKey, 0)
             && !Slavo_Germanic(inputKey)) {
           primary   += "KN";
           secondary += "N";
         } else {
           // not e.g. 'cagney'
           if (!string_at(inputKey, current + 2, 2,
               ["EY"])
               && (inputKey.substring(current + 1, current + 2) != "Y")
               && !Slavo_Germanic(inputKey)) {
             addPrimaryTranslation("N");
             addSecondaryTranslation("KN");
           } else {
             addPrimaryTranslation("KN");
             addSecondaryTranslation("KN");
           }
         }
         current += 2;
         break;
       }

       // 'tagliaro'
       if (string_at(inputKey, current + 1, 2,
           ["LI"])
           && !Slavo_Germanic(inputKey)) {
         addPrimaryTranslation("KL");
         addSecondaryTranslation('L');
         current += 2;
         break;
       }

       // -ges-, -gep-, -gel- at beginning
       if ((current == 0)
           && ((inputKey.substring(current + 1, current + 2) == 'Y')
               || string_at(inputKey, current + 1, 2,
                   ["ES","EP","EB","EL","EY","IB","IL","IN","IE",
                    "EI","ER"]))) {
         addPrimaryTranslation("K");
         addSecondaryTranslation('J');
         current += 2;
         break;
       }

       // -ger-, -gy-
       if ((string_at(inputKey, current + 1, 2,
           ["ER"])
           || (inputKey.substring(current + 1, current + 2) == 'Y'))
           && !string_at(inputKey, 0, 6,
               ["DANGER","RANGER","MANGER"])
               && !string_at(inputKey, current -1, 1,
                   ["E", "I"])
                   && !string_at(inputKey, current -1, 3,
                       ["RGY","OGY"])) {
         addPrimaryTranslation("K");
         addSecondaryTranslation('J');
         current += 2;
         break;
       }

       // italian e.g. 'biaggi'
       if (string_at(inputKey, current + 1, 1,
           ["E","I","Y"])
           || string_at(inputKey, current -1, 4,
               ["AGGI","OGGI"])) {
         // obvious germanic
         if ((string_at(inputKey, 0, 4, ["VAN ", "VON "])
             || string_at(inputKey, 0, 3, ["SCH"]))
             || string_at(inputKey, current + 1, 2,
                 ["ET"])) {
           addPrimaryTranslation("K");
           addSecondaryTranslation('K');
         } else {
           // always soft if french ending
           if (string_at(inputKey, current + 1, 4,
               ["IER "])) {
             addPrimaryTranslation("J");
             addSecondaryTranslation('J');
           } else {
             addPrimaryTranslation("J");
             addSecondaryTranslation('K');
           }
         }
         current += 2;
         break;
       }

       if (inputKey.substring(current + 1, current + 2) == 'G')
         current += 2;
       else
         current += 1;

       addPrimaryTranslation('K');
       addSecondaryTranslation('K');
       break;

     case 'H':
       // only keep if first & before vowel or btw. 2 vowels
       if (((current == 0) ||
           isVowel(inputKey, current - 1))
           && isVowel(inputKey, current + 1)) {
         addPrimaryTranslation('H');
         addSecondaryTranslation('H');
         current += 2;
       } else
         current += 1;
       break;

     case 'J':
       // obvious spanish, 'jose', 'san jacinto'
       if (string_at(inputKey, current, 4,
           ["JOSE"])
           || string_at(inputKey, 0, 4, ["SAN "])) {
         if (((current == 0)
             && (inputKey.substring(current + 4, current + 5) == ' '))
             || string_at(inputKey, 0, 4, ["SAN "])) {
           addPrimaryTranslation('H');
           addSecondaryTranslation('H');
         } else {
           addPrimaryTranslation('J');
           addSecondaryTranslation('H');
         }
         current += 1;
         break;
       }

       if ((current == 0)
           && !string_at(inputKey, current, 4,
               ["JOSE"])) {
         addPrimaryTranslation('J');  // Yankelovich/Jankelowicz
         addSecondaryTranslation('A');
       } else {
         // spanish pron. of .e.g. 'bajador'
         if (isVowel(inputKey, current - 1)
             && !Slavo_Germanic(inputKey)
             && ((inputKey.substring(current + 1, current + 2) == 'A')
                 || (inputKey.substring(current + 1, current + 2) == 'O'))) {
           addPrimaryTranslation('J');
           addSecondaryTranslation('H');
         } else {
           if (current == last) {
             addPrimaryTranslation('J');
             addSecondaryTranslation('');
           } else {
             if (!string_at(inputKey, current + 1, 1,
                 ["L","T","K","S","N","M","B","Z"])
                 && !string_at(inputKey, current - 1, 1,
                     ["S","K","L"])) {
               addPrimaryTranslation('J');
               addSecondaryTranslation('J');
             }
           }
         }
       }

       if (inputKey.substring(current + 1, current + 2) == 'J') // it could happen
         current += 2;
       else
         current += 1;
       break;

     case 'K':
       if (inputKey.substring(current + 1, current + 2) == 'K')
         current += 2;
       else
         current += 1;
       addPrimaryTranslation('K');
       addSecondaryTranslation('K');
       break;

     case 'L':
       if (inputKey.substring(current + 1, current + 2) == 'L') {
         // spanish e.g. 'cabrillo', 'gallegos'
         if (((current == (length - 3))
             && string_at(inputKey, current - 1, 4,
                 ["ILLO","ILLA","ALLE"]))
                 || ((string_at(inputKey, last-1, 2,
                     ["AS","OS"])
                     || string_at(inputKey, last, 1,
                         ["A","O"]))
                         && string_at(inputKey, current - 1, 4,
                             ["ALLE"]))) {
           addPrimaryTranslation('L');
           addSecondaryTranslation('');
           current += 2;
           break;
         }
         current += 2;
       } else
         current += 1;
       addPrimaryTranslation("L");
       addSecondaryTranslation('L');
       break;

     case 'M':
       if ((string_at(inputKey, current - 1, 3,
           ["UMB"])
           && (((current + 1) == last)
               || string_at(inputKey, current + 2, 2,
                   ["ER"])))
                   // 'dumb', 'thumb'
                   || (inputKey.substring(current + 1, current + 2) == 'M')) {
         current += 2;
       } else {
         current += 1;
       }
       addPrimaryTranslation("M");
       addSecondaryTranslation("M");
       break;

     case 'N':
       if (inputKey.substring(current + 1, current + 2) == 'N')
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("N");
       addSecondaryTranslation("N");
       break;

     case 'Ñ':
       current += 1;
       addPrimaryTranslation("N");
       addSecondaryTranslation("N");
       break;

     case 'P':
       if (inputKey.substring(current + 1, current + 2) == 'H') {
         current += 2;
         addPrimaryTranslation("F");
         addSecondaryTranslation('F');
         break;
       }

       // also account for "campbell" and "raspberry"
       if (string_at(inputKey, current + 1, 1,
           ["P","B"]))
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("P");
       addSecondaryTranslation('P');
       break;

     case 'Q':
       if (inputKey.substring(current + 1, current + 2) == 'Q')
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("K");
       addSecondaryTranslation('K');
       break;

     case 'R':
       // french e.g. 'rogier', but exclude 'hochmeier'
       if ((current == last)
           && !Slavo_Germanic(inputKey)
           && string_at(inputKey, current - 2, 2,
               ["IE"])
               && !string_at(inputKey, current - 4, 2,
                   ["ME","MA"])) {
         addPrimaryTranslation("");
         addSecondaryTranslation('R');
       } else {
         addPrimaryTranslation("R");
         addSecondaryTranslation('R');
       }
       if (inputKey.substring(current + 1, current + 2) == 'R')
         current += 2;
       else
         current += 1;
       break;

     case 'S':
       // special cases 'island', 'isle', 'carlisle', 'carlysle'
       if (string_at(inputKey, current - 1, 3,
           ["ISL","YSL"])) {
         current += 1;
         break;
       }

       // special case 'sugar-'
       if ((current == 0)
           && string_at(inputKey, current, 5,
               ["SUGAR"])) {
         addPrimaryTranslation("X");
         addSecondaryTranslation('S');
         current += 1;
         break;
       }

       if (string_at(inputKey, current, 2,
           ["SH"])) {
         // germanic
         if (string_at(inputKey, current + 1, 4,
             ["HEIM","HOEK","HOLM","HOLZ"])) {
           addPrimaryTranslation("S");
           addSecondaryTranslation('S');
         } else {
           addPrimaryTranslation("X");
           addSecondaryTranslation('X');
         }
         current += 2;
         break;
       }

       // italian & armenian
       if (string_at(inputKey, current, 3,
           ["SIO","SIA"])
           || string_at(inputKey, current, 4,
               ["SIAN"])) {
         if (!Slavo_Germanic(inputKey)) {
           addPrimaryTranslation("S");
           addSecondaryTranslation('X');
         } else {
           addPrimaryTranslation("S");
           addSecondaryTranslation('S');
         }
         current += 3;
         break;
       }

       // german & anglicisations, e.g. 'smith' match 'schmidt', 'snider' match 'schneider'
       // also, -sz- in slavic language altho in hungarian it is pronounced 's'
       if (((current == 0)
           && string_at(inputKey, current + 1, 1,
               ["M","N","L","W"]))
               || string_at(inputKey, current + 1, 1,
                   ["Z"])) {
         addPrimaryTranslation("S");
         addSecondaryTranslation('X');
         if (string_at(inputKey, current + 1, 1,
             ["Z"]))
           current += 2;
         else
           current += 1;
         break;
       }

       if (string_at(inputKey, current, 2,
           ["SC"])) {
         // Schlesinger's rule
         if (inputKey.substring(current + 2, current + 3) == 'H')
           // dutch origin, e.g. 'school', 'schooner'
           if (string_at(inputKey, current + 3, 2,
               ["OO","ER","EN","UY","ED","EM"])) {
             // 'schermerhorn', 'schenker'
             if (string_at(inputKey, current + 3, 2,
                 ["ER","EN"])) {
               addPrimaryTranslation("X");
               addSecondaryTranslation("SK");
             } else {
               addPrimaryTranslation("SK");
               addSecondaryTranslation("SK");
             }
             current += 3;
             break;
           } else {
             if ((current == 0)
                 && !isVowel(inputKey, 3)
                 && (inputKey.substring(current + 1, current + 4) != 'W')) {
               addPrimaryTranslation("X");
               addSecondaryTranslation('S');
             } else {
               addPrimaryTranslation("X");
               addSecondaryTranslation('X');
             }
             current += 3;
             break;
           }

         if (string_at(inputKey, current + 2, 1,
             ["I","E","Y"])) {
           addPrimaryTranslation("S");
           addSecondaryTranslation('S');
           current += 3;
           break;
         }

         // else
         addPrimaryTranslation("SK");
         addSecondaryTranslation('SK');
         current += 3;
         break;
       }

       // french e.g. 'resnais', 'artois'
       if ((current == last)
           && string_at(inputKey, current - 2, 2,
               ["AI","OI"])) {
         addPrimaryTranslation("");
         addSecondaryTranslation('S');
       } else {
         addPrimaryTranslation("S");
         addSecondaryTranslation('S');
       }

       if (string_at(inputKey, current + 1, 1,
           ["S","Z"]))
         current += 2;
       else
         current += 1;
       break;

     case 'T':
       if (string_at(inputKey, current, 4,
           ["TION"])) {
         addPrimaryTranslation("X");
         addSecondaryTranslation('X');
         current += 3;
         break;
       }

       if (string_at(inputKey, current, 3,
           ["TIA","TCH"])) {
         addPrimaryTranslation("X");
         addSecondaryTranslation('X');
         current += 3;
         break;
       }

       if (string_at(inputKey, current, 2,
           ["TH"])
           || string_at(inputKey, current, 3,
               ["TTH"])) {
         // special case 'thomas', 'thames' or germanic
         if (string_at(inputKey, current + 2, 2,
             ["OM","AM"])
             || string_at(inputKey, 0, 4, ["VAN ","VON "])
             || string_at(inputKey, 0, 3, ["SCH"])) {
           addPrimaryTranslation("T");
           addSecondaryTranslation('T');
         } else {
           addPrimaryTranslation("0");
           addSecondaryTranslation('T');
         }
         current += 2;
         break;
       }

       if (string_at(inputKey, current + 1, 1,
           ["T","D"]))
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("T");
       addSecondaryTranslation('T');
       break;

     case 'V':
       if (inputKey.substring(current + 1, current + 2) == 'V')
         current += 2;
       else
         current += 1;
       addPrimaryTranslation("F");
       addSecondaryTranslation('F');
       break;

     case 'W':
       // can also be in middle of word
       if (string_at(inputKey, current, 2, ["WR"])) {
         addPrimaryTranslation("R");
         addSecondaryTranslation('R');
         current += 2;
         break;
       }

       if ((current == 0)
           && (isVowel(inputKey, current + 1)
               || string_at(inputKey, current, 2,
                   ["WH"]))) {
         // Wasserman should match Vasserman
         if (isVowel(inputKey, current + 1)) {
           addPrimaryTranslation("A");
           addSecondaryTranslation('F');
         } else {
           // need Uomo to match Womo
           addPrimaryTranslation("A");
           addSecondaryTranslation('A');
         }
       }

       // Arnow should match Arnoff
       if(current > 0) {
         if (((current == last)
             && isVowel(inputKey, current - 1))
             || string_at(inputKey, current - 1, 5,
                 ["EWSKI","EWSKY","OWSKI","OWSKY"])
                 || string_at(inputKey, 0, 3, ["SCH"])) {
           addPrimaryTranslation("");
           addSecondaryTranslation('F');
           current += 1;
           break;
         }
       }

       // polish e.g. 'filipowicz'
       if (string_at(inputKey, current, 4,
           ["WICZ","WITZ"])) {
         addPrimaryTranslation("TS");
         addSecondaryTranslation('FX');
         current += 4;
         break;
       }

       // else skip it
       current += 1;
       break;

     case 'X':
       // french e.g. breaux
       if (current == 0 ) {
         addPrimaryTranslation('S');
         addSecondaryTranslation('S');
         current++;
       } else {
       if (!((current == last)
           && (string_at(inputKey, current - 3, 3,
               ["IAU", "EAU"])
               || string_at(inputKey, current - 2, 2,
                   ["AU", "OU"])))) {
         addPrimaryTranslation("KS");
         addSecondaryTranslation('KS');
       }

       if (string_at(inputKey, current + 1, 1,
           ["C","X"]))
         current += 2;
       else
         current += 1;
       }
       break;

     case 'Z':
       // chinese pinyin e.g. 'zhao'
       if (inputKey.substring(current + 1, current + 2) == "H") {
         addPrimaryTranslation("J");
         addSecondaryTranslation('J');
         current += 2;
         break;
       } else if (string_at(inputKey, current + 1, 2,
           ["ZO", "ZI", "ZA"])
           || (Slavo_Germanic(inputKey)
               && ((current > 0)
                   && inputKey.substring(current - 1, current) != 'T'))) {
         addPrimaryTranslation("S");
         addSecondaryTranslation("TS");
       } else {
         addPrimaryTranslation("S");
         addSecondaryTranslation('S');
       }

       if (inputKey.substring(current + 1, current + 2) == 'Z')
         current += 2;
       else
         current += 1;
       break;


     default:
       current++;
   }
 }

 return [primary, secondary];
}

String asSoundexPhonetic(String aString) #

Calculates the "Soundex" code for the given string. It's the phonetic code for the English language.

main() {
  print(asSoundexPhonetic('Dart'));
}
String asSoundexPhonetic(String aString) {
 if (aString == null || aString.isEmpty) return '';
 var u = aString.toUpperCase();
 var p = u[0];
 var prevCode = _translationDict[u[0]];
 var t;
 List phoneticCodeList = new List.filled(4, 0);
 for (var i = 1; i < u.length; i++) {
   t = _translationDict[u[i]];
   if ((t != null) && ((t != '0') && (t != prevCode))) {
     p = p + t;
   }
   prevCode = t;
 }
 while (p.length < 4) {
   p = p + '0';
 }
 return p.substring(0,4);
}

String asColognePhonetic(String aString) #

Calculates the "Cologne Phonetic" (Kölner Phonetik) code for the given string. It's the phonetic code for the German language.

main() {
  print(asColognePhonetic('Dart'));
}
String asColognePhonetic(String aString) {
 if (aString == null || aString.isEmpty) return '';
 var phoneticString = '';
 var trimmedPhoneticString = '';
 var code;
 var space = ' ';
 var previousCode  = space;

 for (var i = 0; i < aString.length; i++) {
   var each;
   var prevoius;
   var follower;
   each = aString[i].toLowerCase();

   // evtl i 1 höher da 0 indexed
   // (i > 1)
   if (i > 0) {
     // [i - 1]
     prevoius = aString[i - 1].toLowerCase();
   }
   // (i < aString.length)
   if (i < (aString.length - 1)) {
     follower = aString[i + 1].toLowerCase();
   }
   if (_colognePhoneticTable.containsKey(each)) {
     code = _colognePhoneticTable[each];
   } else {
     if (each == 'h') {
       code = space;
     }
     if (each == 'p') {
       if (follower == 'h') {
         code = '3';
       } else {
         code = '1';
       }
     }
     if (each == 'd') {
       if ((follower == 'c') || (follower == 's') || (follower == 'z') || (follower == 'ß')) {
         code = '8';
       } else {
         code = '2';
       }
     }
     if (each == 't') {
       if ((follower == 'c') || (follower == 's') || (follower == 'z') || (follower == 'ß')) {
         code = '8';
       } else {
         code = '2';
       }
     }
     if (each == 'c') {
       if ( i == 1) {
         if ((follower == 'a') || (follower == 'h') || (follower == 'k') || (follower == 'l')
             || (follower == 'o')  || (follower == 'q')   || (follower == 'r')  || (follower == 'u') || (follower == 'x')) {
           code = '4';
         } else {
           code = '8';
         }
       } else {
         if ((prevoius == 's') || (prevoius == 'z') || (prevoius == 'ß')) {
           if ((follower == 'a') || (follower == 'h') || (follower == 'k')
               || (follower == 'o')  || (follower == 'q') || (follower == 'u') || (follower == 'x')) {
             code = '8';
           } else {
             code = '4';
           }
         } else {
           code = '4';
         }
       }
     }
     if (each == 'x') {
       if ((prevoius == 'c') || (prevoius == 'k') || (prevoius == 'q')) {
         code = '8';
       } else {
         code = '48';
       }
     }

   }
   if (previousCode != code) {
     phoneticString = phoneticString + code;
   }
   previousCode = code;
 }
 for (var i = 0; i < phoneticString.length; i++) {
   var each = phoneticString[i];
   if (!((each == space) || (each == '0'))) {
     // "Umlaute ?"
     trimmedPhoneticString = trimmedPhoneticString + each;
   }
 }
 return trimmedPhoneticString;
}

String phonetic(String aString, String aLocale, {String mode}) #

main() {
  print(asColognePhonetic('Dart'));
  print(asSoundexPhonetic('Dart'));
  print(asDoubleMetaphonePhonetic('Dart'));
}

main() {
  print(phonetic('Dart', 'en'));
  print(phonetic('Dart', 'de'));
  print(phonetic('Dart', 'en', mode:'soundex'));
  print(phonetic('Dart', 'en', mode:'doublemetaphone'));
}
String phonetic(String aString, String aLocale, {String  mode}) {
 if (aString == null || aString.isEmpty) return '';
 var phonetic = '';
 var lang = Intl.shortLocale(aLocale);

 switch (lang) {
   case 'en':
     if (mode == 'soundex') {
       phonetic = asSoundexPhonetic(aString);
     } else if (mode == 'doublemetaphone') {
       phonetic = asDoubleMetaphonePhonetic(aString);
     } else {
       phonetic = asDoubleMetaphonePhonetic(aString);
     }
     break;
   case 'de':
     phonetic = asColognePhonetic(aString);
     break;
   default:
     if (mode == 'soundex') {
       phonetic = asSoundexPhonetic(aString);
     } else if (mode == 'doublemetaphone') {
       phonetic = asDoubleMetaphonePhonetic(aString);
     } else {
       phonetic = asDoubleMetaphonePhonetic(aString);
     }
 }

 return phonetic;
}