Localization (i18n)
upup uses ICU locale bundles from @upupjs/core/i18n. Messages are grouped
into namespaces, formatted with ICU MessageFormat (so plurals and interpolation
work in every language), and Arabic switches the layout to right-to-left
automatically.
Built-in locales
| Export | Locale | Direction |
|---|---|---|
enUS | en-US | ltr |
arSA | ar-SA | rtl |
deDE | de-DE | ltr |
esES | es-ES | ltr |
frFR | fr-FR | ltr |
jaJP | ja-JP | ltr |
koKR | ko-KR | ltr |
zhCN | zh-CN | ltr |
zhTW | zh-TW | ltr |
Using a locale
Pass a bundle to i18n.locale:
import { UpupUploader } from '@upupjs/react'
import '@upupjs/react/styles'
import { jaJP } from '@upupjs/core/i18n'
export default function Uploader() {
return (
<UpupUploader
uploadEndpoint="/api/upload-token"
i18n={{ locale: jaJP }}
/>
)
}Arabic automatically sets right-to-left layout:
import { arSA } from '@upupjs/core/i18n'
;<UpupUploader i18n={{ locale: arSA }} />The i18n prop
The i18n object accepts four fields, all optional:
i18n?: {
/** A locale bundle. Takes precedence over `locale`. */
bundle?: LocaleBundle
/** A locale bundle, or a BCP-47 code string (e.g. 'fr-FR') for lang/dir. */
locale?: LocaleBundle | string
/** Bundle/code used when the active locale is missing a key. */
fallbackLocale?: LocaleBundle | string
/** Per-key overrides merged on top of the locale. */
overrides?: PartialMessages
}- Pass a full bundle (like
jaJP) tolocale— that is the field to reach for.bundleexists for when you construct a bundle object yourself and want it to take precedence: if both are set,bundlewins. - A string code (
'fr-FR') is resolved from the registry; an unregistered code falls back to English for content but still sets the language/direction. fallbackLocalesupplies missing keys (defaults to English).
Overrides
Use i18n.overrides for small copy changes. Overrides are a PartialMessages
object — keyed by namespace, then by the message key inside it. browseFiles,
for example, lives under the dropzone namespace:
import { frFR } from '@upupjs/core/i18n'
;<UpupUploader
i18n={{
locale: frFR,
overrides: {
dropzone: { browseFiles: 'choisir des fichiers' },
common: { cancel: 'annuler' },
},
}}
/>Message structure
Every message belongs to a namespace, and every value uses ICU MessageFormat.
The namespaces are common, sources, dropzone, header, fileList,
filePreview, driveBrowser, url, camera, audio, screenCapture,
branding, and errors. A few real keys:
{
common: { cancel: 'Cancel', done: 'Done' },
sources: { myDevice: 'My Device', googleDrive: 'Google Drive', oneDrive: 'OneDrive' },
dropzone: {
browseFiles: 'browse files',
// ICU interpolation:
addDocumentsHere: 'Add your documents here, you can upload up to {limit} files max',
},
fileList: {
// ICU pluralization:
uploadFiles: 'Upload {count, plural, one {# file} other {# files}}',
},
}Your overrides follow this same namespaced shape, so you can retranslate or
tweak any single key without shipping a whole bundle.
Setting the locale per framework
The i18n prop is shared across every framework — it is part of the common
uploader props. Only the binding syntax differs:
| Framework | How to pass it |
|---|---|
| React | <UpupUploader i18n={{ locale: jaJP }} /> |
| Vue | <UpupUploader :i18n="{ locale: jaJP }" /> |
| Svelte | <UpupUploader i18n={{ locale: jaJP }} /> |
| Angular | <upup-uploader [config]="{ i18n: { locale: jaJP } }" /> |
| Vanilla JS | createUploader('#el', { i18n: { locale: jaJP } }) |
Preact re-exports @upupjs/react, so its uploader takes the same i18n prop
as the React row.
In the headless path — useUpupUpload or UpupCore —
there is no i18n wrapper; set the engine's flat locale option to a bundle
or code instead (useUpupUpload({ locale: jaJP })). It also drives the
translator available to pipeline steps.
Adding a locale
Supported locales live behind one compiler-checked registry
(packages/core/src/i18n/locales/registry.ts), so contributing a new one is
a 2-file change:
- Create
packages/core/src/i18n/locales/<code>.ts(e.g.pt-BR.ts) exporting aLocaleBundle— copy the closest existing bundle as a starting point and set itscode,language, anddir. - In
registry.ts, add the import, add the code to theLOCALE_CODEStuple, add the bundle to theLOCALE_REGISTRYmap, and add the identifier to the re-export line.
Everything else — LOCALE_META, the UpupLocaleCode type, both i18n
barrel exports, and the locale test fixtures — derives from the registry
automatically. The Record<RegisteredLocaleCode, LocaleBundle> type
annotation on LOCALE_REGISTRY makes the compiler reject a code that's
missing its bundle or a bundle that's missing its code, and
locale-registry.test.ts asserts every bundle file under locales/ is
actually registered (the one thing the compiler can't see on its own).
Next steps
- Theming — restyle the components whose copy you just localized.
- Headless Usage — set the
localeoption on the engine when you build your own UI.