diff --git a/country.cabal b/country.cabal
--- a/country.cabal
+++ b/country.cabal
@@ -1,5 +1,5 @@
 name: country
-version: 0.1.2
+version: 0.1.3
 synopsis: Country data type and functions
 description:
   The `country` library provides a data type for dealing with
@@ -38,6 +38,8 @@
     Country.Unexposed.Enumerate
     Country.Unexposed.ExtraNames
     Country.Unexposed.Names
+    Country.Unexposed.Trie
+    Country.Unexposed.TrieByte
   build-depends:
       base >= 4.7 && < 4.10
     , text >= 1.2 && < 1.3
@@ -48,6 +50,7 @@
     , hashable >= 1.2 && < 1.3
     , aeson >= 0.11 && < 1.3
     , scientific >= 0.3 && < 0.4
+    , attoparsec >= 0.13 && < 0.14
   default-language: Haskell2010
 
 test-suite test
diff --git a/src/Country.hs b/src/Country.hs
--- a/src/Country.hs
+++ b/src/Country.hs
@@ -11,6 +11,8 @@
     -- * Name
   , encodeEnglish
   , decode
+  , parser
+  , parserUtf8
     -- * Alpha-2 and Alpha-3
   , alphaTwoUpper
   , alphaThreeUpper
@@ -22,14 +24,12 @@
 
 import Country.Unsafe (Country(..))
 import Country.Unexposed.Encode.English (countryNameQuads)
-import Country.Unexposed.Names (englishCountryNamesText,numberOfPossibleCodes,alphaTwoHashMap,alphaThreeHashMap,decodeMap,decodeNumeric,encodeEnglish)
-import Country.Unexposed.Enumerate (enumeratedCountries)
+import Country.Unexposed.Names (numberOfPossibleCodes,alphaTwoHashMap,alphaThreeHashMap,decodeMap,decodeNumeric,encodeEnglish)
+import Country.Unexposed.Trie (Trie,trieFromList,trieParser)
+import Country.Unexposed.TrieByte (TrieByte,trieByteFromList,trieByteParser)
 import Data.Text (Text)
-import Data.ByteString (ByteString)
-import Data.Word (Word16,Word8)
-import Data.Primitive (indexArray,newArray,unsafeFreezeArray,writeArray,
-  writeByteArray,indexByteArray,unsafeFreezeByteArray,newByteArray)
-import Data.HashMap.Strict (HashMap)
+import Data.Word (Word16)
+import Data.Primitive (indexArray,writeByteArray,indexByteArray,unsafeFreezeByteArray,newByteArray)
 import Data.Primitive.Array (Array(..))
 import Data.Primitive.ByteArray (ByteArray(..))
 import GHC.Prim (sizeofByteArray#,sizeofArray#)
@@ -38,10 +38,13 @@
 import Control.Monad
 import Data.Char (ord,chr,toLower)
 import Data.Bits (unsafeShiftL,unsafeShiftR)
-import qualified Data.List as L
+import Data.Coerce (coerce)
 import qualified Data.HashMap.Strict as HM
 import qualified Data.Text.Array as TA
+import qualified Data.Text.Encoding as TE
 import qualified Data.Text.Internal as TI
+import qualified Data.Attoparsec.Text as AT
+import qualified Data.Attoparsec.ByteString as AB
 
 -- | Convert a country to its numeric code. This is a
 --   three-digit number and will consequently be less than 1000.
@@ -86,27 +89,35 @@
 decode :: Text -> Maybe Country
 decode = flip HM.lookup decodeMap
 
+-- | Parse a country from its name using an attoparsec text parser. This
+--   function is language-agnostic and can handle any source language.
+--   In the case that one possible country name is a prefix of another
+--   possible name (for example, United States vs United States of America),
+--   the longest possible will be parsed.
+parser :: AT.Parser Country
+parser = coerce (trieParser decodeTrie)
+
+parserUtf8 :: AB.Parser Country
+parserUtf8 = coerce (trieByteParser decodeTrieUtf8)
+
 word16ToInt :: Word16 -> Int
 word16ToInt = fromIntegral
 
-intToWord16 :: Int -> Word16
-intToWord16 = fromIntegral
-
 charToWord16 :: Char -> Word16
 charToWord16 = fromIntegral . ord
 
 word16ToChar :: Word16 -> Char
 word16ToChar = chr . fromIntegral
 
-arrayFoldl' :: (a -> b -> a) -> a -> Array b -> a
-arrayFoldl' f z a = go 0 z
-  where
-  go i !acc | i < sizeofArray a = go (i+1) (f acc $ indexArray a i)
-            | otherwise         = acc
+-- arrayFoldl' :: (a -> b -> a) -> a -> Array b -> a
+-- arrayFoldl' f z a = go 0 z
+--   where
+--   go i !acc | i < sizeofArray a = go (i+1) (f acc $ indexArray a i)
+--             | otherwise         = acc
 
-sizeofArray :: Array a -> Int
-sizeofArray (Array a) = I# (sizeofArray# a)
-{-# INLINE sizeofArray #-}
+-- sizeofArray :: Array a -> Int
+-- sizeofArray (Array a) = I# (sizeofArray# a)
+-- {-# INLINE sizeofArray #-}
 
 numberOfCountries :: Int
 numberOfCountries = length countryNameQuads
@@ -168,5 +179,13 @@
         else return ()
   go 0
   return m
+
+decodeTrie :: Trie
+decodeTrie = trieFromList (map (\(a,Country x) -> (a,x)) (HM.toList decodeMap))
+{-# NOINLINE decodeTrie #-}
+
+decodeTrieUtf8 :: TrieByte
+decodeTrieUtf8 = trieByteFromList (map (\(a,Country x) -> (TE.encodeUtf8 a,x)) (HM.toList decodeMap))
+{-# NOINLINE decodeTrieUtf8 #-}
 
 
diff --git a/src/Country/Unexposed/Names.hs b/src/Country/Unexposed/Names.hs
--- a/src/Country/Unexposed/Names.hs
+++ b/src/Country/Unexposed/Names.hs
@@ -71,6 +71,7 @@
   , (mexico,"México")
   , (mexico,"Méjico")
   , (ålandIslands,"Aland Islands")
+  , (ålandIslands,"Aaland Islands")
   , (venezuelaBolivarianRepublicOf,"Venezuela")
   , (venezuelaBolivarianRepublicOf,"Bolivarian Republic of Venezuela")
   , (boliviaPlurinationalStateOf,"Bolivia")
diff --git a/src/Country/Unexposed/Trie.hs b/src/Country/Unexposed/Trie.hs
new file mode 100644
--- /dev/null
+++ b/src/Country/Unexposed/Trie.hs
@@ -0,0 +1,62 @@
+module Country.Unexposed.Trie
+  ( Trie
+  , trieFromList
+  , trieParser
+  ) where
+
+import Data.HashMap.Strict (HashMap)
+import Data.Word (Word16)
+import Data.Text (Text)
+import Control.Applicative ((<|>))
+import qualified Data.Text as T
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Attoparsec.Text as AT
+
+-- | If the value is not the max Word16 (65535), there 
+--   is a match. This means that 65535 cannot be used, which 
+--   is fine for this since 65535 is not used as a country code.
+data Trie = Trie
+  { trieValue :: {-# UNPACK #-} !Word16
+  , trieChildren :: !(HashMap Char Trie)
+  }
+
+empty :: Trie
+empty = Trie placeholder HM.empty
+
+append :: Trie -> Trie -> Trie
+append (Trie v1 c1) (Trie v2 c2) = Trie (min v1 v2) (HM.unionWith append c1 c2)
+
+placeholder :: Word16
+placeholder = 0xFFFF
+
+singleton :: Text -> Word16 -> Trie
+singleton fullName code = go fullName where
+  go :: Text -> Trie
+  go name = case T.uncons name of
+    Just (char,nameNext) -> Trie placeholder (HM.singleton char (go nameNext))
+    Nothing -> Trie code HM.empty
+
+instance Monoid Trie where
+  mempty = empty
+  mappend = append
+
+trieFromList :: [(Text,Word16)] -> Trie
+trieFromList = foldMap (uncurry singleton)
+
+-- it seems like attoparsec should have some kind of convenience
+-- for being able to commit to consuming a certain amount of
+-- input once your certain that it will be consumed, but I cannot
+-- find a way to use the api to do this.
+trieParser :: Trie -> AT.Parser Word16
+trieParser = go where
+  go :: Trie -> AT.Parser Word16
+  go (Trie value children) = do
+    let keepGoing = do
+          c <- AT.anyChar
+          case HM.lookup c children of
+            Nothing -> fail "did not recognize country name"
+            Just trieNext -> go trieNext
+    if value == placeholder
+      then keepGoing
+      else keepGoing <|> return value
+
diff --git a/src/Country/Unexposed/TrieByte.hs b/src/Country/Unexposed/TrieByte.hs
new file mode 100644
--- /dev/null
+++ b/src/Country/Unexposed/TrieByte.hs
@@ -0,0 +1,63 @@
+module Country.Unexposed.TrieByte
+  ( TrieByte
+  , trieByteFromList
+  , trieByteParser
+  ) where
+
+import Data.HashMap.Strict (HashMap)
+import Data.Word (Word16,Word8)
+import Data.ByteString (ByteString)
+import Control.Applicative ((<|>))
+import qualified Data.ByteString as B
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Attoparsec.ByteString as AB
+
+-- | If the value is not the max Word16 (65535), there 
+--   is a match. This means that 65535 cannot be used, which 
+--   is fine for this since 65535 is not used as a country code.
+data TrieByte = TrieByte
+  { trieValue :: {-# UNPACK #-} !Word16
+  , trieChildren :: !(HashMap Word8 TrieByte)
+  }
+
+empty :: TrieByte
+empty = TrieByte placeholder HM.empty
+
+append :: TrieByte -> TrieByte -> TrieByte
+append (TrieByte v1 c1) (TrieByte v2 c2) = TrieByte (min v1 v2) (HM.unionWith append c1 c2)
+
+placeholder :: Word16
+placeholder = 0xFFFF
+
+singleton :: ByteString -> Word16 -> TrieByte
+singleton fullName code = go fullName where
+  go :: ByteString -> TrieByte
+  go name = case B.uncons name of
+    Just (char,nameNext) -> TrieByte placeholder (HM.singleton char (go nameNext))
+    Nothing -> TrieByte code HM.empty
+
+instance Monoid TrieByte where
+  mempty = empty
+  mappend = append
+
+trieByteFromList :: [(ByteString,Word16)] -> TrieByte
+trieByteFromList = foldMap (uncurry singleton)
+
+-- it seems like attoparsec should have some kind of convenience
+-- for being able to commit to consuming a certain amount of
+-- input once your certain that it will be consumed, but I cannot
+-- find a way to use the api to do this.
+trieByteParser :: TrieByte -> AB.Parser Word16
+trieByteParser = go where
+  go :: TrieByte -> AB.Parser Word16
+  go (TrieByte value children) = do
+    let keepGoing = do
+          c <- AB.anyWord8
+          case HM.lookup c children of
+            Nothing -> fail "did not recognize country name"
+            Just trieNext -> go trieNext
+    if value == placeholder
+      then keepGoing
+      else keepGoing <|> return value
+
+
