如何让Strapi国际化使用默认语言数据填充未本地化内容?
Strapi多语言数据默认 fallback 实现方案
针对你遇到的「未填充本地化内容时,切换语言无法查看数据」的问题,有两种实用方案可以实现默认语言数据自动补全:
方案一:自定义内容类型控制器(单类型适配)
这种方式针对单个内容类型(比如你的Appointments)做定制化处理,灵活可控。
- 找到对应控制器文件:
src/api/appointment/controllers/appointment.js(TypeScript项目为.ts) - 重写
find和findOne方法,实现 fallback 逻辑:
'use strict'; const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::appointment.appointment', ({ strapi }) => ({ // 列表查询 fallback async find(ctx) { const defaultLocale = strapi.plugin('i18n').service('locales').getDefaultLocale().code; const requestedLocale = ctx.query.locale || defaultLocale; // 先尝试获取请求语言的数据 let { data, meta } = await super.find(ctx); // 如果请求语言无数据,返回默认语言数据 if (data.length === 0) { ctx.query.locale = defaultLocale; ({ data, meta } = await super.find(ctx)); // 可选:标记数据来自默认语言 fallback data = data.map(item => ({ ...item, attributes: { ...item.attributes, __fallback_from: defaultLocale } })); } return { data, meta }; }, // 单条查询 fallback(字段级补全) async findOne(ctx) { const defaultLocale = strapi.plugin('i18n').service('locales').getDefaultLocale().code; const requestedLocale = ctx.query.locale || defaultLocale; // 获取请求语言的条目 let { data } = await super.findOne(ctx); // 如果请求语言无此条目,返回默认语言版本 if (!data) { ctx.query.locale = defaultLocale; ({ data } = await super.findOne(ctx)); return { data }; } // 获取该条目的默认语言版本,补全空字段 const defaultEntry = await strapi.db.query('api::appointment.appointment').findOne({ where: { id: data.id, locale: defaultLocale }, populate: ctx.query.populate }); if (defaultEntry) { const mergedAttrs = { ...defaultEntry.attributes }; // 优先保留请求语言已填充的字段,空字段用默认语言补全 Object.keys(data.attributes).forEach(key => { if (data.attributes[key] != null && data.attributes[key] !== '') { mergedAttrs[key] = data.attributes[key]; } }); data.attributes = mergedAttrs; } return { data }; } }));
方案二:全局中间件(全类型适配)
如果需要所有本地化内容类型都生效,可以用全局中间件统一处理:
- 创建中间件文件:
src/middlewares/locale-fallback.js
'use strict'; module.exports = (config, { strapi }) => { return async (ctx, next) => { await next(); // 仅处理API响应且带locale参数的请求 if (!ctx.response.body?.data || !ctx.query.locale) return; const defaultLocale = strapi.plugin('i18n').service('locales').getDefaultLocale().code; const requestedLocale = ctx.query.locale; if (requestedLocale === defaultLocale) return; const entityType = ctx.route.info.apiName; // 处理列表请求 if (Array.isArray(ctx.response.body.data)) { // 列表为空时,返回默认语言数据 if (ctx.response.body.data.length === 0) { const defaultData = await strapi.entityService.findMany(entityType, { ...ctx.query, locale: defaultLocale }); ctx.response.body = defaultData; return; } // 字段级补全 for (const item of ctx.response.body.data) { const defaultEntry = await strapi.entityService.findOne(entityType, item.id, { locale: defaultLocale, populate: ctx.query.populate }); if (defaultEntry) { Object.keys(defaultEntry.attributes).forEach(key => { if (!item.attributes[key] || item.attributes[key] === '') { item.attributes[key] = defaultEntry.attributes[key]; } }); } } } else { // 处理单条请求 let data = ctx.response.body.data; if (!data) { const defaultEntry = await strapi.entityService.findOne(entityType, ctx.params.id, { ...ctx.query, locale: defaultLocale }); ctx.response.body = { data: defaultEntry }; return; } const defaultEntry = await strapi.entityService.findOne(entityType, data.id, { locale: defaultLocale, populate: ctx.query.populate }); if (defaultEntry) { Object.keys(defaultEntry.attributes).forEach(key => { if (!data.attributes[key] || data.attributes[key] === '') { data.attributes[key] = defaultEntry.attributes[key]; } }); } } }; };
- 在
config/middlewares.js中注册中间件:
module.exports = [ 'strapi::errors', 'strapi::security', 'strapi::cors', 'strapi::poweredBy', 'strapi::logger', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', 'global::locale-fallback' // 添加此行注册中间件 ];
注意事项
- 确保默认语言在Strapi后台的i18n设置中已正确配置
- 中间件方案会对所有API生效,若需排除特定内容类型,可在中间件中添加判断逻辑
- 字段补全逻辑可根据实际需求调整(比如只针对特定字段做fallback)
内容的提问来源于stack exchange,提问作者Tolis Oug
相关产品推荐
相关产品推荐

