Files
zitadel/apps/login/src/lib/i18n.ts
T
f803ea8298 feat(login): add Portuguese (pt) translations for Login V2 (#11897)
## What does this PR do?

Adds complete **Portuguese (pt)** locale translations for the Login V2
interface, making it the 14th supported language.

### Problem

The Login V2 component currently supports 13 languages but does not
include Portuguese — one of the most spoken languages in the world
(~260M native speakers). This was reported in #11782.

### What we found

- The Login V2 translations are stored as JSON files under
`apps/login/locales/`
- Available languages are registered in the `LANGS` array in
`apps/login/src/lib/i18n.ts`
- Portuguese (`pt.json`) was missing from both the locales directory and
the `LANGS` array

### What we did

1. Created `apps/login/locales/pt.json` with complete Portuguese
translations for all login screens (loginname, password, register, MFA,
passkeys, OTP, sessions, errors, etc.)
2. Added `{ name: "Português", code: "pt" }` to the `LANGS` array in
`apps/login/src/lib/i18n.ts`

### Translation methodology

- Based on the existing `en.json` and `es.json` as reference
- Follows Brazilian Portuguese (pt-BR) conventions, which is understood
by all Portuguese-speaking countries
- All keys translated — no fallbacks to English needed

### Files changed

| File | Change |
|------|--------|
| `apps/login/locales/pt.json` | **New** — Complete Portuguese
translations |
| `apps/login/src/lib/i18n.ts` | **Modified** — Added `pt` entry to
`LANGS` array |

### Testing

We built and deployed this change from source (tag v4.12.1) to a
production Zitadel instance:

- ✅ Portuguese appears in the language dropdown  
- ✅ All login screens display correct Portuguese translations  
- ✅ Language selection persists across pages  
- ✅ OIDC flow works correctly with Portuguese selected  
- ✅ No build warnings or errors related to the translation  

Closes #11782

Co-authored-by: rofilho <[email protected]>
2026-04-07 10:51:55 +02:00

83 lines
1.2 KiB
TypeScript

export interface Lang {
name: string;
code: string;
}
export const LANGS: Lang[] = [
{
name: "English",
code: "en",
},
{
name: "Deutsch",
code: "de",
},
{
name: "Italiano",
code: "it",
},
{
name: "Español",
code: "es",
},
{
name: "Français",
code: "fr",
},
{
name: "Nederlands",
code: "nl",
},
{
name: "Polski",
code: "pl",
},
{
name: "Português",
code: "pt",
},
{
name: "简体中文",
code: "zh",
},
{
name: "Русский",
code: "ru",
},
{
name: "Türkçe",
code: "tr",
},
{
name: "日本語",
code: "ja",
},
{
name: "Українська",
code: "uk",
},
{
name: "العربية",
code: "ar",
},
];
export const LANGUAGE_COOKIE_NAME = "NEXT_LOCALE";
export const LANGUAGE_HEADER_NAME = "accept-language";
export function shouldUILocalesOverrideCookie(): boolean {
return process.env.ZITADEL_UI_LOCALES_OVERRIDE_COOKIE === "true";
}
export function getLanguage(code: string): Lang {
const lang = LANGS.find((l) => l.code === code);
if (lang) {
return lang;
}
return {
code,
name: new Intl.DisplayNames([code], { type: "language" }).of(code) || code,
};
}