Hi,
Based on the work by Javier Kohen, I've made a patch for Quodlibet to
improve the titlecase function. The diff is against current trunk.
Improvements:
- Better handling of Unicode characters
- Better handling of various quotes and punctuation
- Capitalization of ALL-CAPS strings.
- Extensive test cases
Most of the work was done by Javier Kohen in response to Debian bug
report #449054
best regards,
Erich Schubert
--
erich@(vitavonni.de|debian.org) -- GPG Key ID: 4B3A135C (o_
Which is worse: ignorance or apathy? Who knows? Who cares? //\
Alles verändert sich, sobald man sich selber verändert. V_/_
Index: quodlibet/util/titlecase.py
===================================================================
--- quodlibet/util/titlecase.py (revision 0)
+++ quodlibet/util/titlecase.py (revision 0)
@@ -0,0 +1,43 @@
+# Copyright 2007 Javier Kohen
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# $Id$
+
+import unicodedata
+
+def iswbound(char):
+ """Returns whether the given character is a word boundary."""
+ category = unicodedata.category(char)
+ # If it's a space separator or punctuation
+ return 'Zs' == category or 'Sk' == category or 'P' == category[0]
+
+def utitle(string):
+ """Title-case a string using a less destructive method than str.title."""
+ new_string = string[0].capitalize()
+ cap = False
+ for i in xrange(1, len(string)):
+ s = string[i]
+ # Special case apostrophe in the middle of a word.
+ if u"'" == s and string[i-1].isalpha(): cap = False
+ elif iswbound(s): cap = True
+ elif cap and s.isalpha():
+ cap = False
+ s = s.capitalize()
+ else: cap = False
+ new_string += s
+ return new_string
+
+from types import UnicodeType
+from locale import getpreferredencoding
+
+def title(string):
+ """Title-case a string using a less destructive method than str.title."""
+ if not string: return ""
+ # if the string is all uppercase, lowercase it
+ if string == string.upper(): string = string.lower()
+ if (not isinstance(string, UnicodeType)):
+ string = unicode(string.decode(getpreferredencoding()))
+ return utitle(string)
Property changes on: quodlibet/util/titlecase.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Index: quodlibet/util/__init__.py
===================================================================
--- quodlibet/util/__init__.py (revision 4263)
+++ quodlibet/util/__init__.py (working copy)
@@ -15,6 +15,9 @@
import urlparse
import warnings
+# title function was moved to a separate module
+from quodlibet.util.titlecase import title
+
from quodlibet.const import FSCODING as fscoding, ENCODING
from quodlibet.util.i18n import GlibTranslations
@@ -288,20 +291,6 @@
except UnicodeError:
return (s + " " + _("[Invalid Encoding]")).encode(charset, "replace")
-def title(string):
- """Title-case a string using a less destructive method than str.title."""
- if not string: return ""
- new_string = string[0].capitalize()
- cap = False
- for s in string[1:]:
- if s.isspace(): cap = True
- elif cap and s.isalpha():
- cap = False
- s = s.capitalize()
- else: cap = False
- new_string += s
- return new_string
-
def iscommand(s):
"""True if an executable file 's' exists in the user's path, or is a
fully-qualified existing executable file."""
Index: tests/test_titlecase.py
===================================================================
--- tests/test_titlecase.py (revision 0)
+++ tests/test_titlecase.py (revision 0)
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright 2007 Javier Kohen
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# $Id$
+
+from tests import TestCase, add
+
+from quodlibet.util.titlecase import title
+
+class TitlecaseTests(TestCase):
+ def test_basics(s):
+ s.assertEquals(u"Mama's Boy", title(u"mama's boy"))
+ s.assertEquals(u"The A-Sides", title(u"the a-sides"))
+ s.assertEquals(u"Hello Goodbye", title(u"hello goodbye"))
+ s.assertEquals(u"Hello Goodbye", title(u"HELLO GOODBYE"))
+
+ def test_quirks(s):
+ # This character is not an apostrophe, it's a single quote!
+ s.assertEquals(u"Mama’S Boy", title(u"mama’s boy"))
+ # This is actually an accent character, not an apostrophe either.
+ s.assertEquals(u"Mama`S Boy", title(u"mama`s boy"))
+
+ def test_quotes(s):
+ s.assertEquals(u"Hello Goodbye (A Song)",
+ title(u"hello goodbye (a song)"))
+ s.assertEquals(u"Hello Goodbye 'A Song'",
+ title(u"hello goodbye 'a song'"))
+ s.assertEquals(u'Hello Goodbye "A Song"',
+ title(u'hello goodbye "a song"'))
+ s.assertEquals(u"Hello Goodbye „A Song”",
+ title(u"hello goodbye „a song”"))
+ s.assertEquals(u"Hello Goodbye ‘A Song’",
+ title(u"hello goodbye ‘a song’"))
+ s.assertEquals(u"Hello Goodbye “A Song”",
+ title(u"hello goodbye “a song”"))
+ s.assertEquals(u"Hello Goodbye »A Song«",
+ title(u"hello goodbye »a song«"))
+ s.assertEquals(u"Hello Goodbye «A Song»",
+ title(u"hello goodbye «a song»"))
+
+ def test_unicode(s):
+ s.assertEquals(u"Fooäbar",
+ title(u"fooäbar"))
+ s.assertEquals(u"Los Años Felices",
+ title(u"los años felices"))
+ s.assertEquals(u"Ñandú",
+ title(u"ñandú"))
+ s.assertEquals(u"Österreich",
+ title(u"österreich"))
+ # Not a real word - there is none with this character at the beginning
+ # but still Python doesn't capitalize the es-zed properly.
+ # s.assertEquals(u"SSbahn", title(u"ßbahn"))
+
+add(TitlecaseTests)
Property changes on: tests/test_titlecase.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native