diff --git a/src/mobile_strings_converter/__init__.py b/src/mobile_strings_converter/__init__.py index 1162289..1a64406 100644 --- a/src/mobile_strings_converter/__init__.py +++ b/src/mobile_strings_converter/__init__.py @@ -4,19 +4,7 @@ # @author: José Carlos López Henestrosa """Imports for the mobile-strings-converter package.""" -from .converter import ( - to_android, - to_csv, - to_google_sheets, - to_html, - to_ios, - to_json, - to_md, - to_ods, - to_pdf, - to_xlsx, - to_yaml, -) +from .converter import convert_strings, to_google_sheets # Constants __version__ = "0.1.0" diff --git a/src/mobile_strings_converter/__main__.py b/src/mobile_strings_converter/__main__.py index 7de3b65..17e2ded 100644 --- a/src/mobile_strings_converter/__main__.py +++ b/src/mobile_strings_converter/__main__.py @@ -1,8 +1,8 @@ import argparse from pathlib import Path -import converter as conv from console_style import ConsoleStyle +from converter import convert_strings, to_google_sheets def main(): @@ -70,46 +70,14 @@ def main(): ) return elif args.google_sheets and args.credentials: - conv.to_google_sheets( + to_google_sheets( input_filepath, sheet_name=args.google_sheets, credentials_filepath=Path(args.credentials), should_print_comments=args.print_comments, ) - if args.output_filepath: - conversion_functions = { - ".csv": conv.to_csv, - ".xlsx": conv.to_xlsx, - ".ods": conv.to_ods, - ".md": conv.to_md, - ".json": conv.to_json, - ".yaml": conv.to_yaml, - ".html": conv.to_html, - ".strings": conv.to_ios, - ".xml": conv.to_android, - ".pdf": conv.to_pdf, - } - - output_path = Path(args.output_filepath) - - if output_path.suffix in conversion_functions: - conversion_functions[output_path.suffix]( - input_filepath, output_path, args.print_comments - ) - else: - print( - f"{ConsoleStyle.YELLOW}File type not supported. Feel free to create " - f"an issue here (https://github.com/HenestrosaConH/mobile-strings" - f"-converter/issues) if you want the file type to be supported by the " - f"package.{ConsoleStyle.END}" - ) - return - - print( - f"{ConsoleStyle.GREEN}Data successfully written to {output_path}" - f"{ConsoleStyle.END}" - ) + convert_strings(input_filepath, Path(args.output_path), args.print_comments) if __name__ == "__main__": diff --git a/src/mobile_strings_converter/converter.py b/src/mobile_strings_converter/converter.py index 9bb255e..3aa3049 100644 --- a/src/mobile_strings_converter/converter.py +++ b/src/mobile_strings_converter/converter.py @@ -5,6 +5,7 @@ import warnings from contextlib import redirect_stderr, redirect_stdout from pathlib import Path +from typing import List import gspread import openpyxl @@ -15,8 +16,80 @@ from google.oauth2.credentials import Credentials from lingua import LanguageDetectorBuilder +from .console_style import ConsoleStyle + + +def convert_strings( + input_filepath: Path, output_filepath: Path, should_print_comments: bool +): + """ + Extracts strings from the input file in either .xml or .strings format and converts + them to the desired output file format. The output file format can be any of the + following: + + - Android strings format (*.xml) + - CSV + - HTML + - iOS strings format (*.strings) + - JSON + - MD + - ODS + - PDF + - XLSX + - YAML + + :param input_filepath: .strings or .xml file to extract the strings + :type input_filepath: Path + :param output_filepath: Name of the sheet to be generated + :type output_filepath: Path + :param should_print_comments: True if the user wants to print comments from + .strings/.xml to the output file + :type should_print_comments: bool + """ + + strings = get_strings(input_filepath, should_print_comments) + + if output_filepath: + conversion_functions = { + ".csv": to_csv, + ".xlsx": to_sheet, + ".ods": to_sheet, + ".md": to_md, + ".json": to_json, + ".yaml": to_yaml, + ".html": to_html, + ".strings": to_ios, + ".xml": to_android, + ".pdf": to_pdf, + } + + if output_filepath.suffix in conversion_functions: + conversion_functions[output_filepath.suffix](strings, output_filepath) + + print( + f"{ConsoleStyle.GREEN}Data successfully written to {output_filepath}" + f"{ConsoleStyle.END}" + ) + else: + raise ValueError( + f"{ConsoleStyle.YELLOW}File type not supported. Feel free to create " + f"an issue here (https://github.com/HenestrosaConH/mobile-strings" + f"-converter/issues) if you want the file type to be supported by the " + f"package.{ConsoleStyle.END}" + ) + def get_strings(input_filepath: Path, should_print_comments: bool): + """ + Creates a Google spreadsheet with the extracted strings from the input filepath + + :param input_filepath: .strings or .xml file to extract the strings + :type input_filepath: Path + :param should_print_comments: True if the user wants to print comments from + .strings/.xml to the file + :type should_print_comments: bool + """ + if input_filepath.suffix == ".strings": if should_print_comments: pattern = r'"(.*?)"\s*=\s*"((?:[^"\\]|\\.)*)"\s*;' @@ -54,6 +127,21 @@ def to_google_sheets( credentials_filepath: Path, should_print_comments: bool, ): + """ + Creates a Google spreadsheet with the extracted strings from the input filepath + + :param input_filepath: .strings or .xml file to extract the strings + :type input_filepath: Path + :param sheet_name: Name of the sheet to be generated + :type sheet_name: str + :param credentials_filepath: Path to the service_account.json in order to be able + to create the sheet in the user's Google account + :type credentials_filepath: Path + :param should_print_comments: True if the user wants to print comments from + .strings/.xml to the sheet + :type should_print_comments: bool + """ + strings = get_strings(input_filepath, should_print_comments) # Authenticate with Google Sheets API @@ -75,8 +163,15 @@ def to_google_sheets( sheet.append_row(string) -def to_csv(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_csv(strings: List[str], output_filepath: Path): + """ + Formats strings to a .csv file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ with open(output_filepath, "w", encoding="utf-8", newline="") as file: writer = csv.writer(file) @@ -90,10 +185,15 @@ def to_csv(input_filepath: Path, output_filepath: Path, should_print_comments: b writer.writerow([name, value]) -def _write_to_sheet( - input_filepath: Path, output_filepath: Path, should_print_comments: bool -): - strings = get_strings(input_filepath, should_print_comments) +def to_sheet(strings: List[str], output_filepath: Path): + """ + Formats strings to a .xlsx / .ods file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ # Create a new workbook workbook = openpyxl.Workbook() @@ -114,16 +214,15 @@ def _write_to_sheet( workbook.save(output_filepath) -def to_xlsx(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - _write_to_sheet(input_filepath, output_filepath, should_print_comments) +def to_json(strings: List[str], output_filepath: Path): + """ + Formats strings to a .json file - -def to_ods(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - _write_to_sheet(input_filepath, output_filepath, should_print_comments) - - -def to_json(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ # Create a list of dictionaries to store the data data_list = [] @@ -135,8 +234,15 @@ def to_json(input_filepath: Path, output_filepath: Path, should_print_comments: json.dump(data_list, file, ensure_ascii=False, indent=2) -def to_yaml(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_yaml(strings: List[str], output_filepath: Path): + """ + Formats strings to a .yaml file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ # Convert the data to a dictionary strings_dict = {name: value for name, value in strings} @@ -146,8 +252,15 @@ def to_yaml(input_filepath: Path, output_filepath: Path, should_print_comments: yaml.dump(strings_dict, file, default_flow_style=False, allow_unicode=True) -def to_html(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_html(strings: List[str], output_filepath: Path): + """ + Formats strings to a .html file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ # Create an HTML file with open(output_filepath, "w", encoding="utf-8") as file: @@ -174,18 +287,30 @@ def to_html(input_filepath: Path, output_filepath: Path, should_print_comments: file.write("\n") -def to_ios(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_ios(strings: List[str], output_filepath: Path): + """ + Formats strings to a .strings file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ with open(output_filepath, "w", encoding="utf-8") as file: for string in strings: file.write(f'"{string[0]}" = "{string[1]}";\n') -def to_android( - input_filepath: Path, output_filepath: Path, should_print_comments: bool -): - strings = get_strings(input_filepath, should_print_comments) +def to_android(strings: List[str], output_filepath: Path): + """ + Formats strings to a .xml file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ with open(output_filepath, "w", encoding="utf-8") as file: file.write("\n") @@ -195,8 +320,15 @@ def to_android( file.write("") -def to_pdf(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_pdf(strings: List[str], output_filepath: Path): + """ + Formats strings to a .pdf file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ # Ignore the following warning when adding a font already added: # UserWarning: Core font or font already added 'dejavusanscondensed': doing nothing @@ -329,8 +461,15 @@ def add_font(font_name, size=12): pdf.output(str(output_filepath)) -def to_md(input_filepath: Path, output_filepath: Path, should_print_comments: bool): - strings = get_strings(input_filepath, should_print_comments) +def to_md(strings: List[str], output_filepath: Path): + """ + Formats strings to a .md file + + :param strings: Strings extracted from a .strings or .xml file + :type strings: List[str] + :param output_filepath: The path where the generated file will be saved. + :type output_filepath: Path + """ with open(output_filepath, "w", encoding="utf-8") as f: # Write each string to the Markdown file in a table format diff --git a/tests/base_tests.py b/tests/base_tests.py index 9ba7e9d..14194e2 100644 --- a/tests/base_tests.py +++ b/tests/base_tests.py @@ -1,15 +1,15 @@ import unittest from pathlib import Path from tempfile import NamedTemporaryFile -from typing import Callable import pandas as pd -from mobile_strings_converter.converter import get_strings +from mobile_strings_converter.converter import convert_strings, get_strings class BaseTests(object): class BaseConverterTest(unittest.TestCase): # Hook methods + def setUp(self): self.files_path = "files" self._file_name: str | None = None @@ -21,8 +21,6 @@ def setUp(self): Path(__file__).parent / self.files_path / "input/Localizable.strings" ) - self.converter_func: Callable[[Path, Path, bool], None] | None = None - def tearDown(self): if self.output_filepath.exists(): self.output_filepath.unlink() @@ -113,9 +111,7 @@ def _converter_creates_file( input_filepath: Path, should_print_comments: bool, ): - self.converter_func( - input_filepath, self.output_filepath, should_print_comments - ) + convert_strings(input_filepath, self.output_filepath, should_print_comments) self.assertTrue(self.output_filepath.exists()) def _converter_writes_correct_data( @@ -124,9 +120,7 @@ def _converter_writes_correct_data( input_filepath: Path, should_print_comments: bool, ): - self.converter_func( - input_filepath, self.output_filepath, should_print_comments - ) + convert_strings(input_filepath, self.output_filepath, should_print_comments) with open(self.output_filepath, "rb") as test_file, open( template_filepath, "rb" @@ -248,7 +242,7 @@ def test_commented_file(self): # Clean up the temporary file filepath.unlink() - class BaseTestToXlsxOds(BaseConverterTest): + class BaseTestToSheet(BaseConverterTest): # Overrides def _converter_writes_correct_data( self, @@ -256,9 +250,7 @@ def _converter_writes_correct_data( input_filepath: Path, should_print_comments: bool, ): - self.converter_func( - input_filepath, self.output_filepath, should_print_comments - ) + convert_strings(input_filepath, self.output_filepath, should_print_comments) # Load the two Excel files into pandas dataframes df1 = pd.read_excel(template_filepath) diff --git a/tests/test_to_android.py b/tests/test_to_android.py index 2bb8115..dec8c6a 100644 --- a/tests/test_to_android.py +++ b/tests/test_to_android.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_android class TestToAndroid(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.xml" - self.converter_func = to_android if __name__ == "__main__": diff --git a/tests/test_to_csv.py b/tests/test_to_csv.py index bbed063..dcaf1b3 100644 --- a/tests/test_to_csv.py +++ b/tests/test_to_csv.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_csv class TestToCsv(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.csv" - self.converter_func = to_csv if __name__ == "__main__": diff --git a/tests/test_to_html.py b/tests/test_to_html.py index 355a38c..c3dfbaf 100644 --- a/tests/test_to_html.py +++ b/tests/test_to_html.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_html class TestToHtml(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.html" - self.converter_func = to_html if __name__ == "__main__": diff --git a/tests/test_to_ios.py b/tests/test_to_ios.py index eb6eb5f..93d6c86 100644 --- a/tests/test_to_ios.py +++ b/tests/test_to_ios.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_ios class TestToIos(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "Localizable.strings" - self.converter_func = to_ios if __name__ == "__main__": diff --git a/tests/test_to_json.py b/tests/test_to_json.py index 45f0ab3..337366e 100644 --- a/tests/test_to_json.py +++ b/tests/test_to_json.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_json class TestToJson(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.json" - self.converter_func = to_json if __name__ == "__main__": diff --git a/tests/test_to_md.py b/tests/test_to_md.py index 80f98c4..efb0f46 100644 --- a/tests/test_to_md.py +++ b/tests/test_to_md.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_md class TestToMd(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.md" - self.converter_func = to_md if __name__ == "__main__": diff --git a/tests/test_to_ods.py b/tests/test_to_ods.py index 764502a..7b6943b 100644 --- a/tests/test_to_ods.py +++ b/tests/test_to_ods.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_ods -class TestToOds(BaseTests.BaseTestToXlsxOds): +class TestToOds(BaseTests.BaseTestToSheet): def setUp(self): super().setUp() self.file_name = "strings.ods" - self.converter_func = to_ods if __name__ == "__main__": diff --git a/tests/test_to_pdf.py b/tests/test_to_pdf.py index be0dfc7..4405061 100644 --- a/tests/test_to_pdf.py +++ b/tests/test_to_pdf.py @@ -2,14 +2,13 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_pdf +from mobile_strings_converter.converter import convert_strings class TestToPdf(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.pdf" - self.converter_func = to_pdf def tearDown(self): super().tearDown() @@ -24,7 +23,7 @@ def _converter_writes_correct_data( input_filepath: Path, should_print_comments: bool, ): - self.converter_func(input_filepath, self.output_filepath, should_print_comments) + convert_strings(input_filepath, self.output_filepath, should_print_comments) with open(self.output_filepath, "rb") as test_file, open( template_filepath, "rb" diff --git a/tests/test_to_xlsx.py b/tests/test_to_xlsx.py index 01eb238..13a33af 100644 --- a/tests/test_to_xlsx.py +++ b/tests/test_to_xlsx.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_xlsx -class TestToXlsx(BaseTests.BaseTestToXlsxOds): +class TestToXlsx(BaseTests.BaseTestToSheet): def setUp(self): super().setUp() self.file_name = "strings.xlsx" - self.converter_func = to_xlsx if __name__ == "__main__": diff --git a/tests/test_to_yaml.py b/tests/test_to_yaml.py index 24e344b..2f38f9c 100644 --- a/tests/test_to_yaml.py +++ b/tests/test_to_yaml.py @@ -1,14 +1,12 @@ from unittest import main from base_tests import BaseTests -from mobile_strings_converter import to_yaml class TestToYaml(BaseTests.BaseConverterTest): def setUp(self): super().setUp() self.file_name = "strings.yaml" - self.converter_func = to_yaml if __name__ == "__main__":