Python locale 多语言模块和我遇到的坑

locale遇到的问题<a id="sec-1" name="sec-1"></a>

今天工作上遇到一个 locale 相关的问题,关于字符串格式化的。不过让我们先从 locale 说起。

locale 简介<a id="sec-1-1" name="sec-1-1"></a>

什么是locale<a id="sec-1-1-1" name="sec-1-1-1"></a>

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 相关命令<a id="sec-1-1-2" name="sec-1-1-2"></a>

在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*的默认值,是最低级别的设置,如果LC*没有设置,则使用该值。类似于 LCALL
  • LCALL,它是一个宏,如果该值设置了,则该值会覆盖所有LC*的设置值。注意,LANG的值不受该宏影响
  • LCALL=C 意思是去除所有本地化的设置

Python locale<a id="sec-1-2" name="sec-1-2"></a>

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')

我踩的坑<a id="sec-1-3" name="sec-1-3"></a>

strptime 和 strftime<a id="sec-1-3-1" name="sec-1-3-1"></a>

正所谓当局者迷,今天当我遇到同样的代码,不同的环境(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 使用遇到的问题<a id="sec-1-3-2" name="sec-1-3-2"></a>

阿里云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 后再还原回来。

参考资料<a id="sec-2" name="sec-2"></a>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容