5. Idioma e tempo relativo¶
Toda tela de lista mostra quando algo aconteceu: "há 3 minutos", "ontem", "em 2 dias". Feito na mão, esse texto erra sempre nos mesmos lugares — "há 1 minutos", "há 0 segundos", "há 60 minutos" — e cada tela erra de um jeito.
O core resolve isso com relative_time, que fala a mesma língua do resto do app:
o mesmo Locale que você já passa para translate. 🌍
O Locale e o translate¶
O Locale é contexto de entrada, como o Theme: a view lê, ele não vira nó da
árvore. O translate (apelidado t) procura a chave na tabela do idioma e
interpola com str.format:
from tempest_core import Locale, translate
textos = {
"pt": {"ola": "Olá, {nome}"},
"en": {"ola": "Hello, {nome}"},
}
print(translate("ola", Locale(language="pt"), textos, nome="Ana"))
print(translate("ola", Locale(language="en"), textos, nome="Ana"))
print(translate("tchau", Locale(language="pt"), textos))
Chave ausente devolve a própria chave — a tela degrada para o texto do desenvolvedor em vez de quebrar.
relative_time: um delta, uma frase¶
relative_time recebe um delta em segundos (alvo - agora): negativo é
passado, positivo é futuro.
from tempest_core import Locale, relative_time
pt = Locale(language="pt", region="BR")
en = Locale(language="en")
for delta in (-1, -45, -90, -3_600, -86_400, 172_800):
print(f"{delta:>8} | {relative_time(delta, pt):<14} | {relative_time(delta, en)}")
-1 | agora | now
-45 | há 45 segundos | 45 seconds ago
-90 | há 2 minutos | 2 minutes ago
-3600 | há 1 hora | 1 hour ago
-86400 | ontem | yesterday
172800 | em 2 dias | in 2 days
Sem locale, vale o Locale() padrão, que é português: relative_time(-45)
devolve "há 45 segundos".
Por que segundos, e não datetime?
O core é puro e determinístico: ele não lê relógio. "Agora" é decisão do app — no browser é o relógio do cliente, no desktop é outro. Receber o delta deixa a função testável sem congelar tempo.
Com datetime, o delta é uma subtração:
from datetime import datetime, timezone
from tempest_core import Locale, relative_time
enviado_em = datetime(2026, 9, 27, 14, 0, tzinfo=timezone.utc)
agora = datetime(2026, 9, 27, 14, 3, 10, tzinfo=timezone.utc)
print(relative_time((enviado_em - agora).total_seconds(), Locale(language="pt")))
As regras, escritas¶
- Janela do "agora": menos de 30 segundos, para qualquer lado — zero
incluído — é
"agora"/"now". Nunca "há 0 segundos". - Unidade: segundos abaixo de 1 minuto, minutos abaixo de 1 hora, horas abaixo de 1 dia, dias abaixo de 1 semana, semanas abaixo de 30 dias, meses abaixo de 365 dias, e anos daí em diante.
- Arredondamento: meio para cima, como o
Math.rounddo JavaScript. Então 89 s é "1 minuto" e 90 s é "2 minutos". - Arredondou até o limite, sobe de unidade: 59,5 s vira "1 minuto", nunca "60 segundos"; 23,5 h vira "ontem", nunca "24 horas".
- Singular só quando a quantidade é exatamente 1.
Mês é 30 dias, ano é 365
A conta não conhece calendário, de propósito: o resultado depende só do delta, e sai idêntico no tempestweb e no tempestroid. Pelo mesmo motivo, "ontem" quer dizer "um dia de 24 horas atrás", não "o dia anterior do calendário".
nan e infinito são recusados
relative_time(float("nan")) levanta ValueError. Um delta que não é número
é dado ruim chegando de fora, e "há inf anos" na tela só esconderia isso.
Outro idioma, ou outro texto¶
As frases vêm de RELATIVE_TIME_TRANSLATIONS e passam pelo mesmo translate.
Para acrescentar um idioma ou trocar uma frase, passe translations com as
chaves que quiser:
from tempest_core import Locale, relative_time
es = {"es": {"relative_time.past.minute.other": "hace {n} minutos"}}
print(relative_time(-180, Locale(language="es"), es))
print(relative_time(-3_600, Locale(language="es"), es))
A chave que a sua tabela não trouxe cai no inglês
(RELATIVE_TIME_FALLBACK_LANGUAGE), uma a uma — nunca vaza a chave crua. Idioma
sem tabela nenhuma também cai no inglês.
As chaves
relative_time.now, e relative_time.<past|future>.<unidade>.<one|other>,
com <unidade> em second, minute, hour, day, week, month,
year. O {n} é a quantidade já arredondada.
Recapitulando¶
relative_time(delta, locale)recebealvo - agoraem segundos e responde no idioma doLocaledo app.- Menos de 30 s é "agora"; arredondamento meio para cima; arredondou até o limite, sobe de unidade.
translationsacrescenta idioma ou troca frase; o que faltar cai no inglês.nan/infinito levantamValueError.