Skip to content

Commit b2e4bfe

Browse files
feat(i18n): add Brazilian Portuguese (pt-BR) locale (#171)
## Summary - Adds full Brazilian Portuguese translation (820/820 UI strings) as a third supported locale next to `en` and `zh-CN`. - Auto-detects `pt-*` system locales; users can also switch manually from the topbar toggle or Settings → Appearance → Language. - Default locale (`DEFAULT_LOCALE`) stays `en`; no behavior change for existing users. ## What's inside - `packages/i18n/src/locales/pt-BR.json` — 820 UI strings, full parity with `en.json` - `packages/templates/src/locales/pt-BR.ts` — 4 built-in demos (title, description, prompt) - `packages/templates/src/examples/locales/pt-BR.ts` — 21 hub examples (title, description) - `packages/i18n/src/index.ts` — register `pt-BR` in `availableLocales`, `resources`, `normalizeLocale` (accepts `pt`, `pt-BR`, `pt_br`, `pt-*`) - Templates registries pointed to the new pt-BR files (no `en` fallback) - `Settings.tsx` dropdown + `LanguageToggle.tsx` cycle now include `pt-BR` - `langPtBR` label added to `en.json` and `zh-CN.json` so the dropdown works across all locales ## Translation quality notes - Terminology glossary applied consistently (tweaks → Ajustes, design system → design system, snapshot → snapshot, etc.) - `{{interpolations}}` and `_one`/`_other` plural suffixes preserved - 32 strings kept identical to EN (product names, file paths, unit suffixes, tech jargon) — no mistranslations - Reviewed end-to-end for tone, grammar, and accuracy (standardized `novamente`, fixed `redacted → redigido` bug, `Nota fiscal → Fatura` for semantic accuracy, etc.) ## Test plan - [ ] \`pnpm lint && pnpm typecheck && pnpm test\` all pass locally (pre-push hook confirms) - [ ] App boots with zero \`missing translation\` / \`unsupported locale\` warnings - [ ] Settings → Appearance → Language shows \`Português (BR)\`; selection persists across restart - [ ] Topbar globe toggle cycles \`EN → ZH → PT → EN\` - [ ] System locale \`pt-BR\` auto-selects pt-BR on first launch - [ ] Hub Examples gallery and built-in demos render in PT - [ ] No UI clipping from longer PT strings (sidebar, settings, onboarding) --------- Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com> Signed-off-by: David Fernandes <davidggffernandes@gmail.com>
1 parent a965e58 commit b2e4bfe

10 files changed

Lines changed: 1232 additions & 5 deletions

File tree

apps/desktop/src/renderer/src/components/LanguageToggle.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ import { useEffect, useState } from 'react';
66

77
const noDragStyle = { WebkitAppRegion: 'no-drag' } as CSSProperties;
88

9+
const LOCALE_CYCLE: Locale[] = ['en', 'zh-CN', 'pt-BR'];
10+
911
function nextLocale(locale: Locale): Locale {
10-
return locale === 'en' ? 'zh-CN' : 'en';
12+
const i = LOCALE_CYCLE.indexOf(locale);
13+
return LOCALE_CYCLE[(i + 1) % LOCALE_CYCLE.length] ?? 'en';
1114
}
1215

1316
function localeLabel(locale: Locale): string {
14-
return locale === 'zh-CN' ? 'ZH' : 'EN';
17+
if (locale === 'zh-CN') return 'ZH';
18+
if (locale === 'pt-BR') return 'PT';
19+
return 'EN';
1520
}
1621

1722
export function LanguageToggle() {

apps/desktop/src/renderer/src/components/Settings.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,7 @@ function AppearanceTab() {
17191719
options={[
17201720
{ value: 'en', label: t('settings.appearance.langEn') },
17211721
{ value: 'zh-CN', label: t('settings.appearance.langZhCN') },
1722+
{ value: 'pt-BR', label: t('settings.appearance.langPtBR') },
17221723
]}
17231724
/>
17241725
</Row>

packages/i18n/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ import i18next from 'i18next';
1616
import { useCallback } from 'react';
1717
import { initReactI18next, useTranslation } from 'react-i18next';
1818
import en from './locales/en.json';
19+
import ptBR from './locales/pt-BR.json';
1920
import zhCN from './locales/zh-CN.json';
2021

21-
export const availableLocales = ['en', 'zh-CN'] as const;
22+
export const availableLocales = ['en', 'zh-CN', 'pt-BR'] as const;
2223
export type Locale = (typeof availableLocales)[number];
2324

2425
const DEFAULT_LOCALE: Locale = 'en';
2526

2627
const resources = {
2728
en: { translation: en },
2829
'zh-CN': { translation: zhCN },
30+
'pt-BR': { translation: ptBR },
2931
} as const;
3032

3133
export function isSupportedLocale(value: string | undefined | null): value is Locale {
@@ -40,6 +42,9 @@ export function normalizeLocale(value: string | undefined | null): Locale {
4042
if (lower === 'zh' || lower.startsWith('zh-hans') || lower === 'zh-cn' || lower === 'zh_cn') {
4143
return 'zh-CN';
4244
}
45+
if (lower === 'pt-br' || lower === 'pt_br' || lower === 'pt' || lower.startsWith('pt-')) {
46+
return 'pt-BR';
47+
}
4348
if (lower.startsWith('en')) return 'en';
4449
console.warn(
4550
`[i18n] unsupported locale "${value}", falling back to "${DEFAULT_LOCALE}". ` +

packages/i18n/src/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@
333333
"languageHint": "Language changes take effect immediately.",
334334
"languageLoadFailed": "Failed to load language",
335335
"langEn": "English",
336-
"langZhCN": "中文 (简体)"
336+
"langZhCN": "中文 (简体)",
337+
"langPtBR": "Português (BR)"
337338
},
338339
"language": {
339340
"label": "Language",

0 commit comments

Comments
 (0)