From e69c51ff91fd74033054a5cf6937ec64b72c8f6c Mon Sep 17 00:00:00 2001 From: Nar Cuenca Date: Thu, 25 Jun 2026 13:27:42 +0800 Subject: [PATCH 1/3] Add MUI localization support --- LOCALIZATION.md | 82 ++++++++++ package-lock.json | 15 +- package.json | 2 +- src/i18n/datagrid-locales/hi-IN.ts | 207 ++++++++++++++++++++++++++ src/i18n/datagrid.ts | 24 +++ src/i18n/datepickers-locales/hi-IN.ts | 115 ++++++++++++++ src/i18n/datepickers.ts | 24 +++ src/i18n/index.ts | 2 + src/i18n/mui-locale.ts | 49 ++++++ src/index.ts | 1 + 10 files changed, 513 insertions(+), 8 deletions(-) create mode 100644 LOCALIZATION.md create mode 100644 src/i18n/datagrid-locales/hi-IN.ts create mode 100644 src/i18n/datagrid.ts create mode 100644 src/i18n/datepickers-locales/hi-IN.ts create mode 100644 src/i18n/datepickers.ts create mode 100644 src/i18n/index.ts create mode 100644 src/i18n/mui-locale.ts diff --git a/LOCALIZATION.md b/LOCALIZATION.md new file mode 100644 index 0000000..bb122a4 --- /dev/null +++ b/LOCALIZATION.md @@ -0,0 +1,82 @@ +# Adding a New Language + +This guide covers adding MUI built-in component label support for a new language — things like DataGrid column menus, DatePicker buttons, and Autocomplete labels. These are separate from any i18next/app-level strings and are resolved through MUI's own locale bundle system. + +`localizeTheme(theme, muiLocaleString)` is the single entry point. It accepts a MUI locale string (e.g. `"frFR"`) and resolves the correct bundles for MUI core, Data Grid, and Date Pickers dynamically — no changes needed here for locales MUI already ships. The only time this repo needs updating is when **MUI X doesn't ship a locale** and a hand-authored bundle is required. + +--- + +## Steps + +### 1. Check MUI core + +Try importing the locale from `@mui/material/locale`: + +```ts +import { frFR } from "@mui/material/locale"; +``` + +If it resolves, MUI core is handled automatically — no changes needed in this repo. + +If it doesn't resolve, a hand-authored core bundle is needed. This is rare — MUI core ships bundles for most major languages. + +### 2. Check MUI X Data Grid + +Try importing the locale from `@mui/x-data-grid-pro/locales`: + +```ts +import { frFR } from "@mui/x-data-grid-pro/locales"; +``` + +**If it resolves** — no changes needed, the dynamic resolver in `src/i18n/datagrid.ts` picks it up automatically. + +**If it doesn't resolve** — create a hand-authored bundle at `src/i18n/datagrid-locales/.ts`. Use `src/i18n/datagrid-locales/hi-IN.ts` as a reference, then register it in the `OVERRIDES` map in `src/i18n/datagrid.ts`: + +```ts +import { frFR } from "./datagrid-locales/fr-FR"; + +const OVERRIDES: Record> = { + hiIN, + frFR, // add here +}; +``` + +### 3. Check MUI X Date Pickers + +Same pattern — try importing from `@mui/x-date-pickers-pro/locales`: + +```ts +import { frFR } from "@mui/x-date-pickers-pro/locales"; +``` + +**If it resolves** — no changes needed. + +**If it doesn't resolve** — create `src/i18n/datepickers-locales/.ts` using `src/i18n/datepickers-locales/hi-IN.ts` as a reference, then register it in `OVERRIDES` in `src/i18n/datepickers.ts`. + +### 4. Build and publish + +```bash +npm run build +npm version patch # or minor/major depending on the change +npm publish --access public +``` + +### 5. Update consuming apps + +Bump `@zesty-io/material` and add the BCP 47 → MUI locale string mapping in the app. For example in manager-ui, add an entry to `MUI_LOCALE` in `LocalizedThemeProvider.tsx`: + +```ts +const MUI_LOCALE: Record = { + ... + "fr-FR": "frFR", +}; +``` + +--- + +## Notes + +- **English variants** (e.g. `en-PH`, `en-AU`) require no changes here — they fall back to MUI's built-in English strings automatically. In the consuming app, map them to `"enUS"`. +- **MUI X does not need an explicit English fallback.** `getDataGridLocaleText` and `getDatePickersLocaleText` return `undefined` for unmapped locales — MUI X treats `undefined` as "use built-in defaults", which are already English. This is intentional and differs from MUI core, which always needs a locale object merged into the theme. +- **The localization layer is i18next-free.** MUI's built-in label system is separate from app-level strings (form labels, page titles, notifications, etc.) — those remain the consuming app's responsibility. +- Consuming apps that don't use `localizeTheme` are unaffected by changes here. diff --git a/package-lock.json b/package-lock.json index ce0f5a8..34f29f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@zesty-io/material", - "version": "0.16.2", + "version": "0.16.3-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zesty-io/material", - "version": "0.16.2", + "version": "0.16.3-rc.1", "license": "MIT", "dependencies": { "@emotion/react": "^11.14.0", @@ -35,7 +35,7 @@ "react-docgen-typescript": "^2.2.2", "react-dom": "^17.0.2", "react-json-view": "^1.21.3", - "typescript": "^4.6.3" + "typescript": "^5.9.3" } }, "node_modules/@ampproject/remapping": { @@ -18722,16 +18722,17 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/ua-parser-js": { diff --git a/package.json b/package.json index af11e11..01962a6 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "react-docgen-typescript": "^2.2.2", "react-dom": "^17.0.2", "react-json-view": "^1.21.3", - "typescript": "^4.6.3" + "typescript": "^5.9.3" }, "files": [ "cjs", diff --git a/src/i18n/datagrid-locales/hi-IN.ts b/src/i18n/datagrid-locales/hi-IN.ts new file mode 100644 index 0000000..9145c83 --- /dev/null +++ b/src/i18n/datagrid-locales/hi-IN.ts @@ -0,0 +1,207 @@ +import type { GridLocaleText } from "@mui/x-data-grid-pro"; + +// Custom MUI X Data Grid localeText for Hindi (hi-IN). +// +// MUI X ships no Hindi Data Grid locale, so we supply our own. This is a FULL +// translation of GridLocaleText (every key MUI exposes), not a reachable-only +// subset — the grid is used across the whole app in many configurations +// (toolbar, filtering, export, row selection, aggregation, detail panels…), +// so translating everything avoids silent English fallback when any grid +// enables a feature. +// +// Typed Partial: any key MUI adds in a future version that we +// haven't translated yet falls back to its built-in English rather than +// erroring. The 7 function entries mirror MUI's default signatures. +// +// To add another unshipped locale: create a sibling file here exporting a +// Partial and register it in ../datagrid.ts. +export const hiIN: Partial = { + // Overlays + noRowsLabel: "कोई पंक्तियाँ नहीं हैं", + noResultsOverlayLabel: "कोई परिणाम नहीं मिला।", + + // Toolbar — density + toolbarDensity: "घनत्व", + toolbarDensityLabel: "घनत्व", + toolbarDensityCompact: "संकुचित", + toolbarDensityStandard: "मानक", + toolbarDensityComfortable: "आरामदायक", + + // Toolbar — columns + toolbarColumns: "कॉलम", + toolbarColumnsLabel: "कॉलम चुनें", + + // Toolbar — filters + toolbarFilters: "फ़िल्टर", + toolbarFiltersLabel: "फ़िल्टर दिखाएं", + toolbarFiltersTooltipHide: "फ़िल्टर छुपाएं", + toolbarFiltersTooltipShow: "फ़िल्टर दिखाएं", + toolbarFiltersTooltipActive: (count) => `${count} सक्रिय फ़िल्टर`, + + // Toolbar — quick filter + toolbarQuickFilterPlaceholder: "खोजें…", + toolbarQuickFilterLabel: "खोजें", + toolbarQuickFilterDeleteIconLabel: "साफ़ करें", + + // Toolbar — export + toolbarExport: "निर्यात", + toolbarExportLabel: "निर्यात", + toolbarExportCSV: "CSV के रूप में डाउनलोड करें", + toolbarExportPrint: "प्रिंट करें", + toolbarExportExcel: "Excel के रूप में डाउनलोड करें", + + // Columns management panel + columnsManagementSearchTitle: "खोजें", + columnsManagementNoColumns: "कोई कॉलम नहीं", + columnsManagementShowHideAllText: "सभी दिखाएं/छुपाएं", + columnsManagementReset: "रीसेट करें", + columnsManagementDeleteIconLabel: "साफ़ करें", + + // Filter panel + filterPanelAddFilter: "फ़िल्टर जोड़ें", + filterPanelRemoveAll: "सभी हटाएं", + filterPanelDeleteIconLabel: "हटाएं", + filterPanelLogicOperator: "तार्किक ऑपरेटर", + filterPanelOperator: "ऑपरेटर", + filterPanelOperatorAnd: "और", + filterPanelOperatorOr: "या", + filterPanelColumns: "कॉलम", + filterPanelInputLabel: "मान", + filterPanelInputPlaceholder: "फ़िल्टर मान", + + // Filter operators + filterOperatorContains: "शामिल है", + filterOperatorDoesNotContain: "शामिल नहीं है", + filterOperatorEquals: "बराबर है", + filterOperatorDoesNotEqual: "बराबर नहीं है", + filterOperatorStartsWith: "से शुरू होता है", + filterOperatorEndsWith: "से समाप्त होता है", + filterOperatorIs: "है", + filterOperatorNot: "नहीं है", + filterOperatorAfter: "के बाद है", + filterOperatorOnOrAfter: "को या उसके बाद है", + filterOperatorBefore: "के पहले है", + filterOperatorOnOrBefore: "को या उससे पहले है", + filterOperatorIsEmpty: "खाली है", + filterOperatorIsNotEmpty: "खाली नहीं है", + filterOperatorIsAnyOf: "इनमें से कोई है", + "filterOperator=": "=", + "filterOperator!=": "!=", + "filterOperator>": ">", + "filterOperator>=": ">=", + "filterOperator<": "<", + "filterOperator<=": "<=", + + // Header filter operators + headerFilterOperatorContains: "शामिल है", + headerFilterOperatorDoesNotContain: "शामिल नहीं है", + headerFilterOperatorEquals: "बराबर है", + headerFilterOperatorDoesNotEqual: "बराबर नहीं है", + headerFilterOperatorStartsWith: "से शुरू होता है", + headerFilterOperatorEndsWith: "से समाप्त होता है", + headerFilterOperatorIs: "है", + headerFilterOperatorNot: "नहीं है", + headerFilterOperatorAfter: "के बाद है", + headerFilterOperatorOnOrAfter: "को या उसके बाद है", + headerFilterOperatorBefore: "के पहले है", + headerFilterOperatorOnOrBefore: "को या उससे पहले है", + headerFilterOperatorIsEmpty: "खाली है", + headerFilterOperatorIsNotEmpty: "खाली नहीं है", + headerFilterOperatorIsAnyOf: "इनमें से कोई है", + "headerFilterOperator=": "बराबर है", + "headerFilterOperator!=": "बराबर नहीं है", + "headerFilterOperator>": "इससे बड़ा है", + "headerFilterOperator>=": "इससे बड़ा या बराबर है", + "headerFilterOperator<": "इससे छोटा है", + "headerFilterOperator<=": "इससे छोटा या बराबर है", + + // Filter values + filterValueAny: "कोई भी", + filterValueTrue: "सत्य", + filterValueFalse: "असत्य", + + // Column menu + columnMenuLabel: "मेनू", + columnMenuAriaLabel: (columnName) => `${columnName} कॉलम मेनू`, + columnMenuShowColumns: "कॉलम दिखाएं", + columnMenuManageColumns: "कॉलम प्रबंधित करें", + columnMenuFilter: "फ़िल्टर", + columnMenuHideColumn: "कॉलम छुपाएं", + columnMenuUnsort: "क्रमबद्धता हटाएं", + columnMenuSortAsc: "बढ़ते क्रम (ASC) में क्रमबद्ध करें", + columnMenuSortDesc: "घटते क्रम (DESC) में क्रमबद्ध करें", + + // Column header + columnHeaderFiltersTooltipActive: (count) => `${count} सक्रिय फ़िल्टर`, + columnHeaderFiltersLabel: "फ़िल्टर दिखाएं", + columnHeaderSortIconLabel: "क्रमबद्ध करें", + + // Footer + footerRowSelected: (count) => + count !== 1 + ? `${count.toLocaleString()} पंक्तियाँ चयनित` + : `${count.toLocaleString()} पंक्ति चयनित`, + footerTotalRows: "कुल पंक्तियाँ:", + footerTotalVisibleRows: (visibleCount, totalCount) => + `${totalCount.toLocaleString()} में से ${visibleCount.toLocaleString()}`, + + // Checkbox selection + checkboxSelectionHeaderName: "चेकबॉक्स चयन", + checkboxSelectionSelectAllRows: "सभी पंक्तियाँ चुनें", + checkboxSelectionUnselectAllRows: "सभी पंक्तियों का चयन रद्द करें", + checkboxSelectionSelectRow: "पंक्ति चुनें", + checkboxSelectionUnselectRow: "पंक्ति का चयन रद्द करें", + + // Boolean cell + booleanCellTrueLabel: "हाँ", + booleanCellFalseLabel: "नहीं", + + // Actions cell + actionsCellMore: "अधिक", + + // Column pinning (DataGridPro) + pinToLeft: "बाईं ओर पिन करें", + pinToRight: "दाईं ओर पिन करें", + unpin: "पिन हटाएं", + + // Tree data + treeDataGroupingHeaderName: "समूह", + treeDataExpand: "उप-आइटम देखें", + treeDataCollapse: "उप-आइटम छुपाएं", + + // Grouping + groupingColumnHeaderName: "समूह", + groupColumn: (name) => `${name} के अनुसार समूहित करें`, + unGroupColumn: (name) => `${name} के अनुसार समूहन रोकें`, + + // Master/detail + detailPanelToggle: "विवरण पैनल टॉगल करें", + expandDetailPanel: "विस्तृत करें", + collapseDetailPanel: "संक्षिप्त करें", + + // Row reordering + rowReorderingHeaderName: "पंक्ति पुनर्क्रमण", + + // Aggregation + aggregationMenuItemHeader: "समुच्चय", + aggregationFunctionLabelSum: "योग", + aggregationFunctionLabelAvg: "औसत", + aggregationFunctionLabelMin: "न्यूनतम", + aggregationFunctionLabelMax: "अधिकतम", + aggregationFunctionLabelSize: "संख्या", + + // Pagination footer + MuiTablePagination: { + labelRowsPerPage: "प्रति पृष्ठ पंक्तियाँ:", + labelDisplayedRows: ({ from, to, count }) => + `${ + count !== -1 ? count.toLocaleString() : `${to.toLocaleString()} से अधिक` + } में से ${from.toLocaleString()}–${to.toLocaleString()}`, + getItemAriaLabel: (type) => { + if (type === "first") { return "पहले पृष्ठ पर जाएं"; } + if (type === "last") { return "अंतिम पृष्ठ पर जाएं"; } + if (type === "next") { return "अगले पृष्ठ पर जाएं"; } + return "पिछले पृष्ठ पर जाएं"; + }, + }, +}; diff --git a/src/i18n/datagrid.ts b/src/i18n/datagrid.ts new file mode 100644 index 0000000..0ab2dec --- /dev/null +++ b/src/i18n/datagrid.ts @@ -0,0 +1,24 @@ +import type { GridLocaleText } from "@mui/x-data-grid-pro"; +import * as dataGridLocales from "@mui/x-data-grid-pro/locales"; + +import { hiIN } from "./datagrid-locales/hi-IN"; + +// Hand-authored bundles for locales MUI X Data Grid doesn't ship. +// Add an entry here whenever a new unsupported locale is needed. +const OVERRIDES: Record> = { + hiIN, +}; + +// Returns undefined (not enUS) for unmapped locales — MUI X treats undefined as +// "use built-in defaults", which are already English. An explicit enUS fallback +// would be redundant. This differs from MUI core, which always needs a locale +// object merged into the theme. +export const getDataGridLocaleText = ( + locale: string | undefined +): Partial | undefined => { + if (!locale) { return undefined; } + if (OVERRIDES[locale]) { return OVERRIDES[locale]; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const entry = dataGridLocales[locale as keyof typeof dataGridLocales] as any; + return entry?.components?.MuiDataGrid?.defaultProps?.localeText; +}; diff --git a/src/i18n/datepickers-locales/hi-IN.ts b/src/i18n/datepickers-locales/hi-IN.ts new file mode 100644 index 0000000..2a70702 --- /dev/null +++ b/src/i18n/datepickers-locales/hi-IN.ts @@ -0,0 +1,115 @@ +import type { PickersLocaleText } from "@mui/x-date-pickers-pro"; + +// Custom MUI X Date Pickers localeText for Hindi (hi-IN). +// +// MUI X ships no Hindi Date Pickers locale (es/zh/ru/nl only), so we supply +// our own. Covers picker UI strings MUI renders itself (OK/Cancel/Clear/Today +// buttons, toolbar titles, clock/calendar aria labels, field placeholders). +// +// Typed Partial>: any key MUI adds in a future version +// falls back to its built-in English rather than erroring. +// +// To add another unshipped locale: create a sibling file exporting a +// Partial> and export it from this package. +const views: Record = { + hours: "घंटे", + minutes: "मिनट", + seconds: "सेकंड", + meridiem: "पूर्वाह्न/अपराह्न", +}; + +export const hiIN: Partial> = { + // Calendar navigation + previousMonth: "पिछला महीना", + nextMonth: "अगला महीना", + openPreviousView: "पिछला दृश्य खोलें", + openNextView: "अगला दृश्य खोलें", + calendarViewSwitchingButtonAriaLabel: (view) => + view === "year" + ? "वर्ष दृश्य खुला है, कैलेंडर दृश्य पर जाएं" + : "कैलेंडर दृश्य खुला है, वर्ष दृश्य पर जाएं", + + // Range labels + start: "प्रारंभ", + end: "समाप्त", + startDate: "प्रारंभ तिथि", + startTime: "प्रारंभ समय", + endDate: "समाप्ति तिथि", + endTime: "समाप्ति समय", + + // Action buttons + cancelButtonLabel: "रद्द करें", + clearButtonLabel: "साफ़ करें", + okButtonLabel: "ठीक है", + todayButtonLabel: "आज", + + // Toolbar titles + datePickerToolbarTitle: "तिथि चुनें", + dateTimePickerToolbarTitle: "तिथि और समय चुनें", + timePickerToolbarTitle: "समय चुनें", + dateRangePickerToolbarTitle: "तिथि सीमा चुनें", + + // Clock labels + clockLabelText: (view, time, utils, formattedTime) => + `${views[view] ?? view} चुनें। ${ + !formattedTime && (time === null || !utils.isValid(time)) + ? "कोई समय चयनित नहीं है" + : `चयनित समय ${formattedTime ?? utils.format(time, "fullTime")} है` + }`, + hoursClockNumberText: (hours) => `${hours} घंटे`, + minutesClockNumberText: (minutes) => `${minutes} मिनट`, + secondsClockNumberText: (seconds) => `${seconds} सेकंड`, + selectViewText: (view) => `${views[view] ?? view} चुनें`, + + // Calendar week number + calendarWeekNumberHeaderLabel: "सप्ताह संख्या", + calendarWeekNumberHeaderText: "#", + calendarWeekNumberAriaLabelText: (weekNumber) => `सप्ताह ${weekNumber}`, + calendarWeekNumberText: (weekNumber) => `${weekNumber}`, + + // Open picker dialogue (aria) + openDatePickerDialogue: (value, utils, formattedDate) => + formattedDate || (value !== null && utils.isValid(value)) + ? `तिथि चुनें, चयनित तिथि ${ + formattedDate ?? utils.format(value, "fullDate") + } है` + : "तिथि चुनें", + openTimePickerDialogue: (value, utils, formattedTime) => + formattedTime || (value !== null && utils.isValid(value)) + ? `समय चुनें, चयनित समय ${ + formattedTime ?? utils.format(value, "fullTime") + } है` + : "समय चुनें", + + // Field section clear + fieldClearLabel: "साफ़ करें", + + // Table aria labels + timeTableLabel: "समय चुनें", + dateTableLabel: "तिथि चुनें", + + // Field section placeholders + fieldYearPlaceholder: (params) => "व".repeat(params.digitAmount), + fieldMonthPlaceholder: (params) => + params.contentType === "letter" ? "मममम" : "मम", + fieldDayPlaceholder: () => "दद", + fieldWeekDayPlaceholder: (params) => + params.contentType === "letter" ? "वववव" : "वव", + fieldHoursPlaceholder: () => "घघ", + fieldMinutesPlaceholder: () => "मिमि", + fieldSecondsPlaceholder: () => "सेसे", + fieldMeridiemPlaceholder: () => "पूअ", + + // Section nouns + year: "वर्ष", + month: "माह", + day: "दिन", + weekDay: "सप्ताह का दिन", + hours: "घंटे", + minutes: "मिनट", + seconds: "सेकंड", + meridiem: "पूर्वाह्न/अपराह्न", + + // Empty value + empty: "खाली", +}; diff --git a/src/i18n/datepickers.ts b/src/i18n/datepickers.ts new file mode 100644 index 0000000..66f2748 --- /dev/null +++ b/src/i18n/datepickers.ts @@ -0,0 +1,24 @@ +import type { PickersLocaleText } from "@mui/x-date-pickers-pro"; +import * as datePickersLocales from "@mui/x-date-pickers-pro/locales"; + +import { hiIN } from "./datepickers-locales/hi-IN"; + +// Hand-authored bundles for locales MUI X Date Pickers doesn't ship. +// Add an entry here whenever a new unsupported locale is needed. +const OVERRIDES: Record>> = { + hiIN, +}; + +// Returns undefined (not enUS) for unmapped locales — MUI X treats undefined as +// "use built-in defaults", which are already English. An explicit enUS fallback +// would be redundant. This differs from MUI core, which always needs a locale +// object merged into the theme. +export const getDatePickersLocaleText = ( + locale: string | undefined +): Partial> | undefined => { + if (!locale) { return undefined; } + if (OVERRIDES[locale]) { return OVERRIDES[locale]; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const entry = datePickersLocales[locale as keyof typeof datePickersLocales] as any; + return entry?.components?.MuiLocalizationProvider?.defaultProps?.localeText; +}; diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..41159d6 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,2 @@ +export { localizeTheme } from "./mui-locale"; +export type { MuiLocaleString } from "./mui-locale"; diff --git a/src/i18n/mui-locale.ts b/src/i18n/mui-locale.ts new file mode 100644 index 0000000..49df8c9 --- /dev/null +++ b/src/i18n/mui-locale.ts @@ -0,0 +1,49 @@ +import { createTheme } from "@mui/material/styles"; +import type { Theme } from "@mui/material/styles"; +import * as muiCoreLocales from "@mui/material/locale"; +import { enUS } from "@mui/material/locale"; + +import { getDataGridLocaleText } from "./datagrid"; +import { getDatePickersLocaleText } from "./datepickers"; + +// All valid MUI locale strings. keyof typeof muiCoreLocales covers every locale +// MUI core ships automatically. Hand-authored locales for MUI X (e.g. "hiIN") +// are added here manually — one entry per bundle added to OVERRIDES in +// datagrid.ts / datepickers.ts. +export type MuiLocaleString = keyof typeof muiCoreLocales | "hiIN"; + +/** + * Apply MUI locale bundles onto a base theme for the given MUI locale string + * (e.g. "esES", "hiIN"). Covers MUI core, Data Grid, and Date Pickers. + * + * MUI core locales are resolved dynamically — any locale MUI ships is + * automatically supported with no changes needed here. Data Grid and Date + * Pickers locales are also resolved dynamically, with hand-authored bundles + * for locales MUI X doesn't ship (e.g. hiIN). + * + * The consuming app is responsible for converting its active language tag + * (e.g. "es-ES") to the MUI locale string (e.g. "esES") before calling this. + * Unmapped or undefined locales fall back to English. + */ +export const localizeTheme = ( + baseTheme: Theme, + locale: string | undefined +): Theme => + createTheme( + baseTheme, + (locale ? muiCoreLocales[locale as keyof typeof muiCoreLocales] : undefined) ?? enUS, + { + components: { + MuiDataGrid: { + defaultProps: { localeText: getDataGridLocaleText(locale) }, + }, + }, + }, + { + components: { + MuiLocalizationProvider: { + defaultProps: { localeText: getDatePickersLocaleText(locale) }, + }, + }, + } + ); diff --git a/src/index.ts b/src/index.ts index e4ca3da..4a8a379 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,3 +14,4 @@ export { default as SSOButtonGroup } from "./SSOButtonGroup"; export { default as SSOButton } from "./SSOButton"; export { IconButton } from "./IconButton"; export * from "./icons"; +export * from "./i18n"; From c5995c9e839b52f2e81866f08a7fcb29cae21f14 Mon Sep 17 00:00:00 2001 From: Nar Cuenca Date: Tue, 7 Jul 2026 12:44:23 +0800 Subject: [PATCH 2/3] Add support for custom label --- src/SSOButton/index.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/SSOButton/index.tsx b/src/SSOButton/index.tsx index fb92181..db8ca75 100644 --- a/src/SSOButton/index.tsx +++ b/src/SSOButton/index.tsx @@ -25,6 +25,13 @@ export interface SSOButtonProps { * Optional properties for underlying MUI Button component. */ ButtonProps?: ButtonProps; + + /** + * Optional pre-translated label to render instead of the default + * "Continue with {service}" text. Consumers are responsible for + * translation; this component does not perform any i18n itself. + */ + label?: string; } const ssoServiceMap = { @@ -42,7 +49,13 @@ const ssoServiceMap = { }, } -const SSOButton = ({ sx, service, onClick, ButtonProps }: SSOButtonProps) => { +const SSOButton = ({ + sx, + service, + onClick, + ButtonProps, + label, +}: SSOButtonProps) => { const ssoService = ssoServiceMap[service]; return ( @@ -60,7 +73,7 @@ const SSOButton = ({ sx, service, onClick, ButtonProps }: SSOButtonProps) => { sx={{ justifyContent: "flex-start", ...sx }} {...ButtonProps} > - Continue with {ssoService.name} + {label ?? `Continue with ${ssoService.name}`} ); }; From 7b46007712894fe633db8803de7637d45bae1bd6 Mon Sep 17 00:00:00 2001 From: Nar Cuenca Date: Mon, 13 Jul 2026 12:07:58 +0800 Subject: [PATCH 3/3] Remove custom hi-IN overrides for Data Grid and Date Pickers MUI X ships no Hindi locale for these components, so silently fall back to each package's built-in English defaults instead of maintaining hand-authored translations. --- LOCALIZATION.md | 23 +-- src/i18n/datagrid-locales/hi-IN.ts | 207 -------------------------- src/i18n/datagrid.ts | 9 -- src/i18n/datepickers-locales/hi-IN.ts | 115 -------------- src/i18n/datepickers.ts | 9 -- src/i18n/mui-locale.ts | 17 +-- 6 files changed, 11 insertions(+), 369 deletions(-) delete mode 100644 src/i18n/datagrid-locales/hi-IN.ts delete mode 100644 src/i18n/datepickers-locales/hi-IN.ts diff --git a/LOCALIZATION.md b/LOCALIZATION.md index bb122a4..e0c23fc 100644 --- a/LOCALIZATION.md +++ b/LOCALIZATION.md @@ -1,8 +1,8 @@ # Adding a New Language -This guide covers adding MUI built-in component label support for a new language — things like DataGrid column menus, DatePicker buttons, and Autocomplete labels. These are separate from any i18next/app-level strings and are resolved through MUI's own locale bundle system. +This guide covers MUI built-in component label support for a language — things like DataGrid column menus, DatePicker buttons, and Autocomplete labels. These are separate from any i18next/app-level strings and are resolved through MUI's own locale bundle system. -`localizeTheme(theme, muiLocaleString)` is the single entry point. It accepts a MUI locale string (e.g. `"frFR"`) and resolves the correct bundles for MUI core, Data Grid, and Date Pickers dynamically — no changes needed here for locales MUI already ships. The only time this repo needs updating is when **MUI X doesn't ship a locale** and a hand-authored bundle is required. +`localizeTheme(theme, muiLocaleString)` is the single entry point. It accepts a MUI locale string (e.g. `"frFR"`) and resolves the correct bundles for MUI core, Data Grid, and Date Pickers dynamically against MUI's own packages — no hand-authored overrides are maintained in this repo. If a package doesn't ship a given locale, that package silently falls back to its built-in English defaults. --- @@ -18,8 +18,6 @@ import { frFR } from "@mui/material/locale"; If it resolves, MUI core is handled automatically — no changes needed in this repo. -If it doesn't resolve, a hand-authored core bundle is needed. This is rare — MUI core ships bundles for most major languages. - ### 2. Check MUI X Data Grid Try importing the locale from `@mui/x-data-grid-pro/locales`: @@ -28,18 +26,7 @@ Try importing the locale from `@mui/x-data-grid-pro/locales`: import { frFR } from "@mui/x-data-grid-pro/locales"; ``` -**If it resolves** — no changes needed, the dynamic resolver in `src/i18n/datagrid.ts` picks it up automatically. - -**If it doesn't resolve** — create a hand-authored bundle at `src/i18n/datagrid-locales/.ts`. Use `src/i18n/datagrid-locales/hi-IN.ts` as a reference, then register it in the `OVERRIDES` map in `src/i18n/datagrid.ts`: - -```ts -import { frFR } from "./datagrid-locales/fr-FR"; - -const OVERRIDES: Record> = { - hiIN, - frFR, // add here -}; -``` +If it resolves, the dynamic resolver in `src/i18n/datagrid.ts` picks it up automatically — no changes needed. If it doesn't resolve, that locale isn't supported for Data Grid; it falls back to English until MUI ships it. ### 3. Check MUI X Date Pickers @@ -49,9 +36,7 @@ Same pattern — try importing from `@mui/x-date-pickers-pro/locales`: import { frFR } from "@mui/x-date-pickers-pro/locales"; ``` -**If it resolves** — no changes needed. - -**If it doesn't resolve** — create `src/i18n/datepickers-locales/.ts` using `src/i18n/datepickers-locales/hi-IN.ts` as a reference, then register it in `OVERRIDES` in `src/i18n/datepickers.ts`. +If it resolves, no changes needed. If it doesn't resolve, that locale falls back to English until MUI ships it. ### 4. Build and publish diff --git a/src/i18n/datagrid-locales/hi-IN.ts b/src/i18n/datagrid-locales/hi-IN.ts deleted file mode 100644 index 9145c83..0000000 --- a/src/i18n/datagrid-locales/hi-IN.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { GridLocaleText } from "@mui/x-data-grid-pro"; - -// Custom MUI X Data Grid localeText for Hindi (hi-IN). -// -// MUI X ships no Hindi Data Grid locale, so we supply our own. This is a FULL -// translation of GridLocaleText (every key MUI exposes), not a reachable-only -// subset — the grid is used across the whole app in many configurations -// (toolbar, filtering, export, row selection, aggregation, detail panels…), -// so translating everything avoids silent English fallback when any grid -// enables a feature. -// -// Typed Partial: any key MUI adds in a future version that we -// haven't translated yet falls back to its built-in English rather than -// erroring. The 7 function entries mirror MUI's default signatures. -// -// To add another unshipped locale: create a sibling file here exporting a -// Partial and register it in ../datagrid.ts. -export const hiIN: Partial = { - // Overlays - noRowsLabel: "कोई पंक्तियाँ नहीं हैं", - noResultsOverlayLabel: "कोई परिणाम नहीं मिला।", - - // Toolbar — density - toolbarDensity: "घनत्व", - toolbarDensityLabel: "घनत्व", - toolbarDensityCompact: "संकुचित", - toolbarDensityStandard: "मानक", - toolbarDensityComfortable: "आरामदायक", - - // Toolbar — columns - toolbarColumns: "कॉलम", - toolbarColumnsLabel: "कॉलम चुनें", - - // Toolbar — filters - toolbarFilters: "फ़िल्टर", - toolbarFiltersLabel: "फ़िल्टर दिखाएं", - toolbarFiltersTooltipHide: "फ़िल्टर छुपाएं", - toolbarFiltersTooltipShow: "फ़िल्टर दिखाएं", - toolbarFiltersTooltipActive: (count) => `${count} सक्रिय फ़िल्टर`, - - // Toolbar — quick filter - toolbarQuickFilterPlaceholder: "खोजें…", - toolbarQuickFilterLabel: "खोजें", - toolbarQuickFilterDeleteIconLabel: "साफ़ करें", - - // Toolbar — export - toolbarExport: "निर्यात", - toolbarExportLabel: "निर्यात", - toolbarExportCSV: "CSV के रूप में डाउनलोड करें", - toolbarExportPrint: "प्रिंट करें", - toolbarExportExcel: "Excel के रूप में डाउनलोड करें", - - // Columns management panel - columnsManagementSearchTitle: "खोजें", - columnsManagementNoColumns: "कोई कॉलम नहीं", - columnsManagementShowHideAllText: "सभी दिखाएं/छुपाएं", - columnsManagementReset: "रीसेट करें", - columnsManagementDeleteIconLabel: "साफ़ करें", - - // Filter panel - filterPanelAddFilter: "फ़िल्टर जोड़ें", - filterPanelRemoveAll: "सभी हटाएं", - filterPanelDeleteIconLabel: "हटाएं", - filterPanelLogicOperator: "तार्किक ऑपरेटर", - filterPanelOperator: "ऑपरेटर", - filterPanelOperatorAnd: "और", - filterPanelOperatorOr: "या", - filterPanelColumns: "कॉलम", - filterPanelInputLabel: "मान", - filterPanelInputPlaceholder: "फ़िल्टर मान", - - // Filter operators - filterOperatorContains: "शामिल है", - filterOperatorDoesNotContain: "शामिल नहीं है", - filterOperatorEquals: "बराबर है", - filterOperatorDoesNotEqual: "बराबर नहीं है", - filterOperatorStartsWith: "से शुरू होता है", - filterOperatorEndsWith: "से समाप्त होता है", - filterOperatorIs: "है", - filterOperatorNot: "नहीं है", - filterOperatorAfter: "के बाद है", - filterOperatorOnOrAfter: "को या उसके बाद है", - filterOperatorBefore: "के पहले है", - filterOperatorOnOrBefore: "को या उससे पहले है", - filterOperatorIsEmpty: "खाली है", - filterOperatorIsNotEmpty: "खाली नहीं है", - filterOperatorIsAnyOf: "इनमें से कोई है", - "filterOperator=": "=", - "filterOperator!=": "!=", - "filterOperator>": ">", - "filterOperator>=": ">=", - "filterOperator<": "<", - "filterOperator<=": "<=", - - // Header filter operators - headerFilterOperatorContains: "शामिल है", - headerFilterOperatorDoesNotContain: "शामिल नहीं है", - headerFilterOperatorEquals: "बराबर है", - headerFilterOperatorDoesNotEqual: "बराबर नहीं है", - headerFilterOperatorStartsWith: "से शुरू होता है", - headerFilterOperatorEndsWith: "से समाप्त होता है", - headerFilterOperatorIs: "है", - headerFilterOperatorNot: "नहीं है", - headerFilterOperatorAfter: "के बाद है", - headerFilterOperatorOnOrAfter: "को या उसके बाद है", - headerFilterOperatorBefore: "के पहले है", - headerFilterOperatorOnOrBefore: "को या उससे पहले है", - headerFilterOperatorIsEmpty: "खाली है", - headerFilterOperatorIsNotEmpty: "खाली नहीं है", - headerFilterOperatorIsAnyOf: "इनमें से कोई है", - "headerFilterOperator=": "बराबर है", - "headerFilterOperator!=": "बराबर नहीं है", - "headerFilterOperator>": "इससे बड़ा है", - "headerFilterOperator>=": "इससे बड़ा या बराबर है", - "headerFilterOperator<": "इससे छोटा है", - "headerFilterOperator<=": "इससे छोटा या बराबर है", - - // Filter values - filterValueAny: "कोई भी", - filterValueTrue: "सत्य", - filterValueFalse: "असत्य", - - // Column menu - columnMenuLabel: "मेनू", - columnMenuAriaLabel: (columnName) => `${columnName} कॉलम मेनू`, - columnMenuShowColumns: "कॉलम दिखाएं", - columnMenuManageColumns: "कॉलम प्रबंधित करें", - columnMenuFilter: "फ़िल्टर", - columnMenuHideColumn: "कॉलम छुपाएं", - columnMenuUnsort: "क्रमबद्धता हटाएं", - columnMenuSortAsc: "बढ़ते क्रम (ASC) में क्रमबद्ध करें", - columnMenuSortDesc: "घटते क्रम (DESC) में क्रमबद्ध करें", - - // Column header - columnHeaderFiltersTooltipActive: (count) => `${count} सक्रिय फ़िल्टर`, - columnHeaderFiltersLabel: "फ़िल्टर दिखाएं", - columnHeaderSortIconLabel: "क्रमबद्ध करें", - - // Footer - footerRowSelected: (count) => - count !== 1 - ? `${count.toLocaleString()} पंक्तियाँ चयनित` - : `${count.toLocaleString()} पंक्ति चयनित`, - footerTotalRows: "कुल पंक्तियाँ:", - footerTotalVisibleRows: (visibleCount, totalCount) => - `${totalCount.toLocaleString()} में से ${visibleCount.toLocaleString()}`, - - // Checkbox selection - checkboxSelectionHeaderName: "चेकबॉक्स चयन", - checkboxSelectionSelectAllRows: "सभी पंक्तियाँ चुनें", - checkboxSelectionUnselectAllRows: "सभी पंक्तियों का चयन रद्द करें", - checkboxSelectionSelectRow: "पंक्ति चुनें", - checkboxSelectionUnselectRow: "पंक्ति का चयन रद्द करें", - - // Boolean cell - booleanCellTrueLabel: "हाँ", - booleanCellFalseLabel: "नहीं", - - // Actions cell - actionsCellMore: "अधिक", - - // Column pinning (DataGridPro) - pinToLeft: "बाईं ओर पिन करें", - pinToRight: "दाईं ओर पिन करें", - unpin: "पिन हटाएं", - - // Tree data - treeDataGroupingHeaderName: "समूह", - treeDataExpand: "उप-आइटम देखें", - treeDataCollapse: "उप-आइटम छुपाएं", - - // Grouping - groupingColumnHeaderName: "समूह", - groupColumn: (name) => `${name} के अनुसार समूहित करें`, - unGroupColumn: (name) => `${name} के अनुसार समूहन रोकें`, - - // Master/detail - detailPanelToggle: "विवरण पैनल टॉगल करें", - expandDetailPanel: "विस्तृत करें", - collapseDetailPanel: "संक्षिप्त करें", - - // Row reordering - rowReorderingHeaderName: "पंक्ति पुनर्क्रमण", - - // Aggregation - aggregationMenuItemHeader: "समुच्चय", - aggregationFunctionLabelSum: "योग", - aggregationFunctionLabelAvg: "औसत", - aggregationFunctionLabelMin: "न्यूनतम", - aggregationFunctionLabelMax: "अधिकतम", - aggregationFunctionLabelSize: "संख्या", - - // Pagination footer - MuiTablePagination: { - labelRowsPerPage: "प्रति पृष्ठ पंक्तियाँ:", - labelDisplayedRows: ({ from, to, count }) => - `${ - count !== -1 ? count.toLocaleString() : `${to.toLocaleString()} से अधिक` - } में से ${from.toLocaleString()}–${to.toLocaleString()}`, - getItemAriaLabel: (type) => { - if (type === "first") { return "पहले पृष्ठ पर जाएं"; } - if (type === "last") { return "अंतिम पृष्ठ पर जाएं"; } - if (type === "next") { return "अगले पृष्ठ पर जाएं"; } - return "पिछले पृष्ठ पर जाएं"; - }, - }, -}; diff --git a/src/i18n/datagrid.ts b/src/i18n/datagrid.ts index 0ab2dec..d1dbff7 100644 --- a/src/i18n/datagrid.ts +++ b/src/i18n/datagrid.ts @@ -1,14 +1,6 @@ import type { GridLocaleText } from "@mui/x-data-grid-pro"; import * as dataGridLocales from "@mui/x-data-grid-pro/locales"; -import { hiIN } from "./datagrid-locales/hi-IN"; - -// Hand-authored bundles for locales MUI X Data Grid doesn't ship. -// Add an entry here whenever a new unsupported locale is needed. -const OVERRIDES: Record> = { - hiIN, -}; - // Returns undefined (not enUS) for unmapped locales — MUI X treats undefined as // "use built-in defaults", which are already English. An explicit enUS fallback // would be redundant. This differs from MUI core, which always needs a locale @@ -17,7 +9,6 @@ export const getDataGridLocaleText = ( locale: string | undefined ): Partial | undefined => { if (!locale) { return undefined; } - if (OVERRIDES[locale]) { return OVERRIDES[locale]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const entry = dataGridLocales[locale as keyof typeof dataGridLocales] as any; return entry?.components?.MuiDataGrid?.defaultProps?.localeText; diff --git a/src/i18n/datepickers-locales/hi-IN.ts b/src/i18n/datepickers-locales/hi-IN.ts deleted file mode 100644 index 2a70702..0000000 --- a/src/i18n/datepickers-locales/hi-IN.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { PickersLocaleText } from "@mui/x-date-pickers-pro"; - -// Custom MUI X Date Pickers localeText for Hindi (hi-IN). -// -// MUI X ships no Hindi Date Pickers locale (es/zh/ru/nl only), so we supply -// our own. Covers picker UI strings MUI renders itself (OK/Cancel/Clear/Today -// buttons, toolbar titles, clock/calendar aria labels, field placeholders). -// -// Typed Partial>: any key MUI adds in a future version -// falls back to its built-in English rather than erroring. -// -// To add another unshipped locale: create a sibling file exporting a -// Partial> and export it from this package. -const views: Record = { - hours: "घंटे", - minutes: "मिनट", - seconds: "सेकंड", - meridiem: "पूर्वाह्न/अपराह्न", -}; - -export const hiIN: Partial> = { - // Calendar navigation - previousMonth: "पिछला महीना", - nextMonth: "अगला महीना", - openPreviousView: "पिछला दृश्य खोलें", - openNextView: "अगला दृश्य खोलें", - calendarViewSwitchingButtonAriaLabel: (view) => - view === "year" - ? "वर्ष दृश्य खुला है, कैलेंडर दृश्य पर जाएं" - : "कैलेंडर दृश्य खुला है, वर्ष दृश्य पर जाएं", - - // Range labels - start: "प्रारंभ", - end: "समाप्त", - startDate: "प्रारंभ तिथि", - startTime: "प्रारंभ समय", - endDate: "समाप्ति तिथि", - endTime: "समाप्ति समय", - - // Action buttons - cancelButtonLabel: "रद्द करें", - clearButtonLabel: "साफ़ करें", - okButtonLabel: "ठीक है", - todayButtonLabel: "आज", - - // Toolbar titles - datePickerToolbarTitle: "तिथि चुनें", - dateTimePickerToolbarTitle: "तिथि और समय चुनें", - timePickerToolbarTitle: "समय चुनें", - dateRangePickerToolbarTitle: "तिथि सीमा चुनें", - - // Clock labels - clockLabelText: (view, time, utils, formattedTime) => - `${views[view] ?? view} चुनें। ${ - !formattedTime && (time === null || !utils.isValid(time)) - ? "कोई समय चयनित नहीं है" - : `चयनित समय ${formattedTime ?? utils.format(time, "fullTime")} है` - }`, - hoursClockNumberText: (hours) => `${hours} घंटे`, - minutesClockNumberText: (minutes) => `${minutes} मिनट`, - secondsClockNumberText: (seconds) => `${seconds} सेकंड`, - selectViewText: (view) => `${views[view] ?? view} चुनें`, - - // Calendar week number - calendarWeekNumberHeaderLabel: "सप्ताह संख्या", - calendarWeekNumberHeaderText: "#", - calendarWeekNumberAriaLabelText: (weekNumber) => `सप्ताह ${weekNumber}`, - calendarWeekNumberText: (weekNumber) => `${weekNumber}`, - - // Open picker dialogue (aria) - openDatePickerDialogue: (value, utils, formattedDate) => - formattedDate || (value !== null && utils.isValid(value)) - ? `तिथि चुनें, चयनित तिथि ${ - formattedDate ?? utils.format(value, "fullDate") - } है` - : "तिथि चुनें", - openTimePickerDialogue: (value, utils, formattedTime) => - formattedTime || (value !== null && utils.isValid(value)) - ? `समय चुनें, चयनित समय ${ - formattedTime ?? utils.format(value, "fullTime") - } है` - : "समय चुनें", - - // Field section clear - fieldClearLabel: "साफ़ करें", - - // Table aria labels - timeTableLabel: "समय चुनें", - dateTableLabel: "तिथि चुनें", - - // Field section placeholders - fieldYearPlaceholder: (params) => "व".repeat(params.digitAmount), - fieldMonthPlaceholder: (params) => - params.contentType === "letter" ? "मममम" : "मम", - fieldDayPlaceholder: () => "दद", - fieldWeekDayPlaceholder: (params) => - params.contentType === "letter" ? "वववव" : "वव", - fieldHoursPlaceholder: () => "घघ", - fieldMinutesPlaceholder: () => "मिमि", - fieldSecondsPlaceholder: () => "सेसे", - fieldMeridiemPlaceholder: () => "पूअ", - - // Section nouns - year: "वर्ष", - month: "माह", - day: "दिन", - weekDay: "सप्ताह का दिन", - hours: "घंटे", - minutes: "मिनट", - seconds: "सेकंड", - meridiem: "पूर्वाह्न/अपराह्न", - - // Empty value - empty: "खाली", -}; diff --git a/src/i18n/datepickers.ts b/src/i18n/datepickers.ts index 66f2748..83b3fd0 100644 --- a/src/i18n/datepickers.ts +++ b/src/i18n/datepickers.ts @@ -1,14 +1,6 @@ import type { PickersLocaleText } from "@mui/x-date-pickers-pro"; import * as datePickersLocales from "@mui/x-date-pickers-pro/locales"; -import { hiIN } from "./datepickers-locales/hi-IN"; - -// Hand-authored bundles for locales MUI X Date Pickers doesn't ship. -// Add an entry here whenever a new unsupported locale is needed. -const OVERRIDES: Record>> = { - hiIN, -}; - // Returns undefined (not enUS) for unmapped locales — MUI X treats undefined as // "use built-in defaults", which are already English. An explicit enUS fallback // would be redundant. This differs from MUI core, which always needs a locale @@ -17,7 +9,6 @@ export const getDatePickersLocaleText = ( locale: string | undefined ): Partial> | undefined => { if (!locale) { return undefined; } - if (OVERRIDES[locale]) { return OVERRIDES[locale]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const entry = datePickersLocales[locale as keyof typeof datePickersLocales] as any; return entry?.components?.MuiLocalizationProvider?.defaultProps?.localeText; diff --git a/src/i18n/mui-locale.ts b/src/i18n/mui-locale.ts index 49df8c9..32d2c63 100644 --- a/src/i18n/mui-locale.ts +++ b/src/i18n/mui-locale.ts @@ -6,20 +6,17 @@ import { enUS } from "@mui/material/locale"; import { getDataGridLocaleText } from "./datagrid"; import { getDatePickersLocaleText } from "./datepickers"; -// All valid MUI locale strings. keyof typeof muiCoreLocales covers every locale -// MUI core ships automatically. Hand-authored locales for MUI X (e.g. "hiIN") -// are added here manually — one entry per bundle added to OVERRIDES in -// datagrid.ts / datepickers.ts. -export type MuiLocaleString = keyof typeof muiCoreLocales | "hiIN"; +// All valid MUI locale strings — keyof typeof muiCoreLocales covers every +// locale MUI core ships. +export type MuiLocaleString = keyof typeof muiCoreLocales; /** * Apply MUI locale bundles onto a base theme for the given MUI locale string - * (e.g. "esES", "hiIN"). Covers MUI core, Data Grid, and Date Pickers. + * (e.g. "esES"). Covers MUI core, Data Grid, and Date Pickers. * - * MUI core locales are resolved dynamically — any locale MUI ships is - * automatically supported with no changes needed here. Data Grid and Date - * Pickers locales are also resolved dynamically, with hand-authored bundles - * for locales MUI X doesn't ship (e.g. hiIN). + * Every locale is resolved dynamically against MUI's own bundles — no + * hand-authored overrides. Locales unsupported by a given package fall back + * to that package's built-in English defaults. * * The consuming app is responsible for converting its active language tag * (e.g. "es-ES") to the MUI locale string (e.g. "esES") before calling this.