## Summary Adds **Português (Brasil)** (`pt-BR`) as a supported locale: - Selectable as **Translation target language** (project settings) and as the dashboard / terminal UI language. - Full machine-drafted catalogs (`app`, `cli`, `common`), disclosed in `packages/i18n/locales/TRANSLATION_STATUS.md` following the pattern #1352 established — reviewed for glossary/register consistency (0.18% untranslated, matching only keys that are empty in `en`), but native-speaker corrections are welcome. - Brazilian Portuguese content-language detection (accent-stripped stopword list — the scorer strips diacritics before matching, so accented entries never match; `com`/`mais` deliberately omitted to avoid bare-domain `.com` and French collisions, with regression tests for both directions). - `pt`/`pt-PT` browser and environment locales resolve to `pt-BR` on all three detection paths (`FALLBACK_LNG` routing plus a `pt` branch in `normalizeToSupportedLocale`, mirroring the existing `zh` handling). - `README.pt-BR.md` + switcher links in all READMEs, docs updates (`settings-reference`, `cli-reference`, `i18n-contributing`, `--lang` help text), changeset (`minor`). Drive-by fixes bundled: `TRANSLATION_STATUS.md` was missing the `ko` row; the LanguageSelector endonym test was missing `한국어`; `docs/i18n-contributing.md` now names the two compile-enforced display maps (`LOCALE_LABELS`, `localeDisplayName`) a new locale must update; the `--lang` CLI help text no longer drifts from its validator. ## Test plan - `pnpm i18n:status` (key parity gate) green; catalogs are `i18n:sync`-idempotent. - Updated/extended suites: core `locale-settings`, i18n `config`/`parity`/`db-banner-catalog`/`i18n-gate-coverage`, dashboard `useLanguage`/`LanguageSelector`/`GeneralSection.importTranslate`/`detectContentLanguage` (incl. new pt-BR detection + bare-domain regression tests), CLI `settings`. - `pnpm verify:fast` (typecheck, build, boot smoke), `pnpm lint`, `pnpm check:changesets`, and the bounded `pnpm test` lane all green locally (the three `test:pg-gate` files fail locally only for lack of a Postgres instance; they fail identically on clean `main`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Brazilian Portuguese (Português (Brasil)) across the dashboard, terminal interface, settings, and translation tools. * Added Portuguese translations for common interface and CLI content. * Added automatic Portuguese language detection, locale normalization, and fallback support. * Added a Portuguese (Brazil) README with product, setup, and usage documentation. * **Documentation** * Updated language selectors, CLI references, settings documentation, and translation guidance. * Added Portuguese README links to translated documentation. * Added French to the documented dashboard language options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import "./LanguageSelector.css";
|
|
import type { Locale } from "@fusion/core";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useLanguage } from "../hooks/useLanguage";
|
|
|
|
/** Each language names itself (endonyms), intentionally untranslated. */
|
|
const ENDONYMS: Record<Locale, string> = {
|
|
en: "English",
|
|
"zh-CN": "简体中文",
|
|
"zh-TW": "繁體中文",
|
|
fr: "Français",
|
|
es: "Español",
|
|
ko: "한국어",
|
|
"pt-BR": "Português (Brasil)",
|
|
};
|
|
|
|
/** Settings control for choosing the UI language. Applies in place (no reload). */
|
|
export function LanguageSelector() {
|
|
const { t } = useTranslation("app");
|
|
const { language, supportedLocales, setLanguage, clearLanguage, hasExplicitChoice } =
|
|
useLanguage();
|
|
const label = t("settings.appearance.language", "Language");
|
|
|
|
return (
|
|
<div className="language-selector">
|
|
<div className="language-selector-title">{label}</div>
|
|
{/* role="group" + aria-pressed: toggle-button semantics (radiogroup would
|
|
conflict with aria-pressed and confuse screen readers). */}
|
|
<div className="language-options" role="group" aria-label={label}>
|
|
<button
|
|
type="button"
|
|
className={`language-option${hasExplicitChoice ? "" : " active"}`}
|
|
onClick={clearLanguage}
|
|
aria-pressed={!hasExplicitChoice}
|
|
title={t("settings.appearance.languageAutoHint", "Follow the browser language")}
|
|
>
|
|
{t("settings.appearance.languageAuto", "Auto")}
|
|
</button>
|
|
{supportedLocales.map((locale) => (
|
|
<button
|
|
key={locale}
|
|
type="button"
|
|
className={`language-option${hasExplicitChoice && language === locale ? " active" : ""}`}
|
|
onClick={() => setLanguage(locale)}
|
|
aria-pressed={hasExplicitChoice && language === locale}
|
|
lang={locale}
|
|
>
|
|
{ENDONYMS[locale]}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|