Raw codepoint test backend. Tweaks to constructor arguments. Try to handle the case...
[python-collate.git] / collate / _locale.py
diff --git a/collate/_locale.py b/collate/_locale.py
new file mode 100644 (file)
index 0000000..bebbc95
--- /dev/null
@@ -0,0 +1,123 @@
+"""Locale utility routines."""
+
+import sys
+
+try:
+    import locale
+except ImportError:
+    locale = None
+
+try:
+    import codecs
+except ImportError:
+    codecs = None
+
+__all__ = ["localelist"]
+
+def localelist(*locales):
+    """Normalize and return a list of locales, with appended defaults.
+
+    e.g. on a system with en_US as the default locale,
+
+    localelist('en_GB.utf8', 'de_DE') =>
+        ['en_GB', 'en', 'de_DE', 'de', 'en_US', 'C']
+
+    """
+
+    locales = list(locales)
+
+    if locale is not None:
+        # Set the locale if it hasn't already been set, but don't fail
+        # if we can't set it for some reason.
+        if locale.getlocale(locale.LC_COLLATE)[0] is None:
+            try:
+                locale.setlocale(locale.LC_COLLATE, '')
+            except locale.Error:
+                pass
+
+        # Throw in the user's specified collation locale, the current locale,
+        # the default locale, and POSIX, for free.
+        locales.append(locale.getlocale(locale.LC_COLLATE)[0])
+        locales.append(locale.getlocale()[0])
+        locales.append(locale.getdefaultlocale()[0])
+    locales.append("C")
+
+    # Don't put the same locale in the return list more than twice.
+    added = set()
+    retlist = []
+
+    for code in locales:
+        if not code:
+            continue
+        if locale is not None:
+            code = locale.normalize(code)
+        # Strip off encoding if present.
+        code = code.split(".")[0]
+        if code.lower() not in added:
+            retlist.append(code)
+            added.add(code.lower())
+        # Strip off territory if present.
+        code = code.split("_")[0]
+        if code.lower() not in added:
+            retlist.append(code)
+            added.add(code.lower())
+
+    return retlist
+
+def encoding(preferred=None):
+    """Try to find an optimal encoding.
+
+    Arguments:
+    preferred - use this encoding if possible
+
+    Otherwise, the locale encoding or the Python system encoding are
+    used.
+    """
+    # can't use any codecs, use the system one (ascii).
+    if codecs is None:
+        return sys.getdefaultencoding()
+
+    # if preferred is a valid codec, use it.
+    if preferred is not None:
+        try:
+            return codecs.lookup(preferred).name
+        except (LookupError, AttributeError):
+            pass
+
+    # preferred is bad and can't get it from locale.
+    if locale is None:
+        return sys.getdefaultencoding()
+
+    # try to get it from the locale, if not there, set it and try again.
+    fromlocale = locale.getlocale(locale.LC_COLLATE)[1]
+    if fromlocale is not None:
+        return fromlocale
+    try:
+        locale.setlocale(locale.LC_COLLATE, '')[1]
+    except locale.Error:
+        pass
+    else:
+        fromlocale = locale.getlocale(locale.LC_COLLATE)
+        if fromlocale is not None:
+            return fromlocale
+
+    # okay, LC_COLLATE isn't set, maybe the generic locale is.
+    fromlocale = locale.getlocale()[1]
+    if fromlocale is not None:
+        return fromlocale
+
+    # but we won't reset the generic locale if it isn't, that'd be
+    # rude.
+
+    # if the locale can't even give us a simple encoding, go back
+    # to the system one, and give up.
+    return locale.getpreferredencoding() or sys.getdefaultencoding()
+
+def getpair(locale_, encoding_):
+    if "." in locale_:
+        if encoding_ is None:
+            locale_, encoding_ = locale_.rsplit(".", 1)
+        else:
+            locale_ = locale_.rsplit(".")[0]
+    return locale_, encoding(encoding_)
+