Documentação do Python em Português: O que já foi traduzido?

Há um bom tempo venho pensando em como melhorar o atual cenário da tradução da documentação do Python para português. Inclusive, já comecei algumas iniciativas para tentar facilitar e melhorar o processo, mas nada promissor. Dessa vez, para não perder o fio da meada pretendo deixar tudo registrado, fazendo uma série de blog posts com o que encontrar pela frente.

Clique no link abaixo para ler o primeiro texto sobre a documentação em português. Nele mostro uma visão geral sobre os processos, as ferramentas usadas, comunidade e etc.


Essa análise visa entender melhor o quão distante estamos de 1) traduzir a documentação do Python por inteiro e 2) ter o mínimo traduzido para ser publicado no site oficial.

Como eu não tenho experiência com ferramentas de análise de dados e para facilitar contribuições/melhorias, optei por usar o Jupyter Notebook e disponibilizar o código no Github. Então caso você tenha alguma ideia de análise que poderia ser feita ou uma sugestão de como melhorar o que eu fiz até aqui, crie uma issue ou abra pull request \o/ (Se precisar de ajuda, clique aqui).

Setup

Para reproduzir esse notebook, é preciso ter o jupyter instalado, além das bibliotecas requests e matplotlib. Se você tiver pipenv instalado, use o comando pipenv install para instalar dependências. Caso não, pip install jupyter requests matplotlib, deve resolver.

Començando com o notebook, importo as bibliotecas necessárias, em especial a helper, que contém funções, constantes, etc, que foram criadas para esconder as gambiarras organizar melhor o notebook.

from collections import defaultdict

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

import helper

Requisitando os dados no Transifex

Para fazer as requisições na API do Transifex, criei a função helper.transifex_api, que nada mais é do que uma camada a cima da biblioteca requests. Nela já configuro autenticação e a url da API, tornando as requisições um pouco mais simples.

Caso você queira executar esse notebook, é preciso definir a variável de ambiente TRANSIFEX_API_TOKEN com o valor do token que você gerar no Transifex (veja aqui como fazer parar gerar um novo token). Você pode fazer isso antes de executar o notebook:

$ export TRANSIFEX_API_TOKEN="token"
$ jupyter notebook

No Transifex existe o conceito de resource, que, no caso da documentação do Python, são as páginas ou seções da documentação, como por exemplo, What’s new in Python 3.7? ou Library Reference. O endpoint resources/ retorna todas as seções disponíveis no Transifex. A resposta contém diversas informações sobre cada seção, porém gero uma lista apenas com os slugs, que serão usados nas próximas requisições.

resources = helper.transifex_api("resources/")
resources = [resource["slug"] for resource in resources]

Para cada uma das seções, acesso as estatísticas básicas que o Transifex disponibiliza. Para a análise, é preciso da quantidade de entidades traduzidas, não traduzidas e revisadas. Uma entidade pode ser uma simples frase até um parágrafo inteiro, depende mesmo de como a documentação foi organizada.

No Transifex não existe o conceito de subseção ou subpágina, então, por exemplo, os tutoria sobre if e sobre for são considerados seções diferentes, mesmo sendo tutoriais. Como não é necessário analisar nesse nível de especificidade, agrupo as estatísticas na seção raíz.

stats = defaultdict(lambda: {
    "translated_entities": 0,
    "reviewed_entities": 0,
    "untranslated_entities": 0,
    "translated_words": 0,
    "untranslated_words": 0,
})
for resource in resources:
    stat = helper.transifex_api(f"resource/{resource}/stats/pt_BR/")
    
    # Group stats considering the first part of slug.
    # Transform "c-api--abstract" and "c-api--allocation" into "c-api".
    resource = resource.split('--')[0]

    stats[resource]["translated_entities"] += stat["translated_entities"]
    stats[resource]["reviewed_entities"] += stat["reviewed"]
    stats[resource]["untranslated_entities"] += stat["untranslated_entities"]
    stats[resource]["translated_words"] += stat["translated_words"]
    stats[resource]["untranslated_words"] += stat["untranslated_words"]
    
stats = {key: stats[key] for key in sorted(stats.keys(), reverse=True)}

Essa parte é maquiagem pura. Todas as seções nos gráficos de barra são de cor azul, exceto as seções que devem ser 100% traduzidas, que são de cor amarela.

# Configure nice colors for the chart. I'm sure there is a better way to do it.
fill_palette, line_palette = helper.palette(stats.keys())

Configurações globais do matplotlib:

%matplotlib inline
plt.rcdefaults()
plt.rcParams.update({'font.size': 14})
plt.style.use('seaborn-whitegrid')

O que já foi traduzido?

priorities_not_translated = sum(
    stat['untranslated_entities']
    for resource, stat in stats.items()
    if resource in helper.REQUIRED_RESOURCES)

not_translated = sum(
    stat['untranslated_entities']
    for stat in stats.values()) - priorities_not_translated

translated = sum(stat['translated_entities'] for stat in stats.values())

figure, ax = plt.subplots(figsize=(12,6))

wedges, texts, autotexts = ax.pie(
    [translated, priorities_not_translated, not_translated],
    labels=["Traduzido", "Não traduzido\n(Seções prioritárias)",
            "Não traduzido\n(Demais seções)"],
    colors=["#f3f9fb", "#f16896", "#c6cbef"],
    autopct="%1.1f%%",
    explode=[0.1]*3,
)
plt.setp(autotexts, size=18, weight="bold")

edgecolors = ["#c6cbef", "#a836ad", "#8186d5"]
for index, color in enumerate(edgecolors):
    wedges[index].set_edgecolor(color)

plt.show()

png

y_resources = list(stats.keys())
x_translated = []
for stat in stats.values():
    all_entities = stat['translated_entities'] + stat["untranslated_entities"]
    x_translated.append(stat["translated_entities"]/all_entities)

figure, ax = plt.subplots(figsize=(12,8))

helper.setup_axes(ax)
ax.set_title("Porcentagem da documentação traduzida por seção", pad=10)
ax.set_xlabel("Porcentagem traduzida", labelpad=10)
ax.set_ylabel("Seção da documentação", labelpad=10)
ax.xaxis.set_major_formatter(ticker.PercentFormatter(xmax=1))
ax.barh(y_resources, x_translated, height=0.7, color=fill_palette,
        edgecolor=line_palette)

plt.show()

png

Quanto falta traduzir em cada seção?

y_resources = list(stats.keys())
x_words = [s['untranslated_entities'] for s in stats.values()]

figure, ax = plt.subplots(figsize=(12,8))
helper.setup_axes(ax)

ax.set_title("Entidades não traduzidas por seção da documentação", pad=10)
ax.set_xlabel("Entidades não traduzidas", labelpad=10)
ax.set_ylabel("Seção da documentação", labelpad=10)
ax.xaxis.set_major_locator(plt.MaxNLocator(10))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(helper.kilo_format))
ax.barh(y_resources, x_words, height=0.7, color=fill_palette,
        edgecolor=line_palette)

plt.show()

png

E o que foi revisado?

total_entities = sum(s['translated_entities'] for s in stats.values())
total_reviewed = sum(s['reviewed_entities'] for s in stats.values())
pct_reviewed = total_reviewed / total_entities * 100
print(f"Entidades traduzidas e revisadas: {pct_reviewed:.2f}%")
Entidades traduzidas e revisadas: 0.03%

Conclusões

O meu foco para o próximo passo será manter o Transifex sincronizado ao repositório no Github. Feito isso, começarei algumas discussões sobre como melhorar o processo de tradução para facilitar e engajar mais pessoas da comunidade a contribuirem.

o/