Il testo UI in Figma è un artefatto di design. È copy del mockup, spesso scritto dai designer piuttosto che dai product manager, raramente revisionato per i18n. figmascope lo estrae comunque — perché anche il copy placeholder ha bisogno di una chiave risorsa se si sta generando codice che verrà eventualmente localizzato, e il processo di estrazione è dove risiede la logica.
strings.json è l'artefatto che mappa gli ID risorsa in notazione puntata ai valori stringa letterali. Questo articolo spiega come vengono generate le chiavi, cosa viene filtrato, cosa succede quando le chiavi collidono, e come questo si mappa su Android strings.xml e altri formati i18n.
Il formato
{
"speed.test": "Speed Test",
"data.transfer.rate": "Data Transfer Rate",
"start": "Start",
"results.download": "Download",
"results.upload": "Upload",
"results.ping": "Ping",
"unit.mbps": "Mbps",
"settings.server.auto": "Auto Select"
}
La struttura è un oggetto piatto con chiavi ID risorsa in notazione puntata. I valori sono il contenuto stringa letterale da Figma. I percorsi puntati nidificati sono semanticamente significativi — results.download e results.upload appartengono allo stesso gruppo logico — ma il file è JSON piatto, non nidificato. Questo corrisponde al funzionamento di Android strings.xml (nomi di risorse piatti con convenzione a punti per il raggruppamento) e al modo in cui la maggior parte delle librerie i18n (i18next, Rosetta, ecc.) gestisce il namespacing delle chiavi piatte.
Come vengono generate le chiavi
La generazione delle chiavi inizia dal nome del layer Figma del nodo, non dal contenuto del testo. Un nodo di testo denominato Data Transfer Rate Label con contenuto "Data Transfer Rate" genera una chiave dal nome del layer, non dal valore.
La pipeline di generazione:
- Prendi il nome del layer
- Applica i filtri
sanitizeName: traslittera i caratteri cirillici, rimuovi i caratteri bidirezionali/di controllo (intervallo Unicode U+202A–U+202E e simili), rimuovi i caratteri non alfanumerici non-spazio - Converti in minuscolo e sostituisci gli spazi con punti
- Rimuovi i punti iniziali e finali
Quindi "Data Transfer Rate Label" → "data.transfer.rate.label". In pratica, i designer spesso nominano i layer di testo senza il sostantivo finale, quindi "Data Transfer Rate" → "data.transfer.rate".
Il passo di traslitterazione cirillica esiste perché figmascope è usato con file Figma in più lingue. Un layer denominato Скорость viene traslitterato in Skorost', poi diventa "skorost'" → dopo la rimozione dei caratteri speciali → "skorost". Non elegante, ma la chiave è stabile tra le esecuzioni per lo stesso nome del layer.
La generazione delle chiavi avviene dal nome del layer, non dal valore del testo. Due nodi di testo con contenuto identico ma nomi di layer diversi ottengono chiavi diverse. Due nodi di testo con contenuto diverso ma lo stesso nome di layer creano una collisione — gestita di seguito.
Filtri delle chiavi — cosa viene escluso
Non ogni nodo di testo in un file Figma dovrebbe essere una stringa localizzabile. figmascope filtra prima di generare le chiavi:
- Stringhe vuote: I nodi di testo senza contenuto o con solo spazi bianchi vengono eliminati.
- Stringhe solo numeriche: Valori come
"42","100%","3.14"vengono eliminati. Questi sono valori di dati, non copy UI, e dovrebbero provenire dal tuo data layer. - Stringhe brevi (≤2 caratteri): I caratteri singoli e le stringhe a due caratteri vengono eliminati. Icone, punti elenco, apici — questi non sono risorse stringa.
- Chiavi che si svuotano dopo la sanitizzazione: Se un nome di layer è composto interamente da caratteri eliminati da
sanitizeName, il risultato è una chiave vuota. Questi nodi vengono eliminati silenziosamente.
Il filtro numerico merita una spiegazione aggiuntiva. Un mockup di design spesso ha numeri placeholder: "42 ms" (ping), "150 Mbps" (velocità di download). "42 ms" supera il filtro perché non è puramente numerico — diventerebbe la chiave "42.ms". Ma probabilmente vorresti estrarre solo "ms" come etichetta di unità e calcolare 42 a runtime. figmascope non fa questa inferenza — prende il testo completo così com'è. Il filtro sulle stringhe brevi elimina "ms" da solo (2 caratteri), ma mantiene "42 ms" come stringa completa.
Questo è un limite riconosciuto. Il testo UI che mescola dati template con etichette letterali è difficile da decomporre automaticamente. Il codice generato dovrebbe trattare la stringa estratta come punto di partenza, non come copy definitivo.
Gestione delle collisioni
Una collisione si verifica quando due diversi nodi di testo producono la stessa chiave ma hanno valori diversi. Ad esempio: un layer denominato "Label" sullo schermo A contiene "Download" e un layer denominato "Label" sullo schermo B contiene "Upload". Entrambi generano la chiave "label".
La regola di collisione di figmascope: elimina completamente la chiave da strings.json e inserisce un avviso in _meta.json:
// _meta.json
{
"warnings": [
"strings-collision:label"
]
}
Mantenerne uno arbitrariamente sarebbe sbagliato — tradurresti silenziosamente una delle due stringhe in modo errato. Mantenere entrambe con chiavi disambiguate (es. "label.1", "label.2") sarebbe una scelta di naming che non serve a nessuno. Eliminare è onesto.
L'IR gestisce la collisione in modo pulito: quando una chiave viene eliminata a causa di una collisione, figmascope percorre l'IR e rimuove il campo stringRef da tutti i nodi leaf che vi facevano riferimento. Il campo text (valore letterale) rimane. L'agente vede un leaf con text ma senza stringRef e sa di usare il letterale come fallback — il che è sicuro perché la regola CONTEXT.md dice solo "usa stringRef se presente".
// Prima della risoluzione collisione — due leaf con stessa chiave, testo diverso
{ "kind": "leaf", "text": "Download", "stringRef": "label" }
{ "kind": "leaf", "text": "Upload", "stringRef": "label" }
// Dopo la risoluzione collisione — chiave eliminata, testo preservato
{ "kind": "leaf", "text": "Download" }
{ "kind": "leaf", "text": "Upload" }
L'avviso ti dice di correggere i nomi dei layer in Figma. Un layer denominato "Download Label" e "Upload Label" produrrebbero chiavi distinte "download.label" e "upload.label" senza collisione.
Mappatura su Android strings.xml
Android strings.xml usa ID risorsa stringa piatti con underscore. La notazione puntata di figmascope si mappa in modo pulito:
// strings.json
{
"speed.test": "Speed Test",
"data.transfer.rate": "Data Transfer Rate",
"results.download": "Download"
}
// → strings.xml (generato)
<resources>
<string name="speed_test">Speed Test</string>
<string name="data_transfer_rate">Data Transfer Rate</string>
<string name="results_download">Download</string>
</resources>
La trasformazione è: sostituire i punti con underscore. Questa è la trasformazione completa. Gli ID risorsa Android non ammettono punti; gli underscore sono il separatore convenzionale. La chiave speed.test in strings.json diventa R.string.speed_test in Kotlin, che è ciò a cui il vincolo CONTEXT.md punta l'agente quando vede stringRef: "speed.test".
// Vincolo CONTEXT.md in azione
// IR leaf: { "text": "Speed Test", "stringRef": "speed.test" }
// Output agente:
Text(text = stringResource(R.string.speed_test))
Mappatura su altri formati i18n
La notazione puntata è una lingua franca per i18n. La maggior parte dei sistemi la accetta direttamente o richiede solo una trasformazione minima:
| Formato | Formato chiave | Trasformazione dalla notazione puntata |
|---|---|---|
| Android strings.xml | speed_test |
Sostituisci . con _ |
| iOS Localizable.strings | "speed.test" |
Nessuna (notazione puntata usata direttamente) |
| i18next (JSON) | Nidificato: { "speed": { "test": "..." } } |
Espandi le chiavi piatte in struttura nidificata |
| Flutter ARB | speedTest |
Converti in camelCase |
figmascope produce il formato in notazione puntata. La trasformazione specifica per target è gestita a valle — dall'agente (se il target CONTEXT.md è sufficientemente esplicito) o da un piccolo script di conversione.
Cosa strings.json non risolve
Nota onesta sullo scope: strings.json viene estratto da qualsiasi testo esista in Figma al momento dell'esportazione. Non è un file di risorse i18n validato. Tre problemi che incontrerai:
Testo placeholder: I mockup Figma usano frequentemente Lorem Ipsum, "[Titolo Qui]", o copy scritto dai designer che non è stato revisionato. Le stringhe estratte sono quelle presenti nel file, revisionate o no.
Contenuto dinamico: I design spesso mostrano "John Doe" o "42 Mbps" come dati rappresentativi. Queste non sono stringhe localizzabili — sono valori template. figmascope non può distinguere dati design-time da copy design-time. Il filtro numerico intercetta i numeri puri, ma le stringhe miste come "42 Mbps" vengono comunque estratte.
Stringhe mancanti dai nodi filtrati: Se un nodo di testo non supera un filtro (troppo breve, solo numerico, vuoto dopo la sanitizzazione), non apparirà in strings.json e il leaf IR non avrà un stringRef. Questo è intenzionale, ma significa che la copertura delle stringhe dipende dalla qualità della denominazione dei layer in Figma.
L'output è un punto di partenza. Copialo nel tuo file i18n, revisionalo rispetto alle tue convenzioni di naming delle risorse, elimina le stringhe placeholder e aggiungi manualmente le chiavi per il contenuto dinamico. Sono dieci minuti di lavoro invece di estrarre manualmente ogni nodo di testo da Figma. Prova a estrarre le stringhe dal tuo file su figmascope.dev.
Per il quadro completo di come stringRef fluisce attraverso l'IR nel codice generato, vedi IR Per-Schermata — Stack, Overlay, Absolute, Leaf. Per come vengono dichiarati i vincoli che governano l'uso delle stringhe, vedi Anatomia di CONTEXT.md. Per il flusso di generazione Jetpack Compose end to end, vedi Jetpack Compose da Figma. Pronto a generare il tuo strings.json? Usa l'app principale su figmascope.dev.