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
+3
View File
@@ -12,11 +12,14 @@
},
"dependencies": {
"@repo/core-api": "workspace:*",
"@repo/core-i18n": "workspace:*",
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*",
"@tailwindcss/vite": "^4.1.18",
"i18next": "^24.2.2",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-i18next": "^15.4.0",
"tailwindcss": "^4.1.18"
},
"devDependencies": {
+2
View File
@@ -1,6 +1,7 @@
import { ThemeProvider } from '@repo/ui/provider';
import { Button } from '@repo/ui/components';
import LandingSample from './features/public-content/presentation/LandingSample';
import I18nLandingSample from './presentation/I18nLandingSample';
export default function App() {
return (
@@ -36,6 +37,7 @@ export default function App() {
<h1 className="text-2xl font-bold mb-4">Enterprise Web App</h1>
<LandingSample />
</div>
<I18nLandingSample />
</div>
</ThemeProvider>
);
+4
View File
@@ -0,0 +1,4 @@
{
"welcome": "Welcome to Our Product",
"cta": "Get Started Now"
}
+4
View File
@@ -0,0 +1,4 @@
{
"welcome": "Selamat Datang di Produk Kami",
"cta": "Mulai Sekarang"
}
+12 -5
View File
@@ -15,10 +15,17 @@ initTelemetry({
import './main.css';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { setupI18n } from '@repo/core-i18n';
import App from './app';
createRoot(document.getElementById('app')!).render(
<StrictMode>
<App />
</StrictMode>,
);
async function bootstrap() {
await setupI18n();
createRoot(document.getElementById('app')!).render(
<StrictMode>
<App />
</StrictMode>,
);
}
bootstrap();
@@ -0,0 +1,50 @@
import { useEffect } from 'react';
import { useTranslation, changeLanguage, i18n } from '@repo/core-i18n';
// Decentralized locale imports
import homeId from '../locales/id/home.json';
import homeEn from '../locales/en/home.json';
export default function I18nLandingSample() {
const { t } = useTranslation(['common', 'home']);
// 1. Lazy-load the 'home' namespace when the module mounts
useEffect(() => {
i18n.addResourceBundle('id', 'home', homeId, true, false);
i18n.addResourceBundle('en', 'home', homeEn, true, false);
}, []);
// 2. Change language without syncCallback to prove decoupling
const setLanguage = (lng: string) => {
// We intentionally omit the second argument (syncCallback)
// because the landing page is public and doesn't need backend syncing.
changeLanguage(lng).catch(console.error);
};
return (
<div style={{ padding: 40, textAlign: 'center', backgroundColor: '#f8fafc', color: '#0f172a' }}>
<h1 style={{ fontSize: 36, fontWeight: 'bold', marginBottom: 16 }}>
{t('home:welcome')}
</h1>
<div style={{ marginBottom: 32 }}>
<button
onClick={() => setLanguage('id')}
style={{ padding: '8px 16px', marginRight: 8, cursor: 'pointer', borderRadius: 4, background: '#3b82f6', color: 'white', border: 'none' }}
>
Bahasa Indonesia
</button>
<button
onClick={() => setLanguage('en')}
style={{ padding: '8px 16px', cursor: 'pointer', borderRadius: 4, background: '#3b82f6', color: 'white', border: 'none' }}
>
English
</button>
</div>
<button style={{ padding: '16px 32px', fontSize: 18, fontWeight: 'bold', cursor: 'pointer', borderRadius: 8, background: '#10b981', color: 'white', border: 'none' }}>
{t('home:cta')}
</button>
</div>
);
}
+14
View File
@@ -0,0 +1,14 @@
import 'react-i18next';
// Import the core types so we don't break the common namespace
import type { resources as coreResources } from '@repo/core-i18n/src/setup';
import homeEn from '../locales/en/home.json';
// Combine core resources with app-specific decentralized resources
declare module 'react-i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: typeof coreResources['en'] & {
home: typeof homeEn;
};
}
}