# General Translation Knowledge Base: Come si trovano le stringhe hardcoded non tradotte?
URL: https://generaltranslation.com/it/kb/find-hardcoded-strings.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Esegui in CI un linter che segnali i letterali stringa visibili all'utente, come la regola no-literal-string di eslint-plugin-i18next per JSX, il controllo HardcodedText di Android Lint o il linter HardCodedString di erb_lint per Rails. Poi naviga nell'app con una pseudo-impostazione regionale per individuare il testo che i controlli statici non riescono a tracciare.
Category: Internazionalizzazione
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Una stringa hardcoded è un testo visibile all&#39;utente scritto direttamente nel codice anziché gestito tramite la libreria i18n, e per questo compare nella lingua di origine in tutte le impostazioni regionali. Aggiungerne una non rompe nulla: in inglese l&#39;app sembra completa, i test passano e il problema emerge solo quando qualcuno apre la build francese.

## Perché le stringhe hardcoded sfuggono ai controlli?

* **La lingua di origine le nasconde.** Una traduzione mancante sembra identica a una corretta finché non si cambia impostazione regionale.
* **Il testo si trova al di fuori del contenuto figlio JSX.** Gli attributi `placeholder`, `title`, `alt` e `aria-label`, i toast e i messaggi di validazione generati in JavaScript e le etichette definite negli oggetti di config sfuggono facilmente in fase di revisione.
* **Ogni funzionalità aggiunge testo.** Il modo più rapido per scrivere un pulsante è usare un letterale, e nessuno lo racchiuderà in seguito, a meno che un controllo non fallisca.

La soluzione, quindi, è un controllo che fallisca: un linter in CI per il codice, più una verifica visiva per tutto ciò che sfugge al linter.

## Come trovare le stringhe hardcoded in ogni stack?

| Stack                       | Controllo                                                                                                                                                                                                                      | Cosa segnala                                                                                                                            | Cosa non rileva                                                                          |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| React e JSX                 | [`eslint-plugin-i18next`](https://github.com/edvardchen/eslint-plugin-i18next) `no-literal-string`                                                                                                                             | Testo JSX per impostazione predefinita; attributi e contenuto figlio `{'...'}` in modalità `jsx-only`; ogni letterale in modalità `all` | Testo che arriva alla UI da costanti, a meno di accettare il rumore della modalità `all` |
| React e JSX                 | [`eslint-plugin-react`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md) `jsx-no-literals`                                                                                         | Testo JSX non racchiuso per impostazione predefinita; stringhe racchiuse e props con `noStrings`                                        | Stringhe al di fuori del JSX                                                             |
| Android                     | Android Lint [`HardcodedText`](https://googlesamples.github.io/android-custom-lint-rules/checks/HardcodedText.md.html) e [`SetTextI18n`](https://googlesamples.github.io/android-custom-lint-rules/checks/SetTextI18n.md.html) | Testo letterale nell&#39;XML dei layout; `TextView.setText` con letterali, concatenazioni o `Number.toString()`                         | Chiamate `Text("...")` di Jetpack Compose                                                |
| iOS e macOS                 | Estrazione dello String Catalog di Xcode e opzione di schema **Show non-localized strings**                                                                                                                                    | Stringhe assenti dal catalogo dopo una build; testo non localizzato mostrato in maiuscolo a runtime                                     | Schermate che nessuno apre                                                               |
| Rails                       | [erb&#95;lint](https://github.com/Shopify/erb-lint) `HardCodedString`                                                                                                                                                          | Testo visibile nel markup ERB                                                                                                           | Stringhe all&#39;interno dei tag JavaScript                                              |
| Qualsiasi base di codice    | ripgrep                                                                                                                                                                                                                        | Testo tra tag su una sola riga                                                                                                          | Testo su più righe; non distingue il testo racchiuso da quello non racchiuso             |
| Qualsiasi app in esecuzione | [Pseudo-localizzazione](/kb/pseudo-localization)                                                                                                                                                                               | Qualsiasi testo renderizzato che non è passato dal livello i18n                                                                         | Schermate che nessuno apre                                                               |

Usa un linter nella CI se vuoi che le nuove stringhe hardcoded facciano fallire la build. Usa ripgrep per un audit una tantum di una base di codice che stai per internazionalizzare. Usa la [pseudo-localizzazione](/kb/pseudo-localization) prima di un lancio: il testo che non è passato dal livello i18n resta invariato, mentre tutto il resto appare accentato, compreso il testo proveniente da API, costanti e risposte del server che l&#39;analisi statica non riesce a tracciare.

## Eseguire il linting di JSX con ESLint

`eslint-plugin-i18next` ha un&#39;unica regola, `no-literal-string`. La modalità predefinita, `jsx-text-only`, segnala solo il testo tra i tag JSX. Imposta `mode: 'jsx-only'` per segnalare anche gli attributi stringa e le espressioni stringa in JSX. Questa flat config è compatibile con ESLint 9 e 10 e include i file TypeScript:

```bash
npm install --save-dev eslint eslint-plugin-i18next typescript typescript-eslint
```

```js title="eslint.config.mjs"
import i18next from 'eslint-plugin-i18next';
import tseslint from 'typescript-eslint';

export default [
  {
    files: ['src/**/*.{js,jsx,ts,tsx}'],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: { ecmaFeatures: { jsx: true } },
    },
    plugins: { i18next },
    rules: {
      // Segnala testo JSX, attributi stringa e figli {'string'}
      'i18next/no-literal-string': ['error', { mode: 'jsx-only' }],
    },
  },
];
```

Ecco il componente che abbiamo analizzato con il linter:

```jsx title="src/Checkout.jsx"
export function Checkout({ total, user }) {
  const error = 'Payment failed';
  return (
    <section className="checkout">
      <h2>Order summary</h2>
      <p>Hello, {user.name}</p>
      <input placeholder="Promo code" type="text" />
      <button title="Pay now" onClick={() => alert(error)}>Pay</button>
      <p>{'Free shipping on orders over $50'}</p>
      <p>{`Total: ${total}`}</p>
      <span>+ 2</span>
    </section>
  );
}
```

```text title="npx eslint src (eslint-plugin-i18next 6.1.5, ESLint 10.13.0)"
src/Checkout.jsx
   5:11  error  disallow literal string: <h2>Order summary</h2>                                             i18next/no-literal-string
   6:10  error  disallow literal string: <p>Hello, {user.name}</p>                                          i18next/no-literal-string
   7:26  error  disallow literal string: placeholder="Promo code"                                           i18next/no-literal-string
   8:21  error  disallow literal string: title="Pay now"                                                    i18next/no-literal-string
   8:60  error  disallow literal string: <button title="Pay now" onClick={() => alert(error)}>Pay</button>  i18next/no-literal-string
   9:11  error  disallow literal string: {'Free shipping on orders over $50'}                               i18next/no-literal-string
  11:13  error  disallow literal string: <span>+ 2</span>                                                   i18next/no-literal-string

✖ 7 problems (7 errors, 0 warnings)
```

Cosa mostra l&#39;esecuzione:

* **La modalità predefinita segnala 4 problemi.** Con la config `flat/recommended` del plugin, gli attributi e il figlio `{'...'}` passano; `jsx-only` aggiunge quei 3 rilevamenti.
* **Alcuni letterali passano comunque.** `className` e `type` sono esclusi per impostazione predefinita, ed è proprio ciò che serve. Il letterale template alla riga 10 passa a meno che non si aggiunga `'should-validate-template': true`, e la costante `'Payment failed'` passa a meno che non si usi `mode: 'all'`, che però segnala anche tutte le stringhe del file non destinate alla UI.
* **Mettiti in conto dei falsi positivi.** `+ 2` viene segnalato perché i modelli da ignorare predefiniti coprono solo le stringhe composte da simboli senza spazi. Aggiungi un modello in `words.exclude` e copiaci i valori predefiniti presenti in `lib/options/defaults.js` del plugin: ogni opzione che passi sostituisce i valori predefiniti di quell&#39;opzione.

Se usi già `eslint-plugin-react`, l&#39;alternativa è la sua regola `jsx-no-literals`. Senza opzioni richiede soltanto che il testo JSX si trovi in un expression container, quindi `<h2>Order summary</h2>` non passa, mentre `<h2>{'Order summary'}</h2>` sì. Imposta `{ noStrings: true, ignoreProps: true }` per segnalare anche le stringhe racchiuse e i letterali template nel contenuto figlio; se rimuovi `ignoreProps`, verrà segnalata anche ogni prop di tipo stringa, compreso `className="checkout"`.

## Altri stack

**Android.** Esegui `./gradlew lint` oppure, in Android Studio, scegli **Code &gt; Inspect Code** e consulta la categoria Internationalization. Per impostazione predefinita, entrambi i controlli generano avvisi; con [`lint.xml`](https://developer.android.com/studio/write/lint) puoi trasformarli in errori tramite `<issue id="HardcodedText" severity="error" />`. Nessuno dei due controlli segnala le chiamate `Text("...")` di Compose, quindi cerca nei file Compose i letterali stringa passati a `Text(`.

**iOS e macOS.** Xcode procede nella direzione opposta. Durante la build, Xcode aggiunge al tuo String Catalog le stringhe passate alle [API localizzabili](https://developer.apple.com/documentation/xcode/preparing-your-apps-text-for-translation). Un letterale stringa in `Text("Title")`, `Button("Checkout")` o `String(localized:)` di SwiftUI viene estratto. Una variabile `String` passata a `Text` viene invece [visualizzata senza localizzazione](https://developer.apple.com/documentation/swiftui/text), così come `Text(verbatim:)`. Dopo una build, quindi, verifica che il catalogo contenga le schermate previste: una voce mancante indica che il testo non è mai passato da un&#39;API localizzabile, spesso perché era memorizzato come semplice `String` anziché come `LocalizedStringResource`. Per individuare lo stesso testo direttamente a schermo, attiva **Show non-localized strings** in **Product &gt; Scheme &gt; Edit Scheme &gt; Run &gt; Options**: durante l&#39;esecuzione dell&#39;app, il testo non localizzato verrà renderizzato tutto in maiuscolo ([Apple: Preparing your interface for localization](https://developer.apple.com/documentation/xcode/preparing-your-interface-for-localization)).

**Rails.** Il linter `HardCodedString` di erb&#95;lint è disattivato per impostazione predefinita; abilitalo sotto `linters:` in `.erb_lint.yml`. i18n-tasks colma una lacuna diversa: il suo comando `missing` segnala le chiavi richiamate dal codice ma assenti in un file dell&#39;impostazione regionale, senza però segnalare il testo letterale nelle viste. (Consulta [General Translation vs. i18n-tasks](/kb/general-translation-vs-i18n-tasks) per capire come si inserisce in una pipeline di traduzione.) Impostando `config.i18n.raise_on_missing_translations = true` nell&#39;ambiente di test, le chiavi mancanti [generano errori](https://guides.rubyonrails.org/i18n.html) anziché renderizzare un messaggio &quot;Translation missing&quot;.

**Qualsiasi base di codice, con ripgrep.** Per una verifica rapida prima di adottare un linter, cerca il testo racchiuso tra i tag e gli attributi che contengono testo:

```bash
# Testo che inizia con una lettera, tra > e < sulla stessa riga
rg -n --glob '*.{jsx,tsx}' '>\s*[A-Za-z][^<>]*<'

# Attributi testuali con valori letterali
rg -n --glob '*.{jsx,tsx}' '\b(placeholder|title|alt|aria-label)="[^"]*[A-Za-z]'
```

Sul componente di esempio, la prima ricerca ha trovato l&#39;intestazione, il messaggio di saluto e il pulsante, mentre la seconda ha individuato entrambi gli attributi. Questo metodo non rileva il testo su una riga a sé e individua anche testo già racchiuso per la traduzione: considera quindi i risultati come un elenco da verificare, non come un controllo bloccante.

## Chiedere a un agente di programmazione di trovare e racchiudere le stringhe

Cursor, Claude Code e agenti simili se la cavano bene con la parte meccanica: leggere un componente, capire cosa è visibile all&#39;utente e riscriverlo usando la tua API i18n. Comunica all&#39;agente le decisioni che non può dedurre da solo e fai del linter il criterio per stabilire quando il lavoro è finito:

```text
Trova le stringhe hardcoded visibili all'utente in src/features/checkout e racchiudile.

- Per visibili all'utente si intendono: testo JSX, valori di placeholder/title/alt/aria-label,
  messaggi toast e messaggi di validazione, ed etichette in costanti renderizzate nella UI.
- Non visibili all'utente: nomi di classi, ID di test, URL, messaggi di log, nomi
  di eventi di analytics e il nome del prodotto "Acme".
- Racchiudi il testo JSX con il componente i18n del progetto, e le props stringa e
  le callback con la funzione restituita dal suo hook i18n. Chiama gli hooks solo al
  livello principale di un componente React o di un hook personalizzato, mai da un helper o
  da un gestore di eventi.
- Per i messaggi a livello di modulo o le utility non React, segui l'API
  documentata per quel framework. Usa src/features/cart/CartSummary.tsx come riferimento.
- Mantieni nomi, prezzi e conteggi come variabili, non come parte del testo.
- Non disabilitare le regole di lint e non aggiungere commenti eslint-disable.
- Al termine, esegui `npx eslint src/features/checkout` e correggi tutti gli
  errori i18next/no-literal-string, poi elenca le stringhe che hai lasciato
  fuori e spiega perché.
```

Poi verifica tu stesso: esegui di nuovo ESLint, incluse le regole di React Hooks, controlla nel diff le stringhe racchiuse per errore (identificatori, valori enum inviati a un&#39;API) e apri le schermate modificate con una pseudo-impostazione regionale o in una seconda lingua. Un&#39;esecuzione di lint senza errori copre solo i modelli rilevati dalle regole abilitate: verifica a parte le costanti e i template literals. Le [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks) di React valgono anche per gli hooks di i18n.

## Eseguire il linting e racchiudere le stringhe React con General Translation

Il plugin ESLint di General Translation, [`@generaltranslation/react-core-linter`](/docs/react/guides/linting-your-code), controlla il codice `gt-react`, `gt-next` e `gt-react-native` già racchiuso: [`static-jsx`](/docs/react/reference/lint-rules#static-jsx) richiede che il contenuto dinamico all&#39;interno di [`<T>`](/docs/react/reference/components/t) sia inserito in un componente variabile come [`<Var>`](/docs/react/reference/components/var), mentre [`static-string`](/docs/react/reference/lint-rules#static-string) richiede l&#39;uso di stringhe statiche nelle funzioni di traduzione. Il plugin non rileva il testo non racchiuso, quindi va abbinato a `no-literal-string`, escludendo [`<T>`](/docs/react/reference/components/t) da quella regola. Le peer dependency del plugin supportano ESLint 8 o 9, quindi installa `eslint@9` insieme a `eslint-plugin-i18next`, `@generaltranslation/react-core-linter`, `typescript` e `typescript-eslint` come dipendenze di sviluppo:

```js title="eslint.config.mjs"
import i18next from 'eslint-plugin-i18next';
import gtLint from '@generaltranslation/react-core-linter';
import tseslint from 'typescript-eslint';

export default [
  {
    files: ['src/**/*.{js,jsx,ts,tsx}'],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: { ecmaFeatures: { jsx: true } },
    },
    plugins: { i18next },
    rules: {
      // Testo non ancora racchiuso in <T>
      'i18next/no-literal-string': [
        'error',
        { mode: 'jsx-only', 'jsx-components': { exclude: ['T'] } },
      ],
    },
  },
  // Contenuto già racchiuso in <T>
  gtLint.configs.recommended,
];
```

`npx eslint src --fix` racchiude un valore dinamico come `{user.name}` presente all&#39;interno di [`<T>`](/docs/react/reference/components/t) in [`<Var>`](/docs/react/reference/components/var) e segnala il testo non racchiuso, così puoi racchiuderlo tu. L&#39;[iniezione automatica di JSX](/docs/cli/guides/using-auto-jsx), disattivata finché non imposti `enableAutoJsxInjection` in `files.gt.parsingFlags` all&#39;interno di `gt.config.json`, racchiude il testo JSX traducibile in [`<T>`](/docs/react/reference/components/t) durante l&#39;esecuzione del compilatore GT. Segui la configurazione della build indicata nel link: Next.js usa il compilatore Babel con `next dev --webpack` e `next build --webpack`, mentre le app React configurano il compilatore per il proprio bundler. Le props di tipo stringa passano comunque da [`useGT`](/docs/react/reference/hooks/use-gt). Per un intero repository, Locadex, l&#39;Agente IA di General Translation, esegue un&#39;automazione **Generate code** che racchiude i contenuti della UI non tradotti e apre una pull request da revisionare. (Consulta [Configurare le automazioni di Locadex](/docs/platform/locadex/guides/configuring-automations).)

## Punti chiave

* Per questa fixture, `no-literal-string` ha segnalato 4 problemi in modalità predefinita e 7 in modalità `jsx-only`. Il template literal e la costante richiedono comunque impostazioni aggiuntive o una revisione.
* `HardcodedText` di Android Lint copre l&#39;XML dei layout e `SetTextI18n` copre `TextView.setText`; nessuno dei due segnala le chiamate `Text` di Compose.
* Xcode estrae le stringhe passate alle API localizzabili, quindi se dopo una build una stringa non compare nello String Catalog, si tratta di testo hardcoded.
* Fornisci a un agente di programmazione un ambito, una definizione di cosa è visibile all&#39;utente e il comando di lint che deve superare, poi verifica l&#39;app in esecuzione.
* Nel codice `gt-react` e `gt-next`, `@generaltranslation/react-core-linter` controlla il contenuto all&#39;interno di [`<T>`](/docs/react/reference/components/t), quindi abbinalo a `no-literal-string` per individuare il testo non ancora racchiuso.

## Related questions

- [Che cos'è la pseudo-localizzazione?](https://generaltranslation.com/it/kb/pseudo-localization.mdx): La pseudo-localizzazione è un test che sostituisce il testo di origine con una versione alterata, con caratteri accentati, allungata di circa il 40% e racchiusa tra parentesi quadre, così che stringhe hardcoded, troncamenti, font che non vengono visualizzati correttamente e frasi concatenate emergano prima ancora che esista una traduzione. Android include a questo scopo le pseudo-impostazioni regionali en-XA e ar-XB.
- [General Translation vs. i18n-tasks: quale scegliere?](https://generaltranslation.com/it/kb/general-translation-vs-i18n-tasks.mdx): i18n-tasks è una gem Ruby che individua le chiavi Rails I18n mancanti e inutilizzate, riordina i file delle impostazioni regionali e può compilare le chiavi mancanti tramite Google Translate, DeepL, OpenAI, Yandex o watsonx. General Translation traduce interi file delle impostazioni regionali con il tuo Glossario, i prompt personalizzati e la revisione nella dashboard. Svolgono compiti diversi e molti team possono usarli entrambi.
- [Checklist per il test di localizzazione delle app multilingue](https://generaltranslation.com/it/kb/multilingual-app-launch-checklist.mdx): Verifica nove aspetti in ogni impostazione regionale supportata su una build di produzione: percorsi completi, inclusi gli stati di errore e le email, cambio di lingua, plurali, valuta e fusi orari, espansione del testo, layout right-to-left, accessibilità, distribuzione delle traduzioni e URL per lingua scansionabili. Assegna a ogni verifica un criterio di superamento esplicito.
- [Posso fare i18n senza file di traduzione?](https://generaltranslation.com/it/kb/i18n-without-translation-files.mdx): Sì. Le librerie gt-react e gt-next di General Translation gestiscono l'i18n in React e Next.js senza chiavi di traduzione e senza dover mantenere un file JSON per ogni lingua. I testi restano inline nel JSX all'interno del componente `<T>` e il comando gt translate genera le traduzioni per ogni lingua durante la build, sfruttando il contesto del tuo codice. Locadex può mantenerle aggiornate tramite pull request.
- [Come si traduce il testo condizionale in JSX?](https://generaltranslation.com/it/kb/translate-conditional-jsx.mdx): Mantieni la frase intera e traducine ogni versione, perché un operatore ternario la spezza in frammenti che perdono il loro significato e la loro posizione nell'ordine delle parole di ciascuna lingua. General Translation lo fa con `<Branch>` all'interno di `<T>`, che traduce la frase come un'unica unità.

## Previous and next questions

- Previous question: [Come si traducono i sottotitoli SRT senza alterare la temporizzazione?](https://generaltranslation.com/it/kb/translate-srt-subtitles.mdx)
- Next question: [Come tradurre Markdown e MDX senza compromettere la sintassi?](https://generaltranslation.com/it/kb/translate-markdown-files.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/it/kb/sitemap.md) for every article.
