feat: implement core-i18n package with decentralized namespace support and integrate into landing and web apps

This commit is contained in:
Firman Ramdhani
2026-05-22 23:30:41 +07:00
parent 255704f867
commit 4655362ff4
25 changed files with 932 additions and 13 deletions
+56
View File
@@ -0,0 +1,56 @@
import i18n from 'i18next';
import { demoSecureStorage, StorageKey } from '@repo/core-storage';
/**
* Changes the active language, saves the preference locally, and optionally syncs with the backend.
*
* @param newLng The new language code (e.g., 'en', 'id')
* @param syncCallback An optional callback to sync the preference to the backend. It receives the new language and the previous language.
*/
export async function changeLanguage(
newLng: string,
syncCallback?: (newLng: string, prevLng: string) => Promise<void>
): Promise<void> {
const prevLng = i18n.language;
if (prevLng === newLng) return;
// 1. Update local storage & i18next optimistically
await demoSecureStorage.setItem(StorageKey.LOCALE, newLng);
await i18n.changeLanguage(newLng);
// 2. Trigger optional backend sync
if (syncCallback) {
try {
await syncCallback(newLng, prevLng);
} catch (error) {
console.error('[i18n] Backend sync failed, rolling back language', error);
// Rollback on failure
await demoSecureStorage.setItem(StorageKey.LOCALE, prevLng);
await i18n.changeLanguage(prevLng);
throw error; // Rethrow so the caller can show an error toast
}
}
}
/**
* Injects tenant-specific vocabulary overrides dynamically at runtime.
*
* Uses a deep-merge strategy. Overrides are applied to the currently active language,
* or across all loaded languages if needed.
*
* @param namespace The i18n namespace to override (e.g., 'common', 'booking')
* @param overrides A deeply nested object containing the overridden string keys and values.
* @param lng Specific language to override. Defaults to currently active language.
*/
export function applyTenantOverrides(
namespace: string,
overrides: Record<string, unknown>,
lng?: string
): void {
const targetLng = lng || i18n.language;
// deep: true -> merges with existing keys rather than replacing the whole namespace
// overwrite: true -> allows replacing existing specific keys
i18n.addResourceBundle(targetLng, namespace, overrides, true, true);
}