Você está certo que atribuir as variáveis LC_*
shell faz com que bash
chame POSIX setlocale()
para a categoria correspondente com o valor da variável, sejam eles exportados ou não. Para LANG
, ele chama setlocale(LC_ALL, thevalue)
seguido de setlocale(LC_*)
novamente para toda a variável LC_*
. Para LANGUAGE
, não faz nada.
Agora, bash
é o shell do projeto GNU. Para localização de texto, ele usa o GNU gettext
, também conhecido como libintl
. Ele ainda vem com sua própria versão empacotada com a fonte que você pode compilar em bash
se você chamar o script configure
com --with-included-gettext
.
gettext
procura traduções de mensagens em um banco de dados por idioma. Qual idioma é determinado pelo valor da categoria LC_MESSAGES
, embora possa ser substituído pela variável de ambiente $LANGUAGE
.
De acordo com a documentação do gettext, a chamada anterior para setlocale()
deve ser a que determina o valor da categoria, mas há algumas complicações:
Para aplicativos multithread, não há atualmente nenhuma API padrão que o gettext possa usar para recuperar esse valor . bash
não é um aplicativo multithread, mas até mesmo o que é um setlocale(category, NULL)
retornos é implementação definida e na prática nem sempre utilizável .
Portanto, na prática, o gettext usa apenas setlocale()
para recuperar o nome do idioma quando compilado como parte do GNU libc ou em um sistema onde o libc é o GNU libc (como o construído com bash
with --with-included-gettext
em um sistema GNU) porque sabe que pode confiar nele.
Em outros sistemas, ele usa getenv()
para determinar a localidade, independentemente de como setlocale()
foi chamado anteriormente, e é por isso que você está vendo esse comportamento.
Exportar essas variáveis é um trabalho fácil. Pode-se argumentar que, se não forem exportados, não farão parte do ambiente. POSIX não é muito claro sobre isso. Outra maneira de ver isso é que a tradução não é feita por bash
, mas por um mecanismo de terceiros, assim como quando executamos outros comandos, precisamos usar variáveis de ambiente para passar a localidade informações entre os dois softwares (aqui bash
e gettext
).
Agora, nos sistemas GNU, na verdade fica pior.
Como visto acima, o gettext está incluído no GNU libc. $LANGUAGE
tem precedência sobre $LC_MESSAGE
, mas $LANGUAGE
não faz parte da API de locale POSIX, que é uma extensão sobre ela.
Assim, em um sistema GNU, o gettext usará setlocale(LC_MESSAGES, NULL)
para obter o nome da categoria LC_MESSAGES, para LANGUAGE
, ele sempre usa getenv()
, LANGUAGE
não é uma categoria de localidade.
O problema é que bash
gerencia o ambiente por si só como parte de sua manipulação de variáveis, desconectada da matriz environ[]
da libc. Ele possui seu próprio getenv()
, que consulta sua própria versão do ambiente, mas quando gettext
é construído como parte da libc e bash
é vinculado dinamicamente dgettext()
chama o getenv()
da libc como isso é uma chamada interna dentro da libc, não a de bash
, portanto, só obteremos o valor $LANGUAGE
do tempo em que bash
foi iniciado.
Portanto, nos sistemas GNU, a menos que bash
tenha sido vinculado estaticamente ou criado com --with-included-gettext
, qualquer alteração em $LANGUAGE
será ignorada para as mensagens geradas por bash
, independentemente de a variável ser exportada ou não. Em outros sistemas, tudo bem (contanto que $LANGUAGE
seja exportado) já que o gettext não faz parte da libc, então ele chama bash
' getenv()
.
No 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'
(mensagem em francês, o valor de $LANGUAGE
no momento em que bash
foi invocado, não em espanhol).
Na verdade, não é muito melhor com outras camadas.
zsh
não está traduzido para outros idiomas, mas usa strerror()
, que usa gettext
internamente nos sistemas 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
O LANGUAGE=es
foi honrado, mas veja como a segunda mensagem para ENOENT não foi exibida em inglês (presumivelmente em cache pelo gettext de alguma forma; esse cache deveria ter sido invalidado quando $LANGUAGE
foi alterado, mas não foi o caso). / p>