| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- #!/usr/bin/env python3
- import pathlib
- import typing
- class file:
- """ This class parse single file, and returns all phrases to translate.
- This open file, and trying to find all phrases to translate in the file.
- Process is separated to few steps, first run filter() to get all lines
- when translate functions exists. Then run parse(), which would get
- phrases and append it to list. On the end, could get results as list by
- result() function, which return list with all phrases, or phrases()
- generator, which iterate on all of them.
- """
- def __init__(self, target: pathlib.Path) -> None:
- """ It creates new file file parser.
- Parameters
- ----------
- target : pathlib.Path
- It is path to the file in the filesystem to parse.
- Raises
- ------
- TypeError
- When target is not afile or not exists.
- """
- if not target.is_file():
- raise TypeError("Target \"" + str(target) + "\" not exists.")
-
- self.__to_parse = None
- self.__phrases = None
- with target.open() as handle:
- self.__content = handle.read().split("\n")
- def __targets(self) -> list:
- """ This return targets function which libtranslate use to translates.
- Returns
- -------
- list
- List with targets to search for in the code.
- """
- return [ "_(", ".translate(", ".tr(", ".phrase ", ".phrase=" ]
- def __string(self) -> list:
- """ This returns javascript string openers and closers.
- Returns
- -------
- list
- List chars with open and close strings in js.
- """
- return [ '"', "'", "`" ]
- def __has_phrase(self, line: str) -> bool:
- """ This check that given line of code has translation instructions.
- Parameters
- ----------
- line : str
- Line of code to search for instructions in.
- Returns
- -------
- bool
- True when line has translations or False when not.
- """
-
- for target in self.__targets():
- if line.find(target) != -1:
- return True
- return False
- def filter(self) -> object:
- """ This filter file contents for lines with translation content.
- This search for translate instructions in all file content. When
- line contain translation instructions, then it append it to new
- list, which would be processed in the next step.
- Returns
- -------
- file
- Own instanct for chain loading.
- """
- self.__to_parse = list()
- for line in self.__content:
- if self.__has_phrase(line):
- self.__to_parse.append(line)
- return self
- def __get_next(self, line: str, start: int) -> int:
- """ This return next speech of the translation instruction.
- This function trying to find next speech of any from translation
- instructions. When it found, then return position of the end of
- that function, but when could not found anything, then return -1.
- Parameters
- ----------
- line : str
- Line to found instruction in.
-
- start : int
- This is position when start to searching for.
-
- Returns
- -------
- int
- Position of the end of found instruction or -1 when not found.
- """
- result = -1
- for target in self.__targets():
- position = line.find(target, start)
-
- if position == -1:
- continue
- position = position + len(target)
- if result == -1:
- result = position
- continue
- if result > position:
- result = position
- return result
- def __get_string_pos(self, line: str, start: int) -> int:
- """ This return next position of the string in the line.
- This trying to search string in the line, when found it, then return
- it position, or return -1 when not found anything. It return position
- of the start string char, in opposition to __get_next.
- Parameters
- ----------
- line : str
- Line to search for string in.
- start : int
- Position to start searching in.
- Returns
- -------
- int
- Position of the string, or -1.
- """
- for target in self.__string():
- position = line.find(target, start)
- if position == -1:
- continue
- return position
-
- return -1
- def __cut_text(self, line: str, position: int) -> str|None:
- """ This get string from given line next to given position.
- This trying to find string next to given position. When found it,
- then get it, and return its content. When not found anything, then
- return None.
- Parameters
- ----------
- line : str
- Line to search for string in.
- position : int
- Position to search for string on.
- Returns
- -------
- str
- Content of the found string.
- None
- When not found any string.
- """
- start = self.__get_string_pos(line, position)
- if start == -1:
- return None
- char = line[start]
- start = start + 1
- end = line.find(char, start)
- if end == -1:
- return None
- return line[start:end]
- def __parse_line(self, line: str) -> typing.Iterator[str]:
- """ This parse single line, and return generator with found phrases.
- Parameters
- ----------
- line : str
- Line to parse.
- Returns
- -------
- Iterator[str]
- All found phrases in the line.
- """
- current = 0
- while True:
- current = self.__get_next(line, current)
- if current == -1:
- break
- text = self.__cut_text(line, current)
- if text is None:
- continue
- current = current + len(text)
- yield text
- def parse(self) -> object:
- """ This parse all lines in the file.
- Returns
- -------
- file
- Own instance to chain loading.
- """
- if self.__to_parse is None:
- raise RuntimeError("Run filter() first.")
- self.__phrases = list()
- for line in self.__to_parse:
- for count in self.__parse_line(line):
- if not count in self.__phrases:
- self.__phrases.append(count)
- return self
- def result(self) -> list:
- """ This return all founded phrases as list.
- Raises
- ------
- RuntimeError
- When not run parse() before.
- Returns
- -------
- list
- List with all found phrases.
- """
- if self.__phrases is None:
- raise RuntimeError("Run parse() first.")
- return self.__phrases
- def phrases(self) ->typing.Iterator[str]:
- """ This returns generator with all phrases.
- Raises
- ------
- RuntimeError
- When not run parse() before.
- Returns
- -------
- Iterator[str]
- Generator with all phrases.
- """
- for phrase in self.result():
- yield phrase
- class directory:
- """ This open all Javascript files in the directory and search phrases.
- This trying to open all Javascript files and all Javascript in the
- subdirectories. Then parsing all that files, and adding phrases from
- its to the list. On the end that phrases could be returned as list, or
- loaded from Iterator.
- """
- def __init__(self, target: pathlib.Path) -> None:
- """ This create new directory instance.
- Raises
- ------
- TypeError
- When target path is not directory.
- Parameters
- ----------
- target : pathlib.Path
- Directory to work in.
- """
- if not target.is_dir():
- raise TypeError("Target \"" + str(target) + "\" is not dir.")
- self.__target = target
- self.__phrases = list()
- def __append(self, phrases: list) -> None:
- """ This append new phrase to phrases list.
- Parameters
- ----------
- phrases : list
- List of phrases to add.
- """
- for phrase in phrases:
- if phrase in self.__phrases:
- continue
- self.__phrases.append(phrase)
- def process(self) -> object:
- """ This process given directory.
- Returns
- -------
- directory
- Own instance to chain loading.
- """
- self.__process_directory(self.__target)
- return self
- def result(self) -> list:
- """ This return list with result.
- Returns
- -------
- list
- List with phrases from the files.
- """
- return self.__phrases
- def phrases(self) -> typing.Iterator[str]:
- """ This return all phrases as iterator.
- Returns
- -------
- Iterator[str]
- All phrases from files.
- """
- for phrase in self.__phrases:
- yield phrase
- def __js_extensions(self) -> list:
- """ This return all extensions for js files.
- Returns
- -------
- list
- All js files extensions.
- """
- return [ "js", "mjs", "ts" ]
-
- def __process_directory(self, directory: pathlib.Path) -> None:
- """ This process given directory.
- This process given directory, when in directory exists any diretory,
- then it would be processed by that function recursive. When found
- file, then parse it, and add phrases from it to the list.
- Parameters
- ----------
- directory : pathlib.Path
- Directory to work on
- """
- for count in directory.iterdir():
- if count.is_dir():
- self.__process_directory(count)
- continue
- if count.is_file():
- self.__process_file(count)
- continue
- def __process_file(self, target: pathlib.Path) -> None:
- """ This process single file.
-
- This process single file. When file is not Javasocript file, then ti
- skip it, but when file is Javascript source code, then it trying to
- extract all phrases from it and adds it to phrases list.
- Parameters
- ----------
- target : pathlib.Path
- Target file to process.
- """
- suffix = target.suffix[1:]
- if not suffix in self.__js_extensions():
- return
-
- self.__append(file(target).filter().parse().result())
- class dictionary:
- """ This create new sample dictionary with phrases.
- This class create sample dictionary file from phrases list.
- It could return result as string, or save it to file.
- """
- def __init__(self, phrases: list) -> None:
- """ This create new dictionary instance.
- Parameters
- ----------
- phrases : list
- List of phrases to prepare dictionary from.
- """
- self.__phrases = phrases
- def __process_single_phrase(self, phrase: str) -> str:
- """ This process single phrase to line in the dictionary.
- Parameters
- ----------
- phrase : str
- Phrase to make line from.
- Returns
- -------
- str
- Phrase as line in dictionary.
- """
- return (" " * 4) + "\"" + phrase + "\": \"\""
- @property
- def result(self) -> str:
- """ It process all phrases to the dictionary file.
- Returns
- -------
- str
- Parsed dictionary.
- """
- lines = []
- start = "{\n"
- stop = "\n}\n"
- for phrase in self.__phrases:
- lines.append(self.__process_single_phrase(phrase))
- return start + str(",\n").join(lines) + stop
- def write(self, target: pathlib.Path) -> None:
- """ It write dictionary to the file.
- Parameters
- ----------
- target : pathlib.Path
- Target file to write dictionary into.
- """
- with target.open("w+") as handle:
- handle.write(self.result)
-
- if __name__ == "__main__":
- import argparse
- parser = argparse.ArgumentParser(
- description = "This script helps to create phrasebook from scripts."
- )
-
- parser.add_argument(
- "source",
- type = pathlib.Path,
- help = "This is source file or directory to parse from."
- )
- parser.add_argument(
- "--output",
- type = pathlib.Path,
- default = "output.json",
- help = "This is output phrasebook file, to save into."
- )
- arguments = parser.parse_args()
- if not arguments.source.exists():
- print("Source \"" + str(arguments.source) + "\" not exists.")
- exit(127)
-
- if arguments.source.is_file():
- target = file(arguments.source).filter().parse().result()
- elif arguments.source.is_dir():
- target = directory(arguments.source).process().result()
- else:
- print("Source is not file or directory.")
- exit(126)
- try:
- dictionary(target).write(arguments.output)
- print("Processed successfull.")
- print("Processed: " + str(len(target)) + " phrases.")
- except Exception as error:
- print(str(error))
- print("Can not save \"" + str(arguments.output) + "\" output file.")
- exit(125)
|