Polylang e Instagram: um perfil para cada idioma do site

Vista de cima de uma mesa de madeira clara com tres celulares de tela apagada lado a lado, caderno espiral, caneta, xicara de cafe e a lateral de um teclado de notebook.

Dá para o artigo em português sair no Instagram em português e a versão em espanhol sair em outro perfil, sem duplicar nada e sem postar à mão? Dá, e o Polylang já entrega metade do caminho: para ele, a versão em espanhol nunca foi o mesmo artigo. A outra metade é do lado da Meta, onde cada perfil tem autorização própria, limite próprio e um relógio de validade próprio.

As duas metades usam a palavra “post” para coisas diferentes, e é daí que vem quase todo erro de configuração. No WordPress com Polylang, três idiomas são três registros no banco. No Instagram, são três contas profissionais que não se conhecem. Quem trata o conjunto como “um artigo com três legendas” acaba com o texto em francês no perfil brasileiro, ou com dois perfis em silêncio.

Três idiomas, três posts

O Polylang não guarda traduções dentro do artigo original. Cada idioma é um post separado, com ID próprio, permalink próprio, imagem destacada própria e data de publicação própria. A página do plugin no diretório do WordPress descreve o alcance disso: ele traduz posts, páginas, mídia, categorias, tags, tipos de conteúdo personalizados, taxonomias e feeds RSS. São 800 mil instalações ativas na versão 3.8.9, testada até o WordPress 7.1 e exigindo PHP 7.4 ou superior.

O que liga os três registros é uma tabela de tradução, e a função que lê essa tabela é pll_get_post_translations( $post_id ). Ela devolve um array associativo com o código do idioma na chave e o ID do post traduzido no valor, incluindo o próprio post consultado. Um artigo em três idiomas totalmente traduzido devolve três entradas; um artigo pela metade devolve duas, e essa diferença é o que você vai usar depois para saber se pode publicar.

Para descobrir em que idioma está um post específico, a função é pll_get_post_language( $post_id, $field ). O segundo parâmetro aceita name, locale ou slug, e o padrão é slug, o código de duas letras. É esse valor que uma automação usa como chave para decidir o destino: pt vai para um lugar, es vai para outro. Há ainda pll_get_post( $post_id, $slug ), que devolve o ID da tradução num idioma específico, e pll_home_url( $slug ), que devolve o endereço da home naquele idioma.

Publicar um artigo traduzido em três idiomas são três eventos, não um. Três transições de rascunho para publicado, possivelmente em dias diferentes, se o tradutor entregar o espanhol na quinta e o inglês na segunda seguinte. Cada uma delas dispara a sua própria rotina, com o seu próprio permalink na legenda. Isso resolve sozinho um problema que atormenta quem tenta fazer o contrário: não existe momento em que o sistema precise escolher entre três textos, porque em cada disparo só existe um.

Três perfis, três autorizações

Do lado do Instagram, a unidade não é o site: é a conta profissional. A publicação pela API oficial acontece em dois passos, e os dois carregam o identificador da conta. Primeiro um POST para /<IG_ID>/media, que cria um contêiner com a imagem e a legenda; depois um POST para /<IG_ID>/media_publish, que publica aquele contêiner. Se o contêiner não for publicado em 24 horas, ele expira e some.

Com três idiomas em três perfis, o IG_ID muda em cada chamada, e o token que assina a chamada também. Cada conta precisou ser autorizada separadamente, com alguém logado naquela conta clicando em permitir. Não existe um consentimento que cubra as três de uma vez só porque pertencem à mesma empresa.

Cada uma dessas autorizações produz um token de longa duração válido por 60 dias. O endpoint GET /refresh_access_token, com grant_type em ig_refresh_token, renova por mais 60 dias a partir da data da renovação, desde que o token tenha pelo menos 24 horas de vida e ainda não tenha expirado. A permissão exigida é instagram_business_basic.

Três contas significam três desses relógios correndo em paralelo, e eles não estão sincronizados. O perfil que costuma quebrar é o do idioma secundário, justamente o que publica menos: o site sai no ar em novembro, o inglês recebe quatro artigos até o Natal, ninguém mexe nele em janeiro e fevereiro, e em março a renovação que dependia de o token estar vivo já não tem mais token vivo para renovar. O sintoma não aparece no idioma principal, que continua funcionando, e por isso demora a ser notado. Se o seu já apareceu, a leitura dos códigos está em por que o token da Meta expira antes da hora.

Existe um número que assusta menos do que parece. A Meta limita cada conta do Instagram a 100 publicações por API dentro de uma janela móvel de 24 horas, e dá o endpoint GET /<IG_ID>/content_publishing_limit para conferir o consumo. O limite é por conta, não por aplicativo e não por site. Um blog que publica dois artigos por dia em três idiomas gasta duas publicações em cada um dos três perfis, e não seis em um só. A conta muda quando os três idiomas vão para o mesmo perfil ou quando uma agência concentra vários clientes na mesma conta, e aí os 100 passam a ser um teto real em vez de um número distante.

Onde o idioma some

A documentação do Polylang abre o capítulo de funções com um aviso: você precisa checar a existência da função antes de usá-la, “caso contrário seu site vai quebrar feio com um erro fatal na próxima atualização do Polylang”. Na prática, toda chamada fica dentro de um function_exists( 'pll_get_post_language' ), e o caminho sem Polylang precisa existir e funcionar. Um site que instala o plugin hoje e desinstala em seis meses não pode levar junto a publicação automática.

O segundo problema é mais sutil e custa mais tempo: nem toda função do Polylang responde igual em todo contexto. Quando o WordPress atende uma requisição REST, o objeto principal do plugin é uma instância de PLL_REST_Request, e não de PLL_Frontend. Funções que checam só pelo segundo caso devolvem vazio. Foi exatamente isso que o relato aberto em outubro de 2019 no repositório do plugin descreveu para pll_the_languages(), que devolve string vazia dentro de uma requisição REST; a mesma raiz voltou em setembro de 2020 em outro relato, com pll_current_language devolvendo false na API. Os dois estão fechados, e o que sobra deles vale para qualquer integração.

O critério é escolher a função pela pergunta que ela responde. “Qual é o idioma atual da requisição” é uma pergunta de frontend e não tem resposta boa quando quem está publicando é o editor de blocos falando com a REST, o WP-CRON ou o WP-CLI. “Qual é o idioma deste post” é uma pergunta sobre um registro do banco, e pll_get_post_language( $post_id ) responde igual em qualquer contexto. Automação de publicação sempre tem o ID do post em mãos, então sempre pode usar a segunda pergunta.

O terceiro problema separa as edições do plugin, e é o que costuma derrubar quem monta a esteira num serviço externo. Filtrar a REST por idioma com GET /wp/v2/posts?lang=fr, e atribuir idioma e traduções com POST /wp/v2/posts/3376?lang=fr&translations[en]=3374, é recurso do Polylang Pro. Os campos lang e translations nas respostas vêm junto. Na versão gratuita eles não existem: uma ferramenta que leia /wp/v2/posts de fora para decidir o destino recebe os três idiomas misturados, sem nenhum campo que diga qual é qual.

Nem tudo ficou atrás da porta paga. O endpoint de idiomas /wp-json/pll/v1/languages passou a vir incluído no Polylang a partir da 3.7, e responde com locale, slug, name, is_default, home_url, flag_url e fallbacks, entre outras propriedades. Leitura é aberta; criar, alterar e apagar idiomas exige um usuário com a capacidade manage_options. Serve para a ferramenta descobrir quais idiomas o site tem antes de pedir que alguém preencha três configurações.

Vale olhar com atenção para qual campo vira a chave do seu mapa, porque slug e locale não são intercambiáveis. O slug é o código de duas letras, e é o que pll_get_post_language devolve quando você não pede nada. O locale é o do WordPress, e é ele que separa pt_BR de pt_PT. Um site com português do Brasil e português de Portugal nos mesmos dois perfis distintos não pode ser roteado por “pt”, porque os dois idiomas reduzem ao mesmo prefixo: a chave precisa ser o locale, pedido explicitamente no segundo parâmetro. O endpoint de idiomas devolve os dois campos lado a lado, junto com um campo facebook por idioma, e ler essa resposta uma vez poupa a descoberta de que o mapa estava ambíguo depois do terceiro post no perfil errado.

O Post Ping resolve essa etapa dentro do WordPress, e é por isso que ela não esbarra na diferença entre as edições: o idioma do artigo é lido no próprio site, no momento da publicação, e o destino sai desse valor. Cada idioma aponta para o seu Instagram, a sua Página do Facebook, o seu canal do Discord e o seu bot do Telegram. A conexão com a Meta continua sendo uma por conta, porque essa parte é regra da Meta e nenhum plugin contorna: você vai autorizar três vezes, uma por perfil, pelo fluxo oficial.

Quando falta a tradução

O desenho acima assume que todo artigo existe nos três idiomas, e quase nenhum blog multilíngue funciona assim. O normal é o idioma principal acumular vários artigos para cada texto que chega traduzido, porque tradução custa dinheiro e nem todo texto merece. Isso cria três situações que precisam de decisão explícita, e a pior escolha é não decidir.

A primeira é o artigo sem tradução. Se o texto existe só em português, o perfil em espanhol não recebe nada naquele dia. Isso é o correto, ainda que o gráfico de frequência fique feio: mandar o texto em português para um perfil onde todo mundo espera espanhol rende deixar de seguir, não alcance. pll_get_post_translations mostra o buraco antes de ele virar problema, porque devolve duas entradas onde deveria haver três.

A segunda é a tradução que chega depois. O artigo em português sai na segunda, o espanhol fica pronto na quinta. O perfil em espanhol recebe o post na quinta, com o permalink da versão em espanhol e a data da versão em espanhol. Quem olha o conjunto de fora vê dois perfis em ritmos diferentes, e isso é o comportamento certo, não um defeito de sincronismo. Forçar os três a saírem juntos significa segurar o português por três dias esperando o tradutor.

A terceira é o idioma que não tem perfil. Muito site tem três idiomas e duas contas, porque abrir e alimentar um perfil no Instagram custa mais trabalho que traduzir uma página. Aí é preciso dizer para onde vai o idioma órfão, e a resposta honesta costuma ser “para lugar nenhum”. A propriedade fallbacks da API de idiomas do Polylang existe para o caso de você querer o contrário, apontando um idioma como reserva de outro. Use com cuidado: reserva silenciosa é como o texto em francês aparece no perfil brasileiro.

E existe o caso em que nada disso vale. Se os três idiomas vão para o mesmo perfil do Instagram, você não tem um problema de roteamento, tem um problema de legenda: escrever bilíngue sem dobrar o tamanho do texto, decidir qual idioma abre a legenda, e conviver com a métrica de um público que entende metade do que você posta. É outro artigo, e a única parte deste que continua valendo é a dos 100 por janela de 24 horas, que agora conta os três idiomas no mesmo balde.

Como conferir que cada idioma chegou

Uma configuração de três idiomas parece certa muito antes de estar certa, então force o teste em vez de esperar o primeiro artigo de verdade.

Crie um rascunho curto no idioma principal e uma tradução dele no segundo idioma, ligando as duas pela caixa de traduções do Polylang. Antes de publicar, confirme na base que o vínculo existe: pll_get_post_translations aplicado ao ID do rascunho principal precisa devolver duas entradas, com os dois códigos de idioma e os dois IDs. Se devolver uma só, o vínculo não foi salvo e o resto do teste não significa nada.

Antes de publicar qualquer coisa, leia GET /<IG_ID>/content_publishing_limit nas duas contas e anote os dois números. Publique só o artigo do idioma principal. Espere a rotina rodar e leia os dois números de novo.

  • Subiu um, e foi o da conta do idioma principal. É o único resultado que significa roteamento correto.
  • Subiram os dois, e aí a detecção de idioma devolveu vazio: sem idioma para filtrar, a rotina mandou para todos os destinos configurados.
  • Subiu o da conta errada. O mapa entre código de idioma e conta está trocado e vai errar em todo artigo, não só neste.
  • Não subiu nenhum, e o problema nem chegou a ser o idioma. Quem responde qual foi é o código de erro da chamada.

Falta conferir o endereço. Abra a publicação que saiu e compare o link da legenda com o que pll_home_url devolve para aquele código de idioma: o permalink precisa estar sob o mesmo prefixo. Um post em espanhol com link para a versão em português é o erro mais silencioso dos quatro, porque a publicação aconteceu, o contador subiu e a única vítima é o leitor que chega numa página que não sabe ler. Se aparecer código de erro em qualquer etapa, a leitura dele começa pelo subcódigo. Vencidas as quatro perguntas no idioma principal, repita o roteiro inteiro publicando a tradução, e só apague os rascunhos e as publicações de teste depois que o segundo contador tiver subido sozinho.

Este blog publica sozinho nas redes

Cada artigo daqui vira post no Instagram e no Facebook automaticamente: legenda escrita por IA, card visual gerado na hora. Quem faz isso é o Post Ping, o mesmo plugin que você pode instalar no seu WordPress.

Ver planos do Post Ping