From 11dd90f2607393094cd48bef7b20e6df37d3f39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Allard?= Date: Thu, 28 Dec 2023 17:08:44 +0100 Subject: [PATCH] feat: Add simple properties file importer (#2050) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See #2042 --------- Co-authored-by: Benoît Allard --- .../dataImport/processors/ProcessorFactory.kt | 1 + .../processors/PropertyFileProcessor.kt | 15 +++++++ .../processors/PropertiesParserTest.kt | 42 +++++++++++++++++++ .../test/resources/import/example.properties | 7 ++++ webapp/src/i18n/cs.json | 2 +- webapp/src/i18n/da.json | 2 +- webapp/src/i18n/de.json | 2 +- webapp/src/i18n/en.json | 2 +- webapp/src/i18n/es.json | 2 +- webapp/src/i18n/fr.json | 2 +- webapp/src/i18n/pt.json | 2 +- webapp/src/i18n/ro.json | 2 +- webapp/src/i18n/zh.json | 2 +- .../import/component/ImportFileInput.tsx | 9 +++- 14 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/PropertyFileProcessor.kt create mode 100644 backend/data/src/test/kotlin/io/tolgee/unit/service/dataImport/processors/processors/PropertiesParserTest.kt create mode 100644 backend/data/src/test/resources/import/example.properties diff --git a/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/ProcessorFactory.kt b/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/ProcessorFactory.kt index b9f0d15580..81dc09e453 100644 --- a/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/ProcessorFactory.kt +++ b/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/ProcessorFactory.kt @@ -21,6 +21,7 @@ class ProcessorFactory { "po" -> PoFileProcessor(context) "xliff" -> XliffFileProcessor(context) "xlf" -> XliffFileProcessor(context) + "properties" -> PropertyFileProcessor(context) else -> throw ImportCannotParseFileException(file.name, "No matching processor") } } diff --git a/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/PropertyFileProcessor.kt b/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/PropertyFileProcessor.kt new file mode 100644 index 0000000000..3d0ec65118 --- /dev/null +++ b/backend/data/src/main/kotlin/io/tolgee/service/dataImport/processors/PropertyFileProcessor.kt @@ -0,0 +1,15 @@ +package io.tolgee.service.dataImport.processors + +import java.util.Properties + +class PropertyFileProcessor( + override val context: FileProcessorContext, +) : ImportFileProcessor() { + override fun process() { + val props = Properties() + props.load(context.file.inputStream) + props.entries.forEachIndexed { idx, it -> + context.addTranslation(it.key.toString(), languageNameGuesses[0], it.value, idx) + } + } +} diff --git a/backend/data/src/test/kotlin/io/tolgee/unit/service/dataImport/processors/processors/PropertiesParserTest.kt b/backend/data/src/test/kotlin/io/tolgee/unit/service/dataImport/processors/processors/PropertiesParserTest.kt new file mode 100644 index 0000000000..e26a9e9eaa --- /dev/null +++ b/backend/data/src/test/kotlin/io/tolgee/unit/service/dataImport/processors/processors/PropertiesParserTest.kt @@ -0,0 +1,42 @@ +package io.tolgee.unit.service.dataImport.processors.processors + +import io.tolgee.dtos.dataImport.ImportFileDto +import io.tolgee.model.dataImport.Import +import io.tolgee.model.dataImport.ImportFile +import io.tolgee.service.dataImport.processors.FileProcessorContext +import io.tolgee.service.dataImport.processors.PropertyFileProcessor +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import java.io.File + +class PropertiesParserTest { + private lateinit var importMock: Import + private lateinit var importFile: ImportFile + private lateinit var importFileDto: ImportFileDto + private lateinit var fileProcessorContext: FileProcessorContext + + @BeforeEach + fun setup() { + importMock = mock() + importFile = ImportFile("messages_en.properties", importMock) + importFileDto = ImportFileDto( + "messages_en.properties", + File("src/test/resources/import/example.properties") + .inputStream() + ) + fileProcessorContext = FileProcessorContext(importFileDto, importFile) + } + + @Test + fun `returns correct parsed result`() { + PropertyFileProcessor(fileProcessorContext).process() + Assertions.assertThat(fileProcessorContext.languages).hasSize(1) + Assertions.assertThat(fileProcessorContext.translations).hasSize(4) + val text = fileProcessorContext.translations["Register"]?.get(0)?.text + Assertions.assertThat(text).isEqualTo("Veuillez vous enregistrer sur la page suivante.") + val multiLineText = fileProcessorContext.translations["Cleanup"]?.get(0)?.text + Assertions.assertThat(multiLineText).hasLineCount(3) + } +} diff --git a/backend/data/src/test/resources/import/example.properties b/backend/data/src/test/resources/import/example.properties new file mode 100644 index 0000000000..a16343b7fe --- /dev/null +++ b/backend/data/src/test/resources/import/example.properties @@ -0,0 +1,7 @@ +Localization_tools=Lokaliza?n nstroje + +Welcome=Herzlich Wilkommen +# This is a comment +Register=Veuillez vous enregistrer \ + sur la page suivante. +Cleanup=Veeg uw\n voeten\n voordat u binnengaat. diff --git a/webapp/src/i18n/cs.json b/webapp/src/i18n/cs.json index 0d8780058c..d070555ffc 100644 --- a/webapp/src/i18n/cs.json +++ b/webapp/src/i18n/cs.json @@ -497,7 +497,7 @@ "import_file_issue_param_type_value" : "hodnota: {value}", "import_file_issues_title" : "Problémy souboru", "import_files_uploaded" : "Soubory byly nahrány", - "import_file_supported_formats" : "podporované formáty jsou .json, .xliff, .po", + "import_file_supported_formats" : "podporované formáty jsou .json, .xliff, .po, .properties", "import_language_select" : "Jazyk", "import_max_file_count_message" : "Příliš mnoho souborů", "import_namespace_name_header" : "Namespace", diff --git a/webapp/src/i18n/da.json b/webapp/src/i18n/da.json index ff1ffbb3f0..f851be2c9f 100644 --- a/webapp/src/i18n/da.json +++ b/webapp/src/i18n/da.json @@ -497,7 +497,7 @@ "import_file_issue_param_type_value" : "værdi: {value}", "import_file_issues_title" : "Problemer med filer", "import_files_uploaded" : "Filer uploaded", - "import_file_supported_formats" : "understøttede formater er .json, .xliff, .po", + "import_file_supported_formats" : "understøttede formater er .json, .xliff, .po, .properties", "import_language_select" : "Sprog", "import_max_file_count_message" : "For mange filer", "import_namespace_name_header" : "Navnerum", diff --git a/webapp/src/i18n/de.json b/webapp/src/i18n/de.json index 17534ad6de..90d888636e 100644 --- a/webapp/src/i18n/de.json +++ b/webapp/src/i18n/de.json @@ -427,7 +427,7 @@ "import_file_issue_param_type_line" : "Zeile: {value}", "import_file_issue_param_type_value" : "Wert: {value}", "import_file_issues_title" : "Datei-Probleme", - "import_file_supported_formats" : "unterstützte Formate sind .json, .xliff, .po", + "import_file_supported_formats" : "unterstützte Formate sind .json, .xliff, .po, .properties", "import_language_select" : "Sprache", "import_max_file_count_message" : "Zu viele Dateien", "import_namespace_name_header" : "Namespace", diff --git a/webapp/src/i18n/en.json b/webapp/src/i18n/en.json index f35f1dafa5..ba54089c7d 100644 --- a/webapp/src/i18n/en.json +++ b/webapp/src/i18n/en.json @@ -497,7 +497,7 @@ "import_file_issue_param_type_value" : "value: {value}", "import_file_issues_title" : "File issues", "import_files_uploaded" : "Files uploaded", - "import_file_supported_formats" : "supported formats are .json, .xliff, .po", + "import_file_supported_formats" : "supported formats are .json, .xliff, .po, .properties", "import_language_select" : "Language", "import_max_file_count_message" : "Too many files", "import_namespace_name_header" : "Namespace", diff --git a/webapp/src/i18n/es.json b/webapp/src/i18n/es.json index 0529fc20d8..55a4db846f 100644 --- a/webapp/src/i18n/es.json +++ b/webapp/src/i18n/es.json @@ -251,7 +251,7 @@ "import_file_issue_param_type_line" : "línea: {value}", "import_file_issue_param_type_value" : "valor: {value}", "import_file_issues_title" : "Problemas con los archivos", - "import_file_supported_formats" : "los formatos compatibles son .json, .xliff, .po", + "import_file_supported_formats" : "los formatos compatibles son .json, .xliff, .po, .properties", "import_language_select" : "Idioma", "import_namespace_name_header" : "Namespace", "import_not_resolved_error_dialog_cancel_button" : "Volver", diff --git a/webapp/src/i18n/fr.json b/webapp/src/i18n/fr.json index a439c692b9..9b06d8995c 100644 --- a/webapp/src/i18n/fr.json +++ b/webapp/src/i18n/fr.json @@ -494,7 +494,7 @@ "import_file_issue_param_type_value" : "valeur : {value}", "import_file_issues_title" : "Problèmes de fichier", "import_files_uploaded" : "Fichiers téléchargés", - "import_file_supported_formats" : "les formats supportés sont .json, .xliff, .po", + "import_file_supported_formats" : "les formats supportés sont .json, .xliff, .po, .properties", "import_language_select" : "Langue", "import_max_file_count_message" : "Trop de fichiers.", "import_namespace_name_header" : "Namespace", diff --git a/webapp/src/i18n/pt.json b/webapp/src/i18n/pt.json index 25df02ea05..4472065fed 100644 --- a/webapp/src/i18n/pt.json +++ b/webapp/src/i18n/pt.json @@ -251,7 +251,7 @@ "import_file_issue_param_type_line" : "linha: {value}", "import_file_issue_param_type_value" : "valor: {value}", "import_file_issues_title" : "Problemas com o arquivo", - "import_file_supported_formats" : "os formatos suportados são .json, .xliff, .po", + "import_file_supported_formats" : "os formatos suportados são .json, .xliff, .po, .properties", "import_language_select" : "Idioma", "import_namespace_name_header" : "Namespace", "import_not_resolved_error_dialog_cancel_button" : "Voltar", diff --git a/webapp/src/i18n/ro.json b/webapp/src/i18n/ro.json index a31ac13915..424d6ab854 100644 --- a/webapp/src/i18n/ro.json +++ b/webapp/src/i18n/ro.json @@ -497,7 +497,7 @@ "import_file_issue_param_type_value" : "valoare: {value}", "import_file_issues_title" : "Probleme fișier", "import_files_uploaded" : "Fișierele au fost încărcate", - "import_file_supported_formats" : "Formatele suportate sunt .json, .xliff, .po", + "import_file_supported_formats" : "Formatele suportate sunt .json, .xliff, .po, .properties", "import_language_select" : "Limba", "import_max_file_count_message" : "Prea multe fișiere", "import_namespace_name_header" : "Spațiu de nume", diff --git a/webapp/src/i18n/zh.json b/webapp/src/i18n/zh.json index 684a8124c7..35e2fcf2a1 100644 --- a/webapp/src/i18n/zh.json +++ b/webapp/src/i18n/zh.json @@ -305,7 +305,7 @@ "import_file_issue_param_type_line" : "行:{value}", "import_file_issue_param_type_value" : "值:{value}", "import_file_issues_title" : "文件问题", - "import_file_supported_formats" : "支持的格式为 .json、.xliff、.po", + "import_file_supported_formats" : "支持的格式为 .json、.xliff、.po、.properties", "import_language_select" : "语言", "import_max_file_count_message" : "文件太多", "import_namespace_name_header" : "命名空间", diff --git a/webapp/src/views/projects/import/component/ImportFileInput.tsx b/webapp/src/views/projects/import/component/ImportFileInput.tsx index a369a9d556..2f9a9eb174 100644 --- a/webapp/src/views/projects/import/component/ImportFileInput.tsx +++ b/webapp/src/views/projects/import/component/ImportFileInput.tsx @@ -36,7 +36,14 @@ const ImportFileInput: FunctionComponent = (props) => { const { t } = useTranslate(); const fileRef = React.createRef(); const config = useConfig(); - const ALLOWED_EXTENSIONS = ['json', 'zip', 'po', 'xliff', 'xlf']; + const ALLOWED_EXTENSIONS = [ + 'json', + 'zip', + 'po', + 'xliff', + 'xlf', + 'properties', + ]; const [resetKey, setResetKey] = useState(0); function resetInput() { setResetKey((key) => key + 1);