refactor: decouple core-storage services from static keys and update i18n setup to use configurable storage adapters

This commit is contained in:
Firman Ramdhani
2026-05-28 13:27:13 +07:00
parent b4af762baa
commit d0ebceb1bb
20 changed files with 377 additions and 375 deletions
+7 -3
View File
@@ -1,5 +1,5 @@
import i18n from 'i18next';
import { secureStorage, StorageKey } from '@repo/core-storage';
import { globalStorageAdapter } from './setup';
/**
* Changes the active language, saves the preference locally, and optionally syncs with the backend.
@@ -16,7 +16,9 @@ export async function changeLanguage(
if (prevLng === newLng) return;
// 1. Update local storage & i18next optimistically
await secureStorage.setItem(StorageKey.LOCALE, newLng);
if (globalStorageAdapter) {
await globalStorageAdapter.setLanguage(newLng);
}
await i18n.changeLanguage(newLng);
// 2. Trigger optional backend sync
@@ -26,7 +28,9 @@ export async function changeLanguage(
} catch (error) {
console.error('[i18n] Backend sync failed, rolling back language', error);
// Rollback on failure
await secureStorage.setItem(StorageKey.LOCALE, prevLng);
if (globalStorageAdapter) {
await globalStorageAdapter.setLanguage(prevLng);
}
await i18n.changeLanguage(prevLng);
throw error; // Rethrow so the caller can show an error toast
}
+21 -7
View File
@@ -1,6 +1,5 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { secureStorage, StorageKey } from '@repo/core-storage';
import commonEn from './locales/en/common.json';
import commonId from './locales/id/common.json';
@@ -14,18 +13,33 @@ export const resources = {
id: { common: commonId.common },
} as const;
export interface I18nStorageAdapter {
getLanguage(): Promise<string | null>;
setLanguage(lng: string): Promise<void>;
}
export interface I18nConfig {
storageAdapter?: I18nStorageAdapter;
}
// Store the adapter module-wide so manager.ts can access it
export let globalStorageAdapter: I18nStorageAdapter | undefined;
/**
* Bootstraps the central i18n engine.
*
* This reads the preferred locale from secureStorage and initializes
* i18next synchronously before React renders.
* It accepts an optional storage adapter to read the initial language.
*/
export async function setupI18n(): Promise<void> {
export async function setupI18n(config: I18nConfig = {}): Promise<void> {
globalStorageAdapter = config.storageAdapter;
let initialLng = DEFAULT_LANGUAGE;
try {
const storedLng = await secureStorage.getItem<string>(StorageKey.LOCALE);
if (storedLng && SUPPORTED_LANGUAGES.includes(storedLng as SupportedLanguage)) {
initialLng = storedLng;
if (globalStorageAdapter) {
const storedLng = await globalStorageAdapter.getLanguage();
if (storedLng && SUPPORTED_LANGUAGES.includes(storedLng as SupportedLanguage)) {
initialLng = storedLng;
}
}
} catch (err) {
console.warn('[i18n] Failed to read locale from storage', err);