summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2017-05-08 22:10:39 +0000
committermilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2017-05-08 22:10:39 +0000
commit0d4b95a23170a0cc5a5dce6bb9fef47972670306 (patch)
tree6ec6a988b75a6429b1117728fa8adaaaff2971bb
parentfc7ea111abc4b8af98efd22bf8702274b083bdf4 (diff)
downloaddocutils-0d4b95a23170a0cc5a5dce6bb9fef47972670306.tar.gz
Add "smartquotes-locales" setting.
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@8068 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
-rw-r--r--docutils/docs/user/config.txt32
-rw-r--r--docutils/docs/user/smartquotes.txt306
-rw-r--r--docutils/docutils/frontend.py42
-rw-r--r--docutils/docutils/parsers/rst/__init__.py8
-rw-r--r--docutils/docutils/transforms/universal.py3
-rw-r--r--docutils/docutils/utils/smartquotes.py2
-rwxr-xr-xdocutils/test/test_settings.py61
-rw-r--r--docutils/test/test_transforms/test_smartquotes.py22
8 files changed, 322 insertions, 154 deletions
diff --git a/docutils/docs/user/config.txt b/docutils/docs/user/config.txt
index 2a77b86eb..eaf649fad 100644
--- a/docutils/docs/user/config.txt
+++ b/docutils/docs/user/config.txt
@@ -681,9 +681,11 @@ smart_quotes
Activate the SmartQuotes_ transform to
change straight quotation marks to typographic form. `Quote characters`_
are selected according to the language of the current block element (see
-language_code_). Also changes consequtive runs of hyphen-minus and full
-stops (``---``, ``--``, ``...``) to em-dash, en-dash and ellipsis Unicode
-characters respectively.
+language_code_, smartquotes_locales_, and the `pre-defined quote sets`__).
+
+Also changes consecutive runs of hyphen-minus and full stops (``---``,
+``--``, ``...``) to em-dash, en-dash and ellipsis Unicode characters
+respectively.
Supported values:
@@ -698,9 +700,33 @@ Default: "no". Option: ``--smart-quotes``.
New in Docutils 0.10.
.. _SmartQuotes: smartquotes.html
+__ smartquotes.html#localisation
.. _quote characters:
http://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks
+
+smartquotes_locales
+~~~~~~~~~~~~~~~~~~~
+
+Typographical quotes used by the SmartQuotes_ transform.
+
+A comma-separated list_ with language tag and a set of four quotes (primary
+open/close, secondary open/close)smartquotes_locales. (If more than one
+character shall be used for a quote (e.g. padding in French quotes), a
+colon-separated list may be used.)
+
+Example:
+ Ensure a correct leading apostrophe in ``'s Gravenhage`` in Dutch (at the
+ cost of incorrect opening single quotes) and set French quotes to double
+ and single guillemets with inner padding::
+
+ smartquote-locales: nl: „”’’,
+ fr: « : »:‹ : ›
+
+Default: None. Option: ``--smartquotes-locales``.
+
+New in Docutils 0.14.
+
syntax_highlight
~~~~~~~~~~~~~~~~
diff --git a/docutils/docs/user/smartquotes.txt b/docutils/docs/user/smartquotes.txt
index 8bc045afe..47e0f9ed8 100644
--- a/docutils/docs/user/smartquotes.txt
+++ b/docutils/docs/user/smartquotes.txt
@@ -17,7 +17,7 @@ Smart Quotes for Docutils
Description
===========
-The `smart_quotes configuration setting`_ triggers the SmartQuotes
+The `"smart_quotes" configuration setting`_ triggers the SmartQuotes
transformation on Text nodes that includes the following steps:
- Straight quotes (``"`` and ``'``) into "curly" quote characters
@@ -30,7 +30,7 @@ generates documents with typographical quotes, dashes, and ellipses.
Advantages:
-* typing speed (especiall when blind-typing),
+* typing speed (especially when blind-typing),
* the possibility to change the quoting style of the
complete document with just one configuration option, and
* restriction to 7-bit characters in the source.
@@ -43,7 +43,8 @@ However, there are `algorithmic shortcomings`_ for 2 reasons:
So, please consider also
`Why You Might Not Want to Use "Smart" Quotes in Your Documents`_.
-.. _smart_quotes configuration setting: config.html#smart-quotes
+.. _"smart_quotes" configuration setting:
+.. _"smart_quotes" setting: config.html#smart-quotes
Escaping
@@ -77,286 +78,301 @@ Localisation
Quotation marks have a `variety of forms`__ in different languages and
media.
-`Smartquotes` selects quotation marks depending on the language of the current
-block element and the value of the `smart_quotes configuration setting`_.
-
__ https://en.wikipedia.org/wiki/Quotation_mark#Summary_table
-Docutils' `smartquotes` support the following languages:
-
+`SmartQuotes` inserts quotation marks depending on the language of the
+current block element and the value of the `"smart_quotes" setting`_.\
+[#x-altquot]_
+There is built-in support for the following languages:\ [#smartquotes-locales]_
-.. class:: language-af
+:af: .. class:: language-af
-"'Afrikaans' quotes"
+ "'Afrikaans' quotes"
-.. class:: language-af-x-altquot
+:af-x-altquot: .. class:: language-af-x-altquot
-"'Afrikaans' alternative quotes"
+ "'Afrikaans' alternative quotes"
-.. class:: language-ca
+:ca: .. class:: language-ca
-"'Catalan' quotes"
+ "'Catalan' quotes"
-.. class:: language-ca-x-altquot
+:ca-x-altquot: .. class:: language-ca-x-altquot
-"'Catalan' alternative quotes"
+ "'Catalan' alternative quotes"
-.. class:: language-cs
+:cs: .. class:: language-cs
-"'Czech' quotes"
+ "'Czech' quotes"
-.. class:: language-cs-x-altquot
+:cs-x-altquot: .. class:: language-cs-x-altquot
-"'Czech' alternative quotes"
+ "'Czech' alternative quotes"
-.. class:: language-da
+:da: .. class:: language-da
-"'Danish' quotes"
+ "'Danish' quotes"
-.. class:: language-da-x-altquot
+:da-x-altquot: .. class:: language-da-x-altquot
-"'Danish' alternative quotes"
+ "'Danish' alternative quotes"
-.. class:: language-de
+:de: .. class:: language-de
-"'German' quotes"
+ "'German' quotes"
-.. class:: language-de-x-altquot
+:de-x-altquot: .. class:: language-de-x-altquot
-"'German' alternative quotes"
+ "'German' alternative quotes"
-.. class:: language-de-ch
+:de-ch: .. class:: language-de-ch
-"'Swiss-German' quotes"
+ "'Swiss-German' quotes"
-.. class:: language-el
+:el: .. class:: language-el
-"'Greek' quotes"
+ "'Greek' quotes"
-.. class:: language-en
+:en: .. class:: language-en
-"'English' quotes"
+ "'English' quotes"
-.. class:: language-en-uk-x-altquot
+:en-uk-x-altquot: .. class:: language-en-uk-x-altquot
-"'British' alternative quotes"
-(swaps single and double quotes: ``"`` → ‘ and ``'`` → “)
+ "'British' alternative quotes"
+ (swaps single and double quotes: ``"`` → ‘ and ``'`` → “)
-.. class:: language-eo
+:eo: .. class:: language-eo
-"'Esperanto' quotes"
+ "'Esperanto' quotes"
-.. class:: language-es
+:es: .. class:: language-es
-"'Spanish' quotes"
+ "'Spanish' quotes"
-.. class:: language-es-x-altquot
+:es-x-altquot: .. class:: language-es-x-altquot
-"'Spanish' alternative quotes"
+ "'Spanish' alternative quotes"
-.. class:: language-et
+:et: .. class:: language-et
-"'Estonian' quotes" (no secondary quote listed in Wikipedia)
+ "'Estonian' quotes" (no secondary quote listed in Wikipedia)
-.. class:: language-et-x-altquot
+:et-x-altquot: .. class:: language-et-x-altquot
-"'Estonian' alternative quotes"
+ "'Estonian' alternative quotes"
-.. class:: language-eu
+:eu: .. class:: language-eu
-"'Basque' quotes"
+ "'Basque' quotes"
-.. class:: language-fi
+:fi: .. class:: language-fi
-"'Finnish' quotes"
+ "'Finnish' quotes"
-.. class:: language-fi-x-altquot
+:fi-x-altquot: .. class:: language-fi-x-altquot
-"'Finnish' alternative quotes"
+ "'Finnish' alternative quotes"
-.. class:: language-fr
+:fr: .. class:: language-fr
-"'French' quotes"
+ "'French' quotes"
-.. class:: language-fr-x-altquot
+:fr-x-altquot: .. class:: language-fr-x-altquot
-"'French' alternative quotes"
+ "'French' alternative quotes"
-.. class:: language-fr-ch
+:fr-ch: .. class:: language-fr-ch
-"'Swiss-French' quotes"
+ "'Swiss-French' quotes"
-.. class:: language-fr-ch-x-altquot
+:fr-ch-x-altquot: .. class:: language-fr-ch-x-altquot
-"'Swiss-French' alternative quotes" (narrow no-break space, see
-http://typoguide.ch/)
+ "'Swiss-French' alternative quotes" (narrow no-break space, see
+ http://typoguide.ch/)
-.. class:: language-gl
+:gl: .. class:: language-gl
-"'Galician' quotes"
+ "'Galician' quotes"
-.. class:: language-he
+:he: .. class:: language-he
-"'Hebrew' quotes"
+ "'Hebrew' quotes"
-.. class:: language-he-x-altquot
+:he-x-altquot: .. class:: language-he-x-altquot
-"'Hebrew' alternative quotes"
+ "'Hebrew' alternative quotes"
-.. class:: language-hr
+:hr: .. class:: language-hr
-"'Croatian' quotes"
+ "'Croatian' quotes"
-.. class:: language-hr-x-altquot
+:hr-x-altquot: .. class:: language-hr-x-altquot
-"'Croatian' alternative quotes"
+ "'Croatian' alternative quotes"
-.. class:: language-hsb
+:hsb: .. class:: language-hsb
-"'Upper Sorbian' quotes"
+ "'Upper Sorbian' quotes"
-.. class:: language-hsb-x-altquot
+:hsb-x-altquot: .. class:: language-hsb-x-altquot
-"'Upper Sorbian' alternative quotes"
+ "'Upper Sorbian' alternative quotes"
-.. class:: language-hu
+:hu: .. class:: language-hu
-"'Hungarian' quotes"
+ "'Hungarian' quotes"
-.. class:: language-is
+:is: .. class:: language-is
-"'Icelandic' quotes"
+ "'Icelandic' quotes"
-.. class:: language-it
+:it: .. class:: language-it
-"'Italian' quotes"
+ "'Italian' quotes"
-.. class:: language-it-ch
+:it-ch: .. class:: language-it-ch
-"'Swiss-Italian' quotes"
+ "'Swiss-Italian' quotes"
-.. class:: language-it-x-altquot
+:it-x-altquot: .. class:: language-it-x-altquot
-"'Italian' alternative quotes"
+ "'Italian' alternative quotes"
-.. class:: language-ja
+:ja: .. class:: language-ja
-"'Japanese' quotes"
+ "'Japanese' quotes"
-.. class:: language-lt
+:lt: .. class:: language-lt
-"'Lithuanian' quotes"
+ "'Lithuanian' quotes"
-.. class:: language-lv
+:lv: .. class:: language-lv
-"'Latvian' quotes"
+ "'Latvian' quotes"
-.. class:: language-nl
+:nl: .. class:: language-nl
-"'Dutch' quotes"
+ "'Dutch' quotes"
-.. class:: language-nl-x-altquot
+:nl-x-altquot: .. class:: language-nl-x-altquot
-"'Dutch' alternative quotes"
+ "'Dutch' alternative quotes"
.. # 'nl-x-altquot2': u'””’’',
-.. class:: language-pl
+:pl: .. class:: language-pl
-"'Polish' quotes"
+ "'Polish' quotes"
-.. class:: language-pl-x-altquot
+:pl-x-altquot: .. class:: language-pl-x-altquot
-"'Polish' alternative quotes"
+ "'Polish' alternative quotes"
-.. class:: language-pt
+:pt: .. class:: language-pt
-"'Portuguese' quotes"
+ "'Portuguese' quotes"
-.. class:: language-pt-br
+:pt-br: .. class:: language-pt-br
-"'Portuguese (Brazil)' quotes"
+ "'Portuguese (Brazil)' quotes"
-.. class:: language-ro
+:ro: .. class:: language-ro
-"'Romanian' quotes"
+ "'Romanian' quotes"
-.. class:: language-ru
+:ru: .. class:: language-ru
-"'Russian' quotes"
+ "'Russian' quotes"
-.. class:: language-sh
+:sh: .. class:: language-sh
-"'Serbo-Croatian' quotes"
+ "'Serbo-Croatian' quotes"
-.. class:: language-sh-x-altquot
+:sh-x-altquot: .. class:: language-sh-x-altquot
-"'Serbo-Croatian' alternative quotes"
+ "'Serbo-Croatian' alternative quotes"
-.. class:: language-sk
+:sk: .. class:: language-sk
-"'Slovak' quotes"
+ "'Slovak' quotes"
-.. class:: language-sk-x-altquot
+:sk-x-altquot: .. class:: language-sk-x-altquot
-"'Slovak' alternative quotes"
+ "'Slovak' alternative quotes"
-.. class:: language-sl
+:sl: .. class:: language-sl
-"'Slovenian' quotes"
+ "'Slovenian' quotes"
-.. class:: language-sl-x-altquot
+:sl-x-altquot: .. class:: language-sl-x-altquot
-"'Slovenian' alternative quotes"
+ "'Slovenian' alternative quotes"
-.. class:: language-sr
+:sr: .. class:: language-sr
-"'Serbian' quotes"
+ "'Serbian' quotes"
-.. class:: language-sr-x-altquot
+:sr-x-altquot: .. class:: language-sr-x-altquot
-"'Serbian' alternative quotes"
+ "'Serbian' alternative quotes"
-.. class:: language-sv
+:sv: .. class:: language-sv
-"'Swedish' quotes"
+ "'Swedish' quotes"
-.. class:: language-sv-x-altquot
+:sv-x-altquot: .. class:: language-sv-x-altquot
-"'Swedish' alternative quotes"
+ "'Swedish' alternative quotes"
-.. class:: language-tr
+:tr: .. class:: language-tr
-"'Turkish' quotes"
+ "'Turkish' quotes"
-.. class:: language-tr-x-altquot
+:tr-x-altquot: .. class:: language-tr-x-altquot
-"'Turkish' alternative quotes"
+ "'Turkish' alternative quotes"
.. 'tr-x-altquot2': u'“„‘‚', # antiquated?
-.. class:: language-uk
+:uk: .. class:: language-uk
+
+ "'Ukrainian' quotes"
+
+:uk-x-altquot: .. class:: language-uk-x-altquot
+
+ "'Ukrainian' alternative quotes"
+
+:zh-cn: .. class:: language-zh-cn
+
+ "'Chinese (China)' quotes"
-"'Ukrainian' quotes"
+:zh-tw: .. class:: language-zh-tw
-.. class:: language-uk-x-altquot
+ "'Chinese (Taiwan)' quotes"
-"'Ukrainian' alternative quotes"
+Quotes in text blocks in a non-configured language are kept as plain quotes:
-.. class:: language-zh-cn
+:undefined: .. class:: language-undefined-example
-"'Chinese (China)' quotes"
+ "'Undefined' quotes"
-.. class:: language-zh-tw
+.. [#x-altquot] Tags with the non-standard extension ``-x-altquot`` define
+ the quote set used with the `"smart_quotes" setting`_ value ``"alt"``.
-"'Chinese (Taiwan)' quotes"
+.. [#smartquotes-locales] The definitions for language-dependend
+ typographical quotes can be extended or overwritten using the
+ `"smartquotes_locales" setting`_.
-Quotes in text blocks in a non-supported language use the document
-language:
+ The following example ensures a correct leading apostrophe in ``'s
+ Gravenhage`` (at the cost of incorrect leading single quotes) in Dutch
+ and sets French quotes to double and single guillemets with inner
+ spacing::
-.. class:: langugage-undefined-example
+ smartquote-locales: nl: „”’’
+ fr: « : »:‹ : ›
-"'Undefined' quotes"
+.. _"smartquotes_locales" setting: config.html#smartquotes-locales
Caveats
@@ -452,7 +468,7 @@ this plug-in. Brad Choate is a fine hacker indeed.
`Jeremy Hedley`_ and `Charles Wiltgen`_ deserve mention for exemplary beta
testing of the original SmartyPants.
-Internationalization and adaption to Docutils by Günter Milde.
+Internationalisation and adaption to Docutils by Günter Milde.
.. _SmartyPants: http://daringfireball.net/projects/smartypants/
.. _Pyblosxom: http://pyblosxom.bluesock.org/
diff --git a/docutils/docutils/frontend.py b/docutils/docutils/frontend.py
index ef6a1f90b..4036b3ba9 100644
--- a/docutils/docutils/frontend.py
+++ b/docutils/docutils/frontend.py
@@ -40,7 +40,8 @@ from optparse import SUPPRESS_HELP
import docutils
import docutils.utils
import docutils.nodes
-from docutils.utils.error_reporting import locale_encoding, ErrorOutput, ErrorString
+from docutils.utils.error_reporting import (locale_encoding, SafeString,
+ ErrorOutput, ErrorString)
def store_multiple(option, opt, value, parser, *args, **kwargs):
@@ -205,10 +206,45 @@ def validate_strip_class(setting, value, option_parser,
for cls in value:
normalized = docutils.nodes.make_id(cls)
if cls != normalized:
- raise ValueError('invalid class value %r (perhaps %r?)'
+ raise ValueError('Invalid class value %r (perhaps %r?)'
% (cls, normalized))
return value
+def validate_smartquotes_locales(setting, value, option_parser,
+ config_parser=None, config_section=None):
+ """Check/normalize a comma separated list of smart quote definitions.
+
+ Return a list of (language-tag, quotes) string tuples."""
+
+ # value is a comma separated string list:
+ value = validate_comma_separated_list(setting, value, option_parser,
+ config_parser, config_section)
+ # validate list elements
+ lc_quotes = []
+ for item in value:
+ try:
+ lang, quotes = item.split(':', 1)
+ except AttributeError:
+ # this function is called for every option added to `value`
+ # -> ignore if already a tuple:
+ lc_quotes.append(item)
+ continue
+ except ValueError:
+ raise ValueError(u'Invalid value "%s".'
+ ' Format is "<language>:<quotes>".'
+ % item.encode('ascii', 'backslashreplace'))
+ # parse colon separated string list:
+ quotes = quotes.strip()
+ multichar_quotes = quotes.split(':')
+ if len(multichar_quotes) == 4:
+ quotes = multichar_quotes
+ elif len(quotes) != 4:
+ raise ValueError('Invalid value "%s". Please specify 4 quotes\n'
+ ' (primary open/close; secondary open/close).'
+ % item.encode('ascii', 'backslashreplace'))
+ lc_quotes.append((lang,quotes))
+ return lc_quotes
+
def make_paths_absolute(pathdict, keys, base_path=None):
"""
Interpret filesystem path settings relative to the `base_path` given.
@@ -568,7 +604,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
try:
config_settings = self.get_standard_config_settings()
except ValueError, error:
- self.error(error)
+ self.error(SafeString(error))
self.set_defaults_from_dict(config_settings.__dict__)
def populate_from_components(self, components):
diff --git a/docutils/docutils/parsers/rst/__init__.py b/docutils/docutils/parsers/rst/__init__.py
index b72af6b8f..a181277fb 100644
--- a/docutils/docutils/parsers/rst/__init__.py
+++ b/docutils/docutils/parsers/rst/__init__.py
@@ -141,7 +141,13 @@ class Parser(docutils.parsers.Parser):
('Change straight quotation marks to typographic form: '
'one of "yes", "no", "alt[ernative]" (default "no").',
['--smart-quotes'],
- {'default': False, 'validator': frontend.validate_ternary}),
+ {'default': False, 'metavar': '<yes/no/alt>',
+ 'validator': frontend.validate_ternary}),
+ ('Characters to use as "smart quotes" for <language>. ',
+ ['--smartquotes-locales'],
+ {'metavar': '<language:quotes[,language:quotes,...]>',
+ 'action': 'append',
+ 'validator': frontend.validate_smartquotes_locales}),
('Inline markup recognized at word boundaries only '
'(adjacent to punctuation or whitespace). '
'Force character-level inline markup recognition with '
diff --git a/docutils/docutils/transforms/universal.py b/docutils/docutils/transforms/universal.py
index 46b3e3e83..6b1620443 100644
--- a/docutils/docutils/transforms/universal.py
+++ b/docutils/docutils/transforms/universal.py
@@ -249,6 +249,9 @@ class SmartQuotes(Transform):
# print repr(alternative)
document_language = self.document.settings.language_code
+ lc_smartquotes = self.document.settings.smartquotes_locales
+ if lc_smartquotes:
+ smartquotes.smartchars.quotes.update(dict(lc_smartquotes))
# "Educate" quotes in normal text. Handle each block of text
# (TextElement node) as a unit to keep context around inline nodes:
diff --git a/docutils/docutils/utils/smartquotes.py b/docutils/docutils/utils/smartquotes.py
index b3e67508b..8e99ab4ed 100644
--- a/docutils/docutils/utils/smartquotes.py
+++ b/docutils/docutils/utils/smartquotes.py
@@ -592,7 +592,7 @@ def educate_tokens(text_tokens, attr=default_smartypants_attr, language='en'):
text = educateSingleBackticks(text, language)
if do_quotes:
- # Replace plain quotes to prevent converstion to
+ # Replace plain quotes in context to prevent converstion to
# 2-character sequence in French.
context = prev_token_last_char.replace('"',';').replace("'",';')
text = educateQuotes(context+text, language)[1:]
diff --git a/docutils/test/test_settings.py b/docutils/test/test_settings.py
index 397c1de1f..335167436 100755
--- a/docutils/test/test_settings.py
+++ b/docutils/test/test_settings.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
# $Id$
# Author: David Goodger <goodger@python.org>
@@ -176,6 +177,10 @@ class HelperFunctionsTests(unittest.TestCase):
pathdict = {'foo': 'hallo', 'ham': u'h\xE4m', 'spam': u'spam'}
keys = ['foo', 'ham']
+ def setUp(self):
+ self.option_parser = frontend.OptionParser(
+ components=(rst.Parser,), read_config_files=None)
+
def test_make_paths_absolute(self):
pathdict = self.pathdict.copy()
frontend.make_paths_absolute(pathdict, self.keys, base_path='base')
@@ -195,6 +200,33 @@ class HelperFunctionsTests(unittest.TestCase):
# not touched, because key not in keys:
self.assertEqual(pathdict['spam'], u'spam')
+ boolean_settings = (
+ (True, True ),
+ ('1', True ),
+ (u'on', True ),
+ ('yes', True ),
+ (u'true', True ),
+ (u'0', False ),
+ ('off', False ),
+ (u'no', False ),
+ ('false', False ),
+ )
+ def test_validate_boolean(self):
+ for t in self.boolean_settings:
+ self.assertEqual(
+ frontend.validate_boolean(None, t[0], self.option_parser),
+ t[1])
+
+ def test_validate_ternary(self):
+ tests = (
+ ('500V', '500V'),
+ (u'parrot', u'parrot'),
+ )
+ for t in self.boolean_settings + tests:
+ self.assertEqual(
+ frontend.validate_ternary(None, t[0], self.option_parser),
+ t[1])
+
def test_validate_colon_separated_string_list(self):
tests = (
(u'a', ['a',] ),
@@ -209,7 +241,6 @@ class HelperFunctionsTests(unittest.TestCase):
frontend.validate_colon_separated_string_list(None, t[0], None),
t[1])
-
def test_validate_comma_separated_list(self):
tests = (
(u'a', ['a',] ),
@@ -225,5 +256,33 @@ class HelperFunctionsTests(unittest.TestCase):
frontend.validate_comma_separated_list(None, t[0], None),
t[1])
+ def test_validate_url_trailing_slash(self):
+ tests = (
+ ('', './' ),
+ (None, './' ),
+ (u'http://example.org', u'http://example.org/' ),
+ ('http://example.org/', 'http://example.org/' ),
+ )
+ for t in tests:
+ self.assertEqual(
+ frontend.validate_url_trailing_slash(None, t[0], None),
+ t[1])
+
+ def test_validate_smartquotes_locales(self):
+ tests = (
+ ('en:ssvv', [('en', 'ssvv')]),
+ (u'sd:«»°°', [(u'sd', u'«»°°')]),
+ ([('sd', u'«»°°'), u'ds:°°«»'], [('sd', u'«»°°'),
+ ('ds', u'°°«»')]),
+ (u'frs:« : »:((:))', [(u'frs', [u'« ', u' »',
+ u'((', u'))'])]),
+ )
+ for t in tests:
+ self.assertEqual(
+ frontend.validate_smartquotes_locales(None, t[0], None),
+ t[1])
+
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/docutils/test/test_transforms/test_smartquotes.py b/docutils/test/test_transforms/test_smartquotes.py
index a16025213..4b2b6f237 100644
--- a/docutils/test/test_transforms/test_smartquotes.py
+++ b/docutils/test/test_transforms/test_smartquotes.py
@@ -32,12 +32,16 @@ def suite():
s.generateTests(totest_de)
settings['smart_quotes'] = 'alternative'
s.generateTests(totest_de_alt)
+ settings['smart_quotes'] = True
+ settings['smartquotes_locales'] = [('de', u'«»()'), ('nl', u'„”’’')]
+ s.generateTests(totest_locales)
return s
totest = {}
totest_de = {}
totest_de_alt = {}
+totest_locales = {}
totest['transitions'] = ((SmartQuotes,), [
["""\
@@ -299,6 +303,24 @@ u"""\
"""],
])
+totest_locales['transitions'] = ((SmartQuotes,), [
+["""\
+German "smart quotes" and 'secondary smart quotes'.
+
+.. class:: language-nl
+
+Dutch "smart quotes" and 's Gravenhage (leading apostrophe).
+""",
+u"""\
+<document source="test data">
+ <paragraph>
+ German «smart quotes» and (secondary smart quotes).
+ <paragraph classes="language-nl">
+ Dutch „smart quotes” and ’s Gravenhage (leading apostrophe).
+"""],
+])
+
+
if __name__ == '__main__':
import unittest
unittest.main(defaultTest='suite')