Add language support

This commit is contained in:
OMGeeky
2023-03-23 18:22:35 +01:00
parent 8b0c48463e
commit 4192dc52e7
8 changed files with 142 additions and 36 deletions

View File

@@ -1 +1 @@
from .settings import AppSettings
from .settings import AppSettings, FILES_PATH

View File

@@ -1,5 +1,9 @@
from dataclasses import dataclass, asdict
import json
from pathlib import Path
FILES_PATH = Path("files")
settings_file_path = Path(FILES_PATH,'settings.json')
@dataclass
class AppSettings:
@@ -13,7 +17,9 @@ class AppSettings:
def to_json_file(self):
settings_dict = {"settings": asdict(self)}
try:
with open("settings.json", "x") as json_file:
if not FILES_PATH.is_dir():
FILES_PATH.mkdir()
with open(settings_file_path, "x") as json_file:
json.dump(settings_dict, json_file, indent=4)
except FileExistsError:
return
@@ -23,7 +29,7 @@ class AppSettings:
settings_dict = None
try:
with open("settings.json", "r") as json_file:
with open(settings_file_path, "r") as json_file:
settings_dict = json.load(json_file)
except FileNotFoundError:
return None
@@ -41,3 +47,13 @@ class AppSettings:
print(new_settings)
return new_settings
@staticmethod
def get_or_create():
settings = AppSettings.from_json_file()
if settings is None:
settings = AppSettings()
settings.to_json_file()
return settings

1
src/language/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .lang import TranslationProvider, LANGUAGES

46
src/language/lang.py Normal file
View File

@@ -0,0 +1,46 @@
# from data import AppSettings
import json
from pathlib import Path
from typing import Dict, Optional
LANGUAGES = { "DE": "Deutsch", "EN": "English", "FR": "Francais" }
LANGUAGE_FOLDER = Path('res', "lang")
class TranslationProvider:
last_language_key: Optional[str] = None
last_language_dict: Optional[Dict[str,str]] = None
@classmethod
def get_language_file(cls, language: str) -> Dict[str, str]:
if language not in list(LANGUAGES.keys()):
raise ValueError('invalid language key: "{language}"')
if cls.last_language_key == language and cls.last_language_dict is not None:
return cls.last_language_dict
path = Path(LANGUAGE_FOLDER, f'{language}.json' )
try:
with open(path, 'r', encoding='utf-8') as f:
result = json.load(f)
cls.last_language_key = language
cls.last_language_dict = result
return result
except FileNotFoundError:
print(f'Language not found: "{language}"')
raise
@classmethod
def get_translated(cls, key:str, language:str) -> str:
lang_dict = cls.get_language_file(language)
result = lang_dict.get(key, None)
if result:
return result
print(f'unsuccesful try to get {key} in language {language}')
return f'key not found for language: \nkey:"{key}"\nlanguage:"{language}"'

View File

@@ -1,4 +1,5 @@
from os import name
from typing import Optional
from kivy.app import App
from kivy.properties import ListProperty, StringProperty, BooleanProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
@@ -16,8 +17,9 @@ from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.list import OneLineAvatarIconListItem
import json
from data import AppSettings
from language import TranslationProvider, LANGUAGES
LANGUAGES = { "DE": "Deutsch", "EN": "English", "FR": "Francais" }
if kivy.utils.platform not in ['android', 'ios']:
Window.size = (400, 800)
@@ -34,8 +36,13 @@ class ShoppingEntry(OneLineAvatarIconListItem):
class AddDialog(MDBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def get_translated(self, key:str)->str:
settings = AppSettings.get_or_create()
return TranslationProvider.get_translated(key, settings.language)
class ShoppingEntryScreen(Screen):
settings : Optional[AppSettings] = None
add_dialog = None
def on_bestaetigen(self, *args):
@@ -55,14 +62,14 @@ class ShoppingEntryScreen(Screen):
if self.add_dialog:
return
# SPRACHE
language = self.get_settings().language
buttons = [
MDFlatButton(text='Abbrechen', on_release=self.close_add_popup),
MDFlatButton(text='Bestätigen', on_release=lambda args: self.on_bestaetigen(args)),
MDFlatButton(text=TranslationProvider.get_translated('cancel', language), on_release=self.close_add_popup),
MDFlatButton(text=TranslationProvider.get_translated('confirm', language), on_release=lambda args: self.on_bestaetigen(args)),
]
# SPRACHE
self.add_dialog = MDDialog(
title='Eintrag hinzufügen',
title=TranslationProvider.get_translated('add_entry', language),
type='custom',
content_cls=AddDialog(),
buttons=buttons,
@@ -89,6 +96,19 @@ class ShoppingEntryScreen(Screen):
def navigate_to_settings(self):
self.manager.transition.direction = 'left'
self.manager.current = 'settings'
self.settings = None # reset settings to reload them after change
def load_settings(self)->None:
if not self.settings:
self.settings = AppSettings.get_or_create()
def get_settings(self) -> AppSettings:
self.load_settings()
return self.settings # type: ignore
def get_translated(self, key) -> str:
language = self.get_settings().language
return TranslationProvider.get_translated(key,language)
class SettingsScreen(Screen):
settings = ObjectProperty(None)
@@ -96,7 +116,7 @@ class SettingsScreen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.load_settings()
self.settings = AppSettings.get_or_create()
menu_items = [
{
@@ -127,16 +147,13 @@ class SettingsScreen(Screen):
language = LANGUAGES.get(language_key)
self.ids.language_drop_down.set_item(language)
self.menu.dismiss()
def get_translated(self, key) -> str:
if self.settings is None:
self.settings = AppSettings.get_or_create()
def load_settings(self):
settings = AppSettings.from_json_file()
if settings is None:
self.create_settings_json()
def create_settings_json(self):
settings = AppSettings()
settings.to_json_file()
language = self.settings.language
return TranslationProvider.get_translated(key,language)
class ShoppingListApp(MDApp):
def build(self):

15
src/res/lang/DE.json Normal file
View File

@@ -0,0 +1,15 @@
{
"cancel": "Abbrechen"
,"confirm": "Bestätigen"
,"add_entry": "Eintrag hinzufügen"
,"new_entry": "Neuer Eintrag"
,"app_title":"Shopping-List-App"
,"settings": "Einstellungen"
,"language": "Sprache"
,"theme":"Theme"
,"mqtt-server":"MQTT-Server"
,"mqtt-topic":"MQTT-Topic"
,"mqtt-username":"MQTT-Benutzername"
,"mqtt-password":"MQTT-Passwort"
}

15
src/res/lang/EN.json Normal file
View File

@@ -0,0 +1,15 @@
{
"cancel": "Cancel"
,"confirm": "Confirm"
,"add_entry": "Add Entry"
,"new_entry": "New Entry"
,"app_title":"Shopping-List-App"
,"settings": "Settings"
,"language": "Language"
,"theme":"Theme"
,"mqtt-server":"MQTT-Server"
,"mqtt-topic":"MQTT-Topic"
,"mqtt-username":"MQTT-Username"
,"mqtt-password":"MQTT-Password"
}

View File

@@ -21,7 +21,7 @@
id: shopping_entry_text
pos_hint: { 'center_y': 0.4 }
# SPRACHE
hint_text: 'Neuer Eintrag'
hint_text: root.get_translated('new_entry')
mode: "round"
<ShoppingEntryScreen>:
@@ -30,8 +30,7 @@
pos_hint: { 'center_x': 0.5, 'center_y': 0.5 }
MDTopAppBar:
#SPRACHE
title: 'Shopping-List-App'
title: root.get_translated('app_title')
md_bg_color: app.theme_cls.primary_color
specific_text_color: '#1C2120'
right_action_items: [['res/settings.png', lambda x: root.navigate_to_settings()]]
@@ -54,8 +53,7 @@
orientation: 'vertical'
MDTopAppBar:
#SPRACHE
title: 'Einstellungen'
title: root.get_translated('settings')
md_bg_color: app.theme_cls.primary_color
left_action_items: [['res/arrow_back.png', lambda x: root.navigate_to_shopping_list()]]
@@ -68,48 +66,46 @@
row_force_default: True
row_default_height: '75dp'
# SPRACHE
MDLabel:
text: 'Sprache'
text: root.get_translated('language')
MDDropDownItem:
id: language_drop_down
# SPRACHE?
text: 'Deutsch'
on_release: root.show_languages_menu()
# SPRACHE
MDLabel:
text: 'Theme'
text: root.get_translated('theme')
MDSwitch:
id: theme_switch
widget_style: "android"
active: False
# SPRACHE
MDLabel:
text: 'MQTT-Server'
text: root.get_translated('mqtt-server')
MDTextField:
id: mqtt_server_text_field
# SPRACHE?
hint_text: 'mqtt.server.de'
mode: "round"
# SPRACHE
MDLabel:
text: 'MQTT-Topic'
text: root.get_translated('mqtt-topic')
MDTextField:
id: mqtt_topic_text_field
# SPRACHE?
hint_text: 'topic'
mode: "round"
# SPRACHE
MDLabel:
text: 'MQTT-Benutzername'
text: root.get_translated('mqtt-username')
MDTextField:
# SPRACHE?
hint_text: 'username'
mode: "round"
# SPRACHE
MDLabel:
text: 'MQTT-Passwort'
text: root.get_translated('mqtt-password')
MDTextField:
password: True
mode: "round"