Todos os artigosPublicado a

Como reconstruí o meu site no Cloudflare Workers, em duas línguas

De Next.js na Vercel para TanStack Start no Cloudflare Workers: um endereço por língua, uma fonte de conteúdo e os compromissos.

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 objeto env que 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.

lib/i18n/paths.ts
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. /, /sites e /blog saem sempre em português, que é a língua por omissão.
  • Com /en, inglês. As mesmas páginas existem em /en, /en/sites e /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 en nã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.

Precisa de um site que a sua equipa atualize sem ligar a um programador?

Começar um projeto