[IFT] Extract lesson content to bundle
authorAlexey Merkulov <alexey.merkulov@jetbrains.com>
Mon, 7 Sep 2020 14:00:44 +0000 (17:00 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Mon, 7 Sep 2020 15:22:54 +0000 (15:22 +0000)
GitOrigin-RevId: 6b675f02a0f777d675898d9e4daf4ac6b9653cc8

49 files changed:
ide-features-trainer/intellij.featuresTrainer.iml
ide-features-trainer/res/messages/LessonsBundle.properties [new file with mode: 0644]
ide-features-trainer/src/training/commands/kotlin/TaskContext.kt
ide-features-trainer/src/training/learn/LessonsBundle.kt [new file with mode: 0644]
ide-features-trainer/src/training/learn/interfaces/Lesson.kt
ide-features-trainer/src/training/learn/lesson/general/CollapseLesson.kt
ide-features-trainer/src/training/learn/lesson/general/CompletionWithTabLesson.kt
ide-features-trainer/src/training/learn/lesson/general/DuplicateLesson.kt
ide-features-trainer/src/training/learn/lesson/general/GotoActionLesson.kt
ide-features-trainer/src/training/learn/lesson/general/MoveLesson.kt
ide-features-trainer/src/training/learn/lesson/general/MultipleSelectionHtmlLesson.kt
ide-features-trainer/src/training/learn/lesson/general/SelectLesson.kt
ide-features-trainer/src/training/learn/lesson/general/SingleLineCommentLesson.kt
ide-features-trainer/src/training/learn/lesson/general/SurroundAndUnwrapLesson.kt
ide-features-trainer/src/training/learn/lesson/general/completion/BasicCompletionLessonBase.kt
ide-features-trainer/src/training/learn/lesson/general/navigation/DeclarationAndUsagesLesson.kt
ide-features-trainer/src/training/learn/lesson/general/navigation/FileStructureLesson.kt
ide-features-trainer/src/training/learn/lesson/general/refactorings/ExtractMethodCocktailSortLesson.kt
ide-features-trainer/src/training/learn/lesson/general/refactorings/ExtractVariableFromBubleLesson.kt
ide-features-trainer/src/training/learn/lesson/general/run/CommonDebugLesson.kt
ide-features-trainer/src/training/learn/lesson/general/run/CommonRunConfigurationLesson.kt
ide-features-trainer/src/training/learn/lesson/java/completion/BasicCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/java/completion/PostfixCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/java/completion/SmartTypeCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/java/completion/StatementCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/java/run/JavaDebugLesson.kt
ide-features-trainer/src/training/learn/lesson/java/run/JavaRunConfigurationLesson.kt
ide-features-trainer/src/training/learn/lesson/kimpl/KLesson.kt
ide-features-trainer/src/training/learn/lesson/kimpl/LessonContext.kt
ide-features-trainer/src/training/learn/lesson/kimpl/LessonExecutorUtil.kt
ide-features-trainer/src/training/learn/lesson/kimpl/LessonUtil.kt
ide-features-trainer/src/training/learn/lesson/python/completion/FStringCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/python/completion/PythonPostfixCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/python/completion/PythonSmartCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/python/completion/PythonTabCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/python/navigation/PythonDeclarationAndUsagesLesson.kt
ide-features-trainer/src/training/learn/lesson/python/navigation/PythonSearchEverywhereLesson.kt
ide-features-trainer/src/training/learn/lesson/python/refactorings/PythonInPlaceRefactoringLesson.kt
ide-features-trainer/src/training/learn/lesson/python/refactorings/PythonQuickFixesRefactoringLesson.kt
ide-features-trainer/src/training/learn/lesson/python/refactorings/PythonRefactorMenuLesson.kt
ide-features-trainer/src/training/learn/lesson/python/refactorings/PythonRenameLesson.kt
ide-features-trainer/src/training/learn/lesson/python/run/PythonDebugLesson.kt
ide-features-trainer/src/training/learn/lesson/python/run/PythonRunConfigurationLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/completion/RubyHippieCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/completion/RubyPostfixCompletionLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/navigation/RubyClassSearchLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/navigation/RubyDeclarationAndUsagesLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/refactorings/RubyRefactorMenuLesson.kt
ide-features-trainer/src/training/learn/lesson/ruby/refactorings/RubyRenameLesson.kt

index 5b4ef3746363c4ecb4378b840de0f15c6baa28af..4bdb7a34ec20d4ebb070e33e8e4bef753fe8b811 100644 (file)
@@ -27,5 +27,6 @@
     <orderEntry type="module" module-name="intellij.javascript.psi.impl" />
     <orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
     <orderEntry type="module" module-name="intellij.platform.statistics" />
+    <orderEntry type="module" module-name="intellij.platform.tasks.impl" />
   </component>
 </module>
\ No newline at end of file
diff --git a/ide-features-trainer/res/messages/LessonsBundle.properties b/ide-features-trainer/res/messages/LessonsBundle.properties
new file mode 100644 (file)
index 0000000..6ae4f58
--- /dev/null
@@ -0,0 +1,315 @@
+### Ths file contains strings from lessons.
+
+###############################################################################
+## Common lessons
+###############################################################################
+
+## Editor basics module
+
+goto.action.lesson.name=Search for actions
+goto.action.mac.workaround=\nIf <strong>Terminal</strong> search opens instead of {0}, refer to \
+  <a href="{1}">this article</a>.
+goto.action.use.find.action=One of the most useful shortcuts is {0}. It lets you search through all available \
+  actions without having to know their individual shortcuts. Try it now with {1}.
+goto.action.about.word=about
+goto.action.invoke.about.action=Let''s say you want to learn about the IDE, type {0} and press {1}.
+goto.action.to.return.to.the.editor=Hit {0} to return to the editor.
+goto.action.invoke.again={0} can also be used to change the settings, invoke it again now.
+goto.action.show.line.input.required=show line
+goto.action.show.line.numbers.request=Type {0} to see {1} selector.
+goto.action.first.lines.toggle=Switch the line numbers {0, choice, 0#off|1#on}.
+goto.action.second.lines.toggle=Now switch the line numbers back {0, choice, 0#on|1#off}.
+goto.action.propose.to.go.next=Awesome! Click the button below to start the next lesson, or use {0}.
+
+collapse.lesson.name=Collapse
+collapse.try.collapse=Sometimes you need to collapse a piece of code for better readability. Try to collapse a code fragment with {0}.
+collapse.hit.expand=To expand a code region, hit {0}.
+collapse.all.collapse=If you want to collapse all regions in the file, use {0}.
+collapse.all.expand=Similarly, press {0} to expand all available regions.
+
+duplicate.and.delete.lines.lesson.name=Duplicate and delete lines
+duplicate.and.delete.lines.duplicate.line=Duplicate any line with {0}.
+duplicate.and.delete.lines.duplicate.several.lines=You can do the same action with multiple lines, too. \
+  Simply select two or more lines and duplicate them with {0}.
+duplicate.and.delete.lines.delete.line=To delete the current line you can use action {0}.
+
+move.lesson.name=Move
+move.pull.down=Rearranging lines usually takes two actions: cut and paste. \
+  In this IDE, you can do it with just one. Press {0} to pull down the current line.
+move.pull.up=Similarly, to pull a line up, use {0}.
+move.whole.method.up=Now try to move the whole method up with {0}.
+move.whole.method.down=And move it down with {0}.
+
+multiple.selections.lesson.name=Multiple selections
+multiple.selections.select.symbol=Press {0} to select the symbol at the caret.
+multiple.selections.select.next.symbol=Press {0} again to select the next occurrence of this symbol.
+multiple.selections.deselect.symbol=Press {0} to deselect the last occurrence.
+multiple.selections.select.all=Press {0} to select all occurrences in the file.
+multiple.selections.replace=Type {0} to replace all occurrences of {1} with {0}.
+
+selection.lesson.name=Expand and shrink the code selection
+selection.select.word=Place the caret before any word. Press {0} to move the caret to the next word and select everything in between.
+selection.extend.selection=Press {0} to extend the selection to the next code block.
+selection.extend.until.whole.file=Try to increase your selection with {0} until your whole file is selected.
+selection.shrink.selection={0} shrinks selection. Try pressing it.
+
+comment.line.lesson.name=Comment line
+comment.line.comment.any.line=Comment out any line with {0}.
+comment.line.uncomment.that.line=Uncomment the commented line with the same shortcut, {0}.
+comment.line.comment.several.lines=Select several lines and then comment them with {0}.
+
+surround.and.unwrap.lesson.name=Surround and unwrap
+surround.and.unwrap.invoke.surround=Press {0} to surround the selected code fragment with some template code.
+surround.and.unwrap.choose.surround.item=Choose {0} item.
+surround.and.unwrap.invoke.unwrap=Let''s return to the initial file with unwrapping action by {0}.
+surround.and.unwrap.item=Unwrap {0}
+surround.and.unwrap.choose.unwrap.item=Choose {0} item.
+
+## Completion module
+
+basic.completion.lesson.name=Basic completion
+basic.completion.start.typing=By default, the IDE proposes completion for your code instantly. \
+  Start typing {0} right where the caret is, and you will see the Lookup Menu with matching suggestions.
+basic.completion.continue.typing=Continue typing {0} unless it becomes the first item.
+basic.completion.just.press.to.complete=Now just press {0} to complete this statement.
+basic.completion.activate.explicitly=To activate Basic Completion explicitly, press {0}.
+basic.completion.finish.explicit.completion=Select {0} and press {1}.
+
+completion.with.tab.lesson.name=Completion with tab
+completion.with.tab.begin.completion=Press {0} to show completion options.
+completion.with.tab.finish.with.tab=Choose {0}, for example, and press {1}. \
+  This overwrites the word at the caret rather than simply inserting it.
+
+postfix.completion.lesson.name=Postfix completion
+postfix.completion.type.template=The IDE can offer postfix shortcuts. Type {0}.
+
+rename.lesson.name=Rename
+
+refactoring.menu.lesson.name=Refactoring menu
+
+## Navigation module
+
+declaration.and.usages.lesson.name=Declaration and usages
+declaration.and.usages.jump.to.declaration=Use {0} to jump to the declaration of {1, choice, 0#a method|1#an attribute accessor}.
+declaration.and.usages.show.usages=Now the caret is at the {0, choice, 0#method|1#attribute accessor} declaration. \
+  Use the same shortcut {1} to see all of its usages, then select one of them.
+declaration.and.usages.find.usages=Use {0} to see a more detailed view of usages. You can invoke {0} on either a declaration or usage.
+declaration.and.usages.pin.motivation=From the {0} view you can navigate to both usages and declarations. \
+  The next search will override these results in the {0} view. To prevent it, pin the results:
+declaration.and.usages.tab.name=Usages of
+declaration.and.usages.right.click.tab=Right click the tab title, {0}.
+declaration.and.usages.select.pin.item=Select {0}.
+declaration.and.usages.hide.view=When you have finished browsing usages, use {0} to hide the view.
+declaration.and.usages.open.find.view=Press {0} to open the {1} view again.
+
+file.structure.lesson.name=File structure
+file.structure.open.popup=A large source file can be difficult to read and navigate, sometimes you only need an overview of the file. \
+  Use {0} to see the file structure.
+file.structure.request.prefixes=Suppose you want to find some method with {0} and {1} words in its name. \
+  Type {2} (prefixes of required words) to filter file structure.
+file.structure.navigate=Only one item remains. Now press {0} to jump to the selected item.
+file.structure.toolwindow=The IDE can also show you the file structure as a tool window. Open it with {0}.
+
+extract.method.lesson.name=Extract method
+extract.method.invoke.action=Press {0} to extract the selected code block into a method.
+extract.method.start.refactoring=Click {0} to start refactoring.
+extract.method.confirm.several.replaces=Cocktail Sort has two swap places. The first fragment has just been extracted. \
+  Click {0} to extract both of them.
+extract.method.second.fragment=Now you can confirm or reject the replacement of the second fragment.
+
+extract.variable.lesson.name=Extract variable
+extract.variable.start.refactoring=Press {0} to extract a local variable from the selected expression {1}.
+extract.variable.replace.all=This code block contains 3 occurrences of the selected expression. \
+  Choose the second item on the list to replace all of them.
+extract.variable.choose.name=Choose a name for the new variable or leave it as it is. Press {0} to complete the refactoring.
+
+## Run/Debug module
+
+run.configuration.lesson.name=Run configuration
+run.configuration.hide.toolwindow=IDE automatically opened {0} tool window. \
+  Just a tip, at the top of {0} tool window you can see the full runned command. Now let\u2019s hide the tool window with {1}.
+run.configuration.temporary.to.permanent=For each new run IDE create temporary run configuration. \
+  Temporary configurations are automatically deleted if the default limit of 5 is reached. \
+  Lets convert temporary configuration into permanent one. Open the drop-down menu with run configuration.
+run.configuration.select.save.configuration=Select {0}.
+run.configuration.edit.configuration=Suppose you want to change configuration or create another one manually. \
+  Then you need to open the the drop-down menu again and click {0}. Alternatively you can use {1} action.
+run.configuration.settings.description=This is a place for managing run/debug configurations. \
+  You can set here program parameters, JVM arguments, environment variables and so on.
+run.configuration.tip.about.save.configuration.into.file=Just a tip. Sometimes you may want to save configuration to its own file. \
+  Such configurations will be easy to share between colleagues (usually by version control system). \
+  Now close the settings dialog to finish this lesson.
+
+debug.workflow.lesson.name=Debug workflow
+debug.workflow.incorrect.breakpoints=Breakpoints are set incorrect for this lesson.
+debug.workflow.run.program=Before debugging let''s run the program and see what is going wrong {0}.
+debug.workflow.toggle.breakpoint=So there is a problem. Let''s start investigation with placing breakpoint. \
+  To toggle a breakpoint you should click left editor gutter or just press {0}.
+debug.workflow.start.debug=To start debug selected run configuration you need to click at {0}. Or you can just press {1}.
+debug.workflow.return.to.editor=Many trace actions will focus debug toolwindow. Let''s return to the editor with {0}.
+debug.workflow.use.watches=IDE has several ways to show values. For this step, we selected the call. Lets add it to {0}. \
+  You can copy the expression into clipboard, use {1} button on the debug panel and paste copied text. Or you can just use action {2} {3}.
+debug.workflow.consider.to.add.a.shortcut=(consider to add a shortcut for it later)
+debug.workflow.step.into=Lets step into. You can use action {0} or the button {1} at the debug panel.
+debug.workflow.choose.method.to.step.in=In most cases you will want to skip argument calculating so Smart Step Into feature suggest by \
+  default the wrapping method. But here we need to choose the second one: {0}. \
+  You can choose it by keyboard {1} and press {2} or you can click the call by mouse.
+debug.workflow.quick.evaluate=Lets see what we are going to pass to {0}. We selected the argument. Invoke Quick Evaluate Expression {1}.
+debug.workflow.fix.error=Oh, we made a mistake in the array index! Lets fix it right now. Close popup ({0}) and change 0 to 1.
+debug.workflow.step.over=Let''s check that the call of {0} will not throw an exception now. Use Step Over action {1} or click the button {2}.
+debug.workflow.mute.breakpoints=Ups, same breakpoint again. Now we don''t need to stop at this breakpoint. \
+  So let''s mute breakpoints by the button {0} or by action {1}.
+debug.workflow.run.to.cursor=Let''s check the result of {0}. We''ve moved the editor cursor to the {1} statement. \
+  Let''s use {2} or click {3}. Note that {4} works even if breakpoints are muted.
+debug.workflow.evaluate.expression=It seems the {0} is not an average we want to find. We forgot to divide by length. \
+  Seems we need to return {1}. Let''s check the guess. Press {2} or click button {3}.
+debug.workflow.type.result=Type {0} into the {1} field, completion works.
+debug.workflow.evaluate.it=Hit {0} or click {1}.
+debug.workflow.stop.debug=It will be a correct answer! Lets close the dialog and stop debugging by {0} or button {1}.
+debug.workflow.resume=Seems no exceptions by now. Let''s continue execution with {0} or click the button {1}.
+
+###############################################################################
+## Java lessons
+###############################################################################
+
+java.basic.completion.choose.first=You can choose the first item from the Lookup menu by pressing {0}.
+java.basic.completion.activate=To activate Basic Completion, press {0} and you will see lookup menu again.
+java.basic.completion.choose.item=Select {0} inside the lookup menu and press {1}.
+java.basic.completion.complete=Press {0} to complete this statement.
+java.basic.completion.deeper.level=Sometimes you need to see suggestions for static constants or methods. \
+  Press {0} twice to access a deeper level of Code Completion.
+
+java.run.configuration.lets.run=Any code marked with {0} can be run. Let''s run our simple example with {1}. \
+  Alternatively you can click at {0} and select {2} item.
+
+java.postfix.completion.apply=Postfix Completion helps reduce backward caret jumps as you write code. \
+  It lets you transform an already typed expression into another one based on the postfix you add, the type of expression, and its context. \
+  Type {0} after the parenthesis to see the list of postfix completion suggestions. \
+  Select {1} from the list, or type it in editor, and then press {2} to complete the statement.
+
+java.smart.type.completion.lesson.name=Smart type completion
+java.smart.type.completion.apply=Smart Type Completion filters the list of suggestion to include only those types that are applicable in \
+  the current context. Press {0} to see the list of matching suggestions. Choose the first one by pressing {1}.
+java.smart.type.completion.return=Smart Type Completion can also suggest code for a return statement. \
+  Press {0} twice to see the Lookup menu for a return. Choose the first one by pressing {1}
+
+java.statement.completion.lesson.name=Statement completion
+java.statement.completion.complete.for=Press {0} to complete the {1} statement.
+java.statement.completion.complete.if=Write {0} and press {1} to generate the statement.
+java.statement.completion.complete.condition=Add a condition inside parentheses {0} and press {1} to jump inside the {2} statement.
+java.statement.completion.complete.finish.body=Write on one line: {0} and then press {1} to complete the entered statement and apply formatting.
+
+java.debug.workflow.rebuild=For big programs rerun can take too much time. When you find some mistake in pure method you can just rebuild \
+  the project and apply <strong>Hot Swap</strong> JVM feature. Let''s build project: {0} or {1}.
+java.debug.workflow.confirm.hot.swap=Confirm <strong>Hot Swap</strong> replacement
+java.debug.workflow.drop.frame=We patched our method, but right now we are still executing old obsolete {0} and it will throw the \
+  exception again. Let''s drop the frame and return to the state before {1} call. Click {2} at the debug panel or use {3}.
+
+###############################################################################
+## Python lessons
+###############################################################################
+
+python.f.string.completion.lesson.name=F-string completion
+python.f.string.completion.type.prefix=PyCharm supports automatic f-string conversion. Just start to type {0}
+python.f.string.completion.invoke.manually=You can invoke completion manually with {0}
+python.f.string.completion.complete.it=Complete the statement with {0}. Just press {1} to apply the first item.
+python.f.string.completion.result.message=You may see that simple Python string was replaced by f-string after the completion.
+
+python.postfix.completion.select.item=Select {0} item from completion list.
+
+python.smart.completion.lesson.name=Smart completion
+python.smart.completion.try.basic.completion=Try to use Basic Completion by pressing {0}.
+python.smart.completion.use.smart.completion=Unfortunately IDE has no direct information about {0} type. \
+  But sometimes it can guess completion by the context! Use {1} to invoke Smart Completion.
+python.smart.completion.finish.completion=Now just choose {0} item to finish this lesson.
+
+python.tab.completion.lesson.name=Tab completion
+python.tab.completion.start.completion=Suppose you want to replace {0} with {1}. Invoke completion by pressing {2}.
+python.tab.completion.select.item=Select item {0} by keyboard arrows or just start typing it.
+python.tab.completion.use.tab.completion=If you press {0} you will insert {1} before {2}. Instead press {3} to replace {2} with {1}.
+
+python.search.everywhere.lesson.name=Search everywhere
+python.search.everywhere.invoke.search.everywhere=To open {0} you need to press {1} two times in a row.
+python.search.everywhere.type.prefixes=Suppose you are looking for a class with {0} and {1} words in the name. \
+  Type {2} (prefixes of required words) to the search field.
+python.search.everywhere.preview=We found {0}. Now you can preview the found item. Just select it by keyboard arrows and press {1}.
+python.search.everywhere.navigate.to.class=Press {0} to navigate to {1}.
+python.search.everywhere.goto.class=Use {0} to find class faster or in special places.
+python.search.everywhere.type.node.visitor=Suppose you need some library class responsible for visiting nodes. Type {0}.
+python.search.everywhere.use.all.places=Now you see a class inside this demo project. \
+  Lets Switch {0} filter to {1} and you will see available library variants.
+python.search.everywhere.quick.documentation=Use {0} to quickly look at available documentation.
+python.search.everywhere.finish=<strong>Done!</strong> In the same way you can use {0} to look for a method or global variable and use {1} \
+  to look for a file.
+
+python.in.place.refactoring.lesson.name=In-place refactoring
+python.in.place.refactoring.start.type.new.name=Let''s consider an alternative approach to performing refactorings. \
+  Suppose we want to rename local variable {0} to {1}. Just start typing the new name.
+python.in.place.refactoring.invoke.intentions=IDE is guessing that you are going to rename the variable. \
+  You can notice it by the icon {0} in the left editor gutter. Invoke intentions by {1} when you finish to type the new name.
+python.in.place.refactoring.finish.rename=Press {0} to finish rename.
+python.in.place.refactoring.add.parameter=Let''s add an argument to this method. We place the editor caret just after the first parameter. \
+  Now type comma and parameter''s name: {0}.
+python.in.place.refactoring.invoke.intention.for.parameter=IDE is guessing that you are going to change the method signature. \
+  You can notice it by the same icon {0} at the left editor gutter. Invoke intentions by {1} when you finish typing the new parameter.
+python.in.place.refactoring.update.callers=Press {0} to update the callers.
+python.in.place.refactoring.signature.preview=IDE is showing you the short signature preview. Press {0} to continue.
+python.in.place.refactoring.set.default.value=Now you need to type the value which will be inserted as an argument into the each call. \
+  You can choose {0} for this sample. Then press {1} to continue.
+python.in.place.refactoring.remark.about.application.scope=A small note for the end. In-place refactoring may be applied only in the \
+  definition point whiles direct invocation of rename or change-signature refactorings may be called from both definition and usage.
+
+python.quick.fix.refactoring.lesson.name=Quick fix refactoring
+python.quick.fix.refactoring.type.new.argument=Several refactorings can be performed as quick fixes. \
+  Suppose we want to add a parameter to the method {0} and pass the variable {1} to it. Let''s type {2} after the first argument.
+python.quick.fix.refactoring.wait.completion.showed=Wait a little bit for the completion list...
+python.quick.fix.refactoring.close.completion.list=For now, we don''t want to apply any completion. Close the list ({0}).
+python.quick.fix.refactoring.invoke.intentions=As you may notice, IDE is showing you a warning here. Let''s invoke intentions by {0}.
+python.quick.fix.refactoring.choose.change.signature=Choose {0} quick fix.
+python.quick.fix.refactoring.select.new.parameter=Let''s set the default value for the new parameter. Click at the new parameter line. \
+  Alternatively, you can set focus to the parameter without mouse by {0} and then {1}.
+python.quick.fix.refactoring.set.default.value=You may navigate through the fields (and the checkbox) by using {0}. With the checkbox you can let IDE inline the default value to the other callers or set it as the default value for the new parameter. The Signature Preview will help understand the difference. Now set the default value as 0.
+python.quick.fix.refactoring.finish.refactoring=Press {0} (or click {1} to finish the refactoring.
+
+python.refactoring.menu.show.refactoring.list=PyCharm supports a variety of refactorings. Many of them have own shortcuts. But for rare refactorings you can use {0} and see a partial list of them.
+python.refactoring.menu.required.prefix=pa
+python.refactoring.menu.introduce.parameter=Suppose we want to replace this expression with a parameter. So we need to choose {0}. Now simply type {1} (as prefix of needed refactoring name) to reduce proposed list.
+python.refactoring.menu.start.refactoring=Press {0} to start Introduce Parameter refactoring.
+python.refactoring.menu.finish.refactoring=To complete refactoring, you need to choose some name or leave it as default and press {0}.
+
+python.rename.press.rename=Press {0} to rename field {1} (e.g., to {2}).
+python.rename.expand.dynamic.references=In simple cases PyCharm will just rename without confirmation. But in this sample PyCharm sees two calls of  {0} method for objects with unknown type. Expand {1} item.
+#TODO: This prefix can be got from some general bundle when "Dynamic" will be extracted from `BaseRefactoringProcessor.createPresentation`
+python.rename.dynamic.references.prefix=Dynamic references
+python.rename.exclude.item=It seems {0} should be excluded from rename. Select it and press {1}.
+python.rename.finish.refactoring=Now just finish the rename with the {0} button.
+
+python.run.configuration.lets.run=Let''s run our simple example with {0}.
+
+python.debug.workflow.rerun=Let''s rerun our program. Just click again at {0} or use {1}.
+
+###############################################################################
+## Ruby lessons
+###############################################################################
+
+ruby.hippie.completion.lesson.name=Hippie Completion
+ruby.hippie.completion.invoke.hippie.completion=Sometimes you need to complete a word by textual similarity. Press {0} to invoke hippie completion.
+ruby.hippie.completion.repeat.one.time=You can repeat {0} until the desired word is found. Try it once more now.
+ruby.hippie.completion.return.previous=You can return to the previous variant with {0}. Use it now.
+
+ruby.postfix.completion.apply=Now just press {0} to choose the first postfix template.
+ruby.postfix.completion.choose.target=Now choose the second item, {0}.
+
+ruby.class.search.lesson.name=Class Search
+ruby.class.search.invoke.goto.class=Try to find a class with {0}
+ruby.class.search.type.word=Type {0} to see classes that contain the word {1}.
+ruby.class.search.type.second.prefix=You can search for a class by part of its name. Type {0} (the search string will be {1}) to see classes that contain the words {2} and {3}.
+ruby.class.search.preview=To check the selected class before navigating to it, you can use {0} to see its quick definition.
+ruby.class.search.navigate.to.target=Suppose you are looking for {0}.Choose it and then press {1} to navigate.
+
+ruby.refactoring.menu.invoke.refactoring.list=RubyMine supports a variety of refactorings. Press {0} to see a partial list of them.
+ruby.refactoring.menu.use.push.method.down=Some refactorings are seldom used and have no shortcut, but you can find them here. Choose {0} now and complete the refactoring on {1}.
+
+ruby.rename.start.refactoring=Press {0} to rename the attribute accessor {1} (e.g., to {2}).
+ruby.rename.confirm=In order to be confident about the refactoring, RubyMine lets you preview it before confirming.Click {0} to complete the refactoring.
+
index 9c64ff6137149547cd4e425be189ffc778da6da1..2ee5e7fbefcba496a5e51214f4e4dee36b268579 100644 (file)
@@ -7,6 +7,7 @@ import com.intellij.ui.tree.TreeVisitor
 import com.intellij.util.ui.tree.TreeUtil
 import org.fest.swing.timing.Timeout
 import org.intellij.lang.annotations.Language
+import org.jetbrains.annotations.Nls
 import training.learn.LearnBundle
 import training.learn.lesson.kimpl.LessonUtil
 import training.ui.LearningUiHighlightingManager
@@ -42,10 +43,10 @@ abstract class TaskContext {
   /**
    * Write a text to the learn panel (panel with a learning tasks).
    */
-  abstract fun text(@Language("HTML") text: String)
+  abstract fun text(@Language("HTML") @Nls text: String)
 
   /** Write a text to the learn panel (panel with a learning tasks). */
-  abstract fun runtimeText(callback: TaskRuntimeContext.() -> String?)
+  abstract fun runtimeText(@Nls callback: TaskRuntimeContext.() -> String?)
 
   /** Simply wait until an user perform particular action */
   abstract fun trigger(actionId: String)
@@ -182,6 +183,11 @@ abstract class TaskContext {
     return "<code>${StringUtil.escapeXmlEntities(sourceSample)}</code>"
   }
 
+  /** Highlight some [text] */
+  open fun strong(text: String): String  {
+    return "<strong>${StringUtil.escapeXmlEntities(text)}</strong>"
+  }
+
   /** Show an [icon] inside lesson step message */
   open fun icon(icon: Icon): String  {
     val index = LearningUiManager.getIconIndex(icon)
diff --git a/ide-features-trainer/src/training/learn/LessonsBundle.kt b/ide-features-trainer/src/training/learn/LessonsBundle.kt
new file mode 100644 (file)
index 0000000..14844af
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package training.learn
+
+import com.intellij.DynamicBundle
+import org.jetbrains.annotations.Nls
+import org.jetbrains.annotations.NonNls
+import org.jetbrains.annotations.PropertyKey
+
+@NonNls
+private const val BUNDLE = "messages.LessonsBundle"
+
+object LessonsBundle : DynamicBundle(BUNDLE) {
+  @Nls
+  fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): String = getMessage(key, *params)
+
+  @Nls
+  fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String,
+                     vararg params: Any): java.util.function.Supplier<String> = getLazyMessage(key, *params)
+}
\ No newline at end of file
index 414382fc0d233b824e15fe6ad649fe5efde63d62..08d076ac49af86a1fff6b47bbd1e73b0ba267a96 100644 (file)
@@ -1,6 +1,8 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.interfaces
 
+import org.jetbrains.annotations.Nls
+import org.jetbrains.annotations.NonNls
 import training.commands.kotlin.TaskTestContext
 import training.learn.lesson.LessonListener
 import training.learn.lesson.LessonState
@@ -9,9 +11,9 @@ import training.util.findLanguageByID
 
 interface Lesson {
 
-  val name: String
+  val name: @Nls String
 
-  val id: String
+  val id: @NonNls String
 
   /** This name will be used for generated file with lesson sample */
   val fileName: String
index 073a7e324067fbb520760a70e4f98f9f8768cb43..4e8f4849dac8aef5b50540995a795c0204931162 100644 (file)
@@ -1,28 +1,29 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 class CollapseLesson(module: Module, lang: String, private val sample: LessonSample)
-  : KLesson("Collapse", "Collapse", module, lang) {
+  : KLesson("Collapse", LessonsBundle.message("collapse.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
 
       actionTask("CollapseRegion") {
-        "Sometimes you need to collapse a piece of code for better readability. Try to collapse a code fragment with ${action(it)}."
+        LessonsBundle.message("collapse.try.collapse", action(it))
       }
       actionTask("ExpandRegion") {
-        "To expand a code region, hit ${action(it)}."
+        LessonsBundle.message("collapse.hit.expand", action(it))
       }
       actionTask("CollapseAllRegions") {
-        "If you want to collapse all regions in the file, use ${action(it)}."
+        LessonsBundle.message("collapse.all.collapse", action(it))
       }
       actionTask("ExpandAllRegions") {
-        "Similarly, press ${action(it)} to expand all available regions."
+        LessonsBundle.message("collapse.all.expand", action(it))
       }
     }
 }
\ No newline at end of file
index bdd29c3a14f7c2b148c727cc4aedfa4d851cd9c2..25be6ffc2e66a56ab88728fed923f3c546babfc0 100644 (file)
@@ -1,13 +1,14 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 abstract class CompletionWithTabLesson(module: Module, lang: String, private val proposal: String) :
-  KLesson("Completion with tab", "Completion with tab", module, lang) {
+  KLesson("Completion with tab", LessonsBundle.message("completion.with.tab.lesson.name"), module, lang) {
 
   abstract val sample: LessonSample
 
@@ -15,11 +16,10 @@ abstract class CompletionWithTabLesson(module: Module, lang: String, private val
     get() = {
       prepareSample(sample)
 
-      actionTask("CodeCompletion") { "Press ${action(it)} to show completion options." }
+      actionTask("CodeCompletion") { LessonsBundle.message("completion.with.tab.begin.completion", action(it)) }
 
       actionTask("EditorChooseLookupItemReplace") {
-        "Choose <code>$proposal</code>, for example, and press ${action("EditorTab")}. " +
-        "This overwrites the word at the caret rather than simply inserting it."
+        LessonsBundle.message("completion.with.tab.finish.with.tab", code(proposal), action("EditorTab"))
       }
     }
 }
index 76015bbc71de86f284e624af77eacfb9ae83fc09..dd776eba02654ec99aab68946e3cdeb5214f2e94 100644 (file)
@@ -1,21 +1,23 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 class DuplicateLesson(module: Module, lang: String, private val sample: LessonSample) :
-  KLesson("Duplicate", "Duplicate and delete lines", module, lang) {
+  KLesson("Duplicate", LessonsBundle.message("duplicate.and.delete.lines.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
 
-      actionTask("EditorDuplicate") { "Duplicate any line with ${action(it)}." }
+      actionTask("EditorDuplicate") { LessonsBundle.message("duplicate.and.delete.lines.duplicate.line", action(it)) }
 
       task("EditorDuplicate") {
-        text("You can do the same action with multiple lines, too. Simply select two or more lines and duplicate them with ${action(it)}.")
+        text(
+          LessonsBundle.message("duplicate.and.delete.lines.duplicate.several.lines", action(it)))
         trigger(it, {
           val selection = editor.selectionModel
           val start = selection.selectionStartPosition?.line ?: 0
@@ -25,7 +27,7 @@ class DuplicateLesson(module: Module, lang: String, private val sample: LessonSa
         test { actions("EditorUp", "EditorLineStart", "EditorDownWithSelection", "EditorDownWithSelection", it) }
       }
       actionTask("EditorDeleteLine") {
-        "To delete the current line you can use action ${action(it)}."
+        LessonsBundle.message("duplicate.and.delete.lines.delete.line", action(it))
       }
     }
 }
\ No newline at end of file
index a8c6a2b317bc95f4e4aa060f5912c54b8ea238f4..1f82f076618c036998c84d87a514162aefd5d326 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general
 
+import com.intellij.ide.IdeBundle
 import com.intellij.ide.actions.AboutPopup
 import com.intellij.ide.actions.searcheverywhere.SearchEverywhereUIBase
 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
@@ -9,16 +10,16 @@ import com.intellij.openapi.util.SystemInfo
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.impl.jList
 import com.intellij.testGuiFramework.util.Key
-import com.intellij.ui.components.fields.ExtendableTextField
-import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
+import training.learn.lesson.kimpl.LessonUtil
 import javax.swing.JPanel
 
 class GotoActionLesson(module: Module, lang: String, private val sample: LessonSample) :
-  KLesson("Actions", "Search for actions", module, lang) {
+  KLesson("Actions", LessonsBundle.message("goto.action.lesson.name"), module, lang) {
 
   companion object {
     private const val FIND_ACTION_WORKAROUND: String = "https://intellij-support.jetbrains.com/hc/en-us/articles/360005137400-Cmd-Shift-A-hotkey-opens-Terminal-with-apropos-search-instead-of-the-Find-Action-dialog"
@@ -28,16 +29,17 @@ class GotoActionLesson(module: Module, lang: String, private val sample: LessonS
     get() = {
       prepareSample(sample)
       actionTask("GotoAction") {
-        "One of the most useful shortcuts is <strong>Find Action</strong>. It lets you search through all available actions " +
-                "without having to know their individual shortcuts.\nTry it now with ${action(it)}." +
-                if (SystemInfo.isMacOSMojave) "\nIf <strong>Terminal</strong> search opens instead of <strong>Find Action</strong>, " +
-                        "refer to <a href=\"$FIND_ACTION_WORKAROUND\">this article</a>." else ""
+        val macOsWorkaround = if (SystemInfo.isMacOSMojave)
+          LessonsBundle.message("goto.action.mac.workaround", LessonUtil.actionName(it), FIND_ACTION_WORKAROUND)
+        else ""
+        LessonsBundle.message("goto.action.use.find.action", LessonUtil.actionName(it), action(it)) + macOsWorkaround
       }
       actionTask("About") {
-        "Let's say you want to learn about the IDE, type <strong>about</strong> and press ${action("EditorEnter")}."
+        LessonsBundle.message("goto.action.invoke.about.action",
+                              strong(LessonsBundle.message("goto.action.about.word")), LessonUtil.rawEnter())
       }
       task {
-        text("Hit ${action("EditorEscape")} to return to the editor.")
+        text(LessonsBundle.message("goto.action.to.return.to.the.editor", action("EditorEscape")))
         var aboutHasBeenFocused = false
         stateCheck {
           aboutHasBeenFocused = aboutHasBeenFocused || focusOwner is AboutPopup.PopupPanel
@@ -55,12 +57,13 @@ class GotoActionLesson(module: Module, lang: String, private val sample: LessonS
         }
       }
       actionTask("GotoAction") {
-        "${action(it)} can also be used to change the settings, invoke it again now."
+        LessonsBundle.message("goto.action.invoke.again", code("Ran"), action(it))
       }
-      task("show line") {
-        text("Type <strong>$it</strong> to see <strong>Show Line Number</strong> selector.")
+      val showLineNumbersName = IdeBundle.message("label.show.line.numbers")
+      task(LessonsBundle.message("goto.action.show.line.input.required")) {
+        text(LessonsBundle.message("goto.action.show.line.numbers.request", strong(it), strong(showLineNumbersName)))
         triggerByListItemAndHighlight { item ->
-          item.toString().contains("Show Line Numbers")
+          item.toString().contains(showLineNumbersName)
         }
         test {
           waitComponent(SearchEverywhereUIBase::class.java)
@@ -70,32 +73,29 @@ class GotoActionLesson(module: Module, lang: String, private val sample: LessonS
 
       val lineNumbersShown = isLineNumbersShown()
       task {
-        text("Switch the line numbers ${if (lineNumbersShown) "off" else "on"}.")
+        text(LessonsBundle.message("goto.action.first.lines.toggle", if (lineNumbersShown) 0 else 1))
         stateCheck { isLineNumbersShown() == !lineNumbersShown }
         restoreByUi()
         test {
           ideFrame {
-            jList("Show Line Numbers").item("Show Line Numbers").click()
+            jList(showLineNumbersName).item(showLineNumbersName).click()
           }
         }
       }
       task {
-        text("Now switch the line numbers back ${if (lineNumbersShown) "on" else "off"}.")
+        text(LessonsBundle.message("goto.action.second.lines.toggle", if (lineNumbersShown) 0 else 1))
         stateCheck { isLineNumbersShown() == lineNumbersShown }
         test {
           ideFrame {
-            jList("Show Line Numbers").item("Show Line Numbers").click()
+            jList(showLineNumbersName).item(showLineNumbersName).click()
           }
         }
       }
 
       task {
-        text("Awesome! Click the button below to start the next lesson, or use ${action("learn.next.lesson")}.")
+        text(LessonsBundle.message("goto.action.propose.to.go.next", action("learn.next.lesson")))
       }
     }
 
   private fun isLineNumbersShown() = EditorSettingsExternalizable.getInstance().isLineNumbersShown
-
-  private fun TaskRuntimeContext.checkWordInSearch(expected: String): Boolean =
-    (focusOwner as? ExtendableTextField)?.text?.toLowerCase()?.contains(expected.toLowerCase()) == true
 }
\ No newline at end of file
index 0e20f1c1daaaede320aace4265fe82fabde9f0e7..06cc7b1c7bf85def044c420cd4d77a3d8ec9c025 100644 (file)
@@ -2,34 +2,34 @@
 package training.learn.lesson.general
 
 import training.lang.JavaLangSupport
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 class MoveLesson(module: Module, lang: String, private val sample: LessonSample)
-  : KLesson("Move", "Move", module, lang) {
+  : KLesson("Move", LessonsBundle.message("move.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
 
       actionTask("MoveLineDown") {
-        "Rearranging lines usually takes two actions: cut and paste. In this IDE, you can do it with just one. " +
-        "Press ${action(it)} to pull down the current line."
+        LessonsBundle.message("move.pull.down", action(it))
       }
       actionTask("MoveLineUp") {
-        "Similarly, to pull a line up, use ${action(it)}."
+        LessonsBundle.message("move.pull.up", action(it))
       }
       if (lang == JavaLangSupport.lang) caret(9, 5)
       task("MoveStatementUp") {
-        text("Now try to move the whole method up with ${action(it)}.")
+        text(LessonsBundle.message("move.whole.method.up", action(it)))
         trigger(it, { editor.document.text }, { before, now ->
           checkSwapMoreThan2Lines(before, now)
         })
         test { actions("EditorUp", it) }
       }
       actionTask("MoveStatementDown") {
-        "And move it down with ${action(it)}."
+        LessonsBundle.message("move.whole.method.down", action(it))
       }
     }
 }
@@ -57,7 +57,7 @@ private fun <T> commonPrefix(list1: List<T>, list2: List<T>): Int {
 private fun <T> commonSuffix(list1: List<T>, list2: List<T>): Int {
   val size = Integer.min(list1.size, list2.size)
   for (i in 0 until size) {
-    if (list1[list1.size - i - 1] != list2[list2.size - i -1])
+    if (list1[list1.size - i - 1] != list2[list2.size - i - 1])
       return i
   }
   return size
index 32c1a362d274a9c698b06b581e8d6e5a2eeb736f..f535fd685c44b0b6156c2fafc92a682da2a45dd1 100644 (file)
@@ -5,13 +5,14 @@ import com.intellij.psi.PsiDocumentManager
 import com.intellij.psi.html.HtmlTag
 import com.intellij.psi.util.PsiTreeUtil
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class MultipleSelectionHtmlLesson(module: Module)
-  : KLesson("Multiple selections", "Multiple selections", module, "HTML") {
+  : KLesson("Multiple selections", LessonsBundle.message("multiple.selections.lesson.name"), module, "HTML") {
   private val sample = parseLessonSample("""<!doctype html>
 <html lang="en">
     <head>
@@ -40,19 +41,19 @@ class MultipleSelectionHtmlLesson(module: Module)
       prepareSample(sample)
 
       actionTask("SelectNextOccurrence") {
-        "Press ${action(it)} to select the symbol at the caret."
+        LessonsBundle.message("multiple.selections.select.symbol", action(it))
       }
       actionTask("SelectNextOccurrence") {
-        "Press ${action(it)} again to select the next occurrence of this symbol."
+        LessonsBundle.message("multiple.selections.select.next.symbol", action(it))
       }
       actionTask("UnselectPreviousOccurrence") {
-        "Press ${action(it)} to deselect the last occurrence."
+        LessonsBundle.message("multiple.selections.deselect.symbol", action(it))
       }
       actionTask("SelectAllOccurrences") {
-        "Press ${action(it)} to select all occurrences in the file."
+        LessonsBundle.message("multiple.selections.select.all", action(it))
       }
       task {
-        text("Type <code>td</code> to replace all occurrences of <code>th</code> with <code>td</code>.")
+        text(LessonsBundle.message("multiple.selections.replace", code("td"), code("th")))
         stateCheck { checkMultiChange() }
         test { type("td") }
       }
index b1413373a73d6b581293e71a1220567a181a3019..895c66f591620d1335c1d739b178a939578550d6 100644 (file)
@@ -1,25 +1,26 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 class SelectLesson(module: Module, lang: String, private val sample: LessonSample) :
-  KLesson("Select", "Expand and shrink the code selection", module, lang) {
+  KLesson("Select", LessonsBundle.message("selection.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
 
       actionTask("EditorNextWordWithSelection") {
-        "Place the caret before any word. Press ${action(it)} to move the caret to the next word and select everything in between."
+        LessonsBundle.message("selection.select.word", action(it))
       }
       actionTask("EditorSelectWord") {
-        "Press ${action(it)} to extend the selection to the next code block."
+        LessonsBundle.message("selection.extend.selection", action(it))
       }
       task("EditorSelectWord") {
-        text("Try to increase your selection with ${action(it)} until your whole file is selected.")
+        text(LessonsBundle.message("selection.extend.until.whole.file", action(it)))
         trigger(it) {
           editor.selectionModel.selectionStart == 0 && editor.document.textLength == editor.selectionModel.selectionEnd
         }
@@ -30,7 +31,7 @@ class SelectLesson(module: Module, lang: String, private val sample: LessonSampl
         }
       }
       actionTask("EditorUnSelectWord") {
-        "${action(it)} shrinks selection. Try pressing it."
+        LessonsBundle.message("selection.shrink.selection", action(it))
       }
     }
 }
\ No newline at end of file
index cf614c23865909d866616794aaf1d7fab755a034..99e5f5e572bde985d86a11648da0d354356df3f3 100644 (file)
@@ -5,13 +5,14 @@ import com.intellij.psi.PsiComment
 import com.intellij.psi.PsiDocumentManager
 import com.intellij.psi.PsiElement
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 
 class SingleLineCommentLesson(module: Module, lang: String, private val sample: LessonSample)
-  : KLesson("Comment line", "Comment line", module, lang) {
+  : KLesson("Comment line", LessonsBundle.message("comment.line.lesson.name"), module, lang) {
 
   override val lessonContent: LessonContext.() -> Unit
     get() = {
@@ -21,15 +22,15 @@ class SingleLineCommentLesson(module: Module, lang: String, private val sample:
       prepareSample(sample)
 
       actionTask("CommentByLineComment") {
-        "Comment out any line with ${action(it)}."
+        LessonsBundle.message("comment.line.comment.any.line", action(it))
       }
       task("CommentByLineComment") {
-        text("Uncomment the commented line with the same shortcut, ${action(it)}.")
+        text(LessonsBundle.message("comment.line.uncomment.that.line", action(it)))
         trigger(it, { countCommentedLines() }, { _, now -> now == 0 })
         test { actions("EditorUp", it) }
       }
       task("CommentByLineComment") {
-        text("Select several lines and then comment them with ${action(it)}.")
+        text(LessonsBundle.message("comment.line.comment.several.lines", action(it)))
         trigger(it, { countCommentedLines() }, { before, now -> now >= before + 2 })
         test { actions("EditorDownWithSelection", "EditorDownWithSelection", it) }
       }
index 311835bcca09f0372807b9bc95c4e50073872859..1de4e6b999f3ca5814c51f4444dc2be28a279122 100644 (file)
@@ -3,6 +3,7 @@ package training.learn.lesson.general
 
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
@@ -10,7 +11,7 @@ import training.learn.lesson.kimpl.LessonSample
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 
 abstract class SurroundAndUnwrapLesson(module: Module, lang: String)
-  : KLesson("Surround and unwrap", "Surround and unwrap", module, lang) {
+  : KLesson("Surround and unwrap", LessonsBundle.message("surround.and.unwrap.lesson.name"), module, lang) {
 
   protected abstract val sample: LessonSample
 
@@ -29,7 +30,7 @@ abstract class SurroundAndUnwrapLesson(module: Module, lang: String)
           editor.caretModel.currentCaret.selectionStart != previous.sample.selection?.first ||
           editor.caretModel.currentCaret.selectionEnd != previous.sample.selection?.second
         }
-        text("Press ${action(it)} to surround the selected code fragment with some template code.")
+        text(LessonsBundle.message("surround.and.unwrap.invoke.surround", action(it)))
         triggerByListItemAndHighlight { item ->
           surroundItems.all { need -> wordIsPresent(item.toString(), need) }
         }
@@ -37,7 +38,7 @@ abstract class SurroundAndUnwrapLesson(module: Module, lang: String)
       }
 
       task {
-        text("Choose <code>$surroundItemName</code> item.")
+        text(LessonsBundle.message("surround.and.unwrap.choose.surround.item", code(surroundItemName)))
         stateCheck {
           editor.document.charsSequence.let { sequence ->
             surroundItems.all { sequence.contains(it) }
@@ -59,7 +60,7 @@ abstract class SurroundAndUnwrapLesson(module: Module, lang: String)
         proposeIfModified {
           editor.caretModel.currentCaret.logicalPosition.line != previous.position.line
         }
-        text("Let's return to the initial file with unwrapping action by ${action(it)}.")
+        text(LessonsBundle.message("surround.and.unwrap.invoke.unwrap", action(it)))
         triggerByListItemAndHighlight { item ->
           wordIsPresent(item.toString(), surroundItems[0])
         }
@@ -67,7 +68,8 @@ abstract class SurroundAndUnwrapLesson(module: Module, lang: String)
       }
       task {
         restoreByUi()
-        text("Choose <code>Unwrap ${surroundItems[0]}</code> item.")
+        text(LessonsBundle.message("surround.and.unwrap.choose.unwrap.item",
+                                   strong(LessonsBundle.message("surround.and.unwrap.item", surroundItems[0]))))
         stateCheck {
           editor.document.charsSequence.let { sequence ->
             !surroundItems.any { sequence.contains(it) }
index ed2237e0743d22ebf2d22b26d7736a7d05a740db..b121d30311ebb0869b94c6ee482cc0bf2b3c5b2e 100644 (file)
@@ -3,6 +3,7 @@ package training.learn.lesson.general.completion
 
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
@@ -12,7 +13,7 @@ import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import javax.swing.JList
 
 abstract class BasicCompletionLessonBase(module: Module, lang: String)
-  : KLesson("Basic completion", "Basic completion", module, lang) {
+  : KLesson("Basic completion", LessonsBundle.message("basic.completion.lesson.name"), module, lang) {
   protected abstract val sample1: LessonSample
   protected abstract val sample2: LessonSample
 
@@ -34,8 +35,7 @@ abstract class BasicCompletionLessonBase(module: Module, lang: String)
       val result1 = LessonUtil.insertIntoSample(sample1, item1Completion)
       prepareSample(sample1)
       task {
-        text("By default, the IDE proposes completion for your code instantly. Start typing <code>$item1Completion</code> right where " +
-             "the caret is, and you will see the Lookup Menu with matching suggestions.")
+        text(LessonsBundle.message("basic.completion.start.typing", code(item1Completion)))
         triggerByListItemAndHighlight(highlightBorder = false, highlightInside = false) { // no highlighting
           it.toString().contains(item1Completion)
         }
@@ -50,7 +50,7 @@ abstract class BasicCompletionLessonBase(module: Module, lang: String)
         }
       }
       task {
-        text("Continue typing <code>$item1Completion</code> unless it becomes the first item.")
+        text(LessonsBundle.message("basic.completion.continue.typing", code(item1Completion)))
         stateCheck {
           (previous.ui as? JList<*>)?.let {
             isTheFirstVariant(it)
@@ -58,9 +58,9 @@ abstract class BasicCompletionLessonBase(module: Module, lang: String)
         }
         restoreByUi()
       }
-      task {
-        text("Now just press <action>EditorEnter</action> to complete this statement.")
-        trigger("EditorChooseLookupItem") {
+      task("EditorChooseLookupItem") {
+        text(LessonsBundle.message("basic.completion.just.press.to.complete", action(it)))
+        trigger(it) {
           editor.document.text == result1
         }
         restoreState {
@@ -76,7 +76,7 @@ abstract class BasicCompletionLessonBase(module: Module, lang: String)
       val result2 = LessonUtil.insertIntoSample(sample2, item2Inserted)
       prepareSample(sample2)
       task("CodeCompletion") {
-        text("To activate Basic Completion explicitly, press ${action(it)}.")
+        text(LessonsBundle.message("basic.completion.activate.explicitly", action(it)))
         trigger(it)
         triggerByListItemAndHighlight { item ->
           item.toString().contains(item2Completion)
@@ -91,7 +91,8 @@ abstract class BasicCompletionLessonBase(module: Module, lang: String)
         }
       }
       task {
-        text("Select <code>$item2Completion</code> and press <action>EditorEnter</action>.")
+        text(LessonsBundle.message("basic.completion.finish.explicit.completion",
+                                   code(item2Completion), action("EditorChooseLookupItem")))
         stateCheck {
           editor.document.text == result2
         }
index 3e073008ad6b05d65feab0033357169eb1eace3c..049f151983cb9a73146b20f6c0125c9f3b953418 100644 (file)
@@ -10,16 +10,18 @@ import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiFile
 import com.intellij.testGuiFramework.framework.GuiTestUtil.shortcut
 import com.intellij.testGuiFramework.util.Key
+import com.intellij.ui.UIBundle
 import com.intellij.ui.table.JBTable
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 
 abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
-  : KLesson("Declaration and usages", "Declaration and usages", module, lang) {
+  : KLesson("Declaration and usages", LessonsBundle.message("declaration.and.usages.lesson.name"), module, lang) {
   abstract fun LessonContext.setInitialPosition()
-  abstract val typeOfEntity: String
+  abstract val typeOfEntity: Int // 0 - method, 1 - attribute accessor
   abstract override val existedFile: String
 
   override val lessonContent: LessonContext.() -> Unit
@@ -27,7 +29,7 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
       setInitialPosition()
 
       task("GotoDeclaration") {
-        text("Use ${action(it)} to jump to the declaration of $typeOfEntity")
+        text(LessonsBundle.message("declaration.and.usages.jump.to.declaration", action(it), typeOfEntity))
         trigger(it, { state() }) { before, _ ->
           before != null && !isInsidePsi(before.target.navigationElement, before.position)
         }
@@ -35,8 +37,7 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
       }
 
       task("GotoDeclaration") {
-        text("Now the caret is at the attribute accessor declaration. " +
-             "Use the same shortcut ${action(it)} to see all of its usages, then select one of them.")
+        text(LessonsBundle.message("declaration.and.usages.show.usages", typeOfEntity, action(it)))
         trigger(it, { state() }) l@{ before, now ->
           if (before == null || now == null) {
             return@l false
@@ -57,7 +58,7 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
       }
 
       task("FindUsages") {
-        text("Use ${action(it)} to see a more detailed view of usages. You can invoke ${action(it)} on either a declaration or usage.")
+        text(LessonsBundle.message("declaration.and.usages.find.usages", action(it)))
 
         triggerByUiComponentAndHighlight { ui: BaseLabel ->
           ui.text?.contains("Usages of") ?: false
@@ -67,6 +68,7 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
         }
       }
 
+      val pinTabText = UIBundle.message("tabbed.pane.pin.tab.action.name")
       task {
         test {
           ideFrame {
@@ -74,19 +76,18 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
           }
         }
         triggerByUiComponentAndHighlight(highlightInside = false) { ui: ActionMenuItem ->
-          ui.text?.contains("Pin Tab") ?: false
+          ui.text?.contains(pinTabText) ?: false
         }
         restoreByUi()
-        text("From the <strong>Find view</strong> you can navigate to both usages and declarations. " +
-             "The next search will override these results in the <strong>Find view</strong> window. " +
-             "To prevent it, pin the results: ")
-        text("Right click the tab title, <strong>Usages of</strong>.")
+        text(LessonsBundle.message("declaration.and.usages.pin.motivation", strong(UIBundle.message("tool.window.name.find"))))
+        text(LessonsBundle.message("declaration.and.usages.right.click.tab",
+                                   strong(LessonsBundle.message("declaration.and.usages.tab.name"))))
       }
 
       task("PinToolwindowTab") {
         trigger(it)
         restoreByUi()
-        text("Select <strong>Pin tab</strong>.")
+        text(LessonsBundle.message("declaration.and.usages.select.pin.item", strong(pinTabText)))
         test {
           ideFrame {
             jComponent(previous.ui!!).click()
@@ -95,11 +96,12 @@ abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
       }
 
       actionTask("HideActiveWindow") {
-        "When you have finished browsing usages, use ${action(it)} to hide the view."
+        LessonsBundle.message("declaration.and.usages.hide.view", action(it))
       }
 
       actionTask("ActivateFindToolWindow") {
-        "Press ${action(it)} to open the <strong>Find view</strong> again."
+        LessonsBundle.message("declaration.and.usages.open.find.view",
+                              action(it), strong(UIBundle.message("tool.window.name.find")))
       }
     }
 
index c9851c11070c15cec86528f4b5150a7540eabad2..39a1b8c4fc4e1b901b9871b1eac8ea854bf06686 100644 (file)
@@ -7,28 +7,28 @@ import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import com.intellij.ui.speedSearch.SpeedSearchSupply
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
+import training.learn.lesson.kimpl.LessonUtil
 
 abstract class FileStructureLesson(module: Module, lang: String)
-  : KLesson("File structure", "File structure", module, lang) {
+  : KLesson("File structure", LessonsBundle.message("file.structure.lesson.name"), module, lang) {
   abstract override val existedFile: String
-  abstract val searchSubstring : String
-  abstract val firstWord : String
-  abstract val secondWord : String
+  abstract val searchSubstring: String
+  abstract val firstWord: String
+  abstract val secondWord: String
 
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       caret(0)
 
       actionTask("FileStructurePopup") {
-        "A large source file can be difficult to read and navigate, sometimes you only need an overview of the file." +
-        "Use ${action(it)} to see the file structure."
+        LessonsBundle.message("file.structure.open.popup", action(it))
       }
       task(searchSubstring) {
-        text("Suppose you want to find some method with ${code(firstWord)} and ${code(secondWord)} words in its name. " +
-             "Type ${code(searchSubstring)} (prefixes of required words) to filter file structure.")
+        text(LessonsBundle.message("file.structure.request.prefixes", code(firstWord), code(secondWord), code(searchSubstring)))
         stateCheck { checkWordInSearch(it) }
         test {
           ideFrame {
@@ -38,12 +38,12 @@ abstract class FileStructureLesson(module: Module, lang: String)
         }
       }
       task {
-        text("Only one item remains. Now press <strong>Enter</strong> to jump to the selected item.")
+        text(LessonsBundle.message("file.structure.navigate", LessonUtil.rawEnter()))
         stateCheck { focusOwner is EditorComponentImpl }
         test { GuiTestUtil.shortcut(Key.ENTER) }
       }
       task("ActivateStructureToolWindow") {
-        text("The IDE can also show you the file structure as a tool window. Open it with ${action(it)}.")
+        text(LessonsBundle.message("file.structure.toolwindow", action(it)))
         stateCheck { focusOwner?.javaClass?.name?.contains("StructureViewComponent") ?: false }
         test { actions(it) }
       }
index 49c6038deb2d95300fadc77b717630fe2480336c..d9bacb200af01e6b95e5cbda8dd88f63abfc6c4f 100644 (file)
@@ -1,27 +1,35 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general.refactorings
 
+import com.intellij.CommonBundle
+import com.intellij.refactoring.RefactoringBundle
 import com.intellij.testGuiFramework.impl.button
+import com.intellij.ui.UIBundle
 import training.commands.kotlin.TaskTestContext
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
 import javax.swing.JDialog
+import training.learn.LessonsBundle
+import training.learn.lesson.kimpl.dropMnemonic
 
 class ExtractMethodCocktailSortLesson(module: Module, lang: String, private val sample: LessonSample)
-  : KLesson("Extract method", "Extract method", module, lang) {
+  : KLesson("Extract method", LessonsBundle.message("extract.method.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
 
       actionTask("ExtractMethod") {
-        "Press ${action(it)} to extract the selected code block into a method."
+        LessonsBundle.message("extract.method.invoke.action", action(it))
       }
       // Now will be open the first dialog
 
+      val okButtonText = CommonBundle.getOkButtonText()
+      val extractMethodDialogTitle = RefactoringBundle.message("extract.method.title")
+      val replaceFragmentDialogTitle = RefactoringBundle.message("replace.fragment")
       task {
-        text("Click <strong>Ok</strong> to start refactoring.")
+        text(LessonsBundle.message("extract.method.start.refactoring", strong(okButtonText)))
 
         // Wait until the second dialog
         stateCheck {
@@ -32,31 +40,32 @@ class ExtractMethodCocktailSortLesson(module: Module, lang: String, private val
 
         test {
           with(TaskTestContext.guiTestCase) {
-            dialog("Extract Method", needToKeepDialog=true) {
-              button("OK").click()
+            dialog(extractMethodDialogTitle, needToKeepDialog = true) {
+              button(okButtonText).click()
             }
           }
         }
       }
 
+      val yesButtonText = CommonBundle.getYesButtonText().dropMnemonic()
       task {
-        text("Cocktail Sort has two swap places. The first fragment has just been extracted. Click <strong>Yes</strong> to extract both of them.")
+        text(LessonsBundle.message("extract.method.confirm.several.replaces", strong(yesButtonText)))
 
         // Wait until the third dialog
-        triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { dialog : JDialog ->
-          dialog.title == "Replace Fragment"
+        triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { dialog: JDialog ->
+          dialog.title == replaceFragmentDialogTitle
         }
 
         test {
           with(TaskTestContext.guiTestCase) {
-            dialog("Extract Method") {
-              button("Yes").click()
+            dialog(extractMethodDialogTitle) {
+              button(yesButtonText).click()
             }
           }
         }
       }
       task {
-        text("Now you can confirm or reject the replacement of the second fragment.")
+        text(LessonsBundle.message("extract.method.second.fragment"))
 
         stateCheck {
           previous.ui?.isShowing?.not() ?: true
@@ -64,8 +73,8 @@ class ExtractMethodCocktailSortLesson(module: Module, lang: String, private val
 
         test {
           with(TaskTestContext.guiTestCase) {
-            dialog("Replace Fragment") {
-              button("Replace").click()
+            dialog(replaceFragmentDialogTitle) {
+              button(UIBundle.message("replace.prompt.replace.button").dropMnemonic()).click()
             }
           }
         }
index d7f9f76c670806349f47f7f1d001e619458f46ab..e07a7cb4e9e7f77d74ebd37bcf05d74ba08aaa76 100644 (file)
@@ -2,18 +2,20 @@
 package training.learn.lesson.general.refactorings
 
 import com.intellij.testGuiFramework.impl.jList
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
+import training.learn.lesson.kimpl.LessonUtil
 
 class ExtractVariableFromBubbleLesson(module: Module, lang: String, private val sample: LessonSample)
-  : KLesson("Extract variable", "Extract variable", module, lang) {
+  : KLesson("Extract variable", LessonsBundle.message("extract.variable.lesson.name"), module, lang) {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
       task("IntroduceVariable") {
-        text("Press ${action(it)} to extract a local variable from the selected expression ${code("i + 1")}.")
+        text(LessonsBundle.message("extract.variable.start.refactoring", action(it), code("i + 1")))
         triggerStart("IntroduceVariable")
         test {
           actions(it)
@@ -21,8 +23,7 @@ class ExtractVariableFromBubbleLesson(module: Module, lang: String, private val
       }
 
       task {
-        text("This code block contains 3 occurrences of the selected expression. " +
-             "Choose the second item on the list to replace all of them.")
+        text(LessonsBundle.message("extract.variable.replace.all"))
 
         stateCheck {
           editor.document.text.split("i + 1").size == 2
@@ -36,8 +37,8 @@ class ExtractVariableFromBubbleLesson(module: Module, lang: String, private val
       }
 
       actionTask("NextTemplateVariable") {
-        "Choose a name for the new variable or leave it as it is. " +
-        "Press <strong>Enter</strong> to complete the refactoring."
+        //TODO: fix the shortcut: it should be ${action(it)} but with preference for Enter
+        LessonsBundle.message("extract.variable.choose.name", LessonUtil.rawEnter())
       }
     }
 }
index 790c4f9a13ebfd57c82d666758dcf110d1130990..305ff75eb54eee0195e4e4e0c7b07ce62c480519 100644 (file)
@@ -16,12 +16,11 @@ import com.intellij.openapi.application.invokeLater
 import com.intellij.openapi.application.runWriteAction
 import com.intellij.openapi.command.WriteCommandAction
 import com.intellij.openapi.editor.LogicalPosition
-import com.intellij.openapi.editor.event.DocumentEvent
-import com.intellij.openapi.editor.event.DocumentListener
 import com.intellij.openapi.editor.ex.EditorGutterComponentEx
 import com.intellij.openapi.editor.impl.EditorComponentImpl
 import com.intellij.openapi.fileEditor.FileDocumentManager
 import com.intellij.psi.PsiDocumentManager
+import com.intellij.tasks.TaskBundle
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import com.intellij.ui.tabs.impl.JBTabsImpl
@@ -33,11 +32,13 @@ import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil
 import com.intellij.xdebugger.impl.frame.XDebuggerFramesList
 import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
 import org.fest.swing.timing.Timeout
+import org.jetbrains.annotations.Nls
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
 import training.commands.kotlin.TaskTestContext
 import training.keymap.KeymapUtil
 import training.learn.CourseManager
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.LessonManager
 import training.learn.lesson.kimpl.*
@@ -54,7 +55,8 @@ import java.util.concurrent.TimeUnit
 import javax.swing.JDialog
 import javax.swing.text.JTextComponent
 
-abstract class CommonDebugLesson(module: Module, id: String, languageId: String) : KLesson(id, "Debug workflow", module, languageId) {
+abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
+  : KLesson(id, LessonsBundle.message("debug.workflow.lesson.name"), module, languageId) {
   protected abstract val sample: LessonSample
   protected abstract val configurationName: String
   protected abstract val quickEvaluationArgument: String
@@ -70,21 +72,12 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
   private var debugSession: XDebugSession? by WeakReferenceDelegator()
   private var logicalPosition: LogicalPosition = LogicalPosition(0, 0)
 
-  private val incorrectBreakPointsMessage = "Breakpoints are set incorrect for this lesson."
+  @Nls
+  private val incorrectBreakPointsMessage = LessonsBundle.message("debug.workflow.incorrect.breakpoints")
 
   override val lessonContent: LessonContext.() -> Unit = {
     prepareSample(sample)
 
-    if (false) {
-      prepareRuntimeTask {
-        editor.document.addDocumentListener(object : DocumentListener {
-          override fun documentChanged(event: DocumentEvent) {
-
-          }
-        }, lessonDisposable)
-      }
-    }
-
     prepareTask()
 
     toggleBreakpointTask()
@@ -139,7 +132,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
     if (needToRun) {
       // Normally this step should not be shown!
       task {
-        text("Before debugging let's run the program and see what is going wrong ${action("RunClass")}.")
+        text(LessonsBundle.message("debug.workflow.run.program", action("RunClass")))
         addFutureStep {
           subscribeForMessageBus(RunManagerListener.TOPIC, object : RunManagerListener {
             override fun runConfigurationSelected(settings: RunnerAndConfigurationSettings?) {
@@ -176,9 +169,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
     }
 
     task("ToggleLineBreakpoint") {
-      text(
-        "So there is a problem. Let's start investigation with placing breakpoint." +
-        "To toggle a breakpoint you should click left editor gutter or just press ${action(it)}.")
+      text(LessonsBundle.message("debug.workflow.toggle.breakpoint", action(it)))
       stateCheck {
         lineWithBreakpoints() == setOf(logicalPosition.line)
       }
@@ -199,8 +190,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     mayBeStopped = false
     task("Debug") {
-      text("To start debug selected run configuration you need to click at ${icon(AllIcons.Actions.StartDebugger)}. " +
-           "Or you can just press ${action(it)}.")
+      text(LessonsBundle.message("debug.workflow.start.debug", icon(AllIcons.Actions.StartDebugger), action(it)))
       addFutureStep {
         project.messageBus.connect(lessonDisposable).subscribe(XDebuggerManager.TOPIC, object : XDebuggerManagerListener {
           override fun processStarted(debugProcess: XDebugProcess) {
@@ -245,7 +235,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
       before {
         LearningUiHighlightingManager.clearHighlights()
       }
-      text("Many trace actions will focus debug toolwindow. Lets return to the editor with ${action(it)}")
+      text(LessonsBundle.message("debug.workflow.return.to.editor", action(it)))
       stateCheck {
         focusOwner is EditorComponentImpl
       }
@@ -266,9 +256,10 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
                            ?: error("Invalid sample data")
       caret(position)
       val hasShortcut = KeymapUtil.getShortcutByActionId(it) != null
-      text("IDE has several ways to show values. For this step, we selected the call. Lets add it to <strong>Watches</strong>. " +
-           "You can copy the expression into clipboard, use ${icon(AllIcons.General.Add)} button on the debug panel and paste copied text. " +
-           "Or you can just use action ${action(it)} ${if (hasShortcut) "" else " (consider to add a shortcut for it later)"}.")
+      val shortcut = if (hasShortcut) "" else " " + LessonsBundle.message("debug.workflow.consider.to.add.a.shortcut")
+
+      text(LessonsBundle.message("debug.workflow.use.watches",
+                                 strong(TaskBundle.message("debugger.watches")), icon(AllIcons.General.Add), action(it), shortcut))
       stateCheck {
         val watches = (XDebuggerManager.getInstance(project) as XDebuggerManagerImpl).watchesManager.getWatches(confNameForWatches)
         watches.any { watch -> watch.expression == needAddToWatch }
@@ -285,16 +276,17 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("StepInto") {
       proposeModificationRestore(sample.text)
-      "Lets step into. You can use action ${action(it)} or the button ${icon(AllIcons.Actions.TraceInto)} at the debug panel."
+      LessonsBundle.message("debug.workflow.step.into", action(it), icon(AllIcons.Actions.TraceInto))
     }
 
     task {
       before {
         LearningUiHighlightingManager.clearHighlights()
       }
-      text("In most cases you will want to skip argument calculating so Smart Step Into feature suggest by default the wrapping method. " +
-           "But here we need to choose the second one: ${code(methodForStepInto)}. You can choose it by keyboard " +
-           "<raw_action>$stepIntoDirection</raw_action> and press ${action("EditorEnter")} or you can click the call by mouse.")
+      text(LessonsBundle.message("debug.workflow.choose.method.to.step.in",
+                                 code(methodForStepInto),
+                                 "<raw_action>$stepIntoDirection</raw_action>",
+                                 action("EditorEnter")))
       stateCheck {
         val debugLine = debugSession?.currentStackFrame?.sourcePosition?.line
         val sampleLine = editor.offsetToLogicalPosition(sample.getPosition(2).startOffset).line
@@ -316,8 +308,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
       }
       val position = sample.getPosition(2)
       caret(position)
-      text("Lets see what we are going to pass to ${code(quickEvaluationArgument)}. We selected the argument. " +
-           "Invoke Quick Evaluate Expression ${action(it)}.")
+      text(LessonsBundle.message("debug.workflow.quick.evaluate", code(quickEvaluationArgument), action(it)))
       trigger(it)
       proposeRestore {
         checkPositionOfEditor(LessonSample(sample.text, position)) ?: checkForBreakpoints()
@@ -328,8 +319,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
   private fun LessonContext.fixTheErrorTask() {
     task {
-      text("Oh, we made a mistake in the array index! Lets fix it right now. " +
-           "Close popup (${action("EditorEscape")}) and change 0 to 1.")
+      text(LessonsBundle.message("debug.workflow.fix.error", action("EditorEscape")))
       val intermediate = sample.text.replaceFirst("[0]", "[]")
       val restorePosition = sample.text.indexOf("[0]") + 2
       stateCheck {
@@ -360,8 +350,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("StepOver") {
       proposeModificationRestore(afterFixText)
-      "Let's check that the call of ${code("extract_number")} will not throw an exception now. " +
-      "Use Step Over action ${action(it)} or click the button ${icon(AllIcons.Actions.TraceOver)}."
+      LessonsBundle.message("debug.workflow.step.over", code("extract_number"), action(it), icon(AllIcons.Actions.TraceOver))
     }
   }
 
@@ -370,8 +359,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("Resume") {
       proposeModificationRestore(afterFixText)
-      "Seems no exceptions by now. Let's continue execution with ${action(it)} or" +
-      "click the button ${icon(AllIcons.Actions.Resume)}."
+      LessonsBundle.message("debug.workflow.resume", action(it), icon(AllIcons.Actions.Resume))
     }
   }
 
@@ -380,8 +368,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("XDebugger.MuteBreakpoints") {
       proposeModificationRestore(afterFixText)
-      "Ups, same breakpoint again. Now we don't need to stop at this breakpoint. " +
-      "So let's mute breakpoints by the button ${icon(AllIcons.Debugger.MuteBreakpoints)} or by action ${action(it)}."
+      LessonsBundle.message("debug.workflow.mute.breakpoints", icon(AllIcons.Debugger.MuteBreakpoints), action(it))
     }
   }
 
@@ -395,10 +382,12 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
       proposeRestore {
         checkPositionOfEditor(LessonSample(afterFixText, position))
       }
-      "Let's check the result of ${code(debuggingMethodName)}. " +
-      "We've moved the editor cursor to the ${code("return")} statement. " +
-      "Lets use <strong>Run to Cursor</strong> action ${action(it)} or click ${icon(AllIcons.Actions.RunToCursor)}. " +
-      "Note that <strong>Run to Cursor</strong> works even if breakpoints are muted."
+      LessonsBundle.message("debug.workflow.run.to.cursor",
+                            code(debuggingMethodName),
+                            code("return"),
+                            action(it),
+                            icon(AllIcons.Actions.RunToCursor),
+                            LessonUtil.actionName(it))
     }
   }
 
@@ -407,14 +396,13 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("EvaluateExpression") {
       proposeModificationRestore(afterFixText)
-      "It seems the ${code("result")} is not an average we want to find. " +
-      "We forgot to divide by length. Seems we need to return ${code(expressionToBeEvaluated)}. " +
-      "Let's calculate this expresion. Invoke the <strong>Evaluate Expression</strong> action by pressing ${action(it)} or " +
-      "click button ${icon(AllIcons.Debugger.EvaluateExpression)}. "
+      LessonsBundle.message("debug.workflow.evaluate.expression", code("result"), code(expressionToBeEvaluated), action(it),
+                            icon(AllIcons.Debugger.EvaluateExpression))
     }
 
     task(expressionToBeEvaluated) {
-      text("Type ${code(it)} into the <strong>Expression</strong> field, completion works.")
+      text(LessonsBundle.message("debug.workflow.type.result", code(it),
+                                 strong(XDebuggerBundle.message("xdebugger.evaluate.label.expression"))))
       stateCheck { checkWordInTextField(it) }
       proposeModificationRestore(afterFixText)
       test {
@@ -426,7 +414,8 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
       before {
         LearningUiHighlightingManager.clearHighlights()
       }
-      text("Click <strong>Evaluate</strong> or hit ${action("EditorEnter")}.")
+      text(LessonsBundle.message("debug.workflow.evaluate.it", LessonUtil.rawEnter(),
+                                 strong(XDebuggerBundle.message("xdebugger.button.evaluate").dropMnemonic())))
       triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { debugTree: XDebuggerTree ->
         val dialog = UIUtil.getParentOfType(JDialog::class.java, debugTree)
         val root = debugTree.root
@@ -442,8 +431,7 @@ abstract class CommonDebugLesson(module: Module, id: String, languageId: String)
 
     actionTask("Stop") {
       before { mayBeStopped = true }
-      "It will be a correct answer! Lets close the dialog and stop debugging by ${action(it)}" +
-      "or button ${icon(AllIcons.Actions.Suspend)}."
+      LessonsBundle.message("debug.workflow.stop.debug", action(it), icon(AllIcons.Actions.Suspend))
     }
   }
 
index bd1821eee747027afee210e1b693caf4bc5ed30e..719856200149e719ee6067cae77c0bf8f21f63a9 100644 (file)
@@ -1,22 +1,25 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.general.run
 
+import com.intellij.execution.ExecutionBundle
 import com.intellij.execution.RunManager
+import com.intellij.idea.ActionsBundle
 import com.intellij.openapi.editor.impl.EditorComponentImpl
 import com.intellij.testGuiFramework.impl.button
 import com.intellij.testGuiFramework.impl.jList
+import com.intellij.ui.UIBundle
 import com.intellij.ui.components.JBCheckBox
+import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
-import training.learn.lesson.kimpl.KLesson
-import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.LessonSample
-import training.learn.lesson.kimpl.LessonUtil
+import training.learn.lesson.kimpl.*
 import training.ui.LearningUiHighlightingManager
 import javax.swing.JButton
 
-abstract class CommonRunConfigurationLesson(module: Module, id: String, languageId: String) : KLesson(id, "Run configuration", module, languageId) {
+abstract class CommonRunConfigurationLesson(module: Module, id: String, languageId: String)
+  : KLesson(id, LessonsBundle.message("run.configuration.lesson.name"), module, languageId) {
   protected abstract val sample: LessonSample
   protected abstract val demoConfigurationName: String
 
@@ -24,6 +27,8 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
   protected fun TaskRuntimeContext.configurations() =
     runManager().allSettings.filter { it.name.contains(demoConfigurationName) }
 
+  private fun TaskContext.runToolWindow() = strong(UIBundle.message("tool.window.name.run"))
+
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       prepareSample(sample)
@@ -37,8 +42,7 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
 
       actionTask("HideActiveWindow") {
         LearningUiHighlightingManager.clearHighlights()
-        "IDE automatically opened <strong>Run</strong> tool window. Just a tip, at the top of <strong>Run</strong> tool window you can see the full runned command." +
-        " Now let’s hide the tool window with ${action(it)}."
+        LessonsBundle.message("run.configuration.hide.toolwindow", runToolWindow(), action(it))
       }
 
       task {
@@ -47,11 +51,12 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
         }
       }
 
+      val saveConfigurationItemName = ExecutionBundle.message("save.temporary.run.configuration.action.name", demoConfigurationName)
+        .dropMnemonic()
       task {
-        text("For each new run IDE create temporary run configuration. Temporary configurations are automatically deleted if the default limit of 5 is reached. " +
-             "Lets convert temporary configuration into permanent one. Open the drop-down menu with run configuration.")
+        text(LessonsBundle.message("run.configuration.temporary.to.permanent"))
         triggerByListItemAndHighlight { item ->
-          item.toString() == "Save '$demoConfigurationName' Configuration"
+          item.toString() == saveConfigurationItemName
         }
         test {
           ideFrame {
@@ -61,7 +66,7 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
       }
 
       task {
-        text("Select <strong>Save '$demoConfigurationName' Configuration</strong>.")
+        text(LessonsBundle.message("run.configuration.select.save.configuration", strong(saveConfigurationItemName)))
         restoreByUi()
         stateCheck {
           val selectedConfiguration = RunManager.getInstance(project).selectedConfiguration ?: return@stateCheck false
@@ -69,17 +74,18 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
         }
         test {
           ideFrame {
-            jList("Save '$demoConfigurationName' Configuration").click()
+            jList(saveConfigurationItemName).click()
           }
         }
       }
 
       task("editRunConfigurations") {
         LearningUiHighlightingManager.clearHighlights()
-        text("Suppose you want to change configuration or create another one manually. Then you need to open the the drop-down menu again and click <strong>Edit Configurations</strong>. " +
-             "Alternatively you can use ${action(it)} action.")
+        text(LessonsBundle.message("run.configuration.edit.configuration",
+                                   strong(ActionsBundle.message("action.editRunConfigurations.text").dropMnemonic()),
+                                   action(it)))
         triggerByUiComponentAndHighlight<JBCheckBox>(highlightInside = false) { ui ->
-          ui.text == "Store as project file"
+          ui.text == ExecutionBundle.message("run.configuration.store.as.project.file").dropMnemonic()
         }
         test {
           actions(it)
@@ -87,10 +93,8 @@ abstract class CommonRunConfigurationLesson(module: Module, id: String, language
       }
 
       task {
-        text("This is a place for managing run/debug configurations. You can set here program parameters, JVM arguments, environment variables and so on.")
-        text("Just a tip. Sometimes you may want to save configuration to its own file. " +
-             "Such configurations will be easy to share between colleagues (usually by version control system)." +
-             "Now close the settings dialog to finish this lesson.")
+        text(LessonsBundle.message("run.configuration.settings.description"))
+        text(LessonsBundle.message("run.configuration.tip.about.save.configuration.into.file"))
         stateCheck {
           focusOwner is EditorComponentImpl
         }
index 6de6dc639cca978003a7a53111c38c2f3ac6f429..0b3dff2c08467c2fdd3895511ded208de3300a18 100644 (file)
@@ -2,12 +2,14 @@
 package training.learn.lesson.java.completion
 
 import training.lang.JavaLangSupport
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
-class BasicCompletionLesson(module: Module) : KLesson("Basic completion", "Basic completion", module, JavaLangSupport.lang) {
+class BasicCompletionLesson(module: Module)
+  : KLesson("Basic completion", LessonsBundle.message("basic.completion.lesson.name"), module, JavaLangSupport.lang) {
 
   val sample = parseLessonSample("""import java.lang.*;
 import java.util.*;
@@ -36,21 +38,23 @@ class BasicCompletionDemo implements Runnable{
       return {
         prepareSample(sample)
         actionTask("EditorChooseLookupItem") {
-          "By default, IntelliJ IDEA completes your code instantly. Start typing <code>Ran</code> right where the caret is, and you will see the Lookup Menu with matching suggestions. You can choose the first item from the Lookup menu by pressing ${action("EditorEnter")}."
+          LessonsBundle.message("basic.completion.start.typing", code("Run")) + " " +
+          LessonsBundle.message("java.basic.completion.choose.first", action(it))
         }
         caret(18, 36)
         actionTask("CodeCompletion") {
-          "To activate Basic Completion, press ${action(it)} and you will see lookup menu again."
+          LessonsBundle.message("java.basic.completion.activate", action(it))
         }
         actionTask("EditorChooseLookupItem") {
-          "Select <code>i</code> inside the lookup menu and press <action>EditorEnter</action>."
+          LessonsBundle.message("java.basic.completion.choose.item", code("i"), action(it))
         }
         actionTask("EditorCompleteStatement") {
-          "Press ${action(it)} to complete this statement."
+          LessonsBundle.message("java.basic.completion.complete", action(it))
         }
         task("CodeCompletion") {
           caret(13, 27)
-          text("Sometimes you need to see suggestions for static constants or methods. Press ${action(it)} twice to access a deeper level of Code Completion.")
+          text(
+            LessonsBundle.message("java.basic.completion.deeper.level", action(it)))
           triggers(it, it)
         }
 
index 010b1d831810d9f221ca2c4bc8511c01b261a1f4..b6153a07164d50bd9c909d797c0fb6955bcbb3d6 100644 (file)
@@ -2,13 +2,14 @@
 package training.learn.lesson.java.completion
 
 import training.lang.JavaLangSupport
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class PostfixCompletionLesson(module: Module)
-  : KLesson("Postfix completion", "Postfix completion", module, JavaLangSupport.lang) {
+  : KLesson("Postfix completion", LessonsBundle.message("postfix.completion.lesson.name"), module, JavaLangSupport.lang) {
 
   val sample = parseLessonSample("""class PostfixCompletionDemo{
 
@@ -21,8 +22,7 @@ class PostfixCompletionLesson(module: Module)
     prepareSample(sample)
     caret(4, 27)
     actionTask("EditorChooseLookupItem") {
-      "Postfix Completion helps reduce backward caret jumps as you write code. It lets you transform an already typed expression into another one based on the postfix you add, the type of expression, and its context. Type <code>.</code> after the parenthesis to see the list of postfix completion suggestions. Select <code>if</code> from the list, or type it in editor, and then press ${action("EditorEnter")} to complete the statement."
+      LessonsBundle.message("java.postfix.completion.apply", code("."), code("if"), action("EditorChooseLookupItem"))
     }
   }
-
 }
\ No newline at end of file
index bbbc63b8d8bfcb26ed5b50c620bb1b88f733ea5a..c9fb8420740834906574724ce1a1097107e0770f 100644 (file)
@@ -2,13 +2,14 @@
 package training.learn.lesson.java.completion
 
 import training.lang.JavaLangSupport
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class SmartTypeCompletionLesson(module: Module)
-  : KLesson("Smart type completion", "Smart type completion", module, JavaLangSupport.lang) {
+  : KLesson("Smart type completion", LessonsBundle.message("java.smart.type.completion.lesson.name"), module, JavaLangSupport.lang) {
 
   val sample = parseLessonSample("""import java.lang.String;
 import java.util.HashSet;
@@ -40,13 +41,13 @@ class SmartCompletionDemo{
         prepareSample(sample)
         task {
           caret(13, 19)
-          text("Smart Type Completion filters the list of suggestion to include only those types that are applicable in the current context. Press ${action("SmartTypeCompletion")} to see the list of matching suggestions. Choose the first one by pressing ${action("EditorEnter")}.")
+          text(LessonsBundle.message("java.smart.type.completion.apply", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
           trigger("SmartTypeCompletion")
           trigger("EditorChooseLookupItem")
         }
         task {
           caret(20, 16)
-          text("Smart Type Completion can also suggest code for a return statement. Press ${action("SmartTypeCompletion")} twice to see the Lookup menu for a return. Choose the first one by pressing ${action("EditorEnter")}")
+          text(LessonsBundle.message("java.smart.type.completion.return", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
           triggers("SmartTypeCompletion", "SmartTypeCompletion")
           trigger("EditorChooseLookupItem")
         }
index 05968b7569c3390079bf747861cdca2c99f8bba0..1e3cd639e1e8a4bb7591eb28344e0628f94697f1 100644 (file)
@@ -6,13 +6,14 @@ import com.intellij.psi.PsiForStatement
 import com.intellij.psi.util.PsiTreeUtil
 import training.commands.kotlin.TaskRuntimeContext
 import training.lang.JavaLangSupport
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class StatementCompletionLesson(module: Module)
-  : KLesson("Statement completion", "Statement completion", module, JavaLangSupport.lang) {
+  : KLesson("Statement completion", LessonsBundle.message("java.statement.completion.lesson.name"), module, JavaLangSupport.lang) {
 
   val sample = parseLessonSample("""class PrimeNumbers {
     public static void main(String[] args) {
@@ -34,19 +35,19 @@ class StatementCompletionLesson(module: Module)
     prepareSample(sample)
     caret(8, 40)
     actionTask("EditorCompleteStatement") {
-      "Press ${action(it)} to complete the <code>for</code> statement."
+      LessonsBundle.message("java.statement.completion.complete.for", action(it), code("for"))
     }
     task("EditorCompleteStatement") {
-      text("Write <code>if</code> and press ${action(it)} to generate the statement.")
+      text(LessonsBundle.message("java.statement.completion.complete.if", code("if"), action(it)))
       stateCheck {
         return@stateCheck checkIfAppended()
       }
     }
     actionTask("EditorCompleteStatement") {
-      "Add a condition inside parentheses <code>i % j == 0</code> and press ${action(it)} to jump inside the <code>if</code> statement."
+      LessonsBundle.message("java.statement.completion.complete.condition", code("i % j == 0"), action(it), code("if"))
     }
     actionTask("EditorCompleteStatement") {
-      "Write on one line: <code>isPrime = false; break</code> and then press ${action(it)} to complete the entered statement and apply formatting."
+      LessonsBundle.message("java.statement.completion.complete.finish.body", code("isPrime = false; break"), action(it))
     }
   }
 
index c26fec76838b5e687f7adc8e01e75a4b0e65a361..99316004847b00ab15982e3e9d4c843f3acc5667 100644 (file)
@@ -4,12 +4,13 @@ package training.learn.lesson.java.run
 import com.intellij.icons.AllIcons
 import com.intellij.testGuiFramework.impl.button
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.general.run.CommonDebugLesson
 import training.learn.lesson.kimpl.LessonContext
 
 class JavaDebugLesson(module: Module) : CommonDebugLesson(module, "java.debug.workflow", "JAVA") {
-  val demoClassName = JavaRunLessonsUtils.demoClassName
+  private val demoClassName = JavaRunLessonsUtils.demoClassName
   override val configurationName: String = demoClassName
   override val sample = JavaRunLessonsUtils.demoSample
 
@@ -24,9 +25,7 @@ class JavaDebugLesson(module: Module) : CommonDebugLesson(module, "java.debug.wo
     highlightButtonById("CompileDirty")
 
     task("CompileDirty") {
-      text("For big programs rerun can take too much time. " +
-           "When you find some mistake in pure method you can just rebuild the project and apply <strong>Hot Swap</strong> JVM feature. " +
-           "Let's build project: ${action(it)} or ${icon(AllIcons.Actions.Compile)}.")
+      text(LessonsBundle.message("java.debug.workflow.rebuild", action(it), icon(AllIcons.Actions.Compile)))
       stateCheck {
         inHotSwapDialog()
       }
@@ -35,7 +34,7 @@ class JavaDebugLesson(module: Module) : CommonDebugLesson(module, "java.debug.wo
     }
 
     task {
-      text("Confirm <strong>Hot Swap</strong> replacement")
+      text(LessonsBundle.message("java.debug.workflow.confirm.hot.swap"))
       stateCheck {
         !inHotSwapDialog()
       }
@@ -53,9 +52,7 @@ class JavaDebugLesson(module: Module) : CommonDebugLesson(module, "java.debug.wo
 
     actionTask("Debugger.PopFrame") {
       proposeModificationRestore(afterFixText)
-      "We patched our method, but right now we are still executing old obsolete ${code("extractNumber")} and it will throw " +
-      "the exception again. Let's drop the frame and return to the state before ${code("extractNumber")} call. " +
-      "Click ${icon(AllIcons.Actions.PopFrame)} at the debug panel or use ${action(it)}."
+      LessonsBundle.message("java.debug.workflow.drop.frame", code("extractNumber"), code("extractNumber"), icon(AllIcons.Actions.PopFrame), action(it))
     }
   }
 
index b319e888124d6cce9e38103bf50d5e9af0320ae6..6013ab1d9810edde83e7cc10b3b6368116f56a16 100644 (file)
@@ -1,14 +1,17 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.java.run
 
+import com.intellij.execution.ExecutionBundle
 import com.intellij.icons.AllIcons
 import com.intellij.openapi.actionSystem.CommonDataKeys
 import com.intellij.openapi.actionSystem.DataProvider
 import com.intellij.openapi.editor.ex.EditorGutterComponentEx
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.general.run.CommonRunConfigurationLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.LessonSample
+import training.learn.lesson.kimpl.dropMnemonic
 import training.learn.lesson.kimpl.toolWindowShowed
 import java.awt.Rectangle
 
@@ -26,8 +29,8 @@ class JavaRunConfigurationLesson(module: Module) : CommonRunConfigurationLesson(
     }
 
     task("RunClass") {
-      text("Any code marked with ${icon(AllIcons.Actions.Execute)} can be run. Let's run our simple example with ${action(it)}. " +
-           "Alternatively you can click at ${icon(AllIcons.Actions.Execute)} and select <strong>Run</strong> item.")
+      text(LessonsBundle.message("java.run.configuration.lets.run", icon(AllIcons.Actions.Execute), action(it),
+                                 strong(ExecutionBundle.message("default.runner.start.action.text").dropMnemonic())))
       //Wait toolwindow
       toolWindowShowed("Run")
       stateCheck {
index fb87bad3fdcb056fce0f36d40daba6fafa6d3a8c..e61e8b20265fc79dabb78bb224b8027f8a553c26 100644 (file)
@@ -1,12 +1,14 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.kimpl
 
+import org.jetbrains.annotations.Nls
+import org.jetbrains.annotations.NonNls
 import training.learn.interfaces.Lesson
 import training.learn.interfaces.Module
 import training.learn.lesson.LessonListener
 
-abstract class KLesson(final override val id: String,
-                       final override val name: String,
+abstract class KLesson(@NonNls final override val id: String,
+                       @Nls final override val name: String,
                        override var module: Module,
                        override val lang: String) : Lesson {
 
index 1ae4fe059403f48081f4d4547beffb2fca847314..63f0ae5c640882373fdbb77a64326293757b2d69 100644 (file)
@@ -3,6 +3,7 @@ package training.learn.lesson.kimpl
 
 import com.intellij.openapi.application.ModalityState
 import com.intellij.openapi.application.invokeLater
+import org.jetbrains.annotations.Nls
 import com.intellij.util.concurrency.annotations.RequiresEdt
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
@@ -15,7 +16,7 @@ abstract class LessonContext {
   abstract fun task(taskContent: TaskContext.() -> Unit)
 
   /** Describe a simple task: just one action required */
-  fun actionTask(action: String, getText: TaskContext.(action: String) -> String) {
+  fun actionTask(action: String, @Nls getText: TaskContext.(action: String) -> String) {
     task {
       text(getText(action))
       trigger(action)
index b332832470ea383f9cf304af575c63ef06d330bb..9cc30f4745521aa307a9d66487d58c5932ac0579 100644 (file)
@@ -104,5 +104,7 @@ private class FakeTaskContext : TaskContext() {
 
   override fun code(sourceSample: String): String = "" //Doesn't matter what to return
 
+  override fun strong(text: String): String = "" //Doesn't matter what to return
+
   override fun icon(icon: Icon): String = "" //Doesn't matter what to return
 }
\ No newline at end of file
index 2796cff5a87a932042257f19ee48028c77400bc4..565880064549c96ac7a8494b97318a75cc2e6050 100644 (file)
@@ -1,18 +1,26 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.kimpl
 
+import com.intellij.openapi.actionSystem.ActionManager
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.editor.EditorModificationUtil
 import com.intellij.openapi.editor.ex.EditorEx
 import com.intellij.openapi.fileEditor.FileDocumentManager
+import com.intellij.openapi.util.NlsActions
+import com.intellij.openapi.util.SystemInfo
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.openapi.util.text.TextWithMnemonic
 import com.intellij.openapi.wm.ToolWindow
 import com.intellij.openapi.wm.ex.ToolWindowManagerListener
 import com.intellij.util.messages.Topic
 import com.intellij.xdebugger.XDebuggerManager
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
+import training.keymap.KeymapUtil
 import training.learn.LearnBundle
+import java.awt.event.KeyEvent
 import javax.swing.JList
+import javax.swing.KeyStroke
 
 object LessonUtil {
   fun insertIntoSample(sample: LessonSample, inserted: String): String {
@@ -80,6 +88,20 @@ object LessonUtil {
     editor.isViewer = true
     EditorModificationUtil.setReadOnlyHint(editor, LearnBundle.message("learn.task.read.only.hint"))
   }
+
+  fun actionName(actionId: String): @NlsActions.ActionText String {
+    val name = ActionManager.getInstance().getAction(actionId).templatePresentation.text ?: error("No action with ID $actionId")
+    return "<strong>${name}</strong>"
+  }
+
+  fun rawEnter(): String {
+    val keyStrokeEnter = KeymapUtil.getKeyStrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0))
+    return "<raw_action>$keyStrokeEnter</raw_action>"
+  }
+
+  fun rawCtrlEnter(): String {
+    return "<raw_action>${if (SystemInfo.isMacOSMojave) "\u2318\u23CE" else "Ctrl + Enter"}</raw_action>"
+  }
 }
 
 fun TaskContext.toolWindowShowed(toolWindowId: String) {
@@ -106,3 +128,7 @@ fun TaskRuntimeContext.lineWithBreakpoints(): Set<Int> {
     it.sourcePosition?.line
   }.toSet()
 }
+
+fun String.dropMnemonic(): String {
+  return TextWithMnemonic.parse(this).dropMnemonic(true).text
+}
\ No newline at end of file
index 9163b6ae0443ad8a24adb5cfc2d3d24cbdd6c515..b5de2da28a7eb0736ed208dcd1c057a2697fca4f 100644 (file)
@@ -3,14 +3,15 @@ package training.learn.lesson.python.completion
 
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.LessonUtil
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import training.learn.lesson.kimpl.parseLessonSample
 
-class FStringCompletionLesson(module: Module) : KLesson("completion.f.string", "F-string completion", module, "Python") {
+class FStringCompletionLesson(module: Module)
+  : KLesson("completion.f.string", LessonsBundle.message("python.f.string.completion.lesson.name"), module, "Python") {
   private val template = """
     import sys
     
@@ -50,12 +51,12 @@ class FStringCompletionLesson(module: Module) : KLesson("completion.f.string", "
   override val lessonContent: LessonContext.() -> Unit = {
     prepareSample(sample)
     task("\${my") {
-      text("PyCharm supports automatic f-string conversion. Just start to type ${code(it)}")
+      text(LessonsBundle.message("python.f.string.completion.type.prefix", code(it)))
       runtimeText {
         val prefixTyped = checkExpectedStateOfEditor(sample) { change ->
           "\${my_car".startsWith(change) && change.startsWith(it)
         } == null
-        if (prefixTyped) "You can invoke completion manually with ${action("CodeCompletion")}" else null
+        if (prefixTyped) LessonsBundle.message("python.f.string.completion.invoke.manually", action("CodeCompletion")) else null
       }
       triggerByListItemAndHighlight(highlightBorder = false) { item ->
         item.toString().contains(completionItem)
@@ -68,7 +69,7 @@ class FStringCompletionLesson(module: Module) : KLesson("completion.f.string", "
       test { type(it) }
     }
     task {
-      text("Complete the statement with ${code(completionItem)}. Just press ${action("EditorEnter")} to apply the first item.")
+      text(LessonsBundle.message("python.f.string.completion.complete.it", code(completionItem), action("EditorChooseLookupItem")))
       val result = template.replace("<f-place>", "f").replace("<caret>", "\${$completionItem}")
       restoreByUi()
       stateCheck {
@@ -77,7 +78,7 @@ class FStringCompletionLesson(module: Module) : KLesson("completion.f.string", "
       test { GuiTestUtil.shortcut(Key.ENTER) }
     }
     task {
-      text("You may see that simple Python string was replaced by f-string after the completion.")
+      text(LessonsBundle.message("python.f.string.completion.result.message"))
     }
   }
 }
index 1162e9810e6c40e81d052bae00bc76ee91b0802c..0c4e494f14551573ad45f9b5fcdb3ceb2313399e 100644 (file)
@@ -3,17 +3,17 @@ package training.learn.lesson.python.completion
 
 import com.intellij.testGuiFramework.framework.GuiTestUtil.typeText
 import com.intellij.testGuiFramework.impl.jList
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.LessonUtil
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import training.learn.lesson.kimpl.parseLessonSample
 
 private const val completionSuffix = ".ifnn"
 
 class PythonPostfixCompletionLesson(module: Module)
-  : KLesson("Postfix completion", "Postfix completion", module, "Python") {
+  : KLesson("Postfix completion", LessonsBundle.message("postfix.completion.lesson.name"), module, "Python") {
   private val sample = parseLessonSample("""
     movies_dict = {
         'title': 'Aviator',
@@ -40,7 +40,7 @@ class PythonPostfixCompletionLesson(module: Module)
     get() = {
       prepareSample(sample)
       task {
-        text("The IDE can offer postfix shortcuts. Type ${code(completionSuffix)}.")
+        text(LessonsBundle.message("postfix.completion.type.template", code(completionSuffix)))
         triggerByListItemAndHighlight {
           it.toString() == completionSuffix
         }
@@ -54,7 +54,7 @@ class PythonPostfixCompletionLesson(module: Module)
         }
       }
       task {
-        text("Select ${code(completionSuffix)} item from completion list.")
+        text(LessonsBundle.message("python.postfix.completion.select.item", code(completionSuffix)))
         stateCheck { editor.document.text == result }
         restoreByUi()
         test {
index f1a0e042bde1b3daa7af79d4c7d1c0b528090ea7..cbcc25e9ac5e6ab0a0f76f8120d7b1a92baf8d0e 100644 (file)
@@ -2,6 +2,7 @@
 package training.learn.lesson.python.completion
 
 import training.commands.kotlin.TaskContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
@@ -10,7 +11,7 @@ import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import training.learn.lesson.kimpl.parseLessonSample
 
 class PythonSmartCompletionLesson(module: Module)
-  : KLesson("Smart completion", "Smart completion", module, "Python") {
+  : KLesson("Smart completion", LessonsBundle.message("python.smart.completion.lesson.name"), module, "Python") {
   private val sample = parseLessonSample("""
     def f(x, file):
       x.append(file)
@@ -25,11 +26,10 @@ class PythonSmartCompletionLesson(module: Module)
         prepareSample(sample)
         actionTask("CodeCompletion") {
           proposeRestoreMe()
-          "Try to use Basic Completion by pressing ${action(it)}."
+          LessonsBundle.message("python.smart.completion.try.basic.completion", action(it))
         }
         task("SmartTypeCompletion") {
-          text("Unfortunately IDE has no direct information about ${code("x")} type. " +
-               "But sometimes it can guess completion by the context! Use ${action(it)} to invoke Smart Completion.")
+          text(LessonsBundle.message("python.smart.completion.use.smart.completion", code("x"), action(it)))
           triggerByListItemAndHighlight { ui ->
             ui.toString().contains(methodName)
           }
@@ -38,7 +38,7 @@ class PythonSmartCompletionLesson(module: Module)
         }
         task {
           val result = LessonUtil.insertIntoSample(sample, insertedCode)
-          text("Now just choose ${code(methodName)} item to finish this lesson.")
+          text(LessonsBundle.message("python.smart.completion.finish.completion", code(methodName)))
           restoreByUi()
           stateCheck {
             editor.document.text == result
index 7087eae0f35272eb3a29ede636292efbc539b438..c8ccb37bc96bc7c8a93dbac5cfda2186743769b4 100644 (file)
@@ -5,13 +5,14 @@ import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.*
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import javax.swing.JList
 
 class PythonTabCompletionLesson(module: Module)
-  : KLesson("Tab completion", "Tab completion", module, "Python") {
+  : KLesson("Tab completion", LessonsBundle.message("python.tab.completion.lesson.name"), module, "Python") {
   private val template = parseLessonSample("""
     class Calculator:
         def __init__(self):
@@ -34,13 +35,14 @@ class PythonTabCompletionLesson(module: Module)
       return {
         prepareSample(sample)
         task("CodeCompletion") {
-          text("Suppose you want to replace ${code("current")} with ${code("total")}. Invoke completion by pressing ${action(it)}.")
+          text(LessonsBundle.message("python.tab.completion.start.completion",
+                                     code("current"), code("total"), action(it)))
           triggerByListItemAndHighlight(checkList = { ui -> isTotalItem(ui) })
           proposeRestoreMe()
           test { actions(it) }
         }
         task {
-          text("Select item ${code("total")} by keyboard arrows or just start typing it.")
+          text(LessonsBundle.message("python.tab.completion.select.item", code("total")))
           restoreState {
             (previous.ui as? JList<*>)?.let { ui ->
               !ui.isShowing || LessonUtil.findItem(ui, isTotalItem) == null
@@ -57,8 +59,9 @@ class PythonTabCompletionLesson(module: Module)
         }
         task {
           val result = LessonUtil.insertIntoSample(template, "total")
-          text("If you press ${action("EditorEnter")} you will insert ${code("total")} before ${code("current")}. " +
-               "Instead press ${action("EditorTab")} to replace ${code("current")} with ${code("total")}")
+          text(LessonsBundle.message("python.tab.completion.use.tab.completion",
+                                     action("EditorEnter"), code("total"), code("current"),
+                                     action("EditorTab")))
 
           trigger("EditorChooseLookupItemReplace") {
             editor.document.text == result
index bf2dbd414ec9089b25b9590e47297f34dac90d53..d36e7e0ba41d168a1f6b0a001786fe7bf4e48d1c 100644 (file)
@@ -7,6 +7,6 @@ import training.learn.lesson.kimpl.LessonContext
 
 class PythonDeclarationAndUsagesLesson(module: Module) : DeclarationAndUsagesLesson(module, "Python") {
   override fun LessonContext.setInitialPosition() = caret(652, 30)
-  override val typeOfEntity = "a method"
+  override val typeOfEntity = 0
   override val existedFile: String = "src/jinja2/ext.py"
 }
index 5e290a809e18eee362e1d57d8f36e3a40df7a11f..5e92f1a9dfe31eda4f740fe6184780fd16b851d8 100644 (file)
@@ -4,6 +4,8 @@ package training.learn.lesson.python.navigation
 import com.intellij.openapi.actionSystem.impl.ActionButtonWithText
 import com.intellij.openapi.editor.impl.EditorComponentImpl
 import com.intellij.openapi.fileEditor.FileEditorManager
+import com.intellij.psi.search.EverythingGlobalScope
+import com.intellij.psi.search.ProjectScope
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import com.intellij.testGuiFramework.util.Modifier
@@ -11,25 +13,27 @@ import com.intellij.testGuiFramework.util.Shortcut
 import com.intellij.ui.components.fields.ExtendableTextField
 import training.commands.kotlin.TaskRuntimeContext
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
+import training.learn.lesson.kimpl.LessonUtil
 import training.ui.LearningUiHighlightingManager
 import java.awt.event.InputEvent
 import java.awt.event.KeyEvent
 
 class PythonSearchEverywhereLesson(module: Module)
-  : KLesson("Search everywhere", "Search everywhere", module, "Python") {
+  : KLesson("Search everywhere", LessonsBundle.message("python.search.everywhere.lesson.name"), module, "Python") {
   override val lessonContent: LessonContext.() -> Unit = {
     caret(0)
 
     val shift = KeyEvent.getKeyModifiersText(InputEvent.SHIFT_MASK)
     actionTask("SearchEverywhere") {
-      "To open <strong>Search Everywhere</strong> you need to press <shortcut>$shift</shortcut> two times in a row."
+      val shortcut = "<shortcut>$shift</shortcut>"
+      LessonsBundle.message("python.search.everywhere.invoke.search.everywhere", LessonUtil.actionName(it), shortcut)
     }
     task("cae") {
-      text("Suppose you are looking for a class with ${code("cache")} and ${code("extension")} words in the name. " +
-           "Type ${code(it)} (prefixes of required words) to the search field.")
+      text(LessonsBundle.message("python.search.everywhere.type.prefixes", code("cache"), code("extension"), code(it)))
       stateCheck { checkWordInSearch(it) }
       test {
         Thread.sleep(500)
@@ -44,12 +48,11 @@ class PythonSearchEverywhereLesson(module: Module)
         type("f")
         Thread.sleep(500)
       }
-      "We found ${code("FragmentCacheExtension")}. " +
-      "Now you can preview the found item. Just select it by keyboard arrows and press ${action(it)}."
+      LessonsBundle.message("python.search.everywhere.preview", code("FragmentCacheExtension"), action(it))
     }
 
     task {
-      text("Press <strong>Enter</strong> to navigate to ${code("FragmentCacheExtension")}.")
+      text(LessonsBundle.message("python.search.everywhere.navigate.to.class", LessonUtil.rawEnter(), code("FragmentCacheExtension")))
       stateCheck {
         FileEditorManager.getInstance(project).selectedEditor?.file?.name.equals("cache_extension.py")
       }
@@ -61,11 +64,11 @@ class PythonSearchEverywhereLesson(module: Module)
 
     actionTask("GotoClass") {
       // Try to add (class tab in <strong>Search Everywhere</strong>)
-      "Use ${action(it)} to find class faster or in special places."
+      LessonsBundle.message("python.search.everywhere.goto.class", action(it))
     }
 
     task("nodevisitor") {
-      text("Suppose you need some library class responsible for visiting nodes. Type ${code(it)}.")
+      text(LessonsBundle.message("python.search.everywhere.type.node.visitor", code(it)))
       stateCheck { checkWordInSearch(it) }
       test { type(it) }
     }
@@ -74,9 +77,9 @@ class PythonSearchEverywhereLesson(module: Module)
       triggerByUiComponentAndHighlight { _: ActionButtonWithText -> true }
     }
 
-    task("All Places") {
-      text("Now you see a class inside this demo project. " +
-           "Lets Switch <strong>Project Files</strong> filter to <strong>$it</strong> and you will see available library variants.")
+    task(EverythingGlobalScope.getNameText()) {
+      text(LessonsBundle.message("python.search.everywhere.use.all.places",
+                                 strong(ProjectScope.getProjectFilesScopeName()), strong(it)))
       stateCheck {
         (previous.ui as? ActionButtonWithText)?.accessibleContext?.accessibleName == it
       }
@@ -87,12 +90,11 @@ class PythonSearchEverywhereLesson(module: Module)
 
     actionTask("QuickJavaDoc") {
       LearningUiHighlightingManager.clearHighlights()
-      "Use ${action(it)} to quickly look at available documentation."
+      LessonsBundle.message("python.search.everywhere.quick.documentation", action(it))
     }
 
     task {
-      text("<strong>Done!</strong> In the same way you can use ${action("GotoSymbol")} to look for a method or global variable " +
-           "and use ${action("GotoFile")} to look for a file.")
+      text(LessonsBundle.message("python.search.everywhere.finish", action("GotoSymbol"), action("GotoFile")))
     }
 
     if (TaskTestContext.inTestMode) task {
index 4f63e7da858bd2972680ef534c64497c7907cae5..e11c9da2ac3d8c4dbfefb5affb675a4ea9d46b3e 100644 (file)
@@ -5,13 +5,15 @@ import com.intellij.icons.AllIcons
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.*
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import javax.swing.JLabel
 import javax.swing.JPanel
 
-class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.place", "In-place refactoring", module, "Python") {
+class PythonInPlaceRefactoringLesson(module: Module)
+  : KLesson("refactoring.in.place", LessonsBundle.message("python.in.place.refactoring.lesson.name"), module, "Python") {
   private val template = """
     def fibonacci(stop):
         first = 0
@@ -34,8 +36,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
       checkExpectedStateOfEditor(sample) { change -> "econd".startsWith(change) }
 
     task {
-      text("Let's consider an alternative approach to performing refactorings. Suppose we want to rename local variable ${code(variableName)} " +
-           "to ${code("second")}. Just start typing the new name.")
+      text(LessonsBundle.message("python.in.place.refactoring.start.type.new.name", code(variableName), code("second")))
       stateCheck {
         editor.document.text != sample.text && checkFirstChange() == null
       }
@@ -46,9 +47,9 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
     }
 
     task("ShowIntentionActions") {
-      text("IDE is guessing that you are going to rename the variable. " +
-           "You can notice it by the icon ${icon(AllIcons.Gutter.SuggestedRefactoringBulb)} in the left editor gutter. " +
-           "Invoke intentions by ${action(it)} when you finish to type the new name.")
+      text(
+        LessonsBundle.message("python.in.place.refactoring.invoke.intentions",
+                              icon(AllIcons.Gutter.SuggestedRefactoringBulb), action(it)))
       triggerByListItemAndHighlight(highlightBorder = true, highlightInside = false) { ui -> // no highlighting
         ui.toString().contains("Rename usages")
       }
@@ -63,7 +64,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
 
     task {
       val prefix = template.indexOf("<name>")
-      text("Press ${action("EditorEnter")} to finish rename.")
+      text(LessonsBundle.message("python.in.place.refactoring.finish.rename", action("EditorChooseLookupItem")))
       restoreByUi(500)
       stateCheck {
         val newName = newName(editor.document.charsSequence, prefix)
@@ -91,8 +92,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
     }
 
     task {
-      text("Let's add an argument to this method. We place the editor caret just after the first parameter. " +
-           "Now type comma and parameter's name: ${code(", start")} .")
+      text(LessonsBundle.message("python.in.place.refactoring.add.parameter", code(", start")))
       stateCheck {
         val text = editor.document.text
         val parameter = text.substring(secondSample.startOffset, text.indexOf(')'))
@@ -106,9 +106,8 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
     }
 
     task("ShowIntentionActions") {
-      text("IDE is guessing that you are going to change the method signature. " +
-           "You can notice it by the same icon ${icon(AllIcons.Gutter.SuggestedRefactoringBulb)} at the left editor gutter. " +
-           "Invoke intentions by ${action(it)} when you finish typing the new parameter.")
+      text(LessonsBundle.message("python.in.place.refactoring.invoke.intention.for.parameter",
+                                 icon(AllIcons.Gutter.SuggestedRefactoringBulb), action(it)))
       triggerByListItemAndHighlight(highlightBorder = true, highlightInside = false) { item ->
         item.toString().contains("Update usages to")
       }
@@ -122,7 +121,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
     }
 
     task {
-      text("Press ${action("EditorEnter")} to update the callers.")
+      text(LessonsBundle.message("python.in.place.refactoring.update.callers", action("EditorChooseLookupItem")))
       triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { ui: JPanel -> // no highlighting
         ui.javaClass.name.contains("ChangeSignaturePopup")
       }
@@ -131,7 +130,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
     }
 
     task {
-      text("IDE is showing you the short signature preview. Press ${action("EditorEnter")} to continue.")
+      text(LessonsBundle.message("python.in.place.refactoring.signature.preview", LessonUtil.rawEnter()))
       triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { ui: JLabel -> // no highlighting
         ui.text == "Add values for new parameters:"
       }
@@ -143,8 +142,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
       before {
         beforeSecondRefactoring = editor.document.text
       }
-      text("Now you need to type the value which will be inserted as an argument into the each call. " +
-           "You can choose ${code("0")} for this sample. Then press ${action("EditorEnter")} to continue.")
+      text(LessonsBundle.message("python.in.place.refactoring.set.default.value", code("0"),LessonUtil.rawEnter()))
       restoreByUi()
       stateCheck {
         editor.document.text != beforeSecondRefactoring && Thread.currentThread().stackTrace.any {
@@ -156,8 +154,7 @@ class PythonInPlaceRefactoringLesson(module: Module) : KLesson("refactoring.in.p
         GuiTestUtil.shortcut(Key.ENTER)
       }
     }
-    task { text("A small note for the end. In-place refactoring may be applied only in the definition point whiles direct invocation" +
-                " of rename or change-signature refactorings may be called from both definition and usage.") }
+    task { text(LessonsBundle.message("python.in.place.refactoring.remark.about.application.scope")) }
   }
 
   private fun newName(text: CharSequence, prefix: Int): String {
index a1444e85b229f3daac94e2a1e4897590cf32da38..964b3d1285f3dbceb9853d23345cf9092f1a3011 100644 (file)
@@ -4,26 +4,26 @@ package training.learn.lesson.python.refactorings
 import com.intellij.openapi.application.ModalityState
 import com.intellij.openapi.application.invokeAndWaitIfNeeded
 import com.intellij.openapi.editor.impl.EditorComponentImpl
-import com.intellij.openapi.util.SystemInfo
 import com.intellij.openapi.wm.IdeFocusManager
+import com.intellij.refactoring.RefactoringBundle
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.impl.button
 import com.intellij.testGuiFramework.util.Key
 import com.intellij.util.ui.UIUtil
 import com.intellij.util.ui.table.JBTableRow
+import com.jetbrains.python.PyBundle
 import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix
 import training.commands.kotlin.TaskContext
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
-import training.learn.lesson.kimpl.KLesson
-import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.LessonUtil
+import training.learn.lesson.kimpl.*
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
-import training.learn.lesson.kimpl.parseLessonSample
 import javax.swing.JDialog
 import javax.swing.JTable
 
-class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.quick.fix", "Quick fix refactoring", module, "Python") {
+class PythonQuickFixesRefactoringLesson(module: Module)
+  : KLesson("refactoring.quick.fix", LessonsBundle.message("python.quick.fix.refactoring.lesson.name"), module, "Python") {
   private val sample = parseLessonSample("""
     def foo(x):
         print("Hello ", x)
@@ -36,8 +36,7 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
   override val lessonContent: LessonContext.() -> Unit = {
     prepareSample(sample)
     task {
-      text("Several refactorings can be performed as quick fixes. Suppose we want to add a parameter to the method ${code("foo")} " +
-           "and pass the variable ${code("y")} to it. Let's type ${code(", y")} after the first argument.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.type.new.argument", code("foo"), code("y"), code(", y")))
       stateCheck {
         editor.document.text == StringBuilder(sample.text).insert(sample.startOffset, ", y").toString()
       }
@@ -46,7 +45,7 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
     }
 
     task {
-      text("Wait a little bit for the completion list...")
+      text(LessonsBundle.message("python.quick.fix.refactoring.wait.completion.showed"))
       triggerByListItemAndHighlight(highlightBorder = false, highlightInside = false) { item ->
         item.toString().contains("string=y")
       }
@@ -54,7 +53,7 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
     }
 
     task {
-      text("For now, we don't want to apply any completion. Close the list (${action("EditorEscape")}).")
+      text(LessonsBundle.message("python.quick.fix.refactoring.close.completion.list", action("EditorEscape")))
       stateCheck { previous.ui?.isShowing != true }
       proposeMyRestore()
       test { GuiTestUtil.shortcut(Key.ESCAPE) }
@@ -65,7 +64,7 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
     }
 
     task("ShowIntentionActions") {
-      text("As you may notice, IDE is showing you a warning here. Let's invoke intentions by ${action(it)}.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.invoke.intentions", action(it)))
       triggerByListItemAndHighlight(highlightBorder = true, highlightInside = false) { item ->
         item.toString().contains("Change signature of")
       }
@@ -78,9 +77,10 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
       }
     }
     task {
-      text("Choose <strong>Change signature</strong> quick fix.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.choose.change.signature",
+                                 strong(PyBundle.message("QFIX.NAME.change.signature"))))
 
-      triggerByPartOfComponent { table : JTable ->
+      triggerByPartOfComponent { table: JTable ->
         val model = table.model
         if (model.rowCount >= 2 && (model.getValueAt(1, 0) as? JBTableRow)?.getValueAt(0) == "y") {
           table.getCellRect(1, 0, true)
@@ -94,13 +94,13 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
       }
     }
     task {
-      text("Let's set the default value for the new parameter. Click at the new parameter line. " +
-           "Alternatively, you can set focus to the parameter without mouse by ${action("EditorTab")} and then ${action("EditorEnter")}.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.select.new.parameter",
+                                 action("EditorTab"), LessonUtil.rawEnter()))
 
       val selector = { collection: Collection<EditorComponentImpl> ->
         collection.takeIf { it.size > 2 }?.maxBy { it.locationOnScreen.x }
       }
-      triggerByUiComponentAndHighlight(selector = selector) { editor : EditorComponentImpl ->
+      triggerByUiComponentAndHighlight(selector = selector) { editor: EditorComponentImpl ->
         UIUtil.getParentOfType(JDialog::class.java, editor) != null
       }
       restoreByUi()
@@ -113,9 +113,7 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
       }
     }
     task {
-      text("You may navigate through the fields (and the checkbox) by using ${action("EditorTab")}. " +
-           "With the checkbox you can let IDE inline the default value to the other callers or set it as the default value for the new parameter. " +
-           "The Signature Preview will help understand the difference. Now set the default value as 0.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.set.default.value", action("EditorTab")))
       restoreByUi()
       stateCheck {
         (previous.ui as? EditorComponentImpl)?.text == "0"
@@ -138,8 +136,8 @@ class PythonQuickFixesRefactoringLesson(module: Module) : KLesson("refactoring.q
       before {
         beforeRefactoring = editor.document.text
       }
-      text("Press <raw_action>${if (SystemInfo.isMacOSMojave) "\u2318\u23CE" else "Ctrl + Enter"}</raw_action> " +
-           "(or click <strong>Refactor</strong>) to finish the refactoring.")
+      text(LessonsBundle.message("python.quick.fix.refactoring.finish.refactoring",
+                                 LessonUtil.rawCtrlEnter(), strong(RefactoringBundle.message("refactor.button").dropMnemonic())))
 
       stateCheck {
         val b = editor.document.text != beforeRefactoring
index 68df1d1af1c4487eb7bf70401b586decc41f0834..938edfbd3d24d6518058a475c7ff3f52f949dea3 100644 (file)
@@ -1,18 +1,18 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.python.refactorings
 
+import com.intellij.idea.ActionsBundle
 import com.intellij.refactoring.rename.inplace.InplaceRefactoring
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
-import training.learn.lesson.kimpl.KLesson
-import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.parseLessonSample
+import training.learn.lesson.kimpl.*
 import javax.swing.JList
 
 class PythonRefactorMenuLesson(module: Module)
-  : KLesson("Refactoring menu", "Refactoring menu", module, "Python") {
+  : KLesson("Refactoring menu", LessonsBundle.message("refactoring.menu.lesson.name"), module, "Python") {
   private val sample = parseLessonSample("""
     # Need to think about better sample!
     import random
@@ -25,22 +25,21 @@ class PythonRefactorMenuLesson(module: Module)
   override val lessonContent: LessonContext.() -> Unit = {
     prepareSample(sample)
     actionTask("Refactorings.QuickListPopupAction") {
-      "PyCharm supports a variety of refactorings. Many of them have own shortcuts. " +
-      "But for rare refactorings you can use ${action(it)} and see a partial list of them."
+      LessonsBundle.message("python.refactoring.menu.show.refactoring.list", action(it))
     }
-    task("Introduce Parameter") {
-      text("Suppose we want to replace this expression with a parameter. So we need to choose <strong>$it...</strong>. " +
-           "Now simply type ${code("pa")} (as prefix of Parameter) to reduce proposed list.")
+    task(ActionsBundle.message("action.IntroduceParameter.text").dropMnemonic()) {
+      val prefix = LessonsBundle.message("python.refactoring.menu.required.prefix")
+      text(LessonsBundle.message("python.refactoring.menu.introduce.parameter", strong(it), strong(prefix)))
       triggerByUiComponentAndHighlight(highlightBorder = false, highlightInside = false) { ui: JList<*> ->
         ui.model.size > 0 && ui.model.getElementAt(0).toString().contains(it)
       }
       test {
-        type("pa")
+        type(prefix)
       }
     }
 
     task {
-      text("Press ${action("EditorEnter")} to start Introduce Parameter refactoring.")
+      text(LessonsBundle.message("python.refactoring.menu.start.refactoring", action("EditorChooseLookupItem")))
       trigger("IntroduceParameter")
       stateCheck { hasInplaceRename() }
       test {
@@ -49,7 +48,7 @@ class PythonRefactorMenuLesson(module: Module)
     }
 
     task {
-      text("To complete refactoring, you need to choose some name or leave it as default and press ${action("EditorEnter")}.")
+      text(LessonsBundle.message("python.refactoring.menu.finish.refactoring", LessonUtil.rawEnter()))
       stateCheck {
         !hasInplaceRename()
       }
index 22062f30d894e11df844f1253b1219cf8b5d0275..80867301f8830fe3d396dfe185ab7ad643c8a66d 100644 (file)
@@ -4,6 +4,7 @@ package training.learn.lesson.python.refactorings
 import com.intellij.ide.DataManager
 import com.intellij.ide.actions.exclusion.ExclusionHandler
 import com.intellij.openapi.application.runReadAction
+import com.intellij.refactoring.RefactoringBundle
 import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.framework.Timeouts
 import com.intellij.testGuiFramework.impl.button
@@ -13,9 +14,11 @@ import com.intellij.ui.tree.TreeVisitor
 import com.intellij.util.ui.tree.TreeUtil
 import org.jetbrains.annotations.Nullable
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
+import training.learn.lesson.kimpl.dropMnemonic
 import training.learn.lesson.kimpl.parseLessonSample
 import java.util.regex.Pattern
 import javax.swing.JButton
@@ -23,7 +26,7 @@ import javax.swing.JTree
 import javax.swing.tree.TreePath
 
 class PythonRenameLesson(module: Module)
-  : KLesson("Rename", "Rename", module, "Python") {
+  : KLesson("Rename", LessonsBundle.message("rename.lesson.name"), module, "Python") {
   private val template = """
       class Championship:
           def __init__(self):
@@ -66,7 +69,7 @@ class PythonRenameLesson(module: Module)
     prepareSample(sample)
     var replace: String? = null
     task("RenameElement") {
-      text("Press ${action(it)} to rename field <code>teams</code> (e.g., to <code>teams_number</code>).")
+      text(LessonsBundle.message("python.rename.press.rename", action(it), code("teams"), code("teams_number")))
       triggerByFoundPathAndHighlight { tree: JTree, path: TreePath ->
         if (path.pathCount == 2 && path.getPathComponent(1).toString().contains("Dynamic")) {
           replace = replacePreviewPattern.matcher(tree.model.root.toString()).takeIf { m -> m.find() }?.group(1)
@@ -92,8 +95,9 @@ class PythonRenameLesson(module: Module)
           TreeUtil.collapseAll(tree, 1)
         }
       }
-      text("In simple cases PyCharm will just rename without confirmation. But in this sample PyCharm sees two calls of  " +
-           "${code("teams")} method for objects with unknown type. Expand <strong>Dynamic references</strong> item.")
+      val dynamicReferencesString = LessonsBundle.message("python.rename.dynamic.references.prefix")
+      text(LessonsBundle.message("python.rename.expand.dynamic.references",
+                                 code("teams"), strong("dynamicReferencesString")))
 
       triggerByFoundPathAndHighlight { _: JTree, path: TreePath ->
         path.pathCount == 6 && path.getPathComponent(5).toString().contains("company_members")
@@ -101,7 +105,7 @@ class PythonRenameLesson(module: Module)
       test {
         ideFrame {
           val jTree = runReadAction {
-            jTree("Dynamic references", timeout = Timeouts.seconds03, predicate = substringPredicate)
+            jTree(dynamicReferencesString, timeout = Timeouts.seconds03, predicate = substringPredicate)
           }
           // WARNING: several exception will be here because of UsageNode#toString inside info output during this operation
           jTree.doubleClickPath()
@@ -110,8 +114,7 @@ class PythonRenameLesson(module: Module)
     }
 
     task {
-      text("It seems ${code("company_members")} should be excluded from rename. " +
-           "Select it and press ${action("EditorDelete")}.")
+      text(LessonsBundle.message("python.rename.exclude.item", code("company_members"), action("EditorDelete")))
 
       stateCheck {
         val tree = previous.ui as? JTree ?: return@stateCheck false
@@ -133,19 +136,20 @@ class PythonRenameLesson(module: Module)
       }
     }
 
+    val confirmRefactoringButton = RefactoringBundle.message("usageView.doAction").dropMnemonic()
     task {
       triggerByUiComponentAndHighlight(highlightInside = false) { button: JButton ->
-        button.text == "Do Refactor"
+        button.text == confirmRefactoringButton
       }
     }
 
     task {
       val result = replace?.let { template.replace("<name>", it).replace("<caret>", "") }
-      text("Now just finish the rename with the <strong>Do Refactor</strong> button.")
+      text(LessonsBundle.message("python.rename.finish.refactoring", strong(confirmRefactoringButton)))
       stateCheck { editor.document.text == result }
       test {
         ideFrame {
-          button("Do Refactor").click()
+          button(confirmRefactoringButton).click()
         }
       }
     }
index 0da24332db45166c25c6f7270c862fa3c4228045..6bcd02c3dd9885e2ec9e2878d9f6713805f20dbf 100644 (file)
@@ -3,6 +3,7 @@ package training.learn.lesson.python.run
 
 import com.intellij.icons.AllIcons
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.general.run.CommonDebugLesson
 import training.learn.lesson.kimpl.LessonContext
@@ -25,7 +26,7 @@ class PythonDebugLesson(module: Module) : CommonDebugLesson(module, "python.debu
         mayBeStopped = true
       }
       proposeModificationRestore(afterFixText)
-      "Let's rerun our program. Just click again at ${icon(AllIcons.Actions.Restart)} or use ${action(it)}."
+      LessonsBundle.message("python.debug.workflow.rerun", icon(AllIcons.Actions.Restart), action(it))
     }
   }
 
index 4bfcd4bdd4d17b24b19d524abb7e26be1f5bdf96..76e17f9c3c0d3ede4f54901c0aa68006cab0db6c 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.python.run
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.general.run.CommonRunConfigurationLesson
 import training.learn.lesson.kimpl.LessonContext
@@ -13,7 +14,7 @@ class PythonRunConfigurationLesson(module: Module) : CommonRunConfigurationLesso
 
   override fun LessonContext.runTask() {
     task("RunClass") {
-      text("Let's run our simple example with ${action(it)}.")
+      text(LessonsBundle.message("python.run.configuration.lets.run", action(it)))
       //Wait toolwindow
       toolWindowShowed("Run")
       stateCheck {
index 2f004c5202ff6519c8f77cab597e8ecd30638a5c..6bb8eac47202f318410920267dabfaca8a628347 100644 (file)
@@ -1,13 +1,14 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.ruby.completion
 
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class RubyHippieCompletionLesson(module: Module)
-  : KLesson("Hippie Completion", "Hippie Completion", module, "ruby") {
+  : KLesson("Hippie Completion", LessonsBundle.message("ruby.hippie.completion.lesson.name"), module, "ruby") {
 
   private val sample = parseLessonSample("""class SomeExampleClass
   attr_reader :callbacks
@@ -26,17 +27,17 @@ end
       val step1 = calculateResult("fore_save")
       val step2 = calculateResult("fore_create")
       task("HippieCompletion") {
-        text("Sometimes you need to complete a word by textual similarity. Press ${action(it)} to invoke hippie completion.")
+        text(LessonsBundle.message("ruby.hippie.completion.invoke.hippie.completion", action(it)))
         trigger(it) { editor.document.text == step1 }
         test { actions(it) }
       }
       task("HippieCompletion") {
-        text("You can repeat ${action(it)} until the desired word is found. Try it once more now.")
+        text(LessonsBundle.message("ruby.hippie.completion.repeat.one.time", action(it)))
         trigger(it) { editor.document.text == step2 }
         test { actions(it) }
       }
       task("HippieBackwardCompletion") {
-        text("You can return to the previous variant with ${action(it)}. Use it now.")
+        text(LessonsBundle.message("ruby.hippie.completion.return.previous", action(it)))
         trigger(it) { editor.document.text == step1 }
         test { actions(it) }
       }
index d38dbaed23745dd7d015427b363a1b78bff5e805..980030d5cfc256a3d80b3f627f73e6b36b88f96d 100644 (file)
@@ -3,15 +3,15 @@ package training.learn.lesson.ruby.completion
 
 import com.intellij.testGuiFramework.framework.GuiTestUtil.typeText
 import com.intellij.testGuiFramework.impl.jList
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
-import training.learn.lesson.kimpl.LessonUtil
 import training.learn.lesson.kimpl.LessonUtil.checkExpectedStateOfEditor
 import training.learn.lesson.kimpl.parseLessonSample
 
 class RubyPostfixCompletionLesson(module: Module)
-  : KLesson("Postfix completion", "Postfix completion", module, "ruby") {
+  : KLesson("Postfix completion", LessonsBundle.message("postfix.completion.lesson.name"), module, "ruby") {
 
   private val sample = parseLessonSample("""class SomeExampleClass
   # @param string_array [Array<String>]
@@ -34,7 +34,7 @@ end
     get() = {
       prepareSample(sample)
       task {
-        text("The IDE can offer postfix shortcuts. Type ${code(".if")}.")
+        text(LessonsBundle.message("postfix.completion.type.template", code(".if")))
         triggerByListItemAndHighlight {
           it.toString() == ".if"
         }
@@ -48,7 +48,7 @@ end
         }
       }
       task {
-        text("Now just press ${action("EditorEnter")} to choose the first postfix template.")
+        text(LessonsBundle.message("ruby.postfix.completion.apply", action("EditorChooseLookupItem")))
         triggerByListItemAndHighlight {
           it.toString() == "string_array.length > 1"
         }
@@ -61,7 +61,7 @@ end
       }
 
       task("string_array.length > 1") {
-        text("Now choose the second item, ${code(it)}.")
+        text(LessonsBundle.message("ruby.postfix.completion.choose.target", code(it)))
         stateCheck { editor.document.text == result }
         restoreByUi()
         test {
index 7480ffc3d4cde22487aad04b44f2f6899111d2e9..7288816b55c45f8c838863d26c3aec1132cdddc1 100644 (file)
@@ -6,38 +6,38 @@ import com.intellij.testGuiFramework.framework.GuiTestUtil
 import com.intellij.testGuiFramework.util.Key
 import com.intellij.ui.components.fields.ExtendableTextField
 import training.commands.kotlin.TaskRuntimeContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
+import training.learn.lesson.kimpl.LessonUtil
 
 class RubyClassSearchLesson(module: Module)
-  : KLesson("Class Search", "Class Search", module, "ruby") {
+  : KLesson("Class Search", LessonsBundle.message("ruby.class.search.lesson.name"), module, "ruby") {
   override val lessonContent: LessonContext.() -> Unit
     get() = {
       caret(0)
 
       actionTask("GotoClass") {
-        "Try to find a class with ${action(it)}"
+        LessonsBundle.message("ruby.class.search.invoke.goto.class", action(it))
       }
       task("date") {
-        text("Type <code>$it</code> to see classes that contain the word <strong>$it</strong>.")
+        text(LessonsBundle.message("ruby.class.search.type.word", code(it), strong(it)))
         stateCheck { checkWordInSearch(it) }
         test { type(it) }
       }
       task("datebe") {
-        text("You can search for a class by part of its name. Type <code>be</code> (the search string will be <code>$it</code>) " +
-             "to see classes that contain the words <strong>date</strong> and <strong>be</strong>.")
+        text(LessonsBundle.message("ruby.class.search.type.second.prefix", code("be"), code(it), strong("date"), strong("be")))
         stateCheck { checkWordInSearch(it) }
         test { type("be") }
       }
       task("QuickImplementations") {
-        text("To check the selected class before navigating to it, you can use ${action(it)} to see its quick definition.")
+        text(LessonsBundle.message("ruby.class.search.preview", action(it)))
         trigger(it)
         test { actions(it) }
       }
       task {
-        text("Suppose you are looking for ${code("DateAndTimeBehavior")}." +
-             "Choose it and then press <strong>Enter</strong> to navigate.")
+        text(LessonsBundle.message("ruby.class.search.navigate.to.target", code("DateAndTimeBehavior"), LessonUtil.rawEnter()))
         stateCheck {
           FileEditorManager.getInstance(project).selectedEditor?.file?.name.equals("date_and_time_behavior.rb")
         }
index 52214c74bccbc008c30d59d1f3d9c8cbf881fb2d..e6b78650d9ad2f36cb277367d8ee77ddc889064c 100644 (file)
@@ -7,6 +7,6 @@ import training.learn.lesson.kimpl.LessonContext
 
 class RubyDeclarationAndUsagesLesson(module: Module) : DeclarationAndUsagesLesson(module, "ruby") {
   override fun LessonContext.setInitialPosition() = caret(20, 45)
-  override val typeOfEntity = "an attribute accessor"
+  override val typeOfEntity = 1
   override val existedFile: String = "lib/active_support/core_ext/date/calculations.rb"
 }
index d48797b96e533df03acda797a8ed4803ce57fc13..5a2aea8a8782f1b79d08a741e85cbfc9375537e6 100644 (file)
@@ -2,19 +2,21 @@
 package training.learn.lesson.ruby.refactorings
 
 import com.intellij.openapi.project.Project
+import com.intellij.refactoring.RefactoringBundle
 import com.intellij.testGuiFramework.impl.button
 import com.intellij.testGuiFramework.impl.jList
 import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Types
 import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.fqn.FQN
 import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.SymbolUtil
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
 import training.learn.lesson.kimpl.parseLessonSample
 
 class RubyRefactorMenuLesson(module: Module)
-  : KLesson("Refactoring menu", "Refactoring menu", module, "ruby") {
+  : KLesson("Refactoring menu", LessonsBundle.message("refactoring.menu.lesson.name"), module, "ruby") {
 
   private val sample = parseLessonSample("""
     class Animal
@@ -35,11 +37,10 @@ class RubyRefactorMenuLesson(module: Module)
     get() = {
       prepareSample(sample)
       actionTask("Refactorings.QuickListPopupAction") {
-        "RubyMine supports a variety of refactorings. Press ${action(it)} to see a partial list of them."
+        LessonsBundle.message("ruby.refactoring.menu.invoke.refactoring.list", action(it))
       }
-      task("Push Members Down") {
-        text("Some refactorings are seldom used and have no shortcut, but you can find them here. " +
-             "Choose <strong>$it...</strong> now and complete the refactoring on <code>meow()</code>.")
+      task(RefactoringBundle.message("push.members.down.title")) {
+        text(LessonsBundle.message("ruby.refactoring.menu.use.push.method.down", strong(it), code("meow()")))
         trigger("MemberPushDown") { checkMethodMoved(project) }
         test {
           ideFrame {
index 0eb40308ac5cb71be759dc996209e0a3f20e94e9..870b7b29955d9e2b01dd0bd4d29d1bae6c451056 100644 (file)
@@ -1,19 +1,22 @@
 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package training.learn.lesson.ruby.refactorings
 
+import com.intellij.refactoring.RefactoringBundle
 import com.intellij.testGuiFramework.impl.button
 import com.intellij.ui.treeStructure.Tree
 import training.commands.kotlin.TaskTestContext
+import training.learn.LessonsBundle
 import training.learn.interfaces.Module
 import training.learn.lesson.kimpl.KLesson
 import training.learn.lesson.kimpl.LessonContext
+import training.learn.lesson.kimpl.dropMnemonic
 import training.learn.lesson.kimpl.parseLessonSample
 import java.util.concurrent.Future
 import java.util.concurrent.TimeUnit
 import java.util.regex.Pattern
 
 class RubyRenameLesson(module: Module)
-  : KLesson("Rename", "Rename", module, "ruby") {
+  : KLesson("Rename", LessonsBundle.message("rename.lesson.name"), module, "ruby") {
 
   private val template = """
     class Championship
@@ -47,7 +50,7 @@ class RubyRenameLesson(module: Module)
       prepareSample(sample)
       lateinit var replace: Future<String>
       task("RenameElement") {
-        text("Press ${action(it)} to rename the attribute accessor <code>teams</code> (e.g., to <code>teams_number</code>).")
+        text(LessonsBundle.message("ruby.rename.start.refactoring", action(it), code("teams"), code("teams_number")))
         replace = stateRequired {
           (focusOwner as? Tree)?.model?.root?.toString()?.let { root: String ->
             replacePreviewPattern.matcher(root).takeIf { m -> m.find() }?.group(1)
@@ -63,13 +66,12 @@ class RubyRenameLesson(module: Module)
           }
         }
       }
-      task("Do Refactor") {
+      task(RefactoringBundle.message("usageView.doAction").dropMnemonic()) {
         var result = ""
         before {
           result = template.replace("<name>", replace.get(2, TimeUnit.SECONDS)).replace("<caret>", "")
         }
-        text("In order to be confident about the refactoring, RubyMine lets you preview it before confirming." +
-             "Click <strong>$it</strong> to complete the refactoring.")
+        text(LessonsBundle.message("ruby.rename.confirm", strong(it)))
         stateCheck { editor.document.text == result }
         test {
           ideFrame {