DEV Community

Managing i18n keys in React without losing your mind (without leaving your IDE)

t('common:user.profile.title') โ€ฆ and now you just hope that key actually exists.

On any serious i18next project, you end up typing keys blind. Does the key exist? In which namespace? What's its value? Is it translated in every language, or just English? To find out, you open en/common.json, hit Ctrl+F, scroll, come back. Multiply by 200 keys and 5 locales and it's a daily tax on your focus.

The real problem isn't i18next. It's that your keys and your translations live in files your editor treats as dead text. A string in the code, a JSON next door, and zero link between the two.

I've been maintaining and heavily extending a JetBrains plugin (I18n Support Plus) to restore that link. The idea fits in one sentence: your i18n keys become first-class references in the IDE, exactly like code symbols. Here's concretely what that changes.

1. See a key's status without opening a single file

The plugin colors each key by its resolution state, right in the code:

  • key resolved in every locale
  • a segment that doesn't exist (user.profil instead of user.profile)
  • namespace file not found
  • key pointing to an object instead of a value
  • key translated in some locales only (partial translation)

A gutter badge (โœ… / โš ๏ธ / โŒ) gives the verdict at a glance. A red key is a bug caught before the commit, not in QA.

2. Ctrl+Click โ†’ the translation. And back again.

Ctrl+Click on a key takes you straight to the value in the right JSON/YAML file, even if the key is only partially resolved (it jumps to the deepest resolved node). And it works both ways: from a translation entry, you find where it's used in the code. No more "is this key still used anywhere?".

3. Autocomplete knows your keys

As you type t(', the plugin suggests the project's real keys, namespaces included. You no longer guess, you pick. Suggestions come from all your translation files, not a stale index. Both useTranslation('common') and useTranslation(['common', 'auth']) are handled: completion knows which namespace you're in. Even dynamic keys like t(`common:role.${role}`) are resolved through wildcards instead of being flagged as broken.

4. Read the code with the actual sentences

Two ways to stop reading cryptic keys:

  • Inlay hints: the translated value shows up in grey right after the key, without touching the file.
  • Code folding (Ctrl+Alt+Shift+T): replaces the key with its translation inline. Your JSX reads like the real UI.

And on hover (Ctrl+hover), a tooltip shows all locales side by side, missing ones marked -, with a direct link to the file.

5. Extract a hardcoded string in one keystroke

Wrote <h1>User profile</h1> hardcoded? Alt+Enter โ†’ Extract i18n key. The plugin:

  • creates the key in your translation file(s)
  • lets you pick/create the namespace on the fly
  • replaces the string with the matching t() call
  • can insert the key alphabetically (option)

This is the gesture that turns "I'll deal with i18n later" into "it's already done".

6. The translation dashboard

A dedicated tool window pulls it all together:

  • Tree view: the key hierarchy, color-coded nodes (red = missing, orange = empty), double-click to edit.
  • Table view: one column per locale + a usage counter. The Scan Orphans button lists keys no code uses anymore: right-click to delete them.
  • Keys Synchronizer: a key exists in en.json but not fr.json? One click propagates it across all locales, with a dialog to fill in the missing values in bulk.
  • Stats: completion rate per locale (total, translated, missing, %).

Two built-in inspections keep the files clean: empty value (flag a key that was created but never translated) and duplicate value (spot copy-paste mistakes within a locale).

This is where you go from "editing files" to driving the actual state of your localization.

7. Refactor keys like real code

i18n refactoring becomes as safe as refactoring code:

  • Rename (Shift+F6): renames a key across all translation files and every reference in the source at once.
  • Move to another namespace (Ctrl+Alt+Shift+M): moves a key from one namespace to another atomically. It copies the value into the target file for every locale (creating it if missing, so no locale is dropped), deletes the old entry, and rewrites every code reference with the right ns: prefix, all in a single undo step.
  • Sort alphabetically: reorders a JSON translation file (recursively), or auto-sorts on every key insertion.

It works with your stack

Frameworks i18next / react-i18next, vue-i18n, lingui
Languages JS, TS, JSX, TSX, Vue SFC, PHP
Formats JSON / JSON5, YAML, PO/POT (gettext), i18next TS config

On first launch, a setup wizard auto-detects your framework and scans your locales/, i18n/, translations/ folders. Three steps and you're ready.

Wrapping up

The day-to-day pain of i18n isn't translating: it's that the IDE draws no link between your keys and your translations. The whole plugin follows from a single idea: An i18n key should behave like a code symbol: you resolve it, navigate to it, complete it, rename it, and see its value.

If you touch i18next/vue-i18n/lingui in a JetBrains IDE, give it a try โ†’ I18n Support Plus on the Marketplace. It's free, and the code is on GitHub (a โญ helps if it saves you time).

The real question isn't "which i18n library should I pick?". It's: "How many times a day do I open a JSON file just to check whether a key exists?" If the answer isn't "never", this plugin is for you.

I build tools that cut the daily friction of front-end work. Let's talk on LinkedIn.

Comments

No comments yet. Start the discussion.