--- smartypants.php.bak	2009-08-09 20:48:09.429625000 +0200
+++ smartypants.php	2009-08-09 20:43:00.539000000 +0200
@@ -23,7 +23,7 @@
 // A global variable to keep track of our current SmartyPants 
 // configuration setting.
 global $_typogrify_smartypants_attr;
-$_typogrify_smartypants_attr = "1";  # Change this to configure.
+$_typogrify_smartypants_attr = "1Q";  # Change this to configure.
 						  #  1 =>  "--" for em-dashes; no en-dash support
 						  #  2 =>  "---" for em-dashes; "--" for en-dashes
 						  #  3 =>  "--" for em-dashes; "---" for en-dashes
@@ -56,8 +56,8 @@
 	# 3 : set all, using inverted old school en and em- dash shortcuts
 	# 
 	# q : quotes
-	# b : backtick quotes (``double'' only)
-	# B : backtick quotes (``double'' and `single')
+	# b : backtick quotes (``double'' and ,,double`` only)
+	# B : backtick quotes (``double'', ,,double``, ,single` and `single')
 	# d : dashes
 	# D : old school dashes
 	# i : inverted old school dashes
@@ -70,21 +70,21 @@
 	}
 	else if ($attr == "1") {
 		# Do everything, turn all options on.
-		$do_quotes    = 1;
+		$do_quotes    = 2;
 		$do_backticks = 1;
 		$do_dashes    = 1;
 		$do_ellipses  = 1;
 	}
 	else if ($attr == "2") {
 		# Do everything, turn all options on, use old school dash shorthand.
-		$do_quotes    = 1;
+		$do_quotes    = 2;
 		$do_backticks = 1;
 		$do_dashes    = 2;
 		$do_ellipses  = 1;
 	}
 	else if ($attr == "3") {
 		# Do everything, turn all options on, use inverted old school dash shorthand.
-		$do_quotes    = 1;
+		$do_quotes    = 2;
 		$do_backticks = 1;
 		$do_dashes    = 3;
 		$do_ellipses  = 1;
@@ -97,7 +97,8 @@
 		$chars = preg_split('//', $attr);
 		foreach ($chars as $c){
 			if      ($c == "q") { $do_quotes    = 1; }
-			else if ($c == "b") { $do_backticks = 1; }
+		 	else if ($c == "Q") { $do_quotes    = 2; }
+		 	else if ($c == "b") { $do_backticks = 1; }
 			else if ($c == "B") { $do_backticks = 2; }
 			else if ($c == "d") { $do_dashes    = 1; }
 			else if ($c == "D") { $do_dashes    = 2; }
@@ -151,8 +152,7 @@
 					$t = EducateBackticks($t);
 					if ($do_backticks == 2) $t = EducateSingleBackticks($t);
 				}
-
-				if ($do_quotes) {
+				if ($do_quotes == 1) {
 					if ($t == "'") {
 						# Special case: single-character ' token
 						if (preg_match('/\S/', $prev_token_last_char)) {
@@ -175,6 +175,29 @@
 						# Normal case:
 						$t = EducateQuotes($t);
 					}
+				} else if ($do_quotes == 2) {
+					if ($t == "'") {
+						# Special case: single-character ' token
+						if (preg_match('/\S/', $prev_token_last_char)) {
+							$t = "&#8216;";
+						}
+						else {
+							$t = "&#8218;";
+						}
+					}
+					else if ($t == '"') {
+						# Special case: single-character " token
+						if (preg_match('/\S/', $prev_token_last_char)) {
+							$t = "&#8220;";
+						}
+						else {
+							$t = "&#8222;";
+						}
+					}
+					else {
+						# Normal case:
+						if ($ctx['langcode'] == 'de') {
+							$t = EducateGermanQuotes($t);
+						}
+						else if ($ctx['langcode'] == 'nl') {
+							$t = EducateDutchQuotes($t);
+						}
+						else {
+							$t = EducateQuotes($t);
+						}
+					}
 				}
 
 				if ($do_stupefy) $t = StupefyEntities($t);
@@ -457,6 +480,93 @@
 }
 
 
+function EducateGermanQuotes($_) {
+#
+#   Parameter:  String.
+#
+#   Returns:    The string, with "educated" curly quote HTML entities.
+#
+#   Example input:  "Isn't this fun?"
+#   Example output: &#8222;Isn&#8217;t this fun?&#8220;
+#
+	# Make our own "punctuation" character class, because the POSIX-style
+	# [:PUNCT:] is only available in Perl 5.6 or later:
+	$punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
+
+	# Special case if the very first character is a quote
+	# followed by punctuation at a non-word-break. Close the quotes by brute force:
+	$_ = preg_replace(
+		array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
+		array('&#8217;',                 '&#8220;'), $_);
+
+	# Special case for double sets of quotes, e.g.:
+	#   <p>He said, "'Quoted' words in a larger quote."</p>
+	$_ = preg_replace(
+		array("/\"'(?=\w)/",    "/'\"(?=\w)/"),
+		array('&#8222;&#8218;', '&#8218;&#8222;'), $_);
+
+	# Special case for decade abbreviations (the '80s):
+	$_ = preg_replace("/'(?=\\d{2}s)/", '', $_);
+
+	$close_class = '[^\ \t\r\n\[\{\(\-]';
+	$dec_dashes = '&\#8211;|&\#8212;';
+
+	# Get most opening single quotes:
+	$_ = preg_replace("{
+		(
+			\\s          |   # a whitespace char, or
+			&nbsp;      |   # a non-breaking space entity, or
+			--          |   # dashes, or
+			&[mn]dash;  |   # named dash entities
+			$dec_dashes |   # or decimal entities
+			&\\#x201[34];    # or hex
+		)
+		'                   # the quote
+		(?=\\w)              # followed by a word character
+		}x", '\1&#8218;', $_);
+	# Single closing quotes:
+	$_ = preg_replace("{
+		($close_class)?
+		'
+		(?(1)|          # If $1 captured, then do nothing;
+		  (?=\\s | s\\b)  # otherwise, positive lookahead for a whitespace
+		)               # char or an 's' at a word ending position. This
+						# is a special case to handle something like:
+						# \"<i>Custer</i>'s Last Stand.\"
+		}xi", '\1&#8216;', $_);
+
+	# Any remaining single quotes should be opening ones:
+	$_ = str_replace("'", '&#8218;', $_);
+
+
+	# Get most opening double quotes:
+	$_ = preg_replace("{
+		(
+			\\s          |   # a whitespace char, or
+			&nbsp;      |   # a non-breaking space entity, or
+			--          |   # dashes, or
+			&[mn]dash;  |   # named dash entities
+			$dec_dashes |   # or decimal entities
+			&\\#x201[34];    # or hex
+		)
+		\"                   # the quote
+		(?=\\w)              # followed by a word character
+		}x", '\1&#8222;', $_);
+
+	# Double closing quotes:
+	$_ = preg_replace("{
+		($close_class)?
+		\"
+		(?(1)|(?=\\s))   # If $1 captured, then do nothing;
+						   # if not, then make sure the next char is whitespace.
+		}x", '\1&#8220;', $_);
+
+	# Any remaining quotes should be opening ones.
+	$_ = str_replace('"', '&#8222;', $_);
+
+	return $_;
+}
+
+function EducateDutchQuotes($_) {
+#
+#   Parameter:  String.
+#
+#   Returns:    The string, with "educated" curly quote HTML entities.
+#
+#   Example input:  "Isn't this fun?"
+#   Example output: &#8222;Isn&#8217;t this fun?&#8221;
+#
+	# Make our own "punctuation" character class, because the POSIX-style
+	# [:PUNCT:] is only available in Perl 5.6 or later:
+	$punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
+
+	# Special case if the very first character is a quote
+	# followed by punctuation at a non-word-break. Close the quotes by brute force:
+	$_ = preg_replace(
+		array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
+		array('&#8218;',                 '&#8222;'), $_);
+
+	# Special case for double sets of quotes, e.g.:
+	#   <p>He said, "'Quoted' words in a larger quote."</p>
+	$_ = preg_replace(
+		array("/\"'(?=\w)/",    "/'\"(?=\w)/"),
+		array('&#8222;&#8218;', '&#8218;&#8222;'), $_);
+
+	# Special case for decade abbreviations (the '80s):
+	$_ = preg_replace("/'(?=\\d{2}s)/", '&#8217;', $_);
+
+	$close_class = '[^\ \t\r\n\[\{\(\-]';
+	$dec_dashes = '&\#8211;|&\#8212;';
+
+	# Get most opening single quotes:
+	$_ = preg_replace("{
+		(
+			\\s          |   # a whitespace char, or
+			&nbsp;      |   # a non-breaking space entity, or
+			--          |   # dashes, or
+			&[mn]dash;  |   # named dash entities
+			$dec_dashes |   # or decimal entities
+			&\\#x201[34];    # or hex
+		)
+		'                   # the quote
+		(?=\\w)              # followed by a word character
+		}x", '\1&#8218;', $_);
+	# Single closing quotes:
+	$_ = preg_replace("{
+		($close_class)?
+		'
+		(?(1)|          # If $1 captured, then do nothing;
+		  (?=\\s | s\\b)  # otherwise, positive lookahead for a whitespace
+		)               # char or an 's' at a word ending position. This
+						# is a special case to handle something like:
+						# \"<i>Custer</i>'s Last Stand.\"
+		}xi", '\1&#8217;', $_);
+
+	# Any remaining single quotes should be opening ones:
+	$_ = str_replace("'", '&#8218;', $_);
+
+
+	# Get most opening double quotes:
+	$_ = preg_replace("{
+		(
+			\\s          |   # a whitespace char, or
+			&nbsp;      |   # a non-breaking space entity, or
+			--          |   # dashes, or
+			&[mn]dash;  |   # named dash entities
+			$dec_dashes |   # or decimal entities
+			&\\#x201[34];    # or hex
+		)
+		\"                   # the quote
+		(?=\\w)              # followed by a word character
+		}x", '\1&#8222;', $_);
+
+	# Double closing quotes:
+	$_ = preg_replace("{
+		($close_class)?
+		\"
+		(?(1)|(?=\\s))   # If $1 captured, then do nothing;
+						   # if not, then make sure the next char is whitespace.
+		}x", '\1&#8221;', $_);
+
+	# Any remaining quotes should be opening ones:
+	$_ = str_replace('"', '&#8222;', $_);
+
+	return $_;
+}
+
 function EducateBackticks($_) {
 #
 #   Parameter:  String.
@@ -467,8 +577,8 @@
 #   Example output: &#8220;Isn't this fun?&#8221;
 #
 
-	$_ = str_replace(array("``",       "''",),
-					 array('&#8220;', '&#8221;'), $_);
+	$_ = str_replace(array("``",       "''",	",,"),
+					 array('&#8220;', '&#8221;', '&#8222;'), $_);
 	return $_;
 }
 
@@ -483,8 +593,8 @@
 #   Example output: &#8216;Isn&#8217;t this fun?&#8217;
 #
 
-	$_ = str_replace(array("`",       "'",),
-					 array('&#8216;', '&#8217;'), $_);
+	$_ = str_replace(array("`",       "'",	","),
+					 array('&#8216;', '&#8217;', '&#8218;'), $_);
 	return $_;
 }
 
@@ -572,10 +682,10 @@
 					 array('-',       '--'), $_);
 
 	# single quote         open       close
-	$_ = str_replace(array('&#8216;', '&#8217;'), "'", $_);
+	$_ = str_replace(array('&#8216;', '&#8217;','&#8218;'), "'", $_);
 
 	# double quote         open       close
-	$_ = str_replace(array('&#8220;', '&#8221;'), '"', $_);
+	$_ = str_replace(array('&#8220;', '&#8221;','&#8222;'), '"', $_);
 
 	$_ = str_replace('&#8230;', '...', $_); # ellipsis
 
@@ -598,10 +708,11 @@
 #               \.      &#46;
 #               \-      &#45;
 #               \`      &#96;
+#               \,      &#x2c;
 #
 	$_ = str_replace(
-		array('\\\\',  '\"',    "\'",    '\.',    '\-',    '\`'),
-		array('&#92;', '&#34;', '&#39;', '&#46;', '&#45;', '&#96;'), $_);
+		array('\\\\',  '\"',    "\'",    '\.',    '\-',    '\`',	'\,',),
+		array('&#92;', '&#34;', '&#39;', '&#46;', '&#45;', '&#96;', '&#x2c;'), $_);
 
 	return $_;
 }
