Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ChainedBackendConfig,
} from '../../../shared/type';
import type { I18nInstance } from '../instance';
import { getActualI18nextInstance } from '../instance';
import { SdkBackend } from './sdk-backend';

type BackendConfigWithChained = BaseBackendOptions &
Expand Down Expand Up @@ -53,6 +54,13 @@ function setupChainedBackend(
BackendWithSave: new (...args: any[]) => any,
) {
i18nInstance.use(ChainedBackend);
const actualInstance = getActualI18nextInstance(i18nInstance);
if (actualInstance?.options) {
actualInstance.options.backend = buildChainedBackendConfig(
backend,
BackendWithSave,
);
}
if (i18nInstance.options) {
i18nInstance.options.backend = buildChainedBackendConfig(
backend,
Expand Down Expand Up @@ -97,8 +105,10 @@ export function useI18nextBackendCommon(
return i18nInstance.use(SdkBackend);
}

// For non-chained backend, we still need to set the backend config
// so that init() can use it to load resources
const actualInstance = getActualI18nextInstance(i18nInstance);
if (actualInstance?.options) {
actualInstance.options.backend = cleanBackendConfig(backend);
}
if (i18nInstance.options) {
i18nInstance.options.backend = cleanBackendConfig(backend);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,43 @@ interface BackendOptions {
[key: string]: unknown;
}

interface I18nextServices {
resourceStore?: {
data?: {
[language: string]: {
[namespace: string]: Record<string, string>;
};
};
};
store?: {
data?: {
[language: string]: {
[namespace: string]: Record<string, string>;
};
};
};
[key: string]: any;
}

export class SdkBackend {
static type = 'backend';
type = 'backend' as const;
sdk?: I18nSdkLoader;
private allResourcesCache: Resources | null = null;
private loadingPromises = new Map<string, Promise<unknown>>();
private services?: I18nextServices;

constructor(_services: unknown, _options: Record<string, unknown>) {
void _services;
void _options;
}

init(
_services: unknown,
services: I18nextServices,
backendOptions: BackendOptions,
_i18nextOptions: unknown,
): void {
void _services;
this.services = services;
void _i18nextOptions;
this.sdk = backendOptions?.sdk;
if (!this.sdk) {
Expand All @@ -48,7 +67,13 @@ export class SdkBackend {
? this.extractFromCache(language, namespace)
: null;
if (cached !== null) {
callback(null, cached);
// Merge cached data with existing store data to preserve HTTP backend data
const mergedData = this.mergeWithExistingResources(
language,
namespace,
cached,
);
callback(null, mergedData);
return;
}

Expand Down Expand Up @@ -125,11 +150,18 @@ export class SdkBackend {
promise
.then(data => {
const formattedData = this.formatResources(data, language, namespace);
// Merge with existing resources in store to preserve data from other backends (e.g., HTTP backend)
// This is important when using refreshAndUpdateStore mode in chained backend
const mergedData = this.mergeWithExistingResources(
language,
namespace,
formattedData,
);
if (shouldUpdateCache) {
this.updateCache(language, namespace, formattedData);
this.updateCache(language, namespace, mergedData);
this.loadingPromises.delete(cacheKey);
}
callback(null, formattedData);
callback(null, mergedData);
this.triggerI18nextUpdate(language, namespace);
})
.catch(error => {
Expand Down Expand Up @@ -231,6 +263,24 @@ export class SdkBackend {
return value !== null && typeof value === 'object';
}

private mergeWithExistingResources(
language: string,
namespace: string,
sdkData: Record<string, string>,
): Record<string, string> {
// Get existing resources from store (may contain data from HTTP backend)
const store = this.services?.resourceStore || this.services?.store;
const existingData =
store?.data?.[language]?.[namespace] || ({} as Record<string, string>);

// Merge: preserve existing data (from HTTP backend), add/update with SDK data
// This ensures that when using refreshAndUpdateStore, HTTP backend data is not lost
return {
...existingData,
...sdkData,
};
}

private triggerI18nextUpdate(language: string, namespace: string): void {
if (typeof window !== 'undefined') {
const event = new CustomEvent('i18n-sdk-resources-loaded', {
Expand Down
Loading