---
title: "Localizing App UI and Admin UI"
description: "Compose editable English source catalogs with application-owned translations and an ICU formatter."
tags: ["ui","react","localization","i18n","artifacts"]
---

Package UI uses the shared [React localization binding](/docs/packages/foundation/i18n-react). No provider is required for existing English usage. The host owns translated copy and its ICU dependency.

```tsx
'use client'
import { IntlMessageFormat } from 'intl-messageformat' // application dependency
import { appUICatalog } from '@playstack/app-ui/messages'
import { adminUICatalog } from '@playstack/admin-ui/messages'
import { createCatalogStore, createLocalePolicy } from '@playstack/i18n'
import { createI18nReact, type FormatCatalogMessage } from '@playstack/i18n-react'

const catalogs = createCatalogStore({
  policy: createLocalePolicy({ locales: ['en', 'fr'], source: 'en' }),
  source: { ...appUICatalog, ...adminUICatalog },
  translations: {
    fr: {
      'app-ui': { 'search.clear': 'Effacer la recherche' },
      'admin-ui': { 'members.title': 'Membres' },
    },
  },
})
export const { I18nProvider } = createI18nReact(catalogs)
export const formatMessage: FormatCatalogMessage = (message, values) => {
  const result = new IntlMessageFormat(message.message, message.locale).format(values)
  if (typeof result !== 'string') throw new Error('UI messages must be plain text')
  return result
}
// Within your Chakra composition root:
// <I18nProvider locale="fr" timeZone="UTC" formatMessage={formatMessage}>
//   {children}
// </I18nProvider>
```

Use `MessageCatalogProvider` directly if another library already owns the locale context. Always format using the resolved message locale, not the requested locale. Partial translations fall back; configured missing namespaces/IDs and formatter failures remain explicit. Component props/message overrides win over catalog values.

## Editable source artifacts

Both UI packages export data-only `./messages` and `./locales/en.json` entry points. Admin UI also consumes App UI, so compose both namespaces. Configure their artifact destination in `playstack.json`:

```json
{ "i18n": { "target": "libs/i18n", "packages": ["@playstack/app-ui", "@playstack/admin-ui"] } }
```

```sh
playstack i18n init --dry-run
playstack i18n init
playstack i18n init --check
```

The `app-ui.messages.en` and `admin-ui.messages.en` artifacts target `en/app-ui.json` and `en/admin-ui.json`. This copies English catalogs, not translations or extracted application strings. Keep translations application-owned; review ejection/merge before customizing managed English. Do not force-sync over reviewed edits.

Account names, role labels, navigation and business content remain host-owned. Set consistent document `lang`/`dir`, Chakra direction, explicit directional control props and SSR locale inputs. Localization must never alter membership permissions, account scope or mutation ordering. Verify right-to-left layout and screen-reader behavior in your own product.
