StringLane

Browse docsFind Keys Your Code No Longer Uses
How-To

Find Keys Your Code No Longer Uses

StringLane reads your source and shows where each key is called, plus a filter for the ones nothing calls. What the scan can see, the four ways a live key lands in that list, and why not scanned is different from nothing found.

Last updated

Every project accumulates them. A screen is redesigned, a feature is cut, a key is renamed and the old one stays behind — still translated into every language you support, still counted in your completion percentage, still sent to your AI provider on the next run.

StringLane reads your source code after a project opens and answers two questions: where is this key called, and which keys are called nowhere.

Where one key is used#

The detail pane shows it under the key name.

The homeWelcomeBack key. Under the description it reads Used in 3 places, followed by three monospace paths: lib/screens/home_screen.dart:10, :14 and :20

Used in N places, then the file and line of each call site. Past the first ten, Show all opens the rest.

When nothing calls it, the pane says so — and says what that does and does not mean:

The brandSlogan key, whose description reads Marketing strap-line, Ledger is brand, must not translate. Under it: No call site found, then The scanner did not find this key in your code. It can still be live, if its name is built at runtime, or it is read somewhere the scanner cannot see. Check before deleting.

That caveat sits beside the finding rather than in this page, because it is the finding people act on.

Filtering the sidebar to unused keys#

Open the filter popover in the key sidebar and turn on Show only unused.

The sidebar filter popover: a Show only issues checkbox, a checked Show only unused checkbox, and a note reading Based on 2 source files. A key can be live and still appear in this list, if its name is built at runtime, or read somewhere the scanner cannot see. Check before deleting. Below is a Missing in locale dropdown set to Any locale

The line under the toggle is the important one. It says how many source files the answer is based on, so you can tell an answer from an absence of one.

The key sidebar with an Unused chip and a clear link at the top, reading Showing 22 of 30 keys, and the key tree narrowed to the unused keys grouped by namespace

An Unused chip stays visible while the filter is on, with the count beside it, so a filtered list is never mistaken for the whole project.

Not scanned is not the same as nothing found#

A project StringLane has not read yet says not scanned — never "used in 0 places".

Those two look identical in a list and mean opposite things, and only one of them is a reason to delete something. If StringLane read no source files at all, or read them and recognised no keys in them, it tells you that and reports nothing as unused rather than reporting everything.

The scan starts after your project opens, not before, so it never delays the editor.

What the scan can see#

Your codeCall shapes it matches
DartAppLocalizations.of(context)!.key, context.l10n.key, S.of(context).key
JavaScript, TypeScript, Vue, Sveltet('key') and its i18n.t / i18next.t / $t spellings, plus <Trans i18nKey="…">
Swift, Objective-CNSLocalizedString("key", …), String(localized: "key"), SwiftGen's L10n.some.path
Kotlin, Java, Android XMLR.string.key, and @string/key in layouts and manifests

Four ways a key can be live and still appear in the unused list:

  • its name is built at runtimet('errors.' + code) is invisible to any scanner;
  • it is called through a wrapper StringLane does not recognise;
  • it is named only in a file type the scan does not read;
  • it is read directly out of the locale file by the platform, which is how iOS InfoPlist.strings and some Android resources work.

A common Flutter case worth knowing: final l10n = AppLocalizations.of(context)! followed by l10n.homeTitle assigns to a local variable, and the scan sees the assignment rather than the key. Those keys report as unused.

Silencing a key that is built at runtime#

For a key you have checked and know is live, the panel has a decision rather than a dismissal. Open Key Metadata for it and tick Called dynamically, under Code usage. The key stops being reported as unused, and the detail pane says Not reported as unused in place of the usual note, so a later session does not re-litigate a question you have already answered.

It is a record in your repository, not a local preference: x-dynamic-key in the @key block on ARB, and in .stringlane/metadata.yaml on the other four formats. That means it is committed, it applies to everyone on the team, and stringlane scan and stringlane check honour it too, because both read the same metadata. Unticking it drops the field rather than recording a false.

For a whole family of runtime-built keys, a pattern is the better tool: see ignore patterns in the CLI, which stringlane.yaml carries for both surfaces.

Before you delete anything#

  1. Search for the key yourself, including any runtime spelling that differs from the one shown.
  2. Never delete keys in the same change as anything else. A deletion that turns out to be wrong needs to be revertible on its own.

The same answer is available outside the app: stringlane scan from a terminal, and the read_key_usage tool for a coding agent. See Unused keys in the CLI.

Frequently asked questions

How do I find unused translation keys in StringLane?
Open the filter popover in the key sidebar and turn on Show only unused. The list narrows to keys no call site in your source code names, and an Unused chip stays visible so you always know the list is filtered. Treat it as a starting point for a search rather than a delete queue.
Is a key StringLane reports as unused safe to delete?
Not on its own. Not referenced means no call site was found, not that the key is dead. A key whose name is built at runtime, called through a wrapper StringLane does not recognise, named only in a file type it does not read, or read straight out of the locale file by the platform will appear in that list and still be live. Search for it before deleting, and never delete keys in the same change as anything else.