diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Joel Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/src/Data/Text/Normal/NFC.hs b/src/Data/Text/Normal/NFC.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Normal/NFC.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- | Strings normalized according to Normalization Form Canonical
+-- Composition.
+module Data.Text.Normal.NFC (
+    Normal, fromText, toText
+) where
+
+import Control.Arrow (first)
+import Control.DeepSeq
+import Data.Data
+import Data.Monoid
+import Data.String
+import Data.Text (Text)
+import Data.Text.ICU.Normalize
+
+-- | Normalized text.
+newtype Normal = Normal
+               { -- | Convert 'Normal' to 'Text'. This function just unwraps the newtype, so there is zero runtime cost.
+                 toText :: Text } deriving (Eq, Ord, Data, Typeable)
+
+-- | Convert 'Text' efficiently to 'Normal'.
+fromText :: Text -> Normal
+fromText t = Normal $ case quickCheck NFC t of
+    Nothing | isNormalized NFC t -> t
+            | otherwise -> normalize NFC t
+    Just False -> normalize NFC t
+    Just True -> t
+
+instance Show Normal where
+    show = show . toText
+
+instance Read Normal where
+    readsPrec i = map (first fromText) . readsPrec i
+
+instance Monoid Normal where
+    mappend (Normal t1) (Normal t2) = Normal $ t1 <> t2
+    mempty = Normal mempty
+
+instance IsString Normal where
+    fromString = fromText . fromString
+
+instance NFData Normal where
+    rnf = rnf . toText
diff --git a/src/Data/Text/Normal/NFD.hs b/src/Data/Text/Normal/NFD.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Normal/NFD.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- | Strings normalized according to Normalization Form Canonical
+-- Decomposition.
+module Data.Text.Normal.NFD (
+    Normal, fromText, toText
+) where
+
+import Control.Arrow (first)
+import Control.DeepSeq
+import Data.Data
+import Data.Monoid
+import Data.String
+import Data.Text (Text)
+import Data.Text.ICU.Normalize
+
+-- | Normalized text.
+newtype Normal = Normal
+               { -- | Convert 'Normal' to 'Text'. This function just unwraps the newtype, so there is zero runtime cost.
+                 toText :: Text } deriving (Eq, Ord, Data, Typeable)
+
+-- | Convert 'Text' efficiently to 'Normal'.
+fromText :: Text -> Normal
+fromText t = Normal $ case quickCheck NFD t of
+    Nothing | isNormalized NFD t -> t
+            | otherwise -> normalize NFD t
+    Just False -> normalize NFD t
+    Just True -> t
+
+instance Show Normal where
+    show = show . toText
+
+instance Read Normal where
+    readsPrec i = map (first fromText) . readsPrec i
+
+instance Monoid Normal where
+    mappend (Normal t1) (Normal t2) = Normal $ t1 <> t2
+    mempty = Normal mempty
+
+instance IsString Normal where
+    fromString = fromText . fromString
+
+instance NFData Normal where
+    rnf = rnf . toText
diff --git a/src/Data/Text/Normal/NFKC.hs b/src/Data/Text/Normal/NFKC.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Normal/NFKC.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- | Strings normalized according to Normalization Form Compatibility
+-- Composition.
+module Data.Text.Normal.NFKC (
+    Normal,
+    fromText, toText
+) where
+
+import Control.Arrow (first)
+import Control.DeepSeq
+import Data.Data
+import Data.Monoid
+import Data.String
+import Data.Text (Text)
+import Data.Text.ICU.Normalize
+
+-- | Normalized text.
+newtype Normal = Normal
+               { -- | Convert 'Normal' to 'Text'. This function just unwraps the newtype, so there is zero runtime cost.
+                 toText :: Text } deriving (Eq, Ord, Data, Typeable)
+
+-- | Convert 'Text' efficiently to 'Normal'.
+fromText :: Text -> Normal
+fromText t = Normal $ case quickCheck NFKC t of
+    Nothing | isNormalized NFKC t -> t
+            | otherwise -> normalize NFKC t
+    Just False -> normalize NFKC t
+    Just True -> t
+
+instance Show Normal where
+    show = show . toText
+
+instance Read Normal where
+    readsPrec i = map (first fromText) . readsPrec i
+
+instance Monoid Normal where
+    mappend (Normal t1) (Normal t2) = Normal $ t1 <> t2
+    mempty = Normal mempty
+
+instance IsString Normal where
+    fromString = fromText . fromString
+
+instance NFData Normal where
+    rnf = rnf . toText
diff --git a/src/Data/Text/Normal/NFKD.hs b/src/Data/Text/Normal/NFKD.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Normal/NFKD.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- | Strings normalized according to Normalization Form Compatibility
+-- Decomposition.
+module Data.Text.Normal.NFKD (
+    Normal,
+    fromText, toText
+) where
+
+import Control.Arrow (first)
+import Control.DeepSeq
+import Data.Data
+import Data.Monoid
+import Data.String
+import Data.Text (Text)
+import Data.Text.ICU.Normalize
+
+-- | Normalized text.
+newtype Normal = Normal
+               { -- | Convert 'Normal' to 'Text'. This function just unwraps the newtype, so there is zero runtime cost.
+                 toText :: Text } deriving (Eq, Ord, Data, Typeable)
+
+-- | Convert 'Text' efficiently to 'Normal'.
+fromText :: Text -> Normal
+fromText t = Normal $ case quickCheck NFKD t of
+    Nothing | isNormalized NFKD t -> t
+            | otherwise -> normalize NFKD t
+    Just False -> normalize NFKD t
+    Just True -> t
+
+instance Show Normal where
+    show = show . toText
+
+instance Read Normal where
+    readsPrec i = map (first fromText) . readsPrec i
+
+instance Monoid Normal where
+    mappend (Normal t1) (Normal t2) = Normal $ t1 <> t2
+    mempty = Normal mempty
+
+instance IsString Normal where
+    fromString = fromText . fromString
+
+instance NFData Normal where
+    rnf = rnf . toText
diff --git a/text-normal.cabal b/text-normal.cabal
new file mode 100644
--- /dev/null
+++ b/text-normal.cabal
@@ -0,0 +1,37 @@
+name:                text-normal
+version:             0.1.0.0
+synopsis:            Unicode-normalized text
+description:
+  This package provides types and functions for normalizing and comparing
+  @Text@ values according to Unicode equivalence.
+  .
+  An in-depth explanation of the forms of Unicode equivalence can be found
+  <http://en.wikipedia.org/wiki/Unicode_equivalence on Wikipedia>.
+  .
+  The modules in this package are named and function according to the four methods of
+  Unicode normalization. The @Normal@ types exported by each are purposefully
+  not compatible with functions exported by the other modules.
+  .
+  This package depends on @text-icu@, which means it requires that the @icu@
+  C library is installed.
+homepage:            https://github.com/joelteon/text-normal.git
+license:             MIT
+license-file:        LICENSE
+author:              Joel Taylor
+maintainer:          me@joelt.io
+category:            Data
+build-type:          Simple
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: https://github.com/joelteon/text-normal.git
+
+library
+  exposed-modules:     Data.Text.Normal.NFC
+                       Data.Text.Normal.NFD
+                       Data.Text.Normal.NFKC
+                       Data.Text.Normal.NFKD
+  build-depends:       base >=4.6 && <4.7, deepseq, text, text-icu
+  hs-source-dirs:      src
+  default-language:    Haskell2010
