+// Returns the @p name with all letters in lower case, but the first one.
+function mb_ucfirst($name)
+{
+ return mb_strtoupper(mb_substr($name, 0, 1)) . mb_substr($name, 1);
+}
+
+// Capitalizes the @p name using French typographic rules. Returns
+// false when capitalization rule is not known for the name format.
+function capitalize_name($name)
+{
+ // Some suffixes should not be captitalized either, eg 's' in Bennett's.
+ static $suffixes = array('h', 's', 't');
+
+ // Extracts the first token of the name.
+ if (!preg_match('/^(\pL+)(([\' -])(.*))?$/ui', $name, $m)) {
+ return false;
+ }
+
+ $token = mb_strtolower($m[1]);
+ $separator = (isset($m[3]) ? $m[3] : false);
+ $tail = (isset($m[4]) ? $m[4] : false);
+
+ // Special case for "Malloc'h".
+ if ($separator == "'" && in_array(strtolower($tail[0]), $suffixes) &&
+ (strlen($tail) == 1 || $tail[1] == ' ')) {
+ $token .= "'" . strtolower($tail[0]);
+ $separator = (strlen($tail) == 1 ? false : $tail[1]);
+ $tail = (strlen($tail) > 2 ? substr($tail, 2) : false);
+ }
+
+ // Capitalizes the first token.
+ if (!in_array($token, $particles)) {
+ $token = mb_ucfirst($token);
+ }
+
+ // Capitalizes the tail of the name.
+ if ($tail) {
+ if (($tail = capitalize_name($tail))) {
+ return $token . $separator . $tail;
+ }
+ return false;
+ }
+
+ return $token . $separator;
+}
+