Personal tools

Internationalization of Haskell programs using Haskell data types

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(Attribute this page to Felipe Lessa)
Current revision (09:59, 10 December 2011) (edit) (undo)
m (typo)
 
(2 intermediate revisions not shown.)
Line 8: Line 8:
<haskell>
<haskell>
-
data Message =
+
data Message =
-
Hello |
+
Hello
-
WhatsYourName |
+
| WhatsYourName
-
MyNameIs String |
+
| MyNameIs String
-
Ihave_apples Int
+
| Ihave_apples Int
-
GoodBye
+
| GoodBye
</haskell>
</haskell>
Line 20: Line 20:
<haskell>
<haskell>
-
render_en_US :: Message -> String
+
render_en_US :: Message -> String
-
render_en_US Hello = "Hello!"
+
render_en_US Hello = "Hello!"
-
render_en_US WhatsYourName = "What's your name?"
+
render_en_US WhatsYourName = "What's your name?"
-
render_en_US (MyNameIs name) = "My name is " ++ name ++ "."
+
render_en_US (MyNameIs name) = "My name is " ++ name ++ "."
-
render_en_US (Ihave_apples 0) = "I don't have any apples."
+
render_en_US (Ihave_apples 0) = "I don't have any apples."
-
render_en_US (Ihave_apples 1) = "I have one apple."
+
render_en_US (Ihave_apples 1) = "I have one apple."
-
render_en_US (Ihave_apples n) = "I have " ++ n ++ " apples."
+
render_en_US (Ihave_apples n) = "I have " ++ n ++ " apples."
-
render_en_US GoodBye = "Good bye!"
+
render_en_US GoodBye = "Good bye!"
-
render_pt_BR :: Message -> String
+
render_pt_BR :: Message -> String
-
render_pt_BR Hello = "Olá!"
+
render_pt_BR Hello = "Olá!"
-
render_pt_BR WhatsYourName = "Como você se chama?"
+
render_pt_BR WhatsYourName = "Como você se chama?"
-
render_pt_BR (MyNameIs name) = "Eu me chamo " ++ name ++ "."
+
render_pt_BR (MyNameIs name) = "Eu me chamo " ++ name ++ "."
-
render_pt_BR (Ihave_apples 0) = "Não tenho nenhuma maçã."
+
render_pt_BR (Ihave_apples 0) = "Não tenho nenhuma maçã."
-
render_pt_BR (Ihave_apples 1) = "Tenho uma maçã."
+
render_pt_BR (Ihave_apples 1) = "Tenho uma maçã."
-
render_pt_BR (Ihave_apples 2) = "Tenho uma maçã."
+
render_pt_BR (Ihave_apples 2) = "Tenho uma maçã."
-
render_pt_BR (Ihave_apples n) = "Tenho " ++ show n ++ " maçãs."
+
render_pt_BR (Ihave_apples n) = "Tenho " ++ show n ++ " maçãs."
-
render_pt_BR GoodBye = "Tchau!"
+
render_pt_BR GoodBye = "Tchau!"
</haskell>
</haskell>
Line 43: Line 43:
<haskell>
<haskell>
-
type Lang = String
+
type Lang = String
-
render :: [Lang] -> Message -> String
+
render :: [Lang] -> Message -> String
-
render ("pt" :_) = render_pt_BR
+
render ("pt" :_) = render_pt_BR
-
render ("pt_BR":_) = render_pt_BR
+
render ("pt_BR":_) = render_pt_BR
-
render ("en" :_) = render_en_US
+
render ("en" :_) = render_en_US
-
render ("en_US":_) = render_en_US
+
render ("en_US":_) = render_en_US
-
render (_:xs) = render xs
+
render (_:xs) = render xs
-
render _ = render_en_US
+
render _ = render_en_US
</haskell>
</haskell>
Line 60: Line 60:
<haskell>
<haskell>
-
putStrLn $ r Hello
+
putStrLn $ r Hello
-
putStrLn $ r WhatsYourName
+
putStrLn $ r WhatsYourName
-
name <- getLine
+
name <- getLine
-
putStrLn $ r MyNameIs "Alice"
+
putStrLn $ r (MyNameIs "Alice")
-
putStrLn $ r (Ihave_apples $ length name `mod` 4)
+
putStrLn $ r (Ihave_apples $ length name `mod` 4)
-
putStrLn $ r GoodBye
+
putStrLn $ r GoodBye
</haskell>
</haskell>
This approach is used for internationalization
This approach is used for internationalization
in the
in the
-
[http://www.yesodweb.com/ Yesod web framework],
+
[http://www.yesodweb.com/book/internationalization Yesod web framework],
except that instead of one big data type,
except that instead of one big data type,
[http://hackage.haskell.org/packages/archive/yesod-core/0.9.2/doc/html/Yesod-Message.html some type classes]
[http://hackage.haskell.org/packages/archive/yesod-core/0.9.2/doc/html/Yesod-Message.html some type classes]
are used.
are used.

Current revision

You can use Haskell data types for internationalization. The following example is adapted from a haskell-cafe mailing list post by Felipe Lessa.

The idea is to have a data type with all your messages, like

data Message =
    Hello
  | WhatsYourName
  | MyNameIs String
  | Ihave_apples Int
  | GoodBye

For each of your supported languages, you provide a rendering function (they may be in separate source files):

render_en_US :: Message -> String
render_en_US Hello = "Hello!"
render_en_US WhatsYourName = "What's your name?"
render_en_US (MyNameIs name) = "My name is " ++ name ++ "."
render_en_US (Ihave_apples 0) = "I don't have any apples."
render_en_US (Ihave_apples 1) = "I have one apple."
render_en_US (Ihave_apples n) = "I have " ++ n ++ " apples."
render_en_US GoodBye = "Good bye!"
 
render_pt_BR :: Message -> String
render_pt_BR Hello = "Olá!"
render_pt_BR WhatsYourName = "Como você se chama?"
render_pt_BR (MyNameIs name) = "Eu me chamo " ++ name ++ "."
render_pt_BR (Ihave_apples 0) = "Não tenho nenhuma maçã."
render_pt_BR (Ihave_apples 1) = "Tenho uma maçã."
render_pt_BR (Ihave_apples 2) = "Tenho uma maçã."
render_pt_BR (Ihave_apples n) = "Tenho " ++ show n ++ " maçãs."
render_pt_BR GoodBye = "Tchau!"

Given those functions, you can construct something like

type Lang = String
 
render :: [Lang] -> Message -> String
render ("pt"   :_) = render_pt_BR
render ("pt_BR":_) = render_pt_BR
render ("en"   :_) = render_en_US
render ("en_US":_) = render_en_US
render (_:xs) = render xs
render _ = render_en_US
So
r = render ["fr", "pt"]
will do the right thing. You just need to pass this
r
around in your code.

Using is easy and clear:

putStrLn $ r Hello
putStrLn $ r WhatsYourName
name <- getLine
putStrLn $ r (MyNameIs "Alice")
putStrLn $ r (Ihave_apples $ length name `mod` 4)
putStrLn $ r GoodBye

This approach is used for internationalization in the Yesod web framework, except that instead of one big data type, some type classes are used.