Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: generate translations from node-type #47

Open
PRGfx opened this issue Mar 15, 2021 · 6 comments
Open

Feature request: generate translations from node-type #47

PRGfx opened this issue Mar 15, 2021 · 6 comments

Comments

@PRGfx
Copy link
Contributor

PRGfx commented Mar 15, 2021

When creating translated node-types I often find myself writing down the definition with all the "i18n" labels and later open both the node-type and a translation file side-by-side to see which strings I need – sometimes trying to remember what that label of the node-creation-dialog label was etc.

I think it would be really handy to have a context action on node-type definitions to generate xlf files, maybe even on a "label"-basis.

For each language (i.e. folder in Resources/Private/Translations) it would create a file based on the node-type name and stub all properties etc. (e.g. everything with "label: i18n" (or help.message, formatting.placeholder etc.) and a "mappable" path (i.e. we know how to map ui.label, properties.*.ui.label etc. but would maybe ignore other paths).

The default-language could be configurable in the plugin settings. Maybe it would make sense to have the stub configurable as well.

I recognize that it's more difficult to update a translation file (i.e. reading the XML and writing it back - in the best case while preserving comments or whitespaces).
If that would be possible however, it would be a cool context-action to of course just update the translation file and "extract a string" into a translation.
In any case I think it might be possible to get the "content" of /xliff/file/body as string, write it back and append trans-units for new properties/translatables if no matching trans-unit id was found in the file.

@crydotsnake
Copy link

It would also be cool to have the option too create a new translation file from the context menu

SCR-20230302-mtt

With a small .xlf skeleton.

@cvette
Copy link
Owner

cvette commented Jun 3, 2024

@PRGfx @crydotsnake If you're brave enough, you might try this version: https://plugins.jetbrains.com/plugin/9362-neos-support/edit/versions/eap/550359

This adds a context menu action for nodetypes "Translate NodeType". You'll need to configure your locales first. The action should trigger a notification with a link to the settings page.

Best if you backup your files before.

@cvette cvette self-assigned this Jun 4, 2024
@PRGfx
Copy link
Contributor Author

PRGfx commented Jun 4, 2024

Thank you for your work @cvette! I installed 1.22.0-eap.2 now.

  • The notification linking to the settings page, did not lead to the settings page
  • (Maybe it is reasonably simple to add a star button to the list to move a language to the top and maybe at some point there could be some auto-discovered setup-suggestion, but there's always stretch goals)
  • The action creates the translation files and replaces "all" labels in the node-type file with i18n, however the translation file does not contain translations
  • If a file contains multiple node-types (I'm aware that this is considered "bad practice", but I don't see me changing this..), all labels are replaced and a translation file is created for the first of these nodetypes which contains a label
  • I think the product-name needs to match the node-type namespace, not necessarily the containing package
  • (There wasn't much XML generated for me, but the indentation did not match the project settings. I don't know if there are any options for auto-formatting (but then again my make-shift helper scripts don't like autoformatted xlfs ^^))
  • (There was an extra space leading the target-language=".." attribute)

The IDE logged an exception.

java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0
	at java.base/java.util.Vector.get(Vector.java:750)
	at de.vette.idea.neos.actions.TranslateNodeTypeAction.extractNodeType(TranslateNodeTypeAction.java:161)
	at de.vette.idea.neos.actions.TranslateNodeTypeAction.actionPerformed(TranslateNodeTypeAction.java:68)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.doPerformActionOrShowPopup(ActionUtil.kt:304)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks$lambda$4(ActionUtil.kt:275)
	at com.intellij.openapi.actionSystem.impl.ActionManagerImpl.performWithActionCallbacks(ActionManagerImpl.kt:1161)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks(ActionUtil.kt:274)
	at com.intellij.openapi.actionSystem.impl.ActionMenuItem.performAction$lambda$5(ActionMenuItem.kt:269)
	at com.intellij.openapi.wm.impl.FocusManagerImpl.runOnOwnContext(FocusManagerImpl.java:227)
	at com.intellij.openapi.actionSystem.impl.ActionMenuItem.performAction(ActionMenuItem.kt:260)
	at com.intellij.openapi.actionSystem.impl.ActionMenuItem._init_$lambda$0(ActionMenuItem.kt:71)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
	at com.intellij.openapi.actionSystem.impl.ActionMenuItem.fireActionPerformed$lambda$4(ActionMenuItem.kt:102)
	at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:106)
	at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:95)
	at com.intellij.openapi.actionSystem.impl.ActionMenuItem.fireActionPerformed(ActionMenuItem.kt:101)
	at com.intellij.ui.plaf.beg.BegMenuItemUI.doClick(BegMenuItemUI.java:525)
	at com.intellij.ui.plaf.beg.BegMenuItemUI$MyMouseInputHandler.mouseReleased(BegMenuItemUI.java:558)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6657)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3385)
	at java.desktop/java.awt.Component.processEvent(Component.java:6422)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5027)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4855)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4969)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4583)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4524)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2809)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4855)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:794)
	at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:739)
	at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:733)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:766)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:764)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:763)
	at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.kt:699)
	at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.kt:638)
	at com.intellij.ide.IdeEventQueue._dispatchEvent$lambda$10(IdeEventQueue.kt:584)
	at com.intellij.openapi.application.impl.RwLockHolder.runWithEnabledImplicitRead(RwLockHolder.kt:138)
	at com.intellij.openapi.application.impl.RwLockHolder.runWithImplicitRead(RwLockHolder.kt:129)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.kt:584)
	at com.intellij.ide.IdeEventQueue.access$_dispatchEvent(IdeEventQueue.kt:77)
	at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1.compute(IdeEventQueue.kt:362)
	at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1.compute(IdeEventQueue.kt:361)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:843)
	at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1.invoke(IdeEventQueue.kt:361)
	at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1.invoke(IdeEventQueue.kt:356)
	at com.intellij.ide.IdeEventQueueKt.performActivity$lambda$1(IdeEventQueue.kt:1022)
	at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:114)
	at com.intellij.ide.IdeEventQueueKt.performActivity(IdeEventQueue.kt:1022)
	at com.intellij.ide.IdeEventQueue.dispatchEvent$lambda$7(IdeEventQueue.kt:356)
	at com.intellij.openapi.application.impl.RwLockHolder.runIntendedWriteActionOnCurrentThread(RwLockHolder.kt:209)
	at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:830)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.kt:398)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:207)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:92)

(Many other plugins throw this error, so probably not relevant:)

com.intellij.diagnostic.PluginException: `ActionUpdateThread.OLD_EDT` is deprecated and going to be removed soon. 'de.vette.idea.neos.actions.TranslateNodeTypeAction' must override `getActionUpdateThread()` and chose EDT or BGT. See ActionUpdateThread javadoc. [Plugin: vette.neos]
	at com.intellij.diagnostic.PluginProblemReporterImpl.createPluginExceptionByClass(PluginProblemReporterImpl.java:23)
	at com.intellij.diagnostic.PluginException.createByClass(PluginException.java:90)
	at com.intellij.diagnostic.PluginException.reportDeprecatedUsage(PluginException.java:125)
	at com.intellij.openapi.actionSystem.ActionUpdateThreadAware.getActionUpdateThread(ActionUpdateThreadAware.java:21)
	at com.intellij.openapi.actionSystem.AnAction.getActionUpdateThread(AnAction.java:199)

There are some edge-cases with overrides, I think.

# My.NodeTypes/Configuration/NodeTypes.Base.yaml
My.NodeTypes:Text:
  ui:
    label: i18n # My.NodeTypes:NodeTypes.Text:ui.label
# My.Website/Configuration/NodeTypes.Overrides.Base.yaml
My.NodeTypes:Text:
  ui:
    label: i18n # overriding a single label in the distribution is realsitic
    help:
      message: i18n # alternatively add a new label to the existing nodetype
  # everything here is My.NodeTypes:NodeTypes.Text:ui.label etc. (regardless of package where _this_ file is)

My.Website:Text
  superTypes:
    My.NodeTypes:Text: true
  # tweak behavior, add additional properties
  # this might have a label of My.NodeTypes:NodeTypes.Text:ui.label, but My.Website:NodeTypes.Text:properties... etc.
  # however if a property is "redeclared" (i.e. we have a new ui.label=i18n), the label from the My.Website package would be used

This setup with the overrides probably isn't realistic like this, but both approaches individually make sense to me.

As both nodetypes in this scenario have the same name, afaik we could have

<xliff ...>
  <file product-name="My.NodeTypes" ...></file>
  <file product-name="My.Website" ...></file>
</xliff>

From what I understand, they could reside in different xlf files, as long as the file[original] matches the original/expected filename (not sure how this works with nested directories, i.e. NodeTypes/Overrides.Test.xlf + original="Test" vs original="NodeTypes/Test" vs. Overrides/Test.xlf + `original="NodeTypes/Test" etc.)
https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/Internationalization.html#xliff-file-overrides

@PRGfx
Copy link
Contributor Author

PRGfx commented Jun 5, 2024

I tried v1.22.0-eap.3 and it generated multiple translation files for multiple node-types and the settings link in the notification did work, but the translation files were still empty. No further exceptions logged.
For my testing I was working in DistributionPackages/*/Configuration/*, so not in the symlinked location and not with the nodetypes directory, if that helps.

@cvette
Copy link
Owner

cvette commented Jun 6, 2024

Thanks for your detailed feedback!

For my testing I was working in DistributionPackages/*/Configuration/*, so not in the symlinked location and not with the nodetypes directory, if that helps.

This doesn't seem to be the issue. At least I wasn't able to reproduce it this way.

I suspect the problem occurs somewhere in this method: https://github.com/cvette/intellij-neos/blob/main/src/main/java/de/vette/idea/neos/actions/TranslateNodeTypeAction.java#L373C18-L373C46

Do you have any additional plugins installed that might handle XLIFF files? Would you be able to debug this further? That would help a lot.

@PRGfx
Copy link
Contributor Author

PRGfx commented Jun 6, 2024

Phew, that was quite a journey... in the end it turned out that apparently I registered *.xlf as "XML" file-type before and it was then not categorized for your custom "Neos XLIFF" file type (that only included *.xliff).
In turn the file was not "found" when searching for the XliffDomElement and the fileElement was null.
Changing the file-type association brings the plugin to life 👍

However, I don't know if this happened automatically, based on a suggested action, as the file "looks like" XML or because of a plugin that might have come and gone since. So I could imagine, that others might be affected as well – maybe the take-away is only a troubleshooting info in the README.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants