在macOS上,从homebrew安装bash时,我注意到该设置LC_MESSAGES
似乎对当前shell的语言环境设置有一定影响,但是直到LC_MESSAGES
导出消息,消息才真正改变:
取消设置LANG
和LC_MESSAGES
,我得到了预期的英语错误消息:
bash-4.4$ unset LANG LC_MESSAGES
bash-4.4$ if :; fi
bash: syntax error near unexpected token `fi'
设置LC_MESSAGES
为不正确的值会产生以下错误setlocale
:
bash-4.4$ LC_MESSAGES=foo
bash: warning: setlocale: LC_MESSAGES: cannot change locale (foo): No such file or directory
所以当我定型的时候有些改变LC_MESSAGES
。但是将其设置为合理的值无效:
bash-4.4$ LC_MESSAGES=ja_JP.UTF-8
bash-4.4$ if :; fi
bash: syntax error near unexpected token `fi'
直到我将其导出:
bash-4.4$ export LC_MESSAGES
bash-4.4$ if :; fi
bash: 予期しないトークン `fi' 周辺に構文エラーがあります
(所有LANG
这些似乎也都适用。)
Bash手册中有关Bash变量的部分没有说明LC_MESSAGES
或LANG
必须导出(并且那里列出的大多数其他变量也不必导出)。
为什么是这样?
没错,分配LC_*
外壳变量确实会导致使用相应变量的值bash
调用setlocale()
相应类别的POSIX ,而无论是否导出它们。对LANG
,它要求setlocale(LC_ALL, thevalue)
遵循setlocale(LC_*)
再次对所有的LC_*
变量。对于LANGUAGE
,它什么也没做。
现在,bash
是GNU项目的外壳。对于文本的本地化,它使用GNU gettext
(也称为)libintl
。它甚至附带了其自己的版本,该版本与源捆绑在一起,bash
如果使用调用configure
脚本,则可以将其编译到其中--with-included-gettext
。
gettext
在每个语言的数据库中查找消息翻译。LC_MESSAGES
尽管可以被$LANGUAGE
环境变量覆盖,但是由类别值确定的是哪种语言。
根据gettext文档,对的上一次调用setlocale()
应该是确定类别值的调用,但是存在一些复杂性:
对于多线程应用程序,当前没有标准的API,gettext可用于检索该值。bash
不是一个多线程应用程序,但是即使setlocale(category, NULL)
返回的结果也是实现定义的,实际上并不总是可用的。
因此在实践中,gettext仅setlocale()
在作为GNU libc的一部分或在libc是GNU libc的系统上构建时才用于检索语言名称(就像在GNU系统上使用bash
一起构建的那样--with-included-gettext
),因为它知道它可以依赖它。
在其他系统上,它用于getenv()
确定语言环境,而与setlocale()
先前的调用方式无关,这就是为什么您看到这种行为的原因。
导出这些变量很容易。有人可能会争辩说,如果不出口它们,它们无论如何都不是环境的一部分。POSIX对此并不十分清楚。另一种看待它的方式不是由bash
而是由第三方机制完成转换,因此就像执行其他命令时一样,我们需要使用环境变量在两个软件之间(此处bash
和gettext
)传递语言环境信息。
现在,在GNU系统上,情况实际上变得更糟。
如上所示,gettext包含在GNU libc中。$LANGUAGE
优先于$LC_MESSAGE
但$LANGUAGE
不是POSIX语言环境API的一部分,这是它的扩展。
因此,在GNU系统上,gettext将用于setlocale(LC_MESSAGES, NULL)
获取LC_MESSAGES类别的名称,因为LANGUAGE
它始终使用getenv()
,LANGUAGE
而不是语言环境类别。
问题在于,bash
将环境本身作为变量处理的一部分进行管理,与libc的environ[]
数组断开连接。它确实有自己的getenv()
查询环境,但是它gettext
是作为libc的一部分构建的,并且bash
是动态链接的,它是从libc中dgettext()
调用的getenv()
,因为这是libc中的内部调用,而不是libc的内部调用bash
,所以会自动查询该版本的环境。仅从启动时获取$LANGUAGE
值bash
。
因此,在GNU系统上,除非bash
已静态链接或使用构建,否则无论是否导出变量,对于所生成的消息--with-included-gettext
,对的任何更改都$LANGUAGE
将被忽略bash
。在其他系统上,这很好(只要$LANGUAGE
导出即可),因为gettext不是libc的一部分,因此它确实调用bash
的getenv()
。
在Debian上:
$ LANGUAGE=fr bash -c 'LANGUAGE=es; eval fi'
bash: eval: ligne 0: erreur de syntaxe près du symbole inattendu « fi »
bash: eval: ligne 0: `fi'
(消息法语,的值$LANGUAGE
在时间bash
被调用,而不是西班牙语)。
实际上,使用其他shell并没有更好的效果。
zsh
是不是翻译成其他语言,但没有使用strerror()
它确实使用gettext
的GNU系统内部:
$ LANGUAGE=fr zsh -c 'LANGUAGE=es; true</x; LANGUAGE=en; true</a; true < /etc/shadow'
zsh:1: no existe el archivo o el directorio: /x
zsh:1: no existe el archivo o el directorio: /a
zsh:1: permission denied: /etc/shadow
该命令LANGUAGE=es
很荣幸,但请查看ENOENT的第二条消息如何未以英语显示(大概是由gettext缓存;该缓存应该在$LANGUAGE
更改时已失效,但事实并非如此)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句