
# O erro de canonical que esconde as tuas traduções

O agentready.md publica em dez idiomas com prefixo no caminho. Cada página existe em `/about`, `/es/about`, `/ja/about` e mais sete. Durante meses, as nove traduções serviram a mesma linha no `<head>`:

```html
<link rel="canonical" href="https://agentready.md/about">
```

`/es/about` estava a dizer aos motores de busca: sou um duplicado da página inglesa, indexa essa. `/ja/about` dizia o mesmo. As outras sete também. Dez idiomas, um indexável.

## O que um canonical declara mesmo

`rel="canonical"` não é uma sugestão sobre qual URL fica mais arrumado. É uma afirmação: esta página e a que eu indico são a mesma, e a indicada é a que merece o índice. Ligações, sinais de posicionamento e a própria entrada no índice vão todos para o destino. Esta página deixa de competir.

É o comportamento certo quando `/produto?cor=azul` aponta para `/produto`. É o comportamento errado numa tradução. Uma página em espanhol não é um duplicado da inglesa: é outra página, para outro leitor, e tem de posicionar sozinha nas pesquisas em espanhol.

## Porque é que sobreviveu tanto tempo

O nosso router retira o prefixo de idioma antes do encaminhamento, por isso um pedido a `/es/about` chega ao handler como `/about` e o idioma viaja à parte. É um desenho sensato, porque as rotas declaram-se uma vez e funcionam em todos os idiomas, mas deixa `request.url` sem o prefixo, e o parcial do `<head>` construía o canonical a partir desse caminho nu.

Em inglês o bug produz o resultado certo, porque o inglês não leva prefixo. Todas as páginas que vês enquanto desenvolves no teu idioma estão bem. Só parte nos nove que não lês.

A correção é uma chamada, que devolve o prefixo antes de imprimir o URL:

```ejs
<%# request.url já perdeu o prefixo /es/ — localizedUrl devolve-o. %>
<% const canonicalHref = localizedUrl(canonicalPath); %>
<link rel="canonical" href="<%= baseUrl %><%= canonicalHref %>">
```

## A regra: cada versão é canonical de si própria

Canonical autorreferente, sempre. `/es/about` declara `/es/about`. `/ja/about` declara `/ja/about`. Para traduções não há exceção. E se duas versões forem quase iguais, a mesma ficha de produto com três palavras trocadas, não muda nada: idioma não é duplicação.

## hreflang: cada versão lista todas as versões

O canonical diz que página indexar. O `hreflang` diz qual das páginas indexadas mostrar a que leitor. Precisas dos dois, e há duas regras do `hreflang` que quase toda a gente falha.

**Cada versão lista todas as versões, incluindo ela própria.** Dez idiomas são dez ligações `alternate` nas dez páginas, com a autorreferência lá dentro. Faltar essa autorreferência é o defeito mais comum num conjunto por outro lado correto.

**As ligações têm de ser recíprocas.** Se a página inglesa nomeia a espanhola e a espanhola não retribui, o motor descarta a relação, muitas vezes o conjunto inteiro, porque não a consegue verificar. Qualquer um pode dizer que é a tradução de alguém. Só a outra página confirma.

Gera-as a partir de uma lista em vez de manteres dez:

```ejs
<% ['en','es','fr','de','pt','it','ja','zh','ko','ru'].forEach(l => { %>
<link rel="alternate" hreflang="<%= l %>" href="<%= baseUrl %><%= localizedUrl(canonicalPath, l) %>">
<% }) %>
<link rel="alternate" hreflang="x-default" href="<%= baseUrl %><%= canonicalPath %>">
```

Usa o código de idioma sozinho (`es`) quando uma versão serve todos os falantes. Acrescenta região (`es-ES`, `pt-BR`) só se publicares mesmo versões diferentes por mercado; inventar regiões que não tens parte o conjunto para nada.

## x-default não é mais uma tradução

O `x-default` indica a versão a servir a quem lê num idioma que não publicas. Alguém pesquisa em neerlandês e tu não tens neerlandês: leva o que estiver apontado ali.

Não é «o idioma mais importante» nem mais uma entrada na rotação. O nosso fica no URL inglês sem prefixo, que já é o recurso a que todo o site recorre. Apontá-lo para uma página com idioma próprio, `/es/about`, manda para espanhol todos os leitores para quem não tens tradução, e isso é pior do que o predefinido.

## Verifica em uma linha

A falha toda vê-se do terminal. Percorre os teus idiomas e imprime o que cada página declara:

```bash
for p in "" es/ fr/ de/ pt/ it/ ja/ zh/ ko/ ru/; do
  printf '%-4s ' "${p:-en}"
  curl -s "https://exemplo.com/${p}about" | grep -o '<link rel="canonical"[^>]*>'
done
```

A saída correta nomeia em cada linha o seu próprio caminho:

```
en   <link rel="canonical" href="https://exemplo.com/about">
es   <link rel="canonical" href="https://exemplo.com/es/about">
fr   <link rel="canonical" href="https://exemplo.com/fr/about">
```

Dez linhas com o mesmo URL são o bug deste artigo. Corre isto contra os templates que mexeste e não só contra a página inicial: o canonical costuma ser construído por layout, e um site com três layouts pode estar certo em dois.

## As outras formas de partir isto

**Parâmetros de tracking dentro do canonical.** Se construíres o canonical a partir do URL do pedido tal como vem, quem chegar por `?utm_source=newsletter` recebe um canonical a apontar para `?utm_source=newsletter`. Cada partilha cria um URL novo que se declara canonical, ou seja um conjunto sem limite de URLs a servir uma só página. Constrói a partir do caminho e devolve só os parâmetros que identificam mesmo outra página, uma listagem filtrada ou uma paginação, numa ordem fixa, para que uma página não consiga escrever o próprio nome de duas maneiras.

**Canonical a apontar para um redirecionamento.** Se `/es/about` declara `/es/about/` e isso faz um 301 de volta para `/es/about`, deste ao rastreador um ciclo para resolver e um motivo para desconfiar do sinal. Aponta para o URL que devolve `200`.

**Dois canonical em contradição.** O `<link>` no HTML e um cabeçalho HTTP `Link: <...>; rel="canonical"` são ambos válidos, e CDNs, plugins e proxies inversos põem-nos. Quando divergem, o resultado fica indefinido e raramente é o que querias. Vê também os cabeçalhos:

```bash
curl -sI https://exemplo.com/es/about | grep -i "^link:"
```

Não sair nada é a resposta boa, a não ser que o tenhas posto de propósito.

[Analisa o teu site](/pt) · [Gerador de meta tags](/pt/tools/meta-tags-generator) · [Checklist de preparação para IA](/pt/tools/ai-readiness-checklist)
