Python: Formatação de moeda e internacionalização

Python: Formatação de moeda e internacionalização

Como formatar uma moeda utilizando o Python?

Sou programador de uma loja online brasileira e, recentemente, decidimos implementar a venda de produtos para Portugal, com o intuito de aumentar nossa clientela. Para isso, precisamos converter os preços de Real para Euro.

Temos uma função já implementada para realizar a conversão, que toma como parâmetro o valor em Real como float e retorna o valor em Euro, também float:


def converte_real_para_euro(valor_em_real):
    ## implementação da conversão
    return valor_em_euro

valor_inicial_em_real = 50.0
valor_convertido_em_euro = converte_real_para_euro(valor_inicial_em_real)

print(valor_convertido_em_euro)

Vamos testar nosso código:


12.4593681

A função funciona bem! Mas esse resultado basta? Afinal, o que significa todo esse número solto?

Repare que temos um valor com 7 casas decimais (o que atrapalha a legibilidade), sem indicação nenhuma do que estamos nos referindo, do significado dele. Qual o sentido desse número?

Precisamos tornar o valor convertido mais legível, formatá-lo. Queremos transformar todo esse número em 12,46 €, o padrão de Portugal. Como podemos fazer isso?

Arredondando um número com a função round()

O primeiro passo para formatar o número é arredondá-lo, afinal não podemos deixar um valor com 7 casas decimais para nossos clientes.

Uma simples solução para isso é obtida através da função nativa do Python round(), que toma como parâmetro o número a ser arredondado e a quantidade de casas decimais que queremos:


valor_arredondado_em_euro = round(valor_convertido_em_euro, 2)
print(valor_arredondado_em_euro)

E já temos o valor arredondado em 2 casas decimais:


12.46
Banner da Escola de Programação: Matricula-se na escola de Programação. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

Substituindo um caractere de uma string com o método replace()

Precisamos, agora, transformar o . em ,, para seguir o padrão. Para isso, podemos usar o método replace() dos objetos string, especificando o que e para que queremos substituir nos parâmetros, dessa forma:


valor_em_euro_texto = valor_arredondado_em_euro.replace(‘.’, ‘,’)
print(valor_em_euro_texto)

Vamos testar o replace():


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'float' object has no attribute 'replace'

Opa! Recebemos uma exceção!

A exceção, de tipo AttributeError, indica que o tipo float não tem nenhum atributo ou método replace. Isso é porque o replace() é um método da classe string, ou seja, temos de transformar nosso valor convertido de float para string. Vamos de novo:


valor_em_euro_texto = str(valor_arredondado_em_euro).replace(‘.’, ‘,’)
print(valor_em_euro_texto)

E o resultado:


12,46

Agora sim! Enfim, precisamos apenas adicionar o símbolo do Euro, o que pode ser feito com uma simples formatação de string:


valor_em_euro_formatado = ‘{} €’.format(valor_em_euro_texto)
print(valor_em_euro_formatado) 

Vamos ver como fica nosso valor agora:


12,46

Ótimo! Realmente conseguimos formatar nosso valor no padrão português de moeda.

Suportando mais de uma moeda e seguindo padrões diferentes

Se tivéssemos um número maior, como 1000.50, e quiséssemos agrupar o número pelos milhares, transformando em 1.000,50 €, o que faríamos?

Além de um cuidado extra para não substituir o . do agrupamento por uma , com o replace(), precisamos de um código que saiba onde o agrupamento deve ser feito.

Outro problema é que estamos restritos ao padrão português. E se, por exemplo, quiséssemos abrir as portas para clientes estadunidenses?

O padrão não seria mais 12,46 €, mas sim $12.46. Um outro caso seria voltar o valor em Real, portanto, o padrão seria R$ 12,46...

Será que precisamos implementar uma maneira diferente de formatar moeda para cada país?

Conhecendo o módulo locale

O Python nos oferece um módulo inteiro dedicado a serviços de internacionalização - permitindo que nós, programadores, consigamos lidar facilmente com possíveis problemas culturais em uma aplicação - o módulo locale.

A partir desse módulo temos diversas funções que podem nos auxiliar na conversão de formatações de certas especificidades, como tempo e dinheiro.

No nosso caso, podemos usar a função locale.currency(), passando o nosso valor que queremos formatar como parâmetro:


import locale

valor_formatado = locale.currency(12.4593681)
print(valor_formatado)

Vamos testar:


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.6/locale.py", line 262, in currency
    raise ValueError("Currency formatting is not possible using "
ValueError: Currency formatting is not possible using the 'C' locale.

Epa! Novamente uma exceção, dessa vez do tipo ValueError, indicando que formatação de moeda não é possível usando o locale padrão, que é o C.

O C é o locale mais simples, sem as definições específicas para realizarmos a operação que queremos.

Definindo um locale específico com a função setlocale()

Precisamos, então, definir um locale em nosso programa, para passar por cima do padrão. Para isso, usaremos a função locale.setlocale(), que necessita de dois argumentos:

  • category: A categoria que define o tipo de operação que será alterado para o locale especificado

  • locale: Uma string que representa o locale que queremos

Vamos dar uma olhada nas possibilidades de uso desses dois parâmetros. Em primeiro lugar, as categorias permitidas são:

  • locale.LC_CTYPE - Funções relacionadas a caracteres

  • locale.LC_COLLATE - Ordenação de strings

  • locale.LC_TIME - Formatação de tempo

  • locale.LC_MONETARY - Formatação de valores monetários

  • locale.LC_MESSAGES - Exibição de mensagens

  • locale.LC_NUMERIC - Formatação de números

  • locale.LC_ALL - Combinação de todas as categorias

Como estamos tratando de formatação de moedas, usaremos a categoria LC_MONETARY.

No caso do parâmetro locale, as strings aceitas dependem do sistema em que o código está rodando. Por exemplo, em sistemas baseados em UNIX, podemos ver uma lista dos locales registrados em nossa máquina com o seguinte comando no Terminal:


locale -a

No Windows, a coisa complica um pouco. Podemos até ver uma lista de locales publicada pela Microsoft, mas possivelmente existem elementos nessa lista que não funcionem em seu sistema e, ainda, locales no seu sistema que não estão na lista.

Como estou trabalhando no Linux e quero converter para Dólar americano, usarei o locale en_US.UTF-8:


import locale

locale.setlocale(locale.LC_MONETARY, ‘en_US.UTF-8’)

O método setlocale() retorna uma string nos mostrando o locale que escolhemos:


en_US.UTF-8

Formatando moeda com a função currency()

Agora sim, basta usarmos a função currency():


valor_em_dolar_formatado = locale.currency(12.4593681)
print(valor_em_dolar_formatado)

E temos como retorno:


$12.46

Tudo resolvido! Repare que a função currency() chegou até a arredondar o valor pra gente, tirando nossa necessidade de usar a função round()! Vamos testar com um valor mais alto, para ver como fica:


valor_em_dolar_formatado = locale.currency(15000.0)
print(valor_em_dolar_formatado)

O resultado:


$15000.00

Tudo bem, mas queríamos que, na formatação, os milhares ficassem agrupados, lembra? Para isso, além de enviar o valor, também podemos passar um outro argumento pro método currency: o grouping.

O parâmetro grouping espera receber um boolean, que, por padrão, é False. Para agruparmos, então, basta indicar como True:


valor_em_dolar_formatado = locale.currency(15000.0, grouping=True)
print(valor_em_dolar_formatado)

Dessa vez:


$15,000.00

Exatamente como o padrão estadunidense!

Outros parâmetros que podemos definir são symbol, que define se queremos o símbolo da moeda (como por exemplo o $) e é True por padrão, e international, que define se queremos usar o símbolo internacional da moeda (como por exemplo USD) e é por padrão False.

Conclusão

Nesse post, começamos com a necessidade de formatar uma moeda em Euro usando os padrões corretos.

Tentamos, em primeiro lugar, funções como round(), para arredondar o valor, e métodos como replace() e format(), para ajustarmos a string do valor em Euro formatado, e confirmamos que é possível até um certo ponto - funciona, mas podemos encontrar dificuldades.

Conhecemos, por fim, o módulo locale, que consegue solucionar os problemas de internacionalização sem exigir muito do programador. No nosso caso, usamos a função currency() definida nesse módulo.

Para saber mais

Quer saber mais sobre o poder do módulo locale no Python? É sempre uma boa ler a documentação para sabermos o que podemos fazer!

E aí? Ficou mais simples formatar uma moeda agora, não é? E ainda sabemos onde pesquisar caso precisemos de um código que leva em consideração aspectos culturais! Quer aprender mais sobre Python? Dê uma olhada em nossos cursos na Alura e continue estudando!

Veja outros artigos sobre Programação