1515# limitations under the License.
1616from collections import namedtuple
1717from datetime import timedelta
18+ import importlib
1819from inspect import getdoc , isclass
19- from typing import Optional , List
20+ from pathlib import Path
21+ from typing import Optional , List , Union
2022
2123from robot .api import logger
2224from robot .errors import DataError
@@ -544,6 +546,46 @@ class SeleniumLibrary(DynamicCore):
544546 documentation for further details.
545547
546548 Plugin API is new SeleniumLibrary 4.0
549+
550+ = Language =
551+
552+ SeleniumLibrary offers possibility to translte keyword names and documentation to new language. If language
553+ is defined, SeleniumLibrary will search from
554+ [https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#module-search-path | module search path]
555+ Python packages starting with `robotframework_seleniumlibrary_translation` by using
556+ [https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/ | Python pluging API]. Library
557+ is using naming convention to find Python plugins.
558+
559+ The package must implement single API call, ``get_language`` without any arguments. Method must return a
560+ dictionary containing two keys: ``language`` and ``path``. The language key value defines which language
561+ the package contains. Also value should match (case insentive) the library ``language`` import parameter.
562+ The path parameter value should be full path to the translation file.
563+
564+ == Translation file ==
565+
566+ The file name or extension is not important, but data must be in [https://www.json.org/json-en.html | json]
567+ format. The keys of json are the methods names, not the keyword names, which implements keywords. Value of
568+ key is json object which contains two keys: ``name`` and ``doc``. The ``name`` key contains the keyword
569+ translated name and `doc` contains translated documentation. Providing doc and name are optional, example
570+ translation json file can only provide translations to keyword names or only to documentatin. But it is
571+ always recomended to provide translation to both name and doc. Special key ``__intro__`` is for class level
572+ documentation and ``__init__`` is for init level documentation. These special values ``name`` can not be
573+ translated, instead ``name`` should be kept the same.
574+
575+ == Generating template translation file ==
576+
577+ Template translation file, with English language can be created by running:
578+ `rfselib translation /path/to/translation.json` command. Command does not provide translations to other
579+ languages, it only provides easy way to create full list keywords and their documentation in correct
580+ format. It is also possible to add keywords from library plugins by providing `--plugings` arguments
581+ to command. Example: `rfselib translation --plugings myplugin.SomePlugin /path/to/translation.json` The
582+ genered json file contains `sha256` key, which constains the sha256 sum of the library documentation,
583+ the sha256 sum is used by `rfselib translation --compare /path/to/translation.json` command, which compares
584+ transation to to library and prints outs a table which tell if there are changes needed for translation file.
585+
586+ Example project for translation can be found from
587+ [https://github.com/MarketSquare/robotframework-seleniumlibrary-translation-fi | robotframework-seleniumlibrary-translation-fi]
588+ repository.
547589 """
548590
549591 ROBOT_LIBRARY_SCOPE = "GLOBAL"
@@ -559,6 +601,7 @@ def __init__(
559601 event_firing_webdriver : Optional [str ] = None ,
560602 page_load_timeout = timedelta (minutes = 5 ),
561603 action_chain_delay = timedelta (seconds = 0.25 ),
604+ language : Optional [str ] = None ,
562605 ):
563606 """SeleniumLibrary can be imported with several optional arguments.
564607
@@ -581,6 +624,8 @@ def __init__(
581624 Default value to wait for page load to complete until a timeout exception is raised.
582625 - ``action_chain_delay``:
583626 Default value for `ActionChains` delay to wait in between actions.
627+ - ``language``:
628+ Defines language which is used to translate keyword names and documentation.
584629 """
585630 self .timeout = _convert_timeout (timeout )
586631 self .implicit_wait = _convert_timeout (implicit_wait )
@@ -622,7 +667,8 @@ def __init__(
622667 self ._plugins = plugin_libs
623668 libraries = libraries + plugin_libs
624669 self ._drivers = WebDriverCache ()
625- DynamicCore .__init__ (self , libraries )
670+ translation_file = self ._get_translation (language )
671+ DynamicCore .__init__ (self , libraries , translation_file )
626672
627673 def run_keyword (self , name : str , args : tuple , kwargs : dict ):
628674 try :
@@ -798,3 +844,23 @@ def _resolve_screenshot_root_directory(self):
798844 if is_string (screenshot_root_directory ):
799845 if screenshot_root_directory .upper () == EMBED :
800846 self .screenshot_root_directory = EMBED
847+
848+ @staticmethod
849+ def _get_translation (language : Union [str , None ]) -> Union [Path , None ]:
850+ if not language :
851+ return None
852+ discovered_plugins = {
853+ name : importlib .import_module (name )
854+ for _ , name , _ in pkgutil .iter_modules ()
855+ if name .startswith ("robotframework_seleniumlibrary_translation" )
856+ }
857+ for plugin in discovered_plugins .values ():
858+ try :
859+ data = plugin .get_language ()
860+ except AttributeError :
861+ continue
862+ if data .get ("language" , "" ).lower () == language .lower () and data .get (
863+ "path"
864+ ):
865+ return Path (data .get ("path" )).absolute ()
866+ return None
0 commit comments