createInstance mixing up instances
🐛 Bug Report
We're calling createInstance each time we're sending an email for different store and load that store's translation into the instance then use the text, but we have got the reports that translations are mixing up some stores that are missing translations instead of falling back to the default JSON object it sends translation from another random store which we suppose might be the last store that sent email.
To Reproduce
import * as Sentry from '@sentry/node'
import i18next, { InitOptions, i18n } from 'i18next'
import Backend from 'i18next-fs-backend'
import enUS from '../lang/en-US.json'
import esES from '../lang/es-ES.json'
import frCA from '../lang/fr-CA.json'
import { StoreType } from '../models'
import { Localization } from '../models/localization'
const DEFAULT_LOCALE = 'en-US'
async function fetchStoreTranslations(
storeId: string,
language: string = DEFAULT_LOCALE,
): Promise<Record<string, unknown> | null> {
if (!storeId) {
return null
}
try {
const localization = await Localization.get({
store_id: storeId,
locale: language,
})
return localization?.texts || null
} catch (err) {
Sentry.captureException(err)
return null
}
}
async function createInstance(): Promise<i18n> {
const instance = i18next.createInstance()
await instance.use(Backend).init({
debug: process.env.NODE_ENV === 'development',
lng: DEFAULT_LOCALE,
fallbackLng: DEFAULT_LOCALE,
resources: {
'en-US': { translation: enUS },
'fr-CA': { translation: frCA },
'es-ES': { translation: esES },
},
returnNull: false,
interpolation: {
escapeValue: false,
},
saveMissing: true,
missingKeyHandler: (lngs: readonly string[], ns: string, key: string) => {
const message = `Missing translation key: ${key} for language: ${lngs.join(',')}`
Sentry.captureMessage(message)
if (process.env.NODE_ENV === 'development') {
console.warn(message)
}
},
} as InitOptions)
return instance
}
export async function getTranslator(store: StoreType): Promise<i18n> {
if (!store || !('store_id' in store) || !('language' in store)) {
Sentry.captureMessage('Store object must have store_id and language properties, using defaults')
}
const instance = await createInstance()
const storeTranslations = await fetchStoreTranslations(
store.store_id,
store.locales?.defaultLocale || store.language,
)
if (storeTranslations) {
instance.addResourceBundle(store.language, 'translation', storeTranslations, true, true)
}
return instance
}
and used like this
const { t } = await getTranslator(store)
await firebase.notifyStoreCustomer(store, customer, {
type: NOTIFICATION_TYPES.ORDERS,
title: t('notifications.orders.completed.delivery.title'),
body: t('notifications.orders.completed.delivery.title'),
})
Expected behavior
It should have sent the notification/email with the right text, previously we were using cloneInstance that might had issue but then we changed it to createInstance but we got the issues again now we're using namespaces we're not expecting issues but we don't know what was wrong in this impelmentation that I report.
Your Environment
- runtime version: 20.19.4
- i18next version: 25.3.2
- i18next-fs-backend: 2.6.0
- os: Linux
- It's a webhook services and has a lot of load
This seems to be a very strange setup... You are using i18next-fs-backend but also using addResourceBundle.... etc....
Can you please isolate only i18next related code? And please provide a complete but minimal reproducible example repository or codesandbox... not just code snippets... We need to be able to reproduce this locally...
After that we can see where the issue could be and try to address this... but we need a locally reproducible example first. Possible solutions could then be:
- Remove Backend Plugin
- Initialize with Complete Resources
- Use Namespaces (as you mentioned trying)
I have removed the backend plugin as we don't need it but I did not get the initialize with Complete Resources.
For reproducability I'm not able to reproduce it but we have been reported the scenarios, my assumption is as there is too much load on webhook service could it be causing some issues, still don't know but as I said we're not expecting issues with name spaces now but if any we'll report. Also since we're creating an instance for each store do you suggest to create a global instance and just put the each store's translation into it's own namespace? our only concern is the memory will grow too much over time, but currently our all texts combined might be under 10kb
Thanks
not easy to say without being able to track the real root cause.... but given your current load (10kb total) and the concurrency issues you're experiencing, I'd actually try the global instance approach.