File-Based Locale
Create a locale/ folder beside bro.config.js. Each file exports a translation object, and its filename becomes the locale:
locale/
├── en.js
├── fr.js
└── ar.jslocale/en.js:
export default {
welcome: 'Welcome, {name}!'
};locale/fr.js:
export default {
welcome: 'Bienvenue, {name} !'
};The same structure also works with names such as en-US.js, English.js, or Arabic.js.
Configure the Default Locale
import { defineConfig } from 'bro-framework';
export default defineConfig({
locale: {
defaultLocale: 'en'
}
});The locale/ directory and en default are automatic. The configuration is only needed when you want a different fallback locale or directory.
Use Translations in Routes
bro.js selects the locale from the request's Accept-Language header and injects the t function into every route context:
import { defineRoute } from 'bro-framework';
export default defineRoute({
handler: async ({ t }) => ({
message: t('welcome', { name: 'Sam' })
})
});Advanced Header Negotiation
The locale engine natively implements RFC 9110 quality values for HTTP content negotiation.
- It parses headers like
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5. - Locales explicitly marked with
q=0are strictly rejected. - Valid requests are sorted in descending order of quality.
- For each requested locale, it attempts an exact match (e.g.
fr-CH). If missing, it automatically attempts a language-only fallback (e.g.fr). - If the entire list is exhausted with zero matches, it falls back to your configured
defaultLocale. - If a specific translation key is missing from a locale file, it falls back to the default locale file. If missing everywhere, the key string itself is returned.
Nested Keys
Translation objects can be organized by feature:
export default {
auth: {
loggedIn: 'You are authenticated.'
}
};Use the dotted key in a route:
const message = t('auth.loggedIn');The generated SDK also supports the locale header:
import { setLocale } from './bro-sdk.js';
setLocale('fr');