From 86d7c88e7503634aa725be9bfe1577f0c8843574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9lio=20Guilherme?= Date: Mon, 1 Jan 2024 23:35:03 +0000 Subject: [PATCH] Add support for localized test suites and UI (#2678) * Improve reader. DEBUG * Improve reader. * Add check_file_language. Working in project.load_data, but not when opening files. * Add condition fro lang in model * First success opening laang_pt and lang_fr tests * Make unit tests pass * Improve lang headers, pt and fr * Add unit tests. All lnguages tests can be opened. Missing lib lang fixes * Identify where language can be checked from TextEditor * More tables using language * Working opening with lang set. Tests for multiple langs * Working opening directories with different lang files * Make __init__.robot open with language set. WIP, copy _aliases to _settings * OK Opens from directories. * Fixed open test suites directories. Need utests and fix comments * Fix opening directly __init__.robot * Cleanup code * Improve opening of Resource files * Open pt folder, fail en folder. Failed comment blocks at formatter level * Add order of sections. Still broken en directories, and missing memory clear on open * Fix some utests * Fix unit tests * Remove inner comments from the en test directory * Success in multiple Comments sections. Missing isolated comments * Fix # isolated comments and Comments section * Settings Dialogs OK. Need localizaation. Variables is debug * Adjust buttons in table settings to open dialogs for Tests and Tasks * Fix all settings Edit buttons * Fix continuation lines in Documentation other than En * Improve setting of language in TextEditor * Fix Library translated labels * I18n colorization (#143) * Initial modification of pygments. Missing Gerkin * Remove nolower * Almost done. Missing Gherkin and Documentation setting color not OK * Fixed Gherkin calls. Still to fix TestTableSettings Setup/Teardown/Template keywords * Final fixes for colorization * Fix error when opening directories with log or report html files * Update docs and version * Add validator from RF 6.1.1 to Text Edit. WIP fails to save language files * Add new validator * Fix saving modified files with language setting * Fix help dialog for Pygments * Add protection to shared_memory language setting * Remove extra spaces from lines in Comments sections * Remove support for HTML test files. WIP * Make own pygments a module * Fix broken sincronization of changes in Text Editor * Add minimal protection to RF lower than 6.0 * Add tooltip to settings fields localized names * Notes about tooltips * Add language selector to Preferences->General * Initial structure for localization change * Add first example de localized strings * Add test from W2 * Add more debugging for localization * Fix pause translated commands * Update translations for TestRunner * Fix some unit tests * Fix import failed XML * Fix unit test * Fix more translations in in Tests and in Unudrd Keywords * Fix default ui language, Variables and Metadata editors * Improvement in language save or RIDE start * Add pt_BR translation * DEBUG Save from Text Editor --- CHANGELOG.adoc | 10 + README.adoc | 6 +- setup.py | 4 +- src/robotide/action/actioninfo.py | 39 +- src/robotide/application/CHANGELOG.html | 44 +- src/robotide/application/application.py | 68 +- src/robotide/application/releasenotes.py | 43 +- src/robotide/application/updatenotifier.py | 50 +- src/robotide/context/coreplugins.py | 3 +- .../contrib/testrunner/runprofiles.py | 47 +- .../contrib/testrunner/testrunnerplugin.py | 172 +- src/robotide/controller/basecontroller.py | 6 +- src/robotide/controller/dataloader.py | 106 +- src/robotide/controller/filecontrollers.py | 19 +- src/robotide/controller/project.py | 51 +- src/robotide/controller/robotdata.py | 2 +- src/robotide/controller/settingcontrollers.py | 2 +- src/robotide/controller/ui/treecontroller.py | 37 +- src/robotide/editor/__init__.py | 79 +- src/robotide/editor/contentassist.py | 8 +- src/robotide/editor/customsourceeditor.py | 2 +- src/robotide/editor/editordialogs.py | 14 +- src/robotide/editor/editors.py | 22 +- src/robotide/editor/fieldeditors.py | 14 +- src/robotide/editor/gridcolorizer.py | 8 +- src/robotide/editor/kweditor.py | 153 +- src/robotide/editor/listeditor.py | 18 +- src/robotide/editor/macroeditors.py | 2 +- src/robotide/editor/popupwindow.py | 2 +- src/robotide/editor/settingeditors.py | 116 +- src/robotide/editor/texteditor.py | 218 ++- src/robotide/editor/tooltips.py | 2 +- src/robotide/lib/compat/__init__.py | 14 + src/robotide/lib/compat/parsing/__init__.py | 15 + src/robotide/lib/compat/parsing/language.py | 192 ++ src/robotide/lib/compat/parsing/validator.py | 13 + src/robotide/lib/compat/pygments/__init__.py | 14 + .../lib/compat/pygments/robotframework.py | 862 +++++++++ src/robotide/lib/robot/parsing/datarow.py | 6 +- src/robotide/lib/robot/parsing/htmlreader.py | 3 +- src/robotide/lib/robot/parsing/model.py | 363 +++- src/robotide/lib/robot/parsing/populators.py | 88 +- src/robotide/lib/robot/parsing/restreader.py | 7 +- src/robotide/lib/robot/parsing/robotreader.py | 26 +- src/robotide/lib/robot/parsing/settings.py | 31 +- .../lib/robot/parsing/tablepopulators.py | 41 +- src/robotide/lib/robot/utils/__init__.py | 2 +- src/robotide/lib/robot/utils/normalizing.py | 12 + .../lib/robot/writer/dataextractor.py | 5 +- .../lib/robot/writer/datafilewriter.py | 26 +- src/robotide/lib/robot/writer/filewriters.py | 34 +- src/robotide/lib/robot/writer/formatters.py | 21 +- src/robotide/lib/robot/writer/rowsplitter.py | 8 +- src/robotide/locale/RIDE.pot | 1491 +++++++++++++++ src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.mo | Bin 0 -> 33087 bytes src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.po | 1648 +++++++++++++++++ src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.mo | Bin 0 -> 31299 bytes src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.po | 1648 +++++++++++++++++ src/robotide/log/log.py | 11 +- src/robotide/namespace/namespace.py | 14 +- src/robotide/parserlog/parserlog.py | 11 +- src/robotide/postinstall/desktopshortcut.py | 11 +- src/robotide/preferences/__init__.py | 19 + src/robotide/preferences/general.py | 33 + src/robotide/preferences/settings.cfg | 12 +- src/robotide/recentfiles/recentfiles.py | 2 +- src/robotide/run/configmanagerui.py | 17 +- src/robotide/run/runanything.py | 14 +- src/robotide/searchtests/dialogsearchtests.py | 52 +- src/robotide/searchtests/searchtests.py | 13 +- src/robotide/spec/specimporter.py | 18 +- src/robotide/ui/actiontriggers.py | 22 +- src/robotide/ui/excludes_dialogs.py | 4 +- src/robotide/ui/filedialogs.py | 8 +- src/robotide/ui/fileexplorerplugin.py | 2 +- src/robotide/ui/keywordsearch.py | 47 +- src/robotide/ui/mainframe.py | 216 ++- src/robotide/ui/notebook.py | 2 +- src/robotide/ui/preferences_dialogs.py | 4 +- src/robotide/ui/preview.py | 20 +- src/robotide/ui/review.py | 95 +- src/robotide/ui/tagdialogs.py | 59 +- src/robotide/ui/treenodehandlers.py | 40 +- src/robotide/ui/treeplugin.py | 29 +- src/robotide/usages/usagesdialog.py | 16 +- src/robotide/version.py | 2 +- src/robotide/widgets/button.py | 9 +- src/robotide/widgets/popupmenu.py | 15 +- tools/geni18n.py | 72 + utest/action/test_action_dsl.py | 2 +- utest/controller/controller_creator.py | 2 +- .../controller/test_all_files_can_be_seen.py | 1 + utest/controller/test_format_change.py | 4 +- .../controller/test_modifications_on_disk.py | 2 +- utest/controller/test_occurrences.py | 12 +- utest/controller/test_tablecontrollers.py | 2 +- utest/editor/test_texteditor.py | 4 + utest/editor/test_z_editor_plugin.py | 4 + utest/language/__init__.py | 23 + utest/language/test_language.py | 153 ++ utest/namespace/test_namespace.py | 1 + utest/resources/datafilereader.py | 28 +- .../robotdata/language/en/__init__.robot | 35 + .../robotdata/language/en/full_en.json | 5 + .../robotdata/language/en/full_en.py | 4 + .../robotdata/language/en/full_en.resource | 58 + .../robotdata/language/en/full_en.yaml | 7 + .../robotdata/language/en/full_task_en.robot | 81 + .../robotdata/language/en/full_test_en.robot | 81 + .../robotdata/language/en/gherkin_en.robot | 33 + .../robotdata/language/lang_bg.robot | 14 + .../robotdata/language/lang_bs.robot | 14 + .../robotdata/language/lang_cs.robot | 14 + .../robotdata/language/lang_de.robot | 14 + .../robotdata/language/lang_en.robot | 15 + .../robotdata/language/lang_es.robot | 14 + .../robotdata/language/lang_fi.robot | 14 + .../robotdata/language/lang_fr.robot | 14 + .../robotdata/language/lang_hi.robot | 14 + .../robotdata/language/lang_it.robot | 14 + .../robotdata/language/lang_nl.robot | 14 + .../robotdata/language/lang_pl.robot | 26 + .../robotdata/language/lang_pt.robot | 21 + .../robotdata/language/lang_pt_br.robot | 17 + .../robotdata/language/lang_ro.robot | 14 + .../robotdata/language/lang_ru.robot | 17 + .../robotdata/language/lang_sv.robot | 14 + .../robotdata/language/lang_th.robot | 14 + .../robotdata/language/lang_tr.robot | 14 + .../robotdata/language/lang_uk.robot | 14 + .../robotdata/language/lang_vi.robot | 14 + .../robotdata/language/lang_zh_cn.robot | 14 + .../robotdata/language/lang_zh_tw.robot | 14 + .../robotdata/language/preamble_no_lang.robot | 13 + .../language/preamble_unknown_lang.robot | 8 + .../robotdata/language/pt/__init__.robot | 39 + .../robotdata/language/pt/full_pt.json | 4 + .../robotdata/language/pt/full_pt.py | 3 + .../robotdata/language/pt/full_pt.resource | 62 + .../robotdata/language/pt/full_pt.yaml | 7 + .../robotdata/language/pt/full_task_pt.robot | 87 + .../robotdata/language/pt/full_test_pt.robot | 99 + .../language/pt/full_test_single_pt.robot | 114 ++ .../robotdata/language/pt/gherkin_pt.robot | 35 + utest/spec/test_iteminfo.py | 4 +- utest/ui/test_tagdialogs.py | 4 +- utest/ui/test_tree.py | 4 +- utest/widgets/test_popupmenu.py | 2 +- 148 files changed, 9205 insertions(+), 996 deletions(-) create mode 100644 src/robotide/lib/compat/__init__.py create mode 100644 src/robotide/lib/compat/parsing/__init__.py create mode 100644 src/robotide/lib/compat/parsing/language.py create mode 100644 src/robotide/lib/compat/parsing/validator.py create mode 100644 src/robotide/lib/compat/pygments/__init__.py create mode 100644 src/robotide/lib/compat/pygments/robotframework.py create mode 100644 src/robotide/locale/RIDE.pot create mode 100644 src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.mo create mode 100644 src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.po create mode 100644 src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.mo create mode 100644 src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.po create mode 100644 tools/geni18n.py create mode 100644 utest/language/__init__.py create mode 100644 utest/language/test_language.py create mode 100644 utest/resources/robotdata/language/en/__init__.robot create mode 100644 utest/resources/robotdata/language/en/full_en.json create mode 100644 utest/resources/robotdata/language/en/full_en.py create mode 100644 utest/resources/robotdata/language/en/full_en.resource create mode 100644 utest/resources/robotdata/language/en/full_en.yaml create mode 100644 utest/resources/robotdata/language/en/full_task_en.robot create mode 100644 utest/resources/robotdata/language/en/full_test_en.robot create mode 100644 utest/resources/robotdata/language/en/gherkin_en.robot create mode 100644 utest/resources/robotdata/language/lang_bg.robot create mode 100644 utest/resources/robotdata/language/lang_bs.robot create mode 100644 utest/resources/robotdata/language/lang_cs.robot create mode 100644 utest/resources/robotdata/language/lang_de.robot create mode 100644 utest/resources/robotdata/language/lang_en.robot create mode 100644 utest/resources/robotdata/language/lang_es.robot create mode 100644 utest/resources/robotdata/language/lang_fi.robot create mode 100644 utest/resources/robotdata/language/lang_fr.robot create mode 100644 utest/resources/robotdata/language/lang_hi.robot create mode 100644 utest/resources/robotdata/language/lang_it.robot create mode 100644 utest/resources/robotdata/language/lang_nl.robot create mode 100644 utest/resources/robotdata/language/lang_pl.robot create mode 100644 utest/resources/robotdata/language/lang_pt.robot create mode 100644 utest/resources/robotdata/language/lang_pt_br.robot create mode 100644 utest/resources/robotdata/language/lang_ro.robot create mode 100644 utest/resources/robotdata/language/lang_ru.robot create mode 100644 utest/resources/robotdata/language/lang_sv.robot create mode 100644 utest/resources/robotdata/language/lang_th.robot create mode 100644 utest/resources/robotdata/language/lang_tr.robot create mode 100644 utest/resources/robotdata/language/lang_uk.robot create mode 100644 utest/resources/robotdata/language/lang_vi.robot create mode 100644 utest/resources/robotdata/language/lang_zh_cn.robot create mode 100644 utest/resources/robotdata/language/lang_zh_tw.robot create mode 100644 utest/resources/robotdata/language/preamble_no_lang.robot create mode 100644 utest/resources/robotdata/language/preamble_unknown_lang.robot create mode 100644 utest/resources/robotdata/language/pt/__init__.robot create mode 100644 utest/resources/robotdata/language/pt/full_pt.json create mode 100644 utest/resources/robotdata/language/pt/full_pt.py create mode 100644 utest/resources/robotdata/language/pt/full_pt.resource create mode 100644 utest/resources/robotdata/language/pt/full_pt.yaml create mode 100644 utest/resources/robotdata/language/pt/full_task_pt.robot create mode 100644 utest/resources/robotdata/language/pt/full_test_pt.robot create mode 100644 utest/resources/robotdata/language/pt/full_test_single_pt.robot create mode 100644 utest/resources/robotdata/language/pt/gherkin_pt.robot diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 429a1a7f4..c17251870 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -8,10 +8,20 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni == https://github.com/robotframework/RIDE[Unreleased] +=== Added + +- Added support for language configured test suites, with installed Robot Framework version 6.0 or higher. +- Fields are shown in the language of the files in Grid Editor (will be configurable if future versions). +- Tooltips for the fields are always shown in English. +- Colorization for language configured files is working in Text Editor. + === Fixed - Fixed New User Keyword dialog not allowing empty Arguments field +=== Removed + +- Removed support for old Python versions, 3.6 nd 3.7. == https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride-2.0.8.1.rst[2.0.8.1] - 2023-11-01 diff --git a/README.adoc b/README.adoc index 989a9feee..25c29d000 100644 --- a/README.adoc +++ b/README.adoc @@ -20,10 +20,12 @@ If you are looking for the latest released version, you can get the source code See the https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride-2.0.8.1.rst[release notes] for latest release version 2.0.8.1 +**Version https://github.com/robotframework/RIDE/tree/release/2.0.8.1[2.0.8.1] was the last release supporting Python 3.6 and 3.7** + **Version https://github.com/robotframework/RIDE/tree/release/1.7.4.2[1.7.4.2] was the last release supporting Python 2.7** -**The current development version is based on 2.0.8.1, supports Python from 3.6 up to 3.11 (1st November 2023).** +**The current development version is based on 2.0.8.1, supports Python from 3.8 up to 3.11 (20th December 2023).** Currently the unit tests are tested on Python 3.10, and 3.11 (which is the recommended version). Likewise, the current version of wxPython, is 4.2.1, but RIDE is known to work with 4.0.7 and 4.1.1 versions. @@ -32,7 +34,7 @@ Likewise, the current version of wxPython, is 4.2.1, but RIDE is known to work w `pip install -U robotframework-ride` -Install current development version (**2.0.9dev#**) with: +(3.8 < python <= 3.11) Install current development version (**2.1a1**) with: `pip install -U https://github.com/robotframework/RIDE/archive/master.zip` diff --git a/setup.py b/setup.py index 9506b629a..a934d41ba 100644 --- a/setup.py +++ b/setup.py @@ -50,8 +50,6 @@ Development Status :: 5 - Production/Stable License :: OSI Approved :: Apache Software License Operating System :: OS Independent -Programming Language :: Python :: 3.6 -Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 @@ -94,7 +92,7 @@ def run(self): package_dir={'': SOURCE_DIR}, packages=find_packages(SOURCE_DIR), package_data=PACKAGE_DATA, - python_requires='>=3.6', + python_requires='>=3.8, <3.12', # Robot Framework package data is not included, but RIDE does not need it. # Always install everything, since we may be switching between versions options={'install': {'force': True}}, diff --git a/src/robotide/action/actioninfo.py b/src/robotide/action/actioninfo.py index c1f36be73..943c344ba 100644 --- a/src/robotide/action/actioninfo.py +++ b/src/robotide/action/actioninfo.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import re import wx @@ -20,8 +21,11 @@ from ..widgets import ImageProvider from .shortcut import Shortcut +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation -def action_info_collection(data, event_handler, container=None): + +def action_info_collection(data, event_handler, data_nt=None, container=None): """Parses the ``data`` into a list of `ActionInfo` and `SeparatorInfo` objects. The data is parsed based on the simple DSL documented below. @@ -32,6 +36,9 @@ def action_info_collection(data, event_handler, container=None): event_handler The event handler that implements the actions. See `finding handlers`_ for more information. + data_nt + Since RIDE 2.1, this is the original English menudata, or None which will be a copy of data + This is due to the way menu is created for translations, which will not build the correct handler names. container the wxPython element containing the UI components associated with the `ActionInfo`. @@ -91,7 +98,7 @@ def action_info_collection(data, event_handler, container=None): Specifying container -------------------- - By default the given ``container`` is passed to the `ActionInfo.__init__` + By default, the given ``container`` is passed to the `ActionInfo.__init__` method directly. This can be altered by prefixing the ``name`` with an exclamation mark (e.g. ``!Save`` or ``!My Action``) to make that action global. With these actions the container given to the `ActionInfo.__init__` @@ -114,30 +121,41 @@ def action_info_collection(data, event_handler, container=None): Content Assist (Ctrl-Space or Ctrl-Alt-Space) | Has two shortcuts. """ + if not data_nt: + data_nt = data menu = None actions = [] - for row in data.splitlines(): + for row, row_nt in zip(data.splitlines(), data_nt.splitlines()): row = row.strip() - if not row: + row_nt = row_nt.strip() + print(f"DEBUG: actioninfo.py action_info_collection in loop row={row}\noriginal={row_nt} ") + if not row and not row_nt: continue elif row.startswith('[') and row.endswith(']'): menu = row[1:-1].strip() + # print(f"DEBUG: actioninfo.py action_info_collection menu={menu}") else: - actions.append(_create_action_info(event_handler, menu, container, row)) + actions.append(_create_action_info(event_handler, menu, container, row, row_nt)) return actions -def _create_action_info(eventhandler, menu, container, row): +def _create_action_info(eventhandler, menu, container, row, row_nt): if row.startswith('---'): return SeparatorInfo(menu) tokens = [t.strip() for t in row.split('|')] tokens += [''] * (5-len(tokens)) name, doc, shortcut, icon, position = tokens - if name.startswith('!'): + tokens_nt = [t.strip() for t in row_nt.split('|')] + tokens_nt += [''] * (5-len(tokens_nt)) + name_nt, __, __, __, __ = tokens_nt + if name_nt.startswith('!'): name = name[1:] + name_nt = name_nt[1:] container = None - eventhandler_name, name = get_eventhandler_name_and_parsed_name(name) + eventhandler_name, name_nt = get_eventhandler_name_and_parsed_name(name_nt) action = getattr(eventhandler, eventhandler_name) + print(f"DEBUG: actioninfo.py _create_action_info menu={menu} eventhandler_name={eventhandler_name}," + f" name_nt={name_nt}") return ActionInfo(menu, name, action, container, shortcut, icon, doc, position) @@ -152,8 +170,7 @@ def _parse_shortcuts_from_name(name): eventhandler_name, shortcuts = name.split('(', 1) shortcuts = shortcuts.split(')')[0] elements = shortcuts.split(' or ') - name = '%s (%s)' % (eventhandler_name, ' or '.join(Shortcut(e).printable for e in - elements)) + name = '%s (%s)' % (eventhandler_name, ' or '.join(Shortcut(e).printable for e in elements)) return eventhandler_name, name return name, name @@ -187,7 +204,7 @@ class ActionInfo(MenuInfo): def __init__(self, menu_name, name, action=None, container=None, shortcut=None, icon=None, doc='', position=-1): - """Initializes information needed to create actions.. + """Initializes information needed to create actions... :Parameters: menu_name diff --git a/src/robotide/application/CHANGELOG.html b/src/robotide/application/CHANGELOG.html index 4dc2f9003..1337c8ba9 100644 --- a/src/robotide/application/CHANGELOG.html +++ b/src/robotide/application/CHANGELOG.html @@ -1,8 +1,18 @@ Changelog

Changelog


All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog -and this project adheres to Semantic Versioning.

1.1. Fixed

  • +and this project adheres to Semantic Versioning.

    1.1. Added

    • +Added support for language configured test suites, with installed Robot Framework version 6.0 or higher. +
    • +Fields are shown in the language of the files in Grid Editor (will be configurable if future versions). +
    • +Tooltips for the fields are always shown in English. +
    • +Colorization for language configured files is working in Text Editor. +

    1.2. Fixed

    • Fixed New User Keyword dialog not allowing empty Arguments field -

    2. 2.0.8.1 - 2023-11-01

    2.1. Added

    • +

    1.3. Removed

    • +Removed support for old Python versions, 3.6 nd 3.7. +

    2. 2.0.8.1 - 2023-11-01

    2.1. Added

    • Added auto update check when development version is installed
    • Added menu option ``Help→Check for Upgrade`` which allows to force update check and install development version @@ -55,7 +65,7 @@ Improved keyword ``Find Usages`` to return more matches. Fails to find mixed spaces and ``_``
    • In Grid Editor ``Ctrl-Shift-4`` now replaces escaped spaces ``\\ `` by spaces -

    2.4. Removed

    -

    3. 2.0.7 - 2023-08-13

    3.1. Added

    • +

    2.4. Removed

    -

    3. 2.0.7 - 2023-08-13

    3.1. Added

    • Added indication of matching brackets, ``()``, ``{}``, ``[]``, in Text Editor
    • Added context menu to RIDE tray icon. Options Show, Hide and Close @@ -78,7 +88,7 @@ Fixed renaming keywords when they were arguments of ``Run Keywords`` in Setups and Teardowns

    3.3. Changed

    • Improve Text Editor auto-suggestions to keep libraries prefixes. -

    4. 2.0.6 - 2023-06-10

    4.1. Added

    • +

    4. 2.0.6 - 2023-06-10

    4.1. Added

    • Added boolean parameter ``filter newlines`` to Grid Editor with default ``True``, to hide or show newlines in cells

    4.2. Changed

    • Changed ``tasks.py`` to test ``utest/application/test_app_main.py`` isolated from the other tests @@ -86,7 +96,7 @@ Improve auto-suggestions of keywords in Grid Editor by allowing to close suggestions list with keys ARROW_LEFT or ARROW_RIGHT
    • Improve Text Editor auto-suggestions by using: selected text, text at left or at right of cursor -

    5. 2.0.5 - 2023-05-08

    5.1. Added

    • +

    5. 2.0.5 - 2023-05-08

    5.1. Added

    • Added ``FOR`` scope markers (``IN``, ``IN RANGE``, ``IN ENUMERATE``, ``IN ZIP``) to auto-complete list
    • Added support to read environment variable ``ROBOT_VERSION`` to apply some conditions. @@ -116,7 +126,7 @@ Fixed not using defined color for help and HTML content
    • Fixed missing newlines in sections separation -

    9. 2.0 - 2023-03-01

    9.1. Added

      (2.0rc1 - 2023-02-26)
    +

9. 2.0 - 2023-03-01

9.1. Added

  (2.0rc1 - 2023-02-26)
 - Minimal support to accept `*** Comments ***` sections (unfinished code)
 - Added insert and delete cells to Text Editor, by using ``Ctrl-Shift-I`` and ``Ctrl-Shift-D``
 - Added move up and move down rows to Text Editor, by using ``Alt-Up`` and ``Alt-Down``
@@ -169,7 +179,7 @@
 - Added enclosing text in Text Editor or selected text with certain symbols
 - Added enclosing text in Grid Editor or selected text with certain symbols
 - Added 8s timer to shortcut creation dialog on install
-- Added process memory limit on Messages Log

9.2. Removed

  (2.0b2 - 2022-09-05)
+- Added process memory limit on Messages Log

9.2. Removed

  (2.0b2 - 2022-09-05)
 - Removed ``robotframeworklexer`` dependency and local copy
 - Removed alignment flag on grid cell JSON Editor (Ctrl-Shift-J)
 - Removed moving to keyword/variable definition when doing Double-Click in grid cell
@@ -278,7 +288,7 @@
 - Fixed Settings editor
 - Fixed blank Edit screen
 - Fixed Runner arguments parsing
-- Fixed Runner Log window Chinese and Latin encoding chars on Windows

10. 2.0rc1 - 2023-02-26

10.1. Added

  • +- Fixed Runner Log window Chinese and Latin encoding chars on Windows

10. 2.0rc1 - 2023-02-26

10.1. Added

  • Minimal support to accept *** Comments *** sections (unfinished code)
  • Added insert and delete cells to Text Editor, by using ``Ctrl-Shift-I`` and ``Ctrl-Shift-D`` @@ -286,15 +296,15 @@ Added move up and move down rows to Text Editor, by using ``Alt-Up`` and ``Alt-Down``
  • Added insert and delete rows to Text Editor, by using ``Ctrl-I`` and ``Ctrl-D`` -

10.2. Removed

10.3. Changed

10.4. Fixed

  • +

10.2. Removed

10.3. Changed

10.4. Fixed

  • Fixed blank Grid Editor at keywords with steps commented with ``\# ``, by using ``Ctrl-Shift-3 on Text Editor -

11. 2.0b3 - 2023-01-15

11.1. Added

  • +

11. 2.0b3 - 2023-01-15

11.1. Added

  • Added swap row up, by using ``Ctrl-T``
  • Added commenting/uncommenting of content with ``\# ``, by using ``Ctrl-Shift-3`` and ``Ctrl-Shift-4``
  • Added support for editing .robot and .resource files with content before sections -

11.2. Removed

  • +

11.2. Removed

  • None

11.3. Changed

  • Hiding items in Test Suites explorer with names starting with # @@ -309,7 +319,7 @@ Fixed comment and uncomment in Grid Editor when cells contain more than one variables assignement
  • Fixed console log stopping to output certain characters, like chinese and latin -

12. 2.0b2 - 2022-09-05

12.1. Added

  • +

12. 2.0b2 - 2022-09-05

12.1. Added

  • Added menu entry at Help → Offline Change Log to view this file on disk
  • Added skipped tests counter and corresponding colored icon on Project tree @@ -377,7 +387,7 @@ When editing, Ctrl-Home and Ctrl-End move cursor to start and end of cell content respectively.
  • Added Del key to clear Grid Editor cell content when in navigation mode (clear like doing Ctrl-X) -
  • 12.2. Removed

    • +

    12.2. Removed

    • Removed ``robotframeworklexer`` dependency and local copy
    • Removed alignment flag on grid cell JSON Editor (Ctrl-Shift-J) @@ -486,7 +496,7 @@ Fixed RIDE startup crash when Tree or File Explorer plugins use opened=False setting
    • Fixed error occurring when deleting test cases on Tree -

    13. 2.0b1 - 2020-07-26

    13.1. Added

    • +

    13. 2.0b1 - 2020-07-26

    13.1. Added

    • Added CHANGELOG.adoc
    • Added ignoring log.html and report.html on reporting HTML test suites @@ -504,7 +514,7 @@ Added 8s timer to shortcut creation dialog on install
    • Added process memory limit on Messages Log -

    13.2. Removed

    • +

    13.2. Removed

    • Python 2.7 support
    • wxPython/wxPhoenix version conditioning @@ -582,9 +592,9 @@ Fixed Runner arguments parsing
    • Fixed Runner Log window Chinese and Latin encoding chars on Windows -

    14. 1.7.4.2 - 2020-01-20

    14.1. Added

    • +

    14. 1.7.4.2 - 2020-01-20

    14.1. Added

    • wxPython version locked up to 4.0.7.post2. -

    14.2. Removed

    • +

    14.2. Removed

    • None

    14.3. Changed

    • None diff --git a/src/robotide/application/application.py b/src/robotide/application/application.py index a1473a2e2..592399050 100644 --- a/src/robotide/application/application.py +++ b/src/robotide/application/application.py @@ -13,10 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import locale import os import wx from contextlib import contextmanager +from pathlib import Path from ..namespace import Namespace from ..controller import Project from ..spec import librarydatabase @@ -24,14 +26,14 @@ from ..ui.mainframe import RideFrame from .. import publish from .. import context, contrib -from ..context import coreplugins +# from ..context import coreplugins from ..preferences import Preferences, RideSettings from ..application.pluginloader import PluginLoader from ..application.editorprovider import EditorProvider from ..application.releasenotes import ReleaseNotes from ..application.updatenotifier import UpdateNotifierController, UpdateDialog from ..ui.mainframe import ToolBar -from ..ui.treeplugin import TreePlugin +# from ..ui.treeplugin import TreePlugin from ..ui.fileexplorerplugin import FileExplorerPlugin from ..utils import RideFSWatcherHandler, run_python_command from ..lib.robot.utils.encodingsniffer import get_system_encoding @@ -41,8 +43,16 @@ from wx.lib.agw.aui import AuiDefaultToolBarArt from wx.lib.agw.aui.auibar import AuiToolBar from wx.lib.agw.aui.auibook import AuiTabCtrl, TabFrame +try: + from robot.conf import languages +except ImportError: + languages = None locale.setlocale(locale.LC_ALL, 'C') +# add translation macro to builtin similar to what gettext does +# generated pot with: /usr/bin/python /usr/bin/pygettext.py -a -d RIDE -o RIDE.pot -p ./locale ../robotide +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation BACKGROUND_HELP = 'background help' FOREGROUND_TEXT = 'foreground text' @@ -58,7 +68,7 @@ def __init__(self): class RIDE(wx.App): _controller = None _editor_provider = None - _initial_locale = None + _locale = None _plugin_loader = None editor = None fileexplorerplugin = None @@ -78,10 +88,19 @@ def __init__(self, path=None, updatecheck=True): def OnInit(self): # Overrides wx method # DEBUG To test RTL # self._initial_locale = wx.Locale(wx.LANGUAGE_ARABIC) - self._initial_locale = wx.Locale(wx.LANGUAGE_ENGLISH_US) + self._locale = wx.Locale(wx.LANGUAGE_PORTUGUESE) # LANGUAGE_ENGLISH_US) # Needed for SetToolTipString to work wx.HelpProvider.Set(wx.SimpleHelpProvider()) # DEBUG: adjust to wx versions self.settings = RideSettings() + + class Message: + keys = ['General'] + + self.change_locale(Message) # This was done here to have menus translated, but not working + print(f"DEBUG: application.py RIDE OnInit after changing locale {self._locale.GetCanonicalName()=}") + # Importing libraries after setting language + from ..context import coreplugins, SETTINGS_DIRECTORY + from ..ui.treeplugin import TreePlugin librarydatabase.initialize_database() self.preferences = Preferences(self.settings) self.namespace = Namespace(self.settings) @@ -102,7 +121,7 @@ def OnInit(self): # Overrides wx method except Exception as e: print(f"RIDE: There was a problem loading panels position." f" Please delete the definition 'AUI NB Perspective' in " - f"{os.path.join(context.SETTINGS_DIRECTORY, 'settings.cfg')}") + f"{os.path.join(SETTINGS_DIRECTORY, 'settings.cfg')}") if not isinstance(e, IndexError): # If is with all notebooks disabled, continue raise e self.treeplugin = TreePlugin(self) @@ -134,6 +153,8 @@ def OnInit(self): # Overrides wx method PUBLISHER.subscribe(self.SetGlobalColour, RideSettingsChanged) PUBLISHER.subscribe(self.update_excludes, RideSettingsChanged) RideSettingsChanged(keys=('Excludes', 'init'), old=None, new=None).publish() + PUBLISHER.subscribe(self.change_locale, RideSettingsChanged) + RideSettingsChanged(keys=('General', 'ui interface'), old=None, new=None).publish() return True @staticmethod @@ -281,19 +302,50 @@ def _iterate_all_windows(root): w.SetFont(font) """ + def change_locale(self, message): + if message.keys[0] != "General": + return + if languages: + from ..preferences import Languages + names = [n for n in Languages.names] + else: + names = [('English', 'en', wx.LANGUAGE_ENGLISH)] + general = self.settings.get('General', None) + language = general.get('ui language', 'English') + try: + idx = [lang[0] for lang in names].index(language) + except (IndexError, ValueError): + print(f"DEBUG: application.py RIDE change_locale ERROR: Could not find {language=}") + idx = None + if idx: + code = names[idx][2] + else: + code = wx.LANGUAGE_ENGLISH + del self._locale + self._locale = wx.Locale(code) + if self._locale.IsOk(): + lpath = Path(__file__).parent.absolute() + lpath = str(Path(Path.joinpath(lpath.parent, 'locale')).absolute()) + wx.Locale.AddCatalogLookupPathPrefix(lpath) + self._locale.AddCatalog('RIDE') + else: + self._locale = wx.Locale(wx.LANGUAGE_ENGLISH_US) + @staticmethod def update_excludes(message): if message.keys[0] != "Excludes": return from ..preferences.excludes_class import Excludes - excludes = Excludes(context.SETTINGS_DIRECTORY) + from ..context import SETTINGS_DIRECTORY + excludes = Excludes(SETTINGS_DIRECTORY) paths = excludes.get_excludes().split() if paths: RideFSWatcherHandler.exclude_listening(paths) @staticmethod def _publish_system_info(): - publish.RideLogMessage(context.SYSTEM_INFO).publish() + from ..context import SYSTEM_INFO + publish.RideLogMessage(SYSTEM_INFO).publish() @property def model(self): @@ -328,7 +380,7 @@ def _find_robot_installation(): if robot_found: system_encoding = get_system_encoding() rf_file, rf_version = output.strip().split(b", ") - publish.RideLogMessage("Found Robot Framework version %s from %s." % ( + publish.RideLogMessage(_("Found Robot Framework version %s from %s.") % ( str(rf_version, system_encoding), str(os.path.dirname(rf_file), system_encoding))).publish() return rf_version else: diff --git a/src/robotide/application/releasenotes.py b/src/robotide/application/releasenotes.py index 1889e2915..b3684f86c 100644 --- a/src/robotide/application/releasenotes.py +++ b/src/robotide/application/releasenotes.py @@ -61,14 +61,14 @@ def show_if_updated(self): self.application.settings['version_shown'] = VERSION def show(self, event=None): - _ = event + __ = event if not self._view: self._view = self._create_view() self.application.frame.notebook.AddPage(self._view, "Release Notes", select=False) self.application.frame.notebook.show_tab(self._view) def show_changelog(self, event=None): - _ = event + __ = event if not self._dialog: self._dialog = HtmlDialog('Offline Change Log', f"Check the online version at https://github.com/" f"robotframework/RIDE/blob/{VERSION}/CHANGELOG.adoc") @@ -150,16 +150,22 @@ def set_content(self, html_win, content): moment 6.1.1. However, internal library is based on version 3.1.2, to keep compatibility with old formats.

        -
      • This version supports Python 3.6 up to 3.11.
      • +
      • This version supports Python 3.8 up to 3.11.
      • There are some changes, or known issues:
          -
        • On Text Editor, pressing Ctrl when the caret/cursor is near a Keyword will show a detachable window with the documentation, at Mouse Pointer position.
        • +
        • Removed support for Python 3.6 and 3.7
        • +
        • Added support for language configured test suites, with languages from installed Robot Framework version 6.0, or +higher.
        • +
        • On Text Editor, pressing Ctrl when the caret/cursor is near a Keyword will show a detachable window with the +documentation, at Mouse Pointer position.
        • RIDE tray icon now shows a context menu with options Show, Hide and Close.
        • Highlighting and navigation of selected Project Explorer items, in Text Editor.
        • -
        • When editing in Grid Editor with content assistance, the selected content can be edited by escaping the list of suggestions with keys ARROW_LEFT or ARROW_RIGHT.
        • +
        • When editing in Grid Editor with content assistance, the selected content can be edited by escaping the list of +suggestions with keys ARROW_LEFT or ARROW_RIGHT.
        • Newlines in Grid Editor can be made visible with the filter newlines set to False.
        • On Text Editor when Saving the selection of tests in Test Suites (Tree) is cleared.
        • Test Suite with *** Comments *** can be edited but newlines are introduced.
        • -
        • Problems with COPY/PASTE in Text Editor have been reported when using wxPython 4.2.0, but not with version 4.2.1, which we now recommend.
        • +
        • Problems with COPY/PASTE in Text Editor have been reported when using wxPython 4.2.0, but not with version 4.2.1, +which we now recommend.
        • Some argument types detection (and colorization) is not correct in Grid Editor.
        • RIDE DOES NOT KEEP Test Suites formatting or structure, causing differences in files when used on other IDE or Editors.
        • @@ -168,20 +174,26 @@ def set_content(self, html_win, content):

        New Features and Fixes Highlights

          +
        • Added support for language configured test suites. Fields are shown in the language of the files in Grid Editor. + Tooltips are always shown in English. Colorization for language configured files is working in Text Editor.
        • Fixed New User Keyword dialog not allowing empty Arguments field
        • Fixed escaped spaces showing in Text Editor on commented cells
        • Improved keywords documentation search, by adding current dir to search
        • -
        • Improved Move up/down, Alt-UpArrow/Alt-DownArrow in Text Editor, to have proper indentation and selection
        • +
        • Improved Move up/down, Alt-UpArrow/Alt-DownArrow in Text Editor, to have proper indentation and +selection
        • Added auto update check when development version is installed
        • -
        • Added menu option Help->Check for Upgrade which allows to force update check and install development version
        • +
        • Added menu option Help->Check for Upgrade which allows to force update check and install development +version
        • Added Upgrade Now action to update dialog.
        • -
        • Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and Force Tags settings, after Robot Framework 7.0
        • +
        • Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace +Default and Force Tags settings, after Robot Framework 7.0
        • Improved RIDE Log and Parser Log windows to allow Zoom In/Out with Ctrl-Mouse Wheel
        • Hide continuation markers in Project Tree
        • Improved content assistance in Text Editor by allowing to filter list as we type
        • Fixed resource files disappearing from Project tree on Windows
        • Fixed missing indication of link for User Keyword, when pressing Ctrl in Grid Editor
        • -
        • Added content help pop-up on Text Editor by pressing Ctrl for text at cursor position or selected autocomplete list item
        • +
        • Added content help pop-up on Text Editor by pressing Ctrl for text at cursor position or selected +autocomplete list item
        • Added Exclude option in context nenu for Test files, previously was only possible for Test Suites folders
        • Added exclusion of monitoring filesystem changes for files and directories excluded in Preferences
        • Fixed exception when finding GREY color for excluded files and directories in Project Tree
        • @@ -196,9 +208,12 @@ def set_content(self, html_win, content):
        • Added context menu to RIDE tray icon. Options Show, Hide and Close
        • Added synchronization with Project Explorer to navigate to selected item, Test Case, Keyword, Variable, in Text Editor
        • -
        • Control commands (FOR, IF, TRY, etc) will only be colorized as valid keywords when typed in all caps in Grid Editor
        • -
        • Newlines in Grid Editor can be made visible with the filter newlines set to False, by editing settings.cfg
        • -
        • Improve auto-suggestions of keywords in Grid Editor by allowing to close suggestions list with keys ARROW_LEFT or ARROW_RIGHT
        • +
        • Control commands (FOR, IF, TRY, etc) will only be colorized as valid keywords when typed in +all caps in Grid Editor
        • +
        • Newlines in Grid Editor can be made visible with the filter newlines set to False, by editing +settings.cfg
        • +
        • Improve auto-suggestions of keywords in Grid Editor by allowing to close suggestions list with keys ARROW_LEFT or +ARROW_RIGHT
        • Improve Text Editor auto-suggestions by using: selected text, text at left or at right of cursor

        We hope to implement or complete features and make fixes on next major version 2.1 (in the end of 2023).

        @@ -250,6 +265,6 @@ def set_content(self, html_win, content):
         python -m robotide.postinstall -install
         
        -

        RIDE {VERSION} was released on 20/Nov/2023.

        +

        RIDE {VERSION} was released on 24/Dec/2023.

    """ diff --git a/src/robotide/application/updatenotifier.py b/src/robotide/application/updatenotifier.py index fc45030ac..eb9e5027e 100644 --- a/src/robotide/application/updatenotifier.py +++ b/src/robotide/application/updatenotifier.py @@ -12,6 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import builtins import subprocess import sys # Configure wx uversion to allow running test app in __main__ @@ -30,6 +31,9 @@ from ..widgets import ButtonWithHandler, HtmlWindow, RIDEDialog from ..postinstall.__main__ import MessageDialog +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + _CHECK_FOR_UPDATES_SETTING = "check for updates" _LAST_UPDATE_CHECK_SETTING = "last update check" SPC = " " @@ -104,18 +108,18 @@ def upgrade_from_dev_dialog(version_installed): # Here is the Menu Help->Upgrade insertion part, try to highlight menu # wx.CANCEL_DEFAULT command = sys.executable + " -m pip install -U https://github.com/robotframework/RIDE/archive/master.zip" _add_content_to_clipboard(command) - if not _askyesno("Upgrade?", f"{SPC}New development version is available.{SPC}\n{SPC}You may install" - f" version {main_dict['VERSION']} with:\n{SPC}{command}{SPC}\n\n" - f"{SPC}Click OK to Upgrade now!\n{SPC}After upgrade you will see another dialog informing" - f" to close this RIDE instance.{SPC}\n", - wx.GetActiveWindow(), no_default=True): + if not _askyesno(_("Upgrade?"), f"{SPC}{_('New development version is available.')}{SPC}\n{SPC}" + f"{_('You may install version ')}{main_dict['VERSION']}{_(' with:')}\n" + f"{SPC}{command}{SPC}\n\n{SPC}{_('Click OK to Upgrade now!')}\n{SPC}" + f"{_('After upgrade you will see another dialog informing to close this RIDE instance.')}" + f"{SPC}\n", wx.GetActiveWindow(), no_default=True): return False else: do_upgrade(command) return True else: - _askyesno("No Upgrade Available", f"{SPC}You have the latest version of RIDE.{SPC}" - f"\n\n{SPC} Have a nice day :)\n", + _askyesno(_("No Upgrade Available"), f"{SPC}{_('You have the latest version of RIDE.')}{SPC}" + f"\n\n{SPC}{_(' Have a nice day :)')}\n", wx.GetActiveWindow()) return False @@ -172,7 +176,7 @@ def do_upgrade(command): """ time.sleep(1) if result != 0: - _askyesno("Failed to Upgrade", f"{SPC}An error occurred when installing new version", + _askyesno(_("Failed to Upgrade"), f"{SPC}{_('An error occurred when installing new version')}", wx.GetActiveWindow()) return False command = sys.executable + " -m robotide.__init__ --noupdatecheck" @@ -181,7 +185,7 @@ def do_upgrade(command): """ Not working well: wx.CallLater(10000, psutil.Process.kill, my_pid.pid) """ - _askyesno("Completed Upgrade", f"\n{SPC}You should close this RIDE (Process ID = {my_pid.pid}){SPC}", + _askyesno(_("Completed Upgrade"), f"\n{SPC}{_('You should close this RIDE (Process ID = ')}{my_pid.pid}){SPC}", wx.GetActiveWindow()) @@ -201,7 +205,7 @@ def __init__(self, uversion, url, settings, modal=True): self._settings = settings self._command = sys.executable + f" -m pip install -U robotframework-ride=={uversion}" _add_content_to_clipboard(self._command) - RIDEDialog.__init__(self, title="Update available", size=(600, 400), + RIDEDialog.__init__(self, title=_("Update available"), size=(600, 400), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) @@ -209,28 +213,30 @@ def __init__(self, uversion, url, settings, modal=True): self.SetForegroundColour(Colour(self.color_foreground)) sizer = wx.BoxSizer(orient=wx.VERTICAL) hwin = LocalHtmlWindow(self, size=(600, 200)) - hwin.set_content(f"{SPC}New version {uversion} available from {url}
    " - f"{SPC}See this version {url}
    " + f"{SPC}{_('See this version ')}Release Notes

    " - f"{SPC}You can update with the command:
    {self._command}" - f"

    {SPC}Or, click Upgrade Now.
    " - f"{SPC}After upgrade you will see another dialog informing to close this RIDE instance." - f"

    {SPC}See the latest development {self._command}" + f"

    {SPC}{_('Or, click Upgrade Now')}.
    " + f"{SPC}{_('After upgrade you will see another dialog informing to close this RIDE instance.')}" + f"

    {SPC}{_('See the latest development ')}
    CHANGELOG") irep = hwin.GetInternalRepresentation() hwin.SetSize((irep.GetWidth()+25, irep.GetHeight()+20)) sizer.Add(hwin) - checkbox = wx.CheckBox(self, -1, label="I\'m using another method for RIDE updates\n and " - "do not need automatic update checks") + checkbox = wx.CheckBox(self, -1, label=_("I\'m using another method for RIDE updates\n and " + "do not need automatic update checks")) checkbox.Bind(wx.EVT_CHECKBOX, handler=self.on_checkbox_change) sizer.Add(checkbox) hsizer = wx.BoxSizer(orient=wx.HORIZONTAL) - button = ButtonWithHandler(self, label="remind me later", handler=self.on_remind_me_later) + button = ButtonWithHandler(self, label=_("remind me later"), mk_handler="remind me later", + handler=self.on_remind_me_later) button.SetBackgroundColour(Colour(self.color_secondary_background)) button.SetForegroundColour(Colour(self.color_secondary_foreground)) hsizer.Add(button) hsizer.AddSpacer(50) - up_button = ButtonWithHandler(self, label="Upgrade Now", handler=self.on_upgrade_now) + up_button = ButtonWithHandler(self, label=_("Upgrade Now"), mk_handler="Upgrade Now", + handler=self.on_upgrade_now) up_button.SetBackgroundColour(Colour(self.color_secondary_background)) up_button.SetForegroundColour(Colour(self.color_secondary_foreground)) hsizer.Add(up_button) @@ -246,7 +252,7 @@ def __init__(self, uversion, url, settings, modal=True): self.Show() def on_remind_me_later(self, event): - _ = event + __ = event self.Close(True) def on_checkbox_change(self, event): @@ -254,6 +260,6 @@ def on_checkbox_change(self, event): event.Skip() def on_upgrade_now(self, event): - _ = event + __ = event _add_content_to_clipboard(self._command) do_upgrade(self._command) diff --git a/src/robotide/context/coreplugins.py b/src/robotide/context/coreplugins.py index fae0a4ab0..91b9375be 100644 --- a/src/robotide/context/coreplugins.py +++ b/src/robotide/context/coreplugins.py @@ -16,7 +16,6 @@ def get_core_plugins(): from ..run import RunAnything from ..recentfiles import RecentFilesPlugin - from ..ui.preview import PreviewPlugin from ..ui.keywordsearch import KeywordSearch from ..ui.treeplugin import TreePlugin from ..ui.fileexplorerplugin import FileExplorerPlugin @@ -28,5 +27,5 @@ def get_core_plugins(): from ..spec.specimporter import SpecImporterPlugin from ..postinstall.desktopshortcut import ShortcutPlugin - return [LogPlugin, RunAnything, RecentFilesPlugin, PreviewPlugin, SpecImporterPlugin, EditorPlugin, TextEditorPlugin, + return [LogPlugin, RunAnything, RecentFilesPlugin, SpecImporterPlugin, EditorPlugin, TextEditorPlugin, KeywordSearch, TestSearchPlugin, ShortcutPlugin, ParserLogPlugin, TreePlugin, FileExplorerPlugin] diff --git a/src/robotide/contrib/testrunner/runprofiles.py b/src/robotide/contrib/testrunner/runprofiles.py index 0bbebf8a1..50d4898c8 100644 --- a/src/robotide/contrib/testrunner/runprofiles.py +++ b/src/robotide/contrib/testrunner/runprofiles.py @@ -27,12 +27,13 @@ any additional arguments. """ +import builtins import os import re import time import wx -from robotide import pluginapi +from robotide.publish.messages import RideLogMessage from robotide.context import IS_WINDOWS from robotide.contrib.testrunner.usages import USAGE from robotide.lib.robot.utils import format_time @@ -42,6 +43,9 @@ from sys import getfilesystemencoding from wx.lib.filebrowsebutton import FileBrowseButton +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + OUTPUT_ENCODING = getfilesystemencoding() @@ -412,12 +416,12 @@ def _create_error_log_message(self, error, returncode): if b'not found' in error \ or returncode == 127 or \ b'system cannot find the file specified' in error: - return pluginapi.RideLogMessage(RF_INSTALLATION_NOT_FOUND, notify_user=True) + return RideLogMessage(RF_INSTALLATION_NOT_FOUND, notify_user=True) return None def _get_log_options_panel(self, parent): collapsible_pane = wx.CollapsiblePane( - parent, wx.ID_ANY, 'Log options', + parent, wx.ID_ANY, _('Log options'), style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_collapsible_pane_changed, @@ -425,14 +429,14 @@ def _get_log_options_panel(self, parent): pane = collapsible_pane.GetPane() pane.SetBackgroundColour(self._mysettings.color_background) pane.SetForegroundColour(self._mysettings.color_foreground) - label = Label(pane, label="Output directory: ") + label = Label(pane, label=_("Output directory: ")) self._output_directory_text_ctrl = \ self._create_text_ctrl(pane, self.output_directory, "removed due unicode_error (delete this)", self.on_output_directory_changed) self._output_directory_text_ctrl.SetBackgroundColour(self._mysettings.color_secondary_background) self._output_directory_text_ctrl.SetForegroundColour(self._mysettings.color_secondary_foreground) - button = ButtonWithHandler(pane, "...", self._handle_select_directory) + button = ButtonWithHandler(pane, "...", handler=self._handle_select_directory) button.SetBackgroundColour(self._mysettings.color_secondary_background) button.SetForegroundColour(self._mysettings.color_secondary_foreground) horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -443,13 +447,13 @@ def _get_log_options_panel(self, parent): suite_name_outputs_cb = self._create_checkbox( pane, self.are_log_names_with_suite_name, - "Add suite name to log names", self.on_suite_name_outputs_check_box) + _("Add suite name to log names"), self.on_suite_name_outputs_check_box) timestamp_outputs_cb = self._create_checkbox( pane, self.are_log_names_with_timestamp, - "Add timestamp to log names", self.on_timestamp_outputs_checkbox) + _("Add timestamp to log names"), self.on_timestamp_outputs_checkbox) save_logs_cb = self._create_checkbox( pane, self.are_saving_logs, - "Save Console and Message logs", self.on_save_logs_checkbox) + _("Save Console and Message logs"), self.on_save_logs_checkbox) vertical_sizer = wx.BoxSizer(wx.VERTICAL) vertical_sizer.Add(horizontal_sizer, 0, wx.EXPAND) @@ -465,9 +469,9 @@ def on_output_directory_changed(self, evt): self.set_setting("output_directory", value) def _handle_select_directory(self, event): - _ = event + __ = event path = self._output_directory_text_ctrl.GetValue() - dlg = wx.DirDialog(None, "Select Logs Directory", + dlg = wx.DirDialog(None, _("Select Logs Directory"), path, wx.DD_DEFAULT_STYLE) dlg.SetBackgroundColour(self._mysettings.color_background) dlg.SetForegroundColour(self._mysettings.color_foreground) @@ -489,7 +493,7 @@ def on_save_logs_checkbox(self, evt): def _get_arguments_panel(self, parent): collapsible_pane = wx.CollapsiblePane( - parent, wx.ID_ANY, 'Arguments', + parent, wx.ID_ANY, _('Arguments'), style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_collapsible_pane_changed, @@ -501,8 +505,8 @@ def _get_arguments_panel(self, parent): self._create_text_ctrl(pane, self.arguments, "removed due unicode_error (delete this)", self.on_arguments_changed) - self._args_text_ctrl.SetToolTip("Arguments for the test run. " - "Arguments are space separated list.") + self._args_text_ctrl.SetToolTip(_("Arguments for the test run. " + "Arguments are space separated list.")) self._args_text_ctrl.SetBackgroundColour(self._mysettings.color_secondary_background) self._args_text_ctrl.SetForegroundColour(self._mysettings.color_secondary_foreground) horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -527,8 +531,7 @@ def _validate_arguments(self, args): self._args_text_ctrl.SetForegroundColour( 'white' if invalid_message else self._mysettings.color_secondary_foreground) if not bool(invalid_message): - invalid_message = "Arguments for the test run. " \ - "Arguments are space separated list." + invalid_message = _("Arguments for the test run. Arguments are space separated list.") self._args_text_ctrl.SetToolTip(invalid_message) @staticmethod @@ -545,20 +548,20 @@ def _get_invalid_message(args): clean_args[idx] = 'arg' args = " ".join(clean_args) # print(f"DEBUG: run_profiles _get_invalid_message: Check invalid args={args}") - _, invalid = ArgumentParser(USAGE).parse_args(args) # DEBUG .split()) + __, invalid = ArgumentParser(USAGE).parse_args(args) # DEBUG .split()) except Information: - return 'Does not execute - help or version option given' + return _('Does not execute - help or version option given') except (DataError, Exception) as e: if e.message: return e.message if bool(invalid): - return f'Unknown option(s): {invalid}' + return f'{_("Unknown option(s):")} {invalid}' return None def _get_tags_panel(self, parent): """Create a panel to input include/exclude tags""" collapsible_pane = wx.CollapsiblePane( - parent, wx.ID_ANY, 'Tests filters', + parent, wx.ID_ANY, _('Tests filters'), style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_collapsible_pane_changed, @@ -567,10 +570,10 @@ def _get_tags_panel(self, parent): pane.SetBackgroundColour(self._mysettings.color_background) pane.SetForegroundColour(self._mysettings.color_foreground) include_cb = self._create_checkbox(pane, self.apply_include_tags, - "Only run tests with these tags:", + _("Only run tests with these tags:"), self.on_include_checkbox) exclude_cb = self._create_checkbox(pane, self.apply_exclude_tags, - "Skip tests with these tags:", + _("Skip tests with these tags:"), self.on_exclude_checkbox) self._include_tags_text_ctrl = \ self._create_text_ctrl(pane, self.include_tags, "unicode_error", @@ -666,7 +669,7 @@ def _create_error_log_message(self, error, returncode): def _get_run_script_panel(self, parent): panel = wx.Panel(parent, wx.ID_ANY) self._script_ctrl = FileBrowseButton( - panel, labelText="Script to run tests:", size=(-1, -1), + panel, labelText=_("Script to run tests:"), size=(-1, -1), fileMask="*", changeCallback=self.on_custom_script_changed) self._script_ctrl.SetValue(self.runner_script) diff --git a/src/robotide/contrib/testrunner/testrunnerplugin.py b/src/robotide/contrib/testrunner/testrunnerplugin.py index e5fc96f32..9b5d4e892 100644 --- a/src/robotide/contrib/testrunner/testrunnerplugin.py +++ b/src/robotide/contrib/testrunner/testrunnerplugin.py @@ -46,6 +46,7 @@ being used for a currently running test. """ import atexit +import builtins import datetime import shutil import subprocess @@ -83,6 +84,9 @@ from robotide.lib.robot.utils.encodingsniffer import (get_console_encoding, get_system_encoding) +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + CONSOLE_ENCODING = get_console_encoding() SYSTEM_ENCODING = get_system_encoding() OUTPUT_ENCODING = getfilesystemencoding() @@ -91,8 +95,8 @@ 'OUTPUT': OUTPUT_ENCODING} FONT_FACE = 'font face' -STOP_RUNNING_TEST = 'Stop a running test' -STEP_OVER = 'Step over' +STOP_RUNNING_TEST = _('Stop a running test') +STEP_OVER = _('Step over') ID_RUN = wx.NewIdRef() ID_RUNDEBUG = wx.NewIdRef() ID_STOP = wx.NewIdRef() @@ -154,7 +158,7 @@ def open_filemanager(path=None): class TestRunnerPlugin(Plugin): - """A plugin for running tests from within RIDE""" + __doc__ = _("""A plugin for running tests from within RIDE""") defaults = {"auto_save": False, "confirm run": True, "profile_name": "robot", @@ -222,13 +226,13 @@ def _register_shortcuts(self): self.register_shortcut('Del', self._delete_pressed) def _delete_pressed(self, event): - _ = event + __ = event if self.notebook.current_page_title != self.title: return self.get_current_profile().delete_pressed() def _copy_from_log_ctrls(self, event): - _ = event + __ = event if self.notebook.current_page_title != self.title: return if self._console_log_ctrl.GetSTCFocus(): @@ -249,17 +253,17 @@ def enable(self): self._create_temporary_directory() def _register_actions(self): - run_action_info = ActionInfo("Tools", "Run Tests", self.on_run, None, + run_action_info = ActionInfo(_("Tools"), _("Run Tests"), self.on_run, None, "F8", ImageProvider().TOOLBAR_PLAY, - "Run the selected tests", position=10) + _("Run the selected tests"), position=10) self._run_action = self.register_action(run_action_info) - run_action_debug = ActionInfo("Tools", "Run Tests with Debug", + run_action_debug = ActionInfo(_("Tools"), _("Run Tests with Debug"), self.on_run_debug, None, "F9", getBugIconBitmap(), - "Run the selected tests with Debug", + _("Run the selected tests with Debug"), position=8) self._run_action = self.register_action(run_action_debug) - stop_action_info = ActionInfo("Tools", "Stop Test Run", self.on_stop, + stop_action_info = ActionInfo(_("Tools"), _("Stop Test Run"), self.on_stop, None, "CtrlCmd-F8", ImageProvider().TOOLBAR_STOP, STOP_RUNNING_TEST, position=11) @@ -341,46 +345,46 @@ def on_stop(self, event): This sends a SIGINT to the running process, with the same effect as typing control-c when running from the command line.""" - _ = event + __ = event self._reset_memory_calc() - self._append_to_console_log('[ SENDING STOP SIGNAL ]\n', + self._append_to_console_log(_('[ SENDING STOP SIGNAL ]\n'), source='stderr') self._test_runner.send_stop_signal() def on_pause(self, event): - _ = event + __ = event self._reset_memory_calc() - self._append_to_console_log('[ SENDING PAUSE SIGNAL ]\n') + self._append_to_console_log(_('[ SENDING PAUSE SIGNAL ]\n')) self._test_runner.send_pause_signal() def on_continue(self, event): - _ = event + __ = event self._reset_memory_calc() - self._append_to_console_log('[ SENDING CONTINUE SIGNAL ]\n') + self._append_to_console_log(_('[ SENDING CONTINUE SIGNAL ]\n')) self._test_runner.send_continue_signal() def on_step_next(self, event): - _ = event + __ = event self._reset_memory_calc() - self._append_to_console_log('[ SENDING STEP NEXT SIGNAL ]\n') + self._append_to_console_log(_('[ SENDING STEP NEXT SIGNAL ]\n')) self._test_runner.send_step_next_signal() def on_step_over(self, event): - _ = event + __ = event self._reset_memory_calc() - self._append_to_console_log('[ SENDING STEP OVER SIGNAL ]\n') + self._append_to_console_log(_('[ SENDING STEP OVER SIGNAL ]\n')) self._test_runner.send_step_over_signal() def on_run(self, event): """ Called when the user clicks or presses the F8, Run Tests """ - _ = event + __ = event self._run_tests() def on_run_debug(self, event): """ Called when the user clicks or presses the F9, Run Tests with Debug It can still be overwritten in RIDE Arguments line """ - _ = event + __ = event self._run_tests("DEBUG") def _run_tests(self, log_level='INFO'): @@ -401,7 +405,7 @@ def _run_tests(self, log_level='INFO'): self._initialize_variables_for_running(profile.get_settings(), command_args) self._initialize_ui_for_running() # DEBUG on Py3 it not shows correct if tags with latin chars - self._append_to_console_log("command: %s\n" % command) + self._append_to_console_log(_("command: %s\n") % command) try: self._test_runner.run_command(command, self._get_current_working_dir(profile)) self._process_timer.Start(41) # roughly 24fps @@ -471,9 +475,9 @@ def _can_start_running_tests(self): @staticmethod def _ask_user_to_save_before_running(): - ret = wx.MessageBox("""There are unsaved modifications. - Do you want to save all changes and run the tests?""", - "Unsaved Modifications", + ret = wx.MessageBox(_("""There are unsaved modifications. + Do you want to save all changes and run the tests?"""), + _("Unsaved Modifications"), wx.ICON_QUESTION | wx.YES_NO) return ret == wx.YES @@ -482,9 +486,9 @@ def _tests_selected(self): @staticmethod def _ask_user_to_run_anyway(): - ret = wx.MessageBox('No tests selected. \n' - 'Continue anyway?', - 'No tests selected', + ret = wx.MessageBox(_('No tests selected. \n' + 'Continue anyway?'), + _('No tests selected'), wx.ICON_QUESTION | wx.YES_NO) return ret == wx.YES @@ -509,14 +513,14 @@ def _clear_text_ctrl(text_ctrl): def on_open_logs_directory(self, event): """Called when the user clicks on the "Open Logs Directory" button""" - _ = event + __ = event if os.path.exists(self._logs_directory): open_filemanager(self._logs_directory) else: self._notify_user_no_logs_directory() def on_show_report(self, event): - _ = event + __ = event """Called when the user clicks on the "Report" button""" if self._report_file: wx.LaunchDefaultBrowser( @@ -524,12 +528,12 @@ def on_show_report(self, event): def on_show_log(self, event): """Called when the user clicks on the "Log" button""" - _ = event + __ = event if self._log_file: wx.LaunchDefaultBrowser("file:%s" % os.path.abspath(self._log_file)) def on_process_ended(self, event): - _ = event + __ = event output, errors, log_message = self._test_runner.get_output_and_errors( self.get_current_profile()) self._append_to_console_log(output) @@ -541,7 +545,7 @@ def on_process_ended(self, event): self._set_stopped() self._progress_bar.Stop() now = datetime.datetime.now().timetuple() - self._append_to_console_log("\nTest finished {}" + self._append_to_console_log(_("\nTest finished {}") .format(robottime.format_time(now))) self._test_runner.command_ended() if log_message: @@ -567,7 +571,7 @@ def _get_report_or_log(output, regex): def on_timer(self, event): """Get process output""" - _ = event + __ = event if not self._log_message_queue.empty(): if self._process.memory_info()[0] <= self._limitmemory: texts = [] @@ -576,7 +580,7 @@ def on_timer(self, event): self._append_to_message_log('\n' + '\n'.join(texts)) else: if not self._maxmemmsg: - self._maxmemmsg = '\n' + "Messages log exceeded 80% of process memory, stopping for now..." + self._maxmemmsg = '\n' + _("Messages log exceeded 80% of process memory, stopping for now...") self._append_to_message_log(self._maxmemmsg, "stderr") if not self._test_runner.is_running(): self.on_process_ended(None) @@ -734,29 +738,29 @@ def _build_runner_toolbar(self, parent): style=wx.TB_HORIZONTAL | wx.TB_HORZ_TEXT | wx.TB_NODIVIDER) toolbar.SetBackgroundColour(self._mysettings.color_background) toolbar.SetForegroundColour(self._mysettings.color_foreground) - toolbar.AddTool(ID_RUN, "Start", ImageProvider().TOOLBAR_PLAY, - wx.NullBitmap, wx.ITEM_NORMAL, shortHelp="Start robot", - longHelp="Start running the robot test suite") - toolbar.AddTool(ID_RUNDEBUG, "Debug", getBugIconBitmap(), wx.NullBitmap, - wx.ITEM_NORMAL, shortHelp="Start robot", - longHelp="Start running the robot test suite " - "with DEBUG loglevel") - toolbar.AddTool(ID_STOP, "Stop", ImageProvider().TOOLBAR_STOP, + toolbar.AddTool(ID_RUN, _("Start"), ImageProvider().TOOLBAR_PLAY, + wx.NullBitmap, wx.ITEM_NORMAL, shortHelp=_("Start robot"), + longHelp=_("Start running the robot test suite")) + toolbar.AddTool(ID_RUNDEBUG, _("Debug"), getBugIconBitmap(), wx.NullBitmap, + wx.ITEM_NORMAL, shortHelp=_("Start robot"), + longHelp=_("Start running the robot test suite " + "with DEBUG loglevel")) + toolbar.AddTool(ID_STOP, _("Stop"), ImageProvider().TOOLBAR_STOP, wx.NullBitmap, wx.ITEM_NORMAL, shortHelp=STOP_RUNNING_TEST, longHelp=STOP_RUNNING_TEST) - toolbar.AddTool(ID_PAUSE, "Pause", ImageProvider().TOOLBAR_PAUSE, + toolbar.AddTool(ID_PAUSE, _("Pause"), ImageProvider().TOOLBAR_PAUSE, wx.NullBitmap, wx.ITEM_NORMAL, - shortHelp="Pause test execution", - longHelp="Pause test execution") - toolbar.AddTool(ID_CONTINUE, "Continue", + shortHelp=_("Pause test execution"), + longHelp=_("Pause test execution")) + toolbar.AddTool(ID_CONTINUE, _("Continue"), ImageProvider().TOOLBAR_CONTINUE, wx.NullBitmap, wx.ITEM_NORMAL, - shortHelp="Continue test execution", - longHelp="Continue test execution") - toolbar.AddTool(ID_STEP_NEXT, "Next", ImageProvider().TOOLBAR_NEXT, - wx.NullBitmap, wx.ITEM_NORMAL, shortHelp="Step next", - longHelp="Step next") + shortHelp=_("Continue test execution"), + longHelp=_("Continue test execution")) + toolbar.AddTool(ID_STEP_NEXT, _("Next"), ImageProvider().TOOLBAR_NEXT, + wx.NullBitmap, wx.ITEM_NORMAL, shortHelp=_("Step next"), + longHelp=_("Step next")) toolbar.AddTool(ID_STEP_OVER, STEP_OVER, ImageProvider().TOOLBAR_NEXT, wx.NullBitmap, wx.ITEM_NORMAL, shortHelp=STEP_OVER, longHelp=STEP_OVER) @@ -781,39 +785,38 @@ def _build_local_toolbar(self, parent): # print(f"DEBUG: toolbar before {toolbar.UseBackgroundColour()}") toolbar.SetOwnBackgroundColour(self._mysettings.color_background) toolbar.SetOwnForegroundColour(self._mysettings.color_foreground) - profile_label = Label(toolbar, label="Execution Profile: ") + profile_label = Label(toolbar, label=_("Execution Profile: ")) choices = self._test_runner.get_profile_names() self.choice = wx.Choice(toolbar, wx.ID_ANY, choices=choices) - self.choice.SetToolTip(wx.ToolTip("Choose which method to use for " - "running the tests")) + self.choice.SetToolTip(wx.ToolTip(_("Choose which method to use for " + "running the tests"))) toolbar.AddControl(profile_label) toolbar.AddControl(self.choice) toolbar.AddSeparator() report_image = getReportIconBitmap() log_image = getLogIconBitmap() - toolbar.AddTool(ID_OPEN_LOGS_DIR, "Open Logs Directory", + toolbar.AddTool(ID_OPEN_LOGS_DIR, _("Open Logs Directory"), ImageProvider().DATADIRIMG, - shortHelp="View All Logs in Explorer") - toolbar.AddTool(ID_SHOW_REPORT, " Report", report_image, - shortHelp=localize_shortcuts("View Robot Report in " - "Browser (CtrlCmd-R)")) - toolbar.AddTool(ID_SHOW_LOG, " Log", log_image, - shortHelp=localize_shortcuts("View Robot Log in" - " Browser (CtrlCmd-L)")) + shortHelp=_("View All Logs in Explorer")) + toolbar.AddTool(ID_SHOW_REPORT, _(" Report"), report_image, + shortHelp=localize_shortcuts(_("View Robot Report in " + "Browser (CtrlCmd-R)"))) + toolbar.AddTool(ID_SHOW_LOG, _(" Log"), log_image, + shortHelp=localize_shortcuts(_("View Robot Log in" + " Browser (CtrlCmd-L)"))) toolbar.AddSeparator() # the toolbar API doesn't give us a way to specify padding which # is why the label has a couple spaces after the colon. gross, # but effective. self.autosave_cb = \ - self._create_check_box(toolbar, ID_AUTOSAVE, " Autosave ", - self.auto_save, "Automatically save all " - "changes before running") + self._create_check_box(toolbar, ID_AUTOSAVE, _(" Autosave "), + self.auto_save, _("Automatically save all changes before running")) toolbar.AddControl(self.autosave_cb) self.pause_on_failure_cb = \ self._create_check_box(toolbar, ID_PAUSE_ON_FAILURE, - " Pause after failure ", False, - "Automatically pause after failing keyword") + _(" Pause after failure "), False, + _("Automatically pause after failing keyword")) toolbar.AddControl(self.pause_on_failure_cb) toolbar.EnableTool(ID_OPEN_LOGS_DIR, False) @@ -921,11 +924,11 @@ def _build_output_panel(self, parent): panel.SetForegroundColour(self._mysettings.color_foreground) self._progress_bar = ProgressBar(panel, self.fail_color, self.pass_color, self.skip_color) self._console_log_panel, self._console_log_ctrl = \ - self._create_collapsible_pane(panel, 'Console log', + self._create_collapsible_pane(panel, _('Console log'), self.show_console_log, self.on_console_log_pane_changed) self._message_log_panel, self._message_log_ctrl = \ - self._create_collapsible_pane(panel, 'Message log', + self._create_collapsible_pane(panel, _('Message log'), self.show_message_log, self.on_message_log_pane_changed) @@ -1048,12 +1051,12 @@ def _test_runner_events_handler(self, event, *args): def _handle_start_test(self, args): longname = args[1]['longname'].encode('utf-8') self._log_message_queue.put( - f"Starting test: {longname.decode(encoding['OUTPUT'], 'backslashreplace')}") + f"{_('Starting test:')} {longname.decode(encoding['OUTPUT'], 'backslashreplace')}") def _handle_end_test(self, args): longname = args[1]['longname'].encode('utf-8') self._log_message_queue.put( - f"Ending test: {longname.decode(encoding['OUTPUT'], 'backslashreplace')}\n") + f"{_('Ending test:')} {longname.decode(encoding['OUTPUT'], 'backslashreplace')}\n") if args[1]['status'] == 'PASS': self._progress_bar.add_pass() elif args[1]['status'] == 'SKIP': @@ -1061,7 +1064,7 @@ def _handle_end_test(self, args): elif args[1]['status'] == 'FAIL': self._progress_bar.add_fail() else: - self._log_message_queue.put(f"UNKNOWN STATUS: {args[1]['status']}\n") + self._log_message_queue.put(f"{_('UNKNOWN STATUS:')} {args[1]['status']}\n") def _handle_report_file(self, args): self._report_file = args[0] @@ -1087,14 +1090,14 @@ def _handle_log_message(self, args): self._log_message_queue.put(prefix + message) def _handle_paused(self, args): - _ = args + __ = args wx.CallAfter(self._set_paused) - self._log_message_queue.put('<< PAUSED >>') + self._log_message_queue.put(_('<< PAUSED >>')) def _handle_continue(self, args): - _ = args + __ = args wx.CallAfter(self._set_continue) - self._log_message_queue.put('<< CONTINUE >>') + self._log_message_queue.put(_('<< CONTINUE >>')) def _set_running(self): self._run_action.disable() @@ -1135,9 +1138,9 @@ def _enable_runner_toolbar(self, run, paused): @staticmethod def _notify_user_no_logs_directory(): - wx.MessageBox("There isn't logs directory. \n" - "Please, run the tests and try again", - "No logs directory", + wx.MessageBox(_("There isn't logs directory. \n" + "Please, run the tests and try again"), + _("No logs directory"), wx.ICON_INFORMATION | wx.OK) @@ -1179,7 +1182,7 @@ def empty_current_keyword(self): def on_timer(self, event): """A handler for timer events; it updates the statusbar""" - _ = event + __ = event self._gauge.Show() self._gauge.Pulse() self._update_message() @@ -1222,7 +1225,7 @@ def get_visible_color(self, color): def _update_message(self): """Update the displayed elapsed time, passed and failed counts""" elapsed = time.time() - self._start_time - message = "elapsed time: %s pass: %s skip: %s fail: %s" % ( + message = _("elapsed time: %s pass: %s skip: %s fail: %s") % ( self._seconds_to_string(elapsed), self._pass, self._skip, self._fail) message += self._get_current_keyword_text() self._label.SetLabel(message) @@ -1242,8 +1245,7 @@ def _update_message(self): def _get_current_keyword_text(self): if not self._current_keywords: return '' - return ' current keyword: ' + \ - self._fix_size(' -> '.join(self._current_keywords), 50) + return _(' current keyword: ') + self._fix_size(' -> '.join(self._current_keywords), 50) @staticmethod def _fix_size(text, max_length): diff --git a/src/robotide/controller/basecontroller.py b/src/robotide/controller/basecontroller.py index 66380720a..b152ba75b 100644 --- a/src/robotide/controller/basecontroller.py +++ b/src/robotide/controller/basecontroller.py @@ -64,12 +64,16 @@ def datafile(self): def datafiles(self): return self._parent.datafiles + @property + def language(self): + return self._parent.datafile_controller._language + def is_modifiable(self): return self.datafile_controller.is_modifiable() class WithNamespace(object): - namespace = None # Ensure namespace exists + namespace = None # Ensure namespace exists def _set_namespace_from(self, controller): self._set_namespace(controller.namespace) diff --git a/src/robotide/controller/dataloader.py b/src/robotide/controller/dataloader.py index 39a58136d..a9e3f30b1 100644 --- a/src/robotide/controller/dataloader.py +++ b/src/robotide/controller/dataloader.py @@ -16,29 +16,48 @@ import os from threading import Thread +from robotide.lib.compat.parsing import language as lang +from robotide.lib.robot.errors import DataError from .. import robotapi class DataLoader(object): - def __init__(self, namespace, settings): + def __init__(self, namespace, settings, language=None): self.namespace = namespace self.namespace.reset_resource_and_library_cache() self._settings = settings + self.language = language - def load_datafile(self, path, load_observer): - return self._load(_DataLoader(path, self._settings), load_observer) + def load_datafile(self, path, load_observer, language=None): + if not language: + self.language = lang.check_file_language(path) + else: + self.language = language + return self._load(_DataLoader(path, self._settings, language=self.language), load_observer) - def load_initfile(self, path, load_observer): - return self._load(_InitFileLoader(path), load_observer) + def load_initfile(self, path, load_observer, language=None): + if not language: + self.language = lang.check_file_language(path) + else: + self.language = language + # print(f"DEBUG: datloder.py DataLoader.load_initfile ENTER self.language={self.language} language={language}") + return self._load(_InitFileLoader(path, language=self.language), load_observer) - def load_resource_file(self, datafile, load_observer): - return self._load(_ResourceLoader( - datafile, self.namespace.get_resource), load_observer) + def load_resource_file(self, datafile, load_observer, language=None): + if not language: + self.language = lang.check_file_language(datafile) + else: + self.language = language + return self._load(_ResourceLoader(datafile, self.namespace.get_resource, language=self.language), load_observer) - def resources_for(self, datafile, load_observer): - return self._load(_ResourceLoader( - datafile, self.namespace.get_resources), load_observer) + def resources_for(self, datafile, load_observer, language=None): + if not language: + self.language = lang.check_file_language(datafile.source) + else: + self.language = language + return self._load(_ResourceLoader(datafile, self.namespace.get_resources, language=self.language), + load_observer) def _load(self, loader, load_observer): self._wait_until_loaded(loader, load_observer) @@ -71,85 +90,106 @@ def _run(self): class _DataLoader(_DataLoaderThread): - def __init__(self, path, settings): + def __init__(self, path, settings, language=None): _DataLoaderThread.__init__(self) self._path = path self._settings = settings + self.language = language def _run(self): - # print(f"DEBUG: Dataloader returning TestData source={self._path}") - return test_data(source=self._path, settings=self._settings) + if not self.language: + self.language = lang.check_file_language(self._path) + return test_data(source=self._path, settings=self._settings, language=self.language) class _InitFileLoader(_DataLoaderThread): - def __init__(self, path, settings=None): + def __init__(self, path, settings=None, language=None): _DataLoaderThread.__init__(self) self._path = path self._settings = settings + self.language = language def _run(self): - result = robotapi.TestDataDirectory(source=os.path.dirname(self._path), settings=self._settings) + result = robotapi.TestDataDirectory(source=os.path.dirname(self._path), settings=self._settings, + language=self.language) result.initfile = self._path - robotapi.FromFilePopulator(result).populate(self._path) + if not self.language: + self.language = lang.check_file_language(self._path) + robotapi.FromFilePopulator(result, lang=self.language).populate(self._path) return result class _ResourceLoader(_DataLoaderThread): - def __init__(self, datafile, resource_loader): + def __init__(self, datafile, resource_loader, language=None): _DataLoaderThread.__init__(self) self._datafile = datafile + self.language = language self._loader = resource_loader def _run(self): - return self._loader(self._datafile) + return self._loader(self._datafile, self.language) class TestDataDirectoryWithExcludes(robotapi.TestDataDirectory): - def __init__(self, parent, source, settings): + def __init__(self, parent, source, settings, language=None): self._settings = settings - robotapi.TestDataDirectory.__init__(self, parent, source, settings=self._settings) + self.language = language + robotapi.TestDataDirectory.__init__(self, parent, source, settings=self._settings, language=self.language) def add_child(self, path, include_suites, extensions=None, - warn_on_skipped=False): + warn_on_skipped=False, language=None): if not self._settings.excludes.contains(path): - self.children.append(test_data( - parent=self, source=path, settings=self._settings)) + self.children.append(test_data(parent=self, source=path, settings=self._settings, language=self.language)) else: - self.children.append(ExcludedDirectory(self, path)) + self.children.append(ExcludedDirectory(self, path, language=self.language)) -def test_data(source, parent=None, settings=None): +def test_data(source, parent=None, settings=None, language=None): """Parses a file or directory to a corresponding model object. :param source: path where test data is read from. :returns: :class:`~.model.TestDataDirectory` if `source` is a directory, :class:`~.model.TestCaseFile` otherwise. """ + # Check if opening an __init__.robot + if os.path.basename(source) == '__init__.robot': + source = os.path.dirname(source) if os.path.isdir(source): - # print("DEBUG: Dataloader Is dir getting testdada %s\n" % source) - data = TestDataDirectoryWithExcludes(parent, source, settings) + if not language: + init_file = os.path.join(source, '__init__.robot') + if os.path.isfile(init_file): + language = lang.check_file_language(init_file) + data = TestDataDirectoryWithExcludes(parent, source, settings, language) # print("DEBUG: Dataloader testdata %s\n" % data.name) data.populate() # print("DEBUG: Dataloader after populate %s %s\n" % (data._tables, data.name)) return data - # print("DEBUG: Dataloader returning TestCaseFile") - datafile = robotapi.TestCaseFile(parent, source, settings).populate() + language = language if language else lang.check_file_language(source) + # print(f"DEBUG: Dataloader TestCaseFile getting datafile language={language}") + datafile = None + try: + datafile = robotapi.TestCaseFile(parent, source, settings, language).populate() + except DataError: + # print(f"DEBUG: Dataloader TestCaseFile testdata DataError source={source}") + pass # We try once more in case is a Resource if datafile: + # print(f"DEBUG: Dataloader TestCaseFile return datafile={datafile}") return datafile if source.endswith(("resource", "robot")): - datafile = robotapi.ResourceFile(source, settings).populate() + datafile = robotapi.ResourceFile(source, settings, language).populate() # print(f"DEBUG: Dataloader returning TestCaseFile datafile={datafile}, type={type(datafile)}") return datafile class ExcludedDirectory(robotapi.TestDataDirectory): - def __init__(self, parent, path): + def __init__(self, parent, path, language=None): self._parent = parent self._path = path - robotapi.TestDataDirectory.__init__(self, parent, path) + self.language = language + robotapi.TestDataDirectory.__init__(self, parent, path, language=self.language) def has_tests(self): return True diff --git a/src/robotide/controller/filecontrollers.py b/src/robotide/controller/filecontrollers.py index c26c68de7..5a788591d 100644 --- a/src/robotide/controller/filecontrollers.py +++ b/src/robotide/controller/filecontrollers.py @@ -125,6 +125,11 @@ def __init__(self, data, project=None, parent=None): else: self._resource_file_controller_factory = None self.parent = parent + try: + self._language = self.data._language + # print(f"DEBUG: filecontrollers.py _DataController language set = {self._language}") + except AttributeError: + self._language = ['en'] self.set_datafile(data) self.dirty = False self.children = self._children(data) @@ -443,6 +448,10 @@ def default_dir(self): def display_name(self): return self.data.name + @property + def language(self): + return self._language + @property def longname(self): if self.parent: @@ -501,7 +510,7 @@ def _add_directory_children(self, children, path, initfile): @staticmethod def _is_robot_ignored_name(filename): base = os.path.basename(filename) - robotformat = (".txt", ".robot", ".resource", ".rst", " .rest", ".tsv", ".htm", ".html") + robotformat = (".txt", ".robot", ".resource", ".rst", " .rest", ".tsv") # Removed ".htm", ".html" nonrobot_file = os.path.isfile(filename) and not base.endswith(robotformat) hidden = base.startswith('.') private = base.startswith('_') @@ -735,6 +744,10 @@ def suites(self): def preamble(self): return self.data.preamble + @property + def language(self): + return self.data.language + def contains_tests(self): return bool(self.tests) @@ -891,6 +904,10 @@ def display_name(self): _, tail = os.path.split(self.data.source) return tail + @property + def language(self): + return self._language + def is_modifiable(self): return not self.exists() or not self.is_readonly() diff --git a/src/robotide/controller/project.py b/src/robotide/controller/project.py index d11b21e8e..78ab34d24 100644 --- a/src/robotide/controller/project.py +++ b/src/robotide/controller/project.py @@ -29,7 +29,7 @@ class Project(_BaseController, WithNamespace): - def __init__(self, namespace=None, settings=None, library_manager=None): + def __init__(self, namespace=None, settings=None, library_manager=None, file_language=None): from .filecontrollers import ResourceFileControllerFactory self._library_manager = self._construct_library_manager(library_manager, settings) if not self._library_manager.is_alive(): @@ -41,6 +41,7 @@ def __init__(self, namespace=None, settings=None, library_manager=None): self.controller = None self.name = None self.external_resources = [] + self.file_language = file_language self._resource_file_controller_factory = ResourceFileControllerFactory(self._name_space, self) self._serializer = Serializer(settings, LOG) @@ -119,19 +120,21 @@ def new_resource(self, path, parent=None): def load_data(self, path, load_observer=None): """ DEBUG: To be used in Localization + """ from robotide.context import APP try: robot_version = APP.robot_version except AttributeError: - robot_version = b'6.1.1' # It is failing at unit tests - print(f"DEBUG: project.py Project ENTER robot version = {robot_version}") - """ + robot_version = '3.1.2' # It is failing at unit tests + print(f"DEBUG: project.py Project load_data robot version = {robot_version}") + from ..lib.compat.parsing.language import check_file_language + self.file_language = check_file_language(path) load_observer = load_observer or NullObserver() - if self._load_initfile(path, load_observer): + if self._load_initfile(path, load_observer, self.file_language): return - if self._load_datafile(path, load_observer): + if self._load_datafile(path, load_observer, self.file_language): return - if self._load_resource(path, load_observer): + if self._load_resource(path, load_observer, self.file_language): return try: load_observer.error("Given file '%s' is not a valid Robot Framework " @@ -143,39 +146,41 @@ def load_data(self, path, load_observer=None): def is_excluded(self, source=None): return self.internal_settings.excludes.contains(source) if self.internal_settings else False - def _load_initfile(self, path, load_observer): + def _load_initfile(self, path, load_observer, language=None): if os.path.splitext(os.path.split(path)[1])[0] != '__init__': return None - initfile = self._loader.load_initfile(path, load_observer) + initfile = self._loader.load_initfile(path, load_observer, language) if not initfile: return None - self._populate_from_datafile(path, initfile, load_observer) + self._populate_from_datafile(path, initfile, load_observer, language) return initfile - def load_datafile(self, path, load_observer): - datafile = self._load_datafile(path, load_observer) + def load_datafile(self, path, load_observer, language=None): + datafile = self._load_datafile(path, load_observer, language) if datafile: return datafile # print(f"DEBUG: project Before testing Resource load_datafile path={path}") # Let's see if it is a .robot file valid as .resource if path.endswith((".robot", ".resource")): - datafile = self._load_resource(path, load_observer) + datafile = self._load_resource(path, load_observer, language) if datafile: return datafile load_observer.error("Invalid data file '%s'." % path) e = UserWarning("Invalid data file") return e - def _load_datafile(self, path, load_observer): - # print(f"DEBUG: project ENTER _load_datafile path={path}") - datafile = self._loader.load_datafile(path, load_observer) + def _load_datafile(self, path, load_observer, language=None): + # print(f"DEBUG: project ENTER _load_datafile path={path} self.file_language={self.file_language}" + # f" language={language}") + datafile = self._loader.load_datafile(path, load_observer, self.file_language) if not datafile: return None - self._populate_from_datafile(path, datafile, load_observer) + self._populate_from_datafile(path, datafile, load_observer, language) return datafile - def _populate_from_datafile(self, path, datafile, load_observer): - self.__init__(self.namespace, self.internal_settings, library_manager=self._library_manager) + def _populate_from_datafile(self, path, datafile, load_observer, language=None): + self.__init__(self.namespace, self.internal_settings, library_manager=self._library_manager, + file_language=language) resources = self._loader.resources_for(datafile, load_observer) self._create_controllers(datafile, resources) RideOpenSuite(path=path, datafile=self.controller).publish() @@ -192,14 +197,14 @@ def _create_controllers(self, datafile, resources): for controller in new_resource_controllers: self._inform_resource_created(controller) - def load_resource(self, path, load_observer): - resource = self._load_resource(path, load_observer) + def load_resource(self, path, load_observer, language=None): + resource = self._load_resource(path, load_observer, language) if resource: return resource load_observer.error("Invalid resource file '%s'." % path) - def _load_resource(self, path, load_observer): - resource = self._loader.load_resource_file(path, load_observer) + def _load_resource(self, path, load_observer, language=None): + resource = self._loader.load_resource_file(path, load_observer, language) if not resource: return None ctrl = self._create_resource_controller(resource) diff --git a/src/robotide/controller/robotdata.py b/src/robotide/controller/robotdata.py index 589683a0c..4f513a171 100644 --- a/src/robotide/controller/robotdata.py +++ b/src/robotide/controller/robotdata.py @@ -20,7 +20,7 @@ def new_test_case_file(path): datafile = robotapi.TestCaseFile(source=path) - datafile.start_table(['Test Cases']) + datafile.start_table(['Test Cases'], lineno=1, llang=['en']) # It is the unique section, so no problem _create_missing_directories(datafile.directory) return datafile diff --git a/src/robotide/controller/settingcontrollers.py b/src/robotide/controller/settingcontrollers.py index c4f0cb411..945690418 100644 --- a/src/robotide/controller/settingcontrollers.py +++ b/src/robotide/controller/settingcontrollers.py @@ -651,7 +651,7 @@ def _has_format(self): parts = self.name.rsplit('.', 1) if len(parts) == 1: return False - return parts[-1].lower() in ['html', 'txt', 'tsv', 'robot', 'resource'] + return parts[-1].lower() in ['txt', 'tsv', 'robot', 'resource'] class LibraryImportController(_ImportController): diff --git a/src/robotide/controller/ui/treecontroller.py b/src/robotide/controller/ui/treecontroller.py index b593c7b5d..281660324 100644 --- a/src/robotide/controller/ui/treecontroller.py +++ b/src/robotide/controller/ui/treecontroller.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from robotide import utils @@ -23,12 +24,8 @@ from robotide.controller.tags import Tag, DefaultTag from robotide.publish import RideTestSelectedForRunningChanged -tree_actions = """ -[Navigate] -!Go &Back | Go back to previous location in tree | Alt-%s | ART_GO_BACK -!Go &Forward | Go forward to next location in tree | Alt-%s | ART_GO_FORWARD -""" % (('Left', 'Right') if IS_WINDOWS else ('Z', 'X')) -# Left and right cannot be overridden in tree on non Windows OSses, issue 354 +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation class TreeController(object): @@ -41,33 +38,45 @@ def __init__(self, tree, action_registerer, settings, test_selection, history=No self._test_selection = test_selection def register_tree_actions(self): - actions = action_info_collection(tree_actions, self, self._tree) + tree_actions = _("""[Navigate] + !Go &Back | Go back to previous location in tree | Alt-%s | ART_GO_BACK + !Go &Forward | Go forward to next location in tree | Alt-%s | ART_GO_FORWARD + """) % (('Left', 'Right') if IS_WINDOWS else ('Z', 'X')) + # Left and right cannot be overridden in tree on non Windows OSses, issue 354 + + tree_actions_nt = """[Navigate] + !Go &Back | Go back to previous location in tree | Alt-%s | ART_GO_BACK + !Go &Forward | Go forward to next location in tree | Alt-%s | ART_GO_FORWARD + """ % (('Left', 'Right') if IS_WINDOWS else ('Z', 'X')) + + # print(f"DEBUG: treecontroller.py register_tree_actions ENTER tree_actions={tree_actions}") + actions = action_info_collection(tree_actions, self, data_nt=tree_actions_nt, container=self._tree) self._action_registerer.register_actions(actions) - self._action_registerer.register_action(ActionInfo(menu_name='Edit', name='Add Tag to selected', + self._action_registerer.register_action(ActionInfo(menu_name=_('Edit'), name=_('Add Tag to selected'), action=self.on_add_tag_to_selected)) - self._action_registerer.register_action(ActionInfo(menu_name='Edit', name='Clear Selected', + self._action_registerer.register_action(ActionInfo(menu_name=_('Edit'), name=_('Clear Selected'), action=self.on_clear_selected)) def on_go_back(self, event): - _ = event + __ = event node = self._history.back() if node: self._tree.SelectItem(node) def on_add_tag_to_selected(self, event): - _ = event + __ = event if self._test_selection.is_empty(): return - name = wx.GetTextFromUser(message='Enter Tag Name', caption='Add Tag To Selected') + name = wx.GetTextFromUser(message=_('Enter Tag Name'), caption=_('Add Tag To Selected')) if name: self._test_selection.add_tag(name) def on_clear_selected(self, event): - _ = event + __ = event self._test_selection.clear_all(message=None) def on_go_forward(self, event): - _ = event + __ = event node = self._history.forward() if node: self._tree.SelectItem(node) diff --git a/src/robotide/editor/__init__.py b/src/robotide/editor/__init__.py index 499766e5f..ebe863a68 100644 --- a/src/robotide/editor/__init__.py +++ b/src/robotide/editor/__init__.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from .editorcreator import EditorCreator @@ -21,8 +22,35 @@ from ..publish.messages import RideDataFileRemoved from ..widgets import PopupCreator -_EDIT = """ -[Edit] +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + +_EDIT = _("""[Edit] +&Undo | Undo last modification | Ctrlcmd-Z +&Redo | Redo modification | Ctrlcmd-Y +--- +Cu&t | Cut | Ctrlcmd-X +&Copy | Copy | Ctrlcmd-C +&Paste | Paste | Ctrlcmd-V +&Insert | Insert | Shift-Ctrl-V +&Delete | Delete | Del +--- +Comment Rows | Comment selected rows | Ctrlcmd-3 +Comment Cells | Comment cells with # | Ctrlcmd-Shift-3 +Uncomment Rows | Uncomment selected rows | Ctrlcmd-4 +Uncomment Cells | Uncomment cells with # | Ctrlcmd-Shift-4 +--- +Insert Cells | Insert Cells | Ctrlcmd-Shift-I +Delete Cells | Delete Cells | Ctrlcmd-Shift-D +Insert Rows | Insert Rows | Ctrlcmd-I +Delete Rows | Delete Rows | Ctrlcmd-D +Move Rows Up | Move Rows Up | Alt-Up +Move Rows Down | Move Rows Down | Alt-Down +[Tools] +Content Assistance (Ctrl-Space or Ctrl-Alt-Space) | Show possible keyword and variable completions | | | POSITION-70 +""") + +_EDIT_nt = """[Edit] &Undo | Undo last modification | Ctrlcmd-Z &Redo | Redo modification | Ctrlcmd-Y --- @@ -64,7 +92,7 @@ def __init__(self, application): def enable(self): self._creator.register_editors() self._show_editor() - self.register_actions(action_info_collection(_EDIT, self._tab, self._tab)) + self.register_actions(action_info_collection(_EDIT, self._tab, data_nt=_EDIT_nt, container=self._tab)) self.subscribe(self.on_tree_item_selected, RideTreeSelection) self.subscribe(self.on_tab_changed, RideNotebookTabChanged) self.subscribe(self.on_tab_changing, RideNotebookTabChanging) @@ -135,7 +163,7 @@ def get_selected_datafile(self): return Plugin.get_selected_datafile(self) def on_open_editor(self, event): - _ = event + __ = event self._show_editor() def on_tab_changed(self, message): @@ -188,89 +216,88 @@ def hide_editor(self): self.Show(False) def on_save(self, event): - _ = event + __ = event self.plugin.save_selected_datafile() def on_undo(self, event): - _ = event + __ = event self.editor.undo() def on_redo(self, event): - _ = event + __ = event self.editor.redo() def on_cut(self, event): - _ = event + __ = event self.editor.cut() def on_copy(self, event): - _ = event + __ = event self.editor.copy() def on_paste(self, event): - _ = event + __ = event self.editor.paste() def on_insert(self, event): - _ = event + __ = event self.editor.insert() def on_insert_cells(self, event): - _ = event + __ = event self.editor.insert_cells() def on_delete_cells(self, event): - _ = event + __ = event # print("DEBUG init delete cells call") self.editor.delete_cells() def on_insert_rows(self, event): - _ = event + __ = event self.editor.insert_rows() def on_delete_rows(self, event): - _ = event - print(f"DEBUG: Editor __ini__ called {event}") + __ = event wx.CallAfter(self.editor.delete_rows) def on_move_rows_up(self, event): - _ = event + __ = event self.editor.on_move_rows_up() def on_move_rows_down(self, event): - _ = event + __ = event self.editor.on_move_rows_down() def on_delete(self, event): - _ = event + __ = event self.editor.delete() def on_comment_rows(self, event): - _ = event + __ = event self.editor.comment_rows() def on_uncomment_rows(self, event): - _ = event + __ = event self.editor.uncomment_rows() def on_sharp_comment_rows(self, event): - _ = event + __ = event self.editor.sharp_comment_rows() def on_sharp_uncomment_rows(self, event): - _ = event + __ = event self.editor.sharp_uncomment_rows() def on_comment_cells(self, event): - _ = event + __ = event self.editor.comment_cells() def on_uncomment_cells(self, event): - _ = event + __ = event self.editor.uncomment_cells() def on_content_assistance(self, event): - _ = event + __ = event self.editor.show_content_assist() def save(self, message=None): diff --git a/src/robotide/editor/contentassist.py b/src/robotide/editor/contentassist.py index f3ff02830..3fe422305 100755 --- a/src/robotide/editor/contentassist.py +++ b/src/robotide/editor/contentassist.py @@ -277,7 +277,7 @@ def fill_suggestion(self): self.hide() def pop_event_handlers(self, event): - _ = event + __ = event # all pushed eventHandlers need to be popped before close # the last event handler is window object itself - do not pop itself if self: @@ -285,7 +285,7 @@ def pop_event_handlers(self, event): self.PopEventHandler() def on_destroy(self, event): - _ = event + __ = event # all pushed eventHandlers need to be popped before close # the last event handler is window object itself - do not pop itself while self.GetEventHandler() is not self: @@ -414,7 +414,7 @@ def OnBrowse(self, evt=None): # Overrides wx method self._browsed = False def on_destroy(self, event): - _ = event + __ = event # all pushed eventHandlers need to be popped before close # the last event handler is window object itself - do not pop itself try: @@ -598,7 +598,7 @@ def hide(self): self._details_popup.Show(False) def on_list_item_activated(self, event): - _ = event + __ = event self._parent.fill_suggestion() def on_list_item_selected(self, event): diff --git a/src/robotide/editor/customsourceeditor.py b/src/robotide/editor/customsourceeditor.py index 495993824..bb0c3f11a 100644 --- a/src/robotide/editor/customsourceeditor.py +++ b/src/robotide/editor/customsourceeditor.py @@ -614,7 +614,7 @@ def JumpToLine(self, line, highlight=False): self.editor.SelectLine(line) def on_code_modified(self, event): - _ = event + __ = event self.btnSave.Enable(self.editor.IsModified()) def on_save(self, event, filepath=None): diff --git a/src/robotide/editor/editordialogs.py b/src/robotide/editor/editordialogs.py index 33502efc7..e4b997c9c 100644 --- a/src/robotide/editor/editordialogs.py +++ b/src/robotide/editor/editordialogs.py @@ -25,18 +25,24 @@ from .fieldeditors import (ValueEditor, ListValueEditor, MultiLineEditor, ContentAssistEditor, VariableNameEditor, ArgumentEditor, FileNameEditor) from .formatters import ListToStringFormatter +from robotide.lib.compat.parsing import language -def editor_dialog(obj): - return globals()[obj.label.replace(' ', '') + 'Dialog'] +def editor_dialog(obj, lang='en'): + english_label = language.get_english_label(lang, obj.label).replace('Task', 'Test') + # print(f"DEBUG: editordialogs.py editor_dialog object name={obj.label} english_label={english_label}" + # f"lang={lang} ") + return globals()[english_label.replace(' ', '') + 'Dialog'] class _Dialog(RIDEDialog): _title = property(lambda self: utils.name_from_class(self, drop='Dialog')) - def __init__(self, controller, item=None, plugin=None): + def __init__(self, controller, item=None, plugin=None, title=None): # DEBUG: Get rid of item, everything should be in controller - RIDEDialog.__init__(self, self._title) + if not title: + title = self._title + RIDEDialog.__init__(self, title) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY) diff --git a/src/robotide/editor/editors.py b/src/robotide/editor/editors.py index e42f87e03..db8a4bf8e 100644 --- a/src/robotide/editor/editors.py +++ b/src/robotide/editor/editors.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from abc import abstractmethod @@ -29,6 +30,9 @@ from ..widgets import ( ButtonWithHandler, Label, HeaderLabel, HorizontalSizer, HtmlWindow) +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + LIGHT_GREY = 'light grey' @@ -72,6 +76,10 @@ def __init__(self, plugin, parent, controller, tree): self.SetForegroundColour(Colour(self.color_foreground)) self.plugin = plugin self.controller = controller + try: + self.language = controller.datafile_controller.language + except AttributeError: + self.language = ['en'] self._tree = tree def tree_item_selected(self, item): @@ -118,7 +126,7 @@ def _settings_changed(self, message): editor.update_value() def on_idle(self, event): - _ = event + __ = event if self._last_shown_tooltip and self._mouse_outside_tooltip(): self._last_shown_tooltip.hide() self._reset_last_show_tooltip() @@ -151,7 +159,7 @@ def w_destroy(self): def _create_header(self, text, readonly=False): if readonly: - text += ' (READ ONLY)' + text += _(' (READ ONLY)') self._title_display = HeaderLabel(self, text) return self._title_display @@ -213,7 +221,7 @@ class Settings(wx.CollapsiblePane): def __init__(self, parent): wx.CollapsiblePane.__init__( - self, parent, wx.ID_ANY, 'Settings', + self, parent, wx.ID_ANY, _('Settings'), style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) from ..preferences import RideSettings _settings = RideSettings() @@ -332,7 +340,7 @@ def _populate(self): def _create_source_label(self, source): sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add((5, 0)) - sizer.Add(Label(self, label='Source', size=(context.SETTING_LABEL_WIDTH, + sizer.Add(Label(self, label=_('Source'), size=(context.SETTING_LABEL_WIDTH, context.SETTING_ROW_HEIGHT))) self._source = wx.TextCtrl(self, style=wx.TE_READONLY | wx.NO_BORDER) self._source.SetBackgroundColour(Colour(self.color_background)) @@ -370,7 +378,7 @@ def __init__(self, parent, header, usages_callback, color_foreground, color_back HorizontalSizer.__init__(self) self._header = HeaderLabel(parent, header) self.add_expanding(self._header) - self.add_sizer(ButtonWithHandler(parent, 'Find Usages', usages_callback, + self.add_sizer(ButtonWithHandler(parent, _('Find Usages'), handler=usages_callback, color_secondary_foreground=color_foreground, color_secondary_background=color_background)) @@ -383,10 +391,10 @@ class ResourceFileEditor(_FileEditor): def _create_header(self, text, readonly=False): if readonly: - text += ' (READ ONLY)' + text += _(' (READ ONLY)') def cb(event): - _ = event + __ = event ResourceFileUsages(self.controller, self._tree.highlight).show() self._title_display = FindUsagesHeader(self, text, cb, color_foreground=self.color_secondary_foreground, color_background=self.color_secondary_background) diff --git a/src/robotide/editor/fieldeditors.py b/src/robotide/editor/fieldeditors.py index e50c25b43..92a53f6ae 100644 --- a/src/robotide/editor/fieldeditors.py +++ b/src/robotide/editor/fieldeditors.py @@ -147,7 +147,7 @@ def on_focus(self, event): event.Skip() def SetSelection(self, event): - _ = event + __ = event self._editor.SetSelection(2, len(self._editor.Value) - 1) @@ -310,27 +310,27 @@ def _delete_cells_from_multiple_rows(self, event): GridEditor.on_delete_cells(self, event) def on_copy(self, event): - _ = event + __ = event self.copy() def on_cut(self, event): - _ = event + __ = event self.cut() def on_paste(self, event): - _ = event + __ = event self.paste() def on_delete(self, event): - _ = event + __ = event self.delete() def on_undo(self, event): - _ = event + __ = event self.undo() def on_select_all(self, event): - _ = event + __ = event self.SelectAll() def resize_columns(self, width): diff --git a/src/robotide/editor/gridcolorizer.py b/src/robotide/editor/gridcolorizer.py index b6e8656c4..ed090bc5d 100755 --- a/src/robotide/editor/gridcolorizer.py +++ b/src/robotide/editor/gridcolorizer.py @@ -97,15 +97,15 @@ class ColorizationSettings(object): def __init__(self, settings=None): self._settings = settings - def get_background_color(self, type): + def get_background_color(self, elem_type): if not self._settings: return self.DEFAULT_BACKGROUND - return self._get('background %s' % type) + return self._get('background %s' % elem_type) - def get_text_color(self, type): + def get_text_color(self, elem_type): if not self._settings: return self.DEFAULT_TEXT - return self._get('text %s' % type) + return self._get('text %s' % elem_type) def get_highlight_color(self): return self.get_background_color('highlight') diff --git a/src/robotide/editor/kweditor.py b/src/robotide/editor/kweditor.py index 0369c7739..f5c052dff 100755 --- a/src/robotide/editor/kweditor.py +++ b/src/robotide/editor/kweditor.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import json from json.decoder import JSONDecodeError @@ -42,6 +43,9 @@ from ..utils import variablematcher from ..widgets import RIDEDialog, PopupMenu, PopupMenuItems +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + _DEFAULT_FONT_SIZE = 11 @@ -64,31 +68,31 @@ class KeywordEditor(GridEditor, Plugin): dirty = property(lambda self: self.controller.dirty) _popup_items = [ - 'Create Keyword', - 'Extract Keyword', - 'Extract Variable', - 'Rename Keyword', - 'Find Where Used', - 'JSON Editor\tCtrl-Shift-J', + _('Create Keyword'), + _('Extract Keyword'), + _('Extract Variable'), + _('Rename Keyword'), + _('Find Where Used'), + _('JSON Editor\tCtrl-Shift-J'), '---', - 'Go to Definition\tCtrl-B', + _('Go to Definition\tCtrl-B'), '---', - 'Undo\tCtrl-Z', - 'Redo\tCtrl-Y', + _('Undo\tCtrl-Z'), + _('Redo\tCtrl-Y'), '---', - 'Make Variable\tCtrl-1', - 'Make List Variable\tCtrl-2', - 'Make Dict Variable\tCtrl-5', + _('Make Variable\tCtrl-1'), + _('Make List Variable\tCtrl-2'), + _('Make Dict Variable\tCtrl-5'), '---', - 'Comment Cells\tCtrl-Shift-3', - 'Uncomment Cells\tCtrl-Shift-4', - 'Move Cursor Down\tAlt-Enter', + _('Comment Cells\tCtrl-Shift-3'), + _('Uncomment Cells\tCtrl-Shift-4'), + _('Move Cursor Down\tAlt-Enter'), '---', - 'Comment Rows\tCtrl-3', - 'Uncomment Rows\tCtrl-4', - 'Move Rows Up\tAlt-Up', - 'Move Rows Down\tAlt-Down', - 'Swap Row Up\tCtrl-T', + _('Comment Rows\tCtrl-3'), + _('Uncomment Rows\tCtrl-4'), + _('Move Rows Up\tAlt-Up'), + _('Move Rows Down\tAlt-Down'), + _('Swap Row Up\tCtrl-T'), '---', ] + GridEditor._popup_items @@ -277,16 +281,16 @@ def _row_label_right_click(self, event): self.SelectRow(selected_row, addToSelected=False) self.SetGridCursor(event.Row, 0) popupitems = [ - 'Comment Rows\tCtrl-3', - 'Uncomment Rows\tCtrl-4', - 'Move Rows Up\tAlt-Up', - 'Move Rows Down\tAlt-Down', - 'Swap Row Up\tCtrl-T', - 'Insert Rows\tCtrl-I', - 'Delete Rows\tCtrl-D', + _('Comment Rows\tCtrl-3'), + _('Uncomment Rows\tCtrl-4'), + _('Move Rows Up\tAlt-Up'), + _('Move Rows Down\tAlt-Down'), + _('Swap Row Up\tCtrl-T'), + _('Insert Rows\tCtrl-I'), + _('Delete Rows\tCtrl-D'), '---', - 'Comment Cells\tCtrl-Shift-3', - 'Uncomment Cells\tCtrl-Shift-4', + _('Comment Cells\tCtrl-Shift-3'), + _('Uncomment Cells\tCtrl-Shift-4'), ] PopupMenu(self, PopupMenuItems(self, popupitems)) event.Skip() @@ -390,15 +394,15 @@ def on_sharp_uncomment_rows(self, event=None): self._skip_except_on_mac(event) def on_move_rows_up(self, event=None): - _ = event + __ = event self._row_move(MoveRowsUp, -1) def on_move_rows_down(self, event=None): - _ = event + __ = event self._row_move(MoveRowsDown, 1) def on_swap_row_up(self, event=None): - _ = event + __ = event self._row_move(MoveRowsUp, 1, True) def _row_move(self, command, change, swap=False): @@ -466,8 +470,7 @@ def _write_headers(self, controller): self.SetColLabelValue(empty_col, '') def _colorize_grid(self): - selection_content = \ - self._get_single_selection_content_or_none_on_first_call() + selection_content = self._get_single_selection_content_or_none_on_first_call() if selection_content is None: self.highlight(None) else: @@ -509,7 +512,7 @@ def get_selected_datafile_controller(self): # DEBUG @requires_focus def on_copy(self, event=None): - _ = event + __ = event # print("DEBUG: OnCopy called event %s\n" % str(event)) self.copy() @@ -519,7 +522,7 @@ def on_cut(self, event=None): self.on_delete(event) def on_delete(self, event=None): - _ = event + __ = event if not self.IsCellEditControlShown(): self._execute(clear_area(self.selection.topleft, self.selection.bottomright)) @@ -527,7 +530,7 @@ def on_delete(self, event=None): # DEBUG @requires_focus def on_paste(self, event=None): - _ = event + __ = event if self.IsCellEditControlShown(): self.paste() else: @@ -543,7 +546,7 @@ def _execute_clipboard_command(self, command_class): # DEBUG @requires_focus def on_insert(self, event=None): - _ = event + __ = event self._execute_clipboard_command(insert_area) self._resize_grid() @@ -555,7 +558,7 @@ def on_delete_rows(self, event): # DEBUG @requires_focus def on_undo(self, event=None): - _ = event + __ = event if not self.IsCellEditControlShown(): self._execute(Undo()) else: @@ -564,7 +567,7 @@ def on_undo(self, event=None): # DEBUG @requires_focus def on_redo(self, event=None): - _ = event + __ = event self._execute(Redo()) self._resize_grid() @@ -574,16 +577,14 @@ def close(self): PUBLISHER.unsubscribe_all(self) if self._namespace_updated: # Prevent re-entry to unregister method - self._controller.datafile_controller.unregister_namespace_updates( - self._namespace_updated) + self._controller.datafile_controller.unregister_namespace_updates(self._namespace_updated) self._namespace_updated = None def save(self): self._tooltips.hide() if self.IsCellEditControlShown(): cell_editor = self.GetCellEditor(*self.selection.cell) - cell_editor.EndEdit(self.selection.topleft.row, - self.selection.topleft.col, self) + cell_editor.EndEdit(self.selection.topleft.row, self.selection.topleft.col, self) def show_content_assist(self): if self.IsCellEditControlShown(): @@ -718,7 +719,7 @@ def on_char(self, event): event.Skip() def on_go_to_definition(self, event): - _ = event + __ = event self._navigate_to_matching_user_keyword( self.GetGridCursorRow(), self.GetGridCursorCol()) @@ -742,10 +743,8 @@ def _show_keyword_details(self, cell, value): details = self._plugin.get_keyword_details(value) if not details: info = self._controller.get_cell_info(cell.Row, cell.Col) - if info.cell_type == CellType.KEYWORD and info.content_type == \ - ContentType.STRING: - details = """\ - Keyword was not detected by RIDE + if info.cell_type == CellType.KEYWORD and info.content_type == ContentType.STRING: + details = _("""Keyword was not detected by RIDE
    Possible corrections:
    • Import library or resource file containing the keyword.
    • @@ -753,9 +752,8 @@ def _show_keyword_details(self, cell, value): (Tools / Import Library Spec XML or by adding the XML file with the correct name to PYTHONPATH) to enable keyword completion for example for Java libraries. - Library spec XML can be created using libdoc tool from Robot Frame\ -work. -
    """ + Library spec XML can be created using libdoc tool from Robot Framework. + """) if details: self._tooltips.show_info_at( details, value, self._cell_to_screen_coordinates(cell)) @@ -804,22 +802,20 @@ def _open_cell_editor_with_content_assist(self): wx.CallAfter(self.open_cell_editor().show_content_assist) # wx.CallAfter(self._move_grid_cursor, wx.grid.GridEvent(), wx.WXK_RETURN) - def _open_cell_editor_and_execute_variable_creator(self, list_variable=False, - dict_variable=False): + def _open_cell_editor_and_execute_variable_creator(self, list_variable=False, dict_variable=False): cell_editor = self.open_cell_editor() - wx.CallAfter(cell_editor.execute_variable_creator, - list_variable, dict_variable) + wx.CallAfter(cell_editor.execute_variable_creator, list_variable, dict_variable) def on_make_variable(self, event): - _ = event + __ = event self._open_cell_editor_and_execute_variable_creator(list_variable=False) def on_make_list_variable(self, event): - _ = event + __ = event self._open_cell_editor_and_execute_variable_creator(list_variable=True) def on_make_dict_variable(self, event): - _ = event + __ = event self._open_cell_editor_and_execute_variable_creator(dict_variable=True) def _open_cell_editor_and_execute_sharp_comment(self): @@ -835,14 +831,14 @@ def current_cell(self): return curcell def on_comment_cells(self, event): - _ = event + __ = event if self.GetSelectionBlockTopLeft(): self.on_sharp_comment_rows(event) else: self._open_cell_editor_and_execute_sharp_comment() def on_uncomment_cells(self, event): - _ = event + __ = event if self.GetSelectionBlockTopLeft(): self.on_sharp_uncomment_rows(event) else: @@ -855,7 +851,7 @@ def on_cell_right_click(self, event): self._popup_menu_shown = False def on_select_all(self, event): - _ = event + __ = event self.SelectAll() def on_cell_col_size_changed(self, event): @@ -891,7 +887,7 @@ def _hide_link_if_necessary(self): self._toggle_underlined(self._marked_cell, True) def on_create_keyword(self, event): - _ = event + __ = event cells = self._data_cells_from_current_row() if not cells: return @@ -913,7 +909,7 @@ def _remove_comments(data): return data def on_extract_keyword(self, event): - _ = event + __ = event dlg = UserKeywordNameDialog(self._controller) if dlg.ShowModal() == wx.ID_OK: name, args = dlg.get_value() @@ -921,7 +917,7 @@ def on_extract_keyword(self, event): self._execute(ExtractKeyword(name, args, rows)) def on_extract_variable(self, event): - _ = event + __ = event cells = self.selection.cells() if len(cells) == 1: self._extract_scalar(cells[0]) @@ -930,7 +926,7 @@ def on_extract_variable(self, event): self._resize_grid() def on_find_where_used(self, event): - _ = event + __ = event is_variable, searchstring = self._get_is_variable_and_searchstring() if searchstring: self._execute_find_where_used(is_variable, searchstring) @@ -980,12 +976,11 @@ def _extract_list(self, cells): self._execute(extract_list(name, value, comment, cells)) def on_rename_keyword(self, event): - _ = event + __ = event old_name = self._current_cell_value() if not old_name.strip() or variablematcher.is_variable(old_name): return - new_name = wx.GetTextFromUser('New name', 'Rename Keyword', - default_value=old_name) + new_name = wx.GetTextFromUser(_('New name'), _('Rename Keyword'), default_value=old_name) if new_name: self._execute(RenameKeywordOccurrences( old_name, new_name, RenameProgressObserver(self.GetParent()))) @@ -997,8 +992,8 @@ def on_json_editor(self, event=None): dialog = RIDEDialog() dialog.SetTitle('JSON Editor') dialog.SetSizer(wx.BoxSizer(wx.HORIZONTAL)) - ok_btn = wx.Button(dialog, wx.ID_OK, "Save") - cnl_btn = wx.Button(dialog, wx.ID_CANCEL, "Cancel") + ok_btn = wx.Button(dialog, wx.ID_OK, _("Save")) + cnl_btn = wx.Button(dialog, wx.ID_CANCEL, _("Cancel")) rich_text = wx.TextCtrl(dialog, wx.ID_ANY, "If supported by the native control, this is reversed, and this is" " a different font.", size=(400, 475), style=wx.HSCROLL | wx.TE_MULTILINE | wx.TE_NOHIDESEL) @@ -1024,14 +1019,12 @@ def on_json_editor(self, event=None): try: json.loads(content) # Yes, we need the error except JSONDecodeError as e: - res = wx.MessageDialog(dialog, f"Error in JSON: {e}\n\nSave anyway?", - "Validation Error!", wx.YES_NO) + res = wx.MessageDialog(dialog, f"{_('Error in JSON:')} {e}\n\n{_('Save anyway?')}", + _("Validation Error!"), wx.YES_NO) res.InheritAttributes() response = res.ShowModal() if response == wx.ID_YES: - self.cell_value_edited(self.selection.cell[0], - self.selection.cell[1], - rich_text.GetValue()) + self.cell_value_edited(self.selection.cell[0], self.selection.cell[1], rich_text.GetValue()) # If the json_str is json format, then return True @staticmethod @@ -1168,11 +1161,11 @@ def __init__(self, cellvalue): self.SetBackgroundColour(Colour(200, 222, 40)) self.SetForegroundColour(Colour(7, 0, 70)) """ - self.caption = "Please select what you want to check for usage" + self.caption = _("Please select what you want to check for usage") variables = set(variablematcher.find_variable_basenames(cellvalue)) self.choices = [(False, cellvalue)] + [(True, v) for v in variables] - self.choices_string = ["Complete cell content"] + \ - ["Variable " + var.replace("&", "&&") for var + self.choices_string = [_("Complete cell content")] + \ + [_("Variable ") + var.replace("&", "&&") for var in variables] self._build_ui() @@ -1184,7 +1177,7 @@ def _build_ui(self): sizer.Add(wx.StaticText(self, label=self.caption), 0, wx.ALL | wx.EXPAND, 5) sizer.Add(self.radiobox_choices, 0, wx.ALL | wx.EXPAND, 5) - sizer.Add(wx.Button(self, wx.ID_OK, label="Search"), + sizer.Add(wx.Button(self, wx.ID_OK, label=_("Search")), 0, wx.ALL | wx.ALIGN_CENTER, 5) big_sizer = wx.BoxSizer(wx.VERTICAL) big_sizer.Add(sizer, 0, wx.ALL, 10) diff --git a/src/robotide/editor/listeditor.py b/src/robotide/editor/listeditor.py index cd303b23b..a5b57f921 100644 --- a/src/robotide/editor/listeditor.py +++ b/src/robotide/editor/listeditor.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from wx import Colour from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin @@ -21,14 +22,19 @@ from ..controller import ctrlcommands from ..widgets import PopupMenu, PopupMenuItems, ButtonWithHandler, Font +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + LIGHT_GREY = 'light grey' class ListEditorBase(wx.Panel): - _menu = ['Edit', 'Move Up\tCtrl-Up', 'Move Down\tCtrl-Down', '---', 'Delete'] + _menu = [_('Edit'), _('Move Up\tCtrl-Up'), _('Move Down\tCtrl-Down'), '---', _('Delete')] + _menu_nt = ['Edit', 'Move Up\tCtrl-Up', 'Move Down\tCtrl-Down', '---', 'Delete'] _buttons = [] + _buttons_nt = [] - def __init__(self, parent, columns, controller): + def __init__(self, parent, columns, controller, label=None): wx.Panel.__init__(self, parent) from ..preferences import RideSettings _settings = RideSettings() @@ -70,8 +76,8 @@ def _create_list(self, columns, data): def _create_buttons(self): sizer = wx.BoxSizer(wx.VERTICAL) - for label in self._buttons: - sizer.Add(ButtonWithHandler(self, label, width=120, + for label, mk_h in zip(self._buttons, self._buttons_nt): + sizer.Add(ButtonWithHandler(self, label, mk_handler=mk_h, width=120, color_secondary_foreground=self.color_secondary_foreground, color_secondary_background=self.color_secondary_background), 0, wx.ALL, 1) return sizer @@ -90,7 +96,7 @@ def on_item_selected(self, event): self._selection = event.GetIndex() def on_item_deselected(self, event): - _ = event + __ = event self._selection = wx.NOT_FOUND def on_edit(self, event): @@ -98,7 +104,7 @@ def on_edit(self, event): pass def on_right_click(self, event): - PopupMenu(self, PopupMenuItems(self, self._menu)) + PopupMenu(self, PopupMenuItems(self, self._menu, self._menu_nt)) def on_left_click(self, event): """ Just overriding """ diff --git a/src/robotide/editor/macroeditors.py b/src/robotide/editor/macroeditors.py index 3b030e405..1dc974725 100644 --- a/src/robotide/editor/macroeditors.py +++ b/src/robotide/editor/macroeditors.py @@ -118,7 +118,7 @@ class UserKeywordEditor(TestCaseEditor): def _create_header(self, text, readonly=False): def cb(event): - _ = event + __ = event Usages(self.controller, self._tree.highlight).show() return FindUsagesHeader(self, text, cb, color_foreground=self.color_secondary_foreground, color_background=self.color_secondary_background) diff --git a/src/robotide/editor/popupwindow.py b/src/robotide/editor/popupwindow.py index a31f4fe42..9aef59eff 100644 --- a/src/robotide/editor/popupwindow.py +++ b/src/robotide/editor/popupwindow.py @@ -64,7 +64,7 @@ def show_at(self, position): self.Show() def hide(self, event=None): - _ = event + __ = event self.Show(False) @property diff --git a/src/robotide/editor/settingeditors.py b/src/robotide/editor/settingeditors.py index fa742a7c3..fca46387c 100755 --- a/src/robotide/editor/settingeditors.py +++ b/src/robotide/editor/settingeditors.py @@ -13,14 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from wx import Colour +from multiprocessing import shared_memory from .editordialogs import editor_dialog, DocumentationDialog, MetadataDialog, \ ScalarVariableDialog, ListVariableDialog, DictionaryVariableDialog, LibraryDialog, \ ResourceDialog, VariablesDialog from .formatters import ListToStringFormatter from .gridcolorizer import ColorizationSettings +from ..lib.compat.parsing.language import get_english_label from .listeditor import ListEditor from .popupwindow import HtmlPopupWindow from .tags import TagsDisplay @@ -33,6 +36,9 @@ from ..utils.highlightmatcher import highlight_matcher from ..widgets import ButtonWithHandler, Label, HtmlWindow, PopupMenu, PopupMenuItems, HtmlDialog +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class SettingEditor(wx.Panel): popup_timer = None @@ -55,6 +61,17 @@ def __init__(self, parent, controller, plugin, tree): self.SetForegroundColour(Colour(self.color_foreground)) self.SetOwnForegroundColour(Colour(self.color_foreground)) self._controller = controller + try: + set_lang = shared_memory.ShareableList(name="language") + self._language = [set_lang[0]] + # print(f"DEBUG: settings.py SettingEditor __init__ SHAREDMEM language={self._language}") + except AttributeError: + try: + self._language = self._controller.language + # print(f"DEBUG: settings.py SettingEditor __init__ CONTROLLER language={self._language}") + except AttributeError: + self._language = ['en'] + # print(f"DEBUG: settings.py SettingEditor __init__ language={self._language}") self.plugin = plugin self._datafile = controller.datafile self._create_controls() @@ -70,23 +87,27 @@ def __init__(self, parent, controller, plugin, tree): def _create_controls(self): sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add((5, 0)) - sizer.Add(Label( + label = Label( self, label=self._controller.label, - size=(context.SETTING_LABEL_WIDTH, context.SETTING_ROW_HEIGHT))) + size=(context.SETTING_LABEL_WIDTH, context.SETTING_ROW_HEIGHT)) # Always show the English label as tooltip + label.SetToolTip(get_english_label(self._language, self._controller.label)) + sizer.Add(label) self._value_display = self._create_value_display() self.update_value() self._tooltip = self._get_tooltip() sizer.Add(self._value_display, 1, wx.EXPAND) self._add_edit(sizer) - sizer.Add(ButtonWithHandler(self, 'Clear', color_secondary_foreground=self.color_secondary_foreground, + sizer.Add(ButtonWithHandler(self, _('Clear'), mk_handler='Clear', handler=self.on_clear, + color_secondary_foreground=self.color_secondary_foreground, color_secondary_background=self.color_secondary_background)) sizer.Layout() self.SetSizer(sizer) def _add_edit(self, sizer): sizer.Add( - ButtonWithHandler(self, 'Edit', color_secondary_foreground=self.color_secondary_foreground, - color_secondary_background=self.color_secondary_background), + ButtonWithHandler(self, _('Edit'), mk_handler='Edit', handler=self.on_edit, + color_secondary_foreground=self.color_secondary_foreground, + color_secondary_background=self.color_secondary_background), flag=wx.LEFT | wx.RIGHT, border=5) def _create_value_display(self): @@ -142,7 +163,7 @@ def on_edit(self, event=None): self._editing = False def _create_editor_dialog(self): - dlg_class = editor_dialog(self._controller) + dlg_class = editor_dialog(self._controller, self._language) return dlg_class(self._datafile, self._controller, self.plugin) def _set_value(self, value_list, comment): @@ -179,8 +200,8 @@ def on_leave_window(self, event): self.on_window_destroy(event) def on_popup_timer(self, event): - _ = event - _tooltipallowed = False + __ = event + _tooltipallowed = True # DEBUG: This prevents tool tip for ex. Template edit field in wxPhoenix try: _tooltipallowed = self.Parent.tooltip_allowed(self._tooltip) @@ -226,7 +247,7 @@ def _update_and_notify(self): self.update_value() def on_clear(self, event): - _ = event + __ = event self._controller.execute(ctrlcommands.ClearSetting()) self._update_and_notify() @@ -366,6 +387,7 @@ def _hide_tooltip(self): pass def _create_editor_dialog(self): + # print(f"DEBUG: settingeditors.py DocumentationEditor _create_editor_dialog {self._language}") return DocumentationDialog(self._datafile, self._controller.editable_value) @@ -421,7 +443,12 @@ def close(self): class _AbstractListEditor(ListEditor): _titles = [] - def __init__(self, parent, tree, controller): + def __init__(self, parent, tree, controller, label=None): + try: + # print(f"DEBUG: settingeditors.py _AbstractListEditor dir language={controller.parent.datafile._language}") + self._language = controller.parent.datafile._language + except AttributeError: + self._language = ['en'] ListEditor.__init__(self, parent, self._titles, controller) self._datafile = controller.datafile self._tree = tree @@ -449,8 +476,9 @@ def highlight(self, text, expand=False): class VariablesListEditor(_AbstractListEditor): - _titles = ['Variable', 'Value', 'Comment'] - _buttons = ['Add Scalar', 'Add List', 'Add Dict'] + _titles = [_('Variable'), _('Value'), _('Comment')] + _buttons = [_('Add Scalar'), _('Add List'), _('Add Dict')] + _buttons_nt = ['Add Scalar', 'Add List', 'Add Dict'] def __init__(self, parent, tree, controller): PUBLISHER.subscribe( @@ -482,17 +510,17 @@ def on_move_down(self, event): self._list.SetFocus() def on_add_scalar(self, event): - _ = event + __ = event self._show_dialog( ScalarVariableDialog(self._controller)) def on_add_list(self, event): - _ = event + __ = event self._show_dialog( ListVariableDialog(self._controller, plugin=self.Parent.plugin)) def on_add_dict(self, event): - _ = event + __ = event self._show_dialog( DictionaryVariableDialog(self._controller, plugin=self.Parent.plugin)) @@ -536,11 +564,16 @@ def close(self): class ImportSettingListEditor(_AbstractListEditor): - _titles = ['Import', 'Name / Path', 'Arguments', 'Comment'] - _buttons = ['Library', 'Resource', 'Variables', 'Import Failed Help'] + _titles = [_('Import'), _('Name / Path'), _('Arguments'), _('Comment')] + _buttons = [_('Library'), _('Resource'), _('Variables'), _('Import Failed Help')] + _buttons_nt = ['Library', 'Resource', 'Variables', 'Import Failed Help'] - def __init__(self, parent, tree, controller): + def __init__(self, parent, tree, controller, lang=None): self._import_failed_shown = False + try: + self._language = controller.parent.datafile._language + except AttributeError: + self._language = ['en'] _AbstractListEditor.__init__(self, parent, tree, controller) self.SetBackgroundColour(Colour(self.color_background)) self.SetOwnBackgroundColour(Colour(self.color_background)) @@ -550,10 +583,10 @@ def __init__(self, parent, tree, controller): def _create_buttons(self): sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(Label( - self, label='Add Import', size=wx.Size(120, 20), + self, label=_('Add Import'), size=wx.Size(120, 20), style=wx.ALIGN_CENTER)) - for label in self._buttons: - sizer.Add(ButtonWithHandler(self, label, width=120, + for label, label_nt in zip(self._buttons, self._buttons_nt): + sizer.Add(ButtonWithHandler(self, label, mk_handler=label_nt, width=120, color_secondary_foreground=self.color_secondary_foreground, color_secondary_background=self.color_secondary_background), 0, wx.ALL, 1) return sizer @@ -576,52 +609,55 @@ def has_error(self, controller): return controller.has_error() def on_right_click(self, event): - PopupMenu(self, PopupMenuItems(self, self._create_item_menu())) + menu, menu_nt = self._create_item_menu() + PopupMenu(self, PopupMenuItems(self, menu_names=menu, menu_names_nt=menu_nt)) def _create_item_menu(self): menu = self._menu + menu_nt = self._menu_nt item = self._controller[self._selection] if item.has_error() and item.type == 'Library': - menu = menu[:] + ['Import Library Spec XML'] - return menu + menu = menu[:] + [_('Import Library Spec XML')] + menu_nt = menu_nt[:] + ['Import Library Spec XML'] + return menu, menu_nt @staticmethod def on_import_library_spec_xml(event): - _ = event + __ = event RideExecuteSpecXmlImport().publish() def on_edit(self, event): setting = self._get_setting() self._show_import_editor_dialog( - editor_dialog(setting), + editor_dialog(setting, self._language), lambda v, c: setting.execute(ctrlcommands.SetValues(v, c)), setting, on_empty=self._delete_selected) def on_library(self, event): - _ = event + __ = event self._show_import_editor_dialog( LibraryDialog, lambda v, c: self._controller.execute(ctrlcommands.AddLibrary(v, c))) def on_resource(self, event): - _ = event + __ = event self._show_import_editor_dialog( ResourceDialog, lambda v, c: self._controller.execute(ctrlcommands.AddResource(v, c))) def on_variables(self, event): - _ = event + __ = event self._show_import_editor_dialog( VariablesDialog, lambda v, c: - self._controller.execute(ctrlcommands.AddVariablesFileImport(v, c))) + self._controller.execute(ctrlcommands.AddVariablesFileImport(v, c)), + title=_('Variables')) # DEBUG def on_import_failed_help(self, event): - _ = event + __ = event if self._import_failed_shown: return - dialog = HtmlDialog('Import failure handling', ''' -
    Possible corrections and notes:
    + dialog = HtmlDialog(_('Import failure handling'), _('''
    Possible corrections and notes:
    • Import failure is shown with red color.
    • See Tools / View RIDE Log for detailed information about the failure.
    • @@ -634,7 +670,7 @@ def on_import_failed_help(self, event): For more information see wiki. -
    ''') + ''')) dialog.Bind(wx.EVT_CLOSE, self._import_failed_help_closed) dialog.Show() self._import_failed_shown = True @@ -646,9 +682,8 @@ def _import_failed_help_closed(self, event): def _get_setting(self): return self._controller[self._selection] - def _show_import_editor_dialog( - self, dialog, creator_or_setter, item=None, on_empty=None): - dlg = dialog(self._controller, item=item) + def _show_import_editor_dialog(self, dialog, creator_or_setter, item=None, on_empty=None, title=None): + dlg = dialog(self._controller, item=item, title=title) if dlg.ShowModal() == wx.ID_OK: value = dlg.get_value() if not self._empty_name(value): @@ -669,8 +704,9 @@ def get_column_values(item): class MetadataListEditor(_AbstractListEditor): - _titles = ['Metadata', 'Value', 'Comment'] - _buttons = ['Add Metadata'] + _titles = [_('Metadata'), _('Value'), _('Comment')] + _buttons = [_('Add Metadata')] + _buttons_nt = ['Add Metadata'] _sortable = False def on_edit(self, event): @@ -683,7 +719,7 @@ def on_edit(self, event): dlg.Destroy() def on_add_metadata(self, event): - _ = event + __ = event dlg = MetadataDialog(self._controller.datafile) if dlg.ShowModal() == wx.ID_OK: ctrl = self._controller.add_metadata(*dlg.get_value()) diff --git a/src/robotide/editor/texteditor.py b/src/robotide/editor/texteditor.py index 6918fd591..c9fbaa32b 100644 --- a/src/robotide/editor/texteditor.py +++ b/src/robotide/editor/texteditor.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import builtins import string from io import StringIO, BytesIO from time import time @@ -20,8 +20,9 @@ import wx from wx import stc, Colour from wx.adv import HyperlinkCtrl, EVT_HYPERLINK +from multiprocessing import shared_memory from .popupwindow import HtmlPopupWindow -from . import _EDIT +from . import _EDIT, _EDIT_nt from .. import robotapi from ..context import IS_WINDOWS, IS_MAC from ..controller.ctrlcommands import SetDataFile, INDENTED_START @@ -37,10 +38,22 @@ from ..widgets import TextField, Label, HtmlDialog from ..widgets import VerticalSizer, HorizontalSizer, ButtonWithHandler, RIDEDialog -try: # import installed version first - from pygments.lexers import robotframework as robotframeworklexer -except ImportError: - robotframeworklexer = None +from robotide.lib.compat.parsing.language import Language +robotframeworklexer = None +if Language: + try: # import our modified version + from robotide.lib.compat.pygments import robotframework as robotframeworklexer + except ImportError: + robotframeworklexer = None + +if not robotframeworklexer: + try: # import original version + from pygments.lexers import robotframework as robotframeworklexer + except ImportError: + robotframeworklexer = None + +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation PLUGIN_NAME = 'Text Edit' TXT_NUM_SPACES = 'txt number of spaces' @@ -54,6 +67,7 @@ def __init__(self, application): Plugin.__init__(self, application) self._editor_component = None self._tab = None + self._doc_language = None self._save_flag = 0 # See self.reformat = application.settings.get('reformat', False) self._register_shortcuts() @@ -70,7 +84,7 @@ def _editor(self): def enable(self): self._tab = self._editor - self.register_actions(action_info_collection(_EDIT, self._tab, self._tab)) + self.register_actions(action_info_collection(_EDIT, self._tab, data_nt=_EDIT_nt, container=self._tab)) self.subscribe(self.on_tree_selection, RideTreeSelection) self.subscribe(self.on_data_changed, RideMessage) self.subscribe(self.on_tab_change, RideNotebookTabChanging) @@ -109,7 +123,7 @@ def disable(self): self._editor_component = None def on_open(self, event): - _ = event + __ = event self._open() def _open(self): @@ -117,17 +131,24 @@ def _open(self): if datafile_controller: self._save_flag = 0 """ DEBUG: To be used in Localization - if hasattr(datafile_controller, 'preamble'): # DEBUG: Is failing at resource files - print(f"DEBUG: texteditor _open preamble={datafile_controller.preamble}") - self._open_data_for_controller(datafile_controller) """ + # if hasattr(datafile_controller, 'preamble'): # DEBUG: Is failing at resource files + # print(f"DEBUG: texteditor _open preamble={datafile_controller.preamble}") + if hasattr(datafile_controller, 'language'): + self._doc_language = datafile_controller.language + self._editor.language = datafile_controller.language + # print(f"DEBUG: texteditor _open language={datafile_controller.language}") + self._open_data_for_controller(datafile_controller) self._editor.store_position() def on_data_changed(self, message): """ This block is now inside try/except to avoid errors from unit test """ try: - # print(f"DEBUG: textedit OnDataChanged message={message}") + if self.is_focused() and isinstance(message, RideDataChangedToDirty): + # print("DEBUG: textedit OnDataChanged returning RideDataChangedToDirty") + return if self._should_process_data_changed_message(message): + # print(f"DEBUG: textedit after _should_process_data_changed_message save_flag={self._save_flag}") if isinstance(message, RideOpenSuite): # Not reached self._editor.reset() self._editor.set_editor_caret_position() @@ -162,10 +183,11 @@ def _on_timer(self, event): @staticmethod def _should_process_data_changed_message(message): - return isinstance(message, (RideDataChanged, RideBeforeSaving, RideSaved, RideSaving, RideDataDirtyCleared))\ - and not isinstance(message, RideDataChangedToDirty) + return isinstance(message, (RideDataChanged, RideBeforeSaving, RideSaved, RideSaving, RideDataDirtyCleared)) + # and not isinstance(message, RideDataChangedToDirty)) def on_tree_selection(self, message): + # print(f"DEBUG: texteditor.py TextEditorPlugin on_tree_selection ENTER type={type(message.item)}") self._editor.store_position() if self.is_focused(): next_datafile_controller = message.item and message.item.datafile_controller @@ -196,15 +218,21 @@ def _set_read_only(self, message): def _open_tree_selection_in_editor(self): try: datafile_controller = self.tree.get_selected_datafile_controller() + if datafile_controller: + self._editor.language = datafile_controller.language except AttributeError: return if datafile_controller: - self._editor.open(DataFileWrapper(datafile_controller, self.global_settings)) + self._editor.language = datafile_controller.language + self.global_settings['language'] = datafile_controller.language + self._editor.open(DataFileWrapper(datafile_controller, self.global_settings, self._editor.language)) self._editor.source_editor.readonly = not datafile_controller.is_modifiable() self._editor.set_editor_caret_position() def _open_data_for_controller(self, datafile_controller): - self._editor.selected(DataFileWrapper(datafile_controller, self.global_settings)) + self._editor.language = datafile_controller.language + self.global_settings['language'] = datafile_controller.language + self._editor.selected(DataFileWrapper(datafile_controller, self.global_settings, self._editor.language)) self._editor.source_editor.readonly = not datafile_controller.is_modifiable() def on_tab_change(self, message): @@ -221,7 +249,7 @@ def on_tab_change(self, message): self._editor_component.content_save() def on_tab_changed(self, event): - _ = event + __ = event self._show_editor() event.Skip() @@ -272,8 +300,9 @@ def set_editor(self, editor): def validate_and_update(self, data, text): m_text = text.decode("utf-8") - if not self._sanity_check(data, m_text): - handled = self._handle_sanity_check_failure() + result = self._sanity_check(data, m_text) + if isinstance(result, tuple): + handled = self._handle_sanity_check_failure(result) if not handled: return False self._editor.reset() @@ -290,15 +319,38 @@ def validate_and_update(self, data, text): return True def _sanity_check(self, data, text): + """ # First remove all lines starting with # for line in text.split('\n'): comment = line.strip().startswith('#') if comment: text = text.replace(line, '') + """ + from robotide.lib.compat.parsing import ErrorReporter + from robot.parsing.parser.parser import get_model + from robotide.lib.robot.errors import DataError + + # print(f"DEBUG: textedit.py _sanity_check data is type={type(data)}") + model = get_model(text) + # print(f"DEBUG: textedit.py _sanity_check model is {model}") + validator = ErrorReporter() + result = None + try: + validator.visit(model) + except DataError as err: + result = (err.message, err.details) + model.save("/tmp/model_saved_from_RIDE.robot") + # print(f"DEBUG: textedit.py _sanity_check after calling validator {validator}\n" + # f"Save model in /tmp/model_saved_from_RIDE.robot" + # f" result={result}") + return True if not result else result + + """ DEBUG formatted_text = data.format_text(text) c = self._normalize(formatted_text) e = self._normalize(text) return len(c) == len(e) + """ @staticmethod def _normalize(text): @@ -307,14 +359,14 @@ def _normalize(text): text = text.replace(item, '') return text - def _handle_sanity_check_failure(self): + def _handle_sanity_check_failure(self, message): if self._last_answer == wx.ID_NO and time() - self._last_answer_time <= 0.2: # self.source_editor._mark_file_dirty(True) return False # DEBUG: use widgets.Dialog - dlg = wx.MessageDialog(self._editor, 'ERROR: Data sanity check failed!\n' - 'Reset changes?', - 'Can not apply changes from Txt Editor', style=wx.YES | wx.NO) + dlg = wx.MessageDialog(self._editor, f"{_('ERROR: Data sanity check failed!')}\n{_('Error at line')}" + f" {message[1]}:\n{message[0]}\n\n{_('Reset changes?')}", + _("Can not apply changes from Text Editor"), style=wx.YES | wx.NO) dlg.InheritAttributes() did = dlg.ShowModal() self._last_answer = did @@ -329,10 +381,11 @@ def _handle_sanity_check_failure(self): class DataFileWrapper(object): # DEBUG: bad class name - def __init__(self, data, settings): + def __init__(self, data, settings, language=None): self.wrapper_data = data self._settings = settings self._tab_size = self._settings.get(TXT_NUM_SPACES, 2) if self._settings else 2 + self._doc_language = language def __eq__(self, other): if other is None: @@ -374,7 +427,8 @@ def content(self): def _txt_data(self, data): output = StringIO() - data.save(output=output, format='txt', txt_separating_spaces=self._settings.get(TXT_NUM_SPACES, 4)) + data.save(output=output, fformat='txt', txt_separating_spaces=self._settings.get(TXT_NUM_SPACES, 4), + language=self._doc_language) return output.getvalue() @@ -394,6 +448,7 @@ def __init__(self, plugin, parent, title, data_validator): self._title = title self.tab_size = self.source_editor_parent.app.settings.get(TXT_NUM_SPACES, 4) self.reformat = self.source_editor_parent.app.settings.get('reformat', False) + self._doc_language = None self._create_ui(title) self._data = None self._position = 0 # Start at 0 if first time access @@ -431,7 +486,7 @@ def _create_ui(self, title): if not editor_created: self.SetSizer(VerticalSizer()) self._create_editor_toolbar() - self._create_editor_text_control() + self._create_editor_text_control(language=self.language) self.source_editor_parent.add_tab(self, title, allow_closing=False) def _create_editor_toolbar(self): @@ -439,7 +494,8 @@ def _create_editor_toolbar(self): # text about syntax colorization self.editor_toolbar = HorizontalSizer() default_components = HorizontalSizer() - button = ButtonWithHandler(self, 'Apply Changes', handler=lambda e: self.content_save()) + button = ButtonWithHandler(self, _('Apply Changes'), + handler=lambda e: self.plugin._apply_txt_changes_to_model()) button.SetBackgroundColour(Colour(self.dlg.color_secondary_background)) button.SetForegroundColour(Colour(self.dlg.color_secondary_foreground)) default_components.add_with_padding(button) @@ -454,7 +510,7 @@ def _create_search(self, container_sizer): self.search_field.SetForegroundColour(Colour(self.dlg.color_secondary_foreground)) self.search_field.Bind(wx.EVT_TEXT_ENTER, self.on_find) container_sizer.add_with_padding(self.search_field) - button = ButtonWithHandler(self, 'Search', handler=self.on_find) + button = ButtonWithHandler(self, _('Search'), handler=self.on_find) button.SetBackgroundColour(Colour(self.dlg.color_secondary_background)) button.SetForegroundColour(Colour(self.dlg.color_secondary_foreground)) container_sizer.add_with_padding(button) @@ -464,10 +520,10 @@ def _create_search(self, container_sizer): def create_syntax_colorization_help(self): if self._syntax_colorization_help_exists: return - label = Label(self, label="Syntax colorization disabled due to missing requirements.") - link = HyperlinkCtrl(self, -1, label="Get help", url="") + label = Label(self, label=_("Syntax colorization disabled due to missing requirements.")) + link = HyperlinkCtrl(self, -1, label=_("Get help"), url="") link.Bind(EVT_HYPERLINK, self.show_help_dialog) - flags = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT + flags = wx.ALIGN_RIGHT syntax_colorization_help_sizer = wx.BoxSizer(wx.VERTICAL) syntax_colorization_help_sizer.AddMany([ (label, 0, flags), @@ -479,8 +535,8 @@ def create_syntax_colorization_help(self): @staticmethod def show_help_dialog(event): - _ = event - content = """

    Syntax colorization

    + __ = event + content = _("""

    Syntax colorization

    Syntax colorization for Text Edit uses Pygments syntax highlighter.

    @@ -504,8 +560,8 @@ def show_help_dialog(event):

    For more information about installing Pygments, see the site.

    - """ - HtmlDialog("Getting syntax colorization", content).Show() + """) + HtmlDialog(_("Getting syntax colorization"), content).Show() def store_position(self, force=False): if self.source_editor: # We don't necessarily need a data controller, was: "and self.datafile_controller:" @@ -539,6 +595,14 @@ def dirty(self): def datafile_controller(self): return self._data.wrapper_data if self._data else None + @property + def language(self): + return self._doc_language + + @language.setter + def language(self, flanguage): + self._doc_language = flanguage + def on_find(self, event, forward=True): if self.source_editor: if event.GetEventType() != wx.wxEVT_TEXT_ENTER: # Was getting selected item from Tree @@ -595,7 +659,7 @@ def _show_search_results(self, position, txt): self.source_editor.ScrollToLine(self.source_editor.GetCurrentLine()) self._search_field_notification.SetLabel('') else: - self._search_field_notification.SetLabel('No matches found.') + self._search_field_notification.SetLabel(_('No matches found.')) def locate_tree_item(self, item): """ item is object received from message """ @@ -628,7 +692,7 @@ def on_content_assist(self, event): """ if not self.is_focused(): # DEBUG was typing text when at Grid Editor return - _ = event + __ = event if self._showing_list: self._showing_list = False # Avoid double calls return @@ -666,6 +730,8 @@ def on_content_assist(self, event): def open(self, data): self.reset() self._data = data + self.language = self._data._doc_language + # print(f"DEBUG: texteditor.py SourceEditor open ENTER language={self.language}") try: if isinstance(self._data.wrapper_data, ResourceFileController): self._controller_for_context = DummyController(self._data.wrapper_data, self._data.wrapper_data) @@ -679,13 +745,17 @@ def open(self, data): self.datafile = self.plugin.datafile if not self.source_editor: self._stored_text = self._data.content + self._create_editor_text_control(text=self._stored_text, language=self.language) else: + self.source_editor.set_language(self.language) self.source_editor.set_text(self._data.content) self.set_editor_caret_position() def selected(self, data): if not self.source_editor: - self._create_editor_text_control(self._stored_text) + self._create_editor_text_control(text=self._stored_text, language=self.language) + else: + self.source_editor.set_language(self.language) if self._data == data: return self.open(data) @@ -827,7 +897,6 @@ def content_save(self, *args): if not self._data_validator.validate_and_update(self._data, self.source_editor.utf8_text): self.is_saving = False return False - # self.GetFocus(None) return True """ @@ -846,28 +915,28 @@ def direct_save(self, text): """ # Callbacks taken from __init__.py def on_undo(self, event): - _ = event + __ = event self.undo() def on_redo(self, event): - _ = event + __ = event self.redo() def on_cut(self, event): - _ = event + __ = event self.cut() def on_copy(self, event): - _ = event + __ = event self.copy() def on_paste(self, event): - _ = event + __ = event self.paste() @staticmethod def on_insert(event): - _ = event + __ = event print(f"DEBUG: TextEditor called on_insert event={event}\n TO BE IMPLEMENTED") # self.insert_row() @@ -949,8 +1018,8 @@ def remove_and_store_state(self): self.store_position() self._stored_text = self.source_editor.GetText() - def _create_editor_text_control(self, text=None): - self.source_editor = RobotDataEditor(self) + def _create_editor_text_control(self, text=None, language=None): + self.source_editor = RobotDataEditor(self, language) self.Sizer.add_expanding(self.source_editor) self.Sizer.Layout() if text is not None: @@ -965,7 +1034,7 @@ def _create_editor_text_control(self, text=None): # DEBUG: Add here binding for keyword help def LeaveFocus(self, event): - _ = event + __ = event self.source_editor.hide_kw_doc() self.source_editor.AcceptsFocusFromKeyboard() self.store_position() @@ -1088,8 +1157,6 @@ def on_key_down(self, event): self.source_editor.show_kw_doc() event.Skip() else: - # if self.dirty and keycode >= ord(' '): - self.mark_file_dirty(self.source_editor.GetModify()) event.Skip() # These commands are duplicated by global actions @@ -1200,7 +1267,7 @@ def _enclose_text(open_symbol, value=''): return open_symbol + value + close_symbol def move_row_up(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() new_ini_line = ini_line = self.source_editor.LineFromPosition(start) new_end_line = end_line = self.source_editor.LineFromPosition(end) @@ -1321,7 +1388,7 @@ def last_non_space(text): return idx def move_row_down(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() new_ini_line = ini_line = self.source_editor.LineFromPosition(start) new_end_line = end_line = self.source_editor.LineFromPosition(end) @@ -1390,7 +1457,7 @@ def move_row_down(self, event): self.source_editor.SetAnchor(nstartpos) def delete_row(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() ini_line = self.source_editor.LineFromPosition(start) self.source_editor.SelectNone() @@ -1404,7 +1471,7 @@ def delete_row(self, event): self.store_position() def insert_row(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() ini_line = self.source_editor.LineFromPosition(start) end_line = self.source_editor.LineFromPosition(end) @@ -1421,7 +1488,7 @@ def insert_row(self, event): self.store_position() def execute_comment(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() cursor = self.source_editor.GetCurrentPos() ini_line = self.source_editor.LineFromPosition(start) @@ -1459,7 +1526,7 @@ def execute_comment(self, event): self.store_position() def execute_uncomment(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() cursor = self.source_editor.GetCurrentPos() ini_line = self.source_editor.LineFromPosition(start) @@ -1501,7 +1568,7 @@ def execute_uncomment(self, event): self.store_position() def insert_cell(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() ini_line = self.source_editor.LineFromPosition(start) end_line = self.source_editor.LineFromPosition(end) @@ -1543,7 +1610,7 @@ def insert_cell(self, event): self.source_editor.SetAnchor(new_end) def delete_cell(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() ini_line = self.source_editor.LineFromPosition(start) end_line = self.source_editor.LineFromPosition(end) @@ -1621,7 +1688,7 @@ def _get_position_of_cell(self, begpos, endpos, cell_no): return cellpos def execute_sharp_comment(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() cursor = self.source_editor.GetCurrentPos() ini_line = self.source_editor.LineFromPosition(start) @@ -1687,7 +1754,7 @@ def execute_sharp_comment(self, event): self.store_position() def execute_sharp_uncomment(self, event): - _ = event + __ = event start, end = self.source_editor.GetSelection() cursor = self.source_editor.GetCurrentPos() ini_line = self.source_editor.LineFromPosition(start) @@ -1798,9 +1865,10 @@ def mark_file_dirty(self, dirty=True): class RobotDataEditor(stc.StyledTextCtrl): margin = 1 - def __init__(self, parent, readonly=False): + def __init__(self, parent, readonly=False, language=None): stc.StyledTextCtrl.__init__(self, parent) self.parent = parent + self.language = language self._plugin = parent.plugin self._settings = parent.source_editor_parent.app.settings self._information_popup = None @@ -1866,16 +1934,20 @@ def set_text(self, text): self.EmptyUndoBuffer() self.SetMarginWidth(self.margin, self.calc_margin_width()) + def set_language(self, dlanguage): + self.language = dlanguage + self.stylizer = RobotStylizer(self, self._settings, self.readonly, self.language) + @property def utf8_text(self): return self.GetText().encode('UTF-8') def on_style(self, event): - _ = event + __ = event self.stylizer.stylize() def on_zoom(self, event): - _ = event + __ = event self.SetMarginWidth(self.margin, self.calc_margin_width()) self._set_zoom() @@ -2045,19 +2117,32 @@ def _show_keyword_details(self, value, coords=None): class FromStringIOPopulator(robotapi.populators.FromFilePopulator): def populate(self, content: [str, BytesIO], tab_size: int): - robotapi.RobotReader(spaces=tab_size).read(content, self) + try: + set_lang = shared_memory.ShareableList(name="language") + language = [set_lang[0]] + except AttributeError: + language = ['en'] + robotapi.RobotReader(spaces=tab_size, lang=language).read(content, self) class RobotStylizer(object): - def __init__(self, editor, settings, readonly=False): + def __init__(self, editor, settings, readonly=False, language=None): self.tokens = {} self.editor = editor self.lexer = None self.settings = settings self._readonly = readonly self._ensure_default_font_is_valid() + try: + set_lang = shared_memory.ShareableList(name="language") + except AttributeError: # Unittests fails here + set_lang = [] + set_lang[0] = language[0] if language is not None else 'en' + self.language = [set_lang[0]] + options = {'language': self.language} + # print(f"DEBUG: texteditor.py RobotStylizer _init_ language={self.language}\n") if robotframeworklexer: - self.lexer = robotframeworklexer.RobotFrameworkLexer() + self.lexer = robotframeworklexer.RobotFrameworkLexer(**options) else: self.editor.GetParent().create_syntax_colorization_help() self.set_styles(self._readonly) @@ -2179,6 +2264,7 @@ def stylize(self): self.editor.ConvertEOLs(2) shift = 0 for position, token, value in self.lexer.get_tokens_unprocessed(self.editor.GetText()): + # print(f"DEBUG: texteditor.py RobotStylizer stylize token={token} value={value}") if wx.VERSION < (4, 1, 0): self.editor.StartStyling(position + shift, 31) else: diff --git a/src/robotide/editor/tooltips.py b/src/robotide/editor/tooltips.py index d5414c8f7..d05115d13 100644 --- a/src/robotide/editor/tooltips.py +++ b/src/robotide/editor/tooltips.py @@ -53,7 +53,7 @@ def _start_tooltip_timer(self): self._tooltip_timer.Start(1000, True) def on_show_tool_tip(self, event): - _ = event + __ = event self._hide_tooltip() content = self._grid.get_tooltip_content() if content and self._application_has_focus(): diff --git a/src/robotide/lib/compat/__init__.py b/src/robotide/lib/compat/__init__.py new file mode 100644 index 000000000..ab3b8c315 --- /dev/null +++ b/src/robotide/lib/compat/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/robotide/lib/compat/parsing/__init__.py b/src/robotide/lib/compat/parsing/__init__.py new file mode 100644 index 000000000..c95249cc9 --- /dev/null +++ b/src/robotide/lib/compat/parsing/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .validator import ErrorReporter diff --git a/src/robotide/lib/compat/parsing/language.py b/src/robotide/lib/compat/parsing/language.py new file mode 100644 index 000000000..a2f0419f8 --- /dev/null +++ b/src/robotide/lib/compat/parsing/language.py @@ -0,0 +1,192 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os.path +import sys + +try: + from robot.conf.languages import Language +except ImportError as e: + sys.stderr.write(f"Trying to import robot's languages module returned error: {repr(e)}\n") + sys.stderr.write("You need to have Robot Framework v6.0 or newer to use languages in test suites.\n") + Language = None +from robot.errors import DataError +from robotide.lib.robot.utils import Utf8Reader + + +def check_file_language(path): + """ + Returns the language code if defined and valid, error if not valid, or None if file does not have preamble. + + :param path: Path to robot or resource file + :return: language, error or None + """ + if not Language: + return None + language_string = None + if os.path.isfile(path): + language_string = read(path) + if not language_string: + return None + # If preamble exists, proceed with language detection + try: + from robot.conf.languages import Languages + except ImportError as e: + sys.stderr.write(f"Trying to import robot's languages module returned error: {repr(e)}\n") + return None + try: + build_lang = Languages(language_string, add_english=False) + except (DataError, ModuleNotFoundError) as e: + sys.stderr.write(f"File language definition returned error: {repr(e)}\n") + return None + if build_lang: + # print(f"DEBUG: check_file_language {build_lang.settings}\n{build_lang.headers}\n{build_lang.true_strings}" + # f"\n{build_lang.false_strings}\n{build_lang.bdd_prefixes}") + lang = [] + for ll in build_lang: + lang.append(ll.code) + return lang + + +def read(path): + lang = None + for lineno, line in enumerate(Utf8Reader(path).readlines(), start=1): + row = line.rstrip() + if row and row.strip().startswith('*'): + break + else: + if row.startswith('Language:'): + lang = row[len('Language:'):].strip() + return lang + + +def get_headers_for(language, tables_headers, lowercase=True): + _setting_table_names = 'Setting', 'Settings' + _variable_table_names = 'Variable', 'Variables' + _testcase_table_names = 'Test Case', 'Test Cases', 'Task', 'Tasks' + _keyword_table_names = 'Keyword', 'Keywords' + _comment_table_names = 'Comment', 'Comments' + t_en = [(_setting_table_names,), + (_variable_table_names,), + (_testcase_table_names,), + (_keyword_table_names,), + (_comment_table_names,)] + if not Language: + return t_en + assert tables_headers is not None + if not language: + language = ['en'] + languages = set() + for mlang in language: + try: + lang = Language.from_name(mlang) + languages.add(lang) + except ValueError: + print(f"DEBUG: language.py get_headers_for Exception at language={mlang}") + + tables_headers = [item.lower() for item in list(tables_headers)] if lowercase else tables_headers + if not languages: + # print("DEBUG: language.py get_headers_for languages set is empty returning original tables_headers") + return tables_headers + + build_table = set() + for lang in languages: + headers = lang.headers + # print(f"DEBUG: language.py get_headers_for HEADERS headers={headers}, table_headers={tables_headers}") + for item in tables_headers: + build_headings = [] + inx = 0 + for k, v in zip(headers.keys(), headers.values()): + try: + if v.lower() == item.lower(): + header = list(headers.keys())[inx].lower() if lowercase else list(headers.keys())[inx] + build_headings.append(header) + break + except Exception as e: + pass + inx += 1 + for bh in build_headings: + build_table.add(bh) + if build_table: + # print(f"DEBUG: language.py get_headers_for returning table= {build_table}") + for th in tables_headers: + build_table.add(th) + return tuple(list(build_table)) + return tables_headers + + +def get_settings_for(language, settings_names): + assert settings_names is not None + if not Language: + return settings_names + if not language: + language = ['en'] + languages = set() + if isinstance(language, list): + for mlang in language: + try: + lang = Language.from_name(mlang) + languages.add(lang) + except ValueError: + print(f"DEBUG: language.py get_settings_for Exception at language={mlang}") + else: + lang = Language.from_name(language) + languages.add(lang) + # DEBUG: settings_names = [item.lower() for item in list(settings_names)] + if not languages: + print("DEBUG: language.py get_settings_for languages set is empty") + return {} + + build_table = {} + for lang in languages: + settings = lang.settings + for item in settings_names: + inx = 0 + for k, v in zip(settings.keys(), settings.values()): + try: + if v.lower() == item.lower(): + setting = list(settings.keys())[inx] + build_table[setting] = item + break + except Exception as e: + pass + inx += 1 + # print(f"DEBUG: language.py get_settings_for RETURN build_table={build_table}") + return build_table + + +def get_english_label(lang, label): + assert label is not None + if not Language: + return label + if not lang: + lang = ['en'] + mlang = None + if isinstance(lang, list): + try: + mlang = Language.from_name(lang[0]) # Only care for a single language + except ValueError: + print(f"DEBUG: language.py get_english_label Exception at language={lang}") + else: + mlang = Language.from_name(lang) + if not mlang: + print(f"DEBUG: language.py get_english_label lang={lang} not found") + return None + setting_names = list(mlang.settings.keys()) + try: + index = setting_names.index(label) + except ValueError: + # print(f"DEBUG: language.py get_english_label Exception at getting index {lang} returning={label}") + return label + en_label = list(mlang.settings.values())[index] + return en_label diff --git a/src/robotide/lib/compat/parsing/validator.py b/src/robotide/lib/compat/parsing/validator.py new file mode 100644 index 000000000..570e82d27 --- /dev/null +++ b/src/robotide/lib/compat/parsing/validator.py @@ -0,0 +1,13 @@ +from robot.api.parsing import ModelVisitor +from robotide.lib.robot.errors import DataError + + +class ErrorReporter(ModelVisitor): + + def generic_visit(self, node): + if node.errors: + # print(f"DEBUG: validator.py ErrorReporter: Error on line {node.lineno}:") + for error in node.errors: + # print(f"- {error}") + raise DataError(message=error,details=node.lineno) + ModelVisitor.generic_visit(self, node) diff --git a/src/robotide/lib/compat/pygments/__init__.py b/src/robotide/lib/compat/pygments/__init__.py new file mode 100644 index 000000000..ab3b8c315 --- /dev/null +++ b/src/robotide/lib/compat/pygments/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/robotide/lib/compat/pygments/robotframework.py b/src/robotide/lib/compat/pygments/robotframework.py new file mode 100644 index 000000000..bae52d873 --- /dev/null +++ b/src/robotide/lib/compat/pygments/robotframework.py @@ -0,0 +1,862 @@ +""" + pygments.lexers.robotframework + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Robot Framework. + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import annotations + +# Copyright 2012 Nokia Siemens Networks Oyj +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Copyright 2012-2015 Nokia Networks +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This is a modified copy from Pygments 2.17.0, by Hélio Guilherme 2023-12-09 +# The changes in this file were done with the purpose to add colorization +# support for test suites using languages other than English and supported +# by Robot Framework 6.0 or higher (at the time, version is 6.1.1) + + +import re + +from pygments.lexer import Lexer +from pygments.token import Token +from multiprocessing import shared_memory +from robotide.lib.compat.parsing.language import Language + +if not Language: # Let's import original + raise ImportError + +__all__ = ['RobotFrameworkLexer'] + + +HEADING = Token.Generic.Heading +SETTING = Token.Keyword.Namespace +IMPORT = Token.Name.Namespace +TC_KW_NAME = Token.Generic.Subheading +KEYWORD = Token.Name.Function +ARGUMENT = Token.String +VARIABLE = Token.Name.Variable +COMMENT = Token.Comment +SEPARATOR = Token.Punctuation +SYNTAX = Token.Punctuation +GHERKIN = Token.Generic.Emph +ERROR = Token.Error + + +def normalize(string, remove=''): + string = string.lower() + for char in remove + ' ': + if char in string: + string = string.replace(char, '') + return string + + +def normalize_dict(table: dict) -> dict: + ndict = {} + for key, value in table.items(): + if key: + k = normalize(key) + v = normalize(value) + ndict[k] = v + return ndict + + +def normalize_pipe_list(data: list) -> str: + pipe_list = "|".join(data) + return normalize(pipe_list) + + +def get_key_by_value(table: dict, value: str) -> str: + for k, v in table.items(): + if v == value: + return k + return value # Returns original if not in dict, deprecated/old labels + + +class RobotFrameworkLexer(Lexer): + """ + For Robot Framework test data. + + Supports both space and pipe separated plain text formats. + + ... versionadded:: 1.6 + """ + name = 'RobotFramework' + url = 'http://robotframework.org' + aliases = ['robotframework'] + filenames = ['*.robot', '*.resource'] + mimetypes = ['text/x-robotframework'] + + def __init__(self, **options): + options['tabsize'] = 2 + options['encoding'] = 'UTF-8' + Lexer.__init__(self, **options) + self.language = options['language'] + set_lang = shared_memory.ShareableList(name="language") + if not self.language: + # DEBUG self.new_lang = Language.from_name('en') + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = Language.from_name(self.language[0]) # DEBUG: We consider a single language + # print(f"DEBUG: robotframework.py after RobotFrameworkLexer _init_ mimetypes={self.mimetypes}\n" + # f"options['language']={options['language']}\n" + # f"self.new_lang={self.new_lang.code}") + + def get_tokens_unprocessed(self, text): + row_tokenizer = RowTokenizer(self.new_lang) + var_tokenizer = VariableTokenizer() + index = 0 + for row in text.splitlines(): + for value, token in row_tokenizer.tokenize(row): + for value, token in var_tokenizer.tokenize(value, token): + if value: + yield index, token, str(value) + index += len(value) + + +class VariableTokenizer: + + def tokenize(self, string, token): + var = VariableSplitter(string, identifiers='$@%&') + if var.start < 0 or token in (COMMENT, ERROR): + yield string, token + return + for value, token in self._tokenize(var, string, token): + if value: + yield value, token + + def _tokenize(self, var, string, orig_token): + before = string[:var.start] + yield before, orig_token + yield var.identifier + '{', SYNTAX + yield from self.tokenize(var.base, VARIABLE) + yield '}', SYNTAX + if var.index is not None: + yield '[', SYNTAX + yield from self.tokenize(var.index, VARIABLE) + yield ']', SYNTAX + yield from self.tokenize(string[var.end:], orig_token) + + +class RowTokenizer: + new_lang = None + + def __init__(self, new_lang): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self._table = UnknownTable() + self._splitter = RowSplitter() + testcases = TestCaseTable(new_lang=self.new_lang) + settings = SettingTable(testcases.set_default_template, new_lang=self.new_lang) + variables = VariableTable(new_lang=self.new_lang) + keywords = KeywordTable(new_lang=self.new_lang) + comments = CommentsTable(new_lang=self.new_lang) + normalized_headers = normalize_dict(self.new_lang.headers) + self._tables = {get_key_by_value(normalized_headers, 'settings'): settings, + get_key_by_value(normalized_headers, 'metadata'): settings, + get_key_by_value(normalized_headers, 'variables'): variables, + get_key_by_value(normalized_headers, 'testcases'): testcases, + get_key_by_value(normalized_headers, 'tasks'): testcases, + get_key_by_value(normalized_headers, 'keywords'): keywords, + get_key_by_value(normalized_headers, 'comments'): comments} + + def tokenize(self, row): + commented = False + heading = False + for index, value in enumerate(self._splitter.split(row)): + # First value, and every second after that, is a separator. + index, separator = divmod(index-1, 2) + if value.startswith('#'): + commented = True + elif index == 0 and value.startswith('*'): + self._table = self._start_table(value) + # print(f"DEBUG: robotframework.py RowTokenizer tokenize HEADING value={value}\n" + # f"self._table={self._table} lang={self.new_lang}\n") + heading = True + yield from self._tokenize(value, index, commented, + separator, heading) + self._table.end_row() + + def _start_table(self, header): + name = normalize(header, remove='*') + return self._tables.get(name, UnknownTable()) + + def _tokenize(self, value, index, commented, separator, heading): + # print(f"DEBUG: robotframework.py RowTokenizer _tokenize lang={self.new_lang}\n" + # f"{value=}, {index=}, {commented=}, {separator=}, {heading=}") + if commented: + yield value, COMMENT + elif separator: + yield value, SEPARATOR + elif heading: + yield value, HEADING + else: + yield from self._table.tokenize(value, index) + + +class RowSplitter: + _space_splitter = re.compile('( {2,})') + _pipe_splitter = re.compile(r'((?:^| +)\|(?: +|$))') + + def split(self, row): + splitter = (row.startswith('| ') and self._split_from_pipes + or self._split_from_spaces) + yield from splitter(row) + yield '\n' + + def _split_from_spaces(self, row): + yield '' # Start with (pseudo)separator similarly as with pipes + yield from self._space_splitter.split(row) + + def _split_from_pipes(self, row): + _, separator, rest = self._pipe_splitter.split(row, 1) + yield separator + while self._pipe_splitter.search(rest): + cell, separator, rest = self._pipe_splitter.split(rest, 1) + yield cell + yield separator + yield rest + + +class Tokenizer: + _tokens = None + new_lang = None + + def __init__(self, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self._index = 0 + # print(f"DEBUG: robotframework.py Tokenizer __init__ language={self.new_lang}") + + def tokenize(self, value): + values_and_tokens = self._tokenize(value, self._index) + self._index += 1 + if isinstance(values_and_tokens, type(Token)): + values_and_tokens = [(value, values_and_tokens)] + return values_and_tokens + + def _tokenize(self, value, index): + _ = value + index = min(index, len(self._tokens) - 1) + return self._tokens[index] + + @staticmethod + def _is_assign(value): + if value.endswith('='): + value = value[:-1].strip() + var = VariableSplitter(value, identifiers='$@&') + return var.start == 0 and var.end == len(value) + + +class Comment(Tokenizer): + _tokens = (COMMENT,) + new_lang = None + + +class Setting(Tokenizer): + _tokens = (SETTING, ARGUMENT) + """ + _keyword_settings = ('suitesetup', 'suiteprecondition', 'suiteteardown', + 'suitepostcondition', 'testsetup', 'tasksetup', 'testprecondition', + 'testteardown', 'taskteardown', 'testpostcondition', 'testtemplate', 'tasktemplate') + _import_settings = ('library', 'resource', 'variables') + _other_settings = ('documentation', 'metadata', 'forcetags', 'defaulttags', + 'testtimeout', 'tasktimeout') + """ + _custom_tokenizer = None + new_lang = None + + def __init__(self, template_setter=None, new_lang=None): + self._template_setter = template_setter + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + # print(f"DEBUG: robotframework.py Setting self.new_lang={self.new_lang}\n") + self.normalized_settings = normalize_dict(self.new_lang.settings) + self._keyword_settings = (get_key_by_value(self.normalized_settings,'suitesetup'), + get_key_by_value(self.normalized_settings, 'suiteprecondition'), + get_key_by_value(self.normalized_settings, 'suiteteardown'), + get_key_by_value(self.normalized_settings, 'suitepostcondition'), + # get_key_by_value(self.normalized_settings, 'documentation'), + get_key_by_value(self.normalized_settings, 'arguments'), # Keyword setting + get_key_by_value(self.normalized_settings,'teardown'), # Keyword setting + get_key_by_value(self.normalized_settings,'testsetup'), + get_key_by_value(self.normalized_settings,'tasksetup'), + get_key_by_value(self.normalized_settings, 'testprecondition'), + get_key_by_value(self.normalized_settings,'testteardown'), + get_key_by_value(self.normalized_settings,'taskteardown'), + get_key_by_value(self.normalized_settings, 'testpostcondition'), + get_key_by_value(self.normalized_settings, 'testtemplate'), + get_key_by_value(self.normalized_settings,'tasktemplate'), + get_key_by_value(self.normalized_settings, 'setup'), + get_key_by_value(self.normalized_settings, 'precondition'), # obsolete + get_key_by_value(self.normalized_settings, 'postcondition'), # obsolete + get_key_by_value(self.normalized_settings, 'template')) + self._import_settings = (get_key_by_value(self.normalized_settings,'library'), + get_key_by_value(self.normalized_settings,'resource'), + get_key_by_value(self.normalized_settings,'variables')) + self._other_settings = (get_key_by_value(self.normalized_settings,'documentation'), + get_key_by_value(self.normalized_settings, 'metadata'), + get_key_by_value(self.normalized_settings, 'keywordtags'), # New + get_key_by_value(self.normalized_settings, 'testtags'), # New + get_key_by_value(self.normalized_settings,'tasktags'), # New + get_key_by_value(self.normalized_settings, 'tags'), # New + get_key_by_value(self.normalized_settings,'forcetags'), # Non-existing + get_key_by_value(self.normalized_settings,'defaulttags'), # Non-existing + get_key_by_value(self.normalized_settings, 'testtimeout'), + get_key_by_value(self.normalized_settings, 'tasktimeout'), + get_key_by_value(self.normalized_settings, 'timeout')) + Tokenizer.__init__(self, new_lang=self.new_lang) + # print(f"DEBUG: robotframework.py Setting self.normalized_settings={self.normalized_settings}\n" + # f"self._keyword_settings={self._keyword_settings}\n" + # f"self._import_settings={self._import_settings}\n" + # f"self._other_settings={self._other_settings}\n") + + def _tokenize(self, value, index): + if index == 1 and self._template_setter: + self._template_setter(value) + if index == 0: + normalized = normalize(value) + if normalized in self._keyword_settings: + # print(f"DEBUG: robotframework.py Setting call KeywordCall in _tokenize value={value}") + self._custom_tokenizer = KeywordCall(support_assign=False) + elif normalized in self._import_settings: + self._custom_tokenizer = ImportSetting() + return ERROR + elif self._custom_tokenizer: + return self._custom_tokenizer.tokenize(value) + return Tokenizer._tokenize(self, value, index) + + +class ImportSetting(Tokenizer): + _tokens = (IMPORT, ARGUMENT) + new_lang = None + + def __init__(self, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self.normalized_settings = normalize_dict(self.new_lang.settings) + self._import_settings = (get_key_by_value(self.normalized_settings, 'library'), + get_key_by_value(self.normalized_settings, 'resource'), + get_key_by_value(self.normalized_settings, 'variables')) + Tokenizer.__init__(self, new_lang=self.new_lang) + + +class TestCaseSetting(Setting): + """ + _keyword_settings = ('setup', 'precondition', 'teardown', 'postcondition', + 'template') + _import_settings = () + _other_settings = ('documentation', 'tags', 'timeout') + """ + + def __init__(self, template_setter=None, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self.normalized_settings = normalize_dict(self.new_lang.settings) + self._keyword_settings = (get_key_by_value(self.normalized_settings, 'setup'), + get_key_by_value(self.normalized_settings, 'precondition'), # obsolete + get_key_by_value(self.normalized_settings, 'testsetup'), + get_key_by_value(self.normalized_settings, 'teardown'), + # get_key_by_value(self.normalized_settings, 'documentation'), + get_key_by_value(self.normalized_settings, 'postcondition'), # obsolete + get_key_by_value(self.normalized_settings, 'template')) + # DEBUG self._import_settings = () + self._other_settings = (get_key_by_value(self.normalized_settings, 'documentation'), + get_key_by_value(self.normalized_settings, 'timeout'), + get_key_by_value(self.normalized_settings, 'keywordtags'), # New + get_key_by_value(self.normalized_settings, 'tags')) + Setting.__init__(self, template_setter=template_setter, new_lang=new_lang) + # print(f"DEBUG: robotframework.py TestCaseSetting \n" + # f"self._keyword_settings={self._keyword_settings}\n" + # f"self._import_settings={self._import_settings}\n" + # f"self._other_settings={self._other_settings}\n") + + def _tokenize(self, value, index): + # print(f"DEBUG: robotframework.py TestCaseSetting _tokenize self.new_lang={self.new_lang} value={value}\n") + normalized = normalize(value) + if normalized in self._keyword_settings: + self._custom_tokenizer = KeywordCall(support_assign=False) + if index == 0: + stype = Setting(new_lang=self.new_lang)._tokenize(value[1:-1], index) + return [('[', SYNTAX), (value[1:-1], stype), (']', SYNTAX)] + elif self._custom_tokenizer: + return self._custom_tokenizer.tokenize(value) + return Setting(new_lang=self.new_lang)._tokenize(value, index) + + +class KeywordSetting(TestCaseSetting): + """ + _keyword_settings = ('teardown', ) + _other_settings = ('documentation', 'arguments', 'return', 'timeout', 'tags') + """ + + def __init__(self, template_setter=None, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self.normalized_settings = normalize_dict(self.new_lang.settings) + self._keyword_settings = (get_key_by_value(self.normalized_settings, 'setup'), + get_key_by_value(self.normalized_settings, 'precondition'), # obsolete + get_key_by_value(self.normalized_settings, 'testsetup'), + get_key_by_value(self.normalized_settings, 'teardown'), + get_key_by_value(self.normalized_settings, 'return'), # Non-existing + get_key_by_value(self.normalized_settings, 'postcondition'), # obsolete + get_key_by_value(self.normalized_settings, 'template')) + self._other_settings = (get_key_by_value(self.normalized_settings, 'documentation'), + get_key_by_value(self.normalized_settings, 'timeout'), + get_key_by_value(self.normalized_settings, 'keywordtags'), # New + get_key_by_value(self.normalized_settings, 'tags')) + TestCaseSetting.__init__(self, template_setter=template_setter, new_lang=new_lang) + + +class Variable(Tokenizer): + _tokens = (SYNTAX, ARGUMENT) + new_lang = None + + def _tokenize(self, value, index): + if index == 0 and not self._is_assign(value): + return ERROR + return Tokenizer._tokenize(self, value, index) + + +class KeywordCall(Tokenizer): + _tokens = (KEYWORD, ARGUMENT) + new_lang = None + + def __init__(self, support_assign=True, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + # print(f"DEBUG: robotframework.py KeywordCall __init__ lang={self.new_lang}") + Tokenizer.__init__(self, new_lang=self.new_lang) + self._keyword_found = not support_assign + self._assigns = 0 + + def _tokenize(self, value, index): + # print(f"DEBUG: robotframework.py KeywordCall _tokenize ENTER value={value} index={index}\n") + if not self._keyword_found and self._is_assign(value): + self._assigns += 1 + return SYNTAX # VariableTokenizer tokenizes this later. + if self._keyword_found: + return Tokenizer._tokenize(self, value, index - self._assigns) + self._keyword_found = True + # print(f"DEBUG: robotframework.py KeywordCall _tokenize CALL GHERKIN value={value}\n" + # f"new_lang={self.new_lang}") + return GherkinTokenizer(new_lang=self.new_lang).tokenize(value, KEYWORD) + + +class GherkinTokenizer(object): + # _gherkin_prefix = re.compile('^(Given|When|Then|And|But) ', re.IGNORECASE) + new_lang = None + + def __init__(self, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self.normalized_bdd_prefixes = normalize_pipe_list(list(self.new_lang.bdd_prefixes)) + # print(f"DEBUG: robotframework.py GherkinTokenizer _tokenize DEFINITION GHERKIN" + # f" BDDPREFIXES={self.new_lang.bdd_prefixes}\n" + # f"PATTERN='^({self.normalized_bdd_prefixes}) '" + # f"new_lang={self.new_lang}") + self._gherkin_prefix = re.compile(f'^({self.normalized_bdd_prefixes}) ', re.IGNORECASE) + + def tokenize(self, value, token): + # print(f"DEBUG: robotframework.py GherkinTokenizer tokenize ENTER self._gherkin_prefix={self._gherkin_prefix}:" + # f"\nvalue={value} token={token}") + match = self._gherkin_prefix.match(value) + if not match: + # print(f"DEBUG: robotframework.py GherkinTokenizer tokenize NO MATCH value={value} token={token}") + return [(value, token)] + end = match.end() + # print(f"DEBUG: robotframework.py GherkinTokenizer tokenize RETURN MATCH GHERKIN value={value[:end]} rest={value[end:]}") + return [(value[:end], GHERKIN), (value[end:], token)] + + +class TemplatedKeywordCall(Tokenizer): + _tokens = (ARGUMENT,) + new_lang = None + + +class ForLoop(Tokenizer): + new_lang = None + + def __init__(self, new_lang=None): + if not self.new_lang: + if not new_lang: + self.new_lang = Language.from_name('en') + else: + self.new_lang = new_lang + Tokenizer.__init__(self, new_lang=self.new_lang) + self._in_arguments = False + + def _tokenize(self, value, index): + token = self._in_arguments and ARGUMENT or SYNTAX + if value.upper() in ('IN', 'IN ENUMERATE', 'IN RANGE', 'IN ZIP'): + self._in_arguments = True + return token + + +class _Table: + _tokenizer_class = None + new_lang = None + + def __init__(self, prev_tokenizer=None, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self._tokenizer = self._tokenizer_class() + self._prev_tokenizer = prev_tokenizer + self._prev_values_on_row = [] + + def tokenize(self, value, index): + if self._continues(value, index): + self._tokenizer = self._prev_tokenizer + yield value, SYNTAX + else: + yield from self._tokenize(value, index) + self._prev_values_on_row.append(value) + + def _continues(self, value, index): + _ = index + return value == '...' and all(self._is_empty(t) for t in self._prev_values_on_row) + + def _is_empty(self, value): + return value in ('', '\\') + + def _tokenize(self, value, index): + _ = index + return self._tokenizer.tokenize(value) + + def end_row(self): + self.__init__(prev_tokenizer=self._tokenizer) + + +class CommentsTable(_Table): + _tokenizer_class = Comment + + def _continues(self, value, index): + return False + + +class UnknownTable(_Table): + _tokenizer_class = Comment + + def _continues(self, value, index): + return False + + +class VariableTable(_Table): + _tokenizer_class = Variable + + +class SettingTable(_Table): + _tokenizer_class = Setting + new_lang = None + + def __init__(self, template_setter, prev_tokenizer=None, new_lang=None): + self._template_setter = template_setter + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + self.normalized_settings = normalize_dict(self.new_lang.settings) + _Table.__init__(self, prev_tokenizer, new_lang=self.new_lang) + # print(f"DEBUG: robotframework.py SettingTable self.normalized_settings={self.normalized_settings}") + + def _tokenize(self, value, index): + # print(f"DEBUG: robotframework.py SettingTable ENTER _tokenize " + # f"self.normalized_settings={self.normalized_settings}\n" + # f"value={value} new_lang={self.new_lang}") + if index == 0 and normalize(value) in ( + get_key_by_value(self.normalized_settings, 'metadata'), # DEBUG + get_key_by_value(self.normalized_settings, 'template'), + get_key_by_value(self.normalized_settings, 'documentation'), + get_key_by_value(self.normalized_settings, 'suitesetup'), + get_key_by_value(self.normalized_settings, 'suiteteardown'), + get_key_by_value(self.normalized_settings,'testsetup'), + get_key_by_value(self.normalized_settings,'tasksetup'), + get_key_by_value(self.normalized_settings,'testteardown'), + get_key_by_value(self.normalized_settings,'taskteardown'), + get_key_by_value(self.normalized_settings, 'library'), + get_key_by_value(self.normalized_settings, 'resource'), + get_key_by_value(self.normalized_settings, 'variables'), + get_key_by_value(self.normalized_settings, 'testtemplate'), + get_key_by_value(self.normalized_settings, 'tasktemplate')): + self._tokenizer = Setting(template_setter=self._template_setter, new_lang=self.new_lang) + return _Table._tokenize(self, value, index) + + def end_row(self): + self.__init__(self._template_setter, prev_tokenizer=self._tokenizer, new_lang=self.new_lang) + + +class TestCaseTable(_Table): + _setting_class = TestCaseSetting + _test_template = None + _default_template = None + new_lang = None + + def __init__(self, prev_tokenizer=None, new_lang=None): + if not self.new_lang: + if not new_lang: + set_lang = shared_memory.ShareableList(name="language") + self.new_lang = Language.from_name(set_lang[0]) + else: + self.new_lang = new_lang + # print(f"DEBUG: robotframework.py TestCaseTable __init__ self.new_lang={self.new_lang}") + self.normalized_settings = normalize_dict(self.new_lang.settings) + _Table.__init__(self, prev_tokenizer) + # print(f"DEBUG: robotframework.py TestCaseTable self.normalized_settings={self.normalized_settings}") + + @property + def _tokenizer_class(self): + if self._test_template or (self._default_template and self._test_template is not False): + return TemplatedKeywordCall + return KeywordCall + + def _continues(self, value, index): + return index > 0 and _Table._continues(self, value, index) + + def _tokenize(self, value, index): + if index == 2 and self._test_template and self._is_template_set(value): + return KeywordCall(new_lang=self.new_lang).tokenize(value) + if index == 0 and value: + self._test_template = None + return GherkinTokenizer(new_lang=self.new_lang).tokenize(value, TC_KW_NAME) + if index == 1 and self._is_setting(value): + if self._is_template(value): + self._test_template = True + self._tokenizer = self._setting_class(template_setter=self.set_test_template, new_lang=self.new_lang) + else: + self._test_template = None + self._tokenizer = self._setting_class(new_lang=self.new_lang) + if index == 1 and self._is_for_loop(value): + self._tokenizer = ForLoop() + if index == 1 and self._is_empty(value): + return [(value, SYNTAX)] + if index == 1 and not self._is_setting(value): + test_gherkin = GherkinTokenizer(new_lang=self.new_lang).tokenize(value, KEYWORD) + if test_gherkin[0][1] in (GHERKIN, KEYWORD): + return test_gherkin + return _Table._tokenize(self, value, index) + + @staticmethod + def _is_setting(value): + return value.startswith('[') and value.endswith(']') + + def _is_template(self, value): + return normalize(value) in (f"[{get_key_by_value(self.normalized_settings, 'template')}]", + f"[{get_key_by_value(self.normalized_settings, 'testsetup')}]", + f"[{get_key_by_value(self.normalized_settings, 'tasksetup')}]", + f"[{get_key_by_value(self.normalized_settings, 'testteardown')}]", + f"[{get_key_by_value(self.normalized_settings, 'taskteardown')}]", + f"[{get_key_by_value(self.normalized_settings, 'testtemplate')}]", + f"[{get_key_by_value(self.normalized_settings, 'tasktemplate')}]", + f"[{get_key_by_value(self.normalized_settings, 'setup')}]", + f"[{get_key_by_value(self.normalized_settings, 'teardown')}]") + + @staticmethod + def _is_for_loop(value): + return normalize(value, remove=':') == 'for' + + def set_test_template(self, template): + self._test_template = self._is_template_set(template) + + def set_default_template(self, template): + self._default_template = self._is_template_set(template) + + @staticmethod + def _is_template_set(template): + return normalize(template) not in ('', '\\', 'none', '${empty}') + + +class KeywordTable(TestCaseTable): + _tokenizer_class = KeywordCall + _setting_class = KeywordSetting + + def _is_template(self, value): + return False + + +# Following code copied directly from Robot Framework 2.7.5. + +class VariableSplitter: + + def __init__(self, string, identifiers): + self.identifier = None + self.base = None + self.index = None + self.start = -1 + self.end = -1 + self._identifiers = identifiers + self._may_have_internal_variables = False + try: + self._split(string) + except ValueError: + pass + else: + self._finalize() + + def get_replaced_base(self, variables): + if self._may_have_internal_variables: + return variables.replace_string(self.base) + return self.base + + def _finalize(self): + self.identifier = self._variable_chars[0] + self.base = ''.join(self._variable_chars[2:-1]) + self.end = self.start + len(self._variable_chars) + if self._has_list_or_dict_variable_index(): + self.index = ''.join(self._list_and_dict_variable_index_chars[1:-1]) + self.end += len(self._list_and_dict_variable_index_chars) + + def _has_list_or_dict_variable_index(self): + return self._list_and_dict_variable_index_chars\ + and self._list_and_dict_variable_index_chars[-1] == ']' + + def _split(self, string): + start_index, max_index = self._find_variable(string) + self.start = start_index + self._open_curly = 1 + self._state = self._variable_state + self._variable_chars = [string[start_index], '{'] + self._list_and_dict_variable_index_chars = [] + self._string = string + start_index += 2 + for index, char in enumerate(string[start_index:]): + index += start_index # Giving start to enumerate only in Py 2.6+ + try: + self._state(char, index) + except StopIteration: + return + if index == max_index and not self._scanning_list_variable_index(): + return + + def _scanning_list_variable_index(self): + return self._state in [self._waiting_list_variable_index_state, + self._list_variable_index_state] + + def _find_variable(self, string): + max_end_index = string.rfind('}') + if max_end_index == -1: + raise ValueError('No variable end found') + if self._is_escaped(string, max_end_index): + return self._find_variable(string[:max_end_index]) + start_index = self._find_start_index(string, 1, max_end_index) + if start_index == -1: + raise ValueError('No variable start found') + return start_index, max_end_index + + def _find_start_index(self, string, start, end): + index = string.find('{', start, end) - 1 + if index < 0: + return -1 + if self._start_index_is_ok(string, index): + return index + return self._find_start_index(string, index+2, end) + + def _start_index_is_ok(self, string, index): + return string[index] in self._identifiers\ + and not self._is_escaped(string, index) + + def _is_escaped(self, string, index): + escaped = False + while index > 0 and string[index-1] == '\\': + index -= 1 + escaped = not escaped + return escaped + + def _variable_state(self, char, index): + self._variable_chars.append(char) + if char == '}' and not self._is_escaped(self._string, index): + self._open_curly -= 1 + if self._open_curly == 0: + if not self._is_list_or_dict_variable(): + raise StopIteration + self._state = self._waiting_list_variable_index_state + elif char in self._identifiers: + self._state = self._internal_variable_start_state + + def _is_list_or_dict_variable(self): + return self._variable_chars[0] in ('@','&') + + def _internal_variable_start_state(self, char, index): + self._state = self._variable_state + if char == '{': + self._variable_chars.append(char) + self._open_curly += 1 + self._may_have_internal_variables = True + else: + self._variable_state(char, index) + + def _waiting_list_variable_index_state(self, char, index): + if char != '[': + raise StopIteration + self._list_and_dict_variable_index_chars.append(char) + self._state = self._list_variable_index_state + + def _list_variable_index_state(self, char, index): + self._list_and_dict_variable_index_chars.append(char) + if char == ']': + raise StopIteration diff --git a/src/robotide/lib/robot/parsing/datarow.py b/src/robotide/lib/robot/parsing/datarow.py index 5fe424e4e..2495cee39 100644 --- a/src/robotide/lib/robot/parsing/datarow.py +++ b/src/robotide/lib/robot/parsing/datarow.py @@ -34,11 +34,15 @@ def _parse(self, row): data = [] comments = [] # print(f"DEBUG: datarow enter _parse row={row}\n") + if row[0].startswith('#'): + data.extend(row) + return data, comments # DEBUG keep empty cells for cell in row: # print(f"DEBUG: datarow before clean cell={cell}") cell = self._collapse_whitespace(cell) # print(f"DEBUG: datarow after clean cell={cell}") - if cell and cell[0] == '#' or comments: + # DEBUG: Lets keep # at start of line + if cell and cell.startswith('#') or comments: comments.append(cell) else: data.append(cell) diff --git a/src/robotide/lib/robot/parsing/htmlreader.py b/src/robotide/lib/robot/parsing/htmlreader.py index abe416423..2b0659840 100644 --- a/src/robotide/lib/robot/parsing/htmlreader.py +++ b/src/robotide/lib/robot/parsing/htmlreader.py @@ -35,8 +35,9 @@ class HtmlReader(HTMLParser): INITIAL = 1 PROCESS = 2 - def __init__(self, spaces=2): + def __init__(self, spaces=2, language=None): self._spaces = spaces + _ = language # Ignored, it is here for compatibility with RF 6.1, RIDE > 2.0.8.1 HTMLParser.__init__(self) self._encoding = 'ISO-8859-1' self._handlers = {'table_start' : self.table_start, diff --git a/src/robotide/lib/robot/parsing/model.py b/src/robotide/lib/robot/parsing/model.py index 85a3fb94a..fa0e2b26b 100644 --- a/src/robotide/lib/robot/parsing/model.py +++ b/src/robotide/lib/robot/parsing/model.py @@ -18,11 +18,17 @@ import re import warnings +try: + from robot.conf.languages import Language +except ModuleNotFoundError: + Language = None + +from robotide.lib.compat.parsing import language as lang from robotide.lib.robot.errors import DataError from robotide.lib.robot.variables import is_var from robotide.lib.robot.output import LOGGER from robotide.lib.robot.writer import DataFileWriter -from robotide.lib.robot.utils import abspath, is_string, normalize, py2to3, NormalizedDict +from robotide.lib.robot.utils import abspath, is_string, normalize, normalized_headers, NormalizedDict from .comments import Comment from .populators import FromFilePopulator, FromDirectoryPopulator, NoTestsFound @@ -34,7 +40,7 @@ def TestData(parent=None, source=None, include_suites=None, - warn_on_skipped='DEPRECATED', extensions=None, settings=None): + warn_on_skipped='DEPRECATED', extensions=None, settings=None, language=None): """Parses a file or directory to a corresponding model object. :param parent: Optional parent to be used in creation of the model object. @@ -50,9 +56,8 @@ def TestData(parent=None, source=None, include_suites=None, warnings.warn("Option 'warn_on_skipped' is deprecated and has no " "effect.", DeprecationWarning) if os.path.isdir(source): - return TestDataDirectory(parent, source, settings).populate(include_suites, - extensions) - return TestCaseFile(parent, source, settings).populate() + return TestDataDirectory(parent, source, settings, language).populate(include_suites, extensions) + return TestCaseFile(parent, source, settings, language).populate() class _TestData(object): @@ -63,29 +68,56 @@ class _TestData(object): # remove Comments section, because we want to keep them as they are in files _comment_table_names = 'Comment', 'Comments' - def __init__(self, parent=None, source=None): + def __init__(self, parent=None, source=None, language=None): self.parent = parent self.source = abspath(source) if source else None self.children = [] self._preamble = [] + self._language = language + # self.comment_table = None self._tables = dict(self._get_tables()) def _get_tables(self): - for names, table in [(self._setting_table_names, self.setting_table), + tables = [(self._setting_table_names, self.setting_table), (self._variable_table_names, self.variable_table), (self._testcase_table_names, self.testcase_table), (self._keyword_table_names, self.keyword_table), - (self._comment_table_names, None)]: + [self._comment_table_names, self.comment_table]] + # print(f"DEBUG: model.py _TestData._get_tables {self._language=} tables= {tables}") + if self._language: + tables = self.get_tables_for(self._language) + # print(f"DEBUG: model.py _TestData._get_tables AFTER get_tables_for tables= {tables}") + for names, table in tables: # remove Comments section, because we want to keep them as they are in files # , (self._comment_table_names, None) for name in names: yield name, table - def start_table(self, header_row): - table = self._find_table(header_row) + def start_table(self, header_row, lineno: int, llang: list): + self._language = llang + # local_header = lang.get_headers_for(llang, ('Comments',)) + # print(f"DEBUG: model _TestData start_table ENTER header_row[0]={ header_row[0]} lineno={lineno}\n" + # f"language={llang}") # local_header={local_header}") + if header_row[0] in lang.get_headers_for(llang, ('Comments',), lowercase=False): + self.comment_table = table = CommentsTable(self, llang) # Multiple creation of table only if exists + self.tables.append(self.comment_table) + else: + table = self._find_table(header_row) + # print(f"DEBUG: model _TestData start_table table={table} header_row={header_row}") if table is None or not self._table_is_allowed(table): return None - table.set_header(header_row) + if table.type == 'test case': + self.testcase_table = table = TestCaseTable(self, self.language) + self.tables.append(self.testcase_table) + if table.type == 'keyword': + self.keyword_table = table = KeywordTable(self, self.language) + self.tables.append(self.keyword_table) + if table.type == 'variable': + self.variable_table = table = VariableTable(self) + self.tables.append(self.variable_table) + table.set_header(header_row, lineno=lineno) + # print(f"DEBUG: model _TestData start_table returning table={table} table name={table.name}\n" + # f"table.type={table.type}") return table def has_preamble(self): @@ -102,14 +134,63 @@ def preamble(self): def preamble(self, row): self.add_preamble(row) + @property + def language(self): + return self._language + + @language.setter + def language(self, llang): + self._language = llang + + def get_tables_for(self, language): + t_en = [(self._setting_table_names, self.setting_table), + (self._variable_table_names, self.variable_table), + (self._testcase_table_names, self.testcase_table), + (self._keyword_table_names, self.keyword_table), [self._comment_table_names, self.comment_table]] + try: + _lang = Language.from_name(language[0]) # DEBUG: Consider several languages + except ValueError: + _lang = None + # print(f"DEBUG: model.py get_tables_for Exception at language={language[0]}") + + if isinstance(_lang, Language): + headers = _lang.headers + build_table = [] + for idx, base in enumerate(t_en): + build_headings = [] + for item in base[0]: + inx = 0 + for k, v in zip(headers.keys(), headers.values()): + try: + if v == item: + build_headings.append(list(headers.keys())[inx]) + except Exception as e: + pass + inx += 1 + build_table.append((tuple(build_headings), list(base)[1])) + # print(f"DEBUG: model.py get_tables_for returning table= {build_table}") + return build_table + t_en + return t_en + def _find_table(self, header_row): name = header_row[0] if header_row else '' title = name.title() + self._tables = dict(self._get_tables()) + # print(f"DEBUG: model.py _find_table ENTER {title=} _tables= {self._tables}") + headers = normalized_headers(self._tables) + normalized_name = normalize(name) + # print(f"DEBUG: model.py _find_table HEADERS name={normalized_name} headers={headers}") if title not in self._tables: + if normalized_name in headers: + idx = headers.index(normalized_name) + title = list(self._tables.keys())[idx] + # print(f"DEBUG: model.py _find_table RETURN NEW {title}") + return self._tables[title] title = self._resolve_deprecated_table(name) if title is None: self._report_unrecognized_table(name) return None + # print(f"DEBUG: model.py _find_table FOUND {title=} return {self._tables[title]}") return self._tables[title] def _resolve_deprecated_table(self, used_name): @@ -138,6 +219,7 @@ def _report_unrecognized_table(self, name): ) def _table_is_allowed(self, table): + _ = table return True @property @@ -157,7 +239,8 @@ def _format_name(self, name): name = name.replace('_', ' ').strip() return name.title() if name.islower() else name - def _strip_possible_prefix_from_name(self, name): + @staticmethod + def _strip_possible_prefix_from_name(name): return name.split('__', 1)[-1] @property @@ -182,10 +265,10 @@ def save(self, **options): See also :py:class:`robot.writer.datafilewriter.DataFileWriter` """ + options['language'] = self.language return DataFileWriter(**options).write(self) -@py2to3 class TestCaseFile(_TestData): """The parsed test case file object. @@ -194,18 +277,21 @@ class TestCaseFile(_TestData): """ __test__ = False - def __init__(self, parent=None, source=None, settings=None): + def __init__(self, parent=None, source=None, settings=None, language=None): self.directory = os.path.dirname(source) if source else None - self.setting_table = TestCaseFileSettingTable(self) + self.language = language + self.setting_table = TestCaseFileSettingTable(self, self.language) self.variable_table = VariableTable(self) - self.testcase_table = TestCaseTable(self) - self.keyword_table = KeywordTable(self) + self.testcase_table = TestCaseTable(self, self.language) + self.keyword_table = KeywordTable(self, self.language) + self.comment_table = None # DEBUG: CommentsTable(self, self.language) + self.tables = [self.setting_table, self.variable_table, self.keyword_table, self.comment_table] self._settings = settings self._tab_size = self._settings.get('txt number of spaces', 2) if self._settings else 2 - _TestData.__init__(self, parent, source) + _TestData.__init__(self, parent, source, language) def populate(self): - FromFilePopulator(self, self._tab_size).populate(self.source) + FromFilePopulator(self, self._tab_size, self.language).populate(self.source) self._validate() return self @@ -218,8 +304,7 @@ def has_tests(self): return True def __iter__(self): - for table in [self.setting_table, self.variable_table, - self.testcase_table, self.keyword_table]: + for table in self.tables: yield table def __nonzero__(self): @@ -232,19 +317,24 @@ class ResourceFile(_TestData): :param source: path where resource file is read from. """ - def __init__(self, source=None, settings=None): + def __init__(self, source=None, settings=None, language=None): self.directory = os.path.dirname(source) if source else None - self.setting_table = ResourceFileSettingTable(self) + self.language = language + if not self.language and source: + self.language = lang.check_file_language(source) + self.setting_table = ResourceFileSettingTable(self, self.language) self.variable_table = VariableTable(self) - self.testcase_table = TestCaseTable(self) - self.keyword_table = KeywordTable(self) + self.testcase_table = TestCaseTable(self, self.language) + self.keyword_table = KeywordTable(self, self.language) + self.comment_table = None # DEBUG: CommentsTable(self, self.language) + self.tables = [self.setting_table, self.variable_table, self.keyword_table, self.comment_table] self.settings = settings self._preamble = [] self._tab_size = self.settings.get('txt number of spaces', 2) if self.settings else 2 - _TestData.__init__(self, source=source) + _TestData.__init__(self, source=source, language=self.language) def populate(self): - FromFilePopulator(self, self._tab_size).populate(self.source, resource=True) + FromFilePopulator(self, self._tab_size, self.language).populate(self.source, resource=True) self._report_status() return self @@ -261,8 +351,12 @@ def _table_is_allowed(self, table): "tasks." % self.source) return True + @staticmethod + def has_tests(): + return False + def __iter__(self): - for table in [self.setting_table, self.variable_table, self.keyword_table]: + for table in self.tables: yield table @@ -276,21 +370,31 @@ class TestDataDirectory(_TestData): """ __test__ = False - def __init__(self, parent=None, source=None, settings=None): + def __init__(self, parent=None, source=None, settings=None, language=None): self.directory = source self.initfile = None - self.setting_table = InitFileSettingTable(self) + self.language = language + self.setting_table = InitFileSettingTable(self, self.language) self.variable_table = VariableTable(self) - self.testcase_table = TestCaseTable(self) - self.keyword_table = KeywordTable(self) + self.testcase_table = TestCaseTable(self, self.language) + self.keyword_table = KeywordTable(self, self.language) + self.comment_table = None # DEBUG: CommentsTable(self, self.language) + self.tables = [self.setting_table, self.variable_table, self.keyword_table, self.comment_table] self._settings = settings self._tab_size = self._settings.get('txt number of spaces', 2) if self._settings else 2 - _TestData.__init__(self, parent, source) + _TestData.__init__(self, parent, source, language=self.language) def populate(self, include_suites=None, extensions=None, recurse=True): + # print(f"DEBUG: Dataloader TestDataDirectory ENTER populate DataError source={self.source}") FromDirectoryPopulator().populate(self.source, self, include_suites, extensions, recurse, self._tab_size) - self.children = [ch for ch in self.children if ch.has_tests()] + ch_list = [] + for ch in self.children: + if ch is not None and ch.has_tests(): + # print(f"DEBUG: Dataloader TestDataDirectory ENTER populate children source={ch.source}") + ch_list.append(ch) + self.children = ch_list + # DEBUG self.children = [ch for ch in self.children if ch.has_tests()] # Attr has_tests missing in html logs return self def _get_basename(self): @@ -303,29 +407,31 @@ def _table_is_allowed(self, table): return False return True - def add_child(self, path, include_suites, extensions=None): + def add_child(self, path, include_suites, extensions=None, language=None): self.children.append(TestData(parent=self, source=path, include_suites=include_suites, - extensions=extensions, settings=self._settings)) + extensions=extensions, settings=self._settings, language=language)) def has_tests(self): - return any(ch.has_tests() for ch in self.children) + return any(ch.has_tests() for ch in self.children) if self.children else None def __iter__(self): - for table in [self.setting_table, self.variable_table, self.keyword_table]: + for table in self.tables: yield table -@py2to3 class _Table(object): def __init__(self, parent): self.parent = parent self._header = None + self._lineno = None - def set_header(self, header): + def set_header(self, header, lineno:int): + # print(f"DEBUG: model.py _Table set_header={header} self._lineno={lineno}") self._header = self._prune_old_style_headers(header) + self._lineno = lineno def _prune_old_style_headers(self, header): if len(header) < 3: @@ -336,10 +442,12 @@ def _prune_old_style_headers(self, header): @property def header(self): + # print(f"DEBUG: model.py _Table property header {self._header}") return self._header or [self.type.title() + 's'] @property def name(self): + # print(f"DEBUG: model.py _Table property name self.header[0]={self._header[0]}") return self.header[0] @property @@ -365,11 +473,13 @@ class _WithSettings(object): _aliases = {} def get_setter(self, name): - if name[-1:] == ':': + if name.startswith('#'): + return None + if name.endswith(':'): name = name[:-1] # Patching for ... setting, this way we don't get a Parser Error on log if name == '...': - name = 'Documentation' + name = self.get_localized_setting_name('Documentation') setter = self._get_setter(name) if setter is not None: return setter @@ -381,8 +491,13 @@ def get_setter(self, name): def _get_setter(self, name): title = name.title() + if name in self._setters: + return self._setters[name](self) + if name in self._aliases: + return self._setters[self._aliases[name]](self) if title in self._aliases: - title = self._aliases[name] + title = self._aliases[title] + return self._setters[title](self) if title in self._setters: return self._setters[title](self) return None @@ -404,25 +519,37 @@ def _report_deprecated_setting(self, deprecated, correct): def report_invalid_syntax(self, message, level='ERROR'): raise NotImplementedError + def get_localized_setting_name(self, english_name: str): + raise NotImplementedError + class _SettingTable(_Table, _WithSettings): type = 'setting' + language = [] def __init__(self, parent): _Table.__init__(self, parent) - self.doc = Documentation('Documentation', self) - self.suite_setup = Fixture('Suite Setup', self) - self.suite_teardown = Fixture('Suite Teardown', self) - self.test_setup = Fixture('Test Setup', self) - self.test_teardown = Fixture('Test Teardown', self) - self.force_tags = Tags('Force Tags', self) # To deprecate after RF 7.0 - self.default_tags = Tags('Default Tags', self) # To deprecate after RF 7.0 - self.test_tags = Tags('Test Tags', self) # New since RF 6.0 - self.test_template = Template('Test Template', self) - self.test_timeout = Timeout('Test Timeout', self) + self.doc = Documentation(self.get_localized_setting_name('Documentation'), self) + self.suite_setup = Fixture(self.get_localized_setting_name('Suite Setup'), self) + self.suite_teardown = Fixture(self.get_localized_setting_name('Suite Teardown'), self) + self.test_setup = Fixture(self.get_localized_setting_name('Test Setup'), self) + self.test_teardown = Fixture(self.get_localized_setting_name('Test Teardown'), self) + self.force_tags = Tags(self.get_localized_setting_name('Force Tags'), self) # To deprecate after RF 7.0 + self.default_tags = Tags(self.get_localized_setting_name('Default Tags'), self) # To deprecate after RF 7.0 + self.test_tags = Tags(self.get_localized_setting_name('Test Tags'), self) # New since RF 6.0 + self.test_template = Template(self.get_localized_setting_name('Test Template'), self) + self.test_timeout = Timeout(self.get_localized_setting_name('Test Timeout'), self) self.metadata = MetadataList(self) self.imports = ImportList(self) + def get_localized_setting_name(self, english_name): + if not self._aliases: # DEBUG Or localization disabled + return english_name + for loc, en in self._aliases.items(): + if en == english_name: + return loc + return english_name + @property def _old_header_matcher(self): return OldStyleSettingAndVariableTableHeaderMatcher() @@ -469,6 +596,17 @@ class TestCaseFileSettingTable(_SettingTable): 'Task Template': 'Test Template', 'Task Timeout': 'Test Timeout'} + def __init__(self, parent, language=None): + if language: + self.language = language + self._aliases = lang.get_settings_for(language, + ['Documentation', 'Suite Setup', 'Suite Teardown', + 'Test Setup', 'Test Teardown', 'Force Tags', 'Default Tags', + 'Test Tags', 'Test Template', 'Test Timeout', 'Library', + 'Resource', 'Variables', 'Metadata','Task Setup', + 'Task Teardown', 'Task Template', 'Task Timeout']) + _SettingTable.__init__(self, parent) + def __iter__(self): for setting in [self.doc, self.suite_setup, self.suite_teardown, self.test_setup, self.test_teardown, self.force_tags, @@ -483,6 +621,12 @@ class ResourceFileSettingTable(_SettingTable): 'Resource': lambda s: s.imports.populate_resource, 'Variables': lambda s: s.imports.populate_variables} + def __init__(self, parent, language=None): + if language: + self.language = language + self._aliases = lang.get_settings_for(language,['Documentation', 'Library', 'Resource', 'Variables']) + _SettingTable.__init__(self, parent) + def __iter__(self): for setting in [self.doc] + self.imports.data: yield setting @@ -502,6 +646,15 @@ class InitFileSettingTable(_SettingTable): 'Variables': lambda s: s.imports.populate_variables, 'Metadata': lambda s: s.metadata.populate} + def __init__(self, parent, language=None): + if language: + self.language = language + self._aliases = lang.get_settings_for(language, ['Documentation', 'Suite Setup', + 'Suite Teardown', 'Test Setup', 'Test Teardown', + 'Test Timeout', 'Force Tags', 'Test Tags', 'Library', + 'Resource', 'Variables', 'Metadata']) + _SettingTable.__init__(self, parent) + def __iter__(self): for setting in [self.doc, self.suite_setup, self.suite_teardown, self.test_setup, self.test_teardown, self.force_tags, self.test_tags, @@ -534,14 +687,15 @@ class TestCaseTable(_Table): __test__ = False type = 'test case' - def __init__(self, parent): + def __init__(self, parent, language=None): _Table.__init__(self, parent) self.tests = [] + self.language = language - def set_header(self, header): + def set_header(self, header, lineno: int): if self._header and header: self._validate_mode(self._header[0], header[0]) - _Table.set_header(self, header) + _Table.set_header(self, header, lineno=lineno) def _validate_mode(self, name1, name2): tasks1 = normalize(name1) in ('task', 'tasks') @@ -554,7 +708,7 @@ def _old_header_matcher(self): return OldStyleTestAndKeywordTableHeaderMatcher() def add(self, name): - self.tests.append(TestCase(self, name)) + self.tests.append(TestCase(self, name, self.language)) return self.tests[-1] def __iter__(self): @@ -567,7 +721,8 @@ def is_started(self): class KeywordTable(_Table): type = 'keyword' - def __init__(self, parent): + def __init__(self, parent, language=None): + self.language = language _Table.__init__(self, parent) self.keywords = [] @@ -576,14 +731,39 @@ def _old_header_matcher(self): return OldStyleTestAndKeywordTableHeaderMatcher() def add(self, name): - self.keywords.append(UserKeyword(self, name)) + self.keywords.append(UserKeyword(self, name, self.language)) return self.keywords[-1] def __iter__(self): return iter(self.keywords) -@py2to3 +class CommentsTable(_Table): + type = 'comments' + is_comments = True + + def __init__(self, parent, language=None): + self.language = language + self.section_comments = [] + _Table.__init__(self, parent) + # print(f"DEBUG: model Comments __init__ {self.language=} lineno= {self._lineno}") + + def add(self, content): + # print(f"DEBUG: model.py CommentsTable add content={content}") + self.section_comments.append(''.join(content)) # , self._lineno]) # CommentRow(content, self._lineno)) + + def is_started(self): + # print(f"DEBUG: RFLib model.py CommentsTable is_started {bool(self._header)}") + return bool(self._header) + + def __len__(self): + return len(self.section_comments) # sum(1 for _ in self.section_comments) + + def __iter__(self): + # print(f"DEBUG: RFLib model.py CommentsTable __iter_ {self.section_comments=}") + iter(self.section_comments) + + class Variable(object): def __init__(self, parent, name, value, comment=None): @@ -636,16 +816,22 @@ def copy(self, name): class TestCase(_WithSteps, _WithSettings): __test__ = False + _aliases = {} - def __init__(self, parent, name): + def __init__(self, parent, name, language=None): self.parent = parent self.name = name - self.doc = Documentation('[Documentation]', self) - self.template = Template('[Template]', self) - self.tags = Tags('[Tags]', self) - self.setup = Fixture('[Setup]', self) - self.teardown = Fixture('[Teardown]', self) - self.timeout = Timeout('[Timeout]', self) + self.language = language + # print(f"DEBUG: model.py TestCase INIT language={self.language}") + if self.language: + self._aliases = lang.get_settings_for(language, ['Arguments', 'Documentation', 'Template', + 'Tags', 'Setup', 'Teardown', 'Timeout']) + self.doc = Documentation(self.get_localized_setting_name('[Documentation]'), self) + self.template = Template(self.get_localized_setting_name('[Template]'), self) + self.tags = Tags(self.get_localized_setting_name('[Tags]'), self) + self.setup = Fixture(self.get_localized_setting_name('[Setup]'), self) + self.teardown = Fixture(self.get_localized_setting_name('[Teardown]'), self) + self.timeout = Timeout(self.get_localized_setting_name('[Timeout]'), self) self.steps = [] if name == '...': self.report_invalid_syntax( @@ -655,12 +841,22 @@ def __init__(self, parent, name): ) _setters = {'Documentation': lambda s: s.doc.populate, + 'Arguments': lambda s: s.args.populate, # This is for UserKeyword + 'Return': lambda s: s.return_.populate, # This is for UserKeyword 'Template': lambda s: s.template.populate, 'Setup': lambda s: s.setup.populate, 'Teardown': lambda s: s.teardown.populate, 'Tags': lambda s: s.tags.populate, 'Timeout': lambda s: s.timeout.populate} + def get_localized_setting_name(self, english_name): + if not self._aliases: # DEBUG Or localization disabled + return english_name + for loc, en in self._aliases.items(): + if en == english_name.strip('[]'): + return f"[{loc}]" + return english_name + @property def source(self): return self.parent.source @@ -707,23 +903,24 @@ def settings(self): self.teardown] def __iter__(self): - for element in [self.doc, self.tags, self.setup, - self.template, self.timeout] \ - + self.steps + [self.teardown]: + for element in [self.doc, self.tags, self.setup, self.template, self.timeout] + self.steps + [self.teardown]: yield element class UserKeyword(TestCase): - def __init__(self, parent, name): + def __init__(self, parent, name, language=None): self.parent = parent self.name = name - self.doc = Documentation('[Documentation]', self) - self.args = Arguments('[Arguments]', self) - self.return_ = Return('[Return]', self) - self.timeout = Timeout('[Timeout]', self) - self.teardown = Fixture('[Teardown]', self) - self.tags = Tags('[Tags]', self) + self.language = language + self._aliases = lang.get_settings_for(language, ['Documentation', 'Arguments', 'Return', 'Timeout', + 'Teardown', 'Tags']) + self.doc = Documentation(self.get_localized_setting_name('[Documentation]'), self) + self.args = Arguments(self.get_localized_setting_name('[Arguments]'), self) + self.return_ = Return(self.get_localized_setting_name('[Return]'), self) + self.timeout = Timeout(self.get_localized_setting_name('[Timeout]'), self) + self.teardown = Fixture(self.get_localized_setting_name('[Teardown]'), self) + self.tags = Tags(self.get_localized_setting_name('[Tags]'), self) self.steps = [] if name == '...': self.report_invalid_syntax( @@ -731,6 +928,7 @@ def __init__(self, parent, name): "considered line continuation in Robot Framework 3.2.", level='WARN' ) + TestCase.__init__(self, parent, name, language) _setters = {'Documentation': lambda s: s.doc.populate, 'Arguments': lambda s: s.args.populate, @@ -739,6 +937,14 @@ def __init__(self, parent, name): 'Teardown': lambda s: s.teardown.populate, 'Tags': lambda s: s.tags.populate} + def get_localized_setting_name(self, english_name): + if not self._aliases: # DEBUG Or localization disabled + return english_name + for loc, en in self._aliases.items(): + if en == english_name.strip('[]'): + return f"[{loc}]" + return english_name + def _add_to_parent(self, test): self.parent.keywords.append(test) @@ -747,8 +953,7 @@ def settings(self): return [self.args, self.doc, self.tags, self.timeout, self.teardown, self.return_] def __iter__(self): - for element in [self.args, self.doc, self.tags, self.timeout] \ - + self.steps + [self.teardown, self.return_]: + for element in [self.args, self.doc, self.tags, self.timeout] + self.steps + [self.teardown, self.return_]: yield element diff --git a/src/robotide/lib/robot/parsing/populators.py b/src/robotide/lib/robot/parsing/populators.py index c8003eb28..451c830e1 100644 --- a/src/robotide/lib/robot/parsing/populators.py +++ b/src/robotide/lib/robot/parsing/populators.py @@ -15,6 +15,8 @@ import os +from multiprocessing import shared_memory +from robotide.lib.compat.parsing import language from robotide.lib.robot.errors import DataError from robotide.lib.robot.model import SuiteNamePatterns from robotide.lib.robot.output import LOGGER @@ -23,21 +25,30 @@ from .datarow import DataRow from .tablepopulators import (SettingTablePopulator, VariableTablePopulator, TestTablePopulator, KeywordTablePopulator, - NullPopulator) -from .htmlreader import HtmlReader + CommentsTablePopulator, NullPopulator) +# from .htmlreader import HtmlReader from .tsvreader import TsvReader from .robotreader import RobotReader from .restreader import RestReader -READERS = {'html': HtmlReader, 'htm': HtmlReader, 'xhtml': HtmlReader, - 'tsv': TsvReader, 'rst': RestReader, 'rest': RestReader, - 'txt': RobotReader, 'robot': RobotReader} +READERS = {'tsv': TsvReader, 'rst': RestReader, 'rest': RestReader, + 'txt': RobotReader, 'robot': RobotReader} # Removed 'html':HtmlReader, 'htm':HtmlReader, 'xhtml':HtmlReader, # Hook for external tools for altering ${CURDIR} processing PROCESS_CURDIR = True +def store_language(lang: list): + assert lang is not None + # Shared memory to store language definition + try: + sharemem = shared_memory.ShareableList(['en'], name="language") + except FileExistsError: # Other instance created file + sharemem = shared_memory.ShareableList(name="language") + sharemem[0] = lang[0] + + class NoTestsFound(DataError): pass @@ -49,14 +60,21 @@ class FromFilePopulator(object): 'test cases': TestTablePopulator, 'task': TestTablePopulator, 'tasks': TestTablePopulator, - 'keyword': KeywordTablePopulator} + 'keyword': KeywordTablePopulator, + 'comments': CommentsTablePopulator} - def __init__(self, datafile, tab_size=2): + def __init__(self, datafile, tab_size=2, lang=None): self._datafile = datafile self._populator = NullPopulator() self._curdir = self._get_curdir(datafile.directory) self._tab_size = tab_size - self._comment_table_names = ('comment', 'comments') + if datafile.source: + self._language = lang if lang else language.check_file_language(datafile.source) + else: + self._language = lang if lang else None + if self._language: + store_language(self._language) + # self._comment_table_names = language.get_headers_for(self._language, ('comment', 'comments')) @staticmethod def _get_curdir(path): @@ -72,7 +90,7 @@ def populate(self, path, resource=False): # print(f"DEBUG: populators populate path={path} READER={self._get_reader(path, resource)}") self._get_reader(path, resource).read(source, self) except Exception: - # print(f"DEBUG: populators populate CALLING DATAERROR") + # print("DEBUG: populators populate CALLING DATAERROR") raise DataError(get_error_message()) finally: source.close() @@ -93,18 +111,25 @@ def _get_reader(self, path, resource=False): if resource and file_format == 'resource': file_format = 'robot' try: - return READERS[file_format](self._tab_size) + return READERS[file_format](self._tab_size, self._language) except KeyError: raise DataError("Unsupported file format '%s'." % file_format) - def start_table(self, header): + def start_table(self, header, lineno: int, llang: list = None): + # DEBUG: # print(f"DEBUG: RFLib populators FromFilePopulator ENTER start_table header={header}") - if header[0].lower() in self._comment_table_names: # don't create a Comments section - # print(f"DEBUG: RFLib populators FromFilePopulator comments section header={header}") - return False + # if header[0].lower() in self._comment_table_names: # don't create a Comments section + # print(f"DEBUG: RFLib populators FromFilePopulator comments section header={header}") + # # return False self._populator.populate() - table = self._datafile.start_table(DataRow(header).all) - self._populator = self._populators[table.type](table) if table is not None else NullPopulator() + table = self._datafile.start_table(DataRow(header).all, lineno=lineno, llang=llang) + # print(f"DEBUG: populators start_table header={header} got table={table} at lineno={lineno}") + if header[0] in language.get_headers_for(llang, ('Comment', 'Comments'), lowercase=False): + self._populator = self._populators['comments'](table) + else: + self._populator = self._populators[table.type](table) if table is not None else NullPopulator() + # print(f"DEBUG: populators start_table AFTER _populators table.type={table.type} table={table}\n" + # f"self._populators={self._populators}") return bool(self._populator) def eof(self): @@ -113,12 +138,13 @@ def eof(self): return bool(self._datafile) def add(self, row): - # print(f"DEBUG: populators enter row={row}") + # print(f"DEBUG: populators enter add row={row}") if PROCESS_CURDIR and self._curdir: row = self._replace_curdirs_in(row) data = DataRow(row, self._datafile.source) if data: - # print(f"DEBUG: populators add data={data.cells} + {data.comments}") + # print(f"DEBUG: populators.py FromFilePopulator call _populator add data={data.cells} + {data.comments}\n" + # f"populator = {self._populator}") self._populator.add(data) def _replace_curdirs_in(self, row): @@ -131,30 +157,37 @@ class FromDirectoryPopulator(object): ignored_prefixes = ('_', '.') ignored_dirs = ('CVS',) + def __init__(self, tab_size=2, lang=None): + self.tab_size = tab_size + self.language = lang + def populate(self, path, datadir, include_suites=None, include_extensions=None, recurse=True, tab_size=2): LOGGER.info("Parsing directory '%s'." % path) + if not self.language: + self.language = language.check_file_language(path) + if self.language: + store_language(self.language) include_suites = self._get_include_suites(path, include_suites) - init_file, children = self._get_children(path, include_extensions, - include_suites) + init_file, children = self._get_children(path, include_extensions, include_suites) if init_file: self._populate_init_file(datadir, init_file, tab_size) if recurse: self._populate_children(datadir, children, include_extensions, include_suites) - @staticmethod - def _populate_init_file(datadir, init_file, tab_size): + def _populate_init_file(self, datadir, init_file, tab_size): datadir.initfile = init_file try: - FromFilePopulator(datadir, tab_size).populate(init_file) + if not self.language: + self.language = language.check_file_language(init_file) + FromFilePopulator(datadir, tab_size, self.language).populate(init_file) except DataError as err: LOGGER.error(err.message) - @staticmethod - def _populate_children(datadir, children, include_extensions, include_suites): + def _populate_children(self, datadir, children, include_extensions, include_suites): for child in children: try: - datadir.add_child(child, include_suites, include_extensions) + datadir.add_child(child, include_suites, include_extensions, language=self.language) except NoTestsFound: LOGGER.info("Data source '%s' has no tests or tasks." % child) except DataError as err: @@ -164,8 +197,7 @@ def _get_include_suites(self, path, incl_suites): if not incl_suites: return None if not isinstance(incl_suites, SuiteNamePatterns): - incl_suites = SuiteNamePatterns( - self._create_included_suites(incl_suites)) + incl_suites = SuiteNamePatterns(self._create_included_suites(incl_suites)) # If a directory is included, also all its children should be included. if self._is_in_included_suites(os.path.basename(path), incl_suites): return None diff --git a/src/robotide/lib/robot/parsing/restreader.py b/src/robotide/lib/robot/parsing/restreader.py index fa7f7cbe8..e44f20891 100644 --- a/src/robotide/lib/robot/parsing/restreader.py +++ b/src/robotide/lib/robot/parsing/restreader.py @@ -19,12 +19,15 @@ from .robotreader import RobotReader -def RestReader(): +def RestReader(language=None): from .restsupport import (publish_doctree, publish_from_doctree, RobotDataStorage) class RestReader(object): + def __init__(self, lang=language): + self.language = lang # May be used later, it is here for compatibility with RF 6.1, RIDE > 2.0.8.1 + def read(self, rstfile, rawdata): doctree = publish_doctree( rstfile.read(), source_path=rstfile.name, @@ -39,7 +42,7 @@ def read(self, rstfile, rawdata): def _read_text(self, data, rawdata, path): robotfile = BytesIO(data.encode('UTF-8')) - return RobotReader().read(robotfile, rawdata, path) + return RobotReader(language=self.language).read(robotfile, rawdata, path) def _read_html(self, doctree, rawdata, path): htmlfile = BytesIO() diff --git a/src/robotide/lib/robot/parsing/robotreader.py b/src/robotide/lib/robot/parsing/robotreader.py index adf5173fe..a9bae2325 100644 --- a/src/robotide/lib/robot/parsing/robotreader.py +++ b/src/robotide/lib/robot/parsing/robotreader.py @@ -15,6 +15,7 @@ import re +from robotide.lib.compat.parsing import language from robotide.lib.robot.output import LOGGER from robotide.lib.robot.utils import Utf8Reader, prepr @@ -23,7 +24,7 @@ class RobotReader(object): - def __init__(self, spaces=2): + def __init__(self, spaces=2, lang=None): self._spaces = spaces self._space_splitter = re.compile(r"[ \t\xa0]{2}|\t+") self._pipe_splitter = re.compile(r"[ \t\xa0]+\|(?=[ \t\xa0]+)") @@ -31,31 +32,33 @@ def __init__(self, spaces=2): self._pipe_ends = (' |', '\t|', u'\xa0|') self._separator_check = False self._cell_section = False + self.language = lang def read(self, file, populator, path=None): path = path or getattr(file, 'name', '') _ = path + # print(f"DEBUG: robotreader.read ENTER path={path}") process = table_start = preamble = False - # print(f"DEBUG: RFLib RobotReader start Reading file") + # print(f"DEBUG: RFLib RobotReader start Reading file language={self.language}") for lineno, line in enumerate(Utf8Reader(file).readlines(), start=1): if not self._separator_check: self.check_separator(line.rstrip()) cells = self.split_row(line.rstrip()) - if cells and cells[0].strip().startswith('*'): # For the cases of *** Comments *** - if cells[0].replace('*', '').strip().lower() in ('comment', 'comments'): - # print(f"DEBUG: robotreader.read detection of comments cells={cells}") - process = True if cells and cells[0].strip().startswith('*') and \ - populator.start_table([c.replace('*', '').strip() for c in cells]): + populator.start_table([c.replace('*', '').strip() for c in cells], lineno=lineno, + llang=self.language): process = table_start = True preamble = False # DEBUG removed condition "and not comments" comments = + # print(f"DEBUG: RobotReader After table_start head={cells[0].replace('*', '').strip()}") elif not table_start: # print(f"DEBUG: RFLib RobotReader Enter Preamble block, lineno={lineno} cells={cells}") if not preamble: preamble = True populator.add_preamble(line) elif process and not preamble: + # print(f"DEBUG: RFLib RobotReader before add cells={cells} populator is={populator}") populator.add(cells) + # print(f"DEBUG: RFLib RobotReader after add cells={cells}") return populator.eof() def sharp_strip(self, line): @@ -94,10 +97,15 @@ def _normalize_whitespace(string): return ' '.join(string.split()) def check_separator(self, line): - if line.startswith('*') and not self._cell_section: + # print(f"DEBUG: robotreader.check_separator ENTER line={line}") + if line.startswith('*'): # DEBUG: we want to recheck for new sections, was: and not self._cell_section: row = line.strip('*').strip().lower() - if row in ['keyword', 'keywords', 'test case', 'test cases', 'task', 'tasks', 'variable', 'variables']: + # print(f"DEBUG: robotreader.check_separator SECTION CHECK row={row} lang={self.language}") + if row in language.get_headers_for(self.language, ['keyword', 'keywords', 'test case', + 'test cases', 'task', 'tasks', 'variable', 'variables']): + # print(f"DEBUG: robotreader.check_separator detection of cell section row={row}") self._cell_section = True + self._separator_check = False if not line.startswith('*') and not line.startswith('#'): if not self._separator_check and line[:2] in self._pipe_starts: self._separator_check = True diff --git a/src/robotide/lib/robot/parsing/settings.py b/src/robotide/lib/robot/parsing/settings.py index 5258a0b34..ca00e3575 100644 --- a/src/robotide/lib/robot/parsing/settings.py +++ b/src/robotide/lib/robot/parsing/settings.py @@ -13,18 +13,30 @@ # See the License for the specific language governing permissions and # limitations under the License. -from robotide.lib.robot.utils import is_string, py2to3, unicode +from robotide.lib.robot.utils import is_string, unicode +from robotide.lib.compat.parsing import language as lang from .comments import Comment from ..version import ALIAS_MARKER -@py2to3 +def get_localized_setting(language: [], english_name: str): + if not language: + return english_name + settings = lang.get_settings_for(language, (english_name,)) + try: + result = list(settings.keys())[list(settings.values()).index(english_name)] + except ValueError: + return english_name + return result + + class Setting(object): def __init__(self, setting_name, parent=None, comment=None): self.setting_name = setting_name self.parent = parent + # print(f"DEBUG: settings.py Setting __init__ name= {self.setting_name}") self._set_initial_value() self._set_comment(comment) self._populated = False @@ -103,6 +115,7 @@ def __nonzero__(self): return self.is_set() def __iter__(self): + # print(f"DEBUG: settings.py Setting __iter__ name= {self.setting_name}") return iter(self.value or ()) def __unicode__(self): @@ -271,6 +284,8 @@ class Metadata(Setting): def __init__(self, parent, name, value, comment=None, joined=False): self.parent = parent + self.setting_name = get_localized_setting(parent.language, 'Metadata') + Setting.__init__(self, setting_name=self.setting_name, parent=parent, comment=comment) if value and isinstance(value, list): value = [x.strip() for x in value if x != ''] if not name.strip(): @@ -296,10 +311,12 @@ def _data_as_list(self): class ImportSetting(Setting): + setting_name = None def __init__(self, parent, name, args=None, alias=None, comment=None): self.parent = parent self.name = name.strip() + Setting.__init__(self, setting_name=self.setting_name, parent=parent, comment=comment) if args: self.args = [x.strip() for x in args if x != ''] else: @@ -319,7 +336,7 @@ def is_set(self): return True def _data_as_list(self): - return [self.type, self.name] + self.args + return [self.setting_name, self.name] + self.args def report_invalid_syntax(self, message, level='ERROR', parent=None): parent = parent or getattr(self, 'parent', None) @@ -341,6 +358,7 @@ def __init__(self, parent, name, args=None, alias=None, comment=None): name = args.pop(0) if args and not alias: args, alias = self._split_possible_alias(args) + self.setting_name = get_localized_setting(parent.language, 'Library') ImportSetting.__init__(self, parent, name, args, alias, comment) @staticmethod @@ -350,7 +368,7 @@ def _split_possible_alias(args): return args, None def _data_as_list(self): - data = ['Library', self.name] + self.args + data = [self.setting_name, self.name] + self.args if self.alias: data += [ALIAS_MARKER, self.alias] return data @@ -361,6 +379,10 @@ class Resource(ImportSetting): def __init__(self, parent, name, invalid_args=None, comment=None): if invalid_args: name += ' ' + ' '.join(invalid_args) + try: + self.setting_name = get_localized_setting(parent.language, 'Resource') + except AttributeError: # Unit tests were failing here + self.setting_name = get_localized_setting(None, 'Resource') ImportSetting.__init__(self, parent, name, comment=comment) @@ -372,6 +394,7 @@ def __init__(self, parent, name, args=None, comment=None): args = [x.strip() for x in args if x != ''] or [] if args and not name: name = args.pop(0) + self.setting_name = get_localized_setting(parent.language, 'Variables') ImportSetting.__init__(self, parent, name, args, comment=comment) diff --git a/src/robotide/lib/robot/parsing/tablepopulators.py b/src/robotide/lib/robot/parsing/tablepopulators.py index 634ff4418..44f5d6286 100644 --- a/src/robotide/lib/robot/parsing/tablepopulators.py +++ b/src/robotide/lib/robot/parsing/tablepopulators.py @@ -15,8 +15,6 @@ import re -from robotide.lib.robot.utils import py2to3 - from .comments import Comments from .settings import Documentation, MetadataList @@ -31,7 +29,6 @@ def populate(self): raise NotImplementedError -@py2to3 class NullPopulator(Populator): def add(self, row): @@ -65,7 +62,11 @@ def _add(self, row): self._populator.add(row) def _is_continuing(self, row): - return row.is_continuing() and self._populator + try: + continuing = row.is_continuing() + return continuing and self._populator + except AttributeError: + return False def _get_populator(self, row): raise NotImplementedError @@ -79,8 +80,9 @@ class SettingTablePopulator(_TablePopulator): def _get_populator(self, row): setter = self._table.get_setter(row.head) if row.head else None + # print(f"DEBUG: tablepopulators.py SettingTablePopulator enter _get_populator {row.head=}") if row.head == '...': - setter = self._table.get_setter('Documentation') + setter = self._table.get_setter(self._table.get_localized_setting_name('Documentation')) if not setter: return NullPopulator() if isinstance(setter.__self__, Documentation): @@ -90,6 +92,13 @@ def _get_populator(self, row): return SettingPopulator(setter) +class CommentsTablePopulator(_TablePopulator): + + def _get_populator(self, table): + # print(f"DEBUG: CommentsTablePopulator enter _get_populator self._table={self._table} table={table}") + return CommentsPopulator(self._table.add) + + class VariableTablePopulator(_TablePopulator): def _get_populator(self, row): @@ -341,6 +350,28 @@ def _joiner_based_on_eol_escapes(self): return None +class CommentsPopulator(_PropertyPopulator): + _item_type = 'comments' + + def __init__(self, setter): + _PropertyPopulator.__init__(self, setter) + self._value = [] + # print(f"DEBUG: tablepopulators CommentPopulator __init__ setter={setter}") + + def _add(self, row): + # print(f"DEBUG: tablepopulators CommentPopulator _add {row=}") + if isinstance(row, list): + self._setter(row) + else: + content = row.data+[' ']+row.comments if row.comments else row.data + self._setter(content) + + def populate(self): + if self._value: + print(f"DEBUG: tablepopulators CommentPopulator populate {self._value=}") + self._setter(self._value) + + class MetadataPopulator(DocumentationPopulator): def __init__(self, setter): diff --git a/src/robotide/lib/robot/utils/__init__.py b/src/robotide/lib/robot/utils/__init__.py index 553139f3a..83a5f5777 100644 --- a/src/robotide/lib/robot/utils/__init__.py +++ b/src/robotide/lib/robot/utils/__init__.py @@ -51,7 +51,7 @@ from .match import eq, Matcher, MultiMatcher from .misc import (plural_or_not, printable_name, roundup, seq2str, seq2str2) -from .normalizing import lower, normalize, _normalize, NormalizedDict +from .normalizing import lower, normalize, normalized_headers, _normalize, NormalizedDict from .platform import (IRONPYTHON, JAVA_VERSION, JYTHON, PY_VERSION, PY2, PY3, PYPY, UNIXY, WINDOWS, RERAISED_EXCEPTIONS) from .recommendations import RecommendationFinder diff --git a/src/robotide/lib/robot/utils/normalizing.py b/src/robotide/lib/robot/utils/normalizing.py index 584cdb2a3..6040deaee 100644 --- a/src/robotide/lib/robot/utils/normalizing.py +++ b/src/robotide/lib/robot/utils/normalizing.py @@ -53,6 +53,18 @@ def _normalize(name, ignore=(), caseless=True, spaceless=True): return normalize(name, ignore, caseless, spaceless) +def normalized_headers(table: dict): + """ + Returns the normalized keys from the dictionary + :param table: the dict to get headers normalized + :return: normalized keys from table + """ + result = [] + for name in table.keys(): + result.append(normalize(name)) + return result + + class NormalizedDict(MutableMapping): """Custom dictionary implementation automatically normalizing keys.""" diff --git a/src/robotide/lib/robot/writer/dataextractor.py b/src/robotide/lib/robot/writer/dataextractor.py index 0f12f740d..2ba97aac2 100644 --- a/src/robotide/lib/robot/writer/dataextractor.py +++ b/src/robotide/lib/robot/writer/dataextractor.py @@ -22,7 +22,7 @@ def __init__(self, want_name_on_first_row=None): (lambda t,n: False) def rows_from_table(self, table): - if table.type in ['setting', 'variable']: + if table.type in ['setting', 'variable', 'comments']: return self._rows_from_item(table) return self._rows_from_indented_table(table) @@ -53,6 +53,9 @@ def _rows_from_item(self, item, indent=0): for row in self._rows_from_item(child, indent+1): yield row # DEBUG Must be explicit yield ['', 'END'] + if hasattr(child, 'is_comments'): + for row in child: + yield row def _last(self, items, index): return index >= len(items) -1 diff --git a/src/robotide/lib/robot/writer/datafilewriter.py b/src/robotide/lib/robot/writer/datafilewriter.py index e39f0bbcb..33500c28d 100644 --- a/src/robotide/lib/robot/writer/datafilewriter.py +++ b/src/robotide/lib/robot/writer/datafilewriter.py @@ -45,22 +45,22 @@ def write(self, datafile): class WritingContext(object): """Contains configuration used in writing a test data file to disk.""" txt_format = 'txt' - html_format = 'html' + # html_format = 'html' tsv_format = 'tsv' robot_format = 'robot' txt_column_count = 18 html_column_count = 5 tsv_column_count = 8 - _formats = [txt_format, html_format, tsv_format, robot_format] + _formats = [txt_format, tsv_format, robot_format] # Removed html_format - def __init__(self, datafile, format='', output=None, pipe_separated=False, - txt_separating_spaces=4, line_separator='\n'): + def __init__(self, datafile, fformat='', output=None, pipe_separated=False, + txt_separating_spaces=4, line_separator='\n', language=None): """ :param datafile: The datafile to be written. :type datafile: :py:class:`~robot.parsing.model.TestCaseFile`, :py:class:`~robot.parsing.model.ResourceFile`, :py:class:`~robot.parsing.model.TestDataDirectory` - :param str format: Output file format. If omitted, read from the + :param str fformat: Output file format. If omitted, read from the extension of the `source` attribute of the given `datafile`. :param output: An open, file-like object used in writing. If omitted, value of `source` attribute of the given `datafile` is @@ -87,9 +87,10 @@ def __init__(self, datafile, format='', output=None, pipe_separated=False, self.pipe_separated = pipe_separated self.line_separator = line_separator self._given_output = output - self.format = self._validate_format(format) or self._format_from_file() + self.format = self._validate_format(fformat) or self._format_from_file() self.txt_separating_spaces = txt_separating_spaces self.output = output + self.language = language def __enter__(self): if not self.output: @@ -104,16 +105,17 @@ def __exit__(self, *exc_info): if self._given_output is None: self.output.close() - def _validate_format(self, format): - format = format.lower() if format else '' - if format and format not in self._formats: - raise DataError('Invalid format: %s' % format) - return format + def _validate_format(self, vformat): + vformat = vformat.lower() if vformat else '' + if vformat and vformat not in self._formats: + raise DataError('Invalid format: %s' % vformat) + return vformat def _format_from_file(self): return self._format_from_extension(self._source_from_file()) - def _format_from_extension(self, path): + @staticmethod + def _format_from_extension(path): return os.path.splitext(path)[1][1:].lower() def _output_path(self): diff --git a/src/robotide/lib/robot/writer/filewriters.py b/src/robotide/lib/robot/writer/filewriters.py index 130f826e6..a7ac93149 100644 --- a/src/robotide/lib/robot/writer/filewriters.py +++ b/src/robotide/lib/robot/writer/filewriters.py @@ -26,6 +26,15 @@ from .htmltemplate import TEMPLATE_START, TEMPLATE_END +def table_sorter(tables: list) -> list: + sorted_tables = [] + for idx, tab in enumerate(tables): + sorted_tables.append((tab._lineno, tab)) + sorted_result = sorted(sorted_tables) + sorted_tables = [z[1] for z in sorted_result] + return sorted_tables + + def FileWriter(context): """Creates and returns a ``FileWriter`` object. @@ -33,8 +42,8 @@ def FileWriter(context): on ``context.format``. ``context`` is also passed to created writer. :type context: :class:`~robot.writer.datafilewriter.WritingContext` """ - if context.format == context.html_format: - return HtmlFileWriter(context) + # if context.format == context.html_format: + # return HtmlFileWriter(context) if context.format == context.tsv_format: return TsvFileWriter(context) if context.pipe_separated: @@ -47,21 +56,28 @@ class _DataFileWriter(object): def __init__(self, formatter, configuration): self._formatter = formatter self._output = configuration.output + self.language = configuration.language def write(self, datafile): tables = [table for table in datafile if table] if datafile.has_preamble: self._write_preamble(datafile.preamble) - for table in tables: - self._write_table(table, is_last=table is tables[-1]) + sorted_tables = table_sorter(tables) + for table in sorted_tables: + self._write_table(table, is_last=table is sorted_tables[-1]) def _write_table(self, table, is_last): self._write_header(table) - self._write_rows(self._formatter.format_table(table)) + if table.type == 'comments': + # print(f"DEBUG: filewriters.py _write_table COMMENTS: {table}") + if table.is_started(): + self._write_lines(table.section_comments) + else: + self._write_rows(self._formatter.format_table(table)) if not is_last: # DEBUG: make this configurable # print(f"DEBUG: lib.robot.writer _DataFileWritter write_table empty_row table={table.type}") try: - if table.type == 'variable' and len(list(table)[-1].as_list()) == 0: + if table.type == 'comments' or table.type == 'variable' and len(list(table)[-1].as_list()) == 0: # DEBUG: This is workaround for newline being added ALWAYS to VariableTable return except IndexError: @@ -85,11 +101,15 @@ def _write_preamble(self, rows): for line in rows: self._output.write(line) + def _write_lines(self, rows): + for line in rows: + self._output.write(f"{line}\n") + class SpaceSeparatedTxtWriter(_DataFileWriter): def __init__(self, configuration): - formatter = TxtFormatter(configuration.txt_column_count) + formatter = TxtFormatter(configuration.txt_column_count, configuration.language) self._separator = ' ' * configuration.txt_separating_spaces _DataFileWriter.__init__(self, formatter, configuration) diff --git a/src/robotide/lib/robot/writer/formatters.py b/src/robotide/lib/robot/writer/formatters.py index d1999d738..ad32644a3 100644 --- a/src/robotide/lib/robot/writer/formatters.py +++ b/src/robotide/lib/robot/writer/formatters.py @@ -23,9 +23,12 @@ class _DataFileFormatter(object): _whitespace = re.compile(r"\s{2,}") _split_multiline_doc = True + language = None - def __init__(self, column_count): - self._splitter = RowSplitter(column_count, self._split_multiline_doc) + def __init__(self, column_count, language=None): + if language: + self.language = language + self._splitter = RowSplitter(column_count, self._split_multiline_doc, self.language) self._column_count = column_count self._extractor = DataExtractor(self._want_names_on_first_content_row) @@ -41,7 +44,10 @@ def format_header(self, table): def format_table(self, table): rows = self._extractor.rows_from_table(table) - if self._should_split_rows(table): + if rows and table.type == 'comments': + # print(f"DEBUG: formatters.py format_table rows={[r for r in rows]}") + return [r for r in rows] + if self._should_split_rows(table) and table.type != 'comments': rows = self._split_rows(rows, table) return (self._format_row(r, table) for r in rows) @@ -104,18 +110,25 @@ class TxtFormatter(_DataFileFormatter): _setting_and_variable_name_width = 14 def _format_row(self, row, table=None): + # print(f"DEBUG: formatters.py format_row ENTER row={row}") + if table and table.type == 'comments': + return row + # Unit tests failing here with row[0]==None + if row and row[0] is None: + row[0] = ' ' row = self._escape(row) aligner = self._aligner_for(table) return aligner.align_row(row) def _aligner_for(self, table): - if table and table.type in ['setting', 'variable']: + if table and table.type in ['setting', 'variable', 'comments']: return FirstColumnAligner(self._setting_and_variable_name_width) if self._should_align_columns(table): return ColumnAligner(self._test_or_keyword_name_width, table) return NullAligner() def _format_header(self, header, table): + # print(f"DEBUG: RFLib writer formmaters.py TxtFormatter _format_header headers={header}") header = ['*** %s ***' % header[0]] + header[1:] aligner = self._aligner_for(table) return aligner.align_row(header) diff --git a/src/robotide/lib/robot/writer/rowsplitter.py b/src/robotide/lib/robot/writer/rowsplitter.py index d356d65ba..ea19f1cbb 100644 --- a/src/robotide/lib/robot/writer/rowsplitter.py +++ b/src/robotide/lib/robot/writer/rowsplitter.py @@ -14,6 +14,7 @@ # limitations under the License. import itertools +from robotide.lib.compat.parsing.language import get_settings_for class RowSplitter(object): @@ -24,9 +25,10 @@ class RowSplitter(object): _indented_tables = ('test case', 'keyword') _split_from = ('ELSE', 'ELSE IF', 'AND') - def __init__(self, cols=8, split_multiline_doc=False): + def __init__(self, cols=8, split_multiline_doc=False, language=None): self._cols = cols self._split_multiline_doc = split_multiline_doc + self._language = language def split(self, row, table_type): if not row: @@ -50,9 +52,9 @@ def _get_first_non_empty_index(self, row, indented=False): def _is_doc_row(self, row, table_type): if table_type == self.setting_table: - return len(row) > 1 and row[0] == 'Documentation' + return len(row) > 1 and row[0] in get_settings_for(self._language, ('Documentation',)) if table_type in self._indented_tables: - return len(row) > 2 and row[1] == '[Documentation]' + return len(row) > 2 and row[1].strip('[]') in get_settings_for(self._language, ('Documentation',)) return False def _split_doc_row(self, row, indent): diff --git a/src/robotide/locale/RIDE.pot b/src/robotide/locale/RIDE.pot new file mode 100644 index 000000000..b3c3eeb89 --- /dev/null +++ b/src/robotide/locale/RIDE.pot @@ -0,0 +1,1491 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2024-01-01 19:29+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" + + +#: /home/helio/github/RIDE/src/robotide/application/application.py:383 +msgid "Found Robot Framework version %s from %s." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "New development version is available." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "Upgrade?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid " with:" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid "You may install version " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:113 +msgid "Click OK to Upgrade now!" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:114 +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:221 +msgid "After upgrade you will see another dialog informing to close this RIDE instance." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "No Upgrade Available" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "You have the latest version of RIDE." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:122 +msgid " Have a nice day :)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "An error occurred when installing new version" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "Failed to Upgrade" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "Completed Upgrade" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "You should close this RIDE (Process ID = " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:208 +msgid "Update available" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid " available from " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid "New version " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:217 +msgid "See this version " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:219 +msgid "You can update with the command:" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:220 +msgid "Or, click Upgrade Now" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:222 +msgid "See the latest development " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:227 +msgid "" +"I'm using another method for RIDE updates\n" +" and do not need automatic update checks" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:232 +msgid "remind me later" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:238 +msgid "Upgrade Now" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:424 +msgid "Log options" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:432 +msgid "Output directory: " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:450 +msgid "Add suite name to log names" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:453 +msgid "Add timestamp to log names" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:456 +msgid "Save Console and Message logs" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:474 +msgid "Select Logs Directory" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:496 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Arguments" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:508 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:534 +msgid "Arguments for the test run. Arguments are space separated list." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:553 +msgid "Does not execute - help or version option given" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:558 +msgid "Unknown option(s):" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:564 +msgid "Tests filters" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:573 +msgid "Only run tests with these tags:" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:576 +msgid "Skip tests with these tags:" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:672 +msgid "Script to run tests:" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:98 +msgid "Stop a running test" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:99 +msgid "Step over" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:161 +msgid "A plugin for running tests from within RIDE" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +msgid "Run Tests" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:54 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:41 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:36 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:53 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:738 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:750 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +msgid "Tools" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:258 +msgid "Run the selected tests" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +msgid "Run Tests with Debug" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:263 +msgid "Run the selected tests with Debug" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +msgid "Stop Test Run" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:350 +msgid "" +"[ SENDING STOP SIGNAL ]\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:357 +msgid "" +"[ SENDING PAUSE SIGNAL ]\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:363 +msgid "" +"[ SENDING CONTINUE SIGNAL ]\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:369 +msgid "" +"[ SENDING STEP NEXT SIGNAL ]\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:375 +msgid "" +"[ SENDING STEP OVER SIGNAL ]\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:408 +msgid "" +"command: %s\n" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:478 +msgid "" +"There are unsaved modifications.\n" +" Do you want to save all changes and run the tests?" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:480 +msgid "Unsaved Modifications" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:489 +msgid "" +"No tests selected. \n" +"Continue anyway?" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:491 +msgid "No tests selected" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:548 +msgid "" +"\n" +"Test finished {}" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:583 +msgid "Messages log exceeded 80% of process memory, stopping for now..." +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:741 +msgid "Start" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:742 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:745 +msgid "Start robot" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:743 +msgid "Start running the robot test suite" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:744 +msgid "Debug" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:746 +msgid "Start running the robot test suite with DEBUG loglevel" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:748 +msgid "Stop" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:752 +msgid "Pause" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:754 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:755 +msgid "Pause test execution" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:756 +msgid "Continue" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:759 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:760 +msgid "Continue test execution" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:761 +msgid "Next" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:762 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:763 +msgid "Step next" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:788 +msgid "Execution Profile: " +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:791 +msgid "Choose which method to use for running the tests" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:798 +msgid "Open Logs Directory" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:800 +msgid "View All Logs in Explorer" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:801 +msgid " Report" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:802 +msgid "View Robot Report in Browser (CtrlCmd-R)" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:804 +msgid " Log" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:805 +msgid "View Robot Log in Browser (CtrlCmd-L)" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:812 +msgid " Autosave " +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:813 +msgid "Automatically save all changes before running" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:818 +msgid " Pause after failure " +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:819 +msgid "Automatically pause after failing keyword" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:927 +msgid "Console log" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:931 +msgid "Message log" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1054 +msgid "Starting test:" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1059 +msgid "Ending test:" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1067 +msgid "UNKNOWN STATUS:" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1095 +msgid "<< PAUSED >>" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1100 +msgid "<< CONTINUE >>" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1141 +msgid "" +"There isn't logs directory. \n" +"Please, run the tests and try again" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1143 +msgid "No logs directory" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1228 +msgid "elapsed time: %s pass: %s skip: %s fail: %s" +msgstr "" + +#: +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1248 +msgid " current keyword: " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:41 +msgid "" +"[Navigate]\n" +" !Go &Back | Go back to previous location in tree | Alt-%s | ART_GO_BACK\n" +" !Go &Forward | Go forward to next location in tree | Alt-%s | ART_GO_FORWARD\n" +" " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +msgid "Add Tag to selected" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:108 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Edit" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +msgid "Clear Selected" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Add Tag To Selected" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Enter Tag Name" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/__init__.py:28 +msgid "" +"[Edit]\n" +"&Undo | Undo last modification | Ctrlcmd-Z\n" +"&Redo | Redo modification | Ctrlcmd-Y\n" +"---\n" +"Cu&t | Cut | Ctrlcmd-X\n" +"&Copy | Copy | Ctrlcmd-C\n" +"&Paste | Paste | Ctrlcmd-V\n" +"&Insert | Insert | Shift-Ctrl-V\n" +"&Delete | Delete | Del\n" +"---\n" +"Comment Rows | Comment selected rows | Ctrlcmd-3\n" +"Comment Cells | Comment cells with # | Ctrlcmd-Shift-3\n" +"Uncomment Rows | Uncomment selected rows | Ctrlcmd-4\n" +"Uncomment Cells | Uncomment cells with # | Ctrlcmd-Shift-4\n" +"---\n" +"Insert Cells | Insert Cells | Ctrlcmd-Shift-I\n" +"Delete Cells | Delete Cells | Ctrlcmd-Shift-D\n" +"Insert Rows | Insert Rows | Ctrlcmd-I\n" +"Delete Rows | Delete Rows | Ctrlcmd-D\n" +"Move Rows Up | Move Rows Up | Alt-Up\n" +"Move Rows Down | Move Rows Down | Alt-Down\n" +"[Tools]\n" +"Content Assistance (Ctrl-Space or Ctrl-Alt-Space) | Show possible keyword and variable completions | | | POSITION-70\n" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:162 +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:394 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:186 +msgid " (READ ONLY)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:224 +msgid "Settings" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:343 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Source" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:381 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:207 +msgid "Find Usages" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:71 +msgid "Create Keyword" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:72 +msgid "Extract Keyword" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:73 +msgid "Extract Variable" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:74 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "Rename Keyword" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:75 +msgid "Find Where Used" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:76 +msgid "JSON Editor\tCtrl-Shift-J" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:78 +msgid "Go to Definition\tCtrl-B" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:80 +msgid "Undo\tCtrl-Z" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:81 +msgid "Redo\tCtrl-Y" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:83 +msgid "Make Variable\tCtrl-1" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:84 +msgid "Make List Variable\tCtrl-2" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:85 +msgid "Make Dict Variable\tCtrl-5" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:87 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:292 +msgid "Comment Cells\tCtrl-Shift-3" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:88 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:293 +msgid "Uncomment Cells\tCtrl-Shift-4" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:89 +msgid "Move Cursor Down\tAlt-Enter" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:91 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:284 +msgid "Comment Rows\tCtrl-3" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:92 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:285 +msgid "Uncomment Rows\tCtrl-4" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:93 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:286 +msgid "Move Rows Up\tAlt-Up" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:94 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:287 +msgid "Move Rows Down\tAlt-Down" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:95 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:288 +msgid "Swap Row Up\tCtrl-T" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:289 +msgid "Insert Rows\tCtrl-I" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:290 +msgid "Delete Rows\tCtrl-D" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:747 +msgid "" +"Keyword was not detected by RIDE\n" +"
    Possible corrections:
    \n" +"
      \n" +"
    • Import library or resource file containing the keyword.
    • \n" +"
    • For library import errors: Consider importing library spec XML\n" +" (Tools / Import Library Spec XML or by adding the XML file with the\n" +" correct name to PYTHONPATH) to enable keyword completion\n" +" for example for Java libraries.\n" +" Library spec XML can be created using libdoc tool from Robot Framework.
    • \n" +"
    " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "New name" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:995 +msgid "Save" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:996 +msgid "Cancel" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Error in JSON:" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Save anyway?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1023 +msgid "Validation Error!" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1164 +msgid "Please select what you want to check for usage" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1167 +msgid "Complete cell content" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1168 +msgid "Variable " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1180 +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:513 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:49 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:127 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:184 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:98 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:160 +msgid "Search" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Down\tCtrl-Down" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Up\tCtrl-Up" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Delete" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:100 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Clear" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +msgid "Variable" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Comment" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Value" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Dict" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add List" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Scalar" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Import" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Name / Path" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Import Failed Help" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Library" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Resource" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:654 +msgid "Variables" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:586 +msgid "Add Import" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:620 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:33 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:62 +msgid "Import Library Spec XML" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "" +"
    Possible corrections and notes:
    \n" +"
      \n" +"
    • Import failure is shown with red color.
    • \n" +"
    • See Tools / View RIDE Log for detailed information about the failure.
    • \n" +"
    • If the import contains a variable that RIDE has not initialized, consider adding the variable\n" +" to variable table with a default value.
    • \n" +"
    • For library import failure: Consider importing library spec XML (Tools / Import Library Spec XML or by\n" +" adding the XML file with the correct name to PYTHONPATH) to enable keyword completion\n" +" for example for Java libraries.\n" +" Library spec XML can be created using libdoc tool from Robot Framework.\n" +" For more information see \n" +" wiki.\n" +"
    • \n" +"
    " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "Import failure handling" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Metadata" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:708 +msgid "Add Metadata" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "ERROR: Data sanity check failed!" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "Error at line" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:368 +msgid "Reset changes?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:369 +msgid "Can not apply changes from Text Editor" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:497 +msgid "Apply Changes" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:523 +msgid "Syntax colorization disabled due to missing requirements." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:524 +msgid "Get help" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:539 +msgid "" +"

    Syntax colorization

    \n" +"

    \n" +" Syntax colorization for Text Edit uses Pygments syntax highlighter.\n" +"

    \n" +"

    \n" +" Install Pygments from command line with:\n" +"

    \n"
    +"            pip install pygments\n"
    +"        
    \n" +" Or:\n" +"
    \n"
    +"            easy_install pygments\n"
    +"        
    \n" +" Then, restart RIDE.\n" +"

    \n" +"

    \n" +" If you do not have pip or easy_install,\n" +" follow these instructions.\n" +"

    \n" +"

    \n" +" For more information about installing Pygments, see the site.\n" +"

    \n" +" " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:564 +msgid "Getting syntax colorization" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:662 +msgid "No matches found." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/log/log.py:45 +msgid "RIDE Log" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +msgid "View RIDE Log" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:45 +msgid "Parser Log" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +msgid "View Parser Log" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:55 +msgid "Create RIDE Desktop Shortcut" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:27 +msgid "" +"The specified command string will be split from whitespaces into a command\n" +"and its arguments. If either the command or any of the arguments require\n" +"internal spaces, they must be written as ''.\n" +"\n" +"The command will be executed in the system directly without opening a shell.\n" +"This means that shell commands and extensions are not available. For example,\n" +"in Windows batch files to execute must contain the '.bat' extension and 'dir'\n" +"command does not work.\n" +"\n" +"Examples:\n" +" robot.bat --include smoke C:\\my_tests\n" +" svn update /home/robot\n" +" C:\\ProgramFiles\\App\\prg.exe argumentwithspace,\n" +"Run configurations are stored in the RIDE settings file.\n" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:45 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +msgid "Manage Run Configurations" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "New" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "Remove" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Command" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Documentation" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Name" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:30 +msgid "" +"A plugin for executing commands on the system.\n" +"\n" +" This plugin enables creation of persistent run configurations and\n" +" execution of those. Output of the executed command is displayed in a\n" +" separate tab." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:56 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:63 +msgid "Macros" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:36 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:35 +msgid "Search Tests" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:50 +msgid "Tag Search" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Tags" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Test" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:66 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:90 +msgid "Results: " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:82 +msgid "Find matches using tag patterns. See RF User Guide or 'robot --help' for more information." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:96 +msgid "Include" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:139 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:189 +msgid "Add all to selected" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:156 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:164 +msgid "Results: %d" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:177 +msgid "Info. " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:195 +msgid "Find matches by test name, documentation and/or tag." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:212 +msgid "Search term" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:33 +msgid "A plugin for searching tests based on name, tags and documentation" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:60 +msgid "Library Spec XML|*.xml|All Files|*.*" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:77 +msgid "" +"Library \"%s\" imported\n" +"from \"%s\"\n" +"This may require RIDE restart." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:78 +msgid "Info" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Could not import library from file \"%s\"" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Import failed" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Help" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:140 +msgid "File" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:65 +msgid "&Help" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:35 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:36 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:37 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:39 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:59 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:139 +msgid "Search Keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:43 +msgid "A plugin for searching keywords based on name or documentation." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:55 +msgid "Search keywords from libraries and resources" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:173 +msgid "Search term: " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:180 +msgid "Search documentation" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:185 +msgid "Source: " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Description" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:56 +msgid "" +"[File]\n" +" !&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW\n" +" ---\n" +" !&Open Test Suite | Open file containing tests | Ctrlcmd-O | ART_FILE_OPEN\n" +" !Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O | ART_FOLDER_OPEN\n" +" !Open External File | Open file in Code Editor | | ART_NORMAL_FILE\n" +" ---\n" +" !&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE\n" +" !Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS\n" +" ---\n" +" !E&xit | Exit RIDE | Ctrlcmd-Q\n" +"\n" +" [Tools]\n" +" !Search Unused Keywords | | | | POSITION-54\n" +" !Manage Plugins | | | | POSITION-81\n" +" !View All Tags | | F7 | | POSITION-82\n" +" !Preferences | | | | POSITION-99\n" +"\n" +" [Help]\n" +" !Shortcut keys | RIDE shortcut keys\n" +" !User Guide | Robot Framework User Guide\n" +" !Wiki | RIDE User Guide (Wiki)\n" +" !Report a Problem | Open browser to SEARCH on the RIDE issue tracker\n" +" !Release notes | Shows release notes\n" +" !About | Information about RIDE\n" +" !Check for Upgrade | Looks at PyPi for new released version\n" +" " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:167 +msgid "Saved %s" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:168 +msgid "Saved all files" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:191 +msgid "Validation Error" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:195 +msgid "\"%s\" is read only" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:196 +msgid "Modification prevented" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:246 +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:105 +msgid "Test Suites" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:349 +msgid "" +"There are unsaved modifications.\n" +"Do you want to save your changes before exiting?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:351 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:439 +msgid "Warning" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:438 +msgid "" +"There are unsaved modifications.\n" +"Do you want to proceed without saving?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:473 +msgid "Choose a directory containing Robot files" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:538 +msgid "RIDE - Preferences" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:625 +msgid "Workspace modifications detected on the file system." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:626 +msgid "Do you want to reload the workspace?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:627 +msgid "Answering will ignore the changes on disk." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:629 +msgid "Answering will discard unsaved changes." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:630 +msgid "Files Changed On Disk" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "search unused keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "stop test run" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "preview" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "view ride log" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:793 +msgid "Shortcut keys for RIDE" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:831 +msgid "Show" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:832 +msgid "Hide" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:833 +msgid "Close" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:99 +msgid "Preview" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:42 +msgid "Show preview of the current file" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Pipes)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Spaces)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:116 +msgid "Format" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:122 +msgid "Print" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:39 +msgid "Search unused keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:66 +msgid "" +"This dialog helps you finding unused keywords within your opened project.\n" +"If you want, you can restrict the search to a set of files with the filter." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:70 +msgid "Filter is" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:71 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:242 +msgid "inactive" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:84 +msgid "Filter" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:91 +msgid "Use RegEx" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:93 +msgid "" +"Here you can define one or more strings separated by comma (e.g. common,abc,123).\n" +"The filter matches if at least one string is part of the filename.\n" +"If you don't enter any strings, all opened files are included" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:101 +msgid "Test case files" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:104 +msgid "Resource files" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:106 +msgid "Mode" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "exclude" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "include" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:109 +msgid "Test the filter" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:139 +msgid "Keyword" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:146 +msgid "Delete marked keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:157 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:320 +msgid "Unused Keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:163 +msgid "Abort" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:247 +msgid "active" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:270 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:353 +msgid "Unused Keywords (%d)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:279 +msgid "(None)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:282 +msgid "" +"Keywords of the following files will be included in the search:\n" +"\n" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:283 +msgid "Included files" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:339 +msgid "Searching.%s \t- %s" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:354 +msgid "Search finished - Found %d Unused Keywords" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:402 +msgid "listing datafiles" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:405 +msgid "searching from " +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:37 +msgid "View all tags" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:76 +msgid "Tag" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:77 +msgid "Occurrences" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:82 +msgid "The List" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:85 +msgid "Refresh" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:86 +msgid "Included Tag Search" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:89 +msgid "Excluded Tag Search" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:147 +msgid "" +"Total tests %d, Tests with tags %d, Unique tags %d\n" +"Currently selected tests %d" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Select all" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:262 +msgid "Rename" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests with this tag" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests without this tag" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:261 +msgid "Renaming tag '%s'." +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Confirm" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Delete a tag '%s' ?" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:209 +msgid "External Resources" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:344 +msgid "%s (excluded)" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:58 +msgid "'%s' - %d matches found - Searching%s" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:63 +msgid "'%s' - %d matches" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:85 +msgid "Go to definition" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Location" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Usage" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported Location" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported name" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Location" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Name" +msgstr "" + diff --git a/src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.mo b/src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.mo new file mode 100644 index 0000000000000000000000000000000000000000..56bb8a322b14f20628dcafa36c1e25f286f43e36 GIT binary patch literal 33087 zcmdU&37A|*b?-06>)3#8Y;3?LSGFaMY)#9OWo(aS*`t|}HMTr6o*7AAQSMCNnQ5!L zZ_~G1(h%{0gghWDaq>b!c1%bdn>Zu^lRyG5@ni*(kOe|Evt%QLyqJ)X1@g%6f2!*C zGLmfP``-8EHLm&Js#{gJmUB*>bE--_dH$(ykN92noG7{kY@HQF-^KIC&sS;`o%`Y_ zIuAS#jKRyn7lC8oIpDqE+2AsGD%kSxSHLrfKM0-+z5_fRd<0Z|KLb7wd;&ZT{De>c z6u6%F7eM8I{!0kpg&wc)cnzp_>;hj1-VMI&lqjl!-|^fidJCPo6uc;=Zr)#eIei3A zzaolm2DgE`z$U2gKMtM={vtR4{;H4vE~tKf+Nb{&sP8@H@l+bC_{HE$!Ob8|i1vWf z-~u=Vz8~BIe%{B=-{AUx3&@a1lc3sl7q|(02dHvSfczId#h=r`i#EFVF9WY8z6U%F zTmdysuK_Ox-{#XF2G1b=n8(LKweQ2A+Wl$pLhwtV=H-+@m;VA#^Lh!WaTx?J0=I%> zjV3^S@0FnF=Cz>O@x!3n`D36+=uuGL`7o&Y`aMwfegR|%qkjTL2QQ}68lR0|3?2Ya z1&@Ln|5t$I{ z0nY+&1=YSgL6yHB+z!^jd%$;r%KslARY#{nRA+)EQ1gEssBxMF_1#0DzIP1#eeiWY zKDRlFW{BSns=l8B)$aF!D*x-C`uU`fe+kt5oC;AW{Y9YiUjb@dt^>~icY_*_NuNFs z>ie$*^_|0@%DoyCojeGt+#}!!__IF#6>ul+0KW{1?!OMI{!_L>3*bed%?GIWlc37Yfok6psP8mDbRd5Bd1}L6u+i@sER|+dlTS1loZcy!d*r)$AsQe%C?>`2@3ejglNHjY88rP2N!RHYl1NHqG@O-cWicgM# z=Yj74#fOiA8qZ$_)sByWD))z=+WU843_b;lZqM89>bcV6Zcy!<2UWfTYTTQk#^oq@ z8Tfke<>14h#`j534FJ_KqUf78c5398+H0sa8%*q6Y2iNE1`WRlXsJHe~?_$}b8!MB4S z0oBjBVdfIN2h{f-0Y$GL1TO?X1*$z?0@aUy_33Bd=<=@vFCcv@crLgbRQ>~?+O+`c zJ5^BQ)dZFA7^r!CtAGEapz{AXsC+-|@fW}s6Mq6!`~D2nID83Q5B@ti2*x{H`UEJx zH~=yw(Lpcj-V{0X=T{0evpclea|C zFOdG#F-Je|9e4Bc0q{D~S3$MoYoPk|+}$pI4yb-#;Nu%W^{WJmZg=|lK2ZHkJ?7wP z%$b%}Wew{5ONAfj5B~-%(I>eLr|Q zSOs4S=AioXo#2_^Z-8p=Cp>-@RQadCjo`p-ZeF&557OSdz>68LzugN@Gk&K)jGrUl zt#eKf*|aZ;ZYO>ZNS5d)K#kj1zze~zff|?d_Pg&6g6jYEp!&NPyc~Rm$8QB=;y(eF zz~2BRU%m<^;N=IL-1&A;{rVY@DUH4WijFtj<@oYO@E+p3K}1LN5U6$SlOU`UU2wM> zk4aE;b^yEA11|=1@GS5x zpz8ZxAAb~7x%Y$DgMaAXKkr`G?{%Q^PlIYt8N3KQ1ghR|1J{8+1gae$0X1G91w{w{ z25te*ywA1gdQkQ32PFsY2USl46rH}wF+^!r^s7lRs) ztH76n*MR!Y6sUIW_n3m}PYV>Cyb*jO_(@RZU-1gp-bGOT`Cc#v-vwR@u7djh=Rnc@ zzkn}#4*D3V@)vwFV+~#co&!Dzz6|_f@JjHPK$ZU+a3lC1p!&ByaqYYb)OhR##UClC zdfQ+Oz8X~fe*}abqMrlR&z}cX?jxY)|Bpb8=a<3V;5WblaQD2^`|bp9BK~er`92S- z-lss-cj|)UhqFQTdkd&`-T|uJDflArtstxzy&DvLeHB!@qOyB`8n}+=C7|lr35wqC z11|xOfiD5SA3PO&KdAZoppXA1_;TXE4XXZAQ`g?-gOVecfU18zsByj(yaHSVH6CvU zQH`Q^gPM;|f>(i`2bKT%i!T2VsCLYN8owHNI`{@q?R*<}4fq~Vq1FD{%1U3GTgX+&8fM2U4^6@!P^&SP){x^X(-{2X<-w$pAe+i7i zzxH^>Ay?lZsDAAQ)!tc9VBdBp0 z0b}qL;4bhrpuY12sQ!KoJPZ5`sPcacs@{J9)sJuZ_)BW8-&cZ~uN|P;aXV=31^@aK z+6&Uf==O#!snG{OND%|c@#P};9-*Lu7K zyq@^&p!n|fpy>J$@M`eaJbo1v-EVBW`gVeQh>wA4-&;YI|5;G&{uHQr{bx{gd&Obb zuU(+}e?J(5uLaewhrr)?E_NCCr>A14zX}>9eepH!ds|=Y`gt8VO8OY6d3mG92R*(6 zJdgBuf*Plv11|)h07dVg1~tFWd7b<2x!?uFuLPe1>Nn-U=+-|L@Cu$TCF3cC+j-X5 z>9>Kn=;a$0V}0`Yd!WWszkee9x;?qJ>+$~*o=4el1*et3@7wiX7h`M!;s@1ulM2%6KF^q&RgbNpqnO!y|kZxHnR0SET?3I6^F;ZF&V z5-z69g}&Y|68~eudV*-M`*#|VpCa5uco*Ss2m_QG2KC$QAo>aLV}$?dxtQ>zkGDMvUPstY_%vY;;cCLIgqsP7Df_LX{@?e{Uj;uvc)-X19DFYC zcl-EzJbpJQ-AccAIEelS_<6$r_VJzIM+sv-em?m>%k!%U-$(cwA^cv%!&eAb_&Q$# z{y6c62=@|xi=ef5FX2AIe!>RAF2WBH-bz>?=r`iP{{9Ys&m{aG!mn9M6xG4c5zgWp z`h5ps6X9+}_^l`X55RN59|QkGiTr+$@UMhlBK!+Mzh7}+e_zhuLxg`O{4L>mgs&5- zgewX9UF5&HgXc^9^TRx^BfQkdKf?2i{qtY(tlyUiFC&~v`UQl)>;VHsB1pU5~a1ME9Eav3*U-Epw$KUBO z27i%o9-%_`1mS-XHd6LR@a+Ws5(oD8uCC{ABHz#Z_)mjZ5}yF~5ia!Ug2(*x{oowo zR|yvoUPy=r=F+?sFIMW6d^s)0-}34x?)mLW4ySPv*DDKYTuxTv;Y}{XLc7^a>#g`u zx^g6ImWSgg9-J8;8H=YT_ufsOk#;M~RhAcfv!y7WPTDy&Ew<8TyqHv~?WVn(NgG+S z6~)P6(vtaV8ZS1p8ikHjTFb-HW$W|H;z}Mj)1(|{_3BErK92{}qYKq`IW2FBHtb2O zjcDWgd}BNmuP?{7q_wb|=4(^p#jIT~lW8_hnhVR7`qKJ58l24P>85DMjyRqk**7~r z7RNhxDmpqfIX5x6Z``6uwHmsa^H$MnWxkm-SNfCbVY54Je&_9Oc;X{T9@n#0Tuxi* zLW_Z(Ux{ZX#>RJSncq1O27JeSbLVuH=M@dZLdF0uv?^IWAJ&Ubu6DH(QPCaM%FcodoEilIo#<5;iTn-op!Mnaw_8N*tmnraMtDp};C>Cr^*Ad6;v8^q_k zQAzVsPu|er(7SjcsmJq74-~0+Z0CLq%Gm-f$f}NhXR>)_dv}v&!XAhE271R9<}f2b?yHp)rhwZ)`eZSl6+PEXLxPaj_#Fto)wq3?wnnL zMvppvsl3Xlea9Bki$?Egbe@ls*?49yJ=%)L%as;_A)a1kuV_1_EsaEQ#TS>cm(M&u2 zkR)hS8qAcdCbX}UeM_S||5S6h^Qz~KYD$xQ<=``|YHm5LZ`Lx^N?5rx#m}U>i}6a< zj?0;?rpr<=)O-o_u5WMdv@z)rol#rYOthA>`f`>d#cei{`a!=a9BiyKDhC_Q>;V?w zEqU5%Hze8f(sHX-z1;T?u|L##HNPF$|(R88R(xT^y zEJrVM)vPdC*hja#z5 z_tp`O%u7WcDVO81%7S}v>XfDJWf<+@4%X{((n>5cyO30qrajIjORVxJzEx8_i!>I_ zygN-|q(3chLmS0vR7EpTH*>3_RpF_H8l_TQuS~^nHjq#RJ@Mra##eF)ybV7 zNt;UDF`4Z|`0?$^QeDfD=w)F!sV^a!nJ~!YkZLdT-<{^Z0IwF3CfZ$HHVjIXD@E=` zqq-80x@a`gT(YTVJi?Rlh(4qV7HOB_&bx$0tjiRl}?ntcNA(NR{#Zr|um2DxdMx)D_CIQu{BHJUw z!tt7$THthAnG<=)d1Se=upHM?ShFlDkcr3l!{=?pjnbphDy-xFb|`VQT3I+0Pu;Gf z`#izavm@)sM>`@Gd97lGFjT;JG_6+ivqoFZ>d@?RWwAAMP4Q|bJL2D1nzVoBocz`i zUx}rb!xt%w>}l47d4@>fWR9a7}@r<{Ab0syCg;p^GA-><+ zA<=GU&N~U#W_07rr!se~p zuGv%?m_yhy|DvWa)|Ex0B=j-#1S)rx%8!H=P^b=sQBzcMNYY%l7KlaP6IN3zh&1cy>n;Nl>9T#v7HZ=}Zc zz1im8^k&~mVjXc4&!9Hj(p9L<(H&_nNn|USf6h%a!(j@gG3D1^z23C%X0||HR&JPX zWiynXH5xXkP%A5NsZ^peuTI{PLErqTETW#669~f(sgL1AG8WiqyO~pN49nuIk!owm zM7w)sX|_`9$)#!jJDK~^_cg4FeGQkFsFfyel&3#<6Q`5bax|G9Q9z0uW=U&E=Psle zQ(1VFOP0$>TiL?`M(WX4G}+NSMuH1ki6{XwB)l4=lptKpkxZS4J=5U}9ePblDIO@) z2jt0-WaZ{)%IUDETX{5vKijMyS5a_yN)5&k7>=e8Iq)D287mqx)x7=QJf?9Y)J#&BiwV7$OGd}COf_lw5EP;5$s|Z|&ZE|f*E(Xlq+c59ST8`JdRAfG4!wJY))6+~bQ(M&3;mGSa4#L<_*33`eA0O#4 zH$#qMKW8X*tNnVElseO2UQ=068J*?pJZN~qZIxoWY^aMbv&h!MH}uS4`)0IV?V&cG z%tV$@D9&PUMs+w7tg2;DTOl2`_3z-?f;(r;H0>vUpUUu2#B>ts_4>24Uz8W9ROF}R zBlahjxRQ69MjG^;&h<#k$cPijZH}l z&%x~g^*QCuM#T*7XOP9y;`pw8w@Ox3HJGlLF%JU_6}8d^wlPx9o?yBrPg%qM7!_#| zaj~Rhvr#shJ(4uEP-7%H_MMB4=kml(mdCvum)rCo8ChZX3g-1osB?8ja||aTcBG=m zJet#2^e3K`i@_zJ6v}|qa_ZiR2Z=&z7H4)XqPO^22d68IG~eVSvofp|qm21P^dL_# z>lQ5xE>W)w2E74K5zboE0x)K2h}D7?L2r^!Xh6JO!C#XEGrtt0Gp4wSQq*?76KaR} zq&a#@E3$_H8K7Qgpt$eg@>xz-;#!+=K#4ihtf1wy$&$w#cg#+YjE?Wzh(Apo2_FdU zF8p3W-!bQ#U$IzLw3vuyrM7BDu*)zFs#2FB;#!*2A#=%jOAggLwr6ZpHeZlyrs$gg zAgqlg1WK`cwweCK2e5#&qR(sV)lHsAAy|&qw*|NVV?(vEM9z)8_a8AyvynAx<3Q+S zIhdcecQ!CE?l$Xk=X*A8Q6-o=UhRwXT84HtI(%Pkm1uo2**=3t)IZwfZ646#LC<;|m*|ZsQ`&g*6Jtvu z3z+ODqhIU|(I^=cLhvxvy7a5AEyzCGeT}GMY4lxPVByYWWpMGKSNx|7{mB<`rxx@OzS0Ikg-CAp6K>7zPZGy=O zUgdDIs1(@_Du3CB@r%NG_Alo|g+8;~x=Pzuue?%%(!~eRo0}-DaBHvnLEdQJm~T>znlW^iZptII zS4-%N$AjxRb+OM(*^Y)yg7fyGh=%8zBlC*z7gHr&8b2ECPpTD6VB|_{-kvoH>y(1e zEf_q-LtH%Nk?m8<>O#HD#bo108|-kiLfD%web?fJChB6gEl)`m+|@*EA-@M@VT{(w zLwh&5I-G07olH4y#u=YcTAU^`I*>HY-FhH6)Np2W>?LS69Q&Z}o^+rvw_g(7?Y$x1 z`VVG6kjcZA!M3_o)N4;cPl7Df#gb)`_3F2TI*OF4=S+aymHj^UK}4Y0ffHl#O>uN@ zJUc!)HZgf?=lsj;#I2Jfd*k~Dx?b8*nfQ2bX6MGI4YD;zF*rf4y9b7bhTyvmEfr;X>{M{qz=qMR zv7!_YzUI+^4byx$|uS@@pmB$ zM4oDnZ^tzllU))(Z+u+-Z7*sr?<(MZ;{zSh;lI>LIMKJZcNH>uL$#e!C;i@b>zf~q zP_{1`y5w3oG2q800=?+*ns&jEOlRlKK4cB%-GlH z$|Pq+Pd1;RjCvfn*A|2O&F+`59zh>wj|`*OiJ2kOQswPb8-`TQQZ`Ym43@YN&VPAJ z%2vR`i9a*L8lcS!Q_YT11d^PZotT@LnjE_BYIsyi%>Aa{tlJ=82mGx)Z1VYi2u|{m ztX&7Q7-F}-*CDb=vX0Em9h@9LVAY6Sq)DyYV9p3FYEEG%pQUI`loC{&F0*B?Q{j}a zaQDRC@q<&-AD_7}+S5wepk;rE4M+5 zmajt#N7#((Ds7!xQ^&!P+1}=iZ$P7>s&RoWaGg5tbldNN4sGtdiQd1II|Aw=p=-Ch z$?*=xY1{Od2y*``NbUNqE?=<}%c8SPyRS3o%jWRRzUBze@VHbo*7)c6?-JbdNTWwH#YzSiMr~*k?MFHbX_urH1j8YHY;ZI>y?S>N2vqZ|jg8<-LdO zRJF@9>h{u*8bQ%IhWe8o!ai$p@MF4SUJr4$|3En`|5iS_cM_d`DQS7xU_`;T&4yhx zn6~jauO~WZu9B=Rud(;_!Yi#No*P&MVYOa;k$|{$>fo-C(c7*5qCQN+Bic5%D(Q|z zs8v3QXHw(tshI;KGh;=iQNlqP{A`H58kWv5UZjC2ttJgQvUL1FDf&A5Iys&{AIWjg z6p=-?_Y{fzq2Z`f_qBT4G13maOyt1NdE^~NQ8TS+dk>ehw6134ZGt{S!zZf>^CLv^ zrkf=oV6DlJ@SvJgQ^|mqNlV z%UP?&Na%FkPRiGp+BkBD=F$Wop5;Bta)t48%TmZz+TL^Qu?+l@Lw#N$na^?2P)@zzZ@4&2HvoDTAohjy*du$3h&Yrb1TbK6?F zcHpj|Q8<;Vhx|YfJLg()mT*Wju{PV##Mp5B@@uZ!cEk3rmtmYV0z-^Ir*(l$I!kpb@S4(g=GmRPM`AEnkRxjAnsfJaX?;0~+qHO})9{$Q#liaq z=Xwq|lOaZ!#VBdD6Udvx_am&WBeU_|@rk*8Gb5b9pG~Uh4R#Q}w)zl9OER6s#}tFy zo0&V1&-B;CjK*SupNR%!)@i1-M*3u(@- z9A^v48N56=$?&vmoum6BV^e)+_eW;z@O~>(dH(a`mb-!D^z{kes6+Fdz^@><7m}0b zYv=9NhZriI<=#2nGconmAIp%+6>s{{3y!>16XW&c9{SfX=Rn_29% zIL6#dnvB)qOkH6EOS^Ni{0A z)Hx^3CalW^QN=h?&GWi&yfSOA3GC*C7h8L%k)6u|bZfk?79E&Y|H_pGWTc28#!c&c ztCvt{oE7Vb`clX5=SG!<2rBNrG0;#S@koeI>rO1%J#;E_ICf2&RcaHa0azq!0 zNScLAvc948Y<8pSPmc$h!@ahw58~x-E^tIGag95{2WNGRt*6m}`r9>k31Rgi$u%5+ z%!s&yag~zE=_(VwoLWIdse$E;EmwaVfm*rwkuQi+Ap*AB?ec-EtFDt-^Wt&_Og5X6 zji)&qQqw&I21>+}X`ci-J3-rx_0&u!mHS8kzrubZvccX7mw)kSAufyv=#3)^Rzlb6#7t^DJ3yTZnU~t%+QlBTm$&;a@Ao}cY>hoz*_Mv`%=kK4uPNOzQYI{aQrf2TKVdr z4+vBHaykZ@z)qppT}SRueE9*vjef#m$*L|IVr9D)btLHtkH_NxLNvgQ+y?X4DLAC8 zbA)I#)I`miU@fh+lU-hTR@WffC#y;K0G{zRh%qieRybqUOr6A0KO~5rLC=wKhqHy% zM`KeqWGNwDbQ_}m-}p8}jv)${YN4;#T?mMfMe!u}54x|r9G~ODnxG2%Na0yrhtTlR zFlcY}FU|VdX;E;Ap+$09TDt=^=97FX7~=Yq3e@ z*(0`7m*u3?-$SckOCGDG7EBsdmcfWJ=*YFd02}kBuS1beFZPPJ8e`WUSS{Rw0ksxq zSZom1e7sYa|9Vje%7^s3%`rYv)^HIO4GTw^tu}XiBGFX{r!;a<%^pm1?@IOSp)XWq z)=>gosR)JLu{cJyD{Y+H0`9g&$TjZU6DJI>c?_km_LX`Dc`#C_;3SopdaD_=iyF$P z6ivIRK(<~_OXcHWh;XDa;U~y!2BGv~r=Qt?u%?-$fk##!LcrL}qu=1t$im{MRn*vF zF_rmmOl~~B`Yu^}fl^AGfz#dQwsazvT$Wx{;W-xvb;fO6z9SlqX0Ri$7vanulf-w% z6@B0CbMOVGJJI0~F^?2$NJ=+1vQl@s*-Fh9E2Fq^80UBe#Sgg*W~@cs^5BFW+3adzqaX3-1`f$dlbx2PQ3}=GPC_+Zq zq&wk^enOXY%WeNkgXN;V}8-xmj5ous!RC;YaIAcHY{qHHr?Dnnm{@MstzyO&X z2+XKl_|Ab)*qo+Mdn?TZk{K{Xp;tm@EPA!oM?~$2LmL4b*h1_u(vJ6|y(sm3(E&$_ zxYQI_i|Zs(hBp`ys~@862qx{%K!yl)S4}k1%||g`Q4Z)##dPn4&IG57ijm53 zLVJr>vdvpnZ%+(ON6w3#Y)IVv$<@%AKyUca?JnC?C4#MdnY)`(p*zz~J`at`p%jg> z6j4mZmgi=(ir}n~+I)qSEBXyY3QvU20A!kh9x^cLL8E<1n2z*~hZih2Ej- zFEh^Bqr3>NI+BWqX2SP-Cpo0<_cm&kOE6+z1aXl1%%5|s1kAlO)WQNaa2sYJqsLYG z5~u7e43Zq>VD%xc_AHp^j_rz^HsFcH2gTG(A;?K$8SKik*6}BsFDz>|j9u4d+%%v6{Qf}G`(LJIi<2Cdo+5WiBZH3zBU3$7=!$J!*JutxyG7;+T)atB~7-}q|ljHv{Y_sb3~tw6ll^umvhI$Ie|3>FfV&D9EsBbxmU@G zVVRa9=ji~hbi`O1=(TxJ8{kC9#oWQgy2!Cg)0_BMvFzZOX`&0!mZsJ_+x(HAnq&A2 z(v`J{wHO5!9}T}#al)F^vzRcXQa0T79L_XV$BxlL1ve|5>5>h~MoQo*N4&O7n?oK> z?o~QX^dKb)In(pnN7i~p`9!b7#P{h%ILmXiPeC&E7h>=0%PyLnrH)oi!JTnC`w6 zcp~zOiNt5ms(oA3EBJgY*dmf;dCDf4IROh&SwkWc)WHbRm7>mQcRhArEMmzRpp#l< z2PPmZc!+2{)nTiO6woe*qhG+B^+qv*sf^21GQ*{#`z&yWscmwX`jlZcEg=x!%-_v6-7!+na2uhtd3%M2(sV$b{qb)-ZH^QfRQx#a z7b-|yetPY1J9)uA>fcz+y7(j>GTz$~k0xvwqIXSZhcis>UTaCiC55PdIIA9JcU))K zZAI?h$OIO`O1)Vj(%lgjJM>Fgz zPEmpj3qAgXFG{wL>@;CaT>OP>`d4bYQ-wVlwiHL)wq#*BIJ3dr4vYoLWzZYAW-~Ic z#iPq&kw6H_IE2_{g)3|dw)lc7k7X!x!tPw@43oEaYz%_q#qJ@wy;dz`6Cxm13Ata8 zgl6S=IDuL}4FxYIhchmGh0&3sBFPb!80q)?TO1vBpKh)`GQHUu31|-j-0r!-5$e`m{@*EH3O*^35)9C8i8^+m^c2 zW!sb;Ws|WC+AJ@72DKC6oV1A(Nz^%JH3H=zTRfQLPUON8U6!3ZRFGju-g)!AhoqI3wfP5TbaKUINNS3%QEVcAfy|^* z+S~i#V#zHY3RA!vS6wgs2!>S}txB@h|6?Xjg9z{FzF(metf!#wt6S^Kff|DKW~SCY zN2ShqIe}-8DevA$E%vT;+ouV)TMxrBe7rPRE;MkB+tIW%&nw-nfJ#e@F}+}*_apzc z==6$E(~HsX%=ooD5+sFvh}`tD>m;J2E04{fjCyx(#I1~UX91UkBzV8=&AOtkR;@mU zFjX&yi*>#3;#AEdXBQ7xlY{8%&yZt!_GXySznId?Mas}4J*S$LrL-0~VC*Ms$Fv#H z-*%H*W%XgI^3DtKCf+YBS?5ZyHmVC8xXbL{_;?oCjSclrT6jTwJdSkDk~FeUaV-wf zLnDIX7vdc5&ND=^hT8sOMR%O~nG-Pn4Sp`yZVg-zJ(j z>O^yP4gtM}dyy3yyZTOT|3YWBs>qkl9vik|P@KC+JtQIhyiM$`&vCoa?$)V_Ii@e| z5wD{O#(C$JGSGI895|canf8{!y6W!SW%1xHJS~a8bw?C!)m)eOedsuKfPL0o*nq<^ zl{|R-`Y>g?A3g#9%?j9A)o-i(eXbzDlED1h!*jDN=9Na}RH-_>^nBm$ponsSw^( zi*v;9`A{8)ZA{0II^{3mnLWTRSlx6`?CNG&^UqD*Rg@ z(h+EdPNHlckZoPqFid%s6HM`-UCY}f1`8MUOpf}tYtxovWb)V0b+M>@o)6hIy_kD? zT_bF_^qj%F*v6;NmInJ;?wOmMhr;!PZzEKXN8;o)MuWNY z*Rv4tIsMnQ&A@n@)(j^G#pLFbe#$DdFZFmtv50b$a1wM7wte!yRMXS6wjiXg_S%xq zwLiwEN4pL-C~eb?zm9^dF5`m=J!TLfLkepzNOlbWB9N=g?>IRpEm9-678|{rjD4#; zQy%szDe-_D^v)gBUf#h{U)UE-VEH?SlF-Gh~A-?0)53!g+8tcmgW^~vfFq4IqWSW*2%;Q`Vc!@t*O~iLt3+^Odvo+Bv1;?r3c9$jBwPMmv(-4+q<<|dYe?jMe8uaTK> z_Ykeh$@ka`zD3sV=Sx76;f0PfW0;Oo{|I2nL@%JK|iJy1`f!4EZIV zdo9EhWReZ%)zt2??nEGxi-Z2`g6NCItwvo|M7GaxE-l!p$S`wDpR_Iiv zP88cc$iBC_4tc^?v$Iq(e(S!8$+_|4mYyBiJ4fm15#>C7^)tS9IXRQ&{R1QQ4S3?4 zx`~@NyatQVgk$+c)mP-$-;< zCbeIvmks|=T$@DM^{+hoH{sSKZSdD7?WuFMYiR$#h}%4*i{XJAeBsul6KY;!>C;}! zAup?LZ?+>SN$ZS*U7_q1iPrQ^dB17vg!Az(Q;tdc`TRu-x)Yza5!<=$*r_9kX4=u6 zOf65-l&*W03}06@6Mj#mD=E-HxNqqPwJXQkl-@G_)+JvmIvu`KB!m(ZVfKnQzV;#> za=QDMMV%w&6Lq&R*Cevggu9rXch1S$Um~Qch`W{9wIahmb|zeY)PP^Md zy2k7_;$=73ftWsnxtpp_cxThOH)jh|+w=~qcIjRYOT$IkLI!#brNb`8^~&D6o36NB z-sIFL4){=`SwWUk)ER`mmIf4JG`weghtqw=j}o@HnQO(j{F#3ipU$C_(be~rE1XBt zXxk;$licUji5XqS1kv2WSajruO@S9!v%bFmTb&!69zWM+@Iqb1s#yqEt&yEEbu751 z#x-|5XSHjuN&&ylb=4AXdG^q?tf_S!2iSC=Ogq>Sn{KAfcaw;H1Q~9A29-qu&U7Vr zR8bx1D^c75MYTZvp;G?q=&#Cs^2?y!0ll{7;yS2pT6^~BxW`7^(UfC&H2OSdewfUv zT(@RFpF4^DTl^}}yFKs9`g@|s&2KuxnhT?>bC?EjukMU?7@}JrGpl=geD=ZQ>W7uY58-qg*N{ow(~#>J1;typP2A1~N*D*h88v#gbjXp`^#46G#CalfOrw(V&oc(M_|y8mrRcH?qE9fJ Xkp{?INL{CwZ5@c1(2if#6H4@-6575y literal 0 HcmV?d00001 diff --git a/src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.po b/src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.po new file mode 100644 index 000000000..850c7765d --- /dev/null +++ b/src/robotide/locale/pt_BR/LC_MESSAGES/RIDE.po @@ -0,0 +1,1648 @@ +msgid "" +msgstr "" +"Project-Id-Version: RIDE 2.1\n" +"POT-Creation-Date: 2024-01-01 19:29+0000\n" +"PO-Revision-Date: 2024-01-01 19:49+0000\n" +"Last-Translator: Hélio Guilherme \n" +"Language-Team: helioxentric@gmail.com\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Generated-By: pygettext.py 1.5\n" +"X-Crowdin-Project: robotframework-ride\n" +"X-Crowdin-Project-ID: 637294\n" +"X-Crowdin-Language: pt-BR\n" +"X-Crowdin-File: RIDE.pot\n" +"X-Crowdin-File-ID: 2\n" +"X-Generator: Poedit 3.4.1\n" + +#: /home/helio/github/RIDE/src/robotide/application/application.py:383 +msgid "Found Robot Framework version %s from %s." +msgstr "Versão do Robot Framework %s encontrada de %s." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "New development version is available." +msgstr "Uma nova versão de desenvolvimento está disponível." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "Upgrade?" +msgstr "Atualizar?" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid " with:" +msgstr " com:" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid "You may install version " +msgstr "Você pode instalar a versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:113 +msgid "Click OK to Upgrade now!" +msgstr "Clique em OK para atualizar agora!" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:114 +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:221 +msgid "" +"After upgrade you will see another dialog informing to close this RIDE " +"instance." +msgstr "" +"Após a atualização, você verá outro diálogo informando para fechar esta " +"instância do RIDE." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "No Upgrade Available" +msgstr "Nenhuma atualização disponível" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "You have the latest version of RIDE." +msgstr "Você tem a última versão do RIDE." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:122 +msgid " Have a nice day :)" +msgstr " Tenha um bom dia :)" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "An error occurred when installing new version" +msgstr "Ocorreu um erro ao instalar a nova versão" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "Failed to Upgrade" +msgstr "Falha ao atualizar" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "Completed Upgrade" +msgstr "Atualização Completada" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "You should close this RIDE (Process ID = " +msgstr "Você deve fechar este RIDE (ID do processo = " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:208 +msgid "Update available" +msgstr "Atualização disponível" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid " available from " +msgstr " disponível em " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid "New version " +msgstr "Nova versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:217 +msgid "See this version " +msgstr "Ver esta versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:219 +msgid "You can update with the command:" +msgstr "Você pode atualizar com o comando:" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:220 +msgid "Or, click Upgrade Now" +msgstr "Ou, clique em Atualizar Agora" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:222 +msgid "See the latest development " +msgstr "Ver o desenvolvimento mais recente " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:227 +msgid "" +"I'm using another method for RIDE updates\n" +" and do not need automatic update checks" +msgstr "" +"Estou usando outro método para atualizações RIDE\n" +" e não preciso de verificações de atualização automáticas" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:232 +msgid "remind me later" +msgstr "lembrar-me mais tarde" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:238 +msgid "Upgrade Now" +msgstr "Atualizar Agora" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:424 +msgid "Log options" +msgstr "Opções de registo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:432 +msgid "Output directory: " +msgstr "Diretório de resultados: " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:450 +msgid "Add suite name to log names" +msgstr "Adicionar nome do suite aos nomes dos registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:453 +msgid "Add timestamp to log names" +msgstr "Adicionar data e hora aos nomes dos registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:456 +msgid "Save Console and Message logs" +msgstr "Salvar registros da Consola e das Mensagens" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:474 +msgid "Select Logs Directory" +msgstr "Selecionar diretório dos Registros" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:496 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Arguments" +msgstr "Argumentos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:508 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:534 +msgid "Arguments for the test run. Arguments are space separated list." +msgstr "" +"Argumentos para a execução do teste. Os argumentos são uma lista separada " +"por espaços." + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:553 +msgid "Does not execute - help or version option given" +msgstr "Não executa - opção de ajuda ou versão dada" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:558 +msgid "Unknown option(s):" +msgstr "Opção desconhecida:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:564 +msgid "Tests filters" +msgstr "Filtros de testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:573 +msgid "Only run tests with these tags:" +msgstr "Apenas executar testes com estas etiquetas:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:576 +msgid "Skip tests with these tags:" +msgstr "Ignorar testes com estas etiquetas:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:672 +msgid "Script to run tests:" +msgstr "Script para executar testes:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:98 +msgid "Stop a running test" +msgstr "Parar um teste em execução" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:99 +msgid "Step over" +msgstr "Saltar Passo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:161 +msgid "A plugin for running tests from within RIDE" +msgstr "Um Plugin para executar testes de dentro do RIDE" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +msgid "Run Tests" +msgstr "Executar Testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:54 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:41 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:36 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:53 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:738 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:750 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +msgid "Tools" +msgstr "Ferramentas" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:258 +msgid "Run the selected tests" +msgstr "Executar os testes selecionados" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +msgid "Run Tests with Debug" +msgstr "Executar testes com DEBUG (Depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:263 +msgid "Run the selected tests with Debug" +msgstr "Executar os testes selecionados com DEBUG (Depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +msgid "Stop Test Run" +msgstr "Parar Execução do Teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:350 +msgid "[ SENDING STOP SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PARAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:357 +msgid "[ SENDING PAUSE SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PAUSAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:363 +msgid "[ SENDING CONTINUE SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL CONTINUAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:369 +msgid "[ SENDING STEP NEXT SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PASSO SEGUINTE ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:375 +msgid "[ SENDING STEP OVER SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL SALTAR PASSO ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:408 +msgid "command: %s\n" +msgstr "comando: %s\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:478 +msgid "" +"There are unsaved modifications.\n" +" Do you want to save all changes and run the tests?" +msgstr "" +"Existem modificações não guardadas.\n" +" Você quer salvar todas as alterações e correr os testes?" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:480 +msgid "Unsaved Modifications" +msgstr "Alterações Não Salvadas" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:489 +msgid "" +"No tests selected. \n" +"Continue anyway?" +msgstr "" +"Nenhum teste selecionado.\n" +"Continuar mesmo assim?" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:491 +msgid "No tests selected" +msgstr "Nenhum teste selecionado" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:548 +msgid "" +"\n" +"Test finished {}" +msgstr "" +"\n" +"Teste terminado {}" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:583 +msgid "Messages log exceeded 80% of process memory, stopping for now..." +msgstr "" +"O registro de mensagens excedeu 80% da memória do processo, parando por " +"agora..." + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:741 +msgid "Start" +msgstr "Iniciar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:742 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:745 +msgid "Start robot" +msgstr "Iniciar robot" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:743 +msgid "Start running the robot test suite" +msgstr "Começar a executar a suíte de teste do robot" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:744 +msgid "Debug" +msgstr "Debug (depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:746 +msgid "Start running the robot test suite with DEBUG loglevel" +msgstr "" +"Começar a executar a suíte de teste do robot com o registro DEBUG (Depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:748 +msgid "Stop" +msgstr "Parar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:752 +msgid "Pause" +msgstr "Pausar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:754 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:755 +msgid "Pause test execution" +msgstr "Pausar a execução do teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:756 +msgid "Continue" +msgstr "Continuar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:759 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:760 +msgid "Continue test execution" +msgstr "Continuar a execução do teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:761 +msgid "Next" +msgstr "Seguinte" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:762 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:763 +msgid "Step next" +msgstr "Saltar seguinte" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:788 +msgid "Execution Profile: " +msgstr "Perfil de Execução: " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:791 +msgid "Choose which method to use for running the tests" +msgstr "Escolha qual o método a usar para executar os testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:798 +msgid "Open Logs Directory" +msgstr "Abrir diretório dos Registros" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:800 +msgid "View All Logs in Explorer" +msgstr "Visualizar Todos os Registros no Gerenciador de Arquivos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:801 +msgid " Report" +msgstr " Relatório" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:802 +msgid "View Robot Report in Browser (CtrlCmd-R)" +msgstr "Ver Relatório do Robot no Navegador (CtrlCmd-R)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:804 +msgid " Log" +msgstr " Registro" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:805 +msgid "View Robot Log in Browser (CtrlCmd-L)" +msgstr "Ver o Registro do Robot no Navegador (CtrlCmd-R)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:812 +msgid " Autosave " +msgstr " Salvar Automático " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:813 +msgid "Automatically save all changes before running" +msgstr "Salvar automaticamente todas as alterações antes de executar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:818 +msgid " Pause after failure " +msgstr " Pausar após falha " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:819 +msgid "Automatically pause after failing keyword" +msgstr "Pausar automaticamente após falha da palavra-chave" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:927 +msgid "Console log" +msgstr "Registro da Consola" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:931 +msgid "Message log" +msgstr "Registro das Mensagens" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1054 +msgid "Starting test:" +msgstr "Iniciando teste:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1059 +msgid "Ending test:" +msgstr "Finalizando teste:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1067 +msgid "UNKNOWN STATUS:" +msgstr "ESTADO DESCONHECIDO:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1095 +msgid "<< PAUSED >>" +msgstr "<< PAUSADO >>" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1100 +msgid "<< CONTINUE >>" +msgstr "<< CONTINUAR >>" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1141 +msgid "" +"There isn't logs directory. \n" +"Please, run the tests and try again" +msgstr "" +"Não existe diretório de registros. \n" +"Por favor, corra os testes e tente novamente" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1143 +msgid "No logs directory" +msgstr "Nenhum diretório de registros" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1228 +msgid "elapsed time: %s pass: %s skip: %s fail: %s" +msgstr "tempo decorrido: %s passado: %s saltado: %s falhado: %s" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1248 +msgid " current keyword: " +msgstr " palavra-chave atual: " + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:41 +msgid "" +"[Navigate]\n" +" !Go &Back | Go back to previous location in tree | Alt-%s | " +"ART_GO_BACK\n" +" !Go &Forward | Go forward to next location in tree | Alt-%s | " +"ART_GO_FORWARD\n" +" " +msgstr "" +"[Navegação]\n" +" !Ir para Anterior | Retornar à localização anterior | Alt-%s | " +"ART_GO_BACK\n" +" !Ir para Seguinte | Avançar para a localização seguinte | Alt -%s | " +"ART_GO_FORWARD\n" +" " + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +msgid "Add Tag to selected" +msgstr "Adicionar Etiqueta aos selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:108 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Edit" +msgstr "Editar" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +msgid "Clear Selected" +msgstr "Limpar Selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Add Tag To Selected" +msgstr "Adicionar Etiqueta aos selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Enter Tag Name" +msgstr "Inserir Nome da Etiqueta" + +#: /home/helio/github/RIDE/src/robotide/editor/__init__.py:28 +msgid "" +"[Edit]\n" +"&Undo | Undo last modification | Ctrlcmd-Z\n" +"&Redo | Redo modification | Ctrlcmd-Y\n" +"---\n" +"Cu&t | Cut | Ctrlcmd-X\n" +"&Copy | Copy | Ctrlcmd-C\n" +"&Paste | Paste | Ctrlcmd-V\n" +"&Insert | Insert | Shift-Ctrl-V\n" +"&Delete | Delete | Del\n" +"---\n" +"Comment Rows | Comment selected rows | Ctrlcmd-3\n" +"Comment Cells | Comment cells with # | Ctrlcmd-Shift-3\n" +"Uncomment Rows | Uncomment selected rows | Ctrlcmd-4\n" +"Uncomment Cells | Uncomment cells with # | Ctrlcmd-Shift-4\n" +"---\n" +"Insert Cells | Insert Cells | Ctrlcmd-Shift-I\n" +"Delete Cells | Delete Cells | Ctrlcmd-Shift-D\n" +"Insert Rows | Insert Rows | Ctrlcmd-I\n" +"Delete Rows | Delete Rows | Ctrlcmd-D\n" +"Move Rows Up | Move Rows Up | Alt-Up\n" +"Move Rows Down | Move Rows Down | Alt-Down\n" +"[Tools]\n" +"Content Assistance (Ctrl-Space or Ctrl-Alt-Space) | Show possible keyword " +"and variable completions | | | POSITION-70\n" +msgstr "" +"[Editar]\n" +"An&ular | Anular a alteração anterior | Ctrlcmd-Z\n" +"&Refazer | Repetir a alteração anterior | Ctrlcmd-Y\n" +"---\n" +"Cor&tar | Cortar | Ctrlcmd-X\n" +"&Copiar | Copiar | Ctrlcmd-C\n" +"Colar | Colar | Ctrlcmd-V\n" +"&Inserir | Inserir | Shift-Ctrl-V\n" +"Apagar | Apagar | Del\n" +"---\n" +"Comentar Linhas | Comentar as linhas selecionadas | Ctrlcmd-3\n" +"Comentar Células | Comentar as células com # | Ctrlcmd-Shift-3\n" +"Descomentar Linhas | Descomentar as linhas selecionadas | Ctrlcmd-4\n" +"Descomentar Células | Descomentar as células com # | Ctrlcmd-Shift-4\n" +"---\n" +"Inserir Células | Inserir Células | Ctrlcmd-Shift-I\n" +"Apagar Células | Apagar Células | Ctrlcmd-Shift-D\n" +"Inserir Linhas | Inserir Linhas | Ctrlcmd-I\n" +"Apagar Linhas | Apagar Linhas | Ctrlcmd-D\n" +"Mover Linhas Acima | Mover Linhas Acima | Alt-Up\n" +"Mover Linhas Abaixo | Mover Linhas Abaixo | Alt-Down\n" +"[Ferramentas]\n" +"Assistência de Conteúdo (Ctrl-Space ou Ctrl-Alt-Space) | Mostra possíveis " +"palavras-chave e completar variáveis | | | POSITION-70\n" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:162 +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:394 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:186 +msgid " (READ ONLY)" +msgstr " (APENAS LEITURA)" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:224 +msgid "Settings" +msgstr "Configurações" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:343 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Source" +msgstr "Origem" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:381 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:207 +msgid "Find Usages" +msgstr "Encontrar Usos" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:71 +msgid "Create Keyword" +msgstr "Criar Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:72 +msgid "Extract Keyword" +msgstr "Extrair Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:73 +msgid "Extract Variable" +msgstr "Extrair Variável" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:74 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "Rename Keyword" +msgstr "Renomear Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:75 +msgid "Find Where Used" +msgstr "Procurar Onde Usado" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:76 +msgid "JSON Editor\tCtrl-Shift-J" +msgstr "Editor JSON, Ctrl-Shift-J" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:78 +msgid "Go to Definition\tCtrl-B" +msgstr "Ir para a Definição, Ctrl-B" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:80 +msgid "Undo\tCtrl-Z" +msgstr "Desfazer, Ctrl-Z" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:81 +msgid "Redo\tCtrl-Y" +msgstr "Refazer, Ctrl-Y" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:83 +msgid "Make Variable\tCtrl-1" +msgstr "Fazer Variável, Ctrl-1" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:84 +msgid "Make List Variable\tCtrl-2" +msgstr "Fazer Variável Lista, Ctrl-2" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:85 +msgid "Make Dict Variable\tCtrl-5" +msgstr "Fazer Variável Dicionário, Ctrl-5" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:87 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:292 +msgid "Comment Cells\tCtrl-Shift-3" +msgstr "Comentar Células, Ctrl-Shift-3" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:88 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:293 +msgid "Uncomment Cells\tCtrl-Shift-4" +msgstr "Descomentar Células, Ctrl-Shift-4" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:89 +msgid "Move Cursor Down\tAlt-Enter" +msgstr "Mover Cursor para Baixo Alt-enter" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:91 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:284 +msgid "Comment Rows\tCtrl-3" +msgstr "Comentar Linhas Ctrl-3" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:92 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:285 +msgid "Uncomment Rows\tCtrl-4" +msgstr "Descomentar Linhas Ctrl-4" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:93 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:286 +msgid "Move Rows Up\tAlt-Up" +msgstr "Mover Linhas Acima Alt-Up" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:94 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:287 +msgid "Move Rows Down\tAlt-Down" +msgstr "Mover Linhas Abaixo Alt-Down" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:95 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:288 +msgid "Swap Row Up\tCtrl-T" +msgstr "Trocar Linha para Cima Ctrl-T" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:289 +msgid "Insert Rows\tCtrl-I" +msgstr "Inserir Linhas Ctrl-I" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:290 +msgid "Delete Rows\tCtrl-D" +msgstr "Apagar Linhas Ctrl-D" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:747 +msgid "" +"Keyword was not detected by RIDE\n" +"
    Possible corrections:
    \n" +"
      \n" +"
    • Import library or resource file containing the keyword.\n" +"
    • For library import errors: Consider importing library spec " +"XML\n" +" (Tools / Import Library Spec XML or by adding the XML file with " +"the\n" +" correct name to PYTHONPATH) to enable keyword completion\n" +" for example for Java libraries.\n" +" Library spec XML can be created using libdoc tool from Robot " +"Framework.
    • \n" +"
    " +msgstr "" +"Palavra-chave não foi detectada pela RIDE\n" +"
    Possíveis correções:
    \n" +"
      \n" +"
    • Importar biblioteca ou arquivo de recurso que contém a " +"palavra-chave
    • \n" +"
    • Para erros de importação de biblioteca: Considere importar a " +"especificação de biblioteca XML\n" +" (Ferramentas / Importar Especificação XML ou adicionando o " +"arquivo XML com o\n" +" nome correto em PYTHONPATH) para ativar a conclusão de palavras-" +"chave\n" +" por exemplo para bibliotecas Java.\n" +" Especificações de biblioteca XML podem ser criadas usando " +"ferramenta libdoc do Robot Framework.
    • \n" +"
    " + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "New name" +msgstr "Novo nome" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:995 +msgid "Save" +msgstr "Salvar" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:996 +msgid "Cancel" +msgstr "Cancelar" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Error in JSON:" +msgstr "Erro em JSON:" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Save anyway?" +msgstr "Salvar mesmo assim?" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1023 +msgid "Validation Error!" +msgstr "Erro de Validação!" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1164 +msgid "Please select what you want to check for usage" +msgstr "Por favor, selecione o que verificar a utilização" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1167 +msgid "Complete cell content" +msgstr "Conteúdo completo da célula" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1168 +msgid "Variable " +msgstr "Variável " + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1180 +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:513 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:49 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:127 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:184 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:98 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:160 +msgid "Search" +msgstr "Pesquisa" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Down\tCtrl-Down" +msgstr "Mover para baixo Ctrl-Down" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Up\tCtrl-Up" +msgstr "Mover para Cima Ctrl-Up" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Delete" +msgstr "Apagar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:100 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Clear" +msgstr "Limpar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +msgid "Variable" +msgstr "Variável" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Comment" +msgstr "Comentário" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Value" +msgstr "Valor" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Dict" +msgstr "Adicionar Dicionário" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add List" +msgstr "Adicionar Lista" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Scalar" +msgstr "Adicionar Escalar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Import" +msgstr "Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Name / Path" +msgstr "Nome / Caminho" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Import Failed Help" +msgstr "Ajuda da Falha na Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Library" +msgstr "Biblioteca" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Resource" +msgstr "Recurso" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:654 +msgid "Variables" +msgstr "Variáveis" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:586 +msgid "Add Import" +msgstr "Adicionar Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:620 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:33 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:62 +msgid "Import Library Spec XML" +msgstr "Importar Especificação de Biblioteca XML" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "" +"
    Possible corrections and notes:
    \n" +"
      \n" +"
    • Import failure is shown with red color.
    • \n" +"
    • See Tools / View RIDE Log for detailed information about the " +"failure.
    • \n" +"
    • If the import contains a variable that RIDE has not " +"initialized, consider adding the variable\n" +" to variable table with a default value.
    • \n" +"
    • For library import failure: Consider importing library spec " +"XML (Tools / Import Library Spec XML or by\n" +" adding the XML file with the correct name to PYTHONPATH) to " +"enable keyword completion\n" +" for example for Java libraries.\n" +" Library spec XML can be created using libdoc tool from Robot " +"Framework.\n" +" For more information see \n" +" wiki.\n" +"
    • \n" +"
    " +msgstr "" +"
    Possíveis correções e notas:
    \n" +"
      \n" +"
    • A falha de importação é mostrada em cor vermelha.
    • \n" +"
    • Ver Ferramentas / Visualizar Registro do RIDE, para " +"informação detalhada acerca da falha.
    • \n" +"
    • Se a importação contém uma variável antes que o RIDE a tenha " +"inicializado, considere acrescentar essa variável na tabela de variáveis com " +"um valor por omissão.
    • \n" +"
    • Para uma falha de importação de biblioteca: Considere " +"importar a especificação XML (Ferramentas / Importar Especificação de " +"Biblioteca XML ou por acrescentar o arquivo XML com o nome correcto a " +"PYTHONPATH) para ativar a sugestão de palavras-chave \n" +" por exemplo para bibliotecas Java.\n" +" A especificação de biblioteca XML pode ser criada usando a " +"ferramenta libdoc do Robot Framework.\n" +" Para mais informação, ver\n" +" wiki.\n" +"
    • \n" +"
    " + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "Import failure handling" +msgstr "Gerenciamento de falhas de importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Metadata" +msgstr "Metadados" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:708 +msgid "Add Metadata" +msgstr "Adicionar Metadados" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "ERROR: Data sanity check failed!" +msgstr "ERRO: Falha na verificação!" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "Error at line" +msgstr "Erro na linha" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:368 +msgid "Reset changes?" +msgstr "Restaurar alterações?" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:369 +msgid "Can not apply changes from Text Editor" +msgstr "Não é possível aplicar alterações do Editor de Texto" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:497 +msgid "Apply Changes" +msgstr "Aplicar Alterações" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:523 +msgid "Syntax colorization disabled due to missing requirements." +msgstr "Colorização de sintaxe desativada devido à falta de requisitos." + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:524 +msgid "Get help" +msgstr "Obter ajuda" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:539 +msgid "" +"

    Syntax colorization

    \n" +"

    \n" +" Syntax colorization for Text Edit uses Pygments syntax highlighter.\n" +"

    \n" +"

    \n" +" Install Pygments from command line with:\n" +"

    \n"
    +"            pip install pygments\n"
    +"        
    \n" +" Or:\n" +"
    \n"
    +"            easy_install pygments\n"
    +"        
    \n" +" Then, restart RIDE.\n" +"

    \n" +"

    \n" +" If you do not have pip or easy_install,\n" +" follow these instructions.\n" +"

    \n" +"

    \n" +" For more information about installing Pygments, see the site.\n" +"

    \n" +" " +msgstr "" +"

    Colorização de Sintaxe

    \n" +"

    \n" +" A colorização de Sintaxe do Editor de Texto usa o Pygments como realce de sintaxe.\n" +"

    \n" +"

    \n" +" Instalar o Pygments na linha de comandos com:\n" +"

    \n"
    +"            pip install pygments\n"
    +"        
    \n" +" Ou:\n" +"
    \n"
    +"            easy_install pygments\n"
    +"        
    \n" +" Depois, reiniciar o RIDE.\n" +"

    \n" +"

    \n" +" Se você não tiver pip ou easy_install,\n" +" siga estas instruções.\n" +"

    \n" +"

    \n" +" Para mais informação acerca instalar o Pygments, ver este sítio.\n" +"

    \n" +" " + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:564 +msgid "Getting syntax colorization" +msgstr "Obtendo colorização de sintaxe" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:662 +msgid "No matches found." +msgstr "Nenhuma correspondência encontrada." + +#: /home/helio/github/RIDE/src/robotide/log/log.py:45 +msgid "RIDE Log" +msgstr "Registro do RIDE" + +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +msgid "View RIDE Log" +msgstr "Visualizar Registro do RIDE" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:45 +msgid "Parser Log" +msgstr "Registro do Interpretador" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +msgid "View Parser Log" +msgstr "Visualizar Registro do Interpretador" + +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:55 +msgid "Create RIDE Desktop Shortcut" +msgstr "Criar atalho para RIDE no Ambiente de Trabalho" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:27 +msgid "" +"The specified command string will be split from whitespaces into a command\n" +"and its arguments. If either the command or any of the arguments require\n" +"internal spaces, they must be written as ''.\n" +"\n" +"The command will be executed in the system directly without opening a " +"shell.\n" +"This means that shell commands and extensions are not available. For " +"example,\n" +"in Windows batch files to execute must contain the '.bat' extension and " +"'dir'\n" +"command does not work.\n" +"\n" +"Examples:\n" +" robot.bat --include smoke C:\\my_tests\n" +" svn update /home/robot\n" +" C:\\ProgramFiles\\App\\prg.exe argumentwithspace,\n" +"Run configurations are stored in the RIDE settings file.\n" +msgstr "" +"A sequência de comandos especificada será separada por espaços em branco num " +"comando\n" +"e seus argumentos. Se o comando ou qualquer um dos argumentos requer espaços " +"internos, eles devem ser escritos como ''.\n" +"\n" +"O comando será executado diretamente no sistema sem abrir uma shell.\n" +"Isto significa que comandos e extensões de shell não estão disponíveis. Por " +"exemplo,\n" +"nos arquivos de lote (batch) do Windows para executar deve conter a extensão " +"'.bat ' e o comando 'dir' não funciona.\n" +"\n" +"Exemplos:\n" +" robot.bat --include iniciais C:\\meus_testes\n" +" svn update /home/robot\n" +" C:\\ProgramFiles\\App\\prg.exe argumentwithspace,\n" +"As configurações de execução são armazenadas no arquivo de configurações do " +"RIDE.\n" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:45 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +msgid "Manage Run Configurations" +msgstr "Gerenciar Configurações de Execução" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "New" +msgstr "Novo" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "Remove" +msgstr "Remover" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Command" +msgstr "Comando" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Documentation" +msgstr "Documentação" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Name" +msgstr "Nome" + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:30 +msgid "" +"A plugin for executing commands on the system.\n" +"\n" +" This plugin enables creation of persistent run configurations and\n" +" execution of those. Output of the executed command is displayed in a\n" +" separate tab." +msgstr "" +"Um Plugin para a execução de comandos no sistema.\n" +"\n" +" Este Plugin permite a criação de configurações de execução persistentes " +"e sua\n" +" execução. Os resultados do comando executado são mostrados numa aba " +"separada." + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:56 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:63 +msgid "Macros" +msgstr "Macros" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:36 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:35 +msgid "Search Tests" +msgstr "Pesquisar Testes" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:50 +msgid "Tag Search" +msgstr "Pesquisar Etiquetas" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Tags" +msgstr "Etiquetas" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Test" +msgstr "Teste" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:66 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:90 +msgid "Results: " +msgstr "Resultados: " + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:82 +msgid "" +"Find matches using tag patterns. See RF User Guide or 'robot --help' for " +"more information." +msgstr "" +"Encontre correspondências usando padrões de Etiquetas. Consulte o Guia do " +"Usuário RF ou 'robot --help' para obter mais informações." + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:96 +msgid "Include" +msgstr "Incluir" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:139 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:189 +msgid "Add all to selected" +msgstr "Adicionar todos aos selecionados" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:156 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:164 +msgid "Results: %d" +msgstr "Resultados: %d" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:177 +msgid "Info. " +msgstr "Info. " + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:195 +msgid "Find matches by test name, documentation and/or tag." +msgstr "" +"Encontrar correspondências por nome do teste, documentação e/ou etiqueta." + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:212 +msgid "Search term" +msgstr "Termo para pesquisa" + +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:33 +msgid "A plugin for searching tests based on name, tags and documentation" +msgstr "" +"Um Plugin para pesquisar testes com base em nome, etiquetas e documentação" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:60 +msgid "Library Spec XML|*.xml|All Files|*.*" +msgstr "Especificação de Biblioteca XML|*.xml|Todos os Arquivos|*.*" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:77 +msgid "" +"Library \"%s\" imported\n" +"from \"%s\"\n" +"This may require RIDE restart." +msgstr "" +"Biblioteca \"%s\" importada\n" +"de \"%s\"\n" +"Isto pode necessitar que o RIDE seja reiniciado." + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:78 +msgid "Info" +msgstr "Info" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Could not import library from file \"%s\"" +msgstr "Não foi possível importar a biblioteca do arquivo \"%s\"" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Import failed" +msgstr "Falha na importação" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Help" +msgstr "Ajuda" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:140 +msgid "File" +msgstr "Arquivo" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:65 +msgid "&Help" +msgstr "&Ajuda" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:35 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:36 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:37 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:39 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:59 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:139 +msgid "Search Keywords" +msgstr "Pesquisar Palavras-Chave" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:43 +msgid "A plugin for searching keywords based on name or documentation." +msgstr "" +"Um Plugin para pesquisar palavras-chave com base no nome ou documentação." + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:55 +msgid "Search keywords from libraries and resources" +msgstr "Pesquisar palavras-chave de bibliotecas e recursos" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:173 +msgid "Search term: " +msgstr "Termo para pesquisa: " + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:180 +msgid "Search documentation" +msgstr "Pesquisar na documentação" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:185 +msgid "Source: " +msgstr "Origem: " + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Description" +msgstr "Descrição" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:56 +msgid "" +"[File]\n" +" !&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW\n" +" ---\n" +" !&Open Test Suite | Open file containing tests | Ctrlcmd-O | " +"ART_FILE_OPEN\n" +" !Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O " +"| ART_FOLDER_OPEN\n" +" !Open External File | Open file in Code Editor | | ART_NORMAL_FILE\n" +" ---\n" +" !&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE\n" +" !Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS\n" +" ---\n" +" !E&xit | Exit RIDE | Ctrlcmd-Q\n" +"\n" +" [Tools]\n" +" !Search Unused Keywords | | | | POSITION-54\n" +" !Manage Plugins | | | | POSITION-81\n" +" !View All Tags | | F7 | | POSITION-82\n" +" !Preferences | | | | POSITION-99\n" +"\n" +" [Help]\n" +" !Shortcut keys | RIDE shortcut keys\n" +" !User Guide | Robot Framework User Guide\n" +" !Wiki | RIDE User Guide (Wiki)\n" +" !Report a Problem | Open browser to SEARCH on the RIDE issue tracker\n" +" !Release notes | Shows release notes\n" +" !About | Information about RIDE\n" +" !Check for Upgrade | Looks at PyPi for new released version\n" +" " +msgstr "" +"[Arquivo]\n" +" !&Novo Projecto | Criar uma nova suíte de testes | Ctrlcmd-N | ART_NEW\n" +" ---\n" +" !Abrir Suíte de Teste | Abrir arquivo contendo testes | Ctrlcmd-O | " +"ART_FILE_OPEN\n" +" !Abrir &Diretoria | Abrir diretoria contendo arquivos de dados | Shift-" +"Ctrlcmd-O | ART_FOLDER_OPEN\n" +" !Abrir Arquivo Externo | Abrir rquivo no Editor de Código | | " +"ART_NORMAL_FILE\n" +" ---\n" +" !&Salvar| Salva o arquivo selecionado | Ctrlcmd-S | ART_FILE_SAVE\n" +" !S&alvar Tudo | Salva todas as alterações | Ctrlcmd-Shift-S | " +"ART_FILE_SAVE_AS\n" +" ---\n" +" !Sair | Sair do RIDE | Ctrlcmd-Q\n" +"\n" +" [Ferramentas]\n" +" !Pesquisar Palavras-Chave Não Usadas | | | | POSITION-54\n" +" !Gerenciamento de Plugins | | | | POSITION-81\n" +" !Ver Todas as Etiquetas | | F7 | | POSITION-82\n" +" !Preferências | | | | POSITION-99\n" +"\n" +" [Ajuda]\n" +" !Teclas de Atalho | Teclas de atalho do RIDE\n" +" !Guia do Usuário | Guia do Usuário do Robot Framework\n" +" !Wiki | Guia do Usuário do RIDE (Wiki)\n" +" !Reportar um Problema | Abrir o Navegador para PESQUISAR no reporte de " +"problemas do RIDE\n" +" !Notas da Versão | Mostra as Notas da Versão (Release Notes)\n" +" !Acerca | Informação acerca do RIDE\n" +" !Verificar Atualização | Verifica em PyPi se existe uma nova versão\n" +" " + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:167 +msgid "Saved %s" +msgstr "Salvou-se %s" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:168 +msgid "Saved all files" +msgstr "Salvaram-se todos os arquivos" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:191 +msgid "Validation Error" +msgstr "Erro de Validação" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:195 +msgid "\"%s\" is read only" +msgstr "\"%s\" é apenas de leitura" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:196 +msgid "Modification prevented" +msgstr "Modificação impedida" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:246 +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:105 +msgid "Test Suites" +msgstr "Suites de Teste" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:349 +msgid "" +"There are unsaved modifications.\n" +"Do you want to save your changes before exiting?" +msgstr "" +"Existem modificações não salvadas.\n" +"Você deseja salvar todas as alterações e executar os testes?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:351 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:439 +msgid "Warning" +msgstr "Aviso" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:438 +msgid "" +"There are unsaved modifications.\n" +"Do you want to proceed without saving?" +msgstr "" +"Existem modificações não salvadas.\n" +"Você deseja prosseguir sem salvar?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:473 +msgid "Choose a directory containing Robot files" +msgstr "Escolha um diretório que contenha arquivos do Robot" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:538 +msgid "RIDE - Preferences" +msgstr "RIDE - Preferências" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:625 +msgid "Workspace modifications detected on the file system." +msgstr "Modificações no espaço de trabalho detectadas no sistema de arquivos." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:626 +msgid "Do you want to reload the workspace?" +msgstr "Você deseja recarregar o espaço de trabalho?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:627 +msgid "Answering will ignore the changes on disk." +msgstr "Responder irá ignorar as alterações no disco." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:629 +msgid "Answering will discard unsaved changes." +msgstr "Responder irá descartar as alterações não salvadas." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:630 +msgid "Files Changed On Disk" +msgstr "Arquivos Alterados no Disco" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "search unused keywords" +msgstr "pesquisar palavras-chave não utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "stop test run" +msgstr "parar execução de teste" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "preview" +msgstr "pré-visualizar" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "view ride log" +msgstr "ver registro do RIDE" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:793 +msgid "Shortcut keys for RIDE" +msgstr "Teclas de atalho para RIDE" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:831 +msgid "Show" +msgstr "Mostrar" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:832 +msgid "Hide" +msgstr "Ocultar" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:833 +msgid "Close" +msgstr "Fechar" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:99 +msgid "Preview" +msgstr "Pré-visualização" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:42 +msgid "Show preview of the current file" +msgstr "Mostrar a visualização do arquivo atual" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Pipes)" +msgstr "Texto (Barras)" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Spaces)" +msgstr "Texto (Espaços)" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:116 +msgid "Format" +msgstr "Formato" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:122 +msgid "Print" +msgstr "Imprimir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:39 +msgid "Search unused keywords" +msgstr "Pesquisar palavras-chave não utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:66 +msgid "" +"This dialog helps you finding unused keywords within your opened project.\n" +"If you want, you can restrict the search to a set of files with the filter." +msgstr "" +"Este diálogo ajuda a procurar palavras-chave não utilizadas dentro do " +"projeto aberto.\n" +"Se você quiser, pode restringir a pesquisa a um conjunto de arquivos com o " +"filtro." + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:70 +msgid "Filter is" +msgstr "O filtro está" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:71 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:242 +msgid "inactive" +msgstr "inativo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:84 +msgid "Filter" +msgstr "Filtro" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:91 +msgid "Use RegEx" +msgstr "Usar RegEx" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:93 +msgid "" +"Here you can define one or more strings separated by comma (e.g. common," +"abc,123).\n" +"The filter matches if at least one string is part of the filename.\n" +"If you don't enter any strings, all opened files are included" +msgstr "" +"Aqui você pode definir uma ou mais palavras separadas por vírgula (por " +"exemplo, comum, abc, 123).\n" +"O filtro corresponde se pelo menos uma sequência de caracteres fizer parte " +"do nome de arquivo.\n" +"Se você não inserir nenhuma palavra, todos os arquivos abertos serão " +"incluídos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:101 +msgid "Test case files" +msgstr "Arquivos de Casos de Teste" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:104 +msgid "Resource files" +msgstr "Arquivos de Recursos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:106 +msgid "Mode" +msgstr "Modo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "exclude" +msgstr "excluir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "include" +msgstr "incluir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:109 +msgid "Test the filter" +msgstr "Testar o filtro" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:139 +msgid "Keyword" +msgstr "Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:146 +msgid "Delete marked keywords" +msgstr "Apagar as Palavras-Chave marcadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:157 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:320 +msgid "Unused Keywords" +msgstr "Palavras-Chave Não Utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:163 +msgid "Abort" +msgstr "Abortar" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:247 +msgid "active" +msgstr "ativo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:270 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:353 +msgid "Unused Keywords (%d)" +msgstr "Palavras-Chave Não Utilizadas (%d)" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:279 +msgid "(None)" +msgstr "(Nenhum)" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:282 +msgid "" +"Keywords of the following files will be included in the search:\n" +"\n" +msgstr "" +"Palavras-chave dos seguintes arquivos serão incluídos na pesquisa:\n" +"\n" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:283 +msgid "Included files" +msgstr "Arquivos incluídos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:339 +msgid "Searching.%s \t- %s" +msgstr "Pesquisando. %s - %s" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:354 +msgid "Search finished - Found %d Unused Keywords" +msgstr "Pesquisa concluída - Encontradas %d Palavras-Chave Não Utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:402 +msgid "listing datafiles" +msgstr "listando arquivos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:405 +msgid "searching from " +msgstr "procurando em " + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:37 +msgid "View all tags" +msgstr "Ver todas as etiquetas" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:76 +msgid "Tag" +msgstr "Etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:77 +msgid "Occurrences" +msgstr "Ocorrências" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:82 +msgid "The List" +msgstr "A Lista" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:85 +msgid "Refresh" +msgstr "Atualizar" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:86 +msgid "Included Tag Search" +msgstr "Pesquisa de Etiquetas Incluídas" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:89 +msgid "Excluded Tag Search" +msgstr "Pesquisa de Etiquetas Excluídas" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:147 +msgid "" +"Total tests %d, Tests with tags %d, Unique tags %d\n" +"Currently selected tests %d" +msgstr "" +"Total de testes %d, Testes com etiquetas %d, Etiquetas únicas %d\n" +"Atualmente selecionados %d testes" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Select all" +msgstr "Selecionar todos" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:262 +msgid "Rename" +msgstr "Renomear" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests with this tag" +msgstr "Mostrar testes com esta etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests without this tag" +msgstr "Mostrar testes sem esta etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:261 +msgid "Renaming tag '%s'." +msgstr "Renomeando a etiqueta '%s'." + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Confirm" +msgstr "Confirmar" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Delete a tag '%s' ?" +msgstr "Apagar a etiqueta '%s' ?" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:209 +msgid "External Resources" +msgstr "Recursos Externos" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:344 +msgid "%s (excluded)" +msgstr "%s (excluído)" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:58 +msgid "'%s' - %d matches found - Searching%s" +msgstr "'%s' - %d correspondências encontradas - Pesquisando%s" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:63 +msgid "'%s' - %d matches" +msgstr "'%s' - %d correspondências" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:85 +msgid "Go to definition" +msgstr "Ir para a definição" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Location" +msgstr "Localização" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Usage" +msgstr "Utilização" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported Location" +msgstr "Localização importada" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported name" +msgstr "Nome importado" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Location" +msgstr "Importando Localização" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Name" +msgstr "Importando Nome" diff --git a/src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.mo b/src/robotide/locale/pt_PT/LC_MESSAGES/RIDE.mo new file mode 100644 index 0000000000000000000000000000000000000000..4c39cd6d3a956b3b84c961938e7e3f9614a38de4 GIT binary patch literal 31299 zcmds=eVAldUFWZP;iZTq8bTB#+)0?}9+>LROeQ4hOeX1c_sn#dbT{2SlQ-gcyXtm# zWvc2Hb*pBkvjGuz6;OA972VCQN>~JfWaSZ87F|(k)|V9oLBZw0^^FzeSzmTl_IcEO z`FwxpoLjf5XOdz6(hW&{?>+atpWpeto%3t{%Qs*4j);F=@eNV*ZD8-YdK>)niEoag zZ{qcnU=02q_(FZYI*P6Ut_7b5z6`t!yb;v*o55#;$HA+>Rq$D03aY*)_>JJ}!OOw# z_vvp3ck}*kQ2GB8_j#* zX}`;P~~0^?g2jlYCb*- z>iZYK8^OzVx&Ge_YMyQfzXiOwXVMd zYF@qsUJL#yI0jyRy=$)uif#^pTE_=K_45=6sYL6b+SvlNUOxb;-gkp913w6A9)26t z{QMCZgIC|+=;O?ef5N}lK-J#`S^DUEK(+g?z$?Lj3yS_e3Tk|x0!62P z0BYQS4646>2WlNIrBmwPH-akvt>8g$FZdw%?V$311k^hGBB=TKDya4UOHlK4B}A-t z$H2?LTfxtOclh_K_C(P#@7I8;Zxd9%Gf?H<2x^><`}g;O>i;kL^v{6G|NEfk`A1=KaGULyEozYMd_~_xl}G zeK&$^vFH|$uLiXar~UgIK+Wgl;9cN*z~_Kp2Gy^>07Vyn4T>(WU{clpF;MexyMJE+ zwH_Ow%AEyO?_=Pl;G00EHu`>0_5Tzoy7@U!`F|Buzdi|S9{Q{pyuy!umU~>UJm|yQ1yQloB%%yJ{!E`74CZlRJp4__3sAodEh=! z`ECU@pLcmY3TpipK$Uygzjr{D|4#q@Ch)nu|1kJG@CpC^6sZ1u7}WfJ0%Yq(e*|hi zF5d@#1S_ECX&h9&H-nnz!=TD9fa+Jxr#}K-!TY12zJD*sP@{hh@^ADj{5%i*D^T<+ zCZ_hE4{98{LG6=UL9N3osC~E&R=~$V_2YX%m3u3w{=OHC!4H6<+fRb3=Z`&J!lbL8 z*MTa(8`Qc?fa=dd5Y`gi1-=rjfzJmY2Q`0Bdi)Tm_I@7xM(|^x*8kI>`u`>HHt>If zqPzW*uHQFfKF8BiQ zPeJjmOKx`guLjkxmx5|%9MpVGfXa6(sCAt8-yZ^%KLM5R5sy7keB$+>+WQxv=HY$d zZt!Qoaqx>i{c;vb`{HVlE=4Z}V{i$48TeXIbnrvqUhpSDjrTu;YUgX9==-W$98Q47 z@4$mBTnb*xf;~RP-X-5p!R$mor|)#@(gn#9eJ7}Xd>GWYe!;*0DyVUP+Q0uHsBwK2 zd?ENZ{{5Toa^u|PaT2_e^}PcWUEO=P+m{Kb{(d*8dY=HV0Y3z)-Cy(Xp8>UwUjbGA zuRyKKb7tJQpASBt_i<3^w}9FwcYsVu^Z=;(-U_O}|I*`=pyur(;28K>kADlk>k|0Y zEP4p@^~*<)y_Z5K_e9a>$TtJgRlyf6I(_OGNRjCKK+W5yK#k+`pyuUI!7IVbmR$c| z0BXGB;0@rE$8QH;%KIBZNIrTOcq91RU=4iMs~z8Y1k^gc1C+e_Bq%z*V%hD>UEphY z-v_c)qBny~S@iRu_Hnf0=3@$EOGh)H=BW#c9-aiR20sX14}RRIe;L&H{tVQ5d<{hP zi!NDp$PcOR(z&;h?0eAK`H0H|^PFsODv3|Bje1gbwrLCxa= zsCrXSeEs#H`teRs`{4ti=-_kUH27aZ(bw2XSI-=%c8-HD0KWs&e(ZbvZczPy8+a}F zLGYWvUj?rLzu@2h0@QdexzEkxHK58}2dck!fa=FA_(E_6ycT=}JOsWGd>!};pyuRo~}8(cABW-v&kxyZL(wsC{w}R6p(q z*-}vp6n*_XsDAwlsB)hGUjqIKsQG{Px4ZP~K<)S2LA84VRJjJId1(9h*MYC({f(gN z{|Ko5ZiC_@zX^_mUjQ}kOHa9Wz75p8-URLkUj=GCUI)Gcd<&?4{S3GZ{4G%Z`5LHt zuKEr)4|_q4<3{jy@Lo{;eKV-`e+1M#ya&{J{T!(N|2nAtegV|_eF;?l{|0Iv{u+ES zctzss8wZu|5U6#W1=ZgNLDjbbs{S*e`u#oNOTl-8uL3^~YTd3{bNlaFQ2m?+RnH0V zI&cltc)kl%e|{9yJiQlG{l5Ud2>cW{2YwaQ{M`uC)BalnRc{X*1K;G|KM3By`^Q1$ z`wDnH_&1>HeM#N*qY7$%r$CK&1+0P(fid`IkM9Rn->-uj*Pnyx@7F+;`#+%Q;Mu8L z&uhR{-lswFgC7Fb?vtR#{}J$V@RvcY^Cv*f;~#^X$II5;cwYvpoku{8BL}sP{{&R| ze+H`FcYs>IANTLS1SEC4Iy>I<@*31Qz8lm${0Jzz`3X?#_e-GG`-`CF@oz!pe`(A0 z|2D9~`#dOmT=nm#L9Oo@Q0=`1)VO~P)I9t+sB%B)@n=ER{~tlM_a~tG@tn4kch`a1 zzssQ7c{8Z`-U&*+ya!bI&x3oxzXes#?#%JwSy1bggBsV{K+)9)z!>}#sB!%T_#2nf zw+?&yGW4(C8P&3o~Bqut0i(|Y`mUKhnf{~Sxsq;V3r8?`j9CtLB)otHp|g$N=2Ue3?)>_=k;mOMsmEEnxfSiseztMTr7+)8@2%{1Sc60c|dcAZQsY0|B2HrgAz^Jsh_Yo~joLxuJnvKKr zElp$5udK_AMu$r?r% zTJAW3P$8D|>sa`(qu46>W_Gr1+Xot`GY8GAJJNnw=#?~$!vdXbq-TvJwOwsmpjGxH zRW#bn5<81|jFYvj-!mHWOnuGfB5$i`3gpdal7uzd4}KUL$EX z9!=}}=(ne|5^aRCLo39xhxE8-H5=t5?6viz-|X?V*-tOf&GV4DAJ;|EdVF#8J=EXQ zcy`cw*!BP5=zC~{n$%VXe=ieG4y_I4I+Vnl-E{qq>otXjyic?OWZJcgQdVX4+DR%Y^O0U{Bta?^d)n?7FKddi@CX>U%J9?Ps`K`FQ|M1Ed zGA7A!S8w!S3~3&RF^+{XcD6QJh|PQ=>uyYr z9bWRU>Sdg}4s13yHkzPT@O(_`az8HF`?R|qjCIg}~YAdZoZ!>FeW;tBkRwHSj@mR*P@U z6=A*dbRu5t_d19m>QLWZ4wG|^#y&lgtYVN_QQkYG!rREXfs26id8WyWY{hbk8Nu%*P?MO~D zv9xeuTx%)1TB?Rc)X@yus_c%$`W-TviB*WItgCFbv>DB8W?BSPr-p2g3=79=Zt8*4 zX=P62A?Ml6Mr|{0rEJZ*s6Zwj?T6-V#*NaWnI>Du{SGK`rrD^Sju&60q9;7Tw6n9j z$VWdSguJAfAsZ@SJd-w?`EzG_-DY)Vv$5W*-c)>A&d&NTmL~0=H7CCf#8+ae_0S?^ z;XU1!FwZavAWc2P0v;#At@1`h|T7`Go-bW?rG@M6G={TUh6hGQrV-~ z%kfY zsdyId6XywPJcP5EvbIvD)B3JRDp54oc2*ucV~Q7f0>qmZ;3(0Y49pmdKq`xWg9YES zvbaEAZ|_@nXTXzYa)^Dr)Xg;CQ@9%D&QWPQX~xUm`puQpO=`Vj1;YDDZ-+!joRKpi z!RQFJ*-!3wdOcoj!+Z17N~Hx4I3{>P%{q}M@D%ra9}F%fC9S)6?~#y|Yb-(hbKfw7 zm$;i`^epSda3N=1YPmZ}PeaQm*ga{yd_*mD=sf@uojCKUC!HV?)+dL(vY2fym*qAV_BWv$V)@u9qAGm4nL}e9b z%leC&!dy4jjgrvE&=aWKRVqIcT0o&Xh-kDL6BUeAr&YGcAbBIqq`l>f<@;hW*sPPb z*|#J zv6oMrYc(I6hBS_dCU}2&R7}`$)Embk^XT5B*3AT`Q&ZsLzZ+2g##*KVA;JyrJ z-%ny2b`vk7Hk;@w)aK~kG#4i_3Fe=3)68&~LTN1dtvBvA?Yo=R$cyA=({1g8(z8y- zCKYN$0#9%Uq);dC&7g078tbSh<^;mb#o5iwt?sT(PHP-GvS(-^| zJ-IZk{~+^7`iYKJaiZh$@@l1dH_Fow-o&M(w;3&@XZ0UkBwx+k;N{qF;klktsM?J(?WJuj^xOj zMn$*r$}c5d2?}cA5%tXdj%qVYXlFF*hJt#H92P@7>s1CX%UR>T=qp-c{c#t94b(E@ zZdJr^x(;)Ot_cg4$vex6>E)|{++i(nW#q=~&%+wckbt>{Yj)O2alr{6ykH^0ZnVTU;ah_9+ zowVPLlG0}R>pLn7DxfZ9fb_Gt^dZZEq3RMnWm%U?^BsRikMD9yRA&4Hc4W$8FjbN1P5cKXPxm-`JG2@EqI@P@hxY zY*x(RzKATI7U%9haa6pjsmXN3Z1XU|P*E@KU>n2b?1@d+qZQs@Cd@fJyWO>}{alOy@;gJnauh_g^ggRGew90hyU+hRlk9oAJR`e6E z$i?6iPzq&0N}RfH+Jn48YWB|R+Kay8XB}T^bkcmUe_4@XtvAY;Pec#$1S7ZTVQ`6h zT`=ekc#3e=nihaDOGm60vbrefrJJ^zYD)t(09!F z<|P(lMZ!chE45WCf?bAb(3H9i5x3H$4VjD2TXLx0ZF}Z6I|;Uftq}6oTbwW2?FIADgPN3386{-8W*KW(=7%RtbZw2lLa;&MK8T zcUYG@-?Qx&Rf4(W)xJ1yWoTD3Q?F@lonjGtsM-9JpJ@l3dU7*srS4q8K9Lo>5g%2{ zPxKK{-D~jgyr$FLn4tH?V*3o5QU7R*x341M!N`Ujm*|ZsQ#yF_3uB@X0Vey&=ofoK zG)l&V|AL39Rno6YN01}7`wn-H2(#;Dn2r0+XRdpsPW;?b4hxE*4Z8aE*JFJ{Yv=}1 z4MoF@<)Xzd9MsszWo5y$O8OO=l2buA5GaqvXrqT4D%nUHZH5Nzdgq}G-Q2Vkgq`^n zMnsSeh()k2dF##l^b3nm3eG;OMCK^s4(k(Nfp&E6WUa9Q=^t!qV@!_mDu?4m6Oo;u z^2<4l7Ye&Mzg!g+`pk0cD(ytO@meuT_g+D7?xM7^TZh#T@4&!JJ7I6aNb@N(eQj{WL^<|F;&uyxpUFUq}jj(hOflt?b(sAODXu=g27Wf#Klt{ z**UeWF4W6hn{4h}hZAljgncRLyY^maqVCO(1Zi_>C8_a$9(x84^VYB)0n_7XH3u6;0ePdZSTJ1>bI@ZJz_{RcB3@Z>4W zU`Jgl>b0k!Cqb4P;)G?A_3Dp=28xub=T3m!m7_lPaaf?)f%CKR9dYzfyfU{iJHK#r zaQ|gx{^-K=@%Z6N>7!khiO=_CWp!>TUYNUo^}=5lPtGl0;OpX2`GY)$4_Eexh~vj% zgJy!5EKe*Sw01St?!n3)*&3u6Tp-s2m1?!hzT4ANQN-h*g8M6bX0pzfQat#YXDWM^ zXgK9dz|iQ)${z32cq}d+oJ-oQR=zuLE@l0Ldve|I8`|t)HS6|w!3#v5>W=TnO&F6U z4xlgoUH)y)YHlbM@V@cNfOYtm1_>8x>tLyn@f)fglzQ6s4%*oKY=p8SuVF~8hw~Lb zHzD`P>u~Pbim%KMX80}SE$Z~Se3kw!)qGi>tta|mwok*re(vlwD`zBeGU z1+q>rubx_%yU(i8c9ABvYmYf2B-EV3PCiSKOq3E-+%7Y*H>hyYS9oOp_}r<*rMU%H zot52FxHUp;h3BGNB{Cy%qE~aI^PaGNMdgdfXXlnjtDg&p0h*Q2g7p{XKZ8@tJ0AJu z2fes_@AUCg>gvegS7hkZx}exIpu%$#Cq2TIGYdm>Q2PF5fr3 zJX=&6C0vxj&j#CTVd+faMXE$;GwH~YrRxVuQR|%R1GMYn2FBF_0-il z9X1axQ{%|h`Fi!FyRk6kXz0Mi{>swgYSkDu48A%`lT-158xI_;-ngHCp+O-BLM@&Qe|NV&~KMtUDDS+kUdy$aG}b#QJ1RhcKZU*{O|? zaMxzmYcX58fp?hl?TtRp-s)>mZ-Hj3b#-gc>?@dt)lTb*<5>9Od{; zH+bsRySHdN)w8|P z^dtRxQo5LL9ag3F?f189jih{OpHl=>3N8>)MquIa;=&R#Hum@eGt_Sl?(t90 zE{T>L$7tzoIdF{U4fcAIIVtJaYY z%vzr1vAhtPVIwX2Q|{{pZc1ZVCw33Z*0^EkrwKi^ah2LE1qpB}>$0g=cvj67TRUH& zopA$4xbnqWAA02=dVtZ*?X2bQVtrJLhRm@!5e1DJ+>s*LU<&jdqK&gc!boFtLbZmQ4r^FGK`ZLPU+`KH;@#BK*kY8Xk({OhwNiM?NPjd3ieW(>#>3056wjk{<=1pAn^1qTJdhurw@o$00YJCL z?KP%FDHmvGMK|1rx~r{#p26{E%LOQGKOsJ+U1Dw8?sc1m%PjCJx6wI?^+5{MNI*zl z6TOPqWw2QtLv*qIPP|2H=r3BMkOhyXND}@|oC1?1;>mQ5!p^$D0G32kHbO3GjSPjF z2h=u*VJOz;qPAi@KD-X|VH;oU_8S`Z({II#-F}13_YZ034NZit8>*AsmZt1G`W4{~ zbqG#8*0$%<1wDLS z;S>F*t4Z+#7acw^OW2r#Cv=Af9Tp{TD6gauDn8#O;`F_Z#A;_}6hpw~k=i>kIxN%N zIUm=gpmrCV%t_VJ+#RRWBLST@>Xma7h^C@JoyBDr8^MkSizEI>M2Dnejh zCS{*O+~-#9=PBU$kLpc&eN%{RX7+jB!e-ch0*2C)<$*4Q?;{IhPnxS}2IW;g0Nd2I z0k_{JLold%6I_;)InmWf}aTSwpIhV(SoN60GQSW$Zs z3?drv7e<&v8}ZPlVj7H4iD@b>8)zxD*W{F<*HR4`8U5x1FGJf+7y2}~aDUkBB{q7= zibswO8FySt8#n?mY-+wtVgH7MGe1$BkBd^u-HtTB&>P+1J}=&*J}tUg7@A{7r!zdZ zAe}G)@54;kYix*2P6p}Q^7)gCb(b2Ria3JJN+^(Ax;{H=`&uXsLD$-Tk`6-;QW?ZB zM6DJNz#9|gwtsXSC7;F|4DT``Po&*L2SlG(`DstZ%;yHwzbiUo#2L*vH9!dKb4ZQ@n6~bS^GRwgmG-n58a4R0V2~#w@+9CC1Flq^z(V%EQ(5xb2oSUh5$cu|5k~7?m zCVr-t@wv(9qD8ws14~CY*nUD@aryH&-*8SeCJ8n@NM3}qhi~<4knY_ePQuBHil$Kt zu5pW#PD-!!WHhm(a3`sEO(@jFYkfB2Y)(Qm<}NpDz;_Ha$HddfNs|_y10w>aLIjTS zlpVHSh{4GSe-abuW_^N)NBuQHsOda|NmeAz;u`WLPRxa>XAw+@!f~Tyg1o&H&3l&U zVsh-WV^DV&pEM?261I=tH9Jk%LlEEQZ*p0jSA-GTXdwL*U#;yOGupiRN0PLyGuuxg z@$hhrmz-czbhXuQ5l2%5?G)>865fk$1U8dl&QW+RRAb_Sns+pce~AeelL_k0Kxj-< zh7BCIVNnFlON^AU5flU93MQh2_!3>tb2N<1Un5Y36g+Mcc6L%EFE)4fYf_LU!Fo)I z7eG$UX{8*m*t!ReNCf0g@o=fBA{6LaBR95R;s?@5SiVq-HBpL*b@SE3(}tB#k%5GA z!CZzu`I0^6wZKrl+uypQ5a3Z>fzdjE#xNe zW{74ui&;SKM)QD%Y za3=NvVN5!vVozv`*_F0zpKV3ao;Z=|bc3iS?c{rzDg6wjvfra-U2{{Bk8IKq-88+H zG|pus@l7cHF4=DL=mwB4a$p-C-Z za*O%EE6Yc6;*@23NVA0%leW)f%`+U7bDdowOPK3zSLwiXQV0iHJXGvdL~Z_^l?O@h zIK&EKX`r{$mbW9Rss*#hXe%G=O<1y1%Goj{W7kgZbQ7IMVR_|mgWxIl06P(jwUsD- zirT}0p%IUnY3}gRS7UYE46F+3ux3DVN1f1Q4!LlH-1uV8| zsNBF$V&O)1iYnqmbxXT!W4TCRXJ(&ykSbfKU&?Epjg}zJ_2dk{Ves zYmVx@KV)>Pu+-gtvdS%hlKCyx+C`zpR?v-^5S0}stS}&)Tw=?uYcsoD`aofV$kI@Z zmQeJ9ybBM33p%AcBx{?EsOl4Z)6Oef#tPoD6UdU5Xsh6kCHWfMPd2TC7cN8>YrSf; zGWsq>3ENeH$O@t_a>JxORfPF9FUvE0m%_p_6zAQ6zT;U7xEVHNoJk!lYZ~;M!R%&O z9?@aUO+4Uf-Ub&%Gi}@uY~5+4oieyP_f;h=>4rPClgNjRnW|wlr7hq_ITd7RAxua8 zX+;KXa&5Z7JDY68H5LpDR8?|9eb?U!0?n~ zE*wvrRb=W%5WhQ9Adtdn7^&AZEx%D@5Bhx2=pYvDB#d1x3~@-O;nEAUUnAvViV7c- z7^UZ37iJ`-Eb`4^jCwU&_RMNp!&xBSjAKoHmwpSQ2qZx8$+y)fbVl=Nmj8YJfQN1#-7{LxA3botVNLwD5_IW!2?FRHN8lO7E zzdPY_fe*0b6&=(Zk@#*HH+7($L3DN#0fFzYcd!C*k{wo-OexuS;#y=;W0Y}@ zP2*%Zd3|LSM4*%bt|5j9I%Xrrtg|~9)}E}El5f@Qs)YM`jIvVHE2V}%2O{-HX8@*S zNYidnx%M{4C$jQrmg`XEh`YhKT(>=!)zFHA8K(ew|Eac?b1yLr8kI{6Po*L+Eb7jb zd`__1rfvHBH~xIlMc^q;3VENK?Pn~^R>|zJ;_g+0X^_T1e{{QKUxj8v{9WJ=w$M#3 zEXCs%lDF4Urym00Bjh$ON}>54`=wA$`fKm^*(uQW~zQe@h1eh&0oM*=j)xxf9LgqM9l-fkl%W4-+ec zbgjkzmx_E}M|UX{oUz}0PE#>b$rF@8OK6t)eRU}L}2gY^K3PbN4;Hp`P8bdj6)98XY43hDT`OZgt9QLuS$^{M1x!gxSyWqfJJb8&(@UG^- zyl`gK;Qh{lfb)$}SPIh#9q(YY5Is1j13(tUMG;!%=S#B~!i-?R!dYEBxhO(i%W4ot zhyW;jCBY07cX#20X1_Tlv1Bt1n|C`tn<}?6)%?QJW0EuJhANKlOV@^@4(SY?_j(=Hg{%VD^r8DldmX)w`8T|=}(jhSRxQy0uq7kWQ$vbjf zWa8Yy$@%F8w#v%90uucn=;mIpg(i-CRfOUNKdr1#$=uNs^9!qU=PkW5eSDSDOVi4E z{_2-~?Q$(9-G?jF?LFFdijA=S$50DF!1fY{9Sax2S!^60;WKt}rDBN^>!5N%aGS(&!;I=&YV z1l5H=M#rqY*Go-%_609&+94>SEBJj#8_a`6H4d{n>-wO)KOc3B_k8h}}m6oy`JE^$IUC!m`uH7&wuRpMX z*7S!TT7@ElUchi=x_WJ6uswBm}X<{rN8Y16a zl2vwoy%cD#tRUr5ttdZ2a)ev%a_1uMUf+4L5NWwfQl8;a1OEf9EzTpw;WlwfSzg(-pkN0br&4*{-;ETZov>5Y-jMjF5^6?GwgI9c%U#z zAIRyZBt#@QwWt!?a+-l?WxWBZXcDWWy!u5-Q8b-!Lq_W3=itr4-M}-4pj7Sx6^m9XMO80Xr7v z-7e3GpYG&+kw%Uh8bL++|8S{tVr?2M;(\n" +"Language-Team: helioxentric@gmail.com\n" +"Language: pt_PT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Crowdin-Project: robotframework-ride\n" +"X-Crowdin-Project-ID: 637294\n" +"X-Crowdin-Language: pt-PT\n" +"X-Crowdin-File: RIDE.pot\n" +"X-Crowdin-File-ID: 2\n" + +#: /home/helio/github/RIDE/src/robotide/application/application.py:383 +msgid "Found Robot Framework version %s from %s." +msgstr "Encontrou-se Robot Framework versão %s em %s." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "New development version is available." +msgstr "Está disponível uma nova versão de desenvolvimento." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:111 +msgid "Upgrade?" +msgstr "Atualizar?" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid " with:" +msgstr " com:" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:112 +msgid "You may install version " +msgstr "Você pode instalar a versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:113 +msgid "Click OK to Upgrade now!" +msgstr "Clique em OK para Atualizar agora!" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:114 +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:221 +msgid "" +"After upgrade you will see another dialog informing to close this RIDE " +"instance." +msgstr "" +"Depois da atualização, você verá outro diálogo informando para fechar esta " +"instância do RIDE." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "No Upgrade Available" +msgstr "Não está disponível uma Atualização" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:121 +msgid "You have the latest version of RIDE." +msgstr "Você tem a versão mais recente do RIDE." + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:122 +msgid " Have a nice day :)" +msgstr " Tenha um bom dia :)" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "An error occurred when installing new version" +msgstr "Ocorreu um erro ao instalar a nova versão" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:179 +msgid "Failed to Upgrade" +msgstr "Falha em Atualizar" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "Completed Upgrade" +msgstr "Atualização Completada" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:188 +msgid "You should close this RIDE (Process ID = " +msgstr "Você deve fechar este RIDE (ID do Processo = " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:208 +msgid "Update available" +msgstr "Atualização disponível" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid " available from " +msgstr " disponível desde " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:216 +msgid "New version " +msgstr "Nova versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:217 +msgid "See this version " +msgstr "Ver esta versão " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:219 +msgid "You can update with the command:" +msgstr "Você pode atualizar com o comando:" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:220 +msgid "Or, click Upgrade Now" +msgstr "Ou, clique Atualizar Agora" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:222 +msgid "See the latest development " +msgstr "Ver o desenvolvimento mais recente " + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:227 +msgid "" +"I'm using another method for RIDE updates\n" +" and do not need automatic update checks" +msgstr "" +"Estou a usar outro meio para atualizar o RIDE\n" +" e não preciso de verificações de atualizações automáticas" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:232 +msgid "remind me later" +msgstr "lembrar-me mais tarde" + +#: /home/helio/github/RIDE/src/robotide/application/updatenotifier.py:238 +msgid "Upgrade Now" +msgstr "Atualizar Agora" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:424 +msgid "Log options" +msgstr "Opções de Registo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:432 +msgid "Output directory: " +msgstr "Diretoria de resultados: " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:450 +msgid "Add suite name to log names" +msgstr "Acrescentar o nome da Suite aos nomes dos registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:453 +msgid "Add timestamp to log names" +msgstr "Acrescentar a hora e data aos nomes dos registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:456 +msgid "Save Console and Message logs" +msgstr "Salvar os registos da Consola e Mensagens" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:474 +msgid "Select Logs Directory" +msgstr "Selecionar a Diretoria de Registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:496 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Arguments" +msgstr "Argumentos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:508 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:534 +msgid "Arguments for the test run. Arguments are space separated list." +msgstr "" +"Argumentos para a execução do teste. O argumentos são listas com espaços " +"como separador." + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:553 +msgid "Does not execute - help or version option given" +msgstr "Não irá executar - foi indicada uma opção 'help' ou 'version'" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:558 +msgid "Unknown option(s):" +msgstr "Opção desconhecida:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:564 +msgid "Tests filters" +msgstr "Filtro dos Testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:573 +msgid "Only run tests with these tags:" +msgstr "Executar apenas testes com estas etiquetas:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:576 +msgid "Skip tests with these tags:" +msgstr "Ignorar testes com estas etiquetas:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/runprofiles.py:672 +msgid "Script to run tests:" +msgstr "Script para executar testes:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:98 +msgid "Stop a running test" +msgstr "Parar uma execução de teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:99 +msgid "Step over" +msgstr "Saltar passo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:161 +msgid "A plugin for running tests from within RIDE" +msgstr "Um Plugin para correr testes a partir de RIDE" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +msgid "Run Tests" +msgstr "Executar Testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:256 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:54 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:41 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:36 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:53 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:738 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:750 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +msgid "Tools" +msgstr "Ferramentas" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:258 +msgid "Run the selected tests" +msgstr "Executar os testes selecionados" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:260 +msgid "Run Tests with Debug" +msgstr "Executar Testes em modo Debug" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:263 +msgid "Run the selected tests with Debug" +msgstr "" +"Executar os testes selecionados com o nível de registo DEBUG (depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:266 +msgid "Stop Test Run" +msgstr "Parar Execução de Teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:350 +msgid "[ SENDING STOP SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PARAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:357 +msgid "[ SENDING PAUSE SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PAUSAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:363 +msgid "[ SENDING CONTINUE SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL CONTINUAR ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:369 +msgid "[ SENDING STEP NEXT SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL PASSO SEGUINTE ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:375 +msgid "[ SENDING STEP OVER SIGNAL ]\n" +msgstr "[ ENVIANDO SINAL SALTAR PASSO ]\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:408 +msgid "command: %s\n" +msgstr "comando: %s\n" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:478 +msgid "" +"There are unsaved modifications.\n" +" Do you want to save all changes and run the tests?" +msgstr "" +"Existem modificações não guardadas.\n" +" Você quer salvar todas as alterações e correr os testes?" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:480 +msgid "Unsaved Modifications" +msgstr "Alterações Não Salvadas" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:489 +msgid "" +"No tests selected. \n" +"Continue anyway?" +msgstr "" +"Nenhuns testes selecionados. \n" +"Continuar na mesma?" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:491 +msgid "No tests selected" +msgstr "Sem testes selecionados" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:548 +msgid "" +"\n" +"Test finished {}" +msgstr "" +"\n" +"Teste terminado {}" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:583 +msgid "Messages log exceeded 80% of process memory, stopping for now..." +msgstr "" +"O registo de mensagens excede 80% da memória do processo, parando por " +"agora..." + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:741 +msgid "Start" +msgstr "Iniciar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:742 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:745 +msgid "Start robot" +msgstr "Iniciar Robot" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:743 +msgid "Start running the robot test suite" +msgstr "Iniciar execução da suite de testes Robot" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:744 +msgid "Debug" +msgstr "Debug (depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:746 +msgid "Start running the robot test suite with DEBUG loglevel" +msgstr "" +"Iniciar execução da suite de testes Robot no nível de registo DEBUG " +"(depuração)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:748 +msgid "Stop" +msgstr "Parar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:752 +msgid "Pause" +msgstr "Pausar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:754 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:755 +msgid "Pause test execution" +msgstr "Pausar a execução do teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:756 +msgid "Continue" +msgstr "Continuar" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:759 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:760 +msgid "Continue test execution" +msgstr "Continuar a execução do teste" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:761 +msgid "Next" +msgstr "Seguinte" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:762 +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:763 +msgid "Step next" +msgstr "Saltar seguinte" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:788 +msgid "Execution Profile: " +msgstr "Perfil de Execução: " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:791 +msgid "Choose which method to use for running the tests" +msgstr "Escolher qual o método a usar para executar os testes" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:798 +msgid "Open Logs Directory" +msgstr "Abrir a Diretoria de Registos" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:800 +msgid "View All Logs in Explorer" +msgstr "Ver Todos os Registos no Explorador" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:801 +msgid " Report" +msgstr " Relatório" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:802 +msgid "View Robot Report in Browser (CtrlCmd-R)" +msgstr "Ver o Relatório do Robot no navegador Web (CtrlCmd-R)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:804 +msgid " Log" +msgstr " Registo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:805 +msgid "View Robot Log in Browser (CtrlCmd-L)" +msgstr "Ver o Registo do Robot no navegador Web (CtrlCmd-L)" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:812 +msgid " Autosave " +msgstr " Salvar automático " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:813 +msgid "Automatically save all changes before running" +msgstr "Salvar automaticamente todas as modificações antes da execução" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:818 +msgid " Pause after failure " +msgstr " Pausar após falha " + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:819 +msgid "Automatically pause after failing keyword" +msgstr "Pausar automaticamente após falha de palavra-chave" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:927 +msgid "Console log" +msgstr "Registo de Consola" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:931 +msgid "Message log" +msgstr "Registo de Mensagens" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1054 +msgid "Starting test:" +msgstr "Iniciando o teste:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1059 +msgid "Ending test:" +msgstr "Terminando o teste:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1067 +msgid "UNKNOWN STATUS:" +msgstr "ESTADO DESCONHECIDO:" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1095 +msgid "<< PAUSED >>" +msgstr "<< PAUSADO >>" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1100 +msgid "<< CONTINUE >>" +msgstr "<< CONTINUAR >>" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1141 +msgid "" +"There isn't logs directory. \n" +"Please, run the tests and try again" +msgstr "" +"Não existe diretoria de registo. \n" +"Por favor, corra testes e tente novamente" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1143 +msgid "No logs directory" +msgstr "Sem diretoria de registo" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1228 +msgid "elapsed time: %s pass: %s skip: %s fail: %s" +msgstr "tempo decorrido: %s passado: %s saltado: %s falhado: %s" + +#: /home/helio/github/RIDE/src/robotide/contrib/testrunner/testrunnerplugin.py:1248 +msgid " current keyword: " +msgstr " palavra-chave corrente: " + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:41 +msgid "" +"[Navigate]\n" +" !Go &Back | Go back to previous location in tree | Alt-%s | " +"ART_GO_BACK\n" +" !Go &Forward | Go forward to next location in tree | Alt-%s | " +"ART_GO_FORWARD\n" +" " +msgstr "" +"[Navegação]\n" +"!Recuar | Recuar para a localização anterior | Alt-%s | ART_GO_BACK\n" +"!Avançar | Avançar para a localização seguinte | Alt-%s | ART_GO_FORWARD\n" +" " + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +msgid "Add Tag to selected" +msgstr "Adicionar Etiqueta aos Selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:55 +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:108 +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Edit" +msgstr "Editar" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:57 +msgid "Clear Selected" +msgstr "Limpar Selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Add Tag To Selected" +msgstr "Adicionar Etiqueta aos Selecionados" + +#: /home/helio/github/RIDE/src/robotide/controller/ui/treecontroller.py:70 +msgid "Enter Tag Name" +msgstr "Introduza a Etiqueta" + +#: /home/helio/github/RIDE/src/robotide/editor/__init__.py:28 +msgid "" +"[Edit]\n" +"&Undo | Undo last modification | Ctrlcmd-Z\n" +"&Redo | Redo modification | Ctrlcmd-Y\n" +"---\n" +"Cu&t | Cut | Ctrlcmd-X\n" +"&Copy | Copy | Ctrlcmd-C\n" +"&Paste | Paste | Ctrlcmd-V\n" +"&Insert | Insert | Shift-Ctrl-V\n" +"&Delete | Delete | Del\n" +"---\n" +"Comment Rows | Comment selected rows | Ctrlcmd-3\n" +"Comment Cells | Comment cells with # | Ctrlcmd-Shift-3\n" +"Uncomment Rows | Uncomment selected rows | Ctrlcmd-4\n" +"Uncomment Cells | Uncomment cells with # | Ctrlcmd-Shift-4\n" +"---\n" +"Insert Cells | Insert Cells | Ctrlcmd-Shift-I\n" +"Delete Cells | Delete Cells | Ctrlcmd-Shift-D\n" +"Insert Rows | Insert Rows | Ctrlcmd-I\n" +"Delete Rows | Delete Rows | Ctrlcmd-D\n" +"Move Rows Up | Move Rows Up | Alt-Up\n" +"Move Rows Down | Move Rows Down | Alt-Down\n" +"[Tools]\n" +"Content Assistance (Ctrl-Space or Ctrl-Alt-Space) | Show possible keyword " +"and variable completions | | | POSITION-70\n" +msgstr "" +"[Editar]\n" +"An&ular | Anular a alteração anterior | Ctrlcmd-Z\n" +"&Refazer | Repetir a alteração anterior | Ctrlcmd-Y\n" +"---\n" +"Cor&tar | Cortar | Ctrlcmd-X\n" +"&Copiar | Copiar | Ctrlcmd-C\n" +"Colar | Colar | Ctrlcmd-V\n" +"&Inserir | Inserir | Shift-Ctrl-V\n" +"Apagar | Apagar | Del\n" +"---\n" +"Comentar Linhas | Comentar as linhas selecionadas | Ctrlcmd-3\n" +"Comentar Células | Comentar as células com # | Ctrlcmd-Shift-3\n" +"Descomentar Linhas | Descomentar as linhas selecionadas | Ctrlcmd-4\n" +"Descomentar Células | Descomentar as células com # | Ctrlcmd-Shift-4\n" +"---\n" +"Inserir Células | Inserir Células | Ctrlcmd-Shift-I\n" +"Apagar Células | Apagar Células | Ctrlcmd-Shift-D\n" +"Inserir Linhas | Inserir Linhas | Ctrlcmd-I\n" +"Apagar Linhas | Apagar Linhas | Ctrlcmd-D\n" +"Mover Linhas Acima | Mover Linhas Acima | Alt-Up\n" +"Mover Linhas Abaixo | Mover Linhas Abaixo | Alt-Down\n" +"[Ferramentas]\n" +"Assistência de Conteúdo (Ctrl-Space ou Ctrl-Alt-Space) | Mostra possíveis " +"palavras-chave e completar variáveis | | | POSITION-70\n" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:162 +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:394 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:186 +msgid " (READ ONLY)" +msgstr " (APENAS LEITURA)" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:224 +msgid "Settings" +msgstr "Definições" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:343 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Source" +msgstr "Origem" + +#: /home/helio/github/RIDE/src/robotide/editor/editors.py:381 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:207 +msgid "Find Usages" +msgstr "Procurar Utilizações" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:71 +msgid "Create Keyword" +msgstr "Criar Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:72 +msgid "Extract Keyword" +msgstr "Extrair Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:73 +msgid "Extract Variable" +msgstr "Extrair Variável" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:74 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "Rename Keyword" +msgstr "Renomear Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:75 +msgid "Find Where Used" +msgstr "Procurar Onde Utilizado" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:76 +msgid "JSON Editor\tCtrl-Shift-J" +msgstr "Editor JSON\tCtrl-Shift-J" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:78 +msgid "Go to Definition\tCtrl-B" +msgstr "Ir para Definição\tCtrl-B" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:80 +msgid "Undo\tCtrl-Z" +msgstr "Anular\tCtrl-Z" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:81 +msgid "Redo\tCtrl-Y" +msgstr "Refazer\tCtrl-Y" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:83 +msgid "Make Variable\tCtrl-1" +msgstr "Fazer Variável\tCtrl-1" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:84 +msgid "Make List Variable\tCtrl-2" +msgstr "Fazer Variável Lista\tCtrl-2" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:85 +msgid "Make Dict Variable\tCtrl-5" +msgstr "Fazer Variável Dicionário\tCtrl-5" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:87 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:292 +msgid "Comment Cells\tCtrl-Shift-3" +msgstr "Comentar Células\tCtrl-Shift-3" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:88 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:293 +msgid "Uncomment Cells\tCtrl-Shift-4" +msgstr "Descomentar Células\tCtrl-Shift-4" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:89 +msgid "Move Cursor Down\tAlt-Enter" +msgstr "Mover Cursor Abaixo\tAlt-Enter" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:91 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:284 +msgid "Comment Rows\tCtrl-3" +msgstr "Comentar Linhas\tCtrl-3" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:92 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:285 +msgid "Uncomment Rows\tCtrl-4" +msgstr "Descomentar Linhas\tCtrl-4" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:93 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:286 +msgid "Move Rows Up\tAlt-Up" +msgstr "Mover Linhas Acima\tAlt-Up" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:94 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:287 +msgid "Move Rows Down\tAlt-Down" +msgstr "Mover Linhas Abaixo\tAlt-Down" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:95 +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:288 +msgid "Swap Row Up\tCtrl-T" +msgstr "Trocar a Linha para Cima\tCtrl-T" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:289 +msgid "Insert Rows\tCtrl-I" +msgstr "Inserir Linhas\tCtrl-I" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:290 +msgid "Delete Rows\tCtrl-D" +msgstr "Eliminar Linhas\tCtrl-D" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:747 +msgid "" +"Keyword was not detected by RIDE\n" +"
    Possible corrections:
    \n" +"
      \n" +"
    • Import library or resource file containing the keyword.\n" +"
    • For library import errors: Consider importing library spec " +"XML\n" +" (Tools / Import Library Spec XML or by adding the XML file with " +"the\n" +" correct name to PYTHONPATH) to enable keyword completion\n" +" for example for Java libraries.\n" +" Library spec XML can be created using libdoc tool from Robot " +"Framework.
    • \n" +"
    " +msgstr "" +"Palavra-chave não detetada pelo RIDE\n" +"
    Correções possíveis:
    \n" +"
      \n" +"
    • Importar a biblioteca ou ficheiro de recursos que contenha a " +"palavra-chave.
    • \n" +"
    • Para erros de importação de bibliotecas: Considere importar " +"a especificação de biblioteca XML\n" +" (Ferramentas / Importar Especificação de Biblioteca XML ou por " +"adicionar o ficheiro XML com o\n" +" nome correto a PYTHONPATH) para ativar o completar de palavras-" +"chave\n" +" por exemplo para bibliotecas Java.\n" +" A especificação de biblioteca XML pode ser criada usando a " +"ferramenta libdoc de Robot Framework.
    • \n" +"
    " + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:983 +msgid "New name" +msgstr "Novo nome" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:995 +msgid "Save" +msgstr "Salvar" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:996 +msgid "Cancel" +msgstr "Cancelar" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Error in JSON:" +msgstr "Erro em JSON:" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1022 +msgid "Save anyway?" +msgstr "Salvar mesmo assim?" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1023 +msgid "Validation Error!" +msgstr "Erro de Validação!" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1164 +msgid "Please select what you want to check for usage" +msgstr "Por favor selecione o que pretende verificar a utilização" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1167 +msgid "Complete cell content" +msgstr "Completar conteúdo de célula" + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1168 +msgid "Variable " +msgstr "Variável " + +#: /home/helio/github/RIDE/src/robotide/editor/kweditor.py:1180 +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:513 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:49 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:127 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:184 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:98 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:160 +msgid "Search" +msgstr "Procurar" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Down\tCtrl-Down" +msgstr "Mover Abaixo\tCtrl-Down" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +msgid "Move Up\tCtrl-Up" +msgstr "Mover Acima\tCtrl-Up" + +#: /home/helio/github/RIDE/src/robotide/editor/listeditor.py:32 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Delete" +msgstr "Apagar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:100 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Clear" +msgstr "Limpar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +msgid "Variable" +msgstr "Variável" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Comment" +msgstr "Comentário" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:479 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Value" +msgstr "Valor" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Dict" +msgstr "Adicionar Dicionário" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add List" +msgstr "Adicionar Lista" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:480 +msgid "Add Scalar" +msgstr "Adicionar Escalar" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Import" +msgstr "Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:567 +msgid "Name / Path" +msgstr "Nome / Caminho" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Import Failed Help" +msgstr "Ajuda da Falha na Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Library" +msgstr "Biblioteca" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +msgid "Resource" +msgstr "Recurso" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:568 +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:654 +msgid "Variables" +msgstr "Variáveis" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:586 +msgid "Add Import" +msgstr "Adicionar Importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:620 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:33 +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:62 +msgid "Import Library Spec XML" +msgstr "Importar Especificação de Biblioteca XML" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "" +"
    Possible corrections and notes:
    \n" +"
    " +msgstr "" +"
    Possíveis correções e notas:
    \n" +"
      \n" +"
    • A falha de importação é mostrada com a cor vermelha.
    • \n" +"
    • Ver Ferramentas / Ver Registo do RIDE para informação " +"detalhada acerca da falha.
    • \n" +"
    • Se a importação contiver uma variável que RIDE não tenha " +"inicializado, considere acrescentar a variável\n" +" à tabela de variáveis com um valor por omissão.
    • \n" +"
    • Para falhas na importação de biblioteca: Considere importar " +"a especificação de biblioteca XML (Ferramentas / Importar Especificação de " +"Biblioteca XML ou por\n" +" adicionar o ficheiro XML com o nome correto a PYTHONPATH) para " +"ativar completar palavras-chave\n" +" por exemplo para bibliotecas Java.\n" +" A especificação de biblioteca XML pode ser criada usando a " +"ferramenta libdoc de Robot Framework.\n" +" Para mais informação ver \n" +" wiki.\n" +"
    • \n" +"
    " + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:660 +msgid "Import failure handling" +msgstr "Gestão de falha de importação" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:707 +msgid "Metadata" +msgstr "Metadados" + +#: /home/helio/github/RIDE/src/robotide/editor/settingeditors.py:708 +msgid "Add Metadata" +msgstr "Adicionar Metadados" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "ERROR: Data sanity check failed!" +msgstr "ERRO: Falha na verificação!" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:367 +msgid "Error at line" +msgstr "Erro na linha" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:368 +msgid "Reset changes?" +msgstr "Repor Alterações?" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:369 +msgid "Can not apply changes from Text Editor" +msgstr "Não se conseguiu aplicar alterações do Editor de Texto" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:497 +msgid "Apply Changes" +msgstr "Aplicar Alterações" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:523 +msgid "Syntax colorization disabled due to missing requirements." +msgstr "A colorização de sintaxe está desativada por falha nos requisitos." + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:524 +msgid "Get help" +msgstr "Obter ajuda" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:539 +msgid "" +"

    Syntax colorization

    \n" +"

    \n" +" Syntax colorization for Text Edit uses Pygments syntax highlighter.\n" +"

    \n" +"

    \n" +" Install Pygments from command line with:\n" +"

    \n"
    +"            pip install pygments\n"
    +"        
    \n" +" Or:\n" +"
    \n"
    +"            easy_install pygments\n"
    +"        
    \n" +" Then, restart RIDE.\n" +"

    \n" +"

    \n" +" If you do not have pip or easy_install,\n" +" follow these instructions.\n" +"

    \n" +"

    \n" +" For more information about installing Pygments, see the site.\n" +"

    \n" +" " +msgstr "" +"

    Colorização de Sintaxe

    \n" +"

    \n" +" A Colorização de Sintaxe para o Editor de for Texto usa o realce de " +"sintaxe, Pygments.\n" +"

    \n" +"

    \n" +" Instale Pygments a partir da linha de comandos com:\n" +"

    \n"
    +"            pip install pygments\n"
    +"        
    \n" +" Ou:\n" +"
    \n"
    +"            easy_install pygments\n"
    +"        
    \n" +" Depois, reinicie o RIDE.\n" +"

    \n" +"

    \n" +" Se você não tiver o pip ou easy_install,\n" +" siga estas instruções.\n" +"

    \n" +"

    \n" +" Para mais informação acerca a instalação do Pygments, ver este sítio.\n" +"

    \n" +" " + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:564 +msgid "Getting syntax colorization" +msgstr "Obtendo colorização de sintaxe" + +#: /home/helio/github/RIDE/src/robotide/editor/texteditor.py:662 +msgid "No matches found." +msgstr "Sem correspondências." + +#: /home/helio/github/RIDE/src/robotide/log/log.py:45 +msgid "RIDE Log" +msgstr "Registo do RIDE" + +#: /home/helio/github/RIDE/src/robotide/log/log.py:86 +msgid "View RIDE Log" +msgstr "Ver o Registo do RIDE" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:45 +msgid "Parser Log" +msgstr "Registo do Interpretador" + +#: /home/helio/github/RIDE/src/robotide/parserlog/parserlog.py:85 +msgid "View Parser Log" +msgstr "Ver o Registo do Interpretador" + +#: /home/helio/github/RIDE/src/robotide/postinstall/desktopshortcut.py:55 +msgid "Create RIDE Desktop Shortcut" +msgstr "Criar Atalho para o RIDE no Ambiente de Trabalho" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:27 +msgid "" +"The specified command string will be split from whitespaces into a command\n" +"and its arguments. If either the command or any of the arguments require\n" +"internal spaces, they must be written as ''.\n" +"\n" +"The command will be executed in the system directly without opening a " +"shell.\n" +"This means that shell commands and extensions are not available. For " +"example,\n" +"in Windows batch files to execute must contain the '.bat' extension and " +"'dir'\n" +"command does not work.\n" +"\n" +"Examples:\n" +" robot.bat --include smoke C:\\my_tests\n" +" svn update /home/robot\n" +" C:\\ProgramFiles\\App\\prg.exe argumentwithspace,\n" +"Run configurations are stored in the RIDE settings file.\n" +msgstr "" +"O comando especificado na cadeia de texto, será separado dos espaços para um " +"comando\n" +"e os seus argumentos. Se o comando ou argumentos precisarem de\n" +"espaços internamente, estes podem ser escritos como ''.\n" +"\n" +"O comando será executado no sistema diretamente sem abrir um terminal ou " +"shell.\n" +"Isto significa que comandos shell e extensões não estarão disponíveis. Por " +"exemplo,\n" +"em Windows ficheiros de lote (batch) para executar, têm que ter a extensão '." +"bat' e o comando 'dir'\n" +"não funciona.\n" +"\n" +"Examples:\n" +" robot.bat --include smoke C:\\my_tests\n" +" svn update /home/robot\n" +" C:\\ProgramFiles\\App\\prg.exe argumentwithspace,\n" +"Run configurations are stored in the RIDE settings file.\n" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:45 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +msgid "Manage Run Configurations" +msgstr "Gerir Configurações de Execução" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "New" +msgstr "Novo" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:99 +msgid "Remove" +msgstr "Remover" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Command" +msgstr "Comando" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +msgid "Documentation" +msgstr "Documentação" + +#: /home/helio/github/RIDE/src/robotide/run/configmanagerui.py:101 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Name" +msgstr "Nome" + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:30 +msgid "" +"A plugin for executing commands on the system.\n" +"\n" +" This plugin enables creation of persistent run configurations and\n" +" execution of those. Output of the executed command is displayed in a\n" +" separate tab." +msgstr "" +"Um Plugin para executar comandos no sistema.\n" +"\n" +" Este Plugin permite a criação de configurações persistentes e a sua " +"execução no sistema.\n" +" Os resultados da execução são apresentados num novo separador." + +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:54 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:56 +#: /home/helio/github/RIDE/src/robotide/run/runanything.py:63 +msgid "Macros" +msgstr "Macros" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:36 +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:35 +msgid "Search Tests" +msgstr "Procurar Testes" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:50 +msgid "Tag Search" +msgstr "Procurar Etiqueta" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Tags" +msgstr "Etiquetas" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:61 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:85 +msgid "Test" +msgstr "Teste" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:66 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:90 +msgid "Results: " +msgstr "Resultados: " + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:82 +msgid "" +"Find matches using tag patterns. See RF User Guide or 'robot --help' for " +"more information." +msgstr "" +"Procurar usando padrões com etiquetas. Ver o Guia de Utilizador do Robot " +"Framework ou 'robot --help' para mais informação." + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:96 +msgid "Include" +msgstr "Incluir" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:139 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:189 +msgid "Add all to selected" +msgstr "Adicionar tudo aos selecionados" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:156 +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:164 +msgid "Results: %d" +msgstr "Resultados: %d" + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:177 +msgid "Info. " +msgstr "Info. " + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:195 +msgid "Find matches by test name, documentation and/or tag." +msgstr "Procurar usando nome de teste, documentação e/ou etiqueta." + +#: /home/helio/github/RIDE/src/robotide/searchtests/dialogsearchtests.py:212 +msgid "Search term" +msgstr "Termo de pesquisa" + +#: /home/helio/github/RIDE/src/robotide/searchtests/searchtests.py:33 +msgid "A plugin for searching tests based on name, tags and documentation" +msgstr "Um Plugin para procurar testes por nome, etiquetas e documentação" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:60 +msgid "Library Spec XML|*.xml|All Files|*.*" +msgstr "Especificação de Biblioteca XML|*.xml|Todos os ficheiros|*.*" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:77 +msgid "" +"Library \"%s\" imported\n" +"from \"%s\"\n" +"This may require RIDE restart." +msgstr "" +"Biblioteca \"%s\" importada\n" +"de \"%s\"\n" +"Isto pode precisar de reiniciar RIDE." + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:78 +msgid "Info" +msgstr "Info" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Could not import library from file \"%s\"" +msgstr "Não se conseguiu importar biblioteca do ficheiro \"%s\"" + +#: /home/helio/github/RIDE/src/robotide/spec/specimporter.py:80 +msgid "Import failed" +msgstr "Falhou a importação" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +msgid "Help" +msgstr "Ajuda" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:54 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:140 +msgid "File" +msgstr "Ficheiro" + +#: /home/helio/github/RIDE/src/robotide/ui/actiontriggers.py:65 +msgid "&Help" +msgstr "Ajuda" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:35 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:36 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:37 +msgid "" +msgstr "" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:39 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:59 +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:139 +msgid "Search Keywords" +msgstr "Procurar palavras-chave" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:43 +msgid "A plugin for searching keywords based on name or documentation." +msgstr "Um Plugin para procurar palavras-chave por nome ou documentação." + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:55 +msgid "Search keywords from libraries and resources" +msgstr "Procurar palavras-chave em bibliotecas e recursos" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:173 +msgid "Search term: " +msgstr "Termo de pesquisa: " + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:180 +msgid "Search documentation" +msgstr "Procurar na documentação" + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:185 +msgid "Source: " +msgstr "Origem: " + +#: /home/helio/github/RIDE/src/robotide/ui/keywordsearch.py:352 +msgid "Description" +msgstr "Descrição" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:56 +msgid "" +"[File]\n" +" !&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW\n" +" ---\n" +" !&Open Test Suite | Open file containing tests | Ctrlcmd-O | " +"ART_FILE_OPEN\n" +" !Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O " +"| ART_FOLDER_OPEN\n" +" !Open External File | Open file in Code Editor | | ART_NORMAL_FILE\n" +" ---\n" +" !&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE\n" +" !Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS\n" +" ---\n" +" !E&xit | Exit RIDE | Ctrlcmd-Q\n" +"\n" +" [Tools]\n" +" !Search Unused Keywords | | | | POSITION-54\n" +" !Manage Plugins | | | | POSITION-81\n" +" !View All Tags | | F7 | | POSITION-82\n" +" !Preferences | | | | POSITION-99\n" +"\n" +" [Help]\n" +" !Shortcut keys | RIDE shortcut keys\n" +" !User Guide | Robot Framework User Guide\n" +" !Wiki | RIDE User Guide (Wiki)\n" +" !Report a Problem | Open browser to SEARCH on the RIDE issue tracker\n" +" !Release notes | Shows release notes\n" +" !About | Information about RIDE\n" +" !Check for Upgrade | Looks at PyPi for new released version\n" +" " +msgstr "" +"[Ficheiro]\n" +"!&Novo Projeto | Cria uma suite principal | Ctrlcmd-N | ART_NEW\n" +"---\n" +"!Abrir Suite de Testes | Abre um ficheiro contendo testes | Ctrlcmd-O | " +"ART_FILE_OPEN\n" +"!Abrir Direct&oria | Abre uma diretoria contendo ficheiros de dados | Shift-" +"Ctrlcmd-O | ART_FOLDER_OPEN\n" +"!Abrir Ficheiro Externo | Abre um ficheiro no Editor de Código | | " +"ART_NORMAL_FILE\n" +"---\n" +"!&Salvar | Salva o ficheiro de dados selecionado | Ctrlcmd-S | " +"ART_FILE_SAVE\n" +"!S&alvar Todos | Salvar todas as alterações | Ctrlcmd-Shift-S | " +"ART_FILE_SAVE_AS\n" +"---\n" +"!Sair | Sair do RIDE | Ctrlcmd-Q\n" +"\n" +"[Ferramentas]\n" +"!Procurar Palavras-Chave Não Usadas | | | | POSITION-54\n" +"!Gerir Plugins | | | | POSITION-81\n" +"!Ver Todas as Etiquetas | | F7 | | POSITION-82\n" +"!Preferências | | | | POSITION-99\n" +"\n" +"[Ajuda]\n" +"!Teclas de Atalho | Teclas de atalho/aceleradoras do RIDE\n" +"!Guia de Utilização | Guia de Utilização do Robot Framework\n" +"!Wiki | Guia de Utilização do RIDE (Wiki)\n" +"!Reportar um Problema | Abre o Navegador da Web para PROCURAR no sistema de " +"reporte de defeitos\n" +"!Notas da Produção | Mostra as notas de produção desta versão\n" +"!Acerca de | Informação acerca do RIDE\n" +" \n" +"!Verificar Se Há Atualização | Procura em PyPi por uma nova versão " +"produzida\n" +" " + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:167 +msgid "Saved %s" +msgstr "Salvou-se %s" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:168 +msgid "Saved all files" +msgstr "Salvaram-se todos os ficheiros" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:191 +msgid "Validation Error" +msgstr "Erro de Validação" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:195 +msgid "\"%s\" is read only" +msgstr "\"%s\" é apenas de leitura" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:196 +msgid "Modification prevented" +msgstr "Modificação não permitida" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:246 +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:105 +msgid "Test Suites" +msgstr "Suites de Teste" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:349 +msgid "" +"There are unsaved modifications.\n" +"Do you want to save your changes before exiting?" +msgstr "" +"Existem modificações não guardadas.\n" +"Você quer salvar as alterações antes de sair?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:351 +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:439 +msgid "Warning" +msgstr "Aviso" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:438 +msgid "" +"There are unsaved modifications.\n" +"Do you want to proceed without saving?" +msgstr "" +"Existem modificações não guardadas.\n" +"Você quer prosseguir sem salvar?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:473 +msgid "Choose a directory containing Robot files" +msgstr "Escolha uma diretoria contendo ficheiros Robot" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:538 +msgid "RIDE - Preferences" +msgstr "RIDE - Preferências" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:625 +msgid "Workspace modifications detected on the file system." +msgstr "Detetadas modificações no espaço de trabalho." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:626 +msgid "Do you want to reload the workspace?" +msgstr "Você quer reabrir o espaço de trabalho?" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:627 +msgid "Answering will ignore the changes on disk." +msgstr "Responder irá ignorar as alterações em disco." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:629 +msgid "Answering will discard unsaved changes." +msgstr "Responder irá descartar as alterações não guardadas." + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:630 +msgid "Files Changed On Disk" +msgstr "Ficheiros Modificados No Disco" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "search unused keywords" +msgstr "procurar palavras-chave não utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:751 +msgid "stop test run" +msgstr "parar execução de teste" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "preview" +msgstr "antever" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:752 +msgid "view ride log" +msgstr "ver o registo do ride" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:793 +msgid "Shortcut keys for RIDE" +msgstr "Teclas de atalho do RIDE" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:831 +msgid "Show" +msgstr "Mostrar" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:832 +msgid "Hide" +msgstr "Ocultar" + +#: /home/helio/github/RIDE/src/robotide/ui/mainframe.py:833 +msgid "Close" +msgstr "Fechar" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:40 +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:99 +msgid "Preview" +msgstr "Pré-visualização" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:42 +msgid "Show preview of the current file" +msgstr "Mostrar a visualização do ficheiro corrente" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Pipes)" +msgstr "Texto (Barras)" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:82 +msgid "Text (Spaces)" +msgstr "Texto (Espaços)" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:116 +msgid "Format" +msgstr "Formato" + +#: /home/helio/github/RIDE/src/robotide/ui/preview.py:122 +msgid "Print" +msgstr "Imprimir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:39 +msgid "Search unused keywords" +msgstr "Procurar palavras-chave não utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:66 +msgid "" +"This dialog helps you finding unused keywords within your opened project.\n" +"If you want, you can restrict the search to a set of files with the filter." +msgstr "" +"Este diálogo ajuda a procurar palavras-chave não utilizadas dentro do " +"projeto aberto.\n" +"Se você quiser, pode restringir a procura a um conjunto de ficheiros com o " +"filtro." + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:70 +msgid "Filter is" +msgstr "O filtro está" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:71 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:242 +msgid "inactive" +msgstr "inativo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:84 +msgid "Filter" +msgstr "Filtro" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:91 +msgid "Use RegEx" +msgstr "Usar RegEx" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:93 +msgid "" +"Here you can define one or more strings separated by comma (e.g. common," +"abc,123).\n" +"The filter matches if at least one string is part of the filename.\n" +"If you don't enter any strings, all opened files are included" +msgstr "" +"Aqui, você pode definir uma ou mais cadeias de texto separadas por vírgula " +"(por exemplo: comum, abc,123).\n" +"O filtro corresponderá se pelo menos uma das cadeias de texto é parte do " +"nome do ficheiro.\n" +"Se você não indicar nenhuma cadeia de texto, todos os ficheiros abertos " +"serão incluídos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:101 +msgid "Test case files" +msgstr "Ficheiros de Casos de Teste" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:104 +msgid "Resource files" +msgstr "Ficheiros de Recursos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:106 +msgid "Mode" +msgstr "Modo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "exclude" +msgstr "excluir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:107 +msgid "include" +msgstr "incluir" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:109 +msgid "Test the filter" +msgstr "Testar o filtro" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:139 +msgid "Keyword" +msgstr "Palavra-Chave" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:146 +msgid "Delete marked keywords" +msgstr "Apagar as Palavras-Chave marcadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:157 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:320 +msgid "Unused Keywords" +msgstr "Palavras-Chave Não Utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:163 +msgid "Abort" +msgstr "Abortar" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:247 +msgid "active" +msgstr "ativo" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:270 +#: /home/helio/github/RIDE/src/robotide/ui/review.py:353 +msgid "Unused Keywords (%d)" +msgstr "Palavras-Chave Não Utilizadas (%d)" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:279 +msgid "(None)" +msgstr "(Nenhum)" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:282 +msgid "" +"Keywords of the following files will be included in the search:\n" +"\n" +msgstr "" +"Serão incluídas na pesquisa as Palavras-Chave dos seguintes ficheiros:\n" +"\n" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:283 +msgid "Included files" +msgstr "Ficheiros incluídos" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:339 +msgid "Searching.%s \t- %s" +msgstr "Procurando.%s \t- %s" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:354 +msgid "Search finished - Found %d Unused Keywords" +msgstr "Procura concluída - Encontraram-se %d Palavras-Chave Não Utilizadas" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:402 +msgid "listing datafiles" +msgstr "listando ficheiros de dados" + +#: /home/helio/github/RIDE/src/robotide/ui/review.py:405 +msgid "searching from " +msgstr "procurando em " + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:37 +msgid "View all tags" +msgstr "Ver todas as etiquetas" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:76 +msgid "Tag" +msgstr "Etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:77 +msgid "Occurrences" +msgstr "Ocorrências" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:82 +msgid "The List" +msgstr "A Lista" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:85 +msgid "Refresh" +msgstr "Atualizar" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:86 +msgid "Included Tag Search" +msgstr "Procura por Etiqueta Incluída" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:89 +msgid "Excluded Tag Search" +msgstr "Procura por Etiqueta Excluída" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:147 +msgid "" +"Total tests %d, Tests with tags %d, Unique tags %d\n" +"Currently selected tests %d" +msgstr "" +"Total de testes %d, Testes com etiquetas %d, Etiquetas únicas %d\n" +"Testes selecionados correntemente, %d" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +msgid "Select all" +msgstr "Selecionar tudo" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:233 +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:262 +msgid "Rename" +msgstr "Renomear" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests with this tag" +msgstr "Mostrar testes com esta etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:234 +msgid "Show tests without this tag" +msgstr "Mostrar testes sem esta etiqueta" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:261 +msgid "Renaming tag '%s'." +msgstr "Renomeando a etiqueta '%s'." + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Confirm" +msgstr "Confirmar" + +#: /home/helio/github/RIDE/src/robotide/ui/tagdialogs.py:276 +msgid "Delete a tag '%s' ?" +msgstr "Apagar a etiqueta '%s' ?" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:209 +msgid "External Resources" +msgstr "Recursos Externos" + +#: /home/helio/github/RIDE/src/robotide/ui/treeplugin.py:344 +msgid "%s (excluded)" +msgstr "%s (excluído)" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:58 +msgid "'%s' - %d matches found - Searching%s" +msgstr "'%s' - %d encontradas correspondências - Procurando%s" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:63 +msgid "'%s' - %d matches" +msgstr "'%s' - %d correspondências" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:85 +msgid "Go to definition" +msgstr "Ir para a definição" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Location" +msgstr "Localização" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:143 +msgid "Usage" +msgstr "Procurar Utilizações" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported Location" +msgstr "Localização Importada" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Imported name" +msgstr "Nome Importado" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Location" +msgstr "Importando Localização" + +#: /home/helio/github/RIDE/src/robotide/usages/usagesdialog.py:176 +msgid "Importing Name" +msgstr "Importando Nome" diff --git a/src/robotide/log/log.py b/src/robotide/log/log.py index 7557bf7ac..8899c3600 100644 --- a/src/robotide/log/log.py +++ b/src/robotide/log/log.py @@ -14,12 +14,14 @@ # limitations under the License. import atexit +import builtins import glob import io import os import sys import tempfile import uuid +import wx from .logwindow import LogWindow, message_to_string from .. import context @@ -28,6 +30,9 @@ from ..action import ActionInfo from ..publish.messages import RideLog +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class LogPlugin(Plugin): """Viewer for internal log messages.""" @@ -37,7 +42,7 @@ def __init__(self, app): 'log_to_console': False, 'log_to_file': True }) - self.title = 'RIDE Log' + self.title = _('RIDE Log') self._log = [] self._panel = None self._path = os.path.join( @@ -78,7 +83,7 @@ def disable(self): def _create_menu(self): self.unregister_actions() self.register_action(ActionInfo( - 'Tools', 'View RIDE Log', self.on_view_log, position=84)) + _('Tools'), _('View RIDE Log'), self.on_view_log, position=84)) def _log_message(self, message): self._log.append(message) @@ -95,7 +100,7 @@ def _log_message(self, message): padding=10, font_size=font_size).Show() def on_view_log(self, event): - _ = event + __ = event if not self._panel: self._panel = LogWindow(self.notebook, self.title, self._log) self._panel.update_log() diff --git a/src/robotide/namespace/namespace.py b/src/robotide/namespace/namespace.py index db6b9e95e..2aa513c61 100644 --- a/src/robotide/namespace/namespace.py +++ b/src/robotide/namespace/namespace.py @@ -185,8 +185,8 @@ def _keyword_suggestions(self, datafile, start, ctx): if sug.name_begins_with(start_normalized) or sug.longname_begins_with(start_normalized)) - def get_resources(self, datafile): - return self._retriever.get_resources_from(datafile) + def get_resources(self, datafile, language=None): + return self._retriever.get_resources_from(datafile, language=language) def get_resource(self, path, directory='', report_status=True): return self._resource_factory.get_resource( @@ -565,18 +565,18 @@ def _collect_each_res_import(self, datafile, ctx, collector): collector(res, ctx, items) return items - def get_resources_from(self, datafile): - resources = list(self._get_resources_recursive(datafile, - RetrieverContext())) + def get_resources_from(self, datafile, language=None): + resources = list(self._get_resources_recursive(datafile, RetrieverContext(), language=language)) resources.sort(key=operator.attrgetter('name')) return resources # DEBUG - def _get_resources_recursive(self, datafile, ctx): + def _get_resources_recursive(self, datafile, ctx, language=None): + # DEBUG: at this point it is not relevant the language, we would only need the header resources = set() res = self._collect_each_res_import(datafile, ctx, self._add_resource) resources.update(res) for child in datafile.children: - resources.update(self._get_resources_recursive(child, ctx)) + resources.update(self._get_resources_recursive(child, ctx, language=language)) return resources def _add_resource(self, res, ctx, items): diff --git a/src/robotide/parserlog/parserlog.py b/src/robotide/parserlog/parserlog.py index 3bfefc366..77562887d 100644 --- a/src/robotide/parserlog/parserlog.py +++ b/src/robotide/parserlog/parserlog.py @@ -13,6 +13,7 @@ # limitations under the License. import atexit +import builtins import glob import io import os @@ -29,6 +30,9 @@ from ..action import ActionInfo from ..publish.messages import RideParserLogMessage +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class ParserLogPlugin(Plugin): """Viewer for parser log messages.""" @@ -38,7 +42,7 @@ def __init__(self, app): 'log_to_console': False, 'log_to_file': True }) - self.title = 'Parser Log' + self.title = _('Parser Log') self._log = [] self._panel = None self._path = os.path.join( @@ -78,8 +82,7 @@ def disable(self): def _create_menu(self): self.unregister_actions() - self.register_action(ActionInfo( - 'Tools', 'View Parser Log', self.on_view_log, position=83)) + self.register_action(ActionInfo(_('Tools'), _('View Parser Log'), self.on_view_log, position=83)) def _log_message(self, message): self._log.append(message) @@ -97,7 +100,7 @@ def _log_message(self, message): self.on_view_log(message, show_tab=False) def on_view_log(self, event, show_tab=True): - _ = event + __ = event if not self._panel: self._panel = LogWindow(self.notebook, self.title, self._log) self.notebook.SetPageTextColour(self.notebook.GetPageCount()-1, wx.Colour(255, 165, 0)) diff --git a/src/robotide/postinstall/desktopshortcut.py b/src/robotide/postinstall/desktopshortcut.py index bdfd15bc9..231cddfde 100644 --- a/src/robotide/postinstall/desktopshortcut.py +++ b/src/robotide/postinstall/desktopshortcut.py @@ -14,12 +14,17 @@ # limitations under the License. import atexit +import builtins import sys +import wx from ..pluginapi import Plugin from ..action import ActionInfo from ..postinstall import __main__ as postinstall +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class ShortcutPlugin(Plugin): """Creator of RIDE Desktop Shortcuts.""" @@ -46,13 +51,13 @@ def disable(self): def _create_menu(self): self.unregister_actions() - self.register_action(ActionInfo('Tools', - 'Create RIDE Desktop Shortcut', + self.register_action(ActionInfo(_('Tools'), + _('Create RIDE Desktop Shortcut'), self.on_view_shortcut_create, position=85)) def on_view_shortcut_create(self, event): - _ = event + __ = event self.call_creator(self.notebook) # self.disable() diff --git a/src/robotide/preferences/__init__.py b/src/robotide/preferences/__init__.py index f569b9709..dff69e9d5 100644 --- a/src/robotide/preferences/__init__.py +++ b/src/robotide/preferences/__init__.py @@ -21,6 +21,25 @@ from .settings import Settings, initialize_settings, RideSettings from ..ui import ExcludePreferences +import wx + + +class Languages: + names = [('Bulgarian', 'bg', wx.LANGUAGE_BULGARIAN), ('Bosnian', 'bs', wx.LANGUAGE_BOSNIAN), + ('Czech', 'cs', wx.LANGUAGE_CZECH), ('German', 'de', wx.LANGUAGE_GERMAN), + ('English', 'en', wx.LANGUAGE_ENGLISH), ('Spanish', 'es', wx.LANGUAGE_SPANISH), + ('Finnish', 'fi', wx.LANGUAGE_FINNISH), ('French', 'fr', wx.LANGUAGE_FRENCH), + ('Hindi', 'hi', wx.LANGUAGE_HINDI), ('Italian', 'it', wx.LANGUAGE_ITALIAN), + ('Dutch', 'nl', wx.LANGUAGE_DUTCH), ('Polish', 'pl', wx.LANGUAGE_POLISH), + ('Portuguese', 'pt', wx.LANGUAGE_PORTUGUESE), + ('Brazilian Portuguese', 'pt-BR', wx.LANGUAGE_PORTUGUESE_BRAZILIAN), + ('Romanian', 'ro', wx.LANGUAGE_ROMANIAN), ('Russian', 'ru', wx.LANGUAGE_RUSSIAN), + ('Swedish', 'sv', wx.LANGUAGE_SWEDISH), ('Thai', 'th', wx.LANGUAGE_THAI), + ('Turkish', 'tr', wx.LANGUAGE_TURKISH), ('Ukrainian', 'uk', wx.LANGUAGE_UKRAINIAN), + ('Vietnamese', 'vi', wx.LANGUAGE_VIETNAMESE), + ('Chinese Simplified', 'zh-CN', wx.LANGUAGE_CHINESE_SIMPLIFIED), + ('Chinese Traditional', 'zh-TW', wx.LANGUAGE_CHINESE_TRADITIONAL)] + class Preferences(object): diff --git a/src/robotide/preferences/general.py b/src/robotide/preferences/general.py index 7c49d7d07..0406d1f0a 100644 --- a/src/robotide/preferences/general.py +++ b/src/robotide/preferences/general.py @@ -22,6 +22,10 @@ from .managesettingsdialog import SaveLoadSettings from wx import Colour from ..context import IS_WINDOWS +try: + from robot.conf import languages +except ImportError: + languages = None ID_APPLY_TO_PANEL = wx.NewIdRef() ID_RESET = wx.NewIdRef() @@ -44,6 +48,19 @@ def read_fonts(fixed=False): return names +@lru_cache(maxsize=2) +def read_languages(): + """Returns list with translatqble languages""" + if languages: + from . import Languages + names = [n for n in Languages.names] + else: + names = [('English', 'en'), ('Portuguese', 'pt')] + names = [n[0] for n in names if not n[0].startswith('@')] + names.sort() + return names + + def set_colors(element, bk_color, fg_color): element.SetBackgroundColour(bk_color) element.SetOwnBackgroundColour(bk_color) @@ -65,6 +82,7 @@ def __init__(self, settings, *args, **kwargs): # don't have the time to do that right now, so this will have # to suffice. + ui_language = self._select_ui_language() font_editor = self._create_font_editor() colors_sizer = self.create_colors_sizer() main_sizer = wx.FlexGridSizer(rows=6, cols=1, vgap=10, hgap=10) @@ -86,6 +104,8 @@ def __init__(self, settings, *args, **kwargs): buttons_sizer.AddSpacer(10) buttons_sizer.Add(saveloadsettings) main_sizer.Add(buttons_sizer) + main_sizer.AddSpacer(10) + main_sizer.Add(ui_language) self.SetSizerAndFit(main_sizer) self.Bind(wx.EVT_BUTTON, self.on_reset) self.Bind(wx.EVT_BUTTON, self.on_save_load_settings) @@ -198,6 +218,19 @@ def _create_font_editor(self): sizer.Layout() return sizer + def _select_ui_language(self): + sizer = wx.FlexGridSizer(rows=3, cols=2, vgap=10, hgap=30) + background_color = Colour(LIGHT_GRAY) + foreground_color = Colour("black") + if 'ui language' in self._settings: + ll = StringChoiceEditor(self._settings, 'ui language', 'Language', read_languages()) + l_lang = ll.label(self) + if IS_WINDOWS: + set_colors(l_lang, background_color, foreground_color) + sizer.AddMany([l_lang, ll.chooser(self)]) + sizer.Layout() + return sizer + def create_colors_sizer(self): raise NotImplementedError('Implement me') diff --git a/src/robotide/preferences/settings.cfg b/src/robotide/preferences/settings.cfg index 1ecd4f185..86327c73f 100644 --- a/src/robotide/preferences/settings.cfg +++ b/src/robotide/preferences/settings.cfg @@ -21,6 +21,10 @@ txt number of spaces = 4 txt format separator = 'space' line separator = 'native' default file format = 'robot' +reformat = False +language = None # Test Suites will have sections and parameters in the selected language + # Possible values are the ones from Robot Framework >= 6.0. + [General] font size = 11 @@ -32,6 +36,7 @@ secondary background = 'light grey' background help = (240, 242, 80) foreground text = (7, 0, 70) apply to panels = True +ui language = 'English' [Text Edit] font size = 10 @@ -75,6 +80,7 @@ background error = '#FF9385' background highlight = '#FFFF77' word wrap = True enable auto suggestions = False +filter newlines = False # When enabled, newlines are not shown as \n in the cells. On Windows this may cause a lock. [Plugins] [[Test Runner]] @@ -108,4 +114,8 @@ secondary foreground = 'black' secondary background = 'light grey' background help = (240, 242, 80) foreground text = (7, 0, 70) -own colors = False \ No newline at end of file +own colors = False + +[[Preview]] # This plugin is not usable since RIDE 2.1 (and Robot Framework 3.2.1) +format = 'HTML' +_enabled = False \ No newline at end of file diff --git a/src/robotide/recentfiles/recentfiles.py b/src/robotide/recentfiles/recentfiles.py index 244a3872a..d364560f5 100644 --- a/src/robotide/recentfiles/recentfiles.py +++ b/src/robotide/recentfiles/recentfiles.py @@ -138,7 +138,7 @@ def __init__(self, index, file, plugin): self.doc = 'Open %s' % self.path def on_open_recent(self, event): - _ = event + __ = event if not self.plugin.frame.check_unsaved_modifications(): return self.plugin.open_suite(self.path) diff --git a/src/robotide/run/configmanagerui.py b/src/robotide/run/configmanagerui.py index 9c1833aa6..c2dd087a0 100644 --- a/src/robotide/run/configmanagerui.py +++ b/src/robotide/run/configmanagerui.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from wx import Colour from wx.lib.mixins.listctrl import TextEditMixin @@ -20,7 +21,10 @@ from ..editor.listeditor import AutoWidthColumnList, ListEditorBase from ..widgets import RIDEDialog, HelpLabel, ButtonWithHandler -_CONFIG_HELP = """The specified command string will be split from whitespaces into a command +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + +_CONFIG_HELP = _("""The specified command string will be split from whitespaces into a command and its arguments. If either the command or any of the arguments require internal spaces, they must be written as ''.\n The command will be executed in the system directly without opening a shell. @@ -32,13 +36,13 @@ svn update /home/robot C:\\ProgramFiles\\App\\prg.exe argumentwithspace, Run configurations are stored in the RIDE settings file. -""" +""") class ConfigManagerDialog(RIDEDialog): def __init__(self, configs, plugin): - RIDEDialog.__init__(self, title='Manage Run Configurations') + RIDEDialog.__init__(self, title=_('Manage Run Configurations')) # set Left to Right direction (while we don't have localization) self.SetBackgroundColour(Colour(self.color_background)) @@ -92,8 +96,9 @@ def get_data(self): class _ConfigListEditor(ListEditorBase): - _buttons = ['New', 'Remove'] - _columns = ['Name', 'Command', 'Documentation'] + _buttons = [_('New'), _('Remove')] + _buttons_nt = ['New', 'Remove'] # Non-translated names + _columns = [_('Name'), _('Command'), _('Documentation')] def __init__(self, parent, configs): self._editor_open = False @@ -114,7 +119,7 @@ def on_edit(self, event): self._list.open_editor(self._selection) def on_new(self, event): - _ = event + __ = event self._list.new_item() def on_remove(self, event): diff --git a/src/robotide/run/runanything.py b/src/robotide/run/runanything.py index eb9a0df2a..22e5ac0e7 100644 --- a/src/robotide/run/runanything.py +++ b/src/robotide/run/runanything.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from ..controller.basecontroller import _BaseController @@ -21,13 +22,16 @@ from ..run.configmanagerui import ConfigManagerDialog from ..run.ui import Runner +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class RunAnything(Plugin): - """A plugin for executing commands on the system. + __doc__ = _("""A plugin for executing commands on the system. This plugin enables creation of persistent run configurations and execution of those. Output of the executed command is displayed in a - separate tab.""" + separate tab.""") def __init__(self, app): Plugin.__init__(self, app, default_settings={'configs': []}) @@ -47,16 +51,16 @@ def on_manage_configurations(self, event): def _create_menu(self, configs): self.unregister_actions() - self.register_action(ActionInfo('Macros', 'Manage Run Configurations', + self.register_action(ActionInfo(_('Macros'), _('Manage Run Configurations'), self.on_manage_configurations)) - self.register_action(SeparatorInfo('Macros')) + self.register_action(SeparatorInfo(_('Macros'))) for index, cfg in enumerate(configs): self._add_config_to_menu(cfg, index+1) def _add_config_to_menu(self, config, index): def run(event): Runner(config, self.notebook).run() - info = ActionInfo('Macros', name='%d: %s' % (index, config.name), + info = ActionInfo(_('Macros'), name='%d: %s' % (index, config.name), doc=config.help, action=run) self.register_action(info) diff --git a/src/robotide/searchtests/dialogsearchtests.py b/src/robotide/searchtests/dialogsearchtests.py index 8d05495e3..95ac3154e 100644 --- a/src/robotide/searchtests/dialogsearchtests.py +++ b/src/robotide/searchtests/dialogsearchtests.py @@ -14,7 +14,7 @@ # limitations under the License. from functools import (total_ordering, cmp_to_key) - +import builtins import wx from wx import Colour @@ -22,6 +22,9 @@ HelpLabel, ImageProvider) from ..widgets.list import ListModel +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class TestsDialog(RIDEDialog): @@ -30,7 +33,7 @@ def __init__(self, fuzzy_search_handler, tag_search_handler, add_to_selected_han self._tag_search_handler = tag_search_handler self._add_to_selected_handler = add_to_selected_handler self._selection_listeners = [] - title = "Search Tests" + title = _("Search Tests") RIDEDialog.__init__(self, title=title, size=(800, 400)) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) @@ -43,8 +46,8 @@ def _create_notebook(self): self._notebook = wx.Notebook(self, wx.ID_ANY, style=wx.NB_TOP) self._notebook.SetBackgroundColour(Colour(self.color_background)) self._notebook.SetForegroundColour(Colour(self.color_foreground)) - self._notebook.AddPage(self._text_search_panel(), 'Search') - self._notebook.AddPage(self._tag_pattern_search_panel(), 'Tag Search') + self._notebook.AddPage(self._text_search_panel(), _('Search')) + self._notebook.AddPage(self._tag_pattern_search_panel(), _('Tag Search')) return self._notebook def select_page(self, page=0): @@ -55,12 +58,12 @@ def _text_search_panel(self): panel.SetSizer(VerticalSizer()) self._add_search_control(panel) self.tests = _TestSearchListModel([]) - self.tests_list = VirtualList(panel, ['Test', 'Tags', 'Source'], self.tests) + self.tests_list = VirtualList(panel, [_('Test'), _('Tags'), _('Source')], self.tests) self.tests_list.SetBackgroundColour(Colour(self.color_secondary_background)) self.tests_list.SetForegroundColour(Colour(self.color_secondary_foreground)) self.tests_list.add_selection_listener(self._select_text_search_result) panel.Sizer.add_expanding(self.tests_list) - self._fuzzy_results_text = wx.StaticText(panel, -1, 'Results: ') + self._fuzzy_results_text = wx.StaticText(panel, -1, _('Results: ')) panel.Sizer.Add(self._fuzzy_results_text, 0, wx.ALL, 3) return panel @@ -76,22 +79,21 @@ def _tag_pattern_search_panel(self): controls_sizer.Add(self._create_tag_search_button(panel), 0, wx.ALL | wx.EXPAND, 3) controls_sizer.Add(self._create_add_to_selected_button(panel), 0, wx.ALL | wx.EXPAND, 3) panel.Sizer.Add(controls_sizer) - panel.Sizer.Add(self._add_info_text(panel, "Find matches using tag patterns. See RF User " - "Guide or 'robot --help' for more information."), - 0, wx.ALL, 3) + panel.Sizer.Add(self._add_info_text(panel, _("Find matches using tag patterns. See RF User " + "Guide or 'robot --help' for more information.")), 0, wx.ALL, 3) self._tags_results = _TestSearchListModel([]) - self._tags_list = VirtualList(panel, ['Test', 'Tags', 'Source'], self._tags_results) + self._tags_list = VirtualList(panel, [_('Test'), _('Tags'), _('Source')], self._tags_results) self._tags_list.SetBackgroundColour(Colour(self.color_secondary_background)) self._tags_list.SetForegroundColour(Colour(self.color_secondary_foreground)) self._tags_list.add_selection_listener(self._select_tag_search_result) panel.Sizer.add_expanding(self._tags_list) - self._tags_results_text = wx.StaticText(panel, -1, 'Results: ') + self._tags_results_text = wx.StaticText(panel, -1, _('Results: ')) panel.Sizer.Add(self._tags_results_text, 0, wx.ALL, 3) return panel def _create_include_line(self, panel): include_line = self._horizontal_sizer() - include_line.Add(Label(panel, label='Include', size=(80, -1))) + include_line.Add(Label(panel, label=_('Include'), size=(80, -1))) self._tags_to_include_text = wx.TextCtrl(panel, value='', size=(400, -1), style=wx.TE_PROCESS_ENTER | wx.TE_NOHIDESEL) self._tags_to_include_text.SetBackgroundColour(Colour(self.color_secondary_background)) @@ -122,36 +124,36 @@ def _create_exclude_line(self, panel): return exclude_line def _create_tag_search_button(self, panel): - button = wx.Button(panel, label='Search') + button = wx.Button(panel, label=_('Search')) button.SetBackgroundColour(Colour(self.color_secondary_background)) button.SetForegroundColour(Colour(self.color_secondary_foreground)) button.Bind(wx.EVT_BUTTON, self.on_search_tags) return button def on_search_tags(self, event): - _ = event + __ = event self._tag_search_handler(self._tags_to_include_text.GetValue(), self._tags_to_exclude_text.GetValue()) def _create_add_to_selected_button(self, panel): - button = wx.Button(panel, label='Add all to selected') + button = wx.Button(panel, label=_('Add all to selected')) button.SetBackgroundColour(Colour(self.color_secondary_background)) button.SetForegroundColour(Colour(self.color_secondary_foreground)) button.Bind(wx.EVT_BUTTON, self.on_add_to_selected) return button def on_add_to_selected(self, event): - _ = event + __ = event self._add_to_selected_handler(self._get_current_tests()) def on_search_tests(self, event): - _ = event + __ = event self._fuzzy_search_handler(self._search_control.GetValue()) def set_search_model(self, search_text, results): results = list(results) self._search_control.SetValue(search_text) - self._fuzzy_results_text.SetLabel('Results: %d' % len(results)) + self._fuzzy_results_text.SetLabel(_('Results: %d') % len(results)) self.tests.sorted_tests = results self._refresh_list(self.tests_list) @@ -159,7 +161,7 @@ def set_tag_search_model(self, include_text, exclude_text, results): results = list(results) self._tags_to_include_text.SetValue(include_text) self._tags_to_exclude_text.SetValue(exclude_text) - self._tags_results_text.SetLabel('Results: %d' % len(results)) + self._tags_results_text.SetLabel(_('Results: %d') % len(results)) self._tags_results.sorted_tests = results self._refresh_list(self._tags_list) @@ -172,26 +174,26 @@ def _refresh_list(llist): def _add_info_text(self, panel, text=""): infopanel = self._horizontal_sizer() - infopanel.Add(HelpLabel(panel, "Info. " + text)) + infopanel.Add(HelpLabel(panel, _("Info. ") + text)) return infopanel def _add_search_control(self, panel): panel.SetSizer(VerticalSizer()) line1 = self._horizontal_sizer() self._add_pattern_filter(line1, panel) - fuzzy_search_button = wx.Button(panel, label='Search') + fuzzy_search_button = wx.Button(panel, label=_('Search')) fuzzy_search_button.SetBackgroundColour(Colour(self.color_secondary_background)) fuzzy_search_button.SetForegroundColour(Colour(self.color_secondary_foreground)) fuzzy_search_button.Bind(wx.EVT_BUTTON, self.on_search_tests) line1.Add(fuzzy_search_button, 0, wx.ALL | wx.EXPAND, 3) - add_to_selection_button = wx.Button(panel, label='Add all to selected') + add_to_selection_button = wx.Button(panel, label=_('Add all to selected')) add_to_selection_button.SetBackgroundColour(Colour(self.color_secondary_background)) add_to_selection_button.SetForegroundColour(Colour(self.color_secondary_foreground)) add_to_selection_button.Bind(wx.EVT_BUTTON, self.on_add_to_selected) line1.Add(add_to_selection_button, 0, wx.ALL | wx.EXPAND, 3) panel.Sizer.Add(line1, 0, wx.ALL, 3) - panel.Sizer.Add(self._add_info_text(panel, "Find matches by test name, " - "documentation and/or tag."), 0, wx.ALL, 3) + panel.Sizer.Add(self._add_info_text(panel, _("Find matches by test name, documentation and/or tag.")), + 0, wx.ALL, 3) panel.Sizer.Layout() @staticmethod @@ -207,7 +209,7 @@ def _add_pattern_filter(self, sizer, parent): style=wx.TE_PROCESS_ENTER) self._search_control.SetBackgroundColour(Colour(self.color_secondary_background)) self._search_control.SetForegroundColour(Colour(self.color_secondary_foreground)) - self._search_control.SetDescriptiveText('Search term') + self._search_control.SetDescriptiveText(_('Search term')) self._search_control.Bind(wx.EVT_TEXT_ENTER, self.wrapped) sizer.Add(self._search_control, 0, wx.ALL, 3) diff --git a/src/robotide/searchtests/searchtests.py b/src/robotide/searchtests/searchtests.py index f1f532f35..4bcd10d30 100644 --- a/src/robotide/searchtests/searchtests.py +++ b/src/robotide/searchtests/searchtests.py @@ -14,7 +14,7 @@ # limitations under the License. from functools import (total_ordering, cmp_to_key) - +import builtins import wx from .. import robotapi @@ -24,18 +24,21 @@ from .dialogsearchtests import TestsDialog from ..widgets import ImageProvider +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + @total_ordering class TestSearchPlugin(Plugin): - """A plugin for searching tests based on name, tags and documentation""" + __doc__ = _("""A plugin for searching tests based on name, tags and documentation""") __test__ = False - HEADER = 'Search Tests' + HEADER = _('Search Tests') _selection = None _dialog = None def enable(self): self.register_action(ActionInfo( - 'Tools', self.HEADER, self.show_empty_search, + _('Tools'), self.HEADER, self.show_empty_search, shortcut='F3', doc=self.__doc__, icon=ImageProvider().TEST_SEARCH_ICON, position=50)) self.register_search_action( @@ -82,7 +85,7 @@ def _dialog_closed(self, event): event.Skip() def show_empty_search(self, event): - _ = event + __ = event self.show_search_for('') def _do_with_selection(self, evt=None): diff --git a/src/robotide/spec/specimporter.py b/src/robotide/spec/specimporter.py index f1b9688f1..9af1334d1 100644 --- a/src/robotide/spec/specimporter.py +++ b/src/robotide/spec/specimporter.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import os import shutil import wx @@ -23,13 +24,16 @@ from ..publish import PUBLISHER, RideExecuteSpecXmlImport from .xmlreaders import get_name_from_xml +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class SpecImporterPlugin(Plugin): - HEADER = 'Import Library Spec XML' + HEADER = _('Import Library Spec XML') def enable(self): - self.register_action(ActionInfo('Tools', self.HEADER, + self.register_action(ActionInfo(_('Tools'), self.HEADER, self.execute_spec_import, position=83)) PUBLISHER.subscribe(self._ps_on_execute_spec_import, RideExecuteSpecXmlImport) @@ -53,9 +57,9 @@ def _execute_namespace_update(self): self.model.update_namespace() def _get_path_to_library_spec(self): - wildcard = ('Library Spec XML|*.xml|All Files|*.*') + wildcard = (_('Library Spec XML|*.xml|All Files|*.*')) dlg = wx.FileDialog(self.frame, - message='Import Library Spec XML', + message=_('Import Library Spec XML'), wildcard=wildcard, defaultDir=self.model.default_dir) # DEBUG # , style=wx.OPEN) @@ -70,6 +74,8 @@ def _store_spec(self, path): name = get_name_from_xml(path) if name: shutil.copy(path, os.path.join(context.LIBRARY_XML_DIRECTORY, name+'.xml')) - wx.MessageBox('Library "%s" imported\nfrom "%s"\nThis may require RIDE restart.' % (name, path), 'Info', wx.OK | wx.ICON_INFORMATION) + wx.MessageBox(_('Library "%s" imported\nfrom "%s"\nThis may require RIDE restart.') % (name, path), + _('Info'), wx.OK | wx.ICON_INFORMATION) else: - wx.MessageBox('Could not import library from file "%s"' % path, 'Import failed', wx.OK | wx.ICON_ERROR) + wx.MessageBox(_('Could not import library from file "%s"') % path, _('Import failed'), + wx.OK | wx.ICON_ERROR) diff --git a/src/robotide/ui/actiontriggers.py b/src/robotide/ui/actiontriggers.py index ecb5b7f0e..5a7b9ed81 100644 --- a/src/robotide/ui/actiontriggers.py +++ b/src/robotide/ui/actiontriggers.py @@ -13,13 +13,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import wx from ..context import IS_WINDOWS, IS_MAC +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation ID_CustomizeToolbar = wx.ID_HIGHEST + 1 +def accel_index(menu: list, item: str) -> int: + """ Gets the index from a list ignoring the accelarator marker, & + :param menu: list - list to get index + :param item: str - item to find in menu list + :returns index: int + """ + for idx, m in enumerate(menu): + if m.replace('&', '') == item.replace('&', ''): + return idx + raise ValueError(f"{item} is not in list") + + class MenuBar(object): def __init__(self, frame): @@ -36,17 +51,18 @@ def take_menu_bar_into_use(self): self.m_frame.SetMenuBar(self._mb) def _create_default_menus(self): - for name in ['File', 'Edit', 'Tools', 'Help']: + for name in [_('File'), _('Edit'), _('Tools'), _('Help')]: self._create_menu(name, before_help=False) def _create_menu(self, name, before_help=True): + print(f"DEBUG: actiontriggers.py _create_menu ENTER name={name}") menu = _Menu(self._name_builder.get_name(name), self.m_frame) self._insert_menu(menu, before_help) return menu def _insert_menu(self, menu, before_help): if before_help: - index = [m.name for m in self._menus].index('&Help') + index = accel_index([m.name for m in self._menus], _('&Help')) else: index = len(self._menus) self._menus.insert(index, menu) @@ -71,6 +87,7 @@ class _Menu(object): def __init__(self, name, frame): self.name = name + print(f"DEBUG: actiontriggers.py _Menu name={name}") self._frame = frame self.wx_menu = wx.Menu() self._menu_items = {} @@ -162,6 +179,7 @@ def get_name(self, name): except ValueError: name = self._generate_accelerator(name) self._register(name) + # print(f"DEBUG: actiontriggers.py get_name RETURN name={name}") return name def get_registered_name(self, name): diff --git a/src/robotide/ui/excludes_dialogs.py b/src/robotide/ui/excludes_dialogs.py index 82d3473a8..c68bbc2de 100644 --- a/src/robotide/ui/excludes_dialogs.py +++ b/src/robotide/ui/excludes_dialogs.py @@ -84,7 +84,7 @@ def _add_button_and_status(self, sizer): sizer.Add(status_and_button_sizer) def on_save(self, event): - _ = event + __ = event text = self._text_box.GetValue() self._settings.excludes.write_excludes(set(text.split('\n'))) RideSettingsChanged(keys=('Excludes', 'saved'), old=None, new=None).publish() @@ -94,7 +94,7 @@ def on_save(self, event): @staticmethod def on_help(event): - _ = event + __ = event dialog = ExcludeHelpDialog() dialog.Show() diff --git a/src/robotide/ui/filedialogs.py b/src/robotide/ui/filedialogs.py index 71cb0aedb..49c55f644 100644 --- a/src/robotide/ui/filedialogs.py +++ b/src/robotide/ui/filedialogs.py @@ -33,7 +33,7 @@ class _CreationDialog(RIDEDialog): - formats = ["ROBOT", "TXT", "TSV", "HTML"] + formats = ["ROBOT", "TXT", "TSV"] # Removed "HTML" def __init__(self, default_dir, title): sizer = self._init_dialog(title) @@ -157,7 +157,7 @@ def _is_dir_type(self): def _get_extension(self): if not self._format_chooser: - return 'html' + return 'robot' return self._format_chooser.GetStringSelection().lower() def on_path_changed(self, event): @@ -307,11 +307,11 @@ def _get_wildcard(settings): ("txt", "Robot data (*.txt)|*.txt"), ("resource", "Robot resource file (*.resource)|*.resource"), ("tsv", "Robot Tab Separated data (*.tsv)|*.tsv"), - ("html", "Robot HTML data (pre 3.2.2) (*.html)|*.html"), + # ("html", "Robot HTML data (pre 3.2.2) (*.html)|*.html"), ("all", "All files|*.*") ] default_format = settings.get(DEFAULT_FFORMAT, "robot") - robottypes = settings.get('robot types', ['robot', 'resource', 'txt', 'tsv', 'html']) + robottypes = settings.get('robot types', ['robot', 'resource', 'txt', 'tsv']) # , 'html' if default_format not in robottypes: default_format = "all" first = [ft for ft in filetypes if ft[0] == default_format] diff --git a/src/robotide/ui/fileexplorerplugin.py b/src/robotide/ui/fileexplorerplugin.py index aaadc59c6..3c8561484 100644 --- a/src/robotide/ui/fileexplorerplugin.py +++ b/src/robotide/ui/fileexplorerplugin.py @@ -81,7 +81,7 @@ def is_focused(self): return self._filemgr.HasFocus() def on_show_file_explorer(self, event): - _ = event + __ = event if not self._parent: self._parent = wx.App.Get().GetWindow() # self.frame if not self._filemgr: # This is not needed because file explorer is always created diff --git a/src/robotide/ui/keywordsearch.py b/src/robotide/ui/keywordsearch.py index 6cfd4328f..98534ae72 100644 --- a/src/robotide/ui/keywordsearch.py +++ b/src/robotide/ui/keywordsearch.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import os.path from functools import (cmp_to_key) @@ -28,15 +29,18 @@ from ..usages.UsageRunner import Usages from ..widgets import PopupMenuItem, ButtonWithHandler, Label, Font, HtmlWindow, ImageProvider, RIDEDialog -ALL_KEYWORDS = '' -ALL_USER_KEYWORDS = '' -ALL_LIBRARY_KEYWORDS = '' +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation -SEARCH_KW = 'Search Keywords' +ALL_KEYWORDS = _('') +ALL_USER_KEYWORDS = _('') +ALL_LIBRARY_KEYWORDS = _('') + +SEARCH_KW = _('Search Keywords') class KeywordSearch(Plugin): - """A plugin for searching keywords based on name or documentation.""" + __doc__ = _("""A plugin for searching keywords based on name or documentation.""") def __init__(self, app): Plugin.__init__(self, app) @@ -46,20 +50,20 @@ def __init__(self, app): self._dialog = None def enable(self): - action = ActionInfo('Tools', SEARCH_KW, self.on_search, + action = ActionInfo(_('Tools'), SEARCH_KW, self.on_search, shortcut='F5', - doc='Search keywords from libraries and resources', + doc=_('Search keywords from libraries and resources'), icon=ImageProvider().KW_SEARCH_ICON, position=51) self.register_action(action) - self.register_search_action(SEARCH_KW, self.show_search_for, ImageProvider().KW_SEARCH_ICON) + self.register_search_action(_('Search Keywords'), self.show_search_for, ImageProvider().KW_SEARCH_ICON) self.subscribe(self.mark_dirty, RideOpenSuite, RideOpenResource, RideImportSetting, RideUserKeyword, RideNewProject) self._dialog = KeywordSearchDialog(self.frame, self) self.tree.register_context_menu_hook(self._search_resource) def on_search(self, event): - _ = event + __ = event self._dialog.show_search_with_criteria() def mark_dirty(self, message): @@ -132,7 +136,7 @@ def _contains(string, pattern): class KeywordSearchDialog(RIDEDialog): def __init__(self, parent, searcher): - RIDEDialog.__init__(self, title="Search Keywords", parent=parent, size=(650, 400), + RIDEDialog.__init__(self, title=_("Search Keywords"), parent=parent, size=(650, 400), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) @@ -166,19 +170,19 @@ def _horizontal_sizer(): return wx.BoxSizer(wx.HORIZONTAL) def _add_pattern_filter(self, sizer): - sizer.Add(Label(self, label='Search term: ')) + sizer.Add(Label(self, label=_('Search term: '))) self._search_control = wx.SearchCtrl(self, size=(200, -1), style=wx.TE_PROCESS_ENTER) self._search_control.SetBackgroundColour(Colour(self.color_secondary_background)) self._search_control.SetForegroundColour(Colour(self.color_secondary_foreground)) sizer.Add(self._search_control) def _add_doc_filter(self, sizer): - self._use_doc = wx.CheckBox(self, label='Search documentation') + self._use_doc = wx.CheckBox(self, label=_('Search documentation')) self._use_doc.SetValue(True) sizer.Add(self._use_doc) def _add_source_filter(self, sizer): - sizer.Add(Label(self, label='Source: ')) + sizer.Add(Label(self, label=_('Source: '))) self._source_filter = wx.ComboBox(self, value=ALL_KEYWORDS, size=(300, -1), choices=self._get_sources(), style=wx.CB_READONLY) self._source_filter.SetBackgroundColour(Colour(self.color_secondary_background)) @@ -200,7 +204,7 @@ def _add_keyword_list(self): def _add_keyword_details(self): self._details = HtmlWindow(self) self._add_to_sizer(self._details) - self._find_usages_button = ButtonWithHandler(self, 'Find Usages') + self._find_usages_button = ButtonWithHandler(self, _('Find Usages'), handler=self.on_find_usages) self._find_usages_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._find_usages_button.SetForegroundColour(Colour(self.color_secondary_foreground)) self.Sizer.Add(self._find_usages_button, 0, wx.ALL, 3) @@ -209,7 +213,7 @@ def _add_to_sizer(self, component): self.Sizer.Add(component, 1, wx.EXPAND | wx.ALL, 3) def on_find_usages(self, event): - _ = event + __ = event Usages(self._plugin.model, self._plugin.tree.highlight, self._last_selected_kw.name, self._last_selected_kw).show() @@ -232,17 +236,17 @@ def on_col_click(self, event): event.Skip() def on_activate(self, event): - _ = event + __ = event if self._plugin.have_keywords_changed(): self._update_sources() self._populate_search() def on_use_doc_change(self, event): - _ = event + __ = event self._populate_search() def on_search(self, event): - _ = event + __ = event self._sort_order.searched(self._get_search_text()) self._populate_search() @@ -267,7 +271,7 @@ def _update_sources(self): self._source_filter.SetValue(ALL_KEYWORDS) def on_close(self, event): - _ = event + __ = event self.Hide() def _populate_search(self): @@ -345,7 +349,8 @@ def _has_been_sorted_by(self, col): class _KeywordData(list): - headers = ['Name', 'Source', 'Description'] + headers = [_('Name'), _('Source'), _('Description')] + headers_attr = ['Name', 'Source', 'Description'] # Non-translated names def __init__(self, keywords, sort_order, search_criteria=None): self.extend(self._sort(keywords, sort_order, search_criteria)) @@ -370,7 +375,7 @@ def _sort_by_search(self, keywords, sort_order, search_criteria): def _sort_by_attr(self, keywords, sort_order): return sorted(keywords, key=cmp_to_key(self._get_comparator_for( - self.headers[sort_order.column].lower())), + self.headers_attr[sort_order.column].lower())), reverse=not sort_order.sort_up) @staticmethod diff --git a/src/robotide/ui/mainframe.py b/src/robotide/ui/mainframe.py index 36b8df3ec..d23ade89c 100644 --- a/src/robotide/ui/mainframe.py +++ b/src/robotide/ui/mainframe.py @@ -13,12 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import os import wx import wx.lib.agw.aui as aui from wx import Colour from wx.adv import TaskBarIcon, TBI_DOCK, EVT_TASKBAR_LEFT_DOWN +from multiprocessing import shared_memory from .actiontriggers import (MenuBar, ToolBarButton, ShortcutRegistry, _RideSearchMenuItem) from .filedialogs import (NewProjectDialog, InitFileFormatDialog) @@ -41,34 +43,8 @@ from ..utils import RideFSWatcherHandler from ..widgets import RIDEDialog, ImageProvider, HtmlWindow -_menudata = """ -[File] -!&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW ---- -!&Open Test Suite | Open file containing tests | Ctrlcmd-O | ART_FILE_OPEN -!Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O | ART_FOLDER_OPEN -!Open External File | Open file in Code Editor | | ART_NORMAL_FILE ---- -!&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE -!Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS ---- -!E&xit | Exit RIDE | Ctrlcmd-Q - -[Tools] -!Search Unused Keywords | | | | POSITION-54 -!Manage Plugins | | | | POSITION-81 -!View All Tags | | F7 | | POSITION-82 -!Preferences | | | | POSITION-99 - -[Help] -!Shortcut keys | RIDE shortcut keys -!User Guide | Robot Framework User Guide -!Wiki | RIDE User Guide (Wiki) -!Report a Problem | Open browser to SEARCH on the RIDE issue tracker -!Release notes | Shows release notes -!About | Information about RIDE -!Check for Upgrade | Looks at PyPi for new released version -""" +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation ID_CustomizeToolbar = wx.ID_HIGHEST + 1 ID_SampleItem = ID_CustomizeToolbar + 1 @@ -77,6 +53,61 @@ class RideFrame(wx.Frame): + _menudata = _("""[File] + !&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW + --- + !&Open Test Suite | Open file containing tests | Ctrlcmd-O | ART_FILE_OPEN + !Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O | ART_FOLDER_OPEN + !Open External File | Open file in Code Editor | | ART_NORMAL_FILE + --- + !&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE + !Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS + --- + !E&xit | Exit RIDE | Ctrlcmd-Q + + [Tools] + !Search Unused Keywords | | | | POSITION-54 + !Manage Plugins | | | | POSITION-81 + !View All Tags | | F7 | | POSITION-82 + !Preferences | | | | POSITION-99 + + [Help] + !Shortcut keys | RIDE shortcut keys + !User Guide | Robot Framework User Guide + !Wiki | RIDE User Guide (Wiki) + !Report a Problem | Open browser to SEARCH on the RIDE issue tracker + !Release notes | Shows release notes + !About | Information about RIDE + !Check for Upgrade | Looks at PyPi for new released version + """) + + _menudata_nt = """[File] + !&New Project | Create a new top level suite | Ctrlcmd-N | ART_NEW + --- + !&Open Test Suite | Open file containing tests | Ctrlcmd-O | ART_FILE_OPEN + !Open &Directory | Open directory containing datafiles | Shift-Ctrlcmd-O | ART_FOLDER_OPEN + !Open External File | Open file in Code Editor | | ART_NORMAL_FILE + --- + !&Save | Save selected datafile | Ctrlcmd-S | ART_FILE_SAVE + !Save &All | Save all changes | Ctrlcmd-Shift-S | ART_FILE_SAVE_AS + --- + !E&xit | Exit RIDE | Ctrlcmd-Q + + [Tools] + !Search Unused Keywords | | | | POSITION-54 + !Manage Plugins | | | | POSITION-81 + !View All Tags | | F7 | | POSITION-82 + !Preferences | | | | POSITION-99 + + [Help] + !Shortcut keys | RIDE shortcut keys + !User Guide | Robot Framework User Guide + !Wiki | RIDE User Guide (Wiki) + !Report a Problem | Open browser to SEARCH on the RIDE issue tracker + !Release notes | Shows release notes + !About | Information about RIDE + !Check for Upgrade | Looks at PyPi for new released version + """ def __init__(self, application, controller): size = application.settings.get('mainframe size', (1100, 700)) @@ -85,6 +116,11 @@ def __init__(self, application, controller): pos=application.settings.get(MAINFRAME_POSITION, (50, 30)), size=size, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.BORDER_THEME) + # Shared memory to store language definition + try: + self.sharemem = shared_memory.ShareableList(['en'], name="language") + except FileExistsError: # Other instance created file + self.sharemem = shared_memory.ShareableList(name="language") # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) # self.SetLayoutDirection(wx.Layout_RightToLeft) @@ -128,8 +164,8 @@ def __init__(self, application, controller): def _subscribe_messages(self): for listener, topic in [ - (lambda message: self.SetStatusText('Saved %s' % message.path), RideSaved), - (lambda message: self.SetStatusText('Saved all files'), RideSaveAll), + (lambda message: self.SetStatusText(_('Saved %s') % message.path), RideSaved), + (lambda message: self.SetStatusText(_('Saved all files')), RideSaveAll), (self._set_label, RideTreeSelection), (self._show_validation_error, RideInputValidationError), (self._show_modification_prevented_error, RideModificationPrevented), @@ -147,17 +183,17 @@ def _create_title(message): item = message.item title += ' - ' + item.datafile.name if not item.is_modifiable(): - title += ' (READ ONLY)' + title += _(' (READ ONLY)') return title @staticmethod def _show_validation_error(message): - wx.MessageBox(message.message, 'Validation Error', style=wx.ICON_ERROR) + wx.MessageBox(message.message, _('Validation Error'), style=wx.ICON_ERROR) @staticmethod def _show_modification_prevented_error(message): - wx.MessageBox("\"%s\" is read only" % message.controller.datafile_controller.filename, "Modification prevented", - style=wx.ICON_ERROR) + wx.MessageBox(_("\"%s\" is read only") % message.controller.datafile_controller.filename, + _("Modification prevented"), style=wx.ICON_ERROR) def _init_ui(self): """ DEBUG: @@ -207,11 +243,12 @@ def _init_ui(self): # self.aui_mgr.AddPane(self.leftpanel, aui.AuiPaneInfo().Name("left_panel").Caption("left_panel").Left()) # DEBUG: Next was already called from application.py self.aui_mgr.AddPane(self.tree, - aui.AuiPaneInfo().Name("tree_content").Caption("Test Suites").CloseButton(False). + aui.AuiPaneInfo().Name("tree_content").Caption(_("Test Suites")).CloseButton(False). LeftDockable()) # DEBUG: remove .CloseButton(False) when restore is fixed # DEBUG: self.aui_mgr.GetPane(self.tree).DestroyOnClose() # TreePlugin will manage showing the Tree - self.actions.register_actions(action_info_collection(_menudata, self, self.tree)) + self.actions.register_actions(action_info_collection(self._menudata, self, data_nt=self._menudata_nt, + container=self.tree)) # ##### File explorer panel is always created here self.filemgr = FileExplorer(self, self.controller) self.filemgr.SetMinSize(wx.Size(275, 250)) @@ -309,10 +346,9 @@ def on_release_notes(self, event): def _allowed_to_exit(self): if self.has_unsaved_changes(): - ret = wx.MessageBox("There are unsaved modifications.\n" - "Do you want to save your changes before " - "exiting?", "Warning", - wx.ICON_WARNING | wx.CANCEL | wx.YES_NO) + ret = wx.MessageBox(_("There are unsaved modifications.\n" + "Do you want to save your changes before " + "exiting?"), _("Warning"), wx.ICON_WARNING | wx.CANCEL | wx.YES_NO) if ret == wx.CANCEL: return False if ret == wx.YES: @@ -323,7 +359,7 @@ def has_unsaved_changes(self): return self.controller.is_dirty() def on_new_project(self, event): - _ = event + __ = event if not self.check_unsaved_modifications(): return NewProjectDialog(self.controller).execute() @@ -334,7 +370,7 @@ def _populate_tree(self): self.filemgr.update_tree() def on_open_file(self, event): - _ = event + __ = event if not self.filemgr: return # EVT_DIRCTRL_FILEACTIVATED @@ -342,8 +378,7 @@ def on_open_file(self, event): robottypes = self._application.settings.get('robot types', ['robot', 'resource', 'txt', - 'tsv', - 'html']) + 'tsv']) # Removed 'html' path = self.filemgr.GetFilePath() ext = '' if len(path) > 0: @@ -372,7 +407,7 @@ def on_menu_open_file(self, event): event.Skip() def on_open_external_file(self, event): - _ = event + __ = event if not self._current_external_dir: curdir = self.controller.default_dir else: @@ -388,7 +423,7 @@ def on_open_external_file(self, event): wx.LogError(f"Cannot open file {path}") def on_open_test_suite(self, event): - _ = event + __ = event if not self.check_unsaved_modifications(): return path = RobotFilePathDialog( @@ -400,9 +435,8 @@ def on_open_test_suite(self, event): def check_unsaved_modifications(self): if self.has_unsaved_changes(): - ret = wx.MessageBox("There are unsaved modifications.\n" - "Do you want to proceed without saving?", - "Warning", wx.ICON_WARNING | wx.YES_NO) + ret = wx.MessageBox(_("There are unsaved modifications.\n" + "Do you want to proceed without saving?"), _("Warning"), wx.ICON_WARNING | wx.YES_NO) return ret == wx.YES return True @@ -411,6 +445,13 @@ def open_suite(self, path): # self._controller.default_dir will only save dir path # need to save path to self._application.workspace_path too self._application.workspace_path = path + from ..lib.compat.parsing.language import check_file_language + self.controller.file_language = check_file_language(path) + if self.controller.file_language: + set_lang = shared_memory.ShareableList(name="language") + set_lang[0] = self.controller.file_language[0] + # print(f"DEBUG: project.py Project load_data file_language = {self.controller.file_language}\n" + # f"sharedmem={set_lang}") try: err = self.controller.load_datafile(path, LoadProgressObserver(self)) if isinstance(err, UserWarning): @@ -427,21 +468,20 @@ def refresh_datafile(self, item, event): self.filemgr.ReCreateTree() def on_open_directory(self, event): - _ = event + __ = event if self.check_unsaved_modifications(): - path = wx.DirSelector(message="Choose a directory containing Robot" - " files", + path = wx.DirSelector(message=_("Choose a directory containing Robot files"), default_path=self.controller.default_dir) if path: self.open_suite(path) def on_save(self, event): - _ = event + __ = event RideBeforeSaving().publish() self.save() def on_save_all(self, event): - _ = event + __ = event RideBeforeSaving().publish() self.save_all() @@ -469,28 +509,33 @@ def _show_format_dialog_for(file_controller_without_format): InitFileFormatDialog(file_controller_without_format).execute() def on_exit(self, event): - _ = event + __ = event + try: + self.sharemem.shm.close() + self.sharemem.shm.unlink() + except FileNotFoundError: + pass self.Close() def on_manage_plugins(self, event): - _ = event + __ = event self._plugin_manager.show(self._application.get_plugins()) def on_view_all_tags(self, event): - _ = event + __ = event if self._view_all_tags_dialog is None: self._view_all_tags_dialog = ViewAllTagsDialog(self.controller, self) self._view_all_tags_dialog.show_dialog() def on_search_unused_keywords(self, event): - _ = event + __ = event if self._review_dialog is None: self._review_dialog = ReviewDialog(self.controller, self) self._review_dialog.show_dialog() def on_preferences(self, event): - _ = event - dlg = PreferenceEditor(self, "RIDE - Preferences", + __ = event + dlg = PreferenceEditor(self, _("RIDE - Preferences"), self._application.preferences, style='tree') # Changed to non-modal, original comment follows: # I would prefer that this not be modal, but making it non-modal @@ -501,20 +546,20 @@ def on_preferences(self, event): @staticmethod def on_about(event): - _ = event + __ = event dlg = AboutDialog() dlg.ShowModal() dlg.Destroy() def on_check_for_upgrade(self, event): - _ = event + __ = event from ..application.updatenotifier import UpdateNotifierController, UpdateDialog wx.CallAfter(UpdateNotifierController(self.general_settings).notify_update_if_needed, UpdateDialog, ignore_check_condition=True) - # @staticmethod - def on_shortcut_keys(self, event): - _ = event + @staticmethod + def on_shortcut_keys(event): + __ = event dialog = ShortcutKeysDialog() """ DEBUG: self.aui_mgr.AddPane(dialog.GetContentWindow(),aui.AuiPaneInfo().Name("shortcuts").Caption("Shortcuts Keys") @@ -525,7 +570,7 @@ def on_shortcut_keys(self, event): @staticmethod def on_report_a_problem(event): - _ = event + __ = event wx.LaunchDefaultBrowser("https://github.com/robotframework/RIDE/issues" "?utf8=%E2%9C%93&q=is%3Aissue+%22search" "%20your%20problem%22" @@ -533,12 +578,12 @@ def on_report_a_problem(event): @staticmethod def on_user_guide(event): - _ = event + __ = event wx.LaunchDefaultBrowser("https://robotframework.org/robotframework/#user-guide") @staticmethod def on_wiki(event): - _ = event + __ = event wx.LaunchDefaultBrowser("https://github.com/robotframework/RIDE/wiki") def _has_data(self): @@ -577,12 +622,12 @@ def ensure_on_screen(self): self.SetSize(size) def show_confirm_reload_dlg(self, event): - msg = ['Workspace modifications detected on the file system.', - 'Do you want to reload the workspace?', - 'Answering will ignore the changes on disk.'] + msg = [_('Workspace modifications detected on the file system.'), + _('Do you want to reload the workspace?'), + _('Answering will ignore the changes on disk.')] if self.controller.is_dirty(): - msg.insert(2, 'Answering will discard unsaved changes.') - ret = wx.MessageBox('\n'.join(msg), 'Files Changed On Disk', + msg.insert(2, _('Answering will discard unsaved changes.')) + ret = wx.MessageBox('\n'.join(msg), _('Files Changed On Disk'), style=wx.YES_NO | wx.ICON_WARNING) confirmed = ret == wx.YES if confirmed: @@ -689,7 +734,8 @@ def register_action(self, action_info, update_aui=True): action = action_factory(action_info) self._shortcut_registry.register(action) if hasattr(action_info, "menu_name"): - if action_info.menu_name == "Tools": + print(f"DEBUG: mainframe.py ActionRegister register_action menu_name={action_info.menu_name}") + if action_info.menu_name == _("Tools"): self._tools_items[action_info.position] = action menubar_can_be_registered = False if menubar_can_be_registered: @@ -701,9 +747,9 @@ def register_action(self, action_info, update_aui=True): return action def register_tools(self): - separator_action = action_factory(SeparatorInfo("Tools")) - add_separator_after = ["stop test run", "search unused keywords", - "preview", "view ride log"] + separator_action = action_factory(SeparatorInfo(_("Tools"))) + add_separator_after = [_("stop test run"), _("search unused keywords"), + _("preview"), _("view ride log")] # for key in sorted(self._tools_items.iterkeys()): # print("DEBUG: at register_tools, tools: %s" % self._tools_items) for key in sorted(self._tools_items.keys()): # DEBUG Python3 @@ -744,7 +790,7 @@ def on_key(self, *args): class ShortcutKeysDialog(RIDEDialog): def __init__(self): - RIDEDialog.__init__(self, title="Shortcut keys for RIDE") + RIDEDialog.__init__(self, title=_("Shortcut keys for RIDE")) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -775,29 +821,29 @@ def __init__(self, frame, img_provider): self.Bind(wx.EVT_MENU, self.on_task_bar_close, id=3) def on_click(self, event): - _ = event + __ = event self.frame.Raise() self.frame.Restore() self.frame.Show(True) def CreatePopupMenu(self): menu = wx.Menu() - menu.Append(1, 'Show') - menu.Append(2, 'Hide') - menu.Append(3, 'Close') + menu.Append(1, _('Show')) + menu.Append(2, _('Hide')) + menu.Append(3, _('Close')) return menu def on_task_bar_close(self, event): - _ = event + __ = event self.frame.Close() def on_task_bar_activate(self, event): - _ = event + __ = event if not self.frame.IsShown(): self.frame.Show() self.frame.Restore() def on_task_bar_deactivate(self, event): - _ = event + __ = event if self.frame.IsShown(): self.frame.Hide() diff --git a/src/robotide/ui/notebook.py b/src/robotide/ui/notebook.py index 9e8debc62..d25ef8eec 100644 --- a/src/robotide/ui/notebook.py +++ b/src/robotide/ui/notebook.py @@ -101,7 +101,7 @@ def on_tab_changing(self, event): event.Skip() def on_tab_changed(self, event): - _ = event + __ = event if not self._tab_changed(): self._tab_closing = False return diff --git a/src/robotide/ui/preferences_dialogs.py b/src/robotide/ui/preferences_dialogs.py index f7e0a628f..1bdf69cce 100644 --- a/src/robotide/ui/preferences_dialogs.py +++ b/src/robotide/ui/preferences_dialogs.py @@ -69,6 +69,8 @@ class PreferencesComboBox(wx.ComboBox): def __init__(self, parent, id, settings, key, choices): self.settings = settings self.key = key + # wx.ComboBox(self, parent, id, self._get_value(), size=self._get_size(choices), + # choices=choices, style=wx.CB_READONLY) super(PreferencesComboBox, self).__init__(parent, id, self._get_value(), size=self._get_size(choices), choices=choices, style=wx.CB_READONLY) @@ -90,7 +92,7 @@ def _get_size(self, choices=[]): This issue only occurs in Linux, for Mac and Windows using default size. """ if IS_LINUX and choices: - return wx.Size(max(max(len(str(s)) for s in choices) * 9, 144), 20) + return wx.Size(max(max(len(str(s)) for s in choices) * 9, 144), 30) return wx.DefaultSize def on_select(self, event): diff --git a/src/robotide/ui/preview.py b/src/robotide/ui/preview.py index 9f6f8f5aa..6137bf60e 100644 --- a/src/robotide/ui/preview.py +++ b/src/robotide/ui/preview.py @@ -13,7 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. - +import builtins +import wx import wx.html from io import StringIO from ..pluginapi import Plugin, TreeAwarePluginMixin @@ -23,6 +24,9 @@ from ..widgets import ButtonWithHandler, Font from ..utils import Printing +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class PreviewPlugin(Plugin, TreeAwarePluginMixin): """Provides preview of the test data in HTML, TSV and TXT formats.""" @@ -33,9 +37,9 @@ def __init__(self, application): self._panel = None def enable(self): - self.register_action(ActionInfo('Tools', 'Preview', self.on_show_preview, + self.register_action(ActionInfo(_('Tools'), _('Preview'), self.on_show_preview, shortcut='F6', - doc='Show preview of the current file', + doc=_('Show preview of the current file'), position=71)) self.subscribe(self.on_tree_selection, RideTreeSelection) self.subscribe(self.on_tab_changed, RideNotebookTabChanged) @@ -54,7 +58,7 @@ def is_focused(self): return self.tab_is_visible(self._panel) def on_show_preview(self, event): - _ = event + __ = event if not self._panel: self._panel = PreviewPanel(self, self.notebook) self.show_tab(self._panel) @@ -75,7 +79,7 @@ def _update_preview(self, message): class PreviewPanel(wx.Panel): - _formats = ['HTML', 'Text (Spaces)', 'Text (Pipes)'] + _formats = ['HTML', _('Text (Spaces)'), _('Text (Pipes)')] def __init__(self, parent, notebook): wx.Panel.__init__(self, notebook) @@ -92,7 +96,7 @@ def __init__(self, parent, notebook): else: box.Add(self._print_button(), 1, wx.EXPAND) self.Sizer.Add(box) - notebook.AddPage(self, "Preview") + notebook.AddPage(self, _("Preview")) def on_print(self, evt): _ = evt @@ -109,13 +113,13 @@ def _pipe_separated(self): return 'Pipes' in self._format def _chooser(self): - chooser = wx.RadioBox(self, label='Format', choices=self._formats) + chooser = wx.RadioBox(self, label=_('Format'), choices=self._formats) chooser.SetStringSelection(self._format) self.Bind(wx.EVT_RADIOBOX, self.on_type_changed, chooser) return chooser def _print_button(self): - return ButtonWithHandler(self, 'Print') + return ButtonWithHandler(self, _('Print'), mk_handler='Print', handler=self.on_print) @property def _view(self): diff --git a/src/robotide/ui/review.py b/src/robotide/ui/review.py index 3c7a31615..6af819d83 100644 --- a/src/robotide/ui/review.py +++ b/src/robotide/ui/review.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import os import re import time @@ -28,11 +29,14 @@ from ..usages.commands import FindUsages from ..widgets import ButtonWithHandler, Label, RIDEDialog +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class ReviewDialog(RIDEDialog): def __init__(self, controller, frame): - RIDEDialog.__init__(self, parent=frame, title="Search unused keywords", + RIDEDialog.__init__(self, parent=frame, title=_("Search unused keywords"), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.FRAME_FLOAT_ON_PARENT) # set Left to Right direction (while we don't have localization) @@ -59,12 +63,12 @@ def _build_ui(self): self._build_controls() def _build_header(self): - label_introduction = wx.StaticText(self, label="This dialog helps you finding unused " - "keywords within your opened project.\nIf " - "you want, you can restrict the search to " - "a set of files with the filter.") - label_filter_is = wx.StaticText(self, label='Filter is') - self.label_filter_status = wx.StaticText(self, label='inactive') + label_introduction = wx.StaticText(self, label=_("This dialog helps you finding unused " + "keywords within your opened project.\nIf " + "you want, you can restrict the search to " + "a set of files with the filter.")) + label_filter_is = wx.StaticText(self, label=_('Filter is')) + self.label_filter_status = wx.StaticText(self, label=_('inactive')) header_sizer = wx.BoxSizer(wx.HORIZONTAL) header_sizer.Add(label_introduction, 0, wx.ALL | wx.EXPAND, 3) header_sizer.AddStretchSpacer(1) @@ -77,32 +81,32 @@ def _build_header(self): self.Sizer.Add(header_sizer, 0, wx.ALL | wx.EXPAND, 3) def _build_filter(self): - self._filter_pane = MyCollapsiblePane(self, label="Filter", + self._filter_pane = MyCollapsiblePane(self, label=_("Filter"), style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) self._filter_pane.SetBackgroundColour(Colour(self.color_background)) self._filter_pane.SetForegroundColour(Colour(self.color_foreground)) self._filter_input = wx.TextCtrl(self._filter_pane.GetPane(), size=(-1, 20)) self._filter_regex_switch = wx.CheckBox(self._filter_pane.GetPane(), - wx.ID_ANY, label="Use RegEx") + wx.ID_ANY, label=_("Use RegEx")) self._filter_info = wx.StaticText(self._filter_pane.GetPane(), - label="Here you can define one or more strings separated" - " by comma (e.g. common,abc,123).\nThe filter " - "matches if at least one string is part of " - "the filename.\nIf you don\'t enter any strings, " - "all opened files are included") - filter_source_box = wx.StaticBox(self._filter_pane.GetPane(), label="Search") + label=_("Here you can define one or more strings separated" + " by comma (e.g. common,abc,123).\nThe filter " + "matches if at least one string is part of " + "the filename.\nIf you don\'t enter any strings, " + "all opened files are included")) + filter_source_box = wx.StaticBox(self._filter_pane.GetPane(), label=_("Search")) self._filter_source_testcases = wx.CheckBox(self._filter_pane.GetPane(), wx.ID_ANY, - label="Test case files") + label=_("Test case files")) self._filter_source_resources = wx.CheckBox(self._filter_pane.GetPane(), wx.ID_ANY, - label="Resource files") + label=_("Resource files")) self._filter_mode = wx.RadioBox(self._filter_pane.GetPane(), - label="Mode", - choices=["exclude", "include"]) + label=_("Mode"), + choices=[_("exclude"), _("include")]) self._filter_test_button = wx.Button(self._filter_pane.GetPane(), - wx.ID_INFO, label="Test the filter") + wx.ID_INFO, label=_("Test the filter")) self._filter_test_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._filter_test_button.SetForegroundColour(Colour(self.color_secondary_foreground)) filter_box_sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -132,14 +136,14 @@ def _build_unused_keywords(self): panel_unused_kw.SetSizer(sizer_unused_kw) self._unused_kw_list = ResultListCtrl(panel_unused_kw, style=wx.LC_REPORT) - self._unused_kw_list.InsertColumn(0, "Keyword", width=400) - self._unused_kw_list.InsertColumn(1, "File", width=250) + self._unused_kw_list.InsertColumn(0, _("Keyword"), width=400) + self._unused_kw_list.InsertColumn(1, _("File"), width=250) self._unused_kw_list.SetMinSize((650, 250)) self._unused_kw_list.set_dialog(self) self._unused_kw_list.SetBackgroundColour(Colour(self.color_background)) self._unused_kw_list.SetForegroundColour(Colour(self.color_foreground)) self._delete_button = wx.Button(panel_unused_kw, wx.ID_ANY, - 'Delete marked keywords') + _('Delete marked keywords')) self._delete_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._delete_button.SetForegroundColour(Colour(self.color_secondary_foreground)) sizer_unused_kw.Add(self._unused_kw_list, 1, wx.ALL | wx.EXPAND, 3) @@ -150,13 +154,13 @@ def _build_unused_keywords(self): else: unused_kw_controls.Add(self._delete_button, 0, wx.ALL, 3) sizer_unused_kw.Add(unused_kw_controls, 0, wx.ALL | wx.EXPAND, 3) - self._notebook.AddPage(panel_unused_kw, "Unused Keywords") + self._notebook.AddPage(panel_unused_kw, _("Unused Keywords")) def _build_controls(self): - self._search_button = ButtonWithHandler(self, 'Search') + self._search_button = ButtonWithHandler(self, _('Search'), handler=self.on_search) self._search_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._search_button.SetForegroundColour(Colour(self.color_secondary_foreground)) - self._abort_button = ButtonWithHandler(self, 'Abort') + self._abort_button = ButtonWithHandler(self, _('Abort'), handler=self.on_abort) self._abort_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._abort_button.SetForegroundColour(Colour(self.color_secondary_foreground)) self._status_label = Label(self, label='') @@ -215,15 +219,15 @@ def _update_filter_mode(self, event): self._runner.set_filter_mode(event.GetInt() == 0) def _update_filter_source_testcases(self, event): - _ = event + __ = event self._runner.set_filter_source_testcases(self._filter_source_testcases.IsChecked()) def _update_filter_source_resources(self, event): - _ = event + __ = event self._runner.set_filter_source_resources(self._filter_source_resources.IsChecked()) def _update_filter_regex(self, event): - _ = event + __ = event self._runner.set_filter_use_regex(self._filter_regex_switch.IsChecked()) def _toggle_filter_active(self, event): @@ -235,25 +239,25 @@ def _toggle_filter_active(self, event): def _disable_filter(self): self._runner.set_filter_active(False) - self.label_filter_status.SetLabel('inactive') + self.label_filter_status.SetLabel(_('inactive')) self.label_filter_status.SetForegroundColour(wx.RED) def _enable_filter(self): self._runner.set_filter_active(True) - self.label_filter_status.SetLabel('active') + self.label_filter_status.SetLabel(_('active')) self.label_filter_status.SetForegroundColour((0, 200, 0)) def on_search(self, event): - _ = event + __ = event self.begin_searching() self._runner.run_review() def on_abort(self, event): - _ = event + __ = event self.end_searching() def on_delete_marked_keywords(self, event): - _ = event + __ = event item = self._unused_kw_list.get_next_checked_item() while item: index = item[0] @@ -263,20 +267,20 @@ def on_delete_marked_keywords(self, event): self._unused_kw_list.DeleteItem(index) self._unused_kw_list.RemoveClientData(item_id) kw.delete() - self._update_notebook_text("Unused Keywords (%d)" % self._unused_kw_list.GetItemCount()) + self._update_notebook_text(_("Unused Keywords (%d)") % self._unused_kw_list.GetItemCount()) self.update_status("") item = self._unused_kw_list.get_next_checked_item() self.item_in_kw_list_checked() def on_show_files_to_be_searched(self, event): - _ = event + __ = event df_list = self._runner.get_datafile_list() if not df_list: - string_list = "(None)" + string_list = _("(None)") else: string_list = "\n".join(df.name for df in df_list) - message = "Keywords of the following files will be included in the search:\n\n"+string_list - dlg = RIDEDialog(parent=self, title="Included files", message=message, style=wx.OK | wx.ICON_INFORMATION) + message = _("Keywords of the following files will be included in the search:\n\n")+string_list + dlg = RIDEDialog(parent=self, title=_("Included files"), message=message, style=wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() def on_result_selected(self, event): @@ -313,7 +317,7 @@ def begin_searching(self): def _clear_search_results(self): self._unused_kw_list.ClearAll() - self._update_notebook_text('Unused Keywords') + self._update_notebook_text(_('Unused Keywords')) self._delete_button.Disable() self._status_label.SetLabel('') self._search_model.clear_search() @@ -332,7 +336,7 @@ def _update_unused_keywords(self, dots): count_before = self._unused_kw_list.GetItemCount() for index, kw in list(enumerate(self._search_model.keywords))[count_before:]: self.add_result_unused_keyword(index, kw) - self.update_status("Searching.%s \t- %s" % (dots, self._search_model.status)) + self.update_status(_("Searching.%s \t- %s") % (dots, self._search_model.status)) if not self._search_model.searching: self.end_searching() @@ -346,9 +350,8 @@ def update_status(self, message, increase=1): def end_searching(self): self._dots.stop() self._search_model.end_search() - self._update_notebook_text('Unused Keywords (%d)' % (self._unused_kw_list.GetItemCount())) - self.update_status("Search finished - Found %d Unused Keywords" % - (self._unused_kw_list.GetItemCount())) + self._update_notebook_text(_('Unused Keywords (%d)') % (self._unused_kw_list.GetItemCount())) + self.update_status(_("Search finished - Found %d Unused Keywords") % (self._unused_kw_list.GetItemCount())) self._unused_kw_list.Enable() self._abort_button.Disable() self._filter_pane.Enable() @@ -396,10 +399,10 @@ def run_review(self): def _run(self): self._stop_requested = False - self._model.status = 'listing datafiles' + self._model.status = _('listing datafiles') for df in self.get_datafile_list(): libname = os.path.basename(df.source).rsplit('.', 1)[0] - self._model.status = 'searching from ' + str(libname) + self._model.status = _('searching from ') + str(libname) for keyword in df.keywords: time.sleep(0) # GIVE SPACE TO OTHER THREADS -- Thread.yield in Java self._model.status = "%s.%s" % (libname, keyword.name) diff --git a/src/robotide/ui/tagdialogs.py b/src/robotide/ui/tagdialogs.py index 1e9ee3724..c1e9e9ed7 100644 --- a/src/robotide/ui/tagdialogs.py +++ b/src/robotide/ui/tagdialogs.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import re import wx @@ -24,13 +25,16 @@ from ..publish import RideOpenTagSearch from ..widgets import ButtonWithHandler, PopupMenuItems, RIDEDialog +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class ViewAllTagsDialog(RIDEDialog, listmix.ColumnSorterMixin): def __init__(self, controller, frame): style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | \ wx.FRAME_FLOAT_ON_PARENT - RIDEDialog.__init__(self, parent=frame, title="View all tags", size=(500, 400), style=style) + RIDEDialog.__init__(self, parent=frame, title=_("View all tags"), size=(500, 400), style=style) self.SetBackgroundColour(Colour(self.color_background)) self.SetForegroundColour(Colour(self.color_foreground)) # set Left to Right direction (while we don't have localization) @@ -69,20 +73,22 @@ def _build_tag_lister(self): panel_tag_vw.SetSizer(sizer_tag_vw) self._tags_list = TagsListCtrl(panel_tag_vw, style=wx.LC_REPORT, color_bg=self.color_secondary_background, color_fg=self.color_secondary_foreground) - self._tags_list.InsertColumn(0, "Tag", width=200) - self._tags_list.InsertColumn(1, "Occurrences", width=25, + self._tags_list.InsertColumn(0, _("Tag"), width=200) + self._tags_list.InsertColumn(1, _("Occurrences"), width=25, format=wx.LIST_FORMAT_CENTER) self._tags_list.SetMinSize((450, 250)) self._tags_list.set_dialog(self) sizer_tag_vw.Add(self._tags_list, 1, wx.ALL | wx.EXPAND, 3) - self._notebook.AddPage(panel_tag_vw, "The List") + self._notebook.AddPage(panel_tag_vw, _("The List")) def _build_controls(self): - self._clear_button = ButtonWithHandler(self, 'Refresh', self.on_clear) - self._show_tagged_tests_button = ButtonWithHandler( - self, 'Included Tag Search') - self._show_excluded_tests_button = ButtonWithHandler( - self, 'Excluded Tag Search') + self._clear_button = ButtonWithHandler(self, _('Refresh'), handler=self.on_clear) + self._show_tagged_tests_button = ButtonWithHandler(self, _('Included Tag Search'), + mk_handler='Included Tag Search', + handler=self.on_included_tag_search) + self._show_excluded_tests_button = ButtonWithHandler(self, _('Excluded Tag Search'), + mk_handler='Excluded Tag Search', + handler=self.on_excluded_tag_search) self._clear_button.SetBackgroundColour(Colour(self.color_secondary_background)) self._clear_button.SetForegroundColour(Colour(self.color_secondary_foreground)) self._show_tagged_tests_button.SetBackgroundColour(Colour(self.color_secondary_background)) @@ -138,10 +144,9 @@ def _execute(self): self.SortListItems(self.sort_state[0], self.sort_state[1]) def update_footer(self): - footer_string = ("Total tests %d, Tests with tags %d, Unique tags %d\n" - "Currently selected tests %d") % \ - (self.total_test_cases, len(self.tagged_test_cases), - self.unique_tags, len(self.selected_tests)) + footer_string = (_("Total tests %d, Tests with tags %d, Unique tags %d\n" + "Currently selected tests %d")) % (self.total_test_cases, len(self.tagged_test_cases), + self.unique_tags, len(self.selected_tests)) self._footer_text.SetLabel(footer_string) def show_dialog(self): @@ -197,26 +202,26 @@ def _add_checked_tags_into_list(self): return tags def on_included_tag_search(self, event): - _ = event + __ = event included_tags = self._add_checked_tags_into_list() RideOpenTagSearch(includes=' '.join(included_tags), excludes='').publish() def on_excluded_tag_search(self, event): - _ = event + __ = event excluded_tags = self._add_checked_tags_into_list() RideOpenTagSearch(includes='', excludes=' '.join(excluded_tags)).publish() def on_clear(self, event): - _ = event + __ = event self._execute() for _, tests in self._results: self.tree.DeselectTests(tests) self.update_footer() def on_select_all(self, event): - _ = event + __ = event all_tests = [] for _, tests in self._results: all_tests += tests @@ -225,9 +230,8 @@ def on_select_all(self, event): def on_right_click(self, event): self._index = event.GetIndex() - menu_items = ["Select all", "Clear", "---", "Rename", "Delete", "---", - "Show tests with this tag", - "Show tests without this tag"] + menu_items = [_("Select all"), _("Clear"), "---", _("Rename"), _("Delete"), "---", + _("Show tests with this tag"), _("Show tests without this tag")] self.tree._popup_creator.show(self, PopupMenuItems(self, menu_items), self._controller) def on_select_item(self, event): @@ -235,27 +239,27 @@ def on_select_item(self, event): self._tags_list.CheckItem(self._index, not self._tags_list.IsChecked(self._index)) def on_show_tests_with_this_tag(self, event): - _ = event + __ = event if self._index == -1: return _, tag_name = self._tags_list.get_tag(self._index) RideOpenTagSearch(includes=tag_name, excludes="").publish() def on_show_tests_without_this_tag(self, event): - _ = event + __ = event if self._index == -1: return _, tag_name = self._tags_list.get_tag(self._index) RideOpenTagSearch(includes="", excludes=tag_name).publish() def on_rename(self, event): - _ = event + __ = event if self._index == -1: return tests, tag_name = self._tags_list.get_tag(self._index) tags_to_rename = self._tags[tag_name.lower()] - name = wx.GetTextFromUser(message="Renaming tag '%s'." % tag_name, default_value=tag_name, - caption='Rename').strip() + name = wx.GetTextFromUser(message=_("Renaming tag '%s'.") % tag_name, default_value=tag_name, + caption=_('Rename')).strip() if name: for tag in tags_to_rename: tag.controller.execute(ChangeTag(tag, name)) @@ -264,13 +268,12 @@ def on_rename(self, event): self.tree.DeselectTests(tests) def on_delete(self, event): - _ = event + __ = event if self._index == -1: return tests, tag_name = self._tags_list.get_tag(self._index) tags_to_delete = self._tags[tag_name.lower()] - if wx.MessageBox( - "Delete a tag '%s' ?" % tag_name, caption='Confirm', + if wx.MessageBox(_("Delete a tag '%s' ?") % tag_name, caption=_('Confirm'), style=wx.YES_NO | wx.ICON_QUESTION) == wx.YES: for tag in tags_to_delete: tag.controller.execute(ChangeTag(tag, '')) diff --git a/src/robotide/ui/treenodehandlers.py b/src/robotide/ui/treenodehandlers.py index 46e25559b..e55bdf365 100644 --- a/src/robotide/ui/treenodehandlers.py +++ b/src/robotide/ui/treenodehandlers.py @@ -156,19 +156,19 @@ def on_find_usages(self, event): pass def on_select_all_tests(self, event): - _ = event + __ = event self._tree.SelectAllTests(self._node) def on_deselect_all_tests(self, event): - _ = event + __ = event self._tree.SelectAllTests(self._node, False) def on_select_only_failed_tests(self, event): - _ = event + __ = event self._tree.SelectFailedTests(self._node) def on_select_only_passed_tests(self, event): - _ = event + __ = event self._tree.SelectPassedTests(self._node) def on_safe_delete(self, event): @@ -187,7 +187,7 @@ def on_include(self, event): class _CanBeRenamed(object): def on_rename(self, event): - _ = event + __ = event self._tree.label_editor.on_label_edit() def begin_label_edit(self): @@ -261,17 +261,17 @@ def rename(new_name): return False def on_sort_tests(self, event): - _ = event + __ = event """Sorts the tests inside the treenode""" self.controller.execute(SortTests()) def on_sort_keywords(self, event): - _ = event + __ = event """Sorts the keywords inside the treenode""" self.controller.execute(ctrlcommands.SortKeywords()) def on_sort_variables(self, event): - _ = event + __ = event """Sorts the variables inside the treenode""" self.controller.execute(SortVariables()) @@ -289,7 +289,7 @@ def set_rendered(self): self._rendered = True def on_change_format(self, event): - _ = event + __ = event ChangeFormatDialog(self.controller).execute() def on_new_user_keyword(self, event): @@ -357,11 +357,11 @@ def __init__(self, *args): _ActionHandler._label_collapse_all]) def on_expand_all(self, event): - _ = event + __ = event self._tree.ExpandAllSubNodes(self._node) def on_collapse_all(self, event): - _ = event + __ = event self._tree.CollapseAllSubNodes(self._node) def on_new_suite(self, event): @@ -449,7 +449,7 @@ def on_exclude(self, event): 'You must save data before excluding.') def on_remove_read_only(self, event): - _ = event + __ = event def return_true(): return True @@ -457,7 +457,7 @@ def return_true(): self.controller.execute(ctrlcommands.RemoveReadOnly()) def on_open_containing_folder(self, event): - _ = event + __ = event self.controller.execute(ctrlcommands.OpenContainingFolder()) def on_find_usages(self, event): @@ -518,7 +518,7 @@ def on_exclude(self, event): 'You must save data before excluding.') def on_remove_read_only(self, event): - _ = event + __ = event def return_true(): return True @@ -526,7 +526,7 @@ def return_true(): self.controller.execute(ctrlcommands.RemoveReadOnly()) def on_open_containing_folder(self, event): - _ = event + __ = event self.controller.execute(ctrlcommands.OpenContainingFolder()) def on_new_test_case(self, event): @@ -578,12 +578,12 @@ def on_copy(self, event): dlg.Destroy() def on_move_up(self, event): - _ = event + __ = event if self.controller.move_up(): self._tree.move_up(self._node) def on_move_down(self, event): - _ = event + __ = event if self.controller.move_down(): self._tree.move_down(self._node) @@ -663,12 +663,12 @@ def rename(self, new_name): self.controller.execute(ctrlcommands.UpdateVariableName(new_name)) def on_move_up(self, event): - _ = event + __ = event if self.controller.move_up(): self._tree.move_up(self._node) def on_move_down(self, event): - _ = event + __ = event if self.controller.move_down(): self._tree.move_down(self._node) @@ -697,7 +697,7 @@ def item(self): return None def on_add_resource(self, event): - _ = event + __ = event path = RobotFilePathDialog( self._tree.GetParent(), self.controller, self._settings).execute() if path: diff --git a/src/robotide/ui/treeplugin.py b/src/robotide/ui/treeplugin.py index 7e64e938f..e16f3899a 100644 --- a/src/robotide/ui/treeplugin.py +++ b/src/robotide/ui/treeplugin.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import builtins import os import wx @@ -42,6 +43,10 @@ from .treenodehandlers import ResourceRootHandler, action_handler_class, ResourceFileHandler from .images import TreeImageList +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + + TREETEXTCOLOUR = Colour(0xA9, 0xA9, 0xA9) _TREE_ARGS = {'style': wx.HSCROLL | wx.VSCROLL, @@ -62,6 +67,7 @@ class TreePlugin(Plugin): def __init__(self, application): Plugin.__init__(self, application, default_settings=self.defaults) + print("DEBUG: treeplugin.py TreePlugin __init__") self._app = application self.settings = self._app.settings.config_obj['Plugins']['Tree'] self._parent = None @@ -96,7 +102,7 @@ def register_frame(self, parent=None): else: register = self._mgr.AddPane register(self._tree, wx.lib.agw.aui.AuiPaneInfo().Name("tree_content"). - Caption("Test Suites").LeftDockable(True)) + Caption(_("Test Suites")).LeftDockable(True)) self._mgr.Update() def enable(self): @@ -139,7 +145,7 @@ def set_editor(self, editor): self._tree.set_editor(editor) def on_show_tree(self, event): - _ = event + __ = event if not self._parent: self._parent = self.frame if not self._tree: # This is not needed because tree is always created @@ -186,22 +192,23 @@ def on_tree_selection(self, message): self._tree.tree_node_selected(message.item) def on_tab_changed(self, event): - _ = event + __ = event self._update_tree() def _update_tree(self, event=None): - _ = event + __ = event self._tree.populate(self._model) self._tree.refresh_view() self._tree.Update() class Tree(treemixin.DragAndDrop, customtreectrl.CustomTreeCtrl, wx.Panel): - _RESOURCES_NODE_LABEL = 'External Resources' def __init__(self, parent, action_registerer, settings=None): from ..controller.ui.treecontroller import TreeController - + self._RESOURCES_NODE_LABEL = _('External Resources') + print(f"DEBUG: treeplugin.py Tree after importing TreeController __init__ " + f"translated label={self._RESOURCES_NODE_LABEL}") self._checkboxes_for_tests = False self._test_selection_controller = self._create_test_selection_controller() self.controller = TreeController(self, action_registerer, settings=settings, @@ -334,7 +341,7 @@ def _mark_excludes(self, message): def _set_item_excluded(self, node): self.SetItemTextColour(node, wx.TheColourDatabase.Find(colourName="GREY")) self.SetItemItalic(node, True) - self.SetItemText(node, "%s (excluded)" % self.GetItemText(node)) + self.SetItemText(node, _("%s (excluded)") % self.GetItemText(node)) def _handle_import_setting_message(self, message): if message.is_resource(): @@ -1008,7 +1015,7 @@ def SelectPassedTests(self, item): self._test_selection_controller.select_all(test_controllers) def on_close(self, event): - _ = event + __ = event print("DEBUG: Tree OnClose hidding") self.Hide() @@ -1033,7 +1040,7 @@ def on_left_arrow(self, event): event.Skip() def on_right_click(self, event): - _ = event + __ = event if not self._right_click: self._right_click = True handler = None @@ -1137,7 +1144,7 @@ def on_begin_label_edit(self, event): # a bug if we don't Veto this event def on_label_edit(self, event=None): - _ = event + __ = event if not self._on_label_edit_called: self._on_label_edit_called = True handler = self._tree.controller.get_handler() @@ -1165,7 +1172,7 @@ def _stop_editing(self): control.StopEditing() def on_delete(self, event): - _ = event + __ = event editor = self._tree.GetEditControl() if editor and wx.Window.FindFocus() == editor: start, end = editor.GetSelection() diff --git a/src/robotide/usages/usagesdialog.py b/src/robotide/usages/usagesdialog.py index 1d1f156b5..a61f2390f 100644 --- a/src/robotide/usages/usagesdialog.py +++ b/src/robotide/usages/usagesdialog.py @@ -13,13 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. - +import builtins import wx from wx import Colour from ..widgets import RIDEDialog, VirtualList, VerticalSizer, ImageList, ImageProvider, ButtonWithHandler from ..widgets.list import ListModel +_ = wx.GetTranslation # To keep linter/code analyser happy +builtins.__dict__['_'] = wx.GetTranslation + class UsagesDialog(RIDEDialog): @@ -52,12 +55,12 @@ def begin_searching(self): self._dots.start() def _update_searching(self, dots): - self.SetTitle("'%s' - %d matches found - Searching%s" % (self._name, self.usages.total_usages, dots)) + self.SetTitle(_("'%s' - %d matches found - Searching%s") % (self._name, self.usages.total_usages, dots)) self.usage_list.refresh_items() def end_searching(self): self._dots.stop() - self.SetTitle("'%s' - %d matches" % (self._name, self.usages.total_usages)) + self.SetTitle(_("'%s' - %d matches") % (self._name, self.usages.total_usages)) self.usage_list.refresh_items() def _usage_selected(self, idx): @@ -79,7 +82,8 @@ def __init__(self, name, highlight, controller, usages=None): UsagesDialog.__init__(self, name, usages=usages) def _add_view_components(self): - button = ButtonWithHandler(self, 'Go to definition') + button = ButtonWithHandler(self, _('Go to definition'), mk_handler='Go to definition', + handler=self.on_go_to_definition) button.SetBackgroundColour(Colour(self.color_secondary_background)) button.SetForegroundColour(Colour(self.color_secondary_foreground)) self.Sizer.Add(button, 0, wx.ALL, 3) @@ -136,7 +140,7 @@ class UsagesListModel(_UsagesListModel): def __init__(self, usages): _UsagesListModel.__init__(self, usages) - self.headers = ['Location', 'Usage', 'Source'] + self.headers = [_('Location'), _('Usage'), _('Source')] def item_text(self, row, col): u = self.usage(row) @@ -169,7 +173,7 @@ class RecursiveResourceImportListModel(_UsagesListModel): def __init__(self, usages): _UsagesListModel.__init__(self, usages) - self.headers = ['Imported name', 'Imported Location', 'Importing Name', 'Importing Location'] + self.headers = [_('Imported name'), _('Imported Location'), _('Importing Name'), _('Importing Location')] def item_text(self, row, col): u = self.usage(row) diff --git a/src/robotide/version.py b/src/robotide/version.py index ce5cbb2bb..bd6b76514 100644 --- a/src/robotide/version.py +++ b/src/robotide/version.py @@ -14,4 +14,4 @@ # limitations under the License. # # Automatically generated by `tasks.py`. -VERSION = 'v2.0.9dev1' +VERSION = 'v2.1dev1' diff --git a/src/robotide/widgets/button.py b/src/robotide/widgets/button.py index c8eef63a2..78c2c89b9 100644 --- a/src/robotide/widgets/button.py +++ b/src/robotide/widgets/button.py @@ -19,7 +19,7 @@ class ButtonWithHandler(wx.Button): - def __init__(self, parent, label, handler=None, width=-1, + def __init__(self, parent, label, mk_handler=None, handler=None, width=-1, height=25, color_secondary_foreground='black', color_secondary_background='light grey'): wx.Button.__init__(self, parent, label=label, size=(width, height)) @@ -27,7 +27,10 @@ def __init__(self, parent, label, handler=None, width=-1, self.SetOwnBackgroundColour(Colour(color_secondary_background)) self.SetForegroundColour(Colour(color_secondary_foreground)) self.SetOwnForegroundColour(Colour(color_secondary_foreground)) - if not handler: - name = 'on_%s' % label.strip().replace(' ', '_').lower() + if not handler or mk_handler: + if not mk_handler: + name = 'on_%s' % label.strip().replace(' ', '_').lower() + else: + name = 'on_%s' % mk_handler.strip().replace(' ', '_').lower() handler = getattr(parent, name) parent.Bind(wx.EVT_BUTTON, handler, self) diff --git a/src/robotide/widgets/popupmenu.py b/src/robotide/widgets/popupmenu.py index 974735d42..acacfdd33 100644 --- a/src/robotide/widgets/popupmenu.py +++ b/src/robotide/widgets/popupmenu.py @@ -72,12 +72,15 @@ def _add_item(self, item): class PopupMenuItems(object): - def __init__(self, parent=None, menu_names=None): + def __init__(self, parent=None, menu_names=None, menu_names_nt=None): self._items = [] if menu_names is None: menu_names = [] - for item in menu_names: - self.add_menu_item(PopupMenuItem(item, parent=parent)) + if not menu_names_nt: + menu_names_nt = menu_names + for item, item_nt in zip(menu_names, menu_names_nt): + print(f"DEBUG: PopupMenuItem value of item={item} item_nt={item_nt}") + self.add_menu_item(PopupMenuItem(item, name_nt=item_nt, parent=parent)) def __iter__(self): return iter(self._items) @@ -91,9 +94,11 @@ def add_separator(self): class PopupMenuItem(object): - def __init__(self, name, ccallable=None, parent=None): + def __init__(self, name, name_nt=None, ccallable=None, parent=None): self.name = name - self.callable = self._get_callable(name, ccallable, parent) + nname = name_nt if name_nt else name + print(f"DEBUG: PopupMenuItem value of nname={nname}") + self.callable = self._get_callable(nname, ccallable, parent) @staticmethod def _get_callable(name, ccallable, parent): diff --git a/tools/geni18n.py b/tools/geni18n.py new file mode 100644 index 000000000..58f1baafe --- /dev/null +++ b/tools/geni18n.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +""" +This will generate the .pot and .mo files for the application domain and +languages defined below. + +The .po and .mo files are placed as per convention in + +"appfolder/locale/lang/LC_MESSAGES" + +The .pot file is placed in the locale folder. + +This script or something similar should be added to your build process. + +The actual translation work is normally done using a tool like poEdit or +similar, it allows you to generate a particular language catalog from the .pot +file or to use the .pot to merge new translations into an existing language +catalog. + +""" + + +# we remove English as source code strings are in English +supportedLang = ['pt_PT', 'pt_BR'] +# for l in appC.supLang: +# if l != u"en": +# supportedLang.append(l) + +import os +import sys +import subprocess + +# DEBUG: appFolder = os.getcwd() +# DEBUG: appFolder = os.path.join(appFolder, '../src/robotide') +appFolder = '/home/helio/github/RIDE/src/robotide' +langDomain = 'RIDE' + +# setup some stuff to get at Python I18N tools/utilities + +pyExe = sys.executable +pyFolder = os.path.split(pyExe)[0] +pyI18nFolder = os.path.join(appFolder, 'i18n') +pyGettext = os.path.join(pyFolder, 'pygettext.py') +pyMsgfmt = os.path.join(pyFolder, 'msgfmt.py') +outFolder = os.path.join(appFolder, 'locale') + +# build command for pygettext +gtOptions = '-a -d %s -o %s.pot -p %s %s' +tCmd = pyExe + ' ' + pyGettext + ' ' + (gtOptions % (langDomain, + langDomain, + outFolder, + appFolder)) +print ("Generating the .pot file") +print ("cmd: %s" % tCmd) +rCode = subprocess.call(tCmd.split(' ')) +print ("return code: %s\n\n" % rCode) + +for tLang in supportedLang: + # build command for msgfmt + langDir = os.path.join(appFolder, ('locale/%s/LC_MESSAGES' % tLang)) + poFile = os.path.join(langDir, langDomain + '.po') + if os.path.isfile(poFile): + tCmd = 'msgmerge' + ' ' + '-U' + ' ' + poFile + ' ' + outFolder + '/' + langDomain + '.pot' + print("Updating .po file with .pot\n") + print ("cmd: %s" % tCmd) + rCode = subprocess.call(tCmd.split(' ')) + print ("return code: %s\n\n" % rCode) + + tCmd = pyExe + ' ' + pyMsgfmt + ' ' + poFile + print ("Generating the .mo file") + print ("cmd: %s" % tCmd) + rCode = subprocess.call(tCmd.split(' ')) + print ("return code: %s\n\n" % rCode) diff --git a/utest/action/test_action_dsl.py b/utest/action/test_action_dsl.py index d7852ae6d..94744724f 100644 --- a/utest/action/test_action_dsl.py +++ b/utest/action/test_action_dsl.py @@ -66,7 +66,7 @@ def test_create_entry_with_multi_shortcut(self): handlers = HandlerMock(on_huba=HUBA_ACTION) infos = action_info_collection(data, handlers) assert infos[0].menu_name == 'Hopla' - _check_mac(infos[0].name, u'Huba (Alt-D or Ctrl-H)', u'Huba (\u2325D or \u2318H)') + _check_mac(infos[0].name, u'Huba (Alt-D or CtrlCmd-H)', u'Huba (\u2325D or \u2318H)') assert infos[0].action == HUBA_ACTION assert infos[0].shortcut.value is None diff --git a/utest/controller/controller_creator.py b/utest/controller/controller_creator.py index 61f41f90c..d314854cd 100644 --- a/utest/controller/controller_creator.py +++ b/utest/controller/controller_creator.py @@ -60,7 +60,7 @@ def create(data): tcf = TestCaseFile() tcf.directory = '/path/to' pop = FromFilePopulator(tcf) - pop.start_table(['Test cases']) + pop.start_table(['Test cases'], lineno=1) # for row in [ [cell for cell in line.split(' ')] for line in data]: for line in data: row = line.split(' ') diff --git a/utest/controller/test_all_files_can_be_seen.py b/utest/controller/test_all_files_can_be_seen.py index 91cc62f2b..c5016445e 100644 --- a/utest/controller/test_all_files_can_be_seen.py +++ b/utest/controller/test_all_files_can_be_seen.py @@ -30,6 +30,7 @@ def tearDownClass(cls): def test_all_files_can_be_seen(self): all_files = self.project.data + print(f"DEBUG: test_all_files_can_be_seen all_files={all_files}") self.assertEqual(all_files.name, 'All Files') self.assertEqual(len(all_files.suites), 3) self._verify_names(all_files, 'Used Resource', 'Unused Resource', diff --git a/utest/controller/test_format_change.py b/utest/controller/test_format_change.py index 0a40912dc..a2243d506 100644 --- a/utest/controller/test_format_change.py +++ b/utest/controller/test_format_change.py @@ -80,13 +80,13 @@ def _assert_not_removed(self, path): class ProjectChecker(Project): - def __init__(self, namespace, settings=None, library_manager=None): + def __init__(self, namespace, settings=None, library_manager=None, file_language=None): self.removed_files = [] self.serialized_files = [] library_manager = library_manager or LibraryManager(':memory:') if not library_manager: library_manager.create_database() - Project.__init__(self, namespace, settings, library_manager) + Project.__init__(self, namespace, settings, library_manager, file_language) def save(self, controller): self.serialized_files.append(controller.source) diff --git a/utest/controller/test_modifications_on_disk.py b/utest/controller/test_modifications_on_disk.py index 675328bff..a94bbfc67 100644 --- a/utest/controller/test_modifications_on_disk.py +++ b/utest/controller/test_modifications_on_disk.py @@ -30,7 +30,7 @@ from robotide.publish import PUBLISHER from robotide.namespace.namespace import Namespace from robotide.spec.librarymanager import LibraryManager -from resources.mocks import FakeSettings +from ..resources.mocks import FakeSettings def create_test_data(path, filepath, resourcepath, initpath): diff --git a/utest/controller/test_occurrences.py b/utest/controller/test_occurrences.py index 802132a38..0ea7537a4 100644 --- a/utest/controller/test_occurrences.py +++ b/utest/controller/test_occurrences.py @@ -216,7 +216,7 @@ def test_finding_from_keyword_teardown(self): self._assert_usage('Keyword Teardown Keyword', 'Teardown') def test_finding_from_test_teardown_in_settings(self): - self._assert_usage('Test Teardown in Setting', 'Test Teardown') + self._assert_usage('Test Teardown in Setting', 'Task Teardown') def test_occurrences_in_suite_documentation_should_not_be_found(self): self._assert_no_usages('suitedocmatch') @@ -282,13 +282,13 @@ def test_occurrences_in_suite_metadata(self): assert_occurrence(self.test_ctrl, SUITE_SETUP_KEYWORD, SUITE_NAME, 'Suite Setup') assert_occurrence(self.test_ctrl, 'Test Setup Kw', - SUITE_NAME, 'Test Setup') + SUITE_NAME, 'Task Setup') assert_occurrence(self.test_ctrl, 'Test Teardown Kw', - SUITE_NAME, 'Test Teardown') + SUITE_NAME, 'Task Teardown') assert_occurrence(self.test_ctrl, 'Suite Teardown Kw', SUITE_NAME, 'Suite Teardown') assert_occurrence(self.test_ctrl, 'Test Template Kw', - SUITE_NAME, 'Test Template') + SUITE_NAME, 'Task Template') def test_occurrences_in_user_keywords(self): assert_occurrence(self.test_ctrl, KEYWORD_IN_USERKEYWORD1, @@ -527,13 +527,13 @@ def test_rename_in_suite_metadata(self): def test_rename_in_suite_test_setup(self): self._rename(SUITE_TEST_SETUP_KEYWORD, UNUSED_KEYWORD_NAME, SUITE_NAME, - 'Test Setup') + 'Task Setup') self._expected_messages() self.assertTrue(self.test_ctrl.dirty) def test_rename_in_suite_test_template(self): self._rename(SUITE_TEST_TEMPLATE_KEYWORD, UNUSED_KEYWORD_NAME, - SUITE_NAME, 'Test Template') + SUITE_NAME, 'Task Template') self._expected_messages() self.assertTrue(self.test_ctrl.dirty) diff --git a/utest/controller/test_tablecontrollers.py b/utest/controller/test_tablecontrollers.py index 3acda8c82..488af3c0c 100644 --- a/utest/controller/test_tablecontrollers.py +++ b/utest/controller/test_tablecontrollers.py @@ -71,7 +71,7 @@ def setUp(self): self._parent.mark_dirty = lambda:0 self._parent.datafile_controller = self._parent self._parent.update_namespace = lambda:0 - self._table = TestCaseFileSettingTable(lambda:0) + self._table = TestCaseFileSettingTable(lambda:0, ['en']) self.ctrl = ImportSettingsController(self._parent, self._table) self._lib1 = self.ctrl.add_library('libbi1', '', '') self._lib2 = self.ctrl.add_library('libbi2', '', '') diff --git a/utest/editor/test_texteditor.py b/utest/editor/test_texteditor.py index cd09d8e79..42035121c 100644 --- a/utest/editor/test_texteditor.py +++ b/utest/editor/test_texteditor.py @@ -18,6 +18,7 @@ import wx from wx.lib.agw.aui import AuiManager import wx.lib.agw.aui as aui +from multiprocessing import shared_memory from functools import total_ordering from robotide.ui.tagdialogs import ViewAllTagsDialog from utest.resources import datafilereader, MessageRecordingLoadObserver @@ -122,6 +123,7 @@ class TestEditorCommands(unittest.TestCase): def setUp(self): self.app = MyApp() settings = self.app.settings + self.shared_mem = shared_memory.ShareableList(['en'], name="language") self.frame = self.app.frame self.frame.actions = ActionRegisterer(AuiManager(self.frame), MenuBar(self.frame), ToolBar(self.frame), ShortcutRegistry(self.frame)) @@ -148,6 +150,8 @@ def tearDown(self): wx.CallAfter(self.app.ExitMainLoop) self.app.MainLoop() # With this here, there is no Segmentation fault # wx.CallAfter(wx.Exit) + self.shared_mem.shm.close() + self.shared_mem.shm.unlink() self.app.Destroy() self.app = None diff --git a/utest/editor/test_z_editor_plugin.py b/utest/editor/test_z_editor_plugin.py index d839472da..5ef20bcb1 100644 --- a/utest/editor/test_z_editor_plugin.py +++ b/utest/editor/test_z_editor_plugin.py @@ -16,6 +16,7 @@ import unittest from wx.lib.agw.aui import AuiManager import wx.lib.agw.aui as aui +from multiprocessing import shared_memory from functools import total_ordering from robotide.ui.tagdialogs import ViewAllTagsDialog from utest.resources import datafilereader, MessageRecordingLoadObserver @@ -166,6 +167,7 @@ class EditorPluginTest(unittest.TestCase): def setUp(self): self.app = MyApp() + self.shared_mem = shared_memory.ShareableList(['en'], name="language") settings = self.app.settings self.frame = self.app.frame self.frame.actions = ActionRegisterer(AuiManager(self.frame), MenuBar(self.frame), ToolBar(self.frame), @@ -203,6 +205,8 @@ def tearDown(self): wx.CallAfter(self.app.ExitMainLoop) # self.app.MainLoop() # With this here, there is no Segmentation fault # wx.CallAfter(wx.Exit) + self.shared_mem.shm.close() + self.shared_mem.shm.unlink() self.app.Destroy() self.app = None if os.path.exists(DATADIR): diff --git a/utest/language/__init__.py b/utest/language/__init__.py new file mode 100644 index 000000000..4d1c31f14 --- /dev/null +++ b/utest/language/__init__.py @@ -0,0 +1,23 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys + +# Workaround for relative import in non-module +# see https://stackoverflow.com/questions/16981921/relative-imports-in-python-3 +PACKAGE_PARENT = '..' +SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), + os.path.expanduser(__file__)))) +sys.path.insert(0, os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) diff --git a/utest/language/test_language.py b/utest/language/test_language.py new file mode 100644 index 000000000..bb2919698 --- /dev/null +++ b/utest/language/test_language.py @@ -0,0 +1,153 @@ +# Copyright 2023- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from utest.resources import datafilereader +from robotide.lib.compat.parsing import language + + +class TestLanguage(unittest.TestCase): + + def test_check_file_without_preamble(self): + lang = language.check_file_language(datafilereader.SIMPLE_PROJECT) + assert lang is None + + def test_check_file_with_preamble_no_lang(self): + lang = language.check_file_language(datafilereader.PREAMBLE_NO_LANG) + assert lang is None + + def test_check_file_with_preamble_unknown_lang(self): + lang = language.check_file_language(datafilereader.PREAMBLE_UNKNOWN_LANG) + assert lang is None + + def test_check_file_lang_bg(self): + lang = language.check_file_language(datafilereader.VALID_LANG_BG) + assert lang == ['bg'] + + def test_check_file_lang_bs(self): + lang = language.check_file_language(datafilereader.VALID_LANG_BS) + assert lang == ['bs'] + + def test_check_file_lang_cs(self): + lang = language.check_file_language(datafilereader.VALID_LANG_CS) + assert lang == ['cs'] + + def test_check_file_lang_de(self): + lang = language.check_file_language(datafilereader.VALID_LANG_DE) + assert lang == ['de'] + + def test_check_file_lang_es(self): + lang = language.check_file_language(datafilereader.VALID_LANG_ES) + assert lang == ['es'] + + def test_check_file_lang_en(self): + lang = language.check_file_language(datafilereader.VALID_LANG_EN) + assert lang == ['en'] + + def test_check_file_lang_fr(self): + lang = language.check_file_language(datafilereader.VALID_LANG_FR) + assert lang == ['fr'] + + def test_check_file_lang_fi(self): + lang = language.check_file_language(datafilereader.VALID_LANG_FI) + assert lang == ['fi'] + + def test_check_file_lang_hi(self): + lang = language.check_file_language(datafilereader.VALID_LANG_HI) + assert lang == ['hi'] + + def test_check_file_lang_it(self): + lang = language.check_file_language(datafilereader.VALID_LANG_IT) + assert lang == ['it'] + + def test_check_file_lang_nl(self): + lang = language.check_file_language(datafilereader.VALID_LANG_NL) + assert lang == ['nl'] + + def test_check_file_lang_pl(self): + lang = language.check_file_language(datafilereader.VALID_LANG_PL) + assert lang == ['pl'] + + def test_check_file_lang_pt_br(self): + lang = language.check_file_language(datafilereader.VALID_LANG_PT_BR) + assert lang == ['pt-BR'] + + def test_check_file_lang_pt(self): + lang = language.check_file_language(datafilereader.VALID_LANG_PT) + assert lang == ['pt'] + + def test_check_file_lang_ro(self): + lang = language.check_file_language(datafilereader.VALID_LANG_RO) + assert lang == ['ro'] + + def test_check_file_lang_RU(self): + lang = language.check_file_language(datafilereader.VALID_LANG_RU) + assert lang == ['ru'] + + def test_check_file_lang_sv(self): + lang = language.check_file_language(datafilereader.VALID_LANG_SV) + assert lang == ['sv'] + + def test_check_file_lang_th(self): + lang = language.check_file_language(datafilereader.VALID_LANG_TH) + assert lang == ['th'] + + def test_check_file_lang_tr(self): + lang = language.check_file_language(datafilereader.VALID_LANG_TR) + assert lang == ['tr'] + + def test_check_file_lang_uk(self): + lang = language.check_file_language(datafilereader.VALID_LANG_UK) + assert lang == ['uk'] + + def test_check_file_lang_vi(self): + lang = language.check_file_language(datafilereader.VALID_LANG_VI) + assert lang == ['vi'] + + def test_check_file_lang_zh_cn(self): + lang = language.check_file_language(datafilereader.VALID_LANG_ZH_CN) + assert lang == ['zh-CN'] + + def test_check_file_lang_zh_tw(self): + lang = language.check_file_language(datafilereader.VALID_LANG_ZH_TW) + assert lang == ['zh-TW'] + + def test_get_headers_unknown_language_lw(self): + headers = language.get_headers_for(['Pirates'], ['Settings']) + assert list(headers) == ['settings'] + + def test_get_headers_unknown_language(self): + headers = language.get_headers_for(['Pirates'], ['Settings'], lowercase=False) + assert list(headers) == ['Settings'] + + def test_get_headers_known_language_lw(self): + headers = language.get_headers_for(['pt'], ['Settings']) + assert sorted(headers) == ['definições', 'settings'] + + def test_get_headers_known_language(self): + headers = language.get_headers_for(['pt-BR'], ['Settings'], lowercase=False) + assert sorted(headers) == ['Configurações', 'Settings'] + + def test_get_headers_multiple_languages_lw(self): + headers = language.get_headers_for(['es', 'fr', 'Chinese Simplified ', 'zh-TW'], ['Settings'], lowercase=True) + assert sorted(headers) == ['configuraciones', 'paramètres', 'settings', '設置', '设置'] + + def test_get_headers_multiple_languages(self): + headers = language.get_headers_for(['pt-BR', 'pt', 'Pirates', 'en'], ['Settings'], lowercase=False) + assert sorted(headers) == ['Configurações', 'Definições', 'Settings'] + + +if __name__ == '__main__': + unittest.main() diff --git a/utest/namespace/test_namespace.py b/utest/namespace/test_namespace.py index 7cc122180..aee4dd5e3 100644 --- a/utest/namespace/test_namespace.py +++ b/utest/namespace/test_namespace.py @@ -81,6 +81,7 @@ class ParentMock(object): source = '/tmp/example/parentmock' directory = '/tmp/example' report_invalid_syntax = lambda *args: None + language = ['en'] class _DataFileTest(unittest.TestCase): diff --git a/utest/resources/datafilereader.py b/utest/resources/datafilereader.py index f3cef47dd..cb04e2b1b 100644 --- a/utest/resources/datafilereader.py +++ b/utest/resources/datafilereader.py @@ -67,6 +67,32 @@ def _makepath(*elements): IMPORTS = _makepath('imports') +PREAMBLE_NO_LANG = _makepath('language', 'preamble_no_lang.robot') +PREAMBLE_UNKNOWN_LANG = _makepath('language', 'preamble_unknown_lang.robot') +VALID_LANG_BG = _makepath('language', 'lang_bg.robot') +VALID_LANG_BS = _makepath('language', 'lang_bs.robot') +VALID_LANG_CS = _makepath('language', 'lang_cs.robot') +VALID_LANG_DE = _makepath('language', 'lang_de.robot') +VALID_LANG_ES = _makepath('language', 'lang_es.robot') +VALID_LANG_EN = _makepath('language', 'lang_en.robot') +VALID_LANG_FR = _makepath('language', 'lang_fr.robot') +VALID_LANG_FI = _makepath('language', 'lang_fi.robot') +VALID_LANG_HI = _makepath('language', 'lang_hi.robot') +VALID_LANG_IT = _makepath('language', 'lang_it.robot') +VALID_LANG_NL = _makepath('language', 'lang_nl.robot') +VALID_LANG_PL = _makepath('language', 'lang_pl.robot') +VALID_LANG_PT_BR = _makepath('language', 'lang_pt_br.robot') +VALID_LANG_PT = _makepath('language', 'lang_pt.robot') +VALID_LANG_RO = _makepath('language', 'lang_ro.robot') +VALID_LANG_RU = _makepath('language', 'lang_ru.robot') +VALID_LANG_SV = _makepath('language', 'lang_sv.robot') +VALID_LANG_TH = _makepath('language', 'lang_th.robot') +VALID_LANG_TR = _makepath('language', 'lang_tr.robot') +VALID_LANG_UK = _makepath('language', 'lang_uk.robot') +VALID_LANG_VI = _makepath('language', 'lang_vi.robot') +VALID_LANG_ZH_CN = _makepath('language', 'lang_zh_cn.robot') +VALID_LANG_ZH_TW = _makepath('language', 'lang_zh_tw.robot') + def construct_project(datapath, temp_dir_for_excludes=None): print("DEBUG: construct_project with argpath: %s\n" % datapath) @@ -76,7 +102,7 @@ def construct_project(datapath, temp_dir_for_excludes=None): library_manager.create_database() project = Project(Namespace(settings), settings, library_manager) print("DEBUG: construct_project Project: %s\n" % project.display_name) - project.load_data(datapath) #, NullObserver()) + project.load_data(datapath) # , NullObserver()) # DEBUG print("DEBUG: Path arg is: %s\n" % datapath) return project diff --git a/utest/resources/robotdata/language/en/__init__.robot b/utest/resources/robotdata/language/en/__init__.robot new file mode 100644 index 000000000..c4289c1ee --- /dev/null +++ b/utest/resources/robotdata/language/en/__init__.robot @@ -0,0 +1,35 @@ +# This is the preamble +Language: English + +# A blank line + +*** Comments *** +This is a comments block +Second line of comments +Maybe this block is still in preamble + +*** Settings *** +Library Collections # This is a comment +Resource full_en.resource # This is a comment +Variables full_en.yaml # This is a comment +Variables full_en.json # This is a comment +Variables full_en.py # This is a comment + +Documentation This is the documentation +... A continued line of documentation + +Metadata Name Value # This is a comment + +Suite Setup Suite Keyword Suite Setup +Suite Teardown Log To Console Suite Teardown + +*** Variables *** +${myvar} 123 # This is a comment + +*** Keywords *** +Suite Keyword + [Documentation] This is the documentation + ... A continued line of documentation + [Arguments] ${arg} # This is a comment + [Tags] suite # This is a comment + Log To Console This is the Suite Keyword arg=${arg} diff --git a/utest/resources/robotdata/language/en/full_en.json b/utest/resources/robotdata/language/en/full_en.json new file mode 100644 index 000000000..c649edd3b --- /dev/null +++ b/utest/resources/robotdata/language/en/full_en.json @@ -0,0 +1,5 @@ +{ + "simple":"one", + "secondary":1234 +} + diff --git a/utest/resources/robotdata/language/en/full_en.py b/utest/resources/robotdata/language/en/full_en.py new file mode 100644 index 000000000..f38faef71 --- /dev/null +++ b/utest/resources/robotdata/language/en/full_en.py @@ -0,0 +1,4 @@ +# Sample python variables file +first = "First" +second = 2 + diff --git a/utest/resources/robotdata/language/en/full_en.resource b/utest/resources/robotdata/language/en/full_en.resource new file mode 100644 index 000000000..bc426d82e --- /dev/null +++ b/utest/resources/robotdata/language/en/full_en.resource @@ -0,0 +1,58 @@ +# This is the preamble +Language: English + +# A blank line + +*** Settings *** +Library Collections # This is a comment +Resource full_en.resource # This is a comment +Variables full_en.yaml # This is a comment +Variables full_en.json # This is a comment +Variables full_en.py # This is a comment + +Documentation This is the documentation +... A continued line of documentation + +*** Variables *** +${myvar} 123 # This is a comment + +*** Comments *** +This is a comments block +Second line of comments + +*** Keywords *** +First Resource Keyword + [Documentation] This is the documentation + ... A continued line of documentation + [Arguments] ${arg}=None # This is a comment + [Tags] first second # This is a comment + Log To Console This is the first resource keyword + +Check Logic + [Documentation] Returns True if arg in ['True', 'Yes', 'On'] + ... \nor returns False if arg in ['False', 'No', 'Off'] + [Arguments] ${arg}='True' # This is a comment + [Tags] logic # This is a comment + IF ${arg} # This is a comment + Log Arg it is True: arg=${arg} + RETURN Yes + ELSE + Log Arg it is False: arg=${arg} + RETURN No + END + +${user} is registered + No Operation + +${cart} has objects + No Operation + +${user} clicks in checkout + No Operation + +the total is presented and awaits confirmation + No Operation + +it is shown the unavailable payment method + No Operation + diff --git a/utest/resources/robotdata/language/en/full_en.yaml b/utest/resources/robotdata/language/en/full_en.yaml new file mode 100644 index 000000000..d866f019f --- /dev/null +++ b/utest/resources/robotdata/language/en/full_en.yaml @@ -0,0 +1,7 @@ +main_variable: + secondary: + "Test" + +secondary_variable: + 1234 + diff --git a/utest/resources/robotdata/language/en/full_task_en.robot b/utest/resources/robotdata/language/en/full_task_en.robot new file mode 100644 index 000000000..d74895e5a --- /dev/null +++ b/utest/resources/robotdata/language/en/full_task_en.robot @@ -0,0 +1,81 @@ +# This is the preamble +Language: English + +# A blank line + +*** Settings *** +Library Process # This is a comment +Resource full_en.resource # This is a comment +Variables full_en.yaml # This is a comment +Variables full_en.json # This is a comment +Variables full_en.py # This is a comment + +Documentation This is the documentation +... A continued line of documentation + +Metadata Name Value # This is a comment + +Suite Setup Log To Console Suite Setup +Suite Teardown Log To Console Suite Teardown + +*** Variables *** +${myvar} 123 # This is a comment + +*** Comments *** +This is a comments block +Second line of comments + +*** Tasks *** +first task + [Documentation] This is the documentation + ... A continued line of documentation + [Setup] Log To Console Task Setup + [Teardown] Log To Console Task Teardown + [Timeout] 60 + [Tags] first second + First Keyword + ${first}= Check Logic 'Yes' + ${second}= Check Logic 'Off' + Log Variables + Log Task executed with success, first=${first}, second=${second} + +second task + [Template] First Keyword # This is a comment + [Setup] Log To Console Task Setup # This is a comment + [Teardown] Log To Console Task Teardown # This is a comment + [Timeout] 60 # This is a comment + [Tags] first second # This is a comment + No Operation + First Keyword + Log To Console Task executed with success + +third task + Given "Mr. Smith" is registered + And "cart" has objects + When "Mr. Smith" clicks in checkout + Then the total is presented and awaits confirmation + But it is shown the unavailable payment method + +*** Keywords *** +First Keyword + [Documentation] This is the documentation + ... A continued line of documentation + [Arguments] ${arg}=None # This is a comment + [Tags] first second # This is a comment + Log To Console This is the first keyword + +${user} is registered + No Operation + +${cart} has objects + No Operation + +${user} clicks in checkout + No Operation + +the total is presented and awaits confirmation + No Operation + +it is shown the unavailable payment method + No Operation + diff --git a/utest/resources/robotdata/language/en/full_test_en.robot b/utest/resources/robotdata/language/en/full_test_en.robot new file mode 100644 index 000000000..91bcea6ab --- /dev/null +++ b/utest/resources/robotdata/language/en/full_test_en.robot @@ -0,0 +1,81 @@ +# This is the preamble +Language: English + +# A blank line + +*** Settings *** +Library Process # This is a comment +Resource full_en.resource # This is a comment +Variables full_en.yaml # This is a comment +Variables full_en.json # This is a comment +Variables full_en.py # This is a comment + +Documentation This is the documentation +... A continued line of documentation + +Metadata Name Value # This is a comment + +Suite Setup Log To Console Suite Setup +Suite Teardown Log To Console Suite Teardown + +*** Variables *** +${myvar} 123 # This is a comment + +*** Comments *** +This is a comments block +Second line of comments + +*** Test Cases *** +first test + [Documentation] This is the documentation + ... A continued line of documentation + [Setup] Log To Console Test Setup + [Teardown] Log To Console Test Teardown + [Timeout] 60 + [Tags] first second + First Keyword + ${first}= Check Logic 'Yes' + ${second}= Check Logic 'Off' + Log Variables + Log Test executed with success, first=${first}, second=${second} + +second test + [Template] First Keyword # This is a comment + [Setup] Log To Console Test Setup # This is a comment + [Teardown] Log To Console Test Teardown # This is a comment + [Timeout] 60 # This is a comment + [Tags] first second # This is a comment + No Operation + First Keyword + Log To Console Test executed with success + +third test + Given "Mr. Smith" is registered + And "cart" has objects + When "Mr. Smith" clicks in checkout + Then the total is presented and awaits confirmation + But it is shown the unavailable payment method + +*** Keywords *** +First Keyword + [Documentation] This is the documentation + ... A continued line of documentation + [Arguments] ${arg}=None # This is a comment + [Tags] first second # This is a comment + Log To Console This is the first keyword + +${user} is registered + No Operation + +${cart} has objects + No Operation + +${user} clicks in checkout + No Operation + +the total is presented and awaits confirmation + No Operation + +it is shown the unavailable payment method + No Operation + diff --git a/utest/resources/robotdata/language/en/gherkin_en.robot b/utest/resources/robotdata/language/en/gherkin_en.robot new file mode 100644 index 000000000..e794034cf --- /dev/null +++ b/utest/resources/robotdata/language/en/gherkin_en.robot @@ -0,0 +1,33 @@ +# This is the preamble +Language: English + +# A blank line + +*** Settings *** +Documentation This is the documentation for Gherkin test +... A continued line of documentation + +*** Test Cases *** +third test + Given "Mr. Smith" is registered + And "cart" has objects + When "Mr. Smith" clicks in checkout + Then the total is presented and awaits confirmation + But it is shown the unavailable payment method + +*** Keywords *** +${user} is registered + No Operation + +${cart} has objects + No Operation + +${user} clicks in checkout + No Operation + +the total is presented and awaits confirmation + No Operation + +it is shown the unavailable payment method + No Operation + diff --git a/utest/resources/robotdata/language/lang_bg.robot b/utest/resources/robotdata/language/lang_bg.robot new file mode 100644 index 000000000..e97b119c2 --- /dev/null +++ b/utest/resources/robotdata/language/lang_bg.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Bulgarian + +# A blank line + +*** Тестови случаи *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Ключови думи *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_bs.robot b/utest/resources/robotdata/language/lang_bs.robot new file mode 100644 index 000000000..b8e0b0d6a --- /dev/null +++ b/utest/resources/robotdata/language/lang_bs.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Bosnian + +# A blank line + +*** Test Cases *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Keywords *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_cs.robot b/utest/resources/robotdata/language/lang_cs.robot new file mode 100644 index 000000000..a42ee35ff --- /dev/null +++ b/utest/resources/robotdata/language/lang_cs.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Czech + +# A blank line + +*** Testovací případy *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Klíčová slova *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_de.robot b/utest/resources/robotdata/language/lang_de.robot new file mode 100644 index 000000000..d58bb7053 --- /dev/null +++ b/utest/resources/robotdata/language/lang_de.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: German + +# A blank line + +*** Testfälle *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Schlüsselwörter *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_en.robot b/utest/resources/robotdata/language/lang_en.robot new file mode 100644 index 000000000..ce3f9bfff --- /dev/null +++ b/utest/resources/robotdata/language/lang_en.robot @@ -0,0 +1,15 @@ +# This is the preamble +Language: English + +# A blank line + +*** Test Cases *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Keywords *** +First Keyword + [Arguments] ${arg}=None # This is a comment + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_es.robot b/utest/resources/robotdata/language/lang_es.robot new file mode 100644 index 000000000..df72a57dd --- /dev/null +++ b/utest/resources/robotdata/language/lang_es.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Spanish + +# A blank line + +*** Casos de prueba *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Palabras clave *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_fi.robot b/utest/resources/robotdata/language/lang_fi.robot new file mode 100644 index 000000000..7a164ec87 --- /dev/null +++ b/utest/resources/robotdata/language/lang_fi.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Finnish + +# A blank line + +*** Testit *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Avainsanat *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_fr.robot b/utest/resources/robotdata/language/lang_fr.robot new file mode 100644 index 000000000..c176f3920 --- /dev/null +++ b/utest/resources/robotdata/language/lang_fr.robot @@ -0,0 +1,14 @@ +# This is the french example +Language: French + +Vide ligne + +*** Unités de Test *** +Premiére Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Mots-clés *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_hi.robot b/utest/resources/robotdata/language/lang_hi.robot new file mode 100644 index 000000000..1701a516e --- /dev/null +++ b/utest/resources/robotdata/language/lang_hi.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Hindi + +# A blank line + +*** नियत कार्य प्रवेशिका *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** कुंजीशब्द *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_it.robot b/utest/resources/robotdata/language/lang_it.robot new file mode 100644 index 000000000..b8cae37eb --- /dev/null +++ b/utest/resources/robotdata/language/lang_it.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Italian + +# A blank line + +*** Casi Di Test *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Parole Chiave *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_nl.robot b/utest/resources/robotdata/language/lang_nl.robot new file mode 100644 index 000000000..b5155c512 --- /dev/null +++ b/utest/resources/robotdata/language/lang_nl.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Dutch + +# A blank line + +*** Testgevallen *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Sleutelwoorden *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_pl.robot b/utest/resources/robotdata/language/lang_pl.robot new file mode 100644 index 000000000..dc34b7e1e --- /dev/null +++ b/utest/resources/robotdata/language/lang_pl.robot @@ -0,0 +1,26 @@ +# This is the preamble +Language: Polish + +# A blank line + +*** Variables *** +&{error} name=err + +*** Comments *** +This is a comments block +Second line of comments +Maybe this block is still in preamble +One more line + +*** Przypadki Testowe *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Słowa Kluczowe *** +First Keyword + [Argumenty] ${arg}=None # This is a comment + Log To Console This is First Keyword + No Operation + Log To Console One more line diff --git a/utest/resources/robotdata/language/lang_pt.robot b/utest/resources/robotdata/language/lang_pt.robot new file mode 100644 index 000000000..05fa82814 --- /dev/null +++ b/utest/resources/robotdata/language/lang_pt.robot @@ -0,0 +1,21 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is a comments block +Second line of comments a +Maybe this block is still in preamble +One more line + +*** Casos de Teste *** +teste primeiro + No Operation + Primeira palavra-chave + Log To Console Teste executado com sucesso + +*** Palavras-Chave *** +Primeira palavra-chave + [Argumentos] ${arg}=None # This is a comment + Log To Console Esta é a primeira palavra-chave diff --git a/utest/resources/robotdata/language/lang_pt_br.robot b/utest/resources/robotdata/language/lang_pt_br.robot new file mode 100644 index 000000000..735f97ce7 --- /dev/null +++ b/utest/resources/robotdata/language/lang_pt_br.robot @@ -0,0 +1,17 @@ +# Este é o preâmbulo +Language: Brazilian Portuguese + +# Mais uma linha em branco + +*** Configurações *** +Library Process + +*** Casos de Teste *** +teste primeiro + No Operation + Primeira palavra-chave + Log To Console Teste executado com sucesso + +*** Palavras-Chave *** +Primeira palavra-chave + Log To Console Esta é a primeira palavra-chave diff --git a/utest/resources/robotdata/language/lang_ro.robot b/utest/resources/robotdata/language/lang_ro.robot new file mode 100644 index 000000000..016ef46e5 --- /dev/null +++ b/utest/resources/robotdata/language/lang_ro.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Romanian + +# A blank line + +*** Cazuri De Test *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Cuvinte Cheie *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_ru.robot b/utest/resources/robotdata/language/lang_ru.robot new file mode 100644 index 000000000..ee37843a7 --- /dev/null +++ b/utest/resources/robotdata/language/lang_ru.robot @@ -0,0 +1,17 @@ +# This is the preamble +Language: Russian + +# A blank line + +*** Заголовки тестов *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +# comment here + +*** Ключевые слова *** +First Keyword + Log To Console This is First Keyword + Log To Console Another line diff --git a/utest/resources/robotdata/language/lang_sv.robot b/utest/resources/robotdata/language/lang_sv.robot new file mode 100644 index 000000000..9fed771b6 --- /dev/null +++ b/utest/resources/robotdata/language/lang_sv.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Swedish + +# A blank line + +*** Testfall *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Nyckelord *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_th.robot b/utest/resources/robotdata/language/lang_th.robot new file mode 100644 index 000000000..e0b6e7a7e --- /dev/null +++ b/utest/resources/robotdata/language/lang_th.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Thai + +# A blank line + +*** การทดสอบ *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** คำสั่งเพิ่มเติม *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_tr.robot b/utest/resources/robotdata/language/lang_tr.robot new file mode 100644 index 000000000..2da56dc3b --- /dev/null +++ b/utest/resources/robotdata/language/lang_tr.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Turkish + +# A blank line + +*** Test Durumları *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Anahtar Kelimeler *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_uk.robot b/utest/resources/robotdata/language/lang_uk.robot new file mode 100644 index 000000000..23e2a1101 --- /dev/null +++ b/utest/resources/robotdata/language/lang_uk.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Ukrainian + +# A blank line + +*** Тест-кейси *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Ключових слова *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_vi.robot b/utest/resources/robotdata/language/lang_vi.robot new file mode 100644 index 000000000..71954c27f --- /dev/null +++ b/utest/resources/robotdata/language/lang_vi.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Vietnamese + +# A blank line + +*** Các kịch bản kiểm thử *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** Các từ khóa *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_zh_cn.robot b/utest/resources/robotdata/language/lang_zh_cn.robot new file mode 100644 index 000000000..657dec485 --- /dev/null +++ b/utest/resources/robotdata/language/lang_zh_cn.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Chinese Simplified + +# A blank line + +*** 用例 *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** 关键字 *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/lang_zh_tw.robot b/utest/resources/robotdata/language/lang_zh_tw.robot new file mode 100644 index 000000000..0d65cd280 --- /dev/null +++ b/utest/resources/robotdata/language/lang_zh_tw.robot @@ -0,0 +1,14 @@ +# This is the preamble +Language: Chinese Traditional + +# A blank line + +*** 案例 *** +First Test + No Operation + First Keyword + Log To Console Test execution with success + +*** 關鍵字 *** +First Keyword + Log To Console This is First Keyword diff --git a/utest/resources/robotdata/language/preamble_no_lang.robot b/utest/resources/robotdata/language/preamble_no_lang.robot new file mode 100644 index 000000000..791481ea9 --- /dev/null +++ b/utest/resources/robotdata/language/preamble_no_lang.robot @@ -0,0 +1,13 @@ +# This is the preamble +# Non existing language setting + +Whatever stuff we have here is ignored + +Even not comments are valid + +# If we have a line starting with Language: then we consider the language + +*** Tasks *** +My First Task + No Operation + diff --git a/utest/resources/robotdata/language/preamble_unknown_lang.robot b/utest/resources/robotdata/language/preamble_unknown_lang.robot new file mode 100644 index 000000000..2c4b05b89 --- /dev/null +++ b/utest/resources/robotdata/language/preamble_unknown_lang.robot @@ -0,0 +1,8 @@ +# This is the preamble +# Unknown language setting +Language: Pirates + +*** Loots *** +Aaarr Robb'em All + No Operation + Log Aditional content diff --git a/utest/resources/robotdata/language/pt/__init__.robot b/utest/resources/robotdata/language/pt/__init__.robot new file mode 100644 index 000000000..aebed474d --- /dev/null +++ b/utest/resources/robotdata/language/pt/__init__.robot @@ -0,0 +1,39 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is a comments block +Second line of comments +Maybe this block is still in preamble + +*** Definições *** +Biblioteca Collections # Isto é um comentário +Recurso full_pt.resource # Isto é um comentário +Variável full_pt.yaml # Isto é um comentário +Variável full_pt.json # Isto é um comentário +Variável full_pt.py # Isto é um comentário + +Documentação This is the documentation +... A continued line of documentation + +Metadados Nome Valor # Isto é um comentário + +Inicialização de Suíte Palavra-chave de Suite Suite Setup +Finalização de Suíte Log To Console Suite Teardown + +*** Variáveis *** +${myvar} 123 # Isto é um comentário + +*** Comentários *** +This is a comments block +Second line of comments + +*** Palavras-Chave *** +Palavra-chave de Suite + [Documentação] This is the documentation + ... A continued line of documentation + [Argumentos] ${arg} # Isto é um comentário + [Etiquetas] suite # Isto é um comentário + Log To Console Esta é a palavra-chave de Suíte arg=${arg} diff --git a/utest/resources/robotdata/language/pt/full_pt.json b/utest/resources/robotdata/language/pt/full_pt.json new file mode 100644 index 000000000..4d9733e98 --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_pt.json @@ -0,0 +1,4 @@ +{ + "simples":"one", + "secundária":1234 +} diff --git a/utest/resources/robotdata/language/pt/full_pt.py b/utest/resources/robotdata/language/pt/full_pt.py new file mode 100644 index 000000000..0e14c1fa1 --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_pt.py @@ -0,0 +1,3 @@ +# Sample python variables file +primeira= "Primeira" +segunda= 2 diff --git a/utest/resources/robotdata/language/pt/full_pt.resource b/utest/resources/robotdata/language/pt/full_pt.resource new file mode 100644 index 000000000..49bf51b83 --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_pt.resource @@ -0,0 +1,62 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is a comments block +Second line of comments +Maybe this block is still in preamble + +*** Definições *** +Biblioteca String +Variável full_pt.yaml +Variável full_pt.json +Variável full_pt.py + +Documentação This is the documentation +... A continued line of documentation + +*** Variáveis *** +${myvar} 1234 + +*** Comentários *** +This is a comments block +Second line of comments + +*** Palavras-Chave *** +Primeira palavra-chave do Recurso + [Documentação] This is the documentation + ... A continued line of documentation + [Argumentos] ${arg}=None + [Etiquetas] first second + Log To Console Esta é a primeira palavra-chave do Recurso + +Verifica Lógica + [Documentação] Retorna Verdadeiro se arg em ['Verdadeiro', 'Verdade', 'Sim', 'Ligado'] + ... \nou retorna Falso se arg em ['Falso', 'Não', 'Desligado', 'Desativado', 'Nada'] + [Argumentos] ${arg}='Verdadeiro' + [Etiquetas] lógica + IF ${arg} + Log Arg it True: arg=${arg} + RETURN Verdade + ELSE + Log Arg it False: arg=${arg} + RETURN Não + END + +${utilizador} está registado + No Operation + +${carrinho de compras} tem objectos + No Operation + +${utilizador} clica em finalizar compra + No Operation + +é apresentado o total e aguarda confirmação + No Operation + +é apresentado meio de pagamento indisponível + No Operation + diff --git a/utest/resources/robotdata/language/pt/full_pt.yaml b/utest/resources/robotdata/language/pt/full_pt.yaml new file mode 100644 index 000000000..63731b8c0 --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_pt.yaml @@ -0,0 +1,7 @@ +variável_principal: + secundária: + "Teste" + +variável_secundária: + 1234 + diff --git a/utest/resources/robotdata/language/pt/full_task_pt.robot b/utest/resources/robotdata/language/pt/full_task_pt.robot new file mode 100644 index 000000000..ab246698b --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_task_pt.robot @@ -0,0 +1,87 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is a comments block +Second line of comments +Maybe this block is still in preamble + +*** Definições *** +Biblioteca Process +Recurso full_pt.resource +Variável full_pt.yaml +Variável full_pt.json +Variável full_pt.py + +Documentação This is the documentation +... A continued line of documentation + +Metadados Nome Value + +Inicialização de Suíte No Operation +Finalização de Suíte No Operation + +*** Variáveis *** +${myvar} 123 + +*** Comentários *** +This is a comments block +Second line of comments + +*** Tarefas *** +Tarefa primeira + [Documentação] This is the documentation + ... A continued line of documentation + [Inicialização] Log To Console Task Setup + [Finalização] Log To Console Task Teardown + [Tempo Limite] 60 + [Etiquetas] first second + No Operation + Primeira palavra-chave + ${primeira}= Verifica Lógica 'Ligado' + ${segunda}= Verifica Lógica 'Desativado' + Log Variables + Log Tarefa executada com sucesso, primeira=${primeira}, segunda=${segunda} + +Tarefa segunda + [Modelo] Primeira palavra-chave + [Inicialização] Log To Console Task Setup + [Finalização] Log To Console Task Teardown + [Tempo Limite] 60 + [Etiquetas] first second + No Operation + Primeira palavra-chave + Log To Console Tarefa executada com sucesso + +Tarefa terceira + Dado "Sr. José" está registado + E "carrinho" tem objectos + Quando "Sr. José" clica em finalizar compra + Então é apresentado o total e aguarda confirmação + Mas é apresentado meio de pagamento indisponível + +*** Palavras-Chave *** +Primeira palavra-chave + [Documentação] This is the documentation + ... A continued line of documentation + [Argumentos] @{arg} + [Etiquetas] first second + Log To Console Esta é a primeira palavra-chave arg=${arg} + +${utilizador} está registado + No Operation + +${carrinho de compras} tem objectos + No Operation + +${utilizador} clica em finalizar compra + No Operation + +é apresentado o total e aguarda confirmação + No Operation + +é apresentado meio de pagamento indisponível + No Operation + diff --git a/utest/resources/robotdata/language/pt/full_test_pt.robot b/utest/resources/robotdata/language/pt/full_test_pt.robot new file mode 100644 index 000000000..a1f41955a --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_test_pt.robot @@ -0,0 +1,99 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is the preamble comments block +Second line of comments +Maybe this block is still in preamble +# Comment inside preamble comments block + +*** Definições *** +Biblioteca Process +Recurso full_pt.resource +Variável full_pt.yaml +Variável full_pt.json +Variável full_pt.py +# Comment inside settings block, next is empty line followed by documention + +Documentação This is the documentation +... A continued line of documentation + +Metadados Nome Value +# Comment inside settings block after metadata, next is empty line followed by suite setup + +Inicialização de Suíte No Operation +Finalização de Suíte No Operation +# Comment inside settings block after suite teardown, next is empty line followed by variables section + +*** Variáveis *** +${myvar} 123 +# Comment inside variables, next is empty line followed by a comments section + +*** Comentários *** +This is a comments block +Second line of comments +# Comment inside second comments block + +*** Casos de Teste *** +teste primeiro + [Documentação] This is the documentation + ... A continued line of documentation + [Inicialização] Log To Console Test Setup + [Finalização] Log To Console Test Teardown + [Tempo Limite] 60 + [Etiquetas] first second + Primeira palavra-chave + # Comment inside test case, next is empty line followed by steps + + ${primeira}= Verifica Lógica 'Sim' + ${segunda}= Verifica Lógica 'Desligado' + Log Variables + Log Tarefa executada com sucesso, primeira=${primeira}, segunda=${segunda} + # Comment at end of test case, next is empty line followed another test case WITH a comment before name + +# Comment before test case, next is test case name +teste segundo + [Modelo] Primeira palavra-chave + [Inicialização] Log To Console Test Setup + [Finalização] Log To Console Test Teardown + [Tempo Limite] 60 + [Etiquetas] first second + No Operation + Primeira palavra-chave + Log To Console Teste executado com sucesso + +teste terceiro + Dado "Sr. José" está registado + E "carrinho" tem objectos + Quando "Sr. José" clica em finalizar compra + Então é apresentado o total e aguarda confirmação + Mas é apresentado meio de pagamento indisponível + # Comment at end of test case, next is empty line followed by the section keywords + +*** Palavras-Chave *** +Primeira palavra-chave + [Documentação] This is the documentation + ... A continued line of documentation + [Argumentos] @{arg} + [Etiquetas] first second + Log To Console Esta é a primeira palavra-chave arg=${arg} + # Comment at end of keyword, next is empty line followed by another keyword + +${utilizador} está registado + No Operation + +# Comment before keyword, next is keyword name +${carrinho de compras} tem objectos + No Operation + +${utilizador} clica em finalizar compra + No Operation + +é apresentado o total e aguarda confirmação + No Operation + +é apresentado meio de pagamento indisponível + No Operation + diff --git a/utest/resources/robotdata/language/pt/full_test_single_pt.robot b/utest/resources/robotdata/language/pt/full_test_single_pt.robot new file mode 100644 index 000000000..25f2b33b7 --- /dev/null +++ b/utest/resources/robotdata/language/pt/full_test_single_pt.robot @@ -0,0 +1,114 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Comentários *** +This is the preamble and first comments block +Second line of comments # Isto é um comentário +Maybe this block is still in preamble +# Comment inside preamble comments block + +*** Definições *** +Biblioteca Process # Isto é um comentário +Variável full_pt.yaml # Isto é um comentário +Variável full_pt.json # Isto é um comentário +Variável full_pt.py # Isto é um comentário +# Comment inside settings block, next is empty line followed by documentation + +Documentação This is the documentation +... A continued line of documentation # Isto é um comentário + +Metadados Nome Valor # Isto é um comentário +# Comment inside settings block after metadata, next is empty line followed by suite setup + +Inicialização de Suíte No Operation # Isto é um comentário +Finalização de Suíte No Operation # Isto é um comentário +# Comment inside settings block after suite teardown, next is empty line followed by variables section + +*** Comentários *** +This is the second comments block after settings +Second line of comments # Isto é um comentário +# Comment inside second comments block + +*** Variáveis *** +${myvar} 123 # Isto é um comentário +# Comment inside variables, next is empty line followed by a comments section + +*** Comentários *** +This is the third comments block after variables +Second line of comments # Isto é um comentário +# Comment inside second comments block + +*** Casos de Teste *** +teste primeiro + [Documentação] This is the documentation + ... A continued line of documentation # Isto é um comentário + [Inicialização] Log To Console Test Setup # Isto é um comentário + [Finalização] Log To Console Test Teardown # Isto é um comentário + [Tempo Limite] 60 # Isto é um comentário + [Etiquetas] first second # Isto é um comentário + Primeira palavra-chave # Isto é um comentário + # Comment inside test case, next is empty line followed by steps + + Log First step after empty line + # ${primeira}= Verifica Lógica 'Sim' + # ${segunda}= Verifica Lógica 'Desligado' + Log Variables + # Log Tarefa executada com sucesso, primeira=${primeira}, segunda=${segunda} + # Comment at end of test case, next is empty line followed another test case WITH a comment before name + +# Comment before test case, next is test case name +teste segundo + [Modelo] Primeira palavra-chave + [Inicialização] Log To Console Test Setup + [Finalização] Log To Console Test Teardown + [Tempo Limite] 60 + [Etiquetas] first second + No Operation + Primeira palavra-chave + Log To Console Teste executado com sucesso + +teste terceiro + Dado "Sr. José" está registado + E "carrinho" tem objectos + Quando "Sr. José" clica em finalizar compra + Então é apresentado o total e aguarda confirmação + Mas é apresentado meio de pagamento indisponível + # Comment at end of test case, next is empty line followed by the section keywords + +*** Comentários *** +This is the fourth comments block after test cases +Second line of comments # Isto é um comentário +# Comment inside second comments block + +*** Palavras-Chave *** +Primeira palavra-chave + [Documentação] This is the documentation + ... A continued line of documentation + [Argumentos] @{arg} + [Etiquetas] first second + Log To Console Esta é a primeira palavra-chave arg=${arg} + # Comment at end of keyword, next is empty line followed by another keyword + +${utilizador} está registado + No Operation + +# Comment before keyword, next is keyword name +${carrinho de compras} tem objectos # Isto é um comentário + No Operation # Isto é um comentário + +${utilizador} clica em finalizar compra # Isto é um comentário + No Operation # Isto é um comentário + +é apresentado o total e aguarda confirmação + No Operation + +é apresentado meio de pagamento indisponível + No Operation + +*** Comentários *** +This is the fifth comments block after keywords +Second line of comments # Isto é um comentário +# Comment inside last comments block + diff --git a/utest/resources/robotdata/language/pt/gherkin_pt.robot b/utest/resources/robotdata/language/pt/gherkin_pt.robot new file mode 100644 index 000000000..4f7a86382 --- /dev/null +++ b/utest/resources/robotdata/language/pt/gherkin_pt.robot @@ -0,0 +1,35 @@ +# Este é o preâmbulo +Language: Portuguese + +# Mais uma linha em branco + +*** Definições *** +Documentação Esta é a documentação para o teste Gherkin +... Uma linha de continuação da documentação + +*** Casos de Teste *** +teste terceiro + Dado "Sr. José" está registado + E "carrinho" tem objectos + Quando "Sr. José" clica em finalizar compra + Então é apresentado o total e aguarda confirmação + Mas é apresentado meio de pagamento indisponível + # Comment at end of test case, next is empty line followed by the section keywords + +*** Palavras-Chave *** +${utilizador} está registado + No Operation + +# Comment before keyword, next is keyword name + +${carrinho de compras} tem objectos + No Operation + +${utilizador} clica em finalizar compra + No Operation + +é apresentado o total e aguarda confirmação + No Operation + +é apresentado meio de pagamento indisponível + No Operation diff --git a/utest/spec/test_iteminfo.py b/utest/spec/test_iteminfo.py index 5fa49c996..ec632dce3 100644 --- a/utest/spec/test_iteminfo.py +++ b/utest/spec/test_iteminfo.py @@ -61,7 +61,7 @@ def test_libkw_kwonly_arguments_parsing(self): '[ arg1 | *args | namedarg1 | namedarg2=default value | **kwargs ]') def test_uk_arguments_parsing(self): - uk = UserKeyword(_FakeTestCaseFile(), 'My User keyword') + uk = UserKeyword(_FakeTestCaseFile(), 'My User keyword', ['en']) uk.args.value = ['${arg1}', '${arg2}=def', '@{varargs}'] kw_info = TestCaseUserKeywordInfo(uk) exp_source = 'testcase.robot' @@ -69,7 +69,7 @@ def test_uk_arguments_parsing(self): assert_in_details(kw_info, exp_source, exp_args) def test_resource_uk_longname(self): - uk = UserKeyword(KeywordTable(_FakeResourceFile()), 'UK') + uk = UserKeyword(KeywordTable(_FakeResourceFile(), ['en']), 'UK') kw_info = ResourceUserKeywordInfo(uk) self.assertEqual(kw_info.longname, 'resource.UK') diff --git a/utest/ui/test_tagdialogs.py b/utest/ui/test_tagdialogs.py index f792f7d65..67b143eb1 100644 --- a/utest/ui/test_tagdialogs.py +++ b/utest/ui/test_tagdialogs.py @@ -245,7 +245,7 @@ def _create_model(self): for i in range(3)] res = ResourceFile() res.source = 'resource.robot' - res.keyword_table.keywords.append(UserKeyword(res, 'Resource Keyword')) + res.keyword_table.keywords.append(UserKeyword(res, 'Resource Keyword', ['en'])) library_manager = LibraryManager(':memory:') library_manager.create_database() model = Project( @@ -281,7 +281,7 @@ def _create_suite(self, suite_class, source, is_dir=False): if is_dir: suite.directory = source suite.keyword_table.keywords = [ - UserKeyword(suite.keyword_table, '%s Fake UK %d' % (suite.name, i)) + UserKeyword(suite.keyword_table, '%s Fake UK %d' % (suite.name, i), ['en']) for i in range(5)] return suite diff --git a/utest/ui/test_tree.py b/utest/ui/test_tree.py index e31949357..b22eb123f 100644 --- a/utest/ui/test_tree.py +++ b/utest/ui/test_tree.py @@ -102,7 +102,7 @@ def _create_model(self): for i in range(3)] res = ResourceFile() res.source = 'resource.robot' - res.keyword_table.keywords.append(UserKeyword(res, 'Resource Keyword')) + res.keyword_table.keywords.append(UserKeyword(res, 'Resource Keyword', ['en'])) library_manager = LibraryManager(':memory:') library_manager.create_database() model = Project( @@ -128,7 +128,7 @@ def _create_suite(self, suite_class, source, is_dir=False): if is_dir: suite.directory = source suite.keyword_table.keywords = [ - UserKeyword(suite.keyword_table, '%s Fake UK %d' % (suite.name, i)) + UserKeyword(suite.keyword_table, '%s Fake UK %d' % (suite.name, i), ['en']) for i in range(5)] return suite diff --git a/utest/widgets/test_popupmenu.py b/utest/widgets/test_popupmenu.py index a78689f6b..cba8e9ad5 100644 --- a/utest/widgets/test_popupmenu.py +++ b/utest/widgets/test_popupmenu.py @@ -65,7 +65,7 @@ def test_creation_with_name_and_parent(self): def test_creation_with_name_and_callable(self): def _test(): pass - item = PopupMenuItem('Do Something', _test) + item = PopupMenuItem('Do Something', ccallable=_test) assert item.callable == _test def test_creation_with_name_shortcut_in_name(self):