%@, %1$@, %lld: gli specificatori di formato che si rompono in traduzione

Aggiornato il · LocalePilot

Una stringa iOS con una variabile contiene un %@ o un %lld. Il traduttore deve mantenerlo, al posto giusto e con il tipo giusto. Ecco gli errori più frequenti, uno dei quali tipico del francese, e come individuarli.

Hai fretta? Controlla i placeholder di una traduzione direttamente nel browser: gratis, senza registrazione, niente viene caricato.

Da dove viene il %

In un'app iOS, una stringa localizzata con una variabile diventa una stringa di formato. In SwiftUI succede in modo implicito:

Text("\(count) items left")                  // key: "%lld items left"
Text("Welcome back, \(name)!")               // key: "Welcome back, %@!"
String(localized: "Total: \(price) €")       // price is a Double: "Total: %lf €"

Un Int dà %lld, una String dà %@, un Double dà %lf. Con la vecchia API il formato lo scrivi tu: String(format: NSLocalizedString("items_left", comment: ""), count), e il file .strings contiene %d o %ld.

In entrambi i casi, il traduttore riceve un testo con dei segni % in mezzo, ed è lì che le cose si rompono.

Tre modi di rompere una traduzione

Perdere lo specificatore. "Invitez à modifier cette liste" invece di "Invitez %@ à modifier cette liste". Niente va in crash: l'argomento viene semplicemente ignorato e il nome della persona sparisce dallo schermo. È l'errore più comune e il più silenzioso.

Cambiare il tipo. Sostituire %lld con %@, o viceversa. Il formato legge allora l'argomento con il tipo sbagliato: nel migliore dei casi un numero senza senso, nel peggiore un crash, perché %@ si aspetta un oggetto dove riceve un intero.

Cambiare l'ordine in silenzio. "%@ shared %@" diventa, in una lingua con un ordine delle parole diverso, una frase in cui i due nomi sono scambiati. La sintassi è corretta, il significato no.

Riordinare con le posizioni

Per cambiare l'ordine, numera gli argomenti: %1$@ è il primo argomento, %2$@ il secondo, ovunque si trovino nella frase.

/* source */      "%@ shared %@ with you" = "%1$@ shared %2$@ with you";
/* French */      "%@ shared %@ with you" = "%1$@ vous a partagé %2$@";
/* other order */ "%@ shared %@ with you" = "%2$@ : partagé par %1$@";

Una regola da ricordare: in una stringa, o è tutto numerato o niente. Mescolare %1$@ e %@ è un comportamento indefinito.

Una trappola francese: "20 % de remise"

La tipografia francese mette uno spazio prima del segno di percentuale: "20 % de remise" (20% di sconto). In una stringa di formato è un problema, perché % d (percentuale, spazio, d) è uno specificatore valido: lo spazio è un flag printf ("anteponi uno spazio ai numeri positivi") e d la conversione intera. La frase

"Profitez de %lld % de remise"

contiene quindi due specificatori, %lld e % d, per un solo argomento. Il secondo legge un valore che non esiste. La forma corretta raddoppia il segno:

"Profitez de %lld %% de remise"

Riguarda solo le stringhe che passano da un formato, cioè quelle con almeno una variabile. Un "20 % de remise" fisso senza argomenti viene mostrato così com'è. Ma le traduzioni evolvono: il giorno in cui qualcuno aggiunge una variabile a quella frase, il % isolato diventa un bug.

Plurali

Per "1 elemento / 3 elementi", non scrivere due chiavi e un if nel codice. In uno String Catalog, la chiave %lld items left riceve varianti plurali (one, other, più few o many a seconda della lingua), ognuna con il proprio %lld. Quando una frase ha due numeri che variano indipendentemente, Xcode usa le sostituzioni (%#@items@): un caso più raro, da modificare in Xcode.

Controllare prima di pubblicare

Il controllo di base è meccanico: per ogni traduzione, l'elenco degli specificatori (tipo e posizione) deve corrispondere alla sorgente. È quello che fa la QA di LocalePilot sui file .strings e .xcstrings: riconosce %@, %1$@, %d, %ld, %lld, %.2f, %u, %i e %s, tratta %% come testo e segnala uno specificatore mancante o in più prima dell'esportazione. Ignora di proposito le conversioni rare come %e o %x, proprio perché "20%en" non venga preso per un errore. Il caso "% d" visto sopra richiede una revisione umana: cerca nelle traduzioni francesi un % seguito da uno spazio.

Domande frequenti

Che differenza c'è tra %d, %ld e %lld?

La dimensione dell'intero atteso. SwiftUI usa %lld per un Int interpolato; in un file .strings scritto a mano, mantieni lo specificatore della sorgente.

Come scrivo un segno % in una stringa tradotta?

In una stringa con variabili, raddoppialo: %%. Altrimenti "% d" o "% s" possono essere letti come specificatori.

Un traduttore può cambiare l'ordine delle variabili?

Sì, con le posizioni: %2$@ poi %1$@. Ma allora tutta la stringa deve essere numerata.