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>