Python locale 多語言模組和我遇到的坑
locale遇到的問題
今天工作上遇到一個 locale
相關的問題,關於字串格式化的。不過讓我們先從 locale
說起。
locale 簡介
什麼是locale
locale 這個單詞中文翻譯成地區或者地域,其實這個單詞包含的意義要寬泛很多。locale 是根據計算機使用者所使用的語言,所在國家或者地區,以及當地的文化傳統所定義的一個軟體執行時的語言環境。通常情況下它可以按照涉及使用習慣分為12大類:
- 語言符號及其分類(LC_CTYPE) - 數字(LC_NUMBERIC) - 比較習慣(LC_COLLATE) - 時間顯示格式(LC_TIME) - 貨幣單位(LC_MONETARY) - 資訊主要是提示資訊,錯誤資訊,狀態資訊,標題,標籤,按鈕和選單等(LC_MESSAGES) - 行麼書寫方式(LC_NAME) - 地址書寫方式(LC_ADDRESS) - 電話號碼書寫方式(LC_TELEPHONE) -度量衡表達方式(LC_MEASUREMENT) - 預設紙張尺寸大小(LC_PAPER) - 對locale 自身包含資訊的概述(LC_IDENTIFICATION) - 除此之外還有一個LANGUAGE引數,它與LC_MESSAGES相似
比如像下面的例子裡:
在“簡體中文”環境,執行date 命令,顯示的是:
2016年11月24日 星期四 22時59分26秒 CST
而在英文環境下,執行date 命令,顯示的是
Thu Nov 24 23:05:12 CST 2016
簡單來說, locale
為計算機上提供了國際化和本地化轉化的環境
locale 相關命令
在Unix下可以通過命令 locale
來檢視當前語言環境,我的Mac上的顯示如下:
➜ locale LANG= LC_COLLATE="zh_CN.UTF-8" LC_CTYPE="zh_CN.UTF-8" LC_MESSAGES="zh_CN.UTF-8" LC_MONETARY="zh_CN.UTF-8" LC_NUMERIC="zh_CN.UTF-8" LC_TIME="zh_CN.UTF-8" LC_ALL="zh_CN.UTF-8"
locale值格式類似為: 語言_地區.字符集
可以這樣來檢視系統支援locals值
locale -a
可以用如下的方式來臨時改變shell的locale設定:
➜ test git:(master) ✗ LC_ALL=C
➜ test git:(master) ✗ export LC_ALL
➜ test git:(master) ✗ locale
LANG=
LC_COLLATE="C"
LC_CTYPE="C"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL="C"
設定的時候注意以下幾點:
- LANG,LC*
- LCALL,它是一個巨集,如果該值設定了,則該值會覆蓋所有LC*的設定值。注意,LANG的值不受該巨集影響
- LCALL=C 意思是去除所有本地化的設定
Python locale
python提供了 locale
這個模組,可以用來操作locale相關資料,官方文件參見這裡。
其中主要的結果方法如下
import locale
# 返回當前環境locale categorg相關的設定,category預設為 LC_CTYPE
# LC_CTYPE 決定字元處理函式相關行為,比如 string 函式
locale.getlocale([category])
# 嘗試判斷預設的locale設定,並且以元組的形式返回(language code, encoding)
locale.getdefaultlocale([envvars])
# 修改locale category 的設定為 locale的值, 比如locale.setlocale(locale.LC_ALL, 'C'), C 代表去除所有本地化設定
# 如果第二個引數locale沒有提供,那麼會返回category的設定
locale.setlocale(category[, locale])
# 很多程式會像下面這樣開頭,這樣做會將所有的locale設定成使用者預設的設定(通常是環境變數LANF的值)。
# 但setlocale() 不能在所有系統上保證執行緒安全性,這點要注意
import locale
locale.setlocale(locale.LC_ALL, '')
當在shell裡啟動python repl(互動器)時,預設的環境local設定為’C’, 也就是沒有本地化設定,這時候可以通過 locale.getdefaultlocale()
來檢視shell當前環境的locale設定, 並通過 locale.setlocale(locale.LC_ALL, '')
將python直譯器的locale設定成shell環境的locale,具體事例如下:
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.getlocale()
(None, None)
>>> locale.getdefaultlocale()
('zh_CN', 'UTF-8')
>>> locale.setlocale(locale.LC_ALL, '')
'zh_CN.UTF-8'
>>> locale.getdefaultlocale()
('zh_CN', 'UTF-8')
>>> locale.getlocale()
('zh_CN', 'UTF-8')
我踩的坑
strptime 和 strftime
正所謂當局者迷,今天當我遇到同樣的程式碼,不同的環境(shell執行和pycharm執行)居然有不同的執行結果時,我百思不得其姐(嘿嘿)。
程式碼片段是關於 strptime
的:
import time
time.strptime('Thu, 24 Nov 2016 07:01:59 GMT', '%a, %d %b %Y %H:%M:%S GMT')
其實呢,strptime或者strftime格式化引數裡有一些是跟locale相關的,比如這裡的 %a %b
等,所以在不對的 locale
環境下,格式化出現了錯誤。
可以參考下面的示例:
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> time.strptime('Thu, 24 Nov 2016 07:01:59 GMT', '%a, %d %b %Y %H:%M:%S GMT')
time.struct_time(tm_year=2016, tm_mon=11, tm_mday=24, tm_hour=7, tm_min=1, tm_sec=59, tm_wday=3, tm_yday=329, tm_isdst=-1)
>>> import locale
>>> locale.setlocale(locale.LC_ALL, '')
'zh_CN.UTF-8'
>>> time.strptime('Thu, 24 Nov 2016 07:01:59 GMT', '%a, %d %b %Y %H:%M:%S GMT')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_strptime.py", line 467, in _strptime_time
return _strptime(data_string, format)[0]
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_strptime.py", line 325, in _strptime
(data_string, format))
ValueError: time data 'Thu, 24 Nov 2016 07:01:59 GMT' does not match format '%a, %d %b %Y %H:%M:%S GMT'
>>> time.strptime('2016 07:01:59', '%Y %H:%M:%S')
time.struct_time(tm_year=2016, tm_mon=1, tm_mday=1, tm_hour=7, tm_min=1, tm_sec=59, tm_wday=4, tm_yday=1, tm_isdst=-1)
>>>
阿里雲oss sdk 使用遇到的問題
用阿里雲oss-python-SDK上傳檔案時,當我本地locale設定成 zh_CN.UTF-8
時,就回一直出問題,原因就在於上述的 strptime
, 阿里雲sdk程式碼片段如下:
def to_unixtime(time_string, format_string):
with _STRPTIME_LOCK:
return int(calendar.timegm(time.strptime(time_string, format_string)))
然而從oss伺服器上獲得的timestring是這樣的: Thu, 24 Nov 2016 07:01:59 GMT
, 所以在我的環境裡做格式化就會出錯,所以我對程式碼做了如下修改:
def to_unixtime(time_string, format_string):
with _STRPTIME_LOCK:
time_locale = locale.setlocale(locale.LC_TIME)
if time_locale.find('en') != 0 and time_locale != 'C':
locale.setlocale(locale.LC_TIME, 'en_US')
unixtime = int(calendar.timegm(time.strptime(time_string, format_string)))
locale.setlocale(locale.LC_TIME, time_locale)
else:
unixtime = int(calendar.timegm(time.strptime(time_string, format_string)))
return unixtime
在呼叫 strptime
這個方法前增加了相容,先檢查locale,如果不是英文型別並且不是預設的”C”型別時,將 LC_TIME
切換成英文,執行完 strptime 後再還原回來。