Skip to content

Commit

Permalink
Merge pull request #47 from nyaruka/localization_updates
Browse files Browse the repository at this point in the history
Localization updates
  • Loading branch information
ericnewcomer authored Jul 7, 2017
2 parents 94fab76 + 6a84892 commit 55942ec
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 62 deletions.
52 changes: 39 additions & 13 deletions cmd/flowrunner/testdata/flows/two_questions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,69 @@
"localization": {
"fra": {
"e97cd6d5-3354-4dbd-85bc-6c1f87849eec": {
"text": "Quelle est votres couleur preferee? (rouge/blue)"
"text": [
"Quelle est votres couleur preferee? (rouge/blue)"
]
},
"98503572-25bf-40ce-ad72-8836b6549a38": {
"test": "rouge"
"test": [
"rouge"
]
},
"a51e5c8c-c891-401d-9c62-15fc37278c94": {
"test": "blue"
"test": [
"blue"
]
},
"598ae7a5-2f81-48f1-afac-595262514aa1": {
"label": "Rouge"
"name": [
"Rouge"
]
},
"c70fe86c-9aac-4cc2-a5cb-d35cbe3fed6e": {
"label": "Blue"
"name": [
"Blue"
]
},
"78ae8f05-f92e-43b2-a886-406eaea1b8e0": {
"label": "Autres"
"name": [
"Autres"
]
},
"d2a4052a-3fa9-4608-ab3e-5b9631440447": {
"text": "@(TITLE(input.text))! Bien sur! Quelle est votes soda preferee? (pepsi/coke)"
"text": [
"@(TITLE(input.text))! Bien sur! Quelle est votes soda preferee? (pepsi/coke)"
]
},
"e27c3bce-1095-4d08-9164-dc4530a0688a": {
"test": "pepsi"
"test": [
"pepsi"
]
},
"4a6c3b0b-0658-4a93-ae37-bee68f6a6a87": {
"test": "coke"
"test": [
"coke"
]
},
"2ab9b033-77a8-4e56-a558-b568c00c9492": {
"label": "Pepsi"
"label": [
"Pepsi"
]
},
"c7bca181-0cb3-4ec6-8555-f7e5644238ad": {
"label": "Coke"
"label": [
"Coke"
]
},
"5ce6c69a-fdfe-4594-ab71-26be534d31c3": {
"label": "Autres"
"label": [
"Autres"
]
},
"0a8467eb-911a-41db-8101-ccf415c48e6a": {
"text": "Parfait, vous avez finis et tu aimes @run.results.soda.category"
"text": [
"Parfait, vous avez finis et tu aimes @run.results.soda.category"
]
}
}
},
Expand Down
24 changes: 19 additions & 5 deletions excellent/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,33 @@ func (v vars) String() string {
}

type arbitraryVars struct {
vars vars
base string
nesting string
vars vars
base string
nesting string
nestedVars vars
}

func (v arbitraryVars) Resolve(key string) interface{} {

value, ok := v.vars[key]
if ok {
return fmt.Sprintf("%s.%s", v.base, value)
}

prefix := v.base
if v.nesting != "" {
return fmt.Sprintf("%s.%s.%s", v.base, v.nesting, key)
prefix = fmt.Sprintf("%s.%s", v.base, v.nesting)
}

if v.nestedVars != nil {
return &arbitraryVars{
base: fmt.Sprintf("%s.%s", prefix, key),
vars: v.nestedVars,
}
}

return fmt.Sprintf("%s.%s", v.base, key)
return fmt.Sprintf("%s.%s", prefix, key)

}

func (v arbitraryVars) Default() interface{} {
Expand Down Expand Up @@ -140,6 +151,9 @@ func newVars() vars {
},
"flow": arbitraryVars{
base: "run.results",
nestedVars: map[string]interface{}{
"category": "category_localized",
},
},
"step": vars{
"value": "input.text",
Expand Down
2 changes: 1 addition & 1 deletion excellent/legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestTranslate(t *testing.T) {
{old: "@contact.uuid", new: "@contact.uuid"},
{old: "@contact.blerg", new: "@contact.fields.blerg"},
{old: "@flow.blerg", new: "@run.results.blerg"},
{old: "@flow.blerg.category", new: "@run.results.blerg.category"},
{old: "@flow.blerg.category", new: "@run.results.blerg.category_localized"},
{old: "@step.value", new: "@input.text"},
{old: "@step.contact", new: "@step.contact"},
{old: "@date.now", new: "@(now())"},
Expand Down
11 changes: 7 additions & 4 deletions flows/actions/save_flow_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,20 @@ func (a *SaveFlowResultAction) Execute(run flows.FlowRun, step flows.Step) error
}

template = run.GetText(flows.UUID(a.UUID), "category", a.Category)
category, err := excellent.EvaluateTemplateAsString(run.Environment(), run.Context(), template)
categoryLocalized, err := excellent.EvaluateTemplateAsString(run.Environment(), run.Context(), template)
if err != nil {
run.AddError(step, err)
}

// log our event
event := events.NewSaveFlowResult(step.NodeUUID(), a.ResultName, value, category)
if a.Category == categoryLocalized {
categoryLocalized = ""
}

event := events.NewSaveFlowResult(step.NodeUUID(), a.ResultName, value, a.Category, categoryLocalized)
run.AddEvent(step, event)

// and save our result
run.Results().Save(step.NodeUUID(), a.ResultName, value, a.Category, *event.CreatedOn())
run.Results().Save(step.NodeUUID(), a.ResultName, value, a.Category, categoryLocalized, *event.CreatedOn())

return nil
}
30 changes: 16 additions & 14 deletions flows/definition/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import (
"github.com/satori/go.uuid"
)

type legacyFlow struct {
// LegacyFlow imports an old-world flow so it can be exported anew
type LegacyFlow struct {
flow
envelope legacyFlowEnvelope
}
Expand Down Expand Up @@ -130,8 +131,8 @@ type stringTest struct {
type localizations map[utils.Language]flows.Action

// ReadLegacyFlows reads in legacy formatted flows
func ReadLegacyFlows(data json.RawMessage) ([]legacyFlow, error) {
var flows []legacyFlow
func ReadLegacyFlows(data json.RawMessage) ([]LegacyFlow, error) {
var flows []LegacyFlow
err := json.Unmarshal(data, &flows)
return flows, err
}
Expand All @@ -142,7 +143,7 @@ func addTranslationMap(baseLanguage utils.Language, translations *flowTranslatio
for language, translation := range mapped {
items := itemTranslations{}
expression, _ := excellent.TranslateTemplate(translation)
items[key] = expression
items[key] = []string{expression}
if language != baseLanguage {
addTranslation(baseLanguage, translations, language, uuid, items)
}
Expand Down Expand Up @@ -253,17 +254,16 @@ func createAction(baseLanguage utils.Language, a legacyAction, fieldMap map[stri
addTranslationMap(baseLanguage, translations, msg, flows.UUID(a.UUID), "text")

// TODO translations for each attachment?

text_expression, _ := excellent.TranslateTemplate(msg[baseLanguage])
attachment_expression, _ := excellent.TranslateTemplate(media[baseLanguage])
textExpression, _ := excellent.TranslateTemplate(msg[baseLanguage])
attachmentExpression, _ := excellent.TranslateTemplate(media[baseLanguage])

attachments := []string{}
if attachment_expression != "" {
attachments = append(attachments, attachment_expression)
if attachmentExpression != "" {
attachments = append(attachments, attachmentExpression)
}

return &actions.ReplyAction{
Text: text_expression,
Text: textExpression,
Attachments: attachments,
BaseAction: actions.BaseAction{
UUID: a.UUID,
Expand Down Expand Up @@ -419,7 +419,7 @@ func parseRules(baseLanguage utils.Language, r legacyRuleSet, translations *flow
exits := make([]flows.Exit, len(categoryMap))
exitMap := make(map[string]flows.Exit)
for k, category := range categoryMap {
addTranslationMap(baseLanguage, translations, category.translations, flows.UUID(category.uuid), "label")
addTranslationMap(baseLanguage, translations, category.translations, flows.UUID(category.uuid), "name")

exits[category.order] = &exit{
name: k,
Expand Down Expand Up @@ -559,13 +559,14 @@ func createActionNode(lang utils.Language, a legacyActionSet, fieldMap map[strin
node.exits = make([]flows.Exit, 1)
node.exits[0] = &exit{
destination: a.Destination,
uuid: flows.ExitUUID(a.UUID),
uuid: flows.ExitUUID(uuid.NewV4().String()),
}
return node

}

func (f *legacyFlow) UnmarshalJSON(data []byte) error {
// UnmarshalJSON imports our JSON into a LegacyFlow object
func (f *LegacyFlow) UnmarshalJSON(data []byte) error {
var envelope legacyFlowEnvelope
var err error

Expand Down Expand Up @@ -611,7 +612,8 @@ func (f *legacyFlow) UnmarshalJSON(data []byte) error {
return err
}

func (f *legacyFlow) MarshalJSON() ([]byte, error) {
// MarshalJSON sends turns our legacy flow into bytes
func (f *LegacyFlow) MarshalJSON() ([]byte, error) {

var fe = flowEnvelope{}
fe.Name = f.name
Expand Down
17 changes: 14 additions & 3 deletions flows/definition/localization.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"github.com/nyaruka/goflow/utils"
)

// itemTranslations map a key for a node to a key - say "text" to "je suis francais!"
type itemTranslations map[string]string
// itemTranslations map a key for a node to a key - say "text" to "[je suis francais!]"
type itemTranslations map[string][]string

// languageTranslations map a node uuid to item_translations - say "node1-asdf" to { "text": "je suis francais!" }
type languageTranslations map[flows.UUID]itemTranslations

func (t *languageTranslations) GetText(uuid flows.UUID, key string, backdown string) string {
func (t *languageTranslations) GetTranslations(uuid flows.UUID, key string, backdown []string) []string {
item, found := (*t)[uuid]
if found {
translation, found := item[key]
Expand All @@ -22,6 +22,17 @@ func (t *languageTranslations) GetText(uuid flows.UUID, key string, backdown str
return backdown
}

func (t *languageTranslations) GetText(uuid flows.UUID, key string, backdown string) string {
item, found := (*t)[uuid]
if found {
translation, found := item[key]
if found && len(translation) > 0 {
return translation[0]
}
}
return backdown
}

// flowTranslations are our top level container for all the translations for a language
type flowTranslations map[utils.Language]*languageTranslations

Expand Down
10 changes: 7 additions & 3 deletions flows/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,11 @@ func pickNodeExit(run flows.FlowRun, node flows.Node, step flows.Step) (flows.No
// find our exit
for _, e := range node.Exits() {
if e.UUID() == exitUUID {
exitName = e.Name()

localizedName := run.GetText(flows.UUID(exitUUID), "name", e.Name())
if localizedName != e.Name() {
exitName = localizedName
}
exit = e
break
}
Expand All @@ -277,9 +281,9 @@ func pickNodeExit(run flows.FlowRun, node flows.Node, step flows.Step) (flows.No

// save our results if appropriate
if router != nil && router.ResultName() != "" {
event := events.NewSaveFlowResult(node.UUID(), router.ResultName(), route.Match(), exitName)
event := events.NewSaveFlowResult(node.UUID(), router.ResultName(), route.Match(), exit.Name(), exitName)
run.AddEvent(step, event)
run.Results().Save(node.UUID(), router.ResultName(), route.Match(), exitName, *event.CreatedOn())
run.Results().Save(node.UUID(), router.ResultName(), route.Match(), exit.Name(), exitName, *event.CreatedOn())
}

// log any error we received
Expand Down
13 changes: 7 additions & 6 deletions flows/events/save_flow_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ const TypeSaveFlowResult string = "save_flow_result"
// @event save_flow_result
type SaveFlowResultEvent struct {
BaseEvent
NodeUUID flows.NodeUUID `json:"node_uuid" validate:"required"`
ResultName string `json:"result_name" validate:"required"`
Value string `json:"value"`
Category string `json:"category"`
NodeUUID flows.NodeUUID `json:"node_uuid" validate:"required"`
ResultName string `json:"result_name" validate:"required"`
Value string `json:"value"`
Category string `json:"category"`
CategoryLocalized string `json:"category_localized,omitempty"`
}

// NewSaveFlowResult returns a new save result event for the passed in values
func NewSaveFlowResult(node flows.NodeUUID, name string, value string, category string) *SaveFlowResultEvent {
return &SaveFlowResultEvent{NodeUUID: node, ResultName: name, Value: value, Category: category}
func NewSaveFlowResult(node flows.NodeUUID, name string, value string, categoryName string, categoryLocalized string) *SaveFlowResultEvent {
return &SaveFlowResultEvent{NodeUUID: node, ResultName: name, Value: value, Category: categoryName, CategoryLocalized: categoryLocalized}
}

// Type returns the type of this event
Expand Down
2 changes: 2 additions & 0 deletions flows/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ type FlowTranslations interface {
// Translations provide a way to get the translation for a specific language for a uuid/key pair
type Translations interface {
GetText(uuid UUID, key string, backdown string) string
GetTranslations(uuid UUID, key string, backdown []string) []string
}

type Context interface {
Expand Down Expand Up @@ -249,6 +250,7 @@ type FlowRun interface {
SetLanguage(utils.Language)
SetFlowTranslations(FlowTranslations)
GetText(uuid UUID, key string, backdown string) string
GetTranslations(uuid UUID, key string, backdown []string) []string

Webhook() *utils.RequestResponse
SetWebhook(*utils.RequestResponse)
Expand Down
Loading

0 comments on commit 55942ec

Please sign in to comment.