Translate app (Without extensions)

Hi everyone :partying_face:,

Today I want to share with you a method to translate your app into any language.

ezgif-816fc5f5fdcdbb

The idea is to associate an ID with each component (like with dynamic components) and then use this association to set the translated text.
We will have a JSON file divided by languages containing categories to organize the components and other elements.

json example
{
	"it": {
		"button": {
			"Button_Test": "Bottone"
		},
		"listPicker": {
			"ListPicker_Test": "Selettore da elenco|mela,banana,pera,durian"
		},
		"common": {
			"Back": "Indietro",
			"Cancel": "Cancella",
			"Close": "Chiudi",
			"Infos": "Informazioni",
			"Next": "Avanti",
			"Notification": "Notifica",
			"Select_Language": "Seleziona una lingua",
			"Select_Option": "Seleziona una opzione",
			"User_Selected": "L'utente ha selezionato"
		},
		"error": {
			"Connection_Error": "Errore di connessione",
			"Generic_Error": "Errore",
			"Network_Error": "Errore di rete",
			"Runtime_Error": "Errore di runtime"
		},
		"general": {
			"AboutScreenTitle": "Riguardo questa applicazione",
			"AboutScreen": "Questo progetto dimostra come è possibile tradurre la propria app in modo semplice ed efficace",
			"AppTitle": "Traduttore"
		},
		"label": {
			"Label_Test": "Etichetta"
		},
		"textbox": {
			"TextBox_Test": "Casella di testo"
		}
	},
	"en": {
		"button": {
			"Button_Test": "Button"
		},
		"listPicker": {
			"ListPicker_Test": "List Picker|apple,banana,pear,durian"
		},
		"common": {
			"Back": "Back",
			"Cancel": "Cancel",
			"Close": "Close",
			"Infos": "Information",
			"Next": "Next",
			"Notification": "Notification",
			"Select_Language": "Select a language",
			"Select_Option": "Select an option",
			"User_Selected": "The user selected"
		},
		"error": {
			"Connection_Error": "Connection error",
			"Generic_Error": "Error",
			"Network_Error": "Network error",
			"Runtime_Error": "Runtime error"
		},
		"general": {
			"AboutScreenTitle": "About this application",
			"AboutScreen": "This project demonstrates how to translate your app easily and effectively",
			"AppTitle": "Translator"
		},
		"label": {
			"Label_Test": "Label"
		},
		"textbox": {
			"TextBox_Test": "Text box"
		}
	}
}

As you can see, in each language there are some categories that don’t directly refer to components, such as common, where I included basic words like close, next, back.
Since these can be used not only on specific components, I decided to keep them in a separate category, along with errors.

But anyway, after learning the basics, they can be managed however you prefer.

Mapping

This is the only part that will be a bit tedious in which we will map each component by associating an ID.
We create in a variable called componentsLink a dictionary containing dictionaries with the categories of components button, label, textbox just like the components categories in the json.

And in each of them we’ll need to map the components with "id": "component", for convenience, I kept the same name.

Once this is done, the hard part is over!

Doing all this with dynamic components makes it really much simpler!
Obviously, static components still need to be mapped manually.

Translate function

Let’s put aside translationDictionary local variable for a moment, which points to a translation dictionary (it, es, en, etc.).
I created 4 for-each loops for the components (button, label, textbox, listPicker) so that they iterate over all the components in the category and set the text value.

For each key with value in

"button": {
	"Button_Test": "This is a test button",
	"Button_Close": "Close"

In the first loop, it will look for the key Button_Test in our componentsLink dictionary and return its component (If it doesn’t find the component, i set orIfNotFound to a button to avoid an error you could link a hidden button component)


immagine
then we will set the text (value) for that component "This is a test button".

I handled the ListPickers this way,

"listPicker": {
	"ListPicker_Test": "List Picker|apple,banana,pear,durian"

The ListPicker.Text is the first value, then I added a | followed by the ListPicker.ElementsFromString values separated by commas, so all the data is in a single string.
Then, during the function, we split at the | to get the text and the items.

At the end of the function, I manually inserted the last values, such as the app title, About Screen, and its title pointing to the values in the general category.

That List_Picker_Language you see, I handled differently because the values it receives are the tags as elements directly from Firebase (it, es, en etc.).

Firebase

Since the translation it’s a JSON, we can upload it to Firebase :smiling_face_with_sunglasses:
In the project, at the end, I set up during initialization a getTags on Firebase to get all available languages (it, en, es, etc.)


and set them as the items of List_Picker_Language.


By selecting a language, getValue is executed on that node, retrieving only that translation.


This is then added to the local translations variable, so if the language has already been downloaded, it won’t be downloaded again.
You can add a TinyDB to make everything more practical, but you’d then need a way to alert the app if the dictionary on the server has changed to download the updated version.
You could add a version key inside each language and check that.

And what if we had to provide this prompt to an AI?

Create a file based on this JSON structure (key: translation) containing the nodes of the other major known languages.
"en": {
			"button": {
				"Button_Test": "Button"
			},
			"listPicker": {
				"ListPicker_Test": "List Picker|apple,banana,pear,durian"
			},
			"common": {
				"Back": "Back",
				"Cancel": "Cancel",
				"Close": "Close",
				"Infos": "Information",
				"Next": "Next",
				"Notification": "Notification",
				"Select_Language": "Select a language",
				"Select_Option": "Select an option",
				"User_Selected": "The user selected"
			},
			"error": {
				"Connection_Error": "Connection error",
				"Generic_Error": "Error",
				"Network_Error": "Network error",
				"Runtime_Error": "Runtime error"
			},
			"general": {
				"AboutScreenTitle": "About this application",
				"AboutScreen": "This project demonstrates how to translate your app easily and effectively",
				"AppTitle": "Translator"
			},
			"label": {
				"Label_Test": "Label"
			},
			"textbox": {
				"TextBox_Test": "Text box"
			}
		}
Result

Sure! I can create a JSON file structure that mirrors your English example (en) but adds nodes for other major languages. I’ll include: Spanish (es), French (fr), German (de), Italian (it), Chinese (zh), Japanese (ja), and Russian (ru). I’ll provide translations for each key.

{
  "en": {
    "button": {
      "Button_Test": "Button"
    },
    "listPicker": {
      "ListPicker_Test": "List Picker|apple,banana,pear,durian"
    },
    "common": {
      "Back": "Back",
      "Cancel": "Cancel",
      "Close": "Close",
      "Infos": "Information",
      "Next": "Next",
      "Notification": "Notification",
      "Select_Language": "Select a language",
      "Select_Option": "Select an option",
      "User_Selected": "The user selected"
    },
    "error": {
      "Connection_Error": "Connection error",
      "Generic_Error": "Error",
      "Network_Error": "Network error",
      "Runtime_Error": "Runtime error"
    },
    "general": {
      "AboutScreenTitle": "About this application",
      "AboutScreen": "This project demonstrates how to translate your app easily and effectively",
      "AppTitle": "Translator"
    },
    "label": {
      "Label_Test": "Label"
    },
    "textbox": {
      "TextBox_Test": "Text box"
    }
  },
  "es": {
    "button": {
      "Button_Test": "Botón"
    },
    "listPicker": {
      "ListPicker_Test": "Selector de lista|manzana,plátano,pera,durian"
    },
    "common": {
      "Back": "Atrás",
      "Cancel": "Cancelar",
      "Close": "Cerrar",
      "Infos": "Información",
      "Next": "Siguiente",
      "Notification": "Notificación",
      "Select_Language": "Selecciona un idioma",
      "Select_Option": "Selecciona una opción",
      "User_Selected": "El usuario seleccionó"
    },
    "error": {
      "Connection_Error": "Error de conexión",
      "Generic_Error": "Error",
      "Network_Error": "Error de red",
      "Runtime_Error": "Error de ejecución"
    },
    "general": {
      "AboutScreenTitle": "Acerca de esta aplicación",
      "AboutScreen": "Este proyecto demuestra cómo traducir tu aplicación fácilmente y de manera efectiva",
      "AppTitle": "Traductor"
    },
    "label": {
      "Label_Test": "Etiqueta"
    },
    "textbox": {
      "TextBox_Test": "Caja de texto"
    }
  },
  "fr": {
    "button": {
      "Button_Test": "Bouton"
    },
    "listPicker": {
      "ListPicker_Test": "Sélecteur de liste|pomme,banane,poire,durian"
    },
    "common": {
      "Back": "Retour",
      "Cancel": "Annuler",
      "Close": "Fermer",
      "Infos": "Informations",
      "Next": "Suivant",
      "Notification": "Notification",
      "Select_Language": "Sélectionner une langue",
      "Select_Option": "Sélectionner une option",
      "User_Selected": "L'utilisateur a sélectionné"
    },
    "error": {
      "Connection_Error": "Erreur de connexion",
      "Generic_Error": "Erreur",
      "Network_Error": "Erreur réseau",
      "Runtime_Error": "Erreur d'exécution"
    },
    "general": {
      "AboutScreenTitle": "À propos de cette application",
      "AboutScreen": "Ce projet montre comment traduire votre application facilement et efficacement",
      "AppTitle": "Traducteur"
    },
    "label": {
      "Label_Test": "Étiquette"
    },
    "textbox": {
      "TextBox_Test": "Zone de texte"
    }
  },
  "de": {
    "button": {
      "Button_Test": "Taste"
    },
    "listPicker": {
      "ListPicker_Test": "Listen-Auswahl|Apfel,Banane,Birne,Durian"
    },
    "common": {
      "Back": "Zurück",
      "Cancel": "Abbrechen",
      "Close": "Schließen",
      "Infos": "Information",
      "Next": "Weiter",
      "Notification": "Benachrichtigung",
      "Select_Language": "Sprache auswählen",
      "Select_Option": "Option auswählen",
      "User_Selected": "Der Benutzer hat ausgewählt"
    },
    "error": {
      "Connection_Error": "Verbindungsfehler",
      "Generic_Error": "Fehler",
      "Network_Error": "Netzwerkfehler",
      "Runtime_Error": "Laufzeitfehler"
    },
    "general": {
      "AboutScreenTitle": "Über diese Anwendung",
      "AboutScreen": "Dieses Projekt zeigt, wie man Ihre App einfach und effektiv übersetzt",
      "AppTitle": "Übersetzer"
    },
    "label": {
      "Label_Test": "Beschriftung"
    },
    "textbox": {
      "TextBox_Test": "Textfeld"
    }
  },
  "it": {
    "button": {
      "Button_Test": "Pulsante"
    },
    "listPicker": {
      "ListPicker_Test": "Selettore lista|mela,banana,pera,durian"
    },
    "common": {
      "Back": "Indietro",
      "Cancel": "Annulla",
      "Close": "Chiudi",
      "Infos": "Informazioni",
      "Next": "Avanti",
      "Notification": "Notifica",
      "Select_Language": "Seleziona una lingua",
      "Select_Option": "Seleziona un'opzione",
      "User_Selected": "L'utente ha selezionato"
    },
    "error": {
      "Connection_Error": "Errore di connessione",
      "Generic_Error": "Errore",
      "Network_Error": "Errore di rete",
      "Runtime_Error": "Errore di runtime"
    },
    "general": {
      "AboutScreenTitle": "Informazioni su questa applicazione",
      "AboutScreen": "Questo progetto mostra come tradurre facilmente ed efficacemente la tua app",
      "AppTitle": "Traduttore"
    },
    "label": {
      "Label_Test": "Etichetta"
    },
    "textbox": {
      "TextBox_Test": "Casella di testo"
    }
  },
  "zh": {
    "button": {
      "Button_Test": "按钮"
    },
    "listPicker": {
      "ListPicker_Test": "列表选择器|苹果,香蕉,梨,榴莲"
    },
    "common": {
      "Back": "返回",
      "Cancel": "取消",
      "Close": "关闭",
      "Infos": "信息",
      "Next": "下一步",
      "Notification": "通知",
      "Select_Language": "选择语言",
      "Select_Option": "选择一个选项",
      "User_Selected": "用户选择了"
    },
    "error": {
      "Connection_Error": "连接错误",
      "Generic_Error": "错误",
      "Network_Error": "网络错误",
      "Runtime_Error": "运行时错误"
    },
    "general": {
      "AboutScreenTitle": "关于此应用程序",
      "AboutScreen": "本项目展示了如何轻松有效地翻译您的应用程序",
      "AppTitle": "翻译器"
    },
    "label": {
      "Label_Test": "标签"
    },
    "textbox": {
      "TextBox_Test": "文本框"
    }
  },
  "ja": {
    "button": {
      "Button_Test": "ボタン"
    },
    "listPicker": {
      "ListPicker_Test": "リストピッカー|リンゴ,バナナ,洋ナシ,ドリアン"
    },
    "common": {
      "Back": "戻る",
      "Cancel": "キャンセル",
      "Close": "閉じる",
      "Infos": "情報",
      "Next": "次へ",
      "Notification": "通知",
      "Select_Language": "言語を選択",
      "Select_Option": "オプションを選択",
      "User_Selected": "ユーザーが選択しました"
    },
    "error": {
      "Connection_Error": "接続エラー",
      "Generic_Error": "エラー",
      "Network_Error": "ネットワークエラー",
      "Runtime_Error": "ランタイムエラー"
    },
    "general": {
      "AboutScreenTitle": "このアプリケーションについて",
      "AboutScreen": "このプロジェクトはアプリを簡単かつ効果的に翻訳する方法を示しています",
      "AppTitle": "翻訳者"
    },
    "label": {
      "Label_Test": "ラベル"
    },
    "textbox": {
      "TextBox_Test": "テキストボックス"
    }
  },
  "ru": {
    "button": {
      "Button_Test": "Кнопка"
    },
    "listPicker": {
      "ListPicker_Test": "Выбор списка|яблоко,банан,груша,дуриан"
    },
    "common": {
      "Back": "Назад",
      "Cancel": "Отмена",
      "Close": "Закрыть",
      "Infos": "Информация",
      "Next": "Далее",
      "Notification": "Уведомление",
      "Select_Language": "Выберите язык",
      "Select_Option": "Выберите опцию",
      "User_Selected": "Пользователь выбрал"
    },
    "error": {
      "Connection_Error": "Ошибка соединения",
      "Generic_Error": "Ошибка",
      "Network_Error": "Ошибка сети",
      "Runtime_Error": "Ошибка выполнения"
    },
    "general": {
      "AboutScreenTitle": "О приложении",
      "AboutScreen": "Этот проект демонстрирует, как легко и эффективно переводить ваше приложение",
      "AppTitle": "Переводчик"
    },
    "label": {
      "Label_Test": "Метка"
    },
    "textbox": {
      "TextBox_Test": "Текстовое поле"
    }
  }
}

:exploding_head::exploding_head::exploding_head:

Project

Firebase structure
FirebaseDatabase.json (9.1 KB)
AIA
Translate.aia (10.2 KB)

I tried to summarize as much as possible but didn’t want to omit important details.
I hope I wasn’t boring and that everything is clear.
For any questions, I’m available for clarifications.

Happy :kodular:oding!

1 Like