From 64919ddc7019f8e3f3c77fcb5ef8b6efe3a19b6e Mon Sep 17 00:00:00 2001 From: Andrejs Labunskis Date: Thu, 16 Sep 2021 17:40:28 +0300 Subject: [PATCH 1/4] SGO-10 Added possibility to use map as plural key tranlation --- svc_key_test.go | 112 +++++++++++++++++++++++++++++++++++++++++++++ svc_translation.go | 20 ++++---- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/svc_key_test.go b/svc_key_test.go index 4d0bc15..9188923 100644 --- a/svc_key_test.go +++ b/svc_key_test.go @@ -530,6 +530,118 @@ func TestKeyService_Create_AutomationsDisabled(t *testing.T) { } } +func TestKeyService_Create_PluralTranslationEncoded(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc( + fmt.Sprintf("/projects/%s/keys", testProjectID), + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + testMethod(t, r, "POST") + testHeader(t, r, apiTokenHeader, testApiToken) + data := `{ + "keys": [ + { + "key_name": "index.welcome", + "description": "Index app welcome", + "platforms": [ + "web" + ], + "translations": [ + { + "language_iso": "en", + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ], + "is_plural": true + } + ] + }` + + req := new(bytes.Buffer) + _ = json.Compact(req, []byte(data)) + + testBody(t, r, req.String()) + + _, _ = fmt.Fprint(w, `{ + "project_id": "3002780358964f9bab5a92.87762498", + "keys": [ + { + "key_id": 331223, + "created_at": "2018-12-31 12:00:00 (Etc/UTC)", + "created_at_timestamp": 1546257600, + "key_name": { + "ios": "index.welcome", + "android": "index.welcome", + "web": "index.welcome", + "other": "index.welcome" + }, + "filenames": { + "ios": "", + "android": "", + "web": "", + "other": "" + }, + "description": "Index app welcome", + "platforms": [ + "web" + ], + "tags": [], + "comments": [], + "screenshots": [], + "translations": [ + { + "translation_id": 444921, + "key_id": 331223, + "language_iso": "en", + "translation": "Welcome", + "modified_by": 420, + "modified_by_email": "user@mycompany.com", + "modified_at": "2018-12-31 12:00:00 (Etc/UTC)", + "modified_at_timestamp": 1546257600, + "is_reviewed": false, + "reviewed_by": 0, + "words": 0 + } + ] + } + ], + "errors": [ + { + "message": "This key name is already taken", + "code": 400, + "key": { + "key_name": "index.hello" + } + } + ] + }`) + }) + + _, _ = client.Keys().Create(testProjectID, []NewKey{ + { + KeyName: "index.welcome", + Description: "Index app welcome", + Platforms: []string{"web"}, + IsPlural: true, + Translations: []NewTranslation{ + { + LanguageISO: "en", + Translation: map[string]string{ + "one": "oneText", + "other": "otherText", + }, + }, + }, + }, + }) + +} + func TestKeyService_Delete(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/svc_translation.go b/svc_translation.go index a987255..0c4409e 100644 --- a/svc_translation.go +++ b/svc_translation.go @@ -45,19 +45,19 @@ type Translation struct { // Used for NewKey type NewTranslation struct { - LanguageISO string `json:"language_iso"` - Translation string `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` - MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` + LanguageISO string `json:"language_iso"` + Translation interface{} `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` + MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` } type UpdateTranslation struct { - Translation string `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` + Translation interface{} `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` } type TranslationsResponse struct { From 693e2473d0d4205046b26ea5f8c34361bb7d4887 Mon Sep 17 00:00:00 2001 From: Andrejs Labunskis Date: Thu, 16 Sep 2021 17:40:28 +0300 Subject: [PATCH 2/4] SGO-10 Added possibility to use map as plural key translation --- svc_key_test.go | 112 +++++++++++++++++++++++++++++++++++++++++++++ svc_translation.go | 20 ++++---- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/svc_key_test.go b/svc_key_test.go index 4d0bc15..9188923 100644 --- a/svc_key_test.go +++ b/svc_key_test.go @@ -530,6 +530,118 @@ func TestKeyService_Create_AutomationsDisabled(t *testing.T) { } } +func TestKeyService_Create_PluralTranslationEncoded(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc( + fmt.Sprintf("/projects/%s/keys", testProjectID), + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + testMethod(t, r, "POST") + testHeader(t, r, apiTokenHeader, testApiToken) + data := `{ + "keys": [ + { + "key_name": "index.welcome", + "description": "Index app welcome", + "platforms": [ + "web" + ], + "translations": [ + { + "language_iso": "en", + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ], + "is_plural": true + } + ] + }` + + req := new(bytes.Buffer) + _ = json.Compact(req, []byte(data)) + + testBody(t, r, req.String()) + + _, _ = fmt.Fprint(w, `{ + "project_id": "3002780358964f9bab5a92.87762498", + "keys": [ + { + "key_id": 331223, + "created_at": "2018-12-31 12:00:00 (Etc/UTC)", + "created_at_timestamp": 1546257600, + "key_name": { + "ios": "index.welcome", + "android": "index.welcome", + "web": "index.welcome", + "other": "index.welcome" + }, + "filenames": { + "ios": "", + "android": "", + "web": "", + "other": "" + }, + "description": "Index app welcome", + "platforms": [ + "web" + ], + "tags": [], + "comments": [], + "screenshots": [], + "translations": [ + { + "translation_id": 444921, + "key_id": 331223, + "language_iso": "en", + "translation": "Welcome", + "modified_by": 420, + "modified_by_email": "user@mycompany.com", + "modified_at": "2018-12-31 12:00:00 (Etc/UTC)", + "modified_at_timestamp": 1546257600, + "is_reviewed": false, + "reviewed_by": 0, + "words": 0 + } + ] + } + ], + "errors": [ + { + "message": "This key name is already taken", + "code": 400, + "key": { + "key_name": "index.hello" + } + } + ] + }`) + }) + + _, _ = client.Keys().Create(testProjectID, []NewKey{ + { + KeyName: "index.welcome", + Description: "Index app welcome", + Platforms: []string{"web"}, + IsPlural: true, + Translations: []NewTranslation{ + { + LanguageISO: "en", + Translation: map[string]string{ + "one": "oneText", + "other": "otherText", + }, + }, + }, + }, + }) + +} + func TestKeyService_Delete(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/svc_translation.go b/svc_translation.go index a987255..0c4409e 100644 --- a/svc_translation.go +++ b/svc_translation.go @@ -45,19 +45,19 @@ type Translation struct { // Used for NewKey type NewTranslation struct { - LanguageISO string `json:"language_iso"` - Translation string `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` - MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` + LanguageISO string `json:"language_iso"` + Translation interface{} `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` + MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` } type UpdateTranslation struct { - Translation string `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` + Translation interface{} `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` } type TranslationsResponse struct { From 363d247f9e960ee88c08c2511a42e423dea14d8e Mon Sep 17 00:00:00 2001 From: Andrejs Labunskis Date: Thu, 30 Sep 2021 15:35:36 +0300 Subject: [PATCH 3/4] SGO-10 Plural key translation: added custom json marshalling to support objects --- svc_key_test.go | 5 +- svc_translation.go | 132 +++++++++++++++++++++++++++++++--- svc_translation_test.go | 153 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+), 15 deletions(-) diff --git a/svc_key_test.go b/svc_key_test.go index 9188923..05ff35a 100644 --- a/svc_key_test.go +++ b/svc_key_test.go @@ -631,10 +631,7 @@ func TestKeyService_Create_PluralTranslationEncoded(t *testing.T) { Translations: []NewTranslation{ { LanguageISO: "en", - Translation: map[string]string{ - "one": "oneText", - "other": "otherText", - }, + Translation: "{\"one\":\"oneText\",\"other\":\"otherText\"}", }, }, }, diff --git a/svc_translation.go b/svc_translation.go index 0c4409e..d7a66fb 100644 --- a/svc_translation.go +++ b/svc_translation.go @@ -1,8 +1,8 @@ package lokalise import ( + "encoding/json" "fmt" - "github.com/go-resty/resty/v2" "github.com/google/go-querystring/query" ) @@ -45,19 +45,129 @@ type Translation struct { // Used for NewKey type NewTranslation struct { - LanguageISO string `json:"language_iso"` - Translation interface{} `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` - MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` + LanguageISO string `json:"language_iso"` + Translation string `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` + MergeCustomTranslationStatuses bool `json:"merge_custom_translation_statuses,omitempty"` +} + +func (t NewTranslation) MarshalJSON() ([]byte, error) { + type Alias NewTranslation + + var translation interface{} = t.Translation + + if json.Valid([]byte(t.Translation)) { + _ = json.Unmarshal([]byte(t.Translation), &translation) + } + + return json.Marshal(&struct { + LanguageISO string `json:"language_iso"` + Translation interface{} `json:"translation"` + Alias + }{ + LanguageISO: t.LanguageISO, + Translation: translation, + Alias: (Alias)(t), + }) +} + +func (t *NewTranslation) UnmarshalJSON(data []byte) error { + type Alias NewTranslation + aux := &struct { + LanguageISO string `json:"language_iso"` + Translation interface{} `json:"translation"` + *Alias + }{ + Alias: (*Alias)(t), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + switch aux.Translation.(type) { + case map[string]interface{}: + marshal, err := json.Marshal(aux.Translation) + if err != nil { + fmt.Printf("NewTranslation translation marshalling error %v", err) + return err + } + t.Translation = string(marshal) + case string: + t.Translation = fmt.Sprintf("%+v", aux.Translation) + default: + fmt.Printf("NewTranslation tranlation type is of unknown type") + } + + t.LanguageISO = aux.LanguageISO + t.IsFuzzy = aux.IsFuzzy + t.IsReviewed = aux.IsReviewed + t.CustomTranslationStatusIds = aux.CustomTranslationStatusIds + t.MergeCustomTranslationStatuses = aux.MergeCustomTranslationStatuses + + return nil } type UpdateTranslation struct { - Translation interface{} `json:"translation"` - IsFuzzy *bool `json:"is_fuzzy,omitempty"` - IsReviewed bool `json:"is_reviewed,omitempty"` - CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` + Translation string `json:"translation"` + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIDs []string `json:"custom_translation_status_ids,omitempty"` +} + +func (t UpdateTranslation) MarshalJSON() ([]byte, error) { + type Alias UpdateTranslation + + var translation interface{} = t.Translation + + if json.Valid([]byte(t.Translation)) { + _ = json.Unmarshal([]byte(t.Translation), &translation) + } + + return json.Marshal(&struct { + Translation interface{} `json:"translation"` + Alias + }{ + Translation: translation, + Alias: (Alias)(t), + }) +} + +func (t *UpdateTranslation) UnmarshalJSON(data []byte) error { + type Alias UpdateTranslation + aux := &struct { + LanguageISO string `json:"language_iso"` + Translation interface{} `json:"translation"` + *Alias + }{ + Alias: (*Alias)(t), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + switch aux.Translation.(type) { + case map[string]interface{}: + marshal, err := json.Marshal(aux.Translation) + if err != nil { + fmt.Printf("NewTranslation translation marshalling error %v", err) + return err + } + t.Translation = string(marshal) + case string: + t.Translation = fmt.Sprintf("%+v", aux.Translation) + default: + fmt.Printf("NewTranslation tranlation type is of unknown type") + } + + t.IsFuzzy = aux.IsFuzzy + t.IsReviewed = aux.IsReviewed + t.CustomTranslationStatusIDs = aux.CustomTranslationStatusIDs + + return nil } type TranslationsResponse struct { diff --git a/svc_translation_test.go b/svc_translation_test.go index c55031c..362e2e8 100644 --- a/svc_translation_test.go +++ b/svc_translation_test.go @@ -210,3 +210,156 @@ func TestTranslationService_Update(t *testing.T) { t.Errorf("Translations.Update returned %+v, want %+v", r.Translation, want) } } + +func TestNewTranslation_MarshalJSON(t *testing.T) { + translations := []NewTranslation{ + { + LanguageISO: "en", + Translation: "simple text", + }, + { + LanguageISO: "en", + Translation: "{\"one\":\"oneText\",\"other\":\"otherText\"}", + }, + } + + want := JsonCompact(` + [ + { + "language_iso": "en", + "translation": "simple text" + }, + { + "language_iso": "en", + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ]`) + + marshal, err := json.Marshal(translations) + if err != nil { + t.Errorf("NewTranslation marshalling returned error %s", err) + } + + if string(marshal) != want { + t.Errorf("NewTranslation marshalling mismatch: %+v, want %+v", string(marshal), want) + } +} + +func TestUpdateTranslation_MarshalJSON(t *testing.T) { + translations := []UpdateTranslation{ + { + Translation: "simple text", + }, + { + Translation: "{\"one\":\"oneText\",\"other\":\"otherText\"}", + }, + } + + want := JsonCompact(` + [ + { + "translation": "simple text" + }, + { + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ]`) + + marshal, err := json.Marshal(translations) + if err != nil { + t.Errorf("UpdateTranslation marshalling returned error %s", err) + } + + if string(marshal) != want { + t.Errorf("UpdateTranslation marshalling mismatch: %+v, want %+v", string(marshal), want) + } +} + +func TestNewTranslation_UnmarshalJSON(t *testing.T) { + raw := ` + [ + { + "language_iso": "en", + "translation": "simple text" + }, + { + "language_iso": "en", + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ]` + + want := []NewTranslation{ + { + LanguageISO: "en", + Translation: "simple text", + }, + { + LanguageISO: "en", + Translation: "{\"one\":\"oneText\",\"other\":\"otherText\"}", + }, + } + + var result []NewTranslation + + err := json.Unmarshal([]byte(raw), &result) + if err != nil { + t.Errorf("NewTranslation unmarshalling returned error %s", err) + } + + if !reflect.DeepEqual(want, result) { + t.Errorf("NewTranslation unmarshalling mismatch: %+v, want %+v", result, want) + } +} + +func TestUpdateTranslation_UnmarshalJSON(t *testing.T) { + raw := ` + [ + { + "translation": "simple text" + }, + { + "translation": { + "one": "oneText", + "other": "otherText" + } + } + ]` + + want := []UpdateTranslation{ + { + Translation: "simple text", + }, + { + Translation: "{\"one\":\"oneText\",\"other\":\"otherText\"}", + }, + } + + var result []UpdateTranslation + + err := json.Unmarshal([]byte(raw), &result) + if err != nil { + t.Errorf("UpdateTranslation unmarshalling returned error %s", err) + } + + if !reflect.DeepEqual(want, result) { + t.Errorf("UpdateTranslation unmarshalling mismatch: %+v, want %+v", result, want) + } +} + +func JsonCompact(text string) string { + compactedBuffer := new(bytes.Buffer) + err := json.Compact(compactedBuffer, []byte(text)) + if err != nil { + panic(fmt.Sprintf("Invalid test data definition %+v", err)) + } + return compactedBuffer.String() +} From 301df614a020ac7397161847612c419c8430d832 Mon Sep 17 00:00:00 2001 From: Andrejs Labunskis Date: Thu, 30 Sep 2021 16:05:16 +0300 Subject: [PATCH 4/4] SGO-10 Plural key translation: fix error reporting --- svc_translation.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/svc_translation.go b/svc_translation.go index d7a66fb..f031312 100644 --- a/svc_translation.go +++ b/svc_translation.go @@ -91,14 +91,13 @@ func (t *NewTranslation) UnmarshalJSON(data []byte) error { case map[string]interface{}: marshal, err := json.Marshal(aux.Translation) if err != nil { - fmt.Printf("NewTranslation translation marshalling error %v", err) return err } t.Translation = string(marshal) case string: t.Translation = fmt.Sprintf("%+v", aux.Translation) default: - fmt.Printf("NewTranslation tranlation type is of unknown type") + return fmt.Errorf("NewTranslation tranlation type is of unknown type") } t.LanguageISO = aux.LanguageISO @@ -153,14 +152,13 @@ func (t *UpdateTranslation) UnmarshalJSON(data []byte) error { case map[string]interface{}: marshal, err := json.Marshal(aux.Translation) if err != nil { - fmt.Printf("NewTranslation translation marshalling error %v", err) return err } t.Translation = string(marshal) case string: t.Translation = fmt.Sprintf("%+v", aux.Translation) default: - fmt.Printf("NewTranslation tranlation type is of unknown type") + return fmt.Errorf("NewTranslation tranlation type is of unknown type") } t.IsFuzzy = aux.IsFuzzy