feat(admin-*,dashboard): add dashboard i18n extensions (#13763)
* virtual i18n module
* changeset
* fallback ns
fallback to the default "translation" ns if the key isnt found. Allows to use a single "useTranslation("customNs")" hook for both custom and medusa-provided keys
* simplify merges
* optional for backward compat
* fix HMR
* fix generated deepMerge
* test
This commit is contained in:
@@ -3,6 +3,7 @@ import { DashboardPlugin } from "./dashboard-app/types"
|
||||
|
||||
import displayModule from "virtual:medusa/displays"
|
||||
import formModule from "virtual:medusa/forms"
|
||||
import i18nModule from "virtual:medusa/i18n"
|
||||
import menuItemModule from "virtual:medusa/menu-items"
|
||||
import routeModule from "virtual:medusa/routes"
|
||||
import widgetModule from "virtual:medusa/widgets"
|
||||
@@ -15,6 +16,7 @@ const localPlugin = {
|
||||
displayModule,
|
||||
formModule,
|
||||
menuItemModule,
|
||||
i18nModule,
|
||||
}
|
||||
|
||||
interface AppProps {
|
||||
|
||||
@@ -3,12 +3,16 @@ import LanguageDetector from "i18next-browser-languagedetector"
|
||||
import { initReactI18next } from "react-i18next"
|
||||
|
||||
import { defaultI18nOptions } from "../../../i18n/config"
|
||||
import { useExtension } from "../../../providers/extension-provider"
|
||||
|
||||
export const I18n = () => {
|
||||
const { getI18nResources } = useExtension()
|
||||
|
||||
if (i18n.isInitialized) {
|
||||
return null
|
||||
}
|
||||
|
||||
const resources = getI18nResources()
|
||||
i18n
|
||||
.use(
|
||||
new LanguageDetector(null, {
|
||||
@@ -17,7 +21,11 @@ export const I18n = () => {
|
||||
})
|
||||
)
|
||||
.use(initReactI18next)
|
||||
.init(defaultI18nOptions)
|
||||
.init({
|
||||
...defaultI18nOptions,
|
||||
resources,
|
||||
supportedLngs: Object.keys(resources),
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
CustomFieldFormTab,
|
||||
CustomFieldFormZone,
|
||||
CustomFieldModel,
|
||||
deepMerge,
|
||||
InjectionZone,
|
||||
NESTED_ROUTE_POSITIONS,
|
||||
} from "@medusajs/admin-shared"
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
} from "react-router-dom"
|
||||
import { INavItem } from "../components/layout/nav-item"
|
||||
import { Providers } from "../providers"
|
||||
import coreTranslations from "../i18n/translations"
|
||||
import { getRouteMap } from "./routes/get-route.map"
|
||||
import { createRouteMap, getRouteExtensions } from "./routes/utils"
|
||||
import {
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
FormFieldExtension,
|
||||
FormFieldMap,
|
||||
FormZoneMap,
|
||||
I18nExtension,
|
||||
MenuItemExtension,
|
||||
MenuItemKey,
|
||||
MenuMap,
|
||||
@@ -47,6 +50,7 @@ export class DashboardApp {
|
||||
private displays: DisplayMap
|
||||
private coreRoutes: RouteObject[]
|
||||
private settingsRoutes: RouteObject[]
|
||||
private i18nResources: I18nExtension
|
||||
|
||||
constructor({ plugins }: DashboardAppProps) {
|
||||
this.widgets = this.populateWidgets(plugins)
|
||||
@@ -60,6 +64,7 @@ export class DashboardApp {
|
||||
this.fields = fields
|
||||
this.configs = configs
|
||||
this.displays = this.populateDisplays(plugins)
|
||||
this.i18nResources = this.populateI18n(plugins)
|
||||
}
|
||||
|
||||
private populateRoutes(plugins: DashboardPlugin[]) {
|
||||
@@ -378,6 +383,18 @@ export class DashboardApp {
|
||||
return displays
|
||||
}
|
||||
|
||||
private populateI18n(
|
||||
plugins: DashboardPlugin[]
|
||||
): I18nExtension {
|
||||
let resources: I18nExtension = { ...coreTranslations }
|
||||
|
||||
for (const plugin of plugins) {
|
||||
resources = deepMerge(resources, plugin.i18nModule?.resources)
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
private processDisplays(
|
||||
displays: DisplayExtension[]
|
||||
): Map<CustomFieldContainerZone, React.ComponentType<{ data: any }>[]> {
|
||||
@@ -431,6 +448,10 @@ export class DashboardApp {
|
||||
return this.displays.get(model)?.get(zone) || []
|
||||
}
|
||||
|
||||
private getI18nResources() {
|
||||
return this.i18nResources
|
||||
}
|
||||
|
||||
get api() {
|
||||
return {
|
||||
getMenu: this.getMenu.bind(this),
|
||||
@@ -438,6 +459,7 @@ export class DashboardApp {
|
||||
getFormFields: this.getFormFields.bind(this),
|
||||
getFormConfigs: this.getFormConfigs.bind(this),
|
||||
getDisplays: this.getDisplays.bind(this),
|
||||
getI18nResources: this.getI18nResources.bind(this),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ export type ConfigExtension = {
|
||||
fields: Record<string, ConfigFieldExtension>
|
||||
}
|
||||
|
||||
export type I18nExtension = Record<string, Record<string, any>>
|
||||
|
||||
export type LinkModule = {
|
||||
links: Record<CustomFieldModel, (string | string[])[]>
|
||||
}
|
||||
@@ -90,6 +92,10 @@ export type MenuItemModule = {
|
||||
menuItems: MenuItemExtension[]
|
||||
}
|
||||
|
||||
export type I18nModule = {
|
||||
resources: I18nExtension
|
||||
}
|
||||
|
||||
export type MenuItemKey = "coreExtensions" | "settingsExtensions"
|
||||
|
||||
export type FormField = FormFieldExtension & {
|
||||
@@ -131,4 +137,5 @@ export type DashboardPlugin = {
|
||||
menuItemModule: MenuItemModule
|
||||
widgetModule: WidgetModule
|
||||
routeModule: RouteModule
|
||||
i18nModule?: I18nModule
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { InitOptions } from "i18next"
|
||||
|
||||
import translations from "./translations"
|
||||
|
||||
export const defaultI18nOptions: InitOptions = {
|
||||
debug: process.env.NODE_ENV === "development",
|
||||
detection: {
|
||||
@@ -11,9 +9,8 @@ export const defaultI18nOptions: InitOptions = {
|
||||
order: ["cookie", "localStorage", "header"],
|
||||
},
|
||||
fallbackLng: "en",
|
||||
fallbackNS: "translation",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
resources: translations,
|
||||
supportedLngs: Object.keys(translations),
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -33,3 +33,9 @@ declare module "virtual:medusa/widgets" {
|
||||
const widgetModule: WidgetModule
|
||||
export default widgetModule
|
||||
}
|
||||
|
||||
declare module "virtual:medusa/i18n" {
|
||||
import type { I18nModule } from "./extensions"
|
||||
const i18nModule: I18nModule
|
||||
export default i18nModule
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user