diff --git a/plugin_creator/frontend.py b/plugin_creator/frontend.py index b5ce5af..122a215 100644 --- a/plugin_creator/frontend.py +++ b/plugin_creator/frontend.py @@ -112,9 +112,10 @@ def update_frontend(plugin_dir: str, context: dict) -> None: remove_file(plugin_dir, "frontend", "src", f"{feature.capitalize()}.tsx") if not translation: + # Remove translation files and configuration from the frontend code remove_dir(plugin_dir, "frontend", "src", "locales") remove_file(plugin_dir, "frontend", "src", "locales.tsx") - remove_file(plugin_dir, "frontend", ".linguirc") + remove_file(plugin_dir, "frontend", "lingui.config.ts") # Remove the translation check from the CI configuration remove_file(plugin_dir, ".github", "workflows", "translations.yaml") diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/Panel.tsx b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/Panel.tsx index 32815a6..879700f 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/Panel.tsx +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/Panel.tsx @@ -8,7 +8,8 @@ import { useQuery } from '@tanstack/react-query'; {% if cookiecutter.frontend.translation -%} import { t } from '@lingui/core/macro'; -import { LocalizedComponent } from './locale'; +import { LocalizedComponent } from '@inventreedb/ui'; +import { loadLocale } from './locales'; {%- endif %} // Import for type checking @@ -161,18 +162,22 @@ function {{ cookiecutter.plugin_name }}Panel({ {%- endif %} - + + {partId && } + + Counter: {counter} + {instance ? ( {instance} @@ -254,7 +259,7 @@ export function Render{{ cookiecutter.plugin_name }}Panel(context: InvenTreePlug {% if cookiecutter.frontend.translation -%} return ( - + <{{ cookiecutter.plugin_name }}Panel context={context} /> ); diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locale.tsx b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locale.tsx deleted file mode 100644 index bd130d1..0000000 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locale.tsx +++ /dev/null @@ -1,89 +0,0 @@ - -import { i18n } from '@lingui/core'; -import { Skeleton } from '@mantine/core'; -import { useEffect, useState } from 'react'; -import { I18nProvider } from '@lingui/react'; - - -/** - * Attempt to load the locale file for the given locale, returning null if it fails - */ -async function tryLoadLocale(locale: string): Promise { - try { - const messages = await import(`./locales/${locale}/messages.ts`); - return messages; - } catch (error) { - console.warn(`Failed to load locale ${locale}`); - return null; - } -} - - -/** - * Helper function to dynamically load frontend translations, - * based on the provided locale. - */ -async function loadPluginLocale(locale: string) { - - let messages = null; - - // Find the most specific locale file possible, with fallbacks to less specific locales if necessary - messages = await tryLoadLocale(locale); - - if (!messages && locale.includes('-')) { - const fallbackLocale = locale.split('-')[0]; - console.debug(`Locale ${locale} not found, trying fallback locale ${fallbackLocale}`); - messages = await tryLoadLocale(fallbackLocale); - } - - if (!messages && locale.includes('_')) { - const fallbackLocale = locale.split('_')[0]; - console.debug(`Locale ${locale} not found, trying fallback locale ${fallbackLocale}`); - messages = await tryLoadLocale(fallbackLocale); - } - - if (!messages && locale !== 'en') { - console.debug(`Locale ${locale} not found, trying fallback locale en`); - messages = await tryLoadLocale('en'); - } - - if (messages?.messages) { - i18n.load(locale, messages.messages); - i18n.activate(locale); - } else { - console.error(`Failed to load any locale for ${locale}`); - } -} - - -// Wrapper component for loading dynamic translations -export function LocalizedComponent({ - locale, - children -}: { - locale: string, - children: React.ReactNode -}) { - - const [loaded, setLoaded] = useState(false); - - // Reload componentwhen the locale changes - useEffect(() => { - setLoaded(false); - loadPluginLocale(locale).then(() => { - setLoaded(true); - }); - }, [locale]); - - if (!loaded) { - return ( - - ); - } - - return ( - - {children} - - ); -} diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locales.tsx b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locales.tsx new file mode 100644 index 0000000..381910b --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/src/locales.tsx @@ -0,0 +1,4 @@ +import { type LocaleLoader } from '@inventreedb/ui'; + +// Necessary callback function to dynamically load the locale messages for the plugin +export const loadLocale: LocaleLoader = async (locale: string) => import(`./locales/${locale}/messages.ts`).catch(() => null); diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/vite.dev.config.ts b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/vite.dev.config.ts index 67338aa..eefaae6 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/vite.dev.config.ts +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/frontend/vite.dev.config.ts @@ -2,47 +2,13 @@ import { defineConfig } from 'vite' import { viteExternalsPlugin } from 'vite-plugin-externals' import viteConfig, { externalLibs } from './vite.config' +import InventreeHmrPlugin from '@inventreedb/ui/vite'; {% if cookiecutter.frontend.translation -%} import react from "@vitejs/plugin-react-swc" import { lingui } from "@lingui/vite-plugin" {%- endif %} -import type { Plugin } from 'vite' - -// Enable HMR support for this plugin by hooking into the InvenTree vite dev server -function inventreeHmrPlugin(): Plugin { - const fileRegex = /\.(js|jsx|ts|tsx)(\?|$)/; - - const hmrBlock = [ - '', - '// __inventree_hmr_injected__', - 'if (import.meta.hot) {', - ' import.meta.hot.accept((newModule) => {', - ' const key = new URL(import.meta.url).origin + new URL(import.meta.url).pathname;', - ' window.__plugin_hmr_callbacks?.[key]?.forEach(callback => {', - ' callback(newModule);', - ' });', - ' })', - '}', - ]; - - return { - name: 'inventree-hmr-plugin', - enforce: 'post', - - transform(code, id) { - if (!fileRegex.test(id)) return; - if (id.includes('node_modules')) return; - if (code.includes('__inventree_hmr_injected__')) return; - - return { - code: code + hmrBlock.join('\n'), - map: null, - } - } - } -} /** * Vite config to run the frontend plugin in development mode. @@ -81,7 +47,7 @@ export default defineConfig((cfg) => { }), {%- endif %} viteExternalsPlugin(externalLibs), - inventreeHmrPlugin(), + InventreeHmrPlugin(), ]; return config;