Observed since 20 Oct, 22:34 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Strapi custom field for per-locale translations without full i18n localization
A Strapi 5 custom field that stores per-locale translations as JSON — without requiring full i18n content localization. Perfect for fields like SEO titles, button labels, or any text that needs translations while the entry itself stays in a single locale.
?locale= query param?plugins[localized-field][populateLocalization]=truereact-intl for internationalizationnpm install @0x30-ch/localized-field
# or
yarn add @0x30-ch/localized-field
# or
pnpm add @0x30-ch/localized-field
// config/plugins.ts
export default () => ({
'localized-field': {
enabled: true,
resolve: './node_modules/@0x30-ch/localized-field',
},
});
In the Strapi Content-Type Builder, add the Localized string custom field to your content type. Configure:
| Option | Values | Default | Description |
|---|---|---|---|
| Field type | Short text, Long text | Short text | Single-line input or textarea |
| Default locale | Any locale code | (auto-detected) | Override which locale tab opens first and is used for fallback |
| Fallback strategy | Default locale, First available, None | Default locale | How to resolve when the requested locale has no translation |
After configuration, restart your Strapi server to apply the schema changes.
2/4)The plugin registers a global Koa middleware that automatically resolves localized fields in content API responses.
Pass ?locale= to get the resolved string value instead of the raw JSON object:
# Returns resolved string for French
GET /api/tags?locale=fr
Response:
{
"data": [
{
"id": 1,
"label": "Bonjour"
}
]
}
Add the populateLocalization plugin param to receive all locale values:
GET /api/tags?locale=fr&plugins[localized-field][populateLocalization]=true
Response:
{
"data": [
{
"id": 1,
"label": "Bonjour",
"label_localizations": {
"en": "Hello",
"fr": "Bonjour",
"de": "Hallo"
}
}
]
}
When no ?locale= is provided, the raw JSON object is returned as-is:
{
"data": [
{
"id": 1,
"label": {
"en": "Hello",
"fr": "Bonjour",
"de": "Hallo"
}
}
]
}
| Strategy | Behavior |
|---|---|
| Default locale | Falls back to the configured default locale (or Strapi's i18n default) |
| First available | Returns the first non-empty translation found |
| None | Returns null if the requested locale has no translation |
The field stores a JSON object mapping locale codes to translation strings:
{
"en": "Hello world",
"fr": "Bonjour le monde",
"de": "Hallo Welt"
}
This is stored in a single json column in the database — no additional tables or relations needed.
git clone https://github.com/0x30-ch/strapi-plugins.git
cd strapi-plugins
pnpm install
# Start development
pnpm dev
# Build plugin
cd packages/localized-field
npm run build
# Type checking
npm run test:ts:front # Frontend types
npm run test:ts:back # Backend types
# Verify plugin structure
npm run verify
packages/localized-field/
├── admin/ # Frontend (React + Strapi Design System v2)
│ ├── src/
│ │ ├── components/
│ │ │ ├── LocalizedInput.tsx # Main input with locale tabs
│ │ │ └── LocalizedFieldIcon.tsx # Field icon for content-type builder
│ │ ├── utils/
│ │ │ └── getTranslation.ts # i18n helper
│ │ ├── translations/
│ │ │ └── en.json # English translations
│ │ ├── pluginId.ts # Plugin identifier
│ │ └── index.ts # Plugin registration + custom field config
│ └── tsconfig.json
├── server/ # Backend logic
│ ├── src/
│ │ ├── register.ts # Custom field server registration
│ │ ├── bootstrap.ts # Global middleware for API resolution
│ │ ├── utils/
│ │ │ └── localized.ts # Core logic (collect, resolve, fallback)
│ │ ├── config/ # Plugin configuration
│ │ ├── controllers/ # (empty — no admin routes needed)
│ │ ├── services/ # (empty — logic in middleware)
│ │ └── routes/ # (empty — no custom routes)
│ └── tsconfig.json
└── package.json
localizedString custom field of type jsonLocalizedInput component fetches available locales from /i18n/locales and renders compact tab chips with an input per locale?locale= query parameterStrapi's built-in i18n duplicates the entire entry per locale. This plugin is for cases where:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)MIT License - see LICENSE file for details.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.0.0Registry latest | 9 Feb 2026 | 12 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:34 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:babd385248ec0ea5f0e1f596ced2f2c1fb542c027623307066ecac1460881a95