Abbiamo aggiunto giapponese e portoghese brasiliano a tutto il nostro sito per 31 €

Il 29 settembre abbiamo rilasciato globalize.now in giapponese e portoghese brasiliano. Ogni pagina, ogni tabella prezzi e tutti i 61 articoli del blog. La traduzione in sé ha richiesto otto minuti, e l'app ha addebitato circa 31 €. globalize.now è infrastruttura di localizzazione basata su IA, ed ecco com'è andata eseguirla sul nostro stesso sito, con ogni numero preso dalla pagina del job e dalla cronologia Git, non a memoria.

Il nostro articolo su quanto è costato il playbook di crescita per la localizzazione nel 2017 si chiude dicendo che non pubblichiamo ancora i nostri numeri. Questo è il primo. È un numero di costo, non di crescita: nessuno ha ancora avuto tempo di visitare /ja/.

Cosa abbiamo tradotto esattamente?

Tutto il sito, due volte. Ecco l'inventario, contato direttamente dal repository:

  • Interfaccia: 1.782 stringhe in inglese distribuite su cinque catalog di messaggi (il sito principale, i prezzi, le pagine di confronto, le integrazioni e gli esempi di traduzione), circa 21.000 parole.
  • Blog: 61 articoli, circa 110.000 parole.
  • Lingue di destinazione: giapponese (ja) e portoghese brasiliano (pt-BR).

Sono circa 130.000 parole sorgente destinate a due lingue. La pull request ha toccato 170 file e aggiunto 34.681 righe. Dopo il merge, la sitemap elenca 811 URL distribuiti su 11 lingue, e il sito genera 962 pagine statiche.

Quanto tempo ci è voluto?

Otto minuti di traduzione, poi un passaggio di qualità più lungo. Arturs, il nostro CTO, ha aggiunto le due lingue al progetto e il job è partito il main alle 08:56 UTC. La pagina del job lo scompone così:

FaseDurata
Traduzione (11.264 nuove stringhe, 5.632 per lingua)8 min 1 s
Revisione QA automatica27 min 11 s
Consegna a una pull request5 min 29 s

Il commit con entrambe le lingue è arrivato alle 09:29 UTC. Le altre 45.106 stringhe del job provenivano dalla translation memory, perché le otto lingue già esistenti non erano cambiate.

Arturs aveva ipotizzato dieci minuti quando ne aveva parlato nella chat del team. Aveva ragione sulla traduzione ed era ottimista sull'intero job, e il divario è dato dal passaggio di QA. Preferiamo spendere 27 minuti di tempo macchina per verificare piuttosto che saltarlo.

Cosa ha trovato il QA automatico?

72 segnalazioni su 11.264 nuove stringhe, tutte classificate come minori. 11.192 hanno superato il controllo senza problemi, e il QA ha applicato 182 correzioni automatiche lungo il percorso.

Le segnalazioni aperte riguardavano perlopiù il controllo di lunghezza che si mostra cauto con il giapponese. "Pricing" diventa 料金, due caratteri, e un rapporto di lunghezza di 0,29 finisce appena sotto la soglia minima di 0,3. È una traduzione corretta che fa scattare un'euristica pensata per le lingue alfabetiche, non un errore. Lasciamo comunque le segnalazioni visibili: un controllo che non scatta mai non è un controllo.

Quanto è costato?

Circa 31 €: la vista dei costi per lingua del progetto mostra 15,72 € per il giapponese e 16,15 € per il portoghese brasiliano. È quanto addebita l'app, lo stesso importo che vedrebbe un cliente che traduce lo stesso volume. Non è il nostro costo interno di modello, e non è uno sconto.

Su circa 260.000 parole tradotte, sono circa 12 centesimi ogni 1.000. Nient'altro è stato addebitato in aggiunta. Non c'è alcun costo per lingua né per postazione, quindi aggiungere la dodicesima lingua costa quanto aggiungere la terza. I piani attuali sono nella pagina prezzi.

Cosa ha comportato davvero il lavoro di engineering?

Una sola pull request, unita nello stesso pomeriggio, e niente di tutto ciò era traduzione. All'apertura, il job di traduzione è stato rieseguito e ha preso 56.370 stringhe su 56.390 direttamente dalla translation memory, così i cataloghi già tradotti sono arrivati nei nuovi file in sette minuti. La traduzione è la parte che è stata automatizzata. Registrare un nuovo locale in un sito Next.js resta compito del tuo codice, ed è bene sapere cosa significhi "il tuo codice" prima di pianificarlo.

Aggiungere una lingua a next-intl è una riga nella configurazione di routing. Tutto ciò che costruisce un URL, legge una lingua o elenca le tue lingue è il resto del diff:

  • Routing: l'elenco delle lingue, più un override del prefisso URL per pt-BR (sezione successiva).
  • Metadata SEO: cluster hreflang, canonical, og:locale (ja_JP, pt_BR) e l'elenco delle lingue su cui iterano gli helper dei metadata.
  • Formattazione: i tag Intl ja-JP e pt-BR, così che date e numeri vengano visualizzati correttamente.
  • Selettore lingua: collega a ogni lingua con un vero <a href hreflang>, così che i crawler possano seguirlo.
  • Sitemap e controlli: il generatore della sitemap, il controllo lastmod, il controllo dei link interni e il verificatore delle traduzioni hanno dovuto imparare a riconoscere i due nuovi codici.
  • Cataloghi: file .po vuoti, con solo l'header, per le due lingue, popolati nella stessa pull request a partire dalla translation memory.

Se il tuo sito è meno personalizzato del nostro, la maggior parte di questo elenco è configurazione. Se in qualche punto costruisci gli URL a mano — e la maggior parte dei siti lo fa da qualche parte — te ne renderai conto la prima volta che un link risulta sbagliato. La guida per sviluppatori copre la configurazione, e cosa fa davvero un agente di localizzazione IA copre la distinzione tra ciò che è automatizzato e ciò che non lo è.

Perché il portoghese brasiliano è su /pt-br/ e non su /pt-BR/?

Perché un tag di lingua e un segmento di URL sono due cose diverse con compiti diversi.

Il tag corretto è pt-BR, secondo BCP 47. Lo usiamo ovunque una macchina legga la lingua: nomi dei file, <html lang>, hreflang, og:locale e Intl. Solo l'URL viene reso minuscolo:

// i18n/routing.ts (trimmed)
const prefixes = {
  "pt-BR": "/pt-br",
} as const;

export const routing = defineRouting({
  locales: ["en", "it", "de", "fr", "es", "lv", "hi", "lt", "et", "ja", "pt-BR"],
  defaultLocale: "en",
  localePrefix: { mode: "always", prefixes },
});

/** The URL path segment for a locale: `pt-BR` → `pt-br`, `de` → `de`. */
export function localeUrlSegment(locale: string): string {
  const prefix = (prefixes as Record<string, string | undefined>)[locale];
  return prefix ? prefix.slice(1) : locale;
}

I path con maiuscole e minuscole mescolate vengono digitati male, e un sito che risponde sia su /pt-BR/ che su /pt-br/ finisce con due copie di ogni pagina che competono tra loro. Quindi /pt-BR/ reindirizza a /pt-br/, e localeUrlSegment() costruisce ogni URL fatto a mano a partire dalla mappa dei prefissi. Un'unica fonte di verità significa che il selettore lingua, i canonical e la sitemap non possono disallinearsi.

Perché giapponese e portoghese brasiliano?

Sono grandi lingue del web che non coprivamo ancora. Secondo il conteggio di W3Techs il giorno del rilascio, il giapponese è la lingua dei contenuti del 4,9% dei siti web e il portoghese del 4,1%.

L'argomento a favore è più vecchio di noi. CSA Research ha intervistato 8.709 consumatori in 29 paesi nel 2020, e il 76% ha dichiarato di preferire acquistare prodotti con informazioni nella propria lingua. Questo è il lato della domanda. Ciò che è cambiato è il lato dell'offerta: due lingue prima significavano un rapporto con un fornitore esterno, ora costano 31 €.

La nostra regola per scegliere le lingue è quella descritta nell'articolo sul playbook di crescita. Guarda da dove arriva già il tuo traffico, aggiungi due lingue, non cambiare nient'altro e misura per un trimestre. Faremo esattamente questo con queste due.

Cosa non abbiamo ancora verificato?

La revisione da madrelingua. Nessuno che legga giapponese o portoghese brasiliano come lingua madre ha ancora esaminato l'output.

Ciò che abbiamo verificato: QA automatico su ogni stringa, la build supera ogni controllo di prebuild, e le pagine controllate a campione in staging restituiscono 200 con i tag lang, canonical e hreflang corretti e si renderizzano completamente. Ciò che una build non può verificare è se una frase suona come scritta da una persona. Lo abbiamo imparato a nostre spese quando abbiamo verificato il nostro stesso tedesco e francese e trovato il registro sbagliato su intere pagine.

Quindi la revisione arriva subito dopo, e aggiorneremo questo articolo con quanto emergerà, incluso qualsiasi cosa risultasse sbagliata.

Potresti fare lo stesso sul tuo sito?

Se le tue stringhe vivono già in catalog, sì, e la parte di traduzione richiederà minuti. Se sono ancora hardcoded nei componenti, prima viene la conversione. Questa avviene una sola volta, nel flusso di connessione in-app, e da quel momento in poi i push job traducono le nuove stringhe. La guida per vibe coder copre le app costruite con Lovable, Bolt o Cursor, dove le stringhe hardcoded sono la norma.

Il costo scala con le parole che traduci, non con le lingue o con le persone del team. Ecco perché è cambiata la risposta a "vale la pena aggiungere una lingua?". Ora misurarlo costa meno della riunione per discuterne.

Provalo sul tuo repository

Due lingue ci sono costate otto minuti di traduzione e circa 31 €. Connetti il tuo repository, scegli due lingue verso cui i tuoi analytics già puntano, e scopri come appare il tuo scontrino.

globalize.now trasforma il testo hardcoded dell'app in file di localizzazione pronti e li mantiene aggiornati mentre rilasci.

Prova globalize.now gratis