Na segunda-feira mudei o meu site de sítio e de forma. Estava feito em Next.js e alojado na Vercel; passou a ser uma aplicação TanStack Start a correr no Cloudflare Workers, num domínio novo, diogoduarte.net, e agora em português e em inglês. Não é um texto contra a Vercel: a versão anterior funcionava. É um registo de como a nova está montada, das duas ou três coisas que me apanharam desprevenido e do que isto significa para quem me pede um site.
O que mudou, num só dia
Foram quatro alterações seguidas, cada uma no seu commit, para que qualquer uma pudesse ser revertida sozinha:
- Framework e alojamento: de Next.js na Vercel para TanStack Start (sobre Vite) no Cloudflare Workers, com o formulário de contacto convertido numa rota de servidor do próprio Start.
- Gestor de pacotes: de pnpm para bun. Voltei a correr os testes, o build e um envio real do formulário antes de dar o passo por fechado.
- Duas línguas: todo o texto visível passou a existir em PT e EN, incluindo as mensagens de erro do formulário.
- Revisão do texto: uma passagem só para reescrever o português que soava a tradução literal e acertar o tratamento, que andava entre o "tu" e o "você".
Um site renderizado num Worker
Cada página é gerada no servidor, a cada pedido, dentro de um Worker. Um Worker não é um servidor que fica ligado à espera: é código que a Cloudflare executa na rede dela quando chega um pedido. Corre em isolates, contextos leves que, segundo a documentação da própria Cloudflare, arrancam muito mais depressa do que um processo Node num contentor. Na prática, não há aquele primeiro pedido lento depois de o site estar parado.
"Edge" é a palavra que se usa para isto, e convém ser honesto sobre o que quer dizer para quem visita a partir de Portugal. A Cloudflare tem um centro de dados em Lisboa, e o HTML é produzido perto de quem o pede em vez de atravessar meio mundo. Mas numa página como a minha, o que mais pesa na velocidade é o próprio peso da página: fontes, imagens, JavaScript. O alojamento ajuda; não salva uma página pesada.
Duas coisas que me apanharam
- As chaves não vêm de
process.env. O código herdado do Next.js lia a chave da API de email a partir de variáveis de ambiente, como num servidor Node. No Workers, as chaves e variáveis vivem no objetoenvque o Worker recebe, e é daí que passaram a ser lidas. Dei por isso ao voltar a testar um envio real do formulário. - O limite de pedidos vive em memória. O formulário tem um limitador por IP para travar abusos. Como a memória de um Worker não é partilhada entre instâncias, o limite é por instância e não global. Para um formulário de contacto chega, e o código diz isso num comentário. Para algo com dinheiro em jogo, teria de ir para um armazenamento partilhado. O IP vem do cabeçalho
CF-Connecting-IP, que é a própria Cloudflare a definir.
A língua decide-se pelo endereço
O erro clássico de um site bilingue é mostrar a página em inglês durante meio segundo e depois trocar para português quando o JavaScript carrega. Para evitar isso, a língua é escolhida no servidor, antes de se gerar qualquer HTML. Na primeira versão, o servidor lia um cookie e o cabeçalho Accept-Language. Em outubro troquei isso por uma regra mais simples: cada língua tem o seu endereço, e é o endereço que decide.
export function localeFromParam(param: string | undefined): Locale | null {
if (param === undefined) return DEFAULT_LOCALE
return isLocale(param) && param !== DEFAULT_LOCALE ? param : null
}- Sem prefixo, português.
/,/sitese/blogsaem sempre em português, que é a língua por omissão. - Com
/en, inglês. As mesmas páginas existem em/en,/en/sitese/en/blog, com os mesmos slugs. O botão PT/EN é uma ligação normal para a mesma página na outra língua. - Qualquer outra coisa, 404. Um primeiro segmento que não seja
ennão é uma língua, e a página não existe.
Não há cookie nem Accept-Language na decisão, por isso o mesmo endereço devolve sempre a mesma língua. O primeiro ecrã, o <html lang> e as meta tags já vêm certos e não há troca visível.
Uma fonte de verdade para o conteúdo
Todo o conteúdo vive num único ficheiro, e o que não depende da língua está lá uma só vez: nomes de empresas, links, tecnologias, datas. Só a prosa que muda de facto entre línguas é que tem uma versão PT e outra EN. Assim, um link errado ou uma tecnologia mal escrita não pode existir numa língua e não na outra. Quando corrijo, corrijo uma vez.
Na primeira versão, o site usava um só endereço para as duas línguas, e isso teve um custo. A Google explica que o Googlebot visita as páginas sem enviar Accept-Language e a partir de IPs nos Estados Unidos, e recomenda endereços separados por língua, ligados com hreflang. Sem cookie e sem esse cabeçalho, o motor de busca recebia sempre a versão inglesa, e só essa era indexada, quando quem me procura pesquisa em português. Foi por isso que mudei: hoje cada página aponta para a sua versão em cada língua com hreflang (pt-PT, en e x-default, este último no endereço português), e o sitemap lista as duas.
O que isto dá a um pequeno negócio
Não construo o site de um restaurante da mesma forma que o meu, mas as vantagens que procurei aqui são as mesmas que procuro para um cliente:
- Rapidez sem manutenção de servidor. Não há máquina para atualizar nem para reiniciar.
- Custo baixo de alojamento. O plano gratuito do Workers inclui 100 mil pedidos por dia, e o plano pago começa nos 5 dólares por mês. Um site de negócio local raramente sai do gratuito.
- Nada de plugins para atualizar. O formulário de contacto é código meu, validado do lado do cliente e do servidor com as mesmas regras, e não uma extensão que precisa de atualizações de segurança todos os meses.
- Duas línguas bem feitas. Útil para quem recebe turistas ou clientes estrangeiros, desde que o português seja escrito como português e não traduzido à pressa.
Não é a resposta para tudo. Se o dono quer mudar textos todos os dias sem me ligar, um site assim precisa de uma camada de edição por cima, ou é melhor outra ferramenta. Mas para um site que tem de abrir depressa, estar certo e custar pouco a manter, é o caminho que escolho hoje.
Se quer um site assim para o seu negócio, ou está a pensar mudar o que tem, pode falar comigo.