You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1061 line
51KB

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  3. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  4. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  6. <meta name="description" content="Describes the rationale for using UTF-8, the ramifications otherwise, and how to make the switch." />
  7. <link rel="stylesheet" type="text/css" href="./style.css" />
  8. <style type="text/css">
  9. .minor td {font-style:italic;}
  10. </style>
  11. <title>UTF-8: The Secret of Character Encoding - HTML Purifier</title>
  12. <!-- Note to users: this document, though professing to be UTF-8, attempts
  13. to use only ASCII characters, because most webservers are configured
  14. to send HTML as ISO-8859-1. So I will, many times, go against my
  15. own advice for sake of portability. -->
  16. </head><body>
  17. <h1>UTF-8: The Secret of Character Encoding</h1>
  18. <div id="filing">Filed under End-User</div>
  19. <div id="index">Return to the <a href="index.html">index</a>.</div>
  20. <div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
  21. <p>Character encoding and character sets are not that
  22. difficult to understand, but so many people blithely stumble
  23. through the worlds of programming without knowing what to actually
  24. do about it, or say &quot;Ah, it's a job for those <em>internationalization</em>
  25. experts.&quot; No, it is not! This document will walk you through
  26. determining the encoding of your system and how you should handle
  27. this information. It will stay away from excessive discussion on
  28. the internals of character encoding.</p>
  29. <p>This document is not designed to be read in its entirety: it will
  30. slowly introduce concepts that build on each other: you need not get to
  31. the bottom to have learned something new. However, I strongly
  32. recommend you read all the way to <strong>Why UTF-8?</strong>, because at least
  33. at that point you'd have made a conscious decision not to migrate,
  34. which can be a rewarding (but difficult) task.</p>
  35. <blockquote class="aside">
  36. <div class="label">Asides</div>
  37. <p>Text in this formatting is an <strong>aside</strong>,
  38. interesting tidbits for the curious but not strictly necessary material to
  39. do the tutorial. If you read this text, you'll come out
  40. with a greater understanding of the underlying issues.</p>
  41. </blockquote>
  42. <h2>Table of Contents</h2>
  43. <ol id="toc">
  44. <li><a href="#findcharset">Finding the real encoding</a></li>
  45. <li><a href="#findmetacharset">Finding the embedded encoding</a></li>
  46. <li><a href="#fixcharset">Fixing the encoding</a><ol>
  47. <li><a href="#fixcharset-none">No embedded encoding</a></li>
  48. <li><a href="#fixcharset-diff">Embedded encoding disagrees</a></li>
  49. <li><a href="#fixcharset-server">Changing the server encoding</a><ol>
  50. <li><a href="#fixcharset-server-php">PHP header() function</a></li>
  51. <li><a href="#fixcharset-server-phpini">PHP ini directive</a></li>
  52. <li><a href="#fixcharset-server-nophp">Non-PHP</a></li>
  53. <li><a href="#fixcharset-server-htaccess">.htaccess</a></li>
  54. <li><a href="#fixcharset-server-ext">File extensions</a></li>
  55. </ol></li>
  56. <li><a href="#fixcharset-xml">XML</a></li>
  57. <li><a href="#fixcharset-internals">Inside the process</a></li>
  58. </ol></li>
  59. <li><a href="#whyutf8">Why UTF-8?</a><ol>
  60. <li><a href="#whyutf8-i18n">Internationalization</a></li>
  61. <li><a href="#whyutf8-user">User-friendly</a></li>
  62. <li><a href="#whyutf8-forms">Forms</a><ol>
  63. <li><a href="#whyutf8-forms-urlencoded">application/x-www-form-urlencoded</a></li>
  64. <li><a href="#whyutf8-forms-multipart">multipart/form-data</a></li>
  65. </ol></li>
  66. <li><a href="#whyutf8-support">Well supported</a></li>
  67. <li><a href="#whyutf8-htmlpurifier">HTML Purifiers</a></li>
  68. </ol></li>
  69. <li><a href="#migrate">Migrate to UTF-8</a><ol>
  70. <li><a href="#migrate-db">Configuring your database</a><ol>
  71. <li><a href="#migrate-db-legit">Legit method</a></li>
  72. <li><a href="#migrate-db-binary">Binary</a></li>
  73. </ol></li>
  74. <li><a href="#migrate-editor">Text editor</a></li>
  75. <li><a href="#migrate-bom">Byte Order Mark (headers already sent!)</a></li>
  76. <li><a href="#migrate-fonts">Fonts</a><ol>
  77. <li><a href="#migrate-fonts-obscure">Obscure scripts</a></li>
  78. <li><a href="#migrate-fonts-occasional">Occasional use</a></li>
  79. </ol></li>
  80. <li><a href="#migrate-variablewidth">Dealing with variable width in functions</a></li>
  81. </ol></li>
  82. <li><a href="#externallinks">Further Reading</a></li>
  83. </ol>
  84. <h2 id="findcharset">Finding the real encoding</h2>
  85. <p>In the beginning, there was ASCII, and things were simple. But they
  86. weren't good, for no one could write in Cyrillic or Thai. So there
  87. exploded a proliferation of character encodings to remedy the problem
  88. by extending the characters ASCII could express. This ridiculously
  89. simplified version of the history of character encodings shows us that
  90. there are now many character encodings floating around.</p>
  91. <blockquote class="aside">
  92. <p>A <strong>character encoding</strong> tells the computer how to
  93. interpret raw zeroes and ones into real characters. It
  94. usually does this by pairing numbers with characters.</p>
  95. <p>There are many different types of character encodings floating
  96. around, but the ones we deal most frequently with are ASCII,
  97. 8-bit encodings, and Unicode-based encodings.</p>
  98. <ul>
  99. <li><strong>ASCII</strong> is a 7-bit encoding based on the
  100. English alphabet.</li>
  101. <li><strong>8-bit encodings</strong> are extensions to ASCII
  102. that add a potpourri of useful, non-standard characters
  103. like &eacute; and &aelig;. They can only add 127 characters,
  104. so usually only support one script at a time. When you
  105. see a page on the web, chances are it's encoded in one
  106. of these encodings.</li>
  107. <li><strong>Unicode-based encodings</strong> implement the
  108. Unicode standard and include UTF-8, UTF-16 and UTF-32/UCS-4.
  109. They go beyond 8-bits and support almost
  110. every language in the world. UTF-8 is gaining traction
  111. as the dominant international encoding of the web.</li>
  112. </ul>
  113. </blockquote>
  114. <p>The first step of our journey is to find out what the encoding of
  115. your website is. The most reliable way is to ask your
  116. browser:</p>
  117. <dl>
  118. <dt>Mozilla Firefox</dt>
  119. <dd>Tools &gt; Page Info: Encoding</dd>
  120. <dt>Internet Explorer</dt>
  121. <dd>View &gt; Encoding: bulleted item is unofficial name</dd>
  122. </dl>
  123. <p>Internet Explorer won't give you the MIME (i.e. useful/real) name of the
  124. character encoding, so you'll have to look it up using their description.
  125. Some common ones:</p>
  126. <table class="table">
  127. <thead><tr>
  128. <th>IE's Description</th>
  129. <th>Mime Name</th>
  130. </tr></thead>
  131. <tbody>
  132. <tr><th colspan="2">Windows</th></tr>
  133. <tr><td>Arabic (Windows)</td><td>Windows-1256</td></tr>
  134. <tr><td>Baltic (Windows)</td><td>Windows-1257</td></tr>
  135. <tr><td>Central European (Windows)</td><td>Windows-1250</td></tr>
  136. <tr><td>Cyrillic (Windows)</td><td>Windows-1251</td></tr>
  137. <tr><td>Greek (Windows)</td><td>Windows-1253</td></tr>
  138. <tr><td>Hebrew (Windows)</td><td>Windows-1255</td></tr>
  139. <tr><td>Thai (Windows)</td><td>TIS-620</td></tr>
  140. <tr><td>Turkish (Windows)</td><td>Windows-1254</td></tr>
  141. <tr><td>Vietnamese (Windows)</td><td>Windows-1258</td></tr>
  142. <tr><td>Western European (Windows)</td><td>Windows-1252</td></tr>
  143. </tbody>
  144. <tbody>
  145. <tr><th colspan="2">ISO</th></tr>
  146. <tr><td>Arabic (ISO)</td><td>ISO-8859-6</td></tr>
  147. <tr><td>Baltic (ISO)</td><td>ISO-8859-4</td></tr>
  148. <tr><td>Central European (ISO)</td><td>ISO-8859-2</td></tr>
  149. <tr><td>Cyrillic (ISO)</td><td>ISO-8859-5</td></tr>
  150. <tr class="minor"><td>Estonian (ISO)</td><td>ISO-8859-13</td></tr>
  151. <tr class="minor"><td>Greek (ISO)</td><td>ISO-8859-7</td></tr>
  152. <tr><td>Hebrew (ISO-Logical)</td><td>ISO-8859-8-l</td></tr>
  153. <tr><td>Hebrew (ISO-Visual)</td><td>ISO-8859-8</td></tr>
  154. <tr class="minor"><td>Latin 9 (ISO)</td><td>ISO-8859-15</td></tr>
  155. <tr class="minor"><td>Turkish (ISO)</td><td>ISO-8859-9</td></tr>
  156. <tr><td>Western European (ISO)</td><td>ISO-8859-1</td></tr>
  157. </tbody>
  158. <tbody>
  159. <tr><th colspan="2">Other</th></tr>
  160. <tr><td>Chinese Simplified (GB18030)</td><td>GB18030</td></tr>
  161. <tr><td>Chinese Simplified (GB2312)</td><td>GB2312</td></tr>
  162. <tr><td>Chinese Simplified (HZ)</td><td>HZ</td></tr>
  163. <tr><td>Chinese Traditional (Big5)</td><td>Big5</td></tr>
  164. <tr><td>Japanese (Shift-JIS)</td><td>Shift_JIS</td></tr>
  165. <tr><td>Japanese (EUC)</td><td>EUC-JP</td></tr>
  166. <tr><td>Korean</td><td>EUC-KR</td></tr>
  167. <tr><td>Unicode (UTF-8)</td><td>UTF-8</td></tr>
  168. </tbody>
  169. </table>
  170. <p>Internet Explorer does not recognize some of the more obscure
  171. character encodings, and having to lookup the real names with a table
  172. is a pain, so I recommend using Mozilla Firefox to find out your
  173. character encoding.</p>
  174. <h2 id="findmetacharset">Finding the embedded encoding</h2>
  175. <p>At this point, you may be asking, &quot;Didn't we already find out our
  176. encoding?&quot; Well, as it turns out, there are multiple places where
  177. a web developer can specify a character encoding, and one such place
  178. is in a <code>META</code> tag:</p>
  179. <pre>&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;</pre>
  180. <p>You'll find this in the <code>HEAD</code> section of an HTML document.
  181. The text to the right of <code>charset=</code> is the &quot;claimed&quot;
  182. encoding: the HTML claims to be this encoding, but whether or not this
  183. is actually the case depends on other factors. For now, take note
  184. if your <code>META</code> tag claims that either:</p>
  185. <ol>
  186. <li>The character encoding is the same as the one reported by the
  187. browser,</li>
  188. <li>The character encoding is different from the browser's, or</li>
  189. <li>There is no <code>META</code> tag at all! (horror, horror!)</li>
  190. </ol>
  191. <h2 id="fixcharset">Fixing the encoding</h2>
  192. <p class="aside">The advice given here is for pages being served as
  193. vanilla <code>text/html</code>. Different practices must be used
  194. for <code>application/xml</code> or <code>application/xml+xhtml</code>, see
  195. <a href="http://www.w3.org/TR/2002/NOTE-xhtml-media-types-20020430/">W3C's
  196. document on XHTML media types</a> for more information.</p>
  197. <p>If your <code>META</code> encoding and your real encoding match,
  198. savvy! You can skip this section. If they don't...</p>
  199. <h3 id="fixcharset-none">No embedded encoding</h3>
  200. <p>If this is the case, you'll want to add in the appropriate
  201. <code>META</code> tag to your website. It's as simple as copy-pasting
  202. the code snippet above and replacing UTF-8 with whatever is the mime name
  203. of your real encoding.</p>
  204. <blockquote class="aside">
  205. <p>For all those skeptics out there, there is a very good reason
  206. why the character encoding should be explicitly stated. When the
  207. browser isn't told what the character encoding of a text is, it
  208. has to guess: and sometimes the guess is wrong. Hackers can manipulate
  209. this guess in order to slip XSS past filters and then fool the
  210. browser into executing it as active code. A great example of this
  211. is the <a href="http://shiflett.org/archive/177">Google UTF-7
  212. exploit</a>.</p>
  213. <p>You might be able to get away with not specifying a character
  214. encoding with the <code>META</code> tag as long as your webserver
  215. sends the right Content-Type header, but why risk it? Besides, if
  216. the user downloads the HTML file, there is no longer any webserver
  217. to define the character encoding.</p>
  218. </blockquote>
  219. <h3 id="fixcharset-diff">Embedded encoding disagrees</h3>
  220. <p>This is an extremely common mistake: another source is telling
  221. the browser what the
  222. character encoding is and is overriding the embedded encoding. This
  223. source usually is the Content-Type HTTP header that the webserver (i.e.
  224. Apache) sends. A usual Content-Type header sent with a page might
  225. look like this:</p>
  226. <pre>Content-Type: text/html; charset=ISO-8859-1</pre>
  227. <p>Notice how there is a charset parameter: this is the webserver's
  228. way of telling a browser what the character encoding is, much like
  229. the <code>META</code> tags we touched upon previously.</p>
  230. <blockquote class="aside"><p>In fact, the <code>META</code> tag is
  231. designed as a substitute for the HTTP header for contexts where
  232. sending headers is impossible (such as locally stored files without
  233. a webserver). Thus the name <code>http-equiv</code> (HTTP equivalent).
  234. </p></blockquote>
  235. <p>There are two ways to go about fixing this: changing the <code>META</code>
  236. tag to match the HTTP header, or changing the HTTP header to match
  237. the <code>META</code> tag. How do we know which to do? It depends
  238. on the website's content: after all, headers and tags are only ways of
  239. describing the actual characters on the web page.</p>
  240. <p>If your website:</p>
  241. <dl>
  242. <dt>...only uses ASCII characters,</dt>
  243. <dd>Either way is fine, but I recommend switching both to
  244. UTF-8 (more on this later).</dd>
  245. <dt>...uses special characters, and they display
  246. properly,</dt>
  247. <dd>Change the embedded encoding to the server encoding.</dd>
  248. <dt>...uses special characters, but users often complain that
  249. they come out garbled,</dt>
  250. <dd>Change the server encoding to the embedded encoding.</dd>
  251. </dl>
  252. <p>Changing a META tag is easy: just swap out the old encoding
  253. for the new. Changing the server (HTTP header) encoding, however,
  254. is slightly more difficult.</p>
  255. <h3 id="fixcharset-server">Changing the server encoding</h3>
  256. <h4 id="fixcharset-server-php">PHP header() function</h4>
  257. <p>The simplest way to handle this problem is to send the encoding
  258. yourself, via your programming language. Since you're using HTML
  259. Purifier, I'll assume PHP, although it's not too difficult to do
  260. similar things in
  261. <a href="http://www.w3.org/International/O-HTTP-charset#scripting">other
  262. languages</a>. The appropriate code is:</p>
  263. <pre><a href="http://php.net/function.header">header</a>('Content-Type:text/html; charset=UTF-8');</pre>
  264. <p>...replacing UTF-8 with whatever your embedded encoding is.
  265. This code must come before any output, so be careful about
  266. stray whitespace in your application (i.e., any whitespace before
  267. output excluding whitespace within &lt;?php ?&gt; tags).</p>
  268. <h4 id="fixcharset-server-phpini">PHP ini directive</h4>
  269. <p>PHP also has a neat little ini directive that can save you a
  270. header call: <code><a href="http://php.net/ini.core#ini.default-charset">default_charset</a></code>. Using this code:</p>
  271. <pre><a href="http://php.net/function.ini_set">ini_set</a>('default_charset', 'UTF-8');</pre>
  272. <p>...will also do the trick. If PHP is running as an Apache module (and
  273. not as FastCGI, consult
  274. <a href="http://php.net/phpinfo">phpinfo</a>() for details), you can even use htaccess to apply this property
  275. across many PHP files:</p>
  276. <pre><a href="http://php.net/configuration.changes#configuration.changes.apache">php_value</a> default_charset &quot;UTF-8&quot;</pre>
  277. <blockquote class="aside"><p>As with all INI directives, this can
  278. also go in your php.ini file. Some hosting providers allow you to customize
  279. your own php.ini file, ask your support for details. Use:</p>
  280. <pre>default_charset = &quot;utf-8&quot;</pre></blockquote>
  281. <h4 id="fixcharset-server-nophp">Non-PHP</h4>
  282. <p>You may, for whatever reason, need to set the character encoding
  283. on non-PHP files, usually plain ol' HTML files. Doing this
  284. is more of a hit-or-miss process: depending on the software being
  285. used as a webserver and the configuration of that software, certain
  286. techniques may work, or may not work.</p>
  287. <h4 id="fixcharset-server-htaccess">.htaccess</h4>
  288. <p>On Apache, you can use an .htaccess file to change the character
  289. encoding. I'll defer to
  290. <a href="http://www.w3.org/International/questions/qa-htaccess-charset">W3C</a>
  291. for the in-depth explanation, but it boils down to creating a file
  292. named .htaccess with the contents:</p>
  293. <pre><a href="http://httpd.apache.org/docs/1.3/mod/mod_mime.html#addcharset">AddCharset</a> UTF-8 .html</pre>
  294. <p>Where UTF-8 is replaced with the character encoding you want to
  295. use and .html is a file extension that this will be applied to. This
  296. character encoding will then be set for any file directly in
  297. or in the subdirectories of directory you place this file in.</p>
  298. <p>If you're feeling particularly courageous, you can use:</p>
  299. <pre><a href="http://httpd.apache.org/docs/1.3/mod/core.html#adddefaultcharset">AddDefaultCharset</a> UTF-8</pre>
  300. <p>...which changes the character set Apache adds to any document that
  301. doesn't have any Content-Type parameters. This directive, which the
  302. default configuration file sets to iso-8859-1 for security
  303. reasons, is probably why your headers mismatch
  304. with the <code>META</code> tag. If you would prefer Apache not to be
  305. butting in on your character encodings, you can tell it not
  306. to send anything at all:</p>
  307. <pre><a href="http://httpd.apache.org/docs/1.3/mod/core.html#adddefaultcharset">AddDefaultCharset</a> Off</pre>
  308. <p>...making your internal charset declaration (usually the <code>META</code> tags)
  309. the sole source of character encoding
  310. information. In these cases, it is <em>especially</em> important to make
  311. sure you have valid <code>META</code> tags on your pages and all the
  312. text before them is ASCII.</p>
  313. <blockquote class="aside"><p>These directives can also be
  314. placed in httpd.conf file for Apache, but
  315. in most shared hosting situations you won't be able to edit this file.
  316. </p></blockquote>
  317. <h4 id="fixcharset-server-ext">File extensions</h4>
  318. <p>If you're not allowed to use .htaccess files, you can often
  319. piggy-back off of Apache's default AddCharset declarations to get
  320. your files in the proper extension. Here are Apache's default
  321. character set declarations:</p>
  322. <table class="table">
  323. <thead><tr>
  324. <th>Charset</th>
  325. <th>File extension(s)</th>
  326. </tr></thead>
  327. <tbody>
  328. <tr><td>ISO-8859-1</td><td>.iso8859-1 .latin1</td></tr>
  329. <tr><td>ISO-8859-2</td><td>.iso8859-2 .latin2 .cen</td></tr>
  330. <tr><td>ISO-8859-3</td><td>.iso8859-3 .latin3</td></tr>
  331. <tr><td>ISO-8859-4</td><td>.iso8859-4 .latin4</td></tr>
  332. <tr><td>ISO-8859-5</td><td>.iso8859-5 .latin5 .cyr .iso-ru</td></tr>
  333. <tr><td>ISO-8859-6</td><td>.iso8859-6 .latin6 .arb</td></tr>
  334. <tr><td>ISO-8859-7</td><td>.iso8859-7 .latin7 .grk</td></tr>
  335. <tr><td>ISO-8859-8</td><td>.iso8859-8 .latin8 .heb</td></tr>
  336. <tr><td>ISO-8859-9</td><td>.iso8859-9 .latin9 .trk</td></tr>
  337. <tr><td>ISO-2022-JP</td><td>.iso2022-jp .jis</td></tr>
  338. <tr><td>ISO-2022-KR</td><td>.iso2022-kr .kis</td></tr>
  339. <tr><td>ISO-2022-CN</td><td>.iso2022-cn .cis</td></tr>
  340. <tr><td>Big5</td><td>.Big5 .big5 .b5</td></tr>
  341. <tr><td>WINDOWS-1251</td><td>.cp-1251 .win-1251</td></tr>
  342. <tr><td>CP866</td><td>.cp866</td></tr>
  343. <tr><td>KOI8-r</td><td>.koi8-r .koi8-ru</td></tr>
  344. <tr><td>KOI8-ru</td><td>.koi8-uk .ua</td></tr>
  345. <tr><td>ISO-10646-UCS-2</td><td>.ucs2</td></tr>
  346. <tr><td>ISO-10646-UCS-4</td><td>.ucs4</td></tr>
  347. <tr><td>UTF-8</td><td>.utf8</td></tr>
  348. <tr><td>GB2312</td><td>.gb2312 .gb </td></tr>
  349. <tr><td>utf-7</td><td>.utf7</td></tr>
  350. <tr><td>EUC-TW</td><td>.euc-tw</td></tr>
  351. <tr><td>EUC-JP</td><td>.euc-jp</td></tr>
  352. <tr><td>EUC-KR</td><td>.euc-kr</td></tr>
  353. <tr><td>shift_jis</td><td>.sjis</td></tr>
  354. </tbody>
  355. </table>
  356. <p>So, for example, a file named <code>page.utf8.html</code> or
  357. <code>page.html.utf8</code> will probably be sent with the UTF-8 charset
  358. attached, the difference being that if there is an
  359. <code>AddCharset charset .html</code> declaration, it will override
  360. the .utf8 extension in <code>page.utf8.html</code> (precedence moves
  361. from right to left). By default, Apache has no such declaration.</p>
  362. <h4 id="fixcharset-server-iis">Microsoft IIS</h4>
  363. <p>If anyone can contribute information on how to configure Microsoft
  364. IIS to change character encodings, I'd be grateful.</p>
  365. <h3 id="fixcharset-xml">XML</h3>
  366. <p><code>META</code> tags are the most common source of embedded
  367. encodings, but they can also come from somewhere else: XML
  368. Declarations. They look like:</p>
  369. <pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</pre>
  370. <p>...and are most often found in XML documents (including XHTML).</p>
  371. <p>For XHTML, this XML Declaration theoretically
  372. overrides the <code>META</code> tag. In reality, this happens only when the
  373. XHTML is actually served as legit XML and not HTML, which is almost always
  374. never due to Internet Explorer's lack of support for
  375. <code>application/xhtml+xml</code> (even though doing so is often
  376. argued to be <a href="http://www.hixie.ch/advocacy/xhtml">good
  377. practice</a> and is required by the XHTML 1.1 specification).</p>
  378. <p>For XML, however, this XML Declaration is extremely important.
  379. Since most webservers are not configured to send charsets for .xml files,
  380. this is the only thing a parser has to go on. Furthermore, the default
  381. for XML files is UTF-8, which often butts heads with more common
  382. ISO-8859-1 encoding (you see this in garbled RSS feeds).</p>
  383. <p>In short, if you use XHTML and have gone through the
  384. trouble of adding the XML Declaration, make sure it jives
  385. with your <code>META</code> tags (which should only be present
  386. if served in text/html) and HTTP headers.</p>
  387. <h3 id="fixcharset-internals">Inside the process</h3>
  388. <p>This section is not required reading,
  389. but may answer some of your questions on what's going on in all
  390. this character encoding hocus pocus. If you're interested in
  391. moving on to the next phase, skip this section.</p>
  392. <p>A logical question that follows all of our wheeling and dealing
  393. with multiple sources of character encodings is &quot;Why are there
  394. so many options?&quot; To answer this question, we have to turn
  395. back our definition of character encodings: they allow a program
  396. to interpret bytes into human-readable characters.</p>
  397. <p>Thus, a chicken-egg problem: a character encoding
  398. is necessary to interpret the
  399. text of a document. A <code>META</code> tag is in the text of a document.
  400. The <code>META</code> tag gives the character encoding. How can we
  401. determine the contents of a <code>META</code> tag, inside the text,
  402. if we don't know it's character encoding? And how do we figure out
  403. the character encoding, if we don't know the contents of the
  404. <code>META</code> tag?</p>
  405. <p>Fortunately for us, the characters we need to write the
  406. <code>META</code> are in ASCII, which is pretty much universal
  407. over every character encoding that is in common use today. So,
  408. all the web-browser has to do is parse all the way down until
  409. it gets to the Content-Type tag, extract the character encoding
  410. tag, then re-parse the document according to this new information.</p>
  411. <p>Obviously this is complicated, so browsers prefer the simpler
  412. and more efficient solution: get the character encoding from a
  413. somewhere other than the document itself, i.e. the HTTP headers,
  414. much to the chagrin of HTML authors who can't set these headers.</p>
  415. <h2 id="whyutf8">Why UTF-8?</h2>
  416. <p>So, you've gone through all the trouble of ensuring that your
  417. server and embedded characters all line up properly and are
  418. present. Good job: at
  419. this point, you could quit and rest easy knowing that your pages
  420. are not vulnerable to character encoding style XSS attacks.
  421. However, just as having a character encoding is better than
  422. having no character encoding at all, having UTF-8 as your
  423. character encoding is better than having some other random
  424. character encoding, and the next step is to convert to UTF-8.
  425. But why?</p>
  426. <h3 id="whyutf8-i18n">Internationalization</h3>
  427. <p>Many software projects, at one point or another, suddenly realize
  428. that they should be supporting more than one language. Even regular
  429. usage in one language sometimes requires the occasional special character
  430. that, without surprise, is not available in your character set. Sometimes
  431. developers get around this by adding support for multiple encodings: when
  432. using Chinese, use Big5, when using Japanese, use Shift-JIS, when
  433. using Greek, etc. Other times, they use character references with great
  434. zeal.</p>
  435. <p>UTF-8, however, obviates the need for any of these complicated
  436. measures. After getting the system to use UTF-8 and adjusting for
  437. sources that are outside the hand of the browser (more on this later),
  438. UTF-8 just works. You can use it for any language, even many languages
  439. at once, you don't have to worry about managing multiple encodings,
  440. you don't have to use those user-unfriendly entities.</p>
  441. <h3 id="whyutf8-user">User-friendly</h3>
  442. <p>Websites encoded in Latin-1 (ISO-8859-1) which occasionally need
  443. a special character outside of their scope often will use a character
  444. entity reference to achieve the desired effect. For instance, &theta; can be
  445. written <code>&amp;theta;</code>, regardless of the character encoding's
  446. support of Greek letters.</p>
  447. <p>This works nicely for limited use of special characters, but
  448. say you wanted this sentence of Chinese text: &#28608;&#20809;,
  449. &#36889;&#20841;&#20491;&#23383;&#26159;&#29978;&#40636;&#24847;&#24605;.
  450. The ampersand encoded version would look like this:</p>
  451. <pre>&amp;#28608;&amp;#20809;, &amp;#36889;&amp;#20841;&amp;#20491;&amp;#23383;&amp;#26159;&amp;#29978;&amp;#40636;&amp;#24847;&amp;#24605;</pre>
  452. <p>Extremely inconvenient for those of us who actually know what
  453. character entities are, totally unintelligible to poor users who don't!
  454. Even the slightly more user-friendly, &quot;intelligible&quot; character
  455. entities like <code>&amp;theta;</code> will leave users who are
  456. uninterested in learning HTML scratching their heads. On the other
  457. hand, if they see &theta; in an edit box, they'll know that it's a
  458. special character, and treat it accordingly, even if they don't know
  459. how to write that character themselves.</p>
  460. <blockquote class="aside"><p>Wikipedia is a great case study for
  461. an application that originally used ISO-8859-1 but switched to UTF-8
  462. when it became far to cumbersome to support foreign languages. Bots
  463. will now actually go through articles and convert character entities
  464. to their corresponding real characters for the sake of user-friendliness
  465. and searchability. See
  466. <a href="http://meta.wikimedia.org/wiki/Help:Special_characters">Meta's
  467. page on special characters</a> for more details.
  468. </p></blockquote>
  469. <h3 id="whyutf8-forms">Forms</h3>
  470. <p>While we're on the tack of users, how do non-UTF-8 web forms deal
  471. with characters that are outside of their character set? Rather than
  472. discuss what UTF-8 does right, we're going to show what could go wrong
  473. if you didn't use UTF-8 and people tried to use characters outside
  474. of your character encoding.</p>
  475. <p>The troubles are large, extensive, and extremely difficult to fix (or,
  476. at least, difficult enough that if you had the time and resources to invest
  477. in doing the fix, you would be probably better off migrating to UTF-8).
  478. There are two types of form submission: <code>application/x-www-form-urlencoded</code>
  479. which is used for GET and by default for POST, and <code>multipart/form-data</code>
  480. which may be used by POST, and is required when you want to upload
  481. files.</p>
  482. <p>The following is a summarization of notes from
  483. <a href="http://web.archive.org/web/20060427015200/ppewww.ph.gla.ac.uk/~flavell/charset/form-i18n.html">
  484. <code>FORM</code> submission and i18n</a>. That document contains lots
  485. of useful information, but is written in a rambly manner, so
  486. here I try to get right to the point. (Note: the original has
  487. disappeared off the web, so I am linking to the Web Archive copy.)</p>
  488. <h4 id="whyutf8-forms-urlencoded"><code>application/x-www-form-urlencoded</code></h4>
  489. <p>This is the Content-Type that GET requests must use, and POST requests
  490. use by default. It involves the ubiquitous percent encoding format that
  491. looks something like: <code>%C3%86</code>. There is no official way of
  492. determining the character encoding of such a request, since the percent
  493. encoding operates on a byte level, so it is usually assumed that it
  494. is the same as the encoding the page containing the form was submitted
  495. in. (<a href="http://tools.ietf.org/html/rfc3986#section-2.5">RFC 3986</a>
  496. recommends that textual identifiers be translated to UTF-8; however, browser
  497. compliance is spotty.) You'll run into very few problems
  498. if you only use characters in the character encoding you chose.</p>
  499. <p>However, once you start adding characters outside of your encoding
  500. (and this is a lot more common than you may think: take curly
  501. &quot;smart&quot; quotes from Microsoft as an example),
  502. a whole manner of strange things start to happen. Depending on the
  503. browser you're using, they might:</p>
  504. <ul>
  505. <li>Replace the unsupported characters with useless question marks,</li>
  506. <li>Attempt to fix the characters (example: smart quotes to regular quotes),</li>
  507. <li>Replace the character with a character entity reference, or</li>
  508. <li>Send it anyway as a different character encoding mixed in
  509. with the original encoding (usually Windows-1252 rather than
  510. iso-8859-1 or UTF-8 interspersed in 8-bit)</li>
  511. </ul>
  512. <p>To properly guard against these behaviors, you'd have to sniff out
  513. the browser agent, compile a database of different behaviors, and
  514. take appropriate conversion action against the string (disregarding
  515. a spate of extremely mysterious, random and devastating bugs Internet
  516. Explorer manifests every once in a while). Or you could
  517. use UTF-8 and rest easy knowing that none of this could possibly happen
  518. since UTF-8 supports every character.</p>
  519. <h4 id="whyutf8-forms-multipart"><code>multipart/form-data</code></h4>
  520. <p>Multipart form submission takes away a lot of the ambiguity
  521. that percent-encoding had: the server now can explicitly ask for
  522. certain encodings, and the client can explicitly tell the server
  523. during the form submission what encoding the fields are in.</p>
  524. <p>There are two ways you go with this functionality: leave it
  525. unset and have the browser send in the same encoding as the page,
  526. or set it to UTF-8 and then do another conversion server-side.
  527. Each method has deficiencies, especially the former.</p>
  528. <p>If you tell the browser to send the form in the same encoding as
  529. the page, you still have the trouble of what to do with characters
  530. that are outside of the character encoding's range. The behavior, once
  531. again, varies: Firefox 2.0 converts them to character entity references
  532. while Internet Explorer 7.0 mangles them beyond intelligibility. For
  533. serious internationalization purposes, this is not an option.</p>
  534. <p>The other possibility is to set Accept-Encoding to UTF-8, which
  535. begs the question: Why aren't you using UTF-8 for everything then?
  536. This route is more palatable, but there's a notable caveat: your data
  537. will come in as UTF-8, so you will have to explicitly convert it into
  538. your favored local character encoding.</p>
  539. <p>I object to this approach on idealogical grounds: you're
  540. digging yourself deeper into
  541. the hole when you could have been converting to UTF-8
  542. instead. And, of course, you can't use this method for GET requests.</p>
  543. <h3 id="whyutf8-support">Well supported</h3>
  544. <p>Almost every modern browser in the wild today has full UTF-8 and Unicode
  545. support: the number of troublesome cases can be counted with the
  546. fingers of one hand, and these browsers usually have trouble with
  547. other character encodings too. Problems users usually encounter stem
  548. from the lack of appropriate fonts to display the characters (once
  549. again, this applies to all character encodings and HTML entities) or
  550. Internet Explorer's lack of intelligent font picking (which can be
  551. worked around).</p>
  552. <p>We will go into more detail about how to deal with edge cases in
  553. the browser world in the Migration section, but rest assured that
  554. converting to UTF-8, if done correctly, will not result in users
  555. hounding you about broken pages.</p>
  556. <h3 id="whyutf8-htmlpurifier">HTML Purifier</h3>
  557. <p>And finally, we get to HTML Purifier. HTML Purifier is built to
  558. deal with UTF-8: any indications otherwise are the result of an
  559. encoder that converts text from your preferred encoding to UTF-8, and
  560. back again. HTML Purifier never touches anything else, and leaves
  561. it up to the module iconv to do the dirty work.</p>
  562. <p>This approach, however, is not perfect. iconv is blithely unaware
  563. of HTML character entities. HTML Purifier, in order to
  564. protect against sophisticated escaping schemes, normalizes all character
  565. and numeric entity references before processing the text. This leads to
  566. one important ramification:</p>
  567. <p><strong>Any character that is not supported by the target character
  568. set, regardless of whether or not it is in the form of a character
  569. entity reference or a raw character, will be silently ignored.</strong></p>
  570. <p>Example of this principle at work: say you have <code>&amp;theta;</code>
  571. in your HTML, but the output is in Latin-1 (which, understandably,
  572. does not understand Greek), the following process will occur (assuming you've
  573. set the encoding correctly using %Core.Encoding):</p>
  574. <ul>
  575. <li>The <code>Encoder</code> will transform the text from ISO 8859-1 to UTF-8
  576. (note that theta is preserved here since it doesn't actually use
  577. any non-ASCII characters): <code>&amp;theta;</code></li>
  578. <li>The <code>EntityParser</code> will transform all named and numeric
  579. character entities to their corresponding raw UTF-8 equivalents:
  580. <code>&theta;</code></li>
  581. <li>HTML Purifier processes the code: <code>&theta;</code></li>
  582. <li>The <code>Encoder</code> now transforms the text back from UTF-8
  583. to ISO 8859-1. Since Greek is not supported by ISO 8859-1, it
  584. will be either ignored or replaced with a question mark:
  585. <code>?</code></li>
  586. </ul>
  587. <p>This behaviour is quite unsatisfactory. It is a deal-breaker for
  588. international applications, and it can be mildly annoying for the provincial
  589. soul who occasionally needs a special character. Since 1.4.0, HTML
  590. Purifier has provided a slightly more palatable workaround using
  591. %Core.EscapeNonASCIICharacters. The process now looks like:</p>
  592. <ul>
  593. <li>The <code>Encoder</code> transforms encoding to UTF-8: <code>&amp;theta;</code></li>
  594. <li>The <code>EntityParser</code> transforms entities: <code>&theta;</code></li>
  595. <li>HTML Purifier processes the code: <code>&theta;</code></li>
  596. <li>The <code>Encoder</code> replaces all non-ASCII characters
  597. with numeric entity reference: <code>&amp;#952;</code></li>
  598. <li>For good measure, <code>Encoder</code> transforms encoding back to
  599. original (which is strictly unnecessary for 99% of encodings
  600. out there): <code>&amp;#952;</code> (remember, it's all ASCII!)</li>
  601. </ul>
  602. <p>...which means that this is only good for an occasional foray into
  603. the land of Unicode characters, and is totally unacceptable for Chinese
  604. or Japanese texts. The even bigger kicker is that, supposing the
  605. input encoding was actually ISO-8859-7, which <em>does</em> support
  606. theta, the character would get converted into a character entity reference
  607. anyway! (The Encoder does not discriminate).</p>
  608. <p>The current functionality is about where HTML Purifier will be for
  609. the rest of eternity. HTML Purifier could attempt to preserve the original
  610. form of the character references so that they could be substituted back in, only the
  611. DOM extension kills them off irreversibly. HTML Purifier could also attempt
  612. to be smart and only convert non-ASCII characters that weren't supported
  613. by the target encoding, but that would require reimplementing iconv
  614. with HTML awareness, something I will not do.</p>
  615. <p>So there: either it's UTF-8 or crippled international support. Your pick! (and I'm
  616. not being sarcastic here: some people could care less about other languages).</p>
  617. <h2 id="migrate">Migrate to UTF-8</h2>
  618. <p>So, you've decided to bite the bullet, and want to migrate to UTF-8.
  619. Note that this is not for the faint-hearted, and you should expect
  620. the process to take longer than you think it will take.</p>
  621. <p>The general idea is that you convert all existing text to UTF-8,
  622. and then you set all the headers and META tags we discussed earlier
  623. to UTF-8. There are many ways going about doing this: you could
  624. write a conversion script that runs through the database and re-encodes
  625. everything as UTF-8 or you could do the conversion on the fly when someone
  626. reads the page. The details depend on your system, but I will cover
  627. some of the more subtle points of migration that may trip you up.</p>
  628. <h3 id="migrate-db">Configuring your database</h3>
  629. <p>Most modern databases, the most prominent open-source ones being MySQL
  630. 4.1+ and PostgreSQL, support character encodings. If you're switching
  631. to UTF-8, logically speaking, you'd want to make sure your database
  632. knows about the change too. There are some caveats though:</p>
  633. <h4 id="migrate-db-legit">Legit method</h4>
  634. <p>Standardization in terms of SQL syntax for specifying character
  635. encodings is notoriously spotty. Refer to your respective database's
  636. documentation on how to do this properly.</p>
  637. <p>For <a href="http://dev.mysql.com/doc/refman/5.0/en/charset-conversion.html">MySQL</a>, <code>ALTER</code> will magically perform the
  638. character encoding conversion for you. However, you have
  639. to make sure that the text inside the column is what is says it is:
  640. if you had put Shift-JIS in an ISO 8859-1 column, MySQL will irreversibly mangle
  641. the text when you try to convert it to UTF-8. You'll have to convert
  642. it to a binary field, convert it to a Shift-JIS field (the real encoding),
  643. and then finally to UTF-8. Many a website had pages irreversibly mangled
  644. because they didn't realize that they'd been deluding themselves about
  645. the character encoding all along; don't become the next victim.</p>
  646. <p>For <a href="http://www.postgresql.org/docs/8.2/static/multibyte.html">PostgreSQL</a>, there appears to be no direct way to change the
  647. encoding of a database (as of 8.2). You will have to dump the data, and then reimport
  648. it into a new table. Make sure that your client encoding is set properly:
  649. this is how PostgreSQL knows to perform an encoding conversion.</p>
  650. <p>Many times, you will be also asked about the &quot;collation&quot; of
  651. the new column. Collation is how a DBMS sorts text, like ordering
  652. B, C and A into A, B and C (the problem gets surprisingly complicated
  653. when you get to languages like Thai and Japanese). If in doubt,
  654. going with the default setting is usually a safe bet.</p>
  655. <p>Once the conversion is all said and done, you still have to remember
  656. to set the client encoding (your encoding) properly on each database
  657. connection using <code>SET NAMES</code> (which is standard SQL and is
  658. usually supported).</p>
  659. <h4 id="migrate-db-binary">Binary</h4>
  660. <p>Due to the aforementioned compatibility issues, a more interoperable
  661. way of storing UTF-8 text is to stuff it in a binary datatype.
  662. <code>CHAR</code> becomes <code>BINARY</code>, <code>VARCHAR</code> becomes
  663. <code>VARBINARY</code> and <code>TEXT</code> becomes <code>BLOB</code>.
  664. Doing so can save you some huge headaches:</p>
  665. <ul>
  666. <li>The syntax for binary data types is very portable,</li>
  667. <li>MySQL 4.0 has <em>no</em> support for character encodings, so
  668. if you want to support it you <em>have</em> to use binary,</li>
  669. <li>MySQL, as of 5.1, has no support for four byte UTF-8 characters,
  670. which represent characters beyond the basic multilingual
  671. plane, and</li>
  672. <li>You will never have to worry about your DBMS being too smart
  673. and attempting to convert your text when you don't want it to.</li>
  674. </ul>
  675. <p>MediaWiki, a very prominent international application, uses binary fields
  676. for storing their data because of point three.</p>
  677. <p>There are drawbacks, of course:</p>
  678. <ul>
  679. <li>Database tools like PHPMyAdmin won't be able to offer you inline
  680. text editing, since it is declared as binary,</li>
  681. <li>It's not semantically correct: it's really text not binary
  682. (lying to the database),</li>
  683. <li>Unless you use the not-very-portable wizardry mentioned above,
  684. you have to change the encoding yourself (usually, you'd do
  685. it on the fly), and</li>
  686. <li>You will not have collation.</li>
  687. </ul>
  688. <p>Choose based on your circumstances.</p>
  689. <h3 id="migrate-editor">Text editor</h3>
  690. <p>For more flat-file oriented systems, you will often be tasked with
  691. converting reams of existing text and HTML files into UTF-8, as well as
  692. making sure that all new files uploaded are properly encoded. Once again,
  693. I can only point vaguely in the right direction for converting your
  694. existing files: make sure you backup, make sure you use
  695. <a href="http://php.net/ref.iconv">iconv</a>(), and
  696. make sure you know what the original character encoding of the files
  697. is (or are, depending on the tidiness of your system).</p>
  698. <p>However, I can proffer more specific advice on the subject of
  699. text editors. Many text editors have notoriously spotty Unicode support.
  700. To find out how your editor is doing, you can check out <a
  701. href="http://www.alanwood.net/unicode/utilities_editors.html">this list</a>
  702. or <a href="http://en.wikipedia.org/wiki/Comparison_of_text_editors#Encoding_support">Wikipedia's list.</a>
  703. I personally use Notepad++, which works like a charm when it comes to UTF-8.
  704. Usually, you will have to <strong>explicitly</strong> tell the editor through some dialogue
  705. (usually Save as or Format) what encoding you want it to use. An editor
  706. will often offer &quot;Unicode&quot; as a method of saving, which is
  707. ambiguous. Make sure you know whether or not they really mean UTF-8
  708. or UTF-16 (which is another flavor of Unicode).</p>
  709. <p>The two things to look out for are whether or not the editor
  710. supports <strong>font mixing</strong> (multiple
  711. fonts in one document) and whether or not it adds a <strong>BOM</strong>.
  712. Font mixing is important because fonts rarely have support for every
  713. language known to mankind: in order to be flexible, an editor must
  714. be able to take a little from here and a little from there, otherwise
  715. all your Chinese characters will come as nice boxes. We'll discuss
  716. BOM below.</p>
  717. <h3 id="migrate-bom">Byte Order Mark (headers already sent!)</h3>
  718. <p>The BOM, or <a href="http://en.wikipedia.org/wiki/Byte_Order_Mark">Byte
  719. Order Mark</a>, is a magical, invisible character placed at
  720. the beginning of UTF-8 files to tell people what the encoding is and
  721. what the endianness of the text is. It is also unnecessary.</p>
  722. <p>Because it's invisible, it often
  723. catches people by surprise when it starts doing things it shouldn't
  724. be doing. For example, this PHP file:</p>
  725. <pre><strong>BOM</strong>&lt;?php
  726. header('Location: index.php');
  727. ?&gt;</pre>
  728. <p>...will fail with the all too familiar <strong>Headers already sent</strong>
  729. PHP error. And because the BOM is invisible, this culprit will go unnoticed.
  730. My suggestion is to only use ASCII in PHP pages, but if you must, make
  731. sure the page is saved WITHOUT the BOM.</p>
  732. <blockquote class="aside">
  733. <p>The headers the error is referring to are <strong>HTTP headers</strong>,
  734. which are sent to the browser before any HTML to tell it various
  735. information. The moment any regular text (and yes, a BOM counts as
  736. ordinary text) is output, the headers must be sent, and you are
  737. not allowed to send anymore. Thus, the error.</p>
  738. </blockquote>
  739. <p>If you are reading in text files to insert into the middle of another
  740. page, it is strongly advised (but not strictly necessary) that you replace out the UTF-8 byte
  741. sequence for BOM <code>&quot;\xEF\xBB\xBF&quot;</code> before inserting it in,
  742. via:</p>
  743. <pre>$text = str_replace(&quot;\xEF\xBB\xBF&quot;, '', $text);</pre>
  744. <h3 id="migrate-fonts">Fonts</h3>
  745. <p>Generally speaking, people who are having trouble with fonts fall
  746. into two categories:</p>
  747. <ul>
  748. <li>Those who want to
  749. use an extremely obscure language for which there is very little
  750. support even among native speakers of the language, and</li>
  751. <li>Those where the primary language of the text is
  752. well-supported but there are occasional characters
  753. that aren't supported.</li>
  754. </ul>
  755. <p>Yes, there's always a chance where an English user happens across
  756. a Sinhalese website and doesn't have the right font. But an English user
  757. who happens not to have the right fonts probably has no business reading Sinhalese
  758. anyway. So we'll deal with the other two edge cases.</p>
  759. <h4 id="migrate-fonts-obscure">Obscure scripts</h4>
  760. <p>If you run a Bengali website, you may get comments from users who
  761. would like to read your website but get heaps of question marks or
  762. other meaningless characters. Fixing this problem requires the
  763. installation of a font or language pack which is often highly
  764. dependent on what the language is. <a href="http://bn.wikipedia.org/wiki/%E0%A6%89%E0%A6%87%E0%A6%95%E0%A6%BF%E0%A6%AA%E0%A7%87%E0%A6%A1%E0%A6%BF%E0%A6%AF%E0%A6%BC%E0%A6%BE:Bangla_script_display_and_input_help">Here is an example</a>
  765. of such a help file for the Bengali language; I am sure there are
  766. others out there too. You just have to point users to the appropriate
  767. help file.</p>
  768. <h4 id="migrate-fonts-occasional">Occasional use</h4>
  769. <p>A prime example of when you'll see some very obscure Unicode
  770. characters embedded in what otherwise would be very bland ASCII are
  771. letters of the
  772. <a href="http://en.wikipedia.org/wiki/International_Phonetic_Alphabet">International
  773. Phonetic Alphabet (IPA)</a>, use to designate pronunciations in a very standard
  774. manner (you probably see them all the time in your dictionary). Your
  775. average font probably won't have support for all of the IPA characters
  776. like &#664; (bilabial click) or &#658; (voiced postalveolar fricative).
  777. So what's a poor browser to do? Font mix! Smart browsers like Mozilla Firefox
  778. and Internet Explorer 7 will borrow glyphs from other fonts in order
  779. to make sure that all the characters display properly.</p>
  780. <p>But what happens when the browser isn't smart and happens to be the
  781. most widely used browser in the entire world? Microsoft IE 6
  782. is not smart enough to borrow from other fonts when a character isn't
  783. present, so more often than not you'll be slapped with a nice big &#65533;.
  784. To get things to work, MSIE 6 needs a little nudge. You could configure it
  785. to use a different font to render the text, but you can achieve the same
  786. effect by selectively changing the font for blocks of special characters
  787. to known good Unicode fonts.</p>
  788. <p>Fortunately, the folks over at Wikipedia have already done all the
  789. heavy lifting for you. Get the CSS from the horses mouth here:
  790. <a href="http://en.wikipedia.org/wiki/MediaWiki:Common.css">Common.css</a>,
  791. and search for &quot;.IPA&quot; There are also a smattering of
  792. other classes you can use for other purposes, check out
  793. <a href="http://meta.wikimedia.org/wiki/Help:Special_characters#Displaying_Special_Characters">this page</a>
  794. for more details. For you lazy ones, this should work:</p>
  795. <pre>.Unicode {
  796. font-family: Code2000, &quot;TITUS Cyberbit Basic&quot;, &quot;Doulos SIL&quot;,
  797. &quot;Chrysanthi Unicode&quot;, &quot;Bitstream Cyberbit&quot;,
  798. &quot;Bitstream CyberBase&quot;, Thryomanes, Gentium, GentiumAlt,
  799. &quot;Lucida Grande&quot;, &quot;Arial Unicode MS&quot;, &quot;Microsoft Sans Serif&quot;,
  800. &quot;Lucida Sans Unicode&quot;;
  801. font-family /**/:inherit; /* resets fonts for everyone but IE6 */
  802. }</pre>
  803. <p>The standard usage goes along the lines of <code>&lt;span class=&quot;Unicode&quot;&gt;Crazy
  804. Unicode stuff here&lt;/span&gt;</code>. Characters in the
  805. <a href="http://en.wikipedia.org/wiki/Windows_Glyph_List_4">Windows Glyph List</a>
  806. usually don't need to be fixed, but for anything else you probably
  807. want to play it safe. Unless, of course, you don't care about IE6
  808. users.</p>
  809. <h3 id="migrate-variablewidth">Dealing with variable width in functions</h3>
  810. <p>When people claim that PHP6 will solve all our Unicode problems, they're
  811. misinformed. It will not fix any of the aforementioned troubles. It will,
  812. however, fix the problem we are about to discuss: processing UTF-8 text
  813. in PHP.</p>
  814. <p>PHP (as of PHP5) is blithely unaware of the existence of UTF-8 (with a few
  815. notable exceptions). Sometimes, this will cause problems, other times,
  816. this won't. So far, we've avoided discussing the architecture of
  817. UTF-8, so, we must first ask, what is UTF-8? Yes, it supports Unicode,
  818. and yes, it is variable width. Other traits:</p>
  819. <ul>
  820. <li>Every character's byte sequence is unique and will never be found
  821. inside the byte sequence of another character,</li>
  822. <li>UTF-8 may use up to four bytes to encode a character,</li>
  823. <li>UTF-8 text must be checked for well-formedness,</li>
  824. <li>Pure ASCII is also valid UTF-8, and</li>
  825. <li>Binary sorting will sort UTF-8 in the same order as Unicode.</li>
  826. </ul>
  827. <p>Each of these traits affect different domains of text processing
  828. in different ways. It is beyond the scope of this document to explain
  829. what precisely these implications are. PHPWact provides
  830. a very good <a href="http://www.phpwact.org/php/i18n/utf-8">reference document</a>
  831. on what to expect from each function, although coverage is spotty in
  832. some areas. Their more general notes on
  833. <a href="http://www.phpwact.org/php/i18n/charsets">character sets</a>
  834. are also worth looking at for information on UTF-8. Some rules of thumb
  835. when dealing with Unicode text:</p>
  836. <ul>
  837. <li>Do not EVER use functions that:<ul>
  838. <li>...convert case (strtolower, strtoupper, ucfirst, ucwords)</li>
  839. <li>...claim to be case-insensitive (str_ireplace, stristr, strcasecmp)</li>
  840. </ul></li>
  841. <li>Think twice before using functions that:<ul>
  842. <li>...count characters (strlen will return bytes, not characters;
  843. str_split and word_wrap may corrupt)</li>
  844. <li>...convert characters to entity references (UTF-8 doesn't need entities)</li>
  845. <li>...do very complex string processing (*printf)</li>
  846. </ul></li>
  847. </ul>
  848. <p>Note: this list applies to UTF-8 encoded text only: if you have
  849. a string that you are 100% sure is ASCII, be my guest and use
  850. <code>strtolower</code> (HTML Purifier uses this function.)</p>
  851. <p>Regardless, always think in bytes, not characters. If you use strpos()
  852. to find the position of a character, it will be in bytes, but this
  853. usually won't matter since substr() also operates with byte indices!</p>
  854. <p>You'll also need to make sure your UTF-8 is well-formed and will
  855. probably need replacements for some of these functions. I recommend
  856. using Harry Fuecks' <a href="http://phputf8.sourceforge.net/">PHP
  857. UTF-8</a> library, rather than use mb_string directly. HTML Purifier
  858. also defines a few useful UTF-8 compatible functions: check out
  859. <code>Encoder.php</code> in the <code>/library/HTMLPurifier/</code>
  860. directory.</p>
  861. <h2 id="externallinks">Further Reading</h2>
  862. <p>Well, that's it. Hopefully this document has served as a very
  863. practical springboard into knowledge of how UTF-8 works. You may have
  864. decided that you don't want to migrate yet: that's fine, just know
  865. what will happen to your output and what bug reports you may receive.</p>
  866. <p>Many other developers have already discussed the subject of Unicode,
  867. UTF-8 and internationalization, and I would like to defer to them for
  868. a more in-depth look into character sets and encodings.</p>
  869. <ul>
  870. <li><a href="http://www.joelonsoftware.com/articles/Unicode.html">
  871. The Absolute Minimum Every Software Developer Absolutely,
  872. Positively Must Know About Unicode and Character Sets
  873. (No Excuses!)</a> by Joel Spolsky, provides a <em>very</em>
  874. good high-level look at Unicode and character sets in general.</li>
  875. <li><a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 on Wikipedia</a>,
  876. provides a lot of useful details into the innards of UTF-8, although
  877. it may be a little off-putting to people who don't know much
  878. about Unicode to begin with.</li>
  879. </ul>
  880. </body>
  881. </html>
  882. <!-- vim: et sw=4 sts=4
  883. -->