diff --git a/.current-ai-plugin-version b/.current-ai-plugin-version index b005e307..9097948f 100644 --- a/.current-ai-plugin-version +++ b/.current-ai-plugin-version @@ -1 +1 @@ -0.2.15 +0.2.16 diff --git a/.gitignore b/.gitignore index e65e0822..e3ea25ec 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,7 @@ _ci_* .pnpm-store # Test artifacts -packages/*/test/webpack5-angular-project \ No newline at end of file +packages/*/test/webpack5-angular-project + +# Playwright MCP screenshots +.playwright-mcp/*.png \ No newline at end of file diff --git a/CHANGELOG-AI.md b/CHANGELOG-AI.md index b4055019..51397c76 100644 --- a/CHANGELOG-AI.md +++ b/CHANGELOG-AI.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/examples/ai/src/App.tsx b/examples/ai/src/App.tsx index 038de2f1..3c4c3633 100644 --- a/examples/ai/src/App.tsx +++ b/examples/ai/src/App.tsx @@ -36,6 +36,8 @@ function App() { console.log('Creating CreativeEditorSDK...'); CreativeEditorSDK.create(domElement, { license: import.meta.env.VITE_CESDK_LICENSE_KEY, + // Set to 'de' to test German translations + locale: 'en', userId: 'plugins-vercel', callbacks: { onUpload: 'local', @@ -101,11 +103,18 @@ function App() { providerPartner = 'fal-ai'; } - // Update URL with current parameters + // Check if test translations should be applied during init + const testTranslationsParam = searchParams.get('translations') === 'true'; + + // Update URL with current parameters (preserve translations param) const currentArchive = searchParams.get('archive'); const currentPartner = searchParams.get('partner'); if (currentArchive !== archiveType || currentPartner !== providerPartner) { - setSearchParams({ archive: archiveType, partner: providerPartner }, { replace: true }); + const newParams: Record = { archive: archiveType, partner: providerPartner }; + if (testTranslationsParam) { + newParams.translations = 'true'; + } + setSearchParams(newParams, { replace: true }); } // Handle photo editor mode @@ -234,7 +243,7 @@ function App() { const partnerProviders = getPartnerProviders(); - instance.addPlugin( + await instance.addPlugin( AiApps({ debug: true, dryRun: false, @@ -280,6 +289,15 @@ function App() { ); } + // Apply test translations AFTER plugins are loaded (simulates integrator workflow) + // This must happen after addPlugin() since plugins set their own default translations + if (testTranslationsParam) { + testAllTranslations(instance); + // Expose reset function for debugging + // @ts-ignore + window.resetTranslations = () => resetTranslations(instance); + } + instance.ui.setNavigationBarOrder([ 'sceneModeToggle', 'providerPartnerToggle', diff --git a/examples/ai/src/translations/apps.json b/examples/ai/src/translations/apps.json new file mode 120000 index 00000000..ffa52698 --- /dev/null +++ b/examples/ai/src/translations/apps.json @@ -0,0 +1 @@ +../../../../packages/plugin-ai-apps-web/translations.json \ No newline at end of file diff --git a/examples/ai/src/utils/testTranslations.ts b/examples/ai/src/utils/testTranslations.ts index 199fb381..7507e479 100644 --- a/examples/ai/src/utils/testTranslations.ts +++ b/examples/ai/src/utils/testTranslations.ts @@ -1,6 +1,7 @@ import CreativeEditorSDK from '@cesdk/cesdk-js'; // Import translation files using symlinks +import appsTranslations from '../translations/apps.json'; import baseTranslations from '../translations/base.json'; import imageTranslations from '../translations/image.json'; import videoTranslations from '../translations/video.json'; @@ -9,81 +10,141 @@ import textTranslations from '../translations/text.json'; import stickerTranslations from '../translations/sticker.json'; /** - * Test all translation keys by setting them with prefixes: - * - & for generic/base translations - * - @ for provider-specific translations + * Helper function to determine if a key is provider-specific */ -export function testAllTranslations(cesdk: CreativeEditorSDK) { - const allTranslations: Record = {}; +function isProviderSpecificKey(key: string): boolean { + return ( + key.includes('.fal-ai/') || + key.includes('.open-ai/') || + key.includes('.runware/') || + key.includes('.elevenlabs/') || + key.includes('.anthropic.') || + key.includes('.openai.') + ); +} + +/** + * Helper function to add prefix based on key type + */ +function addPrefix(key: string, value: string): string { + return isProviderSpecificKey(key) ? `@${value}` : `&${value}`; +} + +/** + * Get translations for a specific locale from a translation object, + * falling back to 'en' if the locale doesn't exist + */ +function getLocaleTranslations( + translationObj: { en: Record; de?: Record }, + locale: 'en' | 'de' +): Record { + if (locale === 'de' && translationObj.de) { + return translationObj.de; + } + return translationObj.en; +} + +/** + * Process translations for a specific locale + */ +function processTranslationsForLocale( + locale: 'en' | 'de' +): Record { + const translations: Record = {}; + + // Process apps translations + const appsLocale = getLocaleTranslations( + appsTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(appsLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); + }); - // Process base translations (generic) with & prefix - Object.entries(baseTranslations.en).forEach(([key, value]) => { - allTranslations[key] = `&${value}`; + // Process base translations + const baseLocale = getLocaleTranslations( + baseTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(baseLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Process image generation translations (provider-specific) with @ prefix - Object.entries(imageTranslations.en).forEach(([key, value]) => { - // Check if it's a provider-specific translation - if (key.includes('.fal-ai/') || key.includes('.open-ai/') || key.includes('.runware/') || key.includes('.eachlabs/')) { - allTranslations[key] = `@${value}`; - } else { - // Generic property that might be redefined - allTranslations[key] = `&${value}`; - } + // Process image translations + const imageLocale = getLocaleTranslations( + imageTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(imageLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Process video generation translations (provider-specific) with @ prefix - Object.entries(videoTranslations.en).forEach(([key, value]) => { - if (key.includes('.fal-ai/') || key.includes('.runware/') || key.includes('.eachlabs/')) { - allTranslations[key] = `@${value}`; - } else { - allTranslations[key] = `&${value}`; - } + // Process video translations + const videoLocale = getLocaleTranslations( + videoTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(videoLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Process audio generation translations (provider-specific) with @ prefix - Object.entries(audioTranslations.en).forEach(([key, value]) => { - if (key.includes('.elevenlabs/')) { - allTranslations[key] = `@${value}`; - } else { - allTranslations[key] = `&${value}`; - } + // Process audio translations + const audioLocale = getLocaleTranslations( + audioTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(audioLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Process text generation translations (provider-specific) with @ prefix - Object.entries(textTranslations.en).forEach(([key, value]) => { - if (key.includes('.anthropic.') || key.includes('.openai.')) { - allTranslations[key] = `@${value}`; - } else { - allTranslations[key] = `&${value}`; - } + // Process text translations + const textLocale = getLocaleTranslations( + textTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(textLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Process sticker generation translations (provider-specific) with @ prefix - Object.entries(stickerTranslations.en).forEach(([key, value]) => { - if (key.includes('.fal-ai/')) { - allTranslations[key] = `@${value}`; - } else { - allTranslations[key] = `&${value}`; - } + // Process sticker translations + const stickerLocale = getLocaleTranslations( + stickerTranslations as { en: Record; de?: Record }, + locale + ); + Object.entries(stickerLocale).forEach(([key, value]) => { + translations[key] = addPrefix(key, value); }); - // Set all translations at once + return translations; +} + +/** + * Test all translation keys by setting them with prefixes: + * - & for generic/base translations + * - @ for provider-specific translations + */ +export function testAllTranslations(cesdk: CreativeEditorSDK) { + const enTranslations = processTranslationsForLocale('en'); + const deTranslations = processTranslationsForLocale('de'); + + // Set translations for both locales with their respective language values cesdk.setTranslations({ - en: allTranslations + en: enTranslations, + de: deTranslations }); - // Log summary for debugging - const genericCount = Object.values(allTranslations).filter(v => v.startsWith('&')).length; - const providerCount = Object.values(allTranslations).filter(v => v.startsWith('@')).length; - + // Log summary for debugging (use English translations for counting) + const genericCount = Object.values(enTranslations).filter(v => v.startsWith('&')).length; + const providerCount = Object.values(enTranslations).filter(v => v.startsWith('@')).length; + console.log('🔧 Translation Test Applied:'); - console.log(`📋 Total translations: ${Object.keys(allTranslations).length}`); + console.log(`📋 Total translations (per locale): ${Object.keys(enTranslations).length}`); console.log(`🔄 Generic translations (& prefix): ${genericCount}`); console.log(`🎯 Provider-specific translations (@ prefix): ${providerCount}`); console.log('💡 Look for & and @ prefixes in the UI to verify translation loading'); + console.log('🇩🇪 German locale will show German translations with prefixes'); - return allTranslations; + return { en: enTranslations, de: deTranslations }; } /** @@ -93,6 +154,7 @@ export function resetTranslations(cesdk: CreativeEditorSDK) { const allTranslations: Record = {}; // Merge all original translations without prefixes + Object.assign(allTranslations, appsTranslations.en); Object.assign(allTranslations, baseTranslations.en); Object.assign(allTranslations, imageTranslations.en); Object.assign(allTranslations, videoTranslations.en); diff --git a/packages/plugin-ai-apps-web/CHANGELOG.md b/packages/plugin-ai-apps-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-apps-web/CHANGELOG.md +++ b/packages/plugin-ai-apps-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-apps-web/README.md b/packages/plugin-ai-apps-web/README.md index 9391394c..ce283ccf 100644 --- a/packages/plugin-ai-apps-web/README.md +++ b/packages/plugin-ai-apps-web/README.md @@ -539,10 +539,37 @@ CreativeEditorSDK.create(domElement, { }); ``` -## Translations +## Internationalization (i18n) -The AI Apps plugin uses translations from individual AI generation plugins. For customization, refer to the respective translation files: +The AI Apps plugin supports full internationalization. To customize translations, set them **before** adding the plugin: +```typescript +CreativeEditorSDK.create(domElement, { + license: 'your-license-key', + locale: 'de' +}).then(async (cesdk) => { + // Set custom translations BEFORE adding plugins + cesdk.i18n.setTranslations({ + en: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Create Image', + 'panel.ly.img.ai.apps': 'AI Tools' + }, + de: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Bild erstellen', + 'panel.ly.img.ai.apps': 'KI-Werkzeuge' + } + }); + + // Now add the plugins - they won't override your custom translations + await cesdk.addPlugin(AiApps({ providers: { /* ... */ } })); +}); +``` + +For detailed documentation on the translation system, including all available translation keys and utilities, see the [Internationalization section](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web#internationalization-i18n) in the core AI generation package. + +### Translation Files + +- [AI Apps translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-apps-web/translations.json) - AI Apps panel labels - [Base translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web/translations.json) - Core translation keys - [Image generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-image-generation-web/translations.json) - Image generation interfaces - [Video generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-video-generation-web/translations.json) - Video generation interfaces diff --git a/packages/plugin-ai-apps-web/package.json b/packages/plugin-ai-apps-web/package.json index c9dd2586..6ef7d105 100644 --- a/packages/plugin-ai-apps-web/package.json +++ b/packages/plugin-ai-apps-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-apps-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI apps orchestration plugin for the CE.SDK editor", "keywords": ["CE.SDK", "plugin", "AI", "ai apps"], "repository": { diff --git a/packages/plugin-ai-apps-web/src/plugin.ts b/packages/plugin-ai-apps-web/src/plugin.ts index f2f095fe..27bd0595 100644 --- a/packages/plugin-ai-apps-web/src/plugin.ts +++ b/packages/plugin-ai-apps-web/src/plugin.ts @@ -7,8 +7,11 @@ import { isDefined } from '@imgly/plugin-utils'; import { getPanelId, ActionRegistry, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; +// @ts-ignore - JSON import +import translations from '../translations.json'; import ImageGeneration from '@imgly/plugin-ai-image-generation-web'; import VideoGeneration from '@imgly/plugin-ai-video-generation-web'; @@ -129,7 +132,15 @@ function initializeAppLibrary( gridColumns: 1, gridItemHeight: 'auto', gridBackgroundType: 'cover', - cardLabel: ({ label }) => label, + // Use labelKey for i18n resolution if available, falling back to static label + cardLabel: (context) => { + const { label, meta } = context; + const labelKey = meta?.labelKey as string | undefined; + if (labelKey && typeof cesdk.i18n?.translate === 'function') { + return cesdk.i18n.translate(labelKey); + } + return label; + }, cardLabelPosition: () => 'inside' }); @@ -194,10 +205,12 @@ function registerAppAssetSource( const currentSceneMode = cesdk.engine.scene.getMode(); return registry .getBy({ type: 'plugin' }) - .map(({ id, label, sceneMode }) => { + .map(({ id, label, labelKey, sceneMode }) => { if (currentSceneMode === 'Design' && sceneMode === 'Video') { return undefined; } + // Store labelKey in meta for i18n resolution via cardLabel callback + // The label uses the static fallback for backwards compatibility const assetDefinition: AssetDefinition = { id, label: { @@ -205,6 +218,7 @@ function registerAppAssetSource( }, meta: { sceneMode, + labelKey, thumbUri: config.baseURL != null ? getAppThumbnail(config.baseURL, id) @@ -388,25 +402,7 @@ function overrideAssetLibraryDockComponent(cesdk: CreativeEditorSDK) { } function addTranslations(cesdk: CreativeEditorSDK) { - cesdk.i18n.setTranslations({ - en: { - 'common.generate': 'Generate', - 'panel.ly.img.ai.generation.confirmCancel.content': - 'Are you sure you want to cancel the generation?', - 'panel.ly.img.ai.generation.confirmCancel.confirm': 'Cancel Generation', - 'panel.ly.img.ai.apps': 'AI', - 'ly.img.ai.apps.dock.label': 'AI', - 'panel.ly.img.ai.demo.video': 'Generate Video', - 'panel.ly.img.ai.demo.image': 'Generate Image', - - 'libraries.ly.img.ai.image-generation.history.label': - 'AI Generated Images', - 'libraries.ly.img.ai.video-generation.history.label': - 'AI Generated Videos', - 'libraries.ly.img.ai.audio-generation.history.label': - 'AI Generated Audio', - 'libraries.ly.img.ai.sticker-generation.history.label': - 'AI Generated Stickers' - } - }); + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, translations); } diff --git a/packages/plugin-ai-apps-web/translations.json b/packages/plugin-ai-apps-web/translations.json new file mode 100644 index 00000000..0f8afe41 --- /dev/null +++ b/packages/plugin-ai-apps-web/translations.json @@ -0,0 +1,15 @@ +{ + "en": { + "common.generate": "Generate", + "panel.ly.img.ai.generation.confirmCancel.content": "Are you sure you want to cancel the generation?", + "panel.ly.img.ai.generation.confirmCancel.confirm": "Cancel Generation", + "panel.ly.img.ai.apps": "AI", + "ly.img.ai.apps.dock.label": "AI", + "panel.ly.img.ai.demo.video": "Generate Video", + "panel.ly.img.ai.demo.image": "Generate Image", + "libraries.ly.img.ai.image-generation.history.label": "AI Generated Images", + "libraries.ly.img.ai.video-generation.history.label": "AI Generated Videos", + "libraries.ly.img.ai.audio-generation.history.label": "AI Generated Audio", + "libraries.ly.img.ai.sticker-generation.history.label": "AI Generated Stickers" + } +} diff --git a/packages/plugin-ai-audio-generation-web/CHANGELOG.md b/packages/plugin-ai-audio-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-audio-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-audio-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-audio-generation-web/package.json b/packages/plugin-ai-audio-generation-web/package.json index 8a2cc9a0..586cfe9a 100644 --- a/packages/plugin-ai-audio-generation-web/package.json +++ b/packages/plugin-ai-audio-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-audio-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI audio generation plugin for the CE.SDK editor", "keywords": ["CE.SDK", "plugin", "AI", "audio-generation"], "repository": { diff --git a/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenMultilingualV2.ts b/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenMultilingualV2.ts index 1723dbe2..18da57f2 100644 --- a/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenMultilingualV2.ts +++ b/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenMultilingualV2.ts @@ -3,7 +3,8 @@ import { type AudioOutput, CommonProviderConfiguration, getPanelId, - normalizeBaseURL + normalizeBaseURL, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import CreativeEditorSDK from '@cesdk/cesdk-js'; import schema from './ElevenMultilingualV2.json'; @@ -57,7 +58,7 @@ function getProvider( const voiceAssetSourceId = createVoicesAssetSource(cesdk, baseURL); const modelKey = 'elevenlabs/monolingual/v1'; - cesdk.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${modelKey}`]: 'AI Voice', [`panel.${voiceSelectionPanelId}`]: 'Select a Voice', diff --git a/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenSoundEffects.ts b/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenSoundEffects.ts index bb26e5c0..7c77e6d8 100644 --- a/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenSoundEffects.ts +++ b/packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenSoundEffects.ts @@ -1,7 +1,8 @@ import { type Provider, type AudioOutput, - CommonProviderConfiguration + CommonProviderConfiguration, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import CreativeEditorSDK from '@cesdk/cesdk-js'; import schema from './ElevenSoundEffects.json'; @@ -31,7 +32,7 @@ function getProvider( ): Provider<'audio', ElevenlabsInput, AudioOutput> { const modelKey = 'elevenlabs/sound-generation'; - cesdk.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${modelKey}`]: 'Generate Sound' } diff --git a/packages/plugin-ai-audio-generation-web/src/plugin.ts b/packages/plugin-ai-audio-generation-web/src/plugin.ts index 1c41612c..d8792232 100644 --- a/packages/plugin-ai-audio-generation-web/src/plugin.ts +++ b/packages/plugin-ai-audio-generation-web/src/plugin.ts @@ -4,7 +4,8 @@ import { Output, initializeProviders, registerDockComponent, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { PluginConfiguration } from './types'; import { toArray, translateWithFallback } from '@imgly/plugin-utils'; @@ -39,7 +40,9 @@ export function AudioGeneration( const SOUND_ACTION_LABEL_KEY = `${PLUGIN_ID}.sound.action.label`; const SPEECH_ACTION_LABEL_KEY = `${PLUGIN_ID}.speech.action.label`; - cesdk.setTranslations({ + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, { en: { [`panel.${SPEECH_GENERATION_PANEL_ID}`]: 'AI Voice', [`panel.${SOUND_GENERATION_PANEL_ID}`]: 'Sound Generation', @@ -63,6 +66,7 @@ export function AudioGeneration( SOUND_ACTION_LABEL_KEY, 'Generate Sound' ), + labelKey: SOUND_ACTION_LABEL_KEY, meta: { panelId: SOUND_GENERATION_PANEL_ID }, execute: () => { @@ -86,6 +90,7 @@ export function AudioGeneration( SPEECH_ACTION_LABEL_KEY, 'AI Voice' ), + labelKey: SPEECH_ACTION_LABEL_KEY, meta: { panelId: SPEECH_GENERATION_PANEL_ID }, execute: () => { diff --git a/packages/plugin-ai-audio-generation-web/translations.json b/packages/plugin-ai-audio-generation-web/translations.json index 4f20734a..1fa9f290 100644 --- a/packages/plugin-ai-audio-generation-web/translations.json +++ b/packages/plugin-ai-audio-generation-web/translations.json @@ -1,12 +1,13 @@ { "en": { - "panel.ly.img.ai.speech-generation": "AI Voice", - "panel.ly.img.ai.sound-generation": "Sound Generation", + "panel.ly.img.ai.audio-generation.speech": "AI Voice", + "panel.ly.img.ai.audio-generation.sound": "Sound Generation", + "ly.img.plugin-ai-audio-generation-web.speech.action.label": "AI Voice", + "ly.img.plugin-ai-audio-generation-web.sound.action.label": "Generate Sound", "ly.img.plugin-ai-audio-generation-web.elevenlabs/monolingual/v1.property.prompt": "Prompt", "ly.img.plugin-ai-audio-generation-web.elevenlabs/monolingual/v1.property.voice_id": "Voice", "ly.img.plugin-ai-audio-generation-web.elevenlabs/monolingual/v1.property.speed": "Speech Speed", - "ly.img.plugin-ai-audio-generation-web.elevenlabs/sound-generation.property.text": "Sound description", "ly.img.plugin-ai-audio-generation-web.elevenlabs/sound-generation.property.duration_seconds": "Duration (seconds)" } -} \ No newline at end of file +} diff --git a/packages/plugin-ai-generation-web/CHANGELOG.md b/packages/plugin-ai-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-generation-web/README.md b/packages/plugin-ai-generation-web/README.md index 86f4e895..ba16b457 100644 --- a/packages/plugin-ai-generation-web/README.md +++ b/packages/plugin-ai-generation-web/README.md @@ -1505,6 +1505,116 @@ interface QuickActionDefinition> { } ``` -## Translations +## Internationalization (i18n) -For customization and localization, see the [translations.json](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web/translations.json) file which contains base translation keys that can be overridden for all AI plugins. +The AI plugins support full internationalization, allowing integrators to customize all user-facing text. The translation system is designed to let integrators override default translations **before** plugins are loaded. + +### Custom Translations + +To customize translations, call `cesdk.i18n.setTranslations()` **before** adding the AI plugins: + +```typescript +import CreativeEditorSDK from '@cesdk/cesdk-js'; +import AiApps from '@imgly/plugin-ai-apps-web'; + +CreativeEditorSDK.create(domElement, { + license: 'your-license-key', + locale: 'de' // Set your desired locale +}).then(async (cesdk) => { + // Set custom translations BEFORE adding plugins + cesdk.i18n.setTranslations({ + en: { + // Override AI Apps labels + '@imgly/plugin-ai-image-generation-web.action.label': 'Create Image', + '@imgly/plugin-ai-video-generation-web.action.label': 'Create Video', + + // Override provider-specific labels + 'ly.img.plugin-ai-image-generation-web.fal-ai/recraft-v3.property.style': 'Art Style', + 'ly.img.plugin-ai-image-generation-web.fal-ai/recraft-v3.property.style.realistic_image': 'Photo Realistic' + }, + de: { + // German translations + '@imgly/plugin-ai-image-generation-web.action.label': 'Bild erstellen', + '@imgly/plugin-ai-video-generation-web.action.label': 'Video erstellen', + 'common.generate': 'Generieren', + 'panel.ly.img.ai.apps': 'KI' + } + }); + + // Now add the plugins - they won't override your custom translations + await cesdk.addPlugin(AiApps({ providers: { /* ... */ } })); +}); +``` + +### How It Works + +The AI plugins use `setDefaultTranslations()` internally, which only sets translation keys that don't already exist. This means: + +1. **Your translations take priority**: Any translations you set before loading plugins are preserved +2. **Fallback to defaults**: Keys you don't customize will use the plugin's default English translations +3. **Locale support**: Set translations for any locale supported by CE.SDK + +### Translation Key Structure + +Translation keys follow a consistent naming pattern: + +``` +ly.img.plugin-ai-{type}-generation-web.{provider-id}.property.{property-name}.{value} +``` + +For example: +- `ly.img.plugin-ai-image-generation-web.fal-ai/recraft-v3.property.style.realistic_image` +- `ly.img.plugin-ai-video-generation-web.fal-ai/veo-3.property.duration.5s` + +### Action Labels + +Each AI plugin registers an action with a translatable label. The action uses both a static `label` (for backwards compatibility) and a `labelKey` for dynamic i18n resolution: + +| Plugin | Label Key | +|--------|-----------| +| Image Generation | `@imgly/plugin-ai-image-generation-web.action.label` | +| Video Generation | `@imgly/plugin-ai-video-generation-web.action.label` | +| Audio Generation (Sound) | `@imgly/plugin-ai-audio-generation-web.sound.action.label` | +| Audio Generation (Speech) | `@imgly/plugin-ai-audio-generation-web.speech.action.label` | +| Sticker Generation | `@imgly/plugin-ai-sticker-generation-web.action.label` | + +### Translation Files + +Each AI plugin includes a `translations.json` file with all available translation keys: + +- [Base translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web/translations.json) - Core translation keys +- [AI Apps translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-apps-web/translations.json) - AI Apps panel labels +- [Image generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-image-generation-web/translations.json) - Image generation interfaces +- [Video generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-video-generation-web/translations.json) - Video generation interfaces +- [Text generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-text-generation-web/translations.json) - Text generation interfaces +- [Audio generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-audio-generation-web/translations.json) - Audio generation interfaces +- [Sticker generation translations](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-sticker-generation-web/translations.json) - Sticker generation interfaces + +### Exported Utilities + +The package exports translation utilities for use in custom providers: + +```typescript +import { + setDefaultTranslations, + createTranslationCallback, + buildTranslationKeys +} from '@imgly/plugin-ai-generation-web'; + +// Set translations that won't override existing keys +setDefaultTranslations(cesdk, { + en: { 'my.key': 'My Value' } +}); + +// Create a translation callback for asset sources +const translateLabel = createTranslationCallback(cesdk, 'my-provider', 'style', 'image'); + +// Build translation keys with fallback order +const keys = buildTranslationKeys('my-provider', 'style', 'realistic', 'image'); +// Returns: [ +// 'ly.img.plugin-ai-image-generation-web.my-provider.property.style.realistic', +// 'ly.img.plugin-ai-generation-web.property.style.realistic', +// 'ly.img.plugin-ai-image-generation-web.my-provider.defaults.property.style.realistic', +// 'ly.img.plugin-ai-generation-web.defaults.property.style.realistic' +// ] +``` diff --git a/packages/plugin-ai-generation-web/package.json b/packages/plugin-ai-generation-web/package.json index 202eeafb..6a717d81 100644 --- a/packages/plugin-ai-generation-web/package.json +++ b/packages/plugin-ai-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI generation plugin for the CE.SDK editor", "keywords": [ "CE.SDK", diff --git a/packages/plugin-ai-generation-web/src/__tests__/actionLabelTranslation.test.ts b/packages/plugin-ai-generation-web/src/__tests__/actionLabelTranslation.test.ts new file mode 100644 index 00000000..ffa244dc --- /dev/null +++ b/packages/plugin-ai-generation-web/src/__tests__/actionLabelTranslation.test.ts @@ -0,0 +1,154 @@ +import { describe, expect, it, jest, beforeEach } from '@jest/globals'; +import { + ActionRegistry, + PluginActionDefinition, + BaseActionDefinition +} from '../core/ActionRegistry'; + +/** + * Test case for the bug: AI Plugin Actions cannot be translated into German + * + * The issue is that action labels are set as static strings during plugin + * initialization, rather than using translation keys that can be dynamically + * resolved based on the current language. + * + * Expected behavior: Actions should store a `labelKey` that can be used + * to look up the translated label dynamically. + */ +describe('Action Label Translation', () => { + let registry: ActionRegistry; + + beforeEach(() => { + // Reset global singleton instance for each test + const globalObj = ( + typeof window !== 'undefined' ? window : globalThis + ) as any; + delete globalObj.__imgly_action_registry__; + registry = ActionRegistry.get(); + }); + + it('should export BaseActionDefinition with labelKey property', () => { + // This test verifies that the BaseActionDefinition type is exported + // and includes the labelKey property for i18n support + const baseAction: BaseActionDefinition = { + id: 'test-action', + label: 'Test Label', + labelKey: 'test.action.label' + }; + + expect(baseAction.labelKey).toBe('test.action.label'); + }); + + it('should support labelKey for translation-aware labels', () => { + // This test documents the expected behavior for translatable action labels + const ACTION_LABEL_KEY = + '@imgly/plugin-ai-image-generation-web.action.label'; + + const action: PluginActionDefinition = { + type: 'plugin', + id: '@imgly/plugin-ai-image-generation-web', + pluginId: '@imgly/plugin-ai-image-generation-web', + // label is the English fallback + label: 'Generate Image', + // labelKey is the translation key that should be used for i18n + labelKey: ACTION_LABEL_KEY, + execute: jest.fn() + }; + + registry.register(action); + + const registeredActions = registry.getBy({ + type: 'plugin', + id: '@imgly/plugin-ai-image-generation-web' + }); + + expect(registeredActions).toHaveLength(1); + expect(registeredActions[0].labelKey).toBe(ACTION_LABEL_KEY); + expect(registeredActions[0].label).toBe('Generate Image'); + }); + + it('should allow consumers to resolve labels using labelKey for i18n', () => { + // Simulate the translation lookup that consumers should perform + const mockTranslations: Record> = { + en: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Generate Image', + '@imgly/plugin-ai-sticker-generation-web.action.label': + 'Generate Sticker' + } + }; + + // Mock i18n translate function + const translate = (key: string, lang: string): string => { + return mockTranslations[lang]?.[key] ?? key; + }; + + const imageAction: PluginActionDefinition = { + type: 'plugin', + id: '@imgly/plugin-ai-image-generation-web', + pluginId: '@imgly/plugin-ai-image-generation-web', + label: 'Generate Image', + labelKey: '@imgly/plugin-ai-image-generation-web.action.label', + execute: jest.fn() + }; + + const stickerAction: PluginActionDefinition = { + type: 'plugin', + id: '@imgly/plugin-ai-sticker-generation-web', + pluginId: '@imgly/plugin-ai-sticker-generation-web', + label: 'Generate Sticker', + labelKey: '@imgly/plugin-ai-sticker-generation-web.action.label', + execute: jest.fn() + }; + + registry.register(imageAction); + registry.register(stickerAction); + + const actions = registry.getBy({ type: 'plugin' }); + + // Test English translations + actions.forEach((action) => { + if (action.labelKey) { + const enLabel = translate(action.labelKey, 'en'); + expect(enLabel).not.toBe(action.labelKey); // Should resolve to actual text + } + }); + + // Verify labelKey is set correctly for consumer use + const imageActionResult = actions.find( + (a) => a.id === '@imgly/plugin-ai-image-generation-web' + ); + const stickerActionResult = actions.find( + (a) => a.id === '@imgly/plugin-ai-sticker-generation-web' + ); + + expect(imageActionResult?.labelKey).toBeDefined(); + expect(stickerActionResult?.labelKey).toBeDefined(); + + expect(translate(imageActionResult!.labelKey!, 'en')).toBe( + 'Generate Image' + ); + expect(translate(stickerActionResult!.labelKey!, 'en')).toBe( + 'Generate Sticker' + ); + }); + + it('should work with actions that only have label (backwards compatibility)', () => { + // Actions without labelKey should still work (backwards compatibility) + const action: PluginActionDefinition = { + type: 'plugin', + id: 'legacy-action', + pluginId: 'legacy-plugin', + label: 'Legacy Action', + // No labelKey - this is the legacy behavior + execute: jest.fn() + }; + + registry.register(action); + + const registeredActions = registry.getBy({ id: 'legacy-action' }); + + expect(registeredActions).toHaveLength(1); + expect(registeredActions[0].label).toBe('Legacy Action'); + expect(registeredActions[0].labelKey).toBeUndefined(); + }); +}); diff --git a/packages/plugin-ai-generation-web/src/core/ActionRegistry.ts b/packages/plugin-ai-generation-web/src/core/ActionRegistry.ts index 74608c8e..8098f276 100644 --- a/packages/plugin-ai-generation-web/src/core/ActionRegistry.ts +++ b/packages/plugin-ai-generation-web/src/core/ActionRegistry.ts @@ -10,11 +10,13 @@ import { Result } from '../generation/createGenerateFunction'; /** * Base properties shared by all action definitions. */ -interface BaseActionDefinition { +export interface BaseActionDefinition { /** Unique identifier for the action */ id: string; - /** Human-readable label for the action */ + /** Human-readable label for the action (fallback when labelKey is not available) */ label?: string; + /** Translation key for the label (preferred for i18n support) */ + labelKey?: string; /** Detailed description of what the action does */ description?: string; /** Optional metadata for additional information */ diff --git a/packages/plugin-ai-generation-web/src/index.ts b/packages/plugin-ai-generation-web/src/index.ts index 9feb5038..6897470d 100644 --- a/packages/plugin-ai-generation-web/src/index.ts +++ b/packages/plugin-ai-generation-web/src/index.ts @@ -52,6 +52,7 @@ export { export { default as integrateIntoDefaultAssetLibraryEntry } from './assets/integrateIntoDefaultAssetLibraryEntry'; export { ActionRegistry, + type BaseActionDefinition, type PluginActionDefinition, type QuickActionDefinition, type ActionDefinition, @@ -104,6 +105,7 @@ export { AI_EDIT_MODE, AI_METADATA_KEY } from './ui/quickActions/utils'; // Export AI-specific translation helpers export { + setDefaultTranslations, createTranslationCallback, buildTranslationKeys } from './utils/translationHelpers'; diff --git a/packages/plugin-ai-generation-web/src/openapi/extractSchemaTranslations.ts b/packages/plugin-ai-generation-web/src/openapi/extractSchemaTranslations.ts index e7360d0e..aa7c3ee6 100644 --- a/packages/plugin-ai-generation-web/src/openapi/extractSchemaTranslations.ts +++ b/packages/plugin-ai-generation-web/src/openapi/extractSchemaTranslations.ts @@ -2,6 +2,7 @@ import { Property } from './types'; import Provider, { Output, OutputKind } from '../core/provider'; import { UIOptions } from '../types'; import { defaultTranslations } from './defaultTranslations'; +import { setDefaultTranslations } from '../utils/translationHelpers'; function formatEnumLabel(enumValue: string): string { return ( @@ -93,7 +94,7 @@ export function extractAndSetSchemaTranslations< const allTranslations = { ...defaultTranslations, ...translations }; if (Object.keys(allTranslations).length > 0) { - options.cesdk.i18n.setTranslations({ + setDefaultTranslations(options.cesdk, { en: allTranslations }); } diff --git a/packages/plugin-ai-generation-web/src/providers/initializeProviders.ts b/packages/plugin-ai-generation-web/src/providers/initializeProviders.ts index a2e0730f..45aecb96 100644 --- a/packages/plugin-ai-generation-web/src/providers/initializeProviders.ts +++ b/packages/plugin-ai-generation-web/src/providers/initializeProviders.ts @@ -11,6 +11,7 @@ import { isGeneratingStateKey } from '../ui/components/renderGenerationComponent import { CommonPluginConfiguration } from '../types'; import initializeHistoryCompositeAssetSource from '../assets/initializeHistoryCompositeAssetSource'; import { isDefined } from '@imgly/plugin-utils'; +import { setDefaultTranslations } from '../utils/translationHelpers'; function createLabelArray( kind: K, @@ -70,10 +71,12 @@ async function initializeProviders( }, config: CommonPluginConfiguration ): Promise> { - // Set default translations + // Set default translations - use setDefaultTranslations to allow integrators + // to override these values by calling setTranslations() BEFORE adding the plugin const { cesdk } = options; - cesdk.setTranslations({ + setDefaultTranslations(cesdk, { en: { + 'ly.img.plugin-ai-generation-web.generate': 'Generate', 'ly.img.plugin-ai-generation-web.defaults.fromType.label': 'Input', 'ly.img.plugin-ai-generation-web.defaults.providerSelect.label': 'Provider', diff --git a/packages/plugin-ai-generation-web/src/ui/common/renderImageUrlProperty.ts b/packages/plugin-ai-generation-web/src/ui/common/renderImageUrlProperty.ts index 6a3ce16a..f7a2ff39 100644 --- a/packages/plugin-ai-generation-web/src/ui/common/renderImageUrlProperty.ts +++ b/packages/plugin-ai-generation-web/src/ui/common/renderImageUrlProperty.ts @@ -1,5 +1,6 @@ import CreativeEditorSDK, { AssetResult } from '@cesdk/cesdk-js'; import { RenderCustomProperty } from '../../core/provider'; +import { setDefaultTranslations } from '../../utils/translationHelpers'; /** * Provides render function for a image url property that allows @@ -19,7 +20,7 @@ function renderImageUrlProperty( const propertyKey = options.propertyKey ?? 'image_url'; const panelIdForImageSelection = getImageSelectionPanelId(provderId); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${panelIdForImageSelection}`]: 'Select Image To Change', 'ly.img.ai.imageSelection.selectImage.label': 'Select Image', diff --git a/packages/plugin-ai-generation-web/src/ui/common/renderStyleTransferProperty.ts b/packages/plugin-ai-generation-web/src/ui/common/renderStyleTransferProperty.ts index 0766efce..a4052327 100644 --- a/packages/plugin-ai-generation-web/src/ui/common/renderStyleTransferProperty.ts +++ b/packages/plugin-ai-generation-web/src/ui/common/renderStyleTransferProperty.ts @@ -6,6 +6,7 @@ import { translateWithFallback } from '@imgly/plugin-utils'; import { SelectValue } from '@imgly/plugin-utils/dist/assetSources/CustomAssetSource'; +import { setDefaultTranslations } from '../../utils/translationHelpers'; type StyleSelectionPayload = { onSelect: (asset: AssetResult) => Promise; @@ -69,7 +70,7 @@ function renderStyleTransferProperty( const propertyKey = options.propertyKey ?? 'style'; const panelIdForStyleSelection = getStyleSelectionPanelId(providerId); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${panelIdForStyleSelection}`]: 'Select Style', [`${providerId}.${propertyKey}`]: 'Style', diff --git a/packages/plugin-ai-generation-web/src/ui/components/renderGenerationComponents.ts b/packages/plugin-ai-generation-web/src/ui/components/renderGenerationComponents.ts index 127ac237..8e392d9e 100644 --- a/packages/plugin-ai-generation-web/src/ui/components/renderGenerationComponents.ts +++ b/packages/plugin-ai-generation-web/src/ui/components/renderGenerationComponents.ts @@ -81,7 +81,11 @@ function renderGenerationComponents( builder.Section(`${providerId}.generate.section`, { children: () => { builder.Button(`${providerId}.generate`, { - label: ['common.generate', `panel.${providerId}.generate`], + label: [ + 'ly.img.plugin-ai-generation-web.generate', + 'common.generate', + `panel.${providerId}.generate` + ], isLoading: generatingState.value, color: 'accent', isDisabled, diff --git a/packages/plugin-ai-generation-web/src/ui/panels/createConfirmationRenderFunction.ts b/packages/plugin-ai-generation-web/src/ui/panels/createConfirmationRenderFunction.ts index ba85a48e..802730ed 100644 --- a/packages/plugin-ai-generation-web/src/ui/panels/createConfirmationRenderFunction.ts +++ b/packages/plugin-ai-generation-web/src/ui/panels/createConfirmationRenderFunction.ts @@ -4,6 +4,7 @@ import { Metadata } from '@imgly/plugin-utils'; import { AI_METADATA_KEY } from '../quickActions/utils'; import { OutputKind } from '../../core/provider'; import { Callbacks } from '../../generation/CallbacksRegistry'; +import { setDefaultTranslations } from '../../utils/translationHelpers'; /** * Creates a render function for the AI inference confirmation component. @@ -19,15 +20,17 @@ async function createConfirmationRenderFunction(context: { cesdk: CreativeEditorSDK; }): Promise> { const prefix = `ly.img.ai.${context.kind}.confirmation`; - context.cesdk?.i18n.setTranslations({ - en: { - 'ly.img.ai.processing': 'Generating...', - [`${prefix}.cancel`]: 'Cancel Generation', - [`${prefix}.apply`]: 'Apply Generation', - [`${prefix}.before`]: 'Before', - [`${prefix}.after`]: 'After' - } - }); + if (context.cesdk) { + setDefaultTranslations(context.cesdk, { + en: { + 'ly.img.ai.processing': 'Generating...', + [`${prefix}.cancel`]: 'Cancel Generation', + [`${prefix}.apply`]: 'Apply Generation', + [`${prefix}.before`]: 'Before', + [`${prefix}.after`]: 'After' + } + }); + } const builderRenderFunction: BuilderRenderFunction = ( builderContext ) => { diff --git a/packages/plugin-ai-generation-web/src/ui/quickActions/createQuickActionMenuRenderFunction.ts b/packages/plugin-ai-generation-web/src/ui/quickActions/createQuickActionMenuRenderFunction.ts index 27288802..62bc678b 100644 --- a/packages/plugin-ai-generation-web/src/ui/quickActions/createQuickActionMenuRenderFunction.ts +++ b/packages/plugin-ai-generation-web/src/ui/quickActions/createQuickActionMenuRenderFunction.ts @@ -16,6 +16,7 @@ import { ProviderInitializationResult } from '../../providers/initializeProvider import handleGenerateFromQuickAction from '../../generation/handleGenerateFromQuickAction'; import { Middleware } from '../../middleware/middleware'; import { ProviderRegistry } from '../../core/ProviderRegistry'; +import { setDefaultTranslations } from '../../utils/translationHelpers'; type SupportedQuickAction = { definition: QuickActionDefinition; @@ -60,7 +61,7 @@ function createQuickActionMenuRenderFunction< }): Promise> { const prefix = `ly.img.ai.${context.kind}}`; - context.cesdk.i18n.setTranslations({ + setDefaultTranslations(context.cesdk, { en: { [`ly.img.plugin-ai-generation-web.defaults.quickAction.providerSelect.label`]: 'Provider' diff --git a/packages/plugin-ai-generation-web/src/utils/translationHelpers.ts b/packages/plugin-ai-generation-web/src/utils/translationHelpers.ts index f699672b..9da4cd61 100644 --- a/packages/plugin-ai-generation-web/src/utils/translationHelpers.ts +++ b/packages/plugin-ai-generation-web/src/utils/translationHelpers.ts @@ -1,6 +1,76 @@ import type CreativeEditorSDK from '@cesdk/cesdk-js'; import { supportsTranslateAPI, hasTranslateAPI } from '@imgly/plugin-utils'; +type TranslationDefinition = Partial< + Record>> +>; + +/** + * Sets default translations only for keys that don't already exist. + * + * This allows integrators to set custom translations BEFORE plugins load, + * and the plugins won't override those custom values with their defaults. + * + * @param cesdk - The CE.SDK instance + * @param definition - The translations to set (same format as setTranslations) + * + * @example + * ```ts + * // Integrator sets custom translation BEFORE plugin loads + * cesdk.i18n.setTranslations({ en: { 'my.key': 'Custom Value' } }); + * + * // Plugin uses setDefaultTranslations - won't override 'my.key' + * setDefaultTranslations(cesdk, { en: { 'my.key': 'Default Value', 'other.key': 'Other' } }); + * + * // Result: 'my.key' = 'Custom Value', 'other.key' = 'Other' + * ``` + */ +export function setDefaultTranslations( + cesdk: CreativeEditorSDK, + definition: TranslationDefinition +): void { + // Check if getTranslations API is available (CE.SDK 1.59.0+) + if (typeof cesdk.i18n?.getTranslations !== 'function') { + // Fallback: use regular setTranslations if getTranslations not available + cesdk.i18n.setTranslations(definition); + return; + } + + // Get existing translations for all locales in the definition + const locales = Object.keys(definition) as string[]; + const existingTranslations = cesdk.i18n.getTranslations(locales); + + // Filter out keys that already exist + const filteredDefinition: TranslationDefinition = {}; + + for (const locale of locales) { + const newTranslations = definition[locale]; + if (!newTranslations) continue; + + const existingLocaleTranslations = + (existingTranslations[locale] as Record | undefined) ?? + {}; + const filteredLocaleTranslations: Record = {}; + + for (const [key, value] of Object.entries(newTranslations)) { + // Only include if key doesn't already exist + if (!(key in existingLocaleTranslations) && value !== undefined) { + filteredLocaleTranslations[key] = value; + } + } + + // Only add locale if there are translations to set + if (Object.keys(filteredLocaleTranslations).length > 0) { + filteredDefinition[locale] = filteredLocaleTranslations; + } + } + + // Only call setTranslations if there are new translations to add + if (Object.keys(filteredDefinition).length > 0) { + cesdk.i18n.setTranslations(filteredDefinition); + } +} + /** * Creates a translation callback function for AI asset sources * @param cesdk - The CE.SDK instance diff --git a/packages/plugin-ai-generation-web/translations.json b/packages/plugin-ai-generation-web/translations.json index 18bdbd50..e628c642 100644 --- a/packages/plugin-ai-generation-web/translations.json +++ b/packages/plugin-ai-generation-web/translations.json @@ -1,5 +1,6 @@ { "en": { + "ly.img.plugin-ai-generation-web.generate": "Generate", "ly.img.plugin-ai-generation-web.providerSelect.label": "Provider", "ly.img.plugin-ai-generation-web.fromType.label": "Input", "ly.img.plugin-ai-generation-web.fromText.label": "Text", @@ -86,4 +87,4 @@ "ly.img.ai.styleTransfer.nightBokeh": "Night Bokeh", "ly.img.ai.styleTransfer.popArt": "Pop Art" } -} \ No newline at end of file +} diff --git a/packages/plugin-ai-image-generation-web/CHANGELOG.md b/packages/plugin-ai-image-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-image-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-image-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-image-generation-web/README.md b/packages/plugin-ai-image-generation-web/README.md index 1887f298..da4a0757 100644 --- a/packages/plugin-ai-image-generation-web/README.md +++ b/packages/plugin-ai-image-generation-web/README.md @@ -2110,9 +2110,29 @@ currentOrder.splice(2, 0, 'ly.img.ai.image-generation.dock'); cesdk.ui.setDockOrder(currentOrder); ``` -## Translations +## Internationalization (i18n) -For customization and localization, see the [translations.json](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-image-generation-web/translations.json) file which contains provider-specific translation keys for image generation interfaces. +The Image Generation plugin supports full internationalization. To customize translations, set them **before** adding the plugin: + +```typescript +cesdk.i18n.setTranslations({ + en: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Create Image', + 'ly.img.plugin-ai-image-generation-web.fal-ai/recraft-v3.property.style': 'Art Style' + }, + de: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Bild erstellen', + 'ly.img.plugin-ai-image-generation-web.fal-ai/recraft-v3.property.style': 'Kunststil' + } +}); + +// Then add the plugins - they won't override your custom translations +await cesdk.addPlugin(AiApps({ providers: { /* ... */ } })); +``` + +For detailed documentation on the translation system, see the [Internationalization section](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web#internationalization-i18n) in the core AI generation package. + +For all available translation keys, see the [translations.json](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-image-generation-web/translations.json) file. ## Related Packages diff --git a/packages/plugin-ai-image-generation-web/package.json b/packages/plugin-ai-image-generation-web/package.json index b7ac2adc..ce905df7 100644 --- a/packages/plugin-ai-image-generation-web/package.json +++ b/packages/plugin-ai-image-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-image-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI image generation plugin for the CE.SDK editor", "keywords": [ "CE.SDK", diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2.image2image.ts index 15be884e..df9aa290 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function Flux2Image2Image( const providerId = 'eachlabs/flux-2/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Flex.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Flex.image2image.ts index 2c3b5059..85b3c285 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Flex.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Flex.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -37,7 +38,7 @@ export function Flux2FlexImage2Image( const providerId = 'eachlabs/flux-2-flex/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Pro.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Pro.image2image.ts index 257ed554..18a3ce78 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Pro.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/Flux2Pro.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function Flux2ProImage2Image( const providerId = 'eachlabs/flux-2-pro/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/Gemini3Pro.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/Gemini3Pro.image2image.ts index 05dfddb3..0cb0c6ad 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/Gemini3Pro.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/Gemini3Pro.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function Gemini3ProImage2Image( const providerId = 'eachlabs/gemini-3-pro-image/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/NanoBananaPro.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/NanoBananaPro.image2image.ts index ebe3fc84..6ffb1a57 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/NanoBananaPro.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/NanoBananaPro.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -38,7 +39,7 @@ export function NanoBananaProImage2Image( const providerId = 'eachlabs/nano-banana-pro/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/OpenAIImage.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/OpenAIImage.image2image.ts index de83c4d2..96e48228 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/OpenAIImage.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/OpenAIImage.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function OpenAIImageImage2Image( const providerId = 'eachlabs/openai-gpt-image/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/eachlabs/Seedream45.image2image.ts b/packages/plugin-ai-image-generation-web/src/eachlabs/Seedream45.image2image.ts index e39a87b4..b5416121 100644 --- a/packages/plugin-ai-image-generation-web/src/eachlabs/Seedream45.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/eachlabs/Seedream45.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -37,7 +38,7 @@ export function Seedream45Image2Image( const providerId = 'eachlabs/seedream-v4.5/edit'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextEdit.ts index 5f79ad3f..e716e81d 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextEdit.ts @@ -2,7 +2,8 @@ import { ImageOutput, type Provider, type CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './FluxProKontextEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -23,7 +24,7 @@ export function FluxProKontextEdit( const modelKey = 'fal-ai/flux-pro/kontext'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { 'ly.img.ai.quickAction.styleTransfer': 'Change Art Style', 'ly.img.ai.quickAction.artists': 'Painted By', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextMaxEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextMaxEdit.ts index d9a51e18..9fc42d73 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextMaxEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/FluxProKontextMaxEdit.ts @@ -2,7 +2,8 @@ import { ImageOutput, type Provider, type CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './FluxProKontextMaxEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -24,7 +25,7 @@ export function FluxProKontextMaxEdit( const modelKey = 'fal-ai/flux-pro/kontext/max'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { 'ly.img.ai.quickAction.styleTransfer': 'Change Art Style', 'ly.img.ai.quickAction.artists': 'Painted By', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/Gemini25FlashImageEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/Gemini25FlashImageEdit.ts index c122fea8..685f9aa9 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/Gemini25FlashImageEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/Gemini25FlashImageEdit.ts @@ -3,7 +3,8 @@ import { type Provider, type CommonProviderConfiguration, getPanelId, - CommonProperties + CommonProperties, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Gemini25FlashImageEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -26,7 +27,7 @@ export function Gemini25FlashImageEdit( const modelKey = 'fal-ai/gemini-25-flash-image/edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlash25.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlash25.ts index c1f183ad..16eae789 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlash25.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlash25.ts @@ -2,7 +2,8 @@ import { ImageOutput, type Provider, type CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './GeminiFlash25.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -52,7 +53,7 @@ export function GeminiFlash25( const modelKey = 'fal-ai/gemini-25-flash-image'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`libraries.${getPanelId(modelKey)}.history.label`]: 'Generated From Text', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlashEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlashEdit.ts index 51f06665..1280da31 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlashEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/GeminiFlashEdit.ts @@ -2,7 +2,8 @@ import { ImageOutput, type Provider, type CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './GeminiFlashEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -23,7 +24,7 @@ export function GeminiFlashEdit( const modelKey = 'fal-ai/gemini-flash-edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Change', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBanana.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBanana.ts index a6807201..b9d09e43 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBanana.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBanana.ts @@ -2,7 +2,8 @@ import { ImageOutput, CommonProviderConfiguration, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import NanoBananaSchema from './NanoBanana.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -31,7 +32,7 @@ function getProvider( ): Provider<'image', NanoBananaInput, ImageOutput> { const modelKey = 'fal-ai/nano-banana'; - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`libraries.${getPanelId(modelKey)}.history.label`]: 'Generated From Text' } diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaEdit.ts index f28c3ccc..f782cf02 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaEdit.ts @@ -3,7 +3,8 @@ import { type Provider, type CommonProviderConfiguration, getPanelId, - CommonProperties + CommonProperties, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './NanoBananaEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -26,7 +27,7 @@ export function NanoBananaEdit( const modelKey = 'fal-ai/nano-banana/edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaPro.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaPro.ts index d3757894..b376293d 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaPro.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaPro.ts @@ -2,7 +2,8 @@ import { ImageOutput, CommonProviderConfiguration, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import NanoBananaProSchema from './NanoBananaPro.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -94,7 +95,7 @@ function getProvider( ): Provider<'image', NanoBananaProInput, ImageOutput> { const modelKey = 'fal-ai/nano-banana-pro'; - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`libraries.${getPanelId(modelKey)}.history.label`]: 'Generated From Text' } diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaProEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaProEdit.ts index 65e0d729..1116ea54 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaProEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/NanoBananaProEdit.ts @@ -3,7 +3,8 @@ import { type Provider, type CommonProviderConfiguration, getPanelId, - CommonProperties + CommonProperties, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './NanoBananaProEdit.json'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; @@ -27,7 +28,7 @@ export function NanoBananaProEdit( const modelKey = 'fal-ai/nano-banana-pro/edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/QwenImageEdit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/QwenImageEdit.ts index 1bbda543..0f516227 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/QwenImageEdit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/QwenImageEdit.ts @@ -3,7 +3,8 @@ import { type Provider, type CommonProviderConfiguration, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { Icons, getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './QwenImageEdit.json'; @@ -34,7 +35,7 @@ export function QwenImageEdit( const modelKey = 'fal-ai/qwen-image-edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.ts index b0209c46..568373d2 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.ts @@ -3,7 +3,8 @@ import { type Provider, getPanelId, createTranslationCallback, - normalizeBaseURL + normalizeBaseURL, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import Recraft20bSchema from './Recraft20b.json'; import CreativeEditorSDK, { AssetResult } from '@cesdk/cesdk-js'; @@ -209,7 +210,7 @@ function getProvider( ] = label; }); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId('fal-ai/recraft/v2/text-to-image')}.styleSelection`]: 'Style Selection', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/RecraftV3.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/RecraftV3.ts index 957f394f..146f7f4d 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/RecraftV3.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/RecraftV3.ts @@ -3,7 +3,8 @@ import { type Provider, getPanelId, createTranslationCallback, - normalizeBaseURL + normalizeBaseURL, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { type RecraftV3TextToImageInput } from '@fal-ai/client/endpoints'; import RecraftV3Schema from './RecraftV3.json'; @@ -157,7 +158,7 @@ function getProvider( ] = label; }); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId('fal-ai/recraft-v3')}.styleSelection`]: 'Style Selection', diff --git a/packages/plugin-ai-image-generation-web/src/fal-ai/SeedreamV4Edit.ts b/packages/plugin-ai-image-generation-web/src/fal-ai/SeedreamV4Edit.ts index 41fca673..75c88cf9 100644 --- a/packages/plugin-ai-image-generation-web/src/fal-ai/SeedreamV4Edit.ts +++ b/packages/plugin-ai-image-generation-web/src/fal-ai/SeedreamV4Edit.ts @@ -4,7 +4,8 @@ import { type CommonProviderConfiguration, getPanelId, CommonProperties, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { Icons, getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './SeedreamV4Edit.json'; @@ -33,7 +34,7 @@ export function SeedreamV4Edit( const modelKey = 'fal-ai/bytedance/seedream/v4/edit'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/open-ai/GptImage1.text2image.ts b/packages/plugin-ai-image-generation-web/src/open-ai/GptImage1.text2image.ts index 81bf7172..b8dc24f7 100644 --- a/packages/plugin-ai-image-generation-web/src/open-ai/GptImage1.text2image.ts +++ b/packages/plugin-ai-image-generation-web/src/open-ai/GptImage1.text2image.ts @@ -2,7 +2,8 @@ import { Icons } from '@imgly/plugin-utils'; import { CommonProviderConfiguration, getPanelId, - type Provider + type Provider, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import GptImage1Schema from './GptImage1.text2image.json'; import CreativeEditorSDK, { AssetResult } from '@cesdk/cesdk-js'; @@ -71,7 +72,7 @@ function getProvider( ); cesdk.ui.addIconSet('@imgly/plugin/formats', Icons.Formats); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.styleSelection`]: 'Style Selection', [`panel.gpt-image-1.imageSelection`]: 'Select Image To Change' diff --git a/packages/plugin-ai-image-generation-web/src/open-ai/quickActions/ChangeStyleLibrary.ts b/packages/plugin-ai-image-generation-web/src/open-ai/quickActions/ChangeStyleLibrary.ts index 4fae09fa..fa040ffe 100644 --- a/packages/plugin-ai-image-generation-web/src/open-ai/quickActions/ChangeStyleLibrary.ts +++ b/packages/plugin-ai-image-generation-web/src/open-ai/quickActions/ChangeStyleLibrary.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri, CustomAssetSource, isDefined } from '@imgly/plugin-utils'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -192,7 +193,7 @@ const ChangeStyleLibrary = (context: { }); addStyleAssetSource(styleAssetSource, { cesdk }); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_PREFIX}`]: 'Change Style', [`${I18N_PREFIX}.description`]: 'Apply different art styles to your image' diff --git a/packages/plugin-ai-image-generation-web/src/plugin.ts b/packages/plugin-ai-image-generation-web/src/plugin.ts index 73a68bc6..3dd2f9c3 100644 --- a/packages/plugin-ai-image-generation-web/src/plugin.ts +++ b/packages/plugin-ai-image-generation-web/src/plugin.ts @@ -6,7 +6,8 @@ import { ActionRegistry, initializeQuickActionComponents, AI_EDIT_MODE, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { PluginConfiguration } from './types'; import iconSprite, { PLUGIN_ICON_SET_ID } from './iconSprite'; @@ -62,7 +63,9 @@ export function ImageGeneration( const ACTION_LABEL_KEY = `${PLUGIN_ID}.action.label`; cesdk.ui.addIconSet(PLUGIN_ICON_SET_ID, iconSprite); - cesdk.i18n.setTranslations({ + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, { en: { [`panel.${IMAGE_GENERATION_PANEL_ID}`]: 'Image Generation', [`${IMAGE_GENERATION_PANEL_ID}.dock.label`]: 'AI Image', @@ -78,6 +81,7 @@ export function ImageGeneration( pluginId: PLUGIN_ID, label: translateWithFallback(cesdk, ACTION_LABEL_KEY, 'Generate Image'), + labelKey: ACTION_LABEL_KEY, meta: { panelId: IMAGE_GENERATION_PANEL_ID }, execute: () => { diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/ArtistTransfer.ts b/packages/plugin-ai-image-generation-web/src/quickActions/ArtistTransfer.ts index 8281c4ba..5ec376f3 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/ArtistTransfer.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/ArtistTransfer.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; @@ -120,7 +121,7 @@ const ArtistTransfer: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Painted By', [`${I18N_DEFAULT_PREFIX}.description`]: diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/CombineImages.ts b/packages/plugin-ai-image-generation-web/src/quickActions/CombineImages.ts index 66da4aeb..f09dbf3d 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/CombineImages.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/CombineImages.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; import { GetQuickActionDefinition } from './types'; @@ -52,7 +55,7 @@ const CombineImages: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}.apply`]: 'Combine', [`${I18N_DEFAULT_PREFIX}`]: 'Combine Images...', diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/CreateVariant.ts b/packages/plugin-ai-image-generation-web/src/quickActions/CreateVariant.ts index 611444ff..0a9482af 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/CreateVariant.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/CreateVariant.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; @@ -54,7 +55,7 @@ const CreateVariant: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Create Variant...', [`${I18N_DEFAULT_PREFIX}.description`]: 'Create a variation of the image', diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/EditImage.ts b/packages/plugin-ai-image-generation-web/src/quickActions/EditImage.ts index 3d4663b5..9298b16a 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/EditImage.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/EditImage.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; @@ -54,7 +55,7 @@ const EditImage: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Edit Image...', [`${I18N_DEFAULT_PREFIX}.description`]: diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/RemixPage.ts b/packages/plugin-ai-image-generation-web/src/quickActions/RemixPage.ts index cc31a1ff..b8168940 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/RemixPage.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/RemixPage.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import { GetQuickActionDefinition } from './types'; @@ -50,7 +53,7 @@ const RemixPage: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Turn Page into Image' } diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/RemixPageWithPrompt.ts b/packages/plugin-ai-image-generation-web/src/quickActions/RemixPageWithPrompt.ts index 877c5886..2a6e240d 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/RemixPageWithPrompt.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/RemixPageWithPrompt.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import { GetQuickActionDefinition } from './types'; @@ -52,7 +55,7 @@ const RemixPageWithPrompt: GetQuickActionDefinition = ({ true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Remix Page...', [`${I18N_DEFAULT_PREFIX}.prompt`]: 'Remix Page Prompt', diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/StyleTransfer.ts b/packages/plugin-ai-image-generation-web/src/quickActions/StyleTransfer.ts index f95da469..abef1e27 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/StyleTransfer.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/StyleTransfer.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; @@ -92,7 +93,7 @@ const StyleTransfer: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Change Art Style', [`${I18N_DEFAULT_PREFIX}.description`]: diff --git a/packages/plugin-ai-image-generation-web/src/quickActions/SwapBackground.ts b/packages/plugin-ai-image-generation-web/src/quickActions/SwapBackground.ts index 37b3483e..65ce268a 100644 --- a/packages/plugin-ai-image-generation-web/src/quickActions/SwapBackground.ts +++ b/packages/plugin-ai-image-generation-web/src/quickActions/SwapBackground.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; import { GetQuickActionDefinition } from './types'; @@ -53,7 +54,7 @@ const SwapBackground: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Swap Background...', [`${I18N_DEFAULT_PREFIX}.description`]: diff --git a/packages/plugin-ai-image-generation-web/src/runware/Flux2Dev.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/Flux2Dev.image2image.ts index 22af8a69..beec7865 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/Flux2Dev.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/Flux2Dev.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -47,7 +48,7 @@ export function Flux2DevImage2Image( const providerId = 'runware/bfl/flux-2-dev/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/Flux2Flex.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/Flux2Flex.image2image.ts index d94923e5..72e41ae5 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/Flux2Flex.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/Flux2Flex.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -49,7 +50,7 @@ export function Flux2FlexImage2Image( const providerId = 'runware/bfl/flux-2-flex/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/Flux2Pro.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/Flux2Pro.image2image.ts index 46debbe2..06a29cdc 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/Flux2Pro.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/Flux2Pro.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -46,7 +47,7 @@ export function Flux2ProImage2Image( const providerId = 'runware/bfl/flux-2-pro/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/GptImage1.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/GptImage1.image2image.ts index 734e1126..27a4079b 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/GptImage1.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/GptImage1.image2image.ts @@ -3,7 +3,8 @@ import { type ImageOutput, CommonProperties, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -59,7 +60,7 @@ export function GptImage1Image2Image( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/GptImage1Mini.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/GptImage1Mini.image2image.ts index 22676317..1924a9cd 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/GptImage1Mini.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/GptImage1Mini.image2image.ts @@ -3,7 +3,8 @@ import { type ImageOutput, CommonProperties, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -59,7 +60,7 @@ export function GptImage1MiniImage2Image( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/NanoBanana2Pro.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/NanoBanana2Pro.image2image.ts index 385cda03..d73ba42b 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/NanoBanana2Pro.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/NanoBanana2Pro.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -45,7 +46,7 @@ export function NanoBanana2ProImage2Image( const providerId = 'runware/google/nano-banana-2-pro/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/Seedream4.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/Seedream4.image2image.ts index e1f84594..311aad64 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/Seedream4.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/Seedream4.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -57,7 +58,7 @@ export function Seedream4Image2Image( const providerId = 'runware/bytedance/seedream-4/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/src/runware/Seedream45.image2image.ts b/packages/plugin-ai-image-generation-web/src/runware/Seedream45.image2image.ts index 0393dc97..8a70f2bb 100644 --- a/packages/plugin-ai-image-generation-web/src/runware/Seedream45.image2image.ts +++ b/packages/plugin-ai-image-generation-web/src/runware/Seedream45.image2image.ts @@ -2,7 +2,8 @@ import { type Provider, type ImageOutput, CommonProperties, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -46,7 +47,7 @@ export function Seedream45Image2Image( const providerId = 'runware/bytedance/seedream-4-5/image2image'; // Set translations for the panel - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Edit', diff --git a/packages/plugin-ai-image-generation-web/translations.json b/packages/plugin-ai-image-generation-web/translations.json index f6e00251..186256a2 100644 --- a/packages/plugin-ai-image-generation-web/translations.json +++ b/packages/plugin-ai-image-generation-web/translations.json @@ -2,6 +2,7 @@ "en": { "panel.ly.img.ai.image-generation": "Image Generation", "ly.img.ai.image-generation.dock.label": "AI Image", + "@imgly/plugin-ai-image-generation-web.action.label": "Generate Image", "panel.ly.img.panel.fal-ai.recraft-v3.styleSelection": "Style Selection", "panel.ly.img.panel.fal-ai.recraft.v2.text-to-image.styleSelection": "Style Selection", "panel.gpt-image-1.imageSelection": "Select Image To Change", @@ -534,4 +535,4 @@ "ly.img.plugin-ai-image-generation-web.eachlabs/seedream-v4.5/edit.property.prompt": "Prompt", "ly.img.plugin-ai-image-generation-web.eachlabs/seedream-v4.5/edit.property.prompt.placeholder": "Describe the changes..." } -} \ No newline at end of file +} diff --git a/packages/plugin-ai-sticker-generation-web/CHANGELOG.md b/packages/plugin-ai-sticker-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-sticker-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-sticker-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-sticker-generation-web/package.json b/packages/plugin-ai-sticker-generation-web/package.json index 5662de1c..e180950a 100644 --- a/packages/plugin-ai-sticker-generation-web/package.json +++ b/packages/plugin-ai-sticker-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-sticker-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI sticker generation plugin for the CE.SDK editor", "keywords": [ "CE.SDK", diff --git a/packages/plugin-ai-sticker-generation-web/src/fal-ai/Recraft20b.ts b/packages/plugin-ai-sticker-generation-web/src/fal-ai/Recraft20b.ts index 7a728c99..6612776d 100644 --- a/packages/plugin-ai-sticker-generation-web/src/fal-ai/Recraft20b.ts +++ b/packages/plugin-ai-sticker-generation-web/src/fal-ai/Recraft20b.ts @@ -3,7 +3,8 @@ import { CommonProviderConfiguration, getPanelId, createTranslationCallback, - normalizeBaseURL + normalizeBaseURL, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import Recraft20bSchema from './Recraft20b.json'; import CreativeEditorSDK, { AssetResult } from '@cesdk/cesdk-js'; @@ -126,7 +127,7 @@ function getProvider( ] = label; }); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId( 'fal-ai/recraft/v2/text-to-sticker' diff --git a/packages/plugin-ai-sticker-generation-web/src/plugin.ts b/packages/plugin-ai-sticker-generation-web/src/plugin.ts index 9e6dc145..09a27a78 100644 --- a/packages/plugin-ai-sticker-generation-web/src/plugin.ts +++ b/packages/plugin-ai-sticker-generation-web/src/plugin.ts @@ -4,7 +4,8 @@ import { Output, registerDockComponent, ActionRegistry, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { PluginConfiguration } from './types'; import iconSprite, { PLUGIN_ICON_SET_ID } from './iconSprite'; @@ -44,7 +45,9 @@ export function StickerGeneration( const ACTION_LABEL_KEY = `${PLUGIN_ID}.action.label`; cesdk.ui.addIconSet(PLUGIN_ICON_SET_ID, iconSprite); - cesdk.i18n.setTranslations({ + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, { en: { [`panel.${STICKER_GENERATION_PANEL_ID}`]: 'Sticker Generation', [`${STICKER_GENERATION_PANEL_ID}.dock.label`]: 'AI Sticker', @@ -64,6 +67,7 @@ export function StickerGeneration( ACTION_LABEL_KEY, 'Generate Sticker' ), + labelKey: ACTION_LABEL_KEY, meta: { panelId: STICKER_GENERATION_PANEL_ID }, execute: () => { diff --git a/packages/plugin-ai-sticker-generation-web/translations.json b/packages/plugin-ai-sticker-generation-web/translations.json index 29b50a76..5bd8f3b0 100644 --- a/packages/plugin-ai-sticker-generation-web/translations.json +++ b/packages/plugin-ai-sticker-generation-web/translations.json @@ -2,9 +2,9 @@ "en": { "panel.ly.img.ai.sticker-generation": "Sticker Generation", "ly.img.ai.sticker-generation.dock.label": "AI Sticker", + "@imgly/plugin-ai-sticker-generation-web.action.label": "Generate Sticker", "ly.img.plugin-ai-sticker-generation-web.fal-ai/recraft/v2/text-to-sticker.property.image_size": "Format", "ly.img.plugin-ai-sticker-generation-web.fal-ai/recraft/v2/text-to-sticker.property.style": "Style", - "ly.img.plugin-ai-sticker-generation-web.fal-ai/recraft/v2/text-to-sticker.property.style.icon/broken_line": "Broken Line", "ly.img.plugin-ai-sticker-generation-web.fal-ai/recraft/v2/text-to-sticker.property.style.icon/colored_outline": "Colored Outline", "ly.img.plugin-ai-sticker-generation-web.fal-ai/recraft/v2/text-to-sticker.property.style.icon/colored_shapes": "Colored Shapes", diff --git a/packages/plugin-ai-text-generation-web/CHANGELOG.md b/packages/plugin-ai-text-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-text-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-text-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-text-generation-web/package.json b/packages/plugin-ai-text-generation-web/package.json index 62866656..d2a15553 100644 --- a/packages/plugin-ai-text-generation-web/package.json +++ b/packages/plugin-ai-text-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-text-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI text generation plugin for the CE.SDK editor", "keywords": [ "CE.SDK", diff --git a/packages/plugin-ai-text-generation-web/src/plugin.ts b/packages/plugin-ai-text-generation-web/src/plugin.ts index 5dbf1681..61ff7438 100644 --- a/packages/plugin-ai-text-generation-web/src/plugin.ts +++ b/packages/plugin-ai-text-generation-web/src/plugin.ts @@ -7,7 +7,8 @@ import { ActionRegistry, initializeQuickActionComponents, AI_EDIT_MODE, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { toArray } from '@imgly/plugin-utils'; import Improve from './quickActions/Improve'; @@ -47,7 +48,9 @@ export function TextGeneration( ); cesdk.ui.addIconSet(PLUGIN_ICON_SET_ID, iconSprite); - cesdk.i18n.setTranslations({ + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, { en: { 'common.apply': 'Apply', 'common.back': 'Back' diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTextTo.ts b/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTextTo.ts index d2f1e2e8..5c73edd5 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTextTo.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTextTo.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import changeTextTo from '../prompts/changeTextTo'; import { GetQuickActionDefinition } from './types'; @@ -50,7 +53,7 @@ const ChangeTextTo: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Change Text to...', [`${I18N_DEFAULT_PREFIX}.prompt`]: 'Change Text to...', diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTone.ts b/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTone.ts index bb9c11fa..f698b327 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTone.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/ChangeTone.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import changeTone from '../prompts/changeTone'; import { GetQuickActionDefinition } from './types'; @@ -59,7 +62,7 @@ const ChangeTone: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Change Tone', [`${I18N_DEFAULT_PREFIX}.professional`]: 'Professional', diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/Fix.ts b/packages/plugin-ai-text-generation-web/src/quickActions/Fix.ts index 8f6e32c6..44a51388 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/Fix.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/Fix.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import fix from '../prompts/fix'; import { GetQuickActionDefinition } from './types'; @@ -49,7 +52,7 @@ const Fix: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Fix Spelling & Grammar' } diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/Improve.ts b/packages/plugin-ai-text-generation-web/src/quickActions/Improve.ts index 8c570060..a7a7a826 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/Improve.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/Improve.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import improve from '../prompts/improve'; import { GetQuickActionDefinition } from './types'; @@ -49,7 +52,7 @@ const Improve: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Improve Writing' } diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/Longer.ts b/packages/plugin-ai-text-generation-web/src/quickActions/Longer.ts index 1a488d47..12388acb 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/Longer.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/Longer.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import longer from '../prompts/longer'; import { GetQuickActionDefinition } from './types'; @@ -49,7 +52,7 @@ const Longer: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Make Longer' } diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/Shorter.ts b/packages/plugin-ai-text-generation-web/src/quickActions/Shorter.ts index 1c521bcd..abcacc99 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/Shorter.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/Shorter.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import shorter from '../prompts/shorter'; import { GetQuickActionDefinition } from './types'; @@ -49,7 +52,7 @@ const Shorter: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Make Shorter' } diff --git a/packages/plugin-ai-text-generation-web/src/quickActions/Translate.ts b/packages/plugin-ai-text-generation-web/src/quickActions/Translate.ts index 57329973..e4dc80fd 100644 --- a/packages/plugin-ai-text-generation-web/src/quickActions/Translate.ts +++ b/packages/plugin-ai-text-generation-web/src/quickActions/Translate.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import translate, { LOCALES } from '../prompts/translate'; import { GetQuickActionDefinition } from './types'; @@ -54,7 +57,7 @@ const Translate: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Translate', [`${I18N_DEFAULT_PREFIX}.en_US`]: 'English (US)', diff --git a/packages/plugin-ai-text-generation-web/translations.json b/packages/plugin-ai-text-generation-web/translations.json index 081f8e05..0b62a3c3 100644 --- a/packages/plugin-ai-text-generation-web/translations.json +++ b/packages/plugin-ai-text-generation-web/translations.json @@ -52,4 +52,4 @@ "ly.img.plugin-ai-text-generation-web.openai.property.prompt": "Prompt", "ly.img.plugin-ai-text-generation-web.openai.property.temperature": "GPT Creativity Level" } -} \ No newline at end of file +} diff --git a/packages/plugin-ai-video-generation-web/CHANGELOG.md b/packages/plugin-ai-video-generation-web/CHANGELOG.md index b4055019..51397c76 100644 --- a/packages/plugin-ai-video-generation-web/CHANGELOG.md +++ b/packages/plugin-ai-video-generation-web/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.2.16] - 2026-01-16 + +### New Features + +- [all] **Translatable Action Labels**: AI plugin action labels now support full i18n through `labelKey` property, enabling dynamic translation resolution based on the current locale. +- [generation-web] **setDefaultTranslations Utility**: Added `setDefaultTranslations()` function that only sets translation keys that don't already exist, allowing integrators to customize translations before plugins load without being overwritten. +- [all] **External Translation Files**: Plugin translations are now loaded from external `translations.json` files, making it easier to review and customize available translation keys. + +### Improvements + +- [all] **Translation Override Pattern**: All AI plugins now use `setDefaultTranslations()` instead of `setTranslations()`, ensuring integrator-provided translations take priority over plugin defaults. +- [apps-web] **Dynamic Card Labels**: AI Apps panel now resolves card labels dynamically using `labelKey` from action metadata, enabling proper i18n for app cards. + +### Documentation + +- [all] Added comprehensive i18n documentation to README files explaining how to customize translations before plugin initialization. + ## [0.2.15] - 2026-01-12 ### New Features diff --git a/packages/plugin-ai-video-generation-web/README.md b/packages/plugin-ai-video-generation-web/README.md index 5fafae53..c66aecfd 100644 --- a/packages/plugin-ai-video-generation-web/README.md +++ b/packages/plugin-ai-video-generation-web/README.md @@ -1255,9 +1255,27 @@ currentOrder.splice(2, 0, 'ly.img.ai.video-generation.dock'); cesdk.ui.setDockOrder(currentOrder); ``` -## Translations +## Internationalization (i18n) -For customization and localization, see the [translations.json](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-video-generation-web/translations.json) file which contains provider-specific translation keys for video generation interfaces. +The Video Generation plugin supports full internationalization. To customize translations, set them **before** adding the plugin: + +```typescript +cesdk.i18n.setTranslations({ + en: { + '@imgly/plugin-ai-video-generation-web.action.label': 'Create Video' + }, + de: { + '@imgly/plugin-ai-video-generation-web.action.label': 'Video erstellen' + } +}); + +// Then add the plugins - they won't override your custom translations +await cesdk.addPlugin(AiApps({ providers: { /* ... */ } })); +``` + +For detailed documentation on the translation system, see the [Internationalization section](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-generation-web#internationalization-i18n) in the core AI generation package. + +For all available translation keys, see the [translations.json](https://github.com/imgly/plugins/tree/main/packages/plugin-ai-video-generation-web/translations.json) file. ## Related Packages diff --git a/packages/plugin-ai-video-generation-web/package.json b/packages/plugin-ai-video-generation-web/package.json index a99f9eb2..495c9c32 100644 --- a/packages/plugin-ai-video-generation-web/package.json +++ b/packages/plugin-ai-video-generation-web/package.json @@ -1,6 +1,6 @@ { "name": "@imgly/plugin-ai-video-generation-web", - "version": "0.2.15", + "version": "0.2.16", "description": "AI video generation plugin for the CE.SDK editor", "keywords": ["CE.SDK", "plugin", "AI", "video-generation"], "repository": { diff --git a/packages/plugin-ai-video-generation-web/src/eachlabs/KlingO1.image2video.ts b/packages/plugin-ai-video-generation-web/src/eachlabs/KlingO1.image2video.ts index f64978ae..0b0aff12 100644 --- a/packages/plugin-ai-video-generation-web/src/eachlabs/KlingO1.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/eachlabs/KlingO1.image2video.ts @@ -1,7 +1,8 @@ import { type Provider, type VideoOutput, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function KlingO1ImageToVideo( const providerId = 'eachlabs/kling-o1-image-to-video'; // Set translations for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/eachlabs/KlingV26Pro.image2video.ts b/packages/plugin-ai-video-generation-web/src/eachlabs/KlingV26Pro.image2video.ts index 39bd7f22..d6830757 100644 --- a/packages/plugin-ai-video-generation-web/src/eachlabs/KlingV26Pro.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/eachlabs/KlingV26Pro.image2video.ts @@ -1,7 +1,8 @@ import { type Provider, type VideoOutput, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function KlingV26ProImageToVideo( const providerId = 'eachlabs/kling-v2-6-pro-image-to-video'; // Set translations for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/eachlabs/Veo31.image2video.ts b/packages/plugin-ai-video-generation-web/src/eachlabs/Veo31.image2video.ts index e9f2e449..b827805e 100644 --- a/packages/plugin-ai-video-generation-web/src/eachlabs/Veo31.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/eachlabs/Veo31.image2video.ts @@ -1,7 +1,8 @@ import { type Provider, type VideoOutput, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -38,7 +39,7 @@ export function Veo31ImageToVideo( const providerId = 'eachlabs/veo3-1-image-to-video'; // Set translations for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/ByteDanceSeedanceV1ProImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/ByteDanceSeedanceV1ProImageToVideo.ts index b2ff9623..79dc9e8c 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/ByteDanceSeedanceV1ProImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/ByteDanceSeedanceV1ProImageToVideo.ts @@ -2,7 +2,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './ByteDanceSeedanceV1ProImageToVideo.json'; @@ -37,7 +38,7 @@ export function ByteDanceSeedanceV1ProImageToVideo( const modelKey = 'fal-ai/bytedance/seedance/v1/pro/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Generate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/KlingVideoV21MasterImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/KlingVideoV21MasterImageToVideo.ts index a40726fa..07a67aec 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/KlingVideoV21MasterImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/KlingVideoV21MasterImageToVideo.ts @@ -2,7 +2,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL, getImageUri } from '@imgly/plugin-utils'; import schema from './KlingVideoV21MasterImageToVideo.json'; @@ -34,7 +35,7 @@ export function KlingVideoV21MasterImageToVideo( const modelKey = 'fal-ai/kling-video/v2.1/master/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { 'ly.img.ai.quickAction.createVideoKling': 'Create Video (Kling)...', [`panel.${getPanelId(modelKey)}.imageSelection`]: diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxHailuo02StandardImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxHailuo02StandardImageToVideo.ts index d6c001c3..6086fd0f 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxHailuo02StandardImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxHailuo02StandardImageToVideo.ts @@ -2,7 +2,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './MinimaxHailuo02StandardImageToVideo.json'; @@ -34,7 +35,7 @@ export function MinimaxHailuo02StandardImageToVideo( const modelKey = 'fal-ai/minimax/hailuo-02/standard/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Generate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxVideo01LiveImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxVideo01LiveImageToVideo.ts index 8b804154..8e5438dd 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxVideo01LiveImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/MinimaxVideo01LiveImageToVideo.ts @@ -3,7 +3,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './MinimaxVideo01LiveImageToVideo.json'; @@ -27,7 +28,7 @@ export function MinimaxVideo01LiveImageToVideo( const modelKey = 'fal-ai/minimax/video-01-live/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Generate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/PixverseV35TextToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/PixverseV35TextToVideo.ts index 0cd57d6f..efd61892 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/PixverseV35TextToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/PixverseV35TextToVideo.ts @@ -2,7 +2,8 @@ import { CommonProviderConfiguration, VideoOutput, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './PixverseV35TextToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -30,7 +31,7 @@ export function PixverseV35TextToVideo( const modelKey = 'fal-ai/pixverse/v3.5/text-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`libraries.${getPanelId(modelKey)}.history.label`]: 'Generated From Text' diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastFirstLastFrameToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastFirstLastFrameToVideo.ts index f8bac1ca..ea801419 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastFirstLastFrameToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastFirstLastFrameToVideo.ts @@ -3,7 +3,8 @@ import { type Provider, CommonProviderConfiguration, getPanelId, - mergeQuickActionsConfig + mergeQuickActionsConfig, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './Veo31FastFirstLastFrameToVideo.json'; @@ -38,7 +39,7 @@ export function Veo31FastFirstLastFrameToVideo( const modelKey = 'fal-ai/veo3.1/fast/first-last-frame-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Images To Generate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastImageToVideo.ts index 8ed9de2e..d7b0b84e 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastImageToVideo.ts @@ -2,7 +2,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Veo31FastImageToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -36,7 +37,7 @@ export function Veo31FastImageToVideo( const modelKey = 'fal-ai/veo3.1/fast/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastTextToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastTextToVideo.ts index 71b3e163..e7b03f95 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastTextToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FastTextToVideo.ts @@ -2,7 +2,8 @@ import { CommonProviderConfiguration, VideoOutput, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Veo31FastTextToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -32,7 +33,7 @@ export function Veo31FastTextToVideo( const modelKey = 'fal-ai/veo3.1/fast'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.prompt`]: 'Enter your prompt', [`panel.${modelKey}.prompt`]: 'Enter your prompt', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FirstLastFrameToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FirstLastFrameToVideo.ts index b3884630..3b394814 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FirstLastFrameToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31FirstLastFrameToVideo.ts @@ -3,7 +3,8 @@ import { type Provider, CommonProviderConfiguration, getPanelId, - mergeQuickActionsConfig + mergeQuickActionsConfig, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL } from '@imgly/plugin-utils'; import schema from './Veo31FirstLastFrameToVideo.json'; @@ -36,7 +37,7 @@ export function Veo31FirstLastFrameToVideo( const modelKey = 'fal-ai/veo3.1/first-last-frame-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Images To Generate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31ImageToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31ImageToVideo.ts index 25902927..e3d30148 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31ImageToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31ImageToVideo.ts @@ -2,7 +2,8 @@ import { VideoOutput, type Provider, CommonProviderConfiguration, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Veo31ImageToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -29,7 +30,7 @@ export function Veo31ImageToVideo( const modelKey = 'fal-ai/veo3.1/image-to-video'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31TextToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31TextToVideo.ts index 287a0800..ce5b6314 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31TextToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo31TextToVideo.ts @@ -2,7 +2,8 @@ import { CommonProviderConfiguration, VideoOutput, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Veo31TextToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -32,7 +33,7 @@ export function Veo31TextToVideo( const modelKey = 'fal-ai/veo3.1'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.prompt`]: 'Enter your prompt', [`panel.${modelKey}.prompt`]: 'Enter your prompt', diff --git a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo3TextToVideo.ts b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo3TextToVideo.ts index db54035b..8b17ba73 100644 --- a/packages/plugin-ai-video-generation-web/src/fal-ai/Veo3TextToVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/fal-ai/Veo3TextToVideo.ts @@ -2,7 +2,8 @@ import { CommonProviderConfiguration, VideoOutput, type Provider, - getPanelId + getPanelId, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import schema from './Veo3TextToVideo.json'; import CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -27,7 +28,7 @@ export function Veo3TextToVideo( const modelKey = 'fal-ai/veo3'; // Set translations - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(modelKey)}.prompt`]: 'Enter your prompt', [`panel.${modelKey}.prompt`]: 'Enter your prompt', diff --git a/packages/plugin-ai-video-generation-web/src/plugin.ts b/packages/plugin-ai-video-generation-web/src/plugin.ts index ad0ad752..92a46000 100644 --- a/packages/plugin-ai-video-generation-web/src/plugin.ts +++ b/packages/plugin-ai-video-generation-web/src/plugin.ts @@ -4,7 +4,8 @@ import { initializeProviders, Output, registerDockComponent, - checkAiPluginVersion + checkAiPluginVersion, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { PluginConfiguration } from './types'; import { toArray, translateWithFallback } from '@imgly/plugin-utils'; @@ -51,7 +52,9 @@ export function VideoGeneration( const ACTION_LABEL_KEY = `${PLUGIN_ID}.action.label`; - cesdk.setTranslations({ + // Use setDefaultTranslations to allow integrators to override these values + // by calling setTranslations() BEFORE adding the plugin + setDefaultTranslations(cesdk, { en: { [`panel.${VIDEO_GENERATION_PANEL_ID}`]: 'Video Generation', [`${VIDEO_GENERATION_PANEL_ID}.dock.label`]: 'AI Video', @@ -72,6 +75,7 @@ export function VideoGeneration( pluginId: PLUGIN_ID, label: translateWithFallback(cesdk, ACTION_LABEL_KEY, 'Generate Video'), + labelKey: ACTION_LABEL_KEY, meta: { panelId: VIDEO_GENERATION_PANEL_ID }, execute: () => { diff --git a/packages/plugin-ai-video-generation-web/src/quickActions/AnimateBetweenImages.ts b/packages/plugin-ai-video-generation-web/src/quickActions/AnimateBetweenImages.ts index f3f29b4d..a19143b2 100644 --- a/packages/plugin-ai-video-generation-web/src/quickActions/AnimateBetweenImages.ts +++ b/packages/plugin-ai-video-generation-web/src/quickActions/AnimateBetweenImages.ts @@ -1,4 +1,7 @@ -import { QuickActionDefinition } from '@imgly/plugin-ai-generation-web'; +import { + QuickActionDefinition, + setDefaultTranslations +} from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; import { GetQuickActionDefinition } from './types'; @@ -54,7 +57,7 @@ const AnimateBetweenImages: GetQuickActionDefinition = ({ true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Animate Between Images...', [`${I18N_DEFAULT_PREFIX}.description`]: diff --git a/packages/plugin-ai-video-generation-web/src/quickActions/CreateVideo.ts b/packages/plugin-ai-video-generation-web/src/quickActions/CreateVideo.ts index 8ebc5811..5f02a87e 100644 --- a/packages/plugin-ai-video-generation-web/src/quickActions/CreateVideo.ts +++ b/packages/plugin-ai-video-generation-web/src/quickActions/CreateVideo.ts @@ -1,6 +1,7 @@ import { enableQuickActionForImageFill, - QuickActionDefinition + QuickActionDefinition, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageUri } from '@imgly/plugin-utils'; @@ -54,7 +55,7 @@ const CreateVideo: GetQuickActionDefinition = ({ cesdk }) => { true ); - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`${I18N_DEFAULT_PREFIX}`]: 'Create Video...', [`${I18N_DEFAULT_PREFIX}.description`]: 'Create a video from the image' diff --git a/packages/plugin-ai-video-generation-web/src/runware/Sora2.image2video.ts b/packages/plugin-ai-video-generation-web/src/runware/Sora2.image2video.ts index 4f3a29f1..a2cfb60f 100644 --- a/packages/plugin-ai-video-generation-web/src/runware/Sora2.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/runware/Sora2.image2video.ts @@ -2,7 +2,8 @@ import { type Provider, type VideoOutput, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL, Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -73,7 +74,7 @@ export function Sora2Image2Video( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Required i18n for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/runware/Sora2Pro.image2video.ts b/packages/plugin-ai-video-generation-web/src/runware/Sora2Pro.image2video.ts index cf62266d..29e697c0 100644 --- a/packages/plugin-ai-video-generation-web/src/runware/Sora2Pro.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/runware/Sora2Pro.image2video.ts @@ -2,7 +2,8 @@ import { type Provider, type VideoOutput, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL, Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -79,7 +80,7 @@ export function Sora2ProImage2Video( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Required i18n for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/runware/Veo31.image2video.ts b/packages/plugin-ai-video-generation-web/src/runware/Veo31.image2video.ts index 80e9ac9e..2953893a 100644 --- a/packages/plugin-ai-video-generation-web/src/runware/Veo31.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/runware/Veo31.image2video.ts @@ -2,7 +2,8 @@ import { type Provider, type VideoOutput, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL, Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -77,7 +78,7 @@ export function Veo31Image2Video( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Required i18n for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/src/runware/Veo31Fast.image2video.ts b/packages/plugin-ai-video-generation-web/src/runware/Veo31Fast.image2video.ts index 14697032..0b237492 100644 --- a/packages/plugin-ai-video-generation-web/src/runware/Veo31Fast.image2video.ts +++ b/packages/plugin-ai-video-generation-web/src/runware/Veo31Fast.image2video.ts @@ -2,7 +2,8 @@ import { type Provider, type VideoOutput, getPanelId, - addIconSetOnce + addIconSetOnce, + setDefaultTranslations } from '@imgly/plugin-ai-generation-web'; import { getImageDimensionsFromURL, Icons } from '@imgly/plugin-utils'; import type CreativeEditorSDK from '@cesdk/cesdk-js'; @@ -82,7 +83,7 @@ export function Veo31FastImage2Video( addIconSetOnce(cesdk, '@imgly/plugin/formats', Icons.Formats); // Required i18n for image selection UI - cesdk.i18n.setTranslations({ + setDefaultTranslations(cesdk, { en: { [`panel.${getPanelId(providerId)}.imageSelection`]: 'Select Image To Animate', diff --git a/packages/plugin-ai-video-generation-web/translations.json b/packages/plugin-ai-video-generation-web/translations.json index 673a2e35..45d37b12 100644 --- a/packages/plugin-ai-video-generation-web/translations.json +++ b/packages/plugin-ai-video-generation-web/translations.json @@ -2,6 +2,7 @@ "en": { "panel.ly.img.ai.video-generation": "Video Generation", "ly.img.ai.video-generation.dock.label": "AI Video", + "@imgly/plugin-ai-video-generation-web.action.label": "Generate Video", "ly.img.ai.video.generation.hint": "Video generation may take up to a few minutes. This panel can be closed and you'll be notified when it's ready.", "ly.img.plugin-ai-video-generation-web.quickAction.createVideo": "Create Video", "ly.img.plugin-ai-video-generation-web.quickAction.createVideo.apply": "Generate Video", @@ -244,4 +245,4 @@ "ly.img.plugin-ai-video-generation-web.eachlabs/kling-o1-image-to-video.property.duration.5": "5 seconds", "ly.img.plugin-ai-video-generation-web.eachlabs/kling-o1-image-to-video.property.duration.10": "10 seconds" } -} \ No newline at end of file +} diff --git a/specs/providers/patterns/i18n.md b/specs/providers/patterns/i18n.md index 05e1b5a1..5d4a42f6 100644 --- a/specs/providers/patterns/i18n.md +++ b/specs/providers/patterns/i18n.md @@ -103,8 +103,80 @@ export const defaultTranslations: Record = { This makes the translation available to all providers automatically. +## Integrator Translation Customization + +Integrators can customize translations (e.g., to rebrand "Generate Image" to their own label) in two ways: + +### Option 1: Set Translations BEFORE Adding Plugins (Recommended) + +The AI plugins use `setDefaultTranslations()` internally, which only sets translations for keys that don't already exist. This means integrators can set their custom translations **before** loading the plugins: + +```typescript +// 1. Create the editor +const cesdk = await CreativeEditorSDK.create(container, { ... }); + +// 2. Set custom translations BEFORE adding plugins +// These will NOT be overwritten by plugin defaults +cesdk.setTranslations({ + en: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Create AI Image', + 'panel.ly.img.ai.image-generation': 'AI Image Creator', + 'ly.img.plugin-ai-generation-web.generate': 'Create' + } +}); + +// 3. Add AI plugins - your translations are preserved! +await cesdk.addPlugin(AiApps({ ... })); +``` + +This approach is simpler and more intuitive - you set your customizations once, and they're respected. + +### Option 2: Set Translations AFTER Adding Plugins + +Alternatively, you can override translations after plugins are loaded: + +```typescript +// 1. Create the editor +const cesdk = await CreativeEditorSDK.create(container, { ... }); + +// 2. Add AI plugins - MUST use await! +await cesdk.addPlugin(AiApps({ ... })); + +// 3. AFTER plugins are fully loaded, override translations +cesdk.setTranslations({ + en: { + '@imgly/plugin-ai-image-generation-web.action.label': 'Create AI Image', + 'panel.ly.img.ai.image-generation': 'AI Image Creator', + 'ly.img.plugin-ai-generation-web.generate': 'Create' + } +}); +``` + +**Important:** You **must `await`** the `addPlugin()` call if using this approach. + +### Action Label Keys + +Each plugin registers an action label that appears in the AI Apps panel: + +| Plugin | Translation Key | Default Value | +|--------|-----------------|---------------| +| Image Generation | `@imgly/plugin-ai-image-generation-web.action.label` | `'Generate Image'` | +| Video Generation | `@imgly/plugin-ai-video-generation-web.action.label` | `'Generate Video'` | +| Sticker Generation | `@imgly/plugin-ai-sticker-generation-web.action.label` | `'Generate Sticker'` | +| Audio (Sound) | `@imgly/plugin-ai-audio-generation-web.sound.action.label` | `'Generate Sound'` | +| Audio (Speech) | `@imgly/plugin-ai-audio-generation-web.speech.action.label` | `'AI Voice'` | + +### Testing Translations + +The AI demo app supports a `?translations=true` query parameter that applies test prefixes to all translations: +- `&` prefix for generic translations (e.g., `&Generate Image`) +- `@` prefix for provider-specific translations (e.g., `@Realistic Image`) + +This helps verify that all labels can be customized via `setTranslations()`. + ## Checklist - [ ] History label registered (`libraries.{panelId}.history.label`) - [ ] Custom enum values translated (if not using schema labels) - [ ] Common values added to `defaultTranslations.ts` (if reusable across providers) +- [ ] Integrator translations applied **after** `addPlugin()` completes