Internationalization (i18n)¶
AppArt Agent supports French and English via next-intl. The locale is reflected in the URL path (/fr/dashboard, /en/dashboard).
Architecture¶
flowchart LR
subgraph Browser["Browser Request"]
URL["/fr/dashboard"]
end
subgraph Middleware["next-intl Middleware"]
Detect["Detect locale from URL"]
Redirect["Redirect if missing"]
end
subgraph App["Next.js App"]
Layout["[locale]/layout.tsx"]
Page["Page Component"]
Messages["messages/fr.json"]
end
subgraph Backend["FastAPI Backend"]
Header["Accept-Language header"]
i18n["i18n.py translate()"]
end
URL --> Detect --> Layout --> Page
Layout --> Messages
Page --> Header --> i18n
Frontend¶
File Structure¶
frontend/
├── messages/
│ ├── en.json # English translations
│ └── fr.json # French translations
├── src/
│ ├── i18n/
│ │ ├── config.ts # Locale list and default locale
│ │ ├── navigation.ts # Locale-aware Link, useRouter, etc.
│ │ └── routing.ts # Routing config (prefix strategy)
│ ├── middleware.ts # next-intl middleware (locale detection)
│ └── app/
│ └── [locale]/ # All pages are under [locale] segment
│ ├── layout.tsx
│ ├── page.tsx
│ ├── dashboard/
│ └── ...
Configuration¶
src/i18n/config.ts defines the supported locales:
src/i18n/routing.ts configures the routing strategy:
export const routing = defineRouting({
locales,
defaultLocale,
localePrefix: 'always', // Always show /fr/ or /en/ in the URL
localeDetection: true, // Auto-detect from browser Accept-Language
});
src/middleware.ts intercepts requests and adds the locale prefix:
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'], // Skip API routes and static files
};
Using Translations in Components¶
import { useTranslations } from 'next-intl';
export default function Dashboard() {
const t = useTranslations('dashboard');
return <h1>{t('welcome', { name: user.name })}</h1>;
// EN: "Welcome back, John!"
// FR: "Bon retour, John !"
}
Translation Files¶
Translation files are in frontend/messages/. Each file is a flat JSON object organized by page/component namespace:
{
"header": {
"dashboard": "Tableau de bord",
"properties": "Biens",
"logout": "Déconnexion"
},
"dashboard": {
"welcome": "Bon retour, {name} !",
"stats": {
"totalProperties": "Total des biens"
}
}
}
Locale-Aware Navigation¶
Use the locale-aware Link and useRouter from @/i18n/navigation instead of the standard Next.js imports:
import { Link, useRouter } from '@/i18n/navigation';
// Link automatically includes the current locale in the URL
<Link href="/dashboard">Dashboard</Link>
// Renders: /fr/dashboard or /en/dashboard
// useRouter.replace switches locale without losing the current path
const router = useRouter();
router.replace(pathname, { locale: 'en' });
Locale Switcher¶
The Header component includes a locale toggle button that calls router.replace() with the alternate locale. The button has a fixed min-w to prevent layout shifts between locales.
Backend¶
The backend uses the Accept-Language header to determine the response language.
File: backend/app/core/i18n.py¶
from app.core.i18n import get_local, translate, get_output_language
# In an API route:
locale = get_local(request) # "fr" or "en" from Accept-Language
message = translate("email_already_registered", locale) # Translated error message
output_lang = get_output_language(locale) # "French" or "English" (for AI prompts)
get_local(request)— extracts locale from theAccept-Languageheadertranslate(key, locale)— looks up a translation string from theMESSAGESdictget_output_language(locale)— returns"French"or"English"for use in AI prompt templates
AI Prompt Language¶
AI prompt templates accept an {output_language} variable that controls the language of AI-generated responses:
from app.prompts import get_prompt
prompt = get_prompt("dp_process_pv_ag", filename="pv.pdf", output_language="French")
This ensures document analysis results, recommendations, and summaries are returned in the user's language.
Translation Key Namespaces¶
The translation files are organized by feature area. Key namespaces include:
| Namespace | Description |
|---|---|
header |
Navigation and locale switcher |
dashboard |
Dashboard page, property cards |
propertyForm |
Property creation/editing form fields |
propertyDetail |
Property detail page sections |
propertyDetail.info |
Property info card (edit, save, cancel, field labels) |
propertyDetail.synthesis |
AI synthesis display (confidence, breakdowns, themes, tantiemes) |
propertyDetail.documents |
Document list, upload phases, bulk operations |
propertyDetail.documents.documentList |
Document list UI (expand, collapse, resynthesize) |
propertyDetail.photos |
Photo management (promote/demote redesigns) |
propertyCard |
Dashboard property cards (risk levels, annual costs, doc counts) |
Recently Added Keys¶
The following translation namespaces were added to support new features:
- Property editing:
propertyDetail.info.edit,save,cancel,saveFailed,address,postalCode,city,department,selectType,appartement,maison,buildingFloors,buildingFloorsHouse - Risk badges:
propertyCard.risk.low,risk.medium,risk.high - Synthesis details:
synthesis.confidence,annualBreakdown,oneTimeBreakdown,crossDocThemes,actionItems,riskFactors,tantiemes,lotTantiemes,totalTantiemes,sharePercentage,editTantiemes,notDetected,tantiemesHint - Upload phases:
documents.stepUpload,stepAnalysis,stepSynthesis,uploading,uploadingSubtitle,synthesizing,synthesizingSubtitle - Bulk operations:
documents.selectAll,deselectAll,deleteSelected,selectedCount,bulkDeleteTitle,bulkDeleteMessage - Document management:
documents.rename,renameDocument,documentList.title,expandDetails,collapseDetails,resynthesize,resynthesizing,emptyState - Photo redesigns:
photos.promoteRedesign,demoteRedesign,promoted,redesignPromoted,redesignDemoted,promoteFailed
Adding a New Translation Key¶
- Add the key to both
messages/en.jsonandmessages/fr.json - Use it in your component with
useTranslations('namespace') - If the key is used in backend error messages, also add it to
MESSAGESinbackend/app/core/i18n.py