The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
8.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include "../../Utility/Helpers/jucer_TranslationHelpers.h"
  20. //==============================================================================
  21. class TranslationToolComponent : public Component
  22. {
  23. public:
  24. TranslationToolComponent()
  25. : editorOriginal (documentOriginal, nullptr),
  26. editorPre (documentPre, nullptr),
  27. editorPost (documentPost, nullptr),
  28. editorResult (documentResult, nullptr)
  29. {
  30. instructionsLabel.setText (
  31. "This utility converts translation files to/from a format that can be passed to automatic translation tools."
  32. "\n\n"
  33. "First, choose whether to scan the current project for all TRANS() macros, or "
  34. "pick an existing translation file to load:", dontSendNotification);
  35. addAndMakeVisible (instructionsLabel);
  36. label1.setText ("..then copy-and-paste this annotated text into Google Translate or some other translator:", dontSendNotification);
  37. addAndMakeVisible (label1);
  38. label2.setText ("...then, take the translated result and paste it into the box below:", dontSendNotification);
  39. addAndMakeVisible (label2);
  40. label3.setText ("Finally, click the 'Generate' button, and a translation file will be created below. "
  41. "Remember to update its language code at the top!", dontSendNotification);
  42. addAndMakeVisible (label3);
  43. label4.setText ("If you load an existing file the already translated strings will be removed. Ensure this box is empty to create a fresh translation", dontSendNotification);
  44. addAndMakeVisible (label4);
  45. addAndMakeVisible (editorOriginal);
  46. addAndMakeVisible (editorPre);
  47. addAndMakeVisible (editorPost);
  48. addAndMakeVisible (editorResult);
  49. addAndMakeVisible (generateButton);
  50. generateButton.onClick = [this] { generate(); };
  51. addAndMakeVisible (scanProjectButton);
  52. scanProjectButton.onClick = [this] { scanProject(); };
  53. addAndMakeVisible (scanFolderButton);
  54. scanFolderButton.onClick = [this] { scanFolder(); };
  55. addAndMakeVisible (loadTranslationButton);
  56. loadTranslationButton.onClick = [this] { loadFile(); };
  57. }
  58. void paint (Graphics& g) override
  59. {
  60. g.fillAll (findColour (backgroundColourId));
  61. }
  62. void resized() override
  63. {
  64. const int m = 6;
  65. const int textH = 44;
  66. const int extraH = (7 * textH);
  67. const int editorH = (getHeight() - extraH) / 4;
  68. const int numButtons = 3;
  69. Rectangle<int> r (getLocalBounds().withTrimmedBottom (m));
  70. const int buttonWidth = r.getWidth() / numButtons;
  71. instructionsLabel.setBounds (r.removeFromTop (textH * 2).reduced (m));
  72. r.removeFromTop (m);
  73. Rectangle<int> r2 (r.removeFromTop (textH - (2 * m)));
  74. scanProjectButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
  75. scanFolderButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
  76. loadTranslationButton.setBounds (r2.reduced (m, 0));
  77. label1 .setBounds (r.removeFromTop (textH) .reduced (m));
  78. editorPre.setBounds (r.removeFromTop (editorH).reduced (m, 0));
  79. label2 .setBounds (r.removeFromTop (textH) .reduced (m));
  80. editorPost.setBounds (r.removeFromTop (editorH).reduced (m, 0));
  81. r2 = r.removeFromTop (textH);
  82. generateButton.setBounds (r2.removeFromRight (152).reduced (m));
  83. label3 .setBounds (r2.reduced (m));
  84. editorResult .setBounds (r.removeFromTop (editorH).reduced (m, 0));
  85. label4 .setBounds (r.removeFromTop (textH).reduced (m));
  86. editorOriginal.setBounds (r.reduced (m, 0));
  87. }
  88. private:
  89. //==============================================================================
  90. void generate()
  91. {
  92. StringArray preStrings (TranslationHelpers::breakApart (documentPre.getAllContent()));
  93. StringArray postStrings (TranslationHelpers::breakApart (documentPost.getAllContent()));
  94. if (postStrings.size() != preStrings.size())
  95. {
  96. auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
  97. TRANS ("Error"),
  98. TRANS ("The pre- and post-translation text doesn't match!\n\n"
  99. "Perhaps it got mangled by the translator?"));
  100. messageBox = AlertWindow::showScopedAsync (options, nullptr);
  101. return;
  102. }
  103. const LocalisedStrings originalTranslation (documentOriginal.getAllContent(), false);
  104. documentResult.replaceAllContent (TranslationHelpers::createFinishedTranslationFile (preStrings, postStrings, originalTranslation));
  105. }
  106. void scanProject()
  107. {
  108. if (Project* project = ProjucerApplication::getApp().mainWindowList.getFrontmostProject())
  109. {
  110. setPreTranslationText (TranslationHelpers::getPreTranslationText (*project));
  111. }
  112. else
  113. {
  114. auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
  115. "Translation Tool",
  116. "This will only work when you have a project open!");
  117. messageBox = AlertWindow::showScopedAsync (options, nullptr);
  118. }
  119. }
  120. void scanFolder()
  121. {
  122. chooser = std::make_unique<FileChooser> ("Choose the root folder to search for the TRANS macros",
  123. File(), "*");
  124. auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
  125. chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
  126. {
  127. if (fc.getResult() == File{})
  128. return;
  129. StringArray strings;
  130. TranslationHelpers::scanFolderForTranslations (strings, fc.getResult());
  131. setPreTranslationText (TranslationHelpers::mungeStrings(strings));
  132. });
  133. }
  134. void loadFile()
  135. {
  136. chooser = std::make_unique<FileChooser> ("Choose a translation file to load", File(), "*");
  137. auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
  138. chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
  139. {
  140. if (fc.getResult() == File{})
  141. return;
  142. const LocalisedStrings loadedStrings (fc.getResult(), false);
  143. documentOriginal.replaceAllContent (fc.getResult().loadFileAsString().trim());
  144. setPreTranslationText (TranslationHelpers::getPreTranslationText (loadedStrings));
  145. });
  146. }
  147. void setPreTranslationText (const String& text)
  148. {
  149. documentPre.replaceAllContent (text);
  150. editorPre.grabKeyboardFocus();
  151. editorPre.selectAll();
  152. }
  153. //==============================================================================
  154. CodeDocument documentOriginal, documentPre, documentPost, documentResult;
  155. CodeEditorComponent editorOriginal, editorPre, editorPost, editorResult;
  156. Label label1, label2, label3, label4;
  157. Label instructionsLabel;
  158. TextButton generateButton { TRANS("Generate") },
  159. scanProjectButton { "Scan project for TRANS macros" },
  160. scanFolderButton { "Scan folder for TRANS macros" },
  161. loadTranslationButton { "Load existing translation file..."};
  162. std::unique_ptr<FileChooser> chooser;
  163. ScopedMessageBox messageBox;
  164. };