I want to implement language XML into my project and change all hard-code strings into language.xml references based on the way Android uses string resources. (I have not found anything that does this)
en_gb.xml:
<resource>
<section1>
<string name="hello">Hello</string>
<string name="bye">Goodbye</string>
</section1>
<section2>
<string name="world">World</string>
<string name="end">!</string>
</section2>
</resource>
jp_tr.xml
<resource>
<section1>
<string name="hello">こんにちは</string>
<string name="bye">さようなら</string>
</section1>
<section2>
<string name="world">世界</string>
<string name="end">!</string>
</section2>
</resource>
Not using this anymore See Edit-> now using ElementTree and exec() i can build classes based on these files
class en_gb:
tr = ET.parse(r'.\assets\local\en_gb.xml')
for rs in tr.getroot():
exec(f'class {rs.tag}:pass')
for c in rs:
exec(f'{rs.tag}.{c.attrib["name"]}=str("{c.text}")')
This creates a structure as 'lang.section.strname' which i can then use in code
from localization import en_gb, jp_tr
lang = en_gb
print(lang.section1.hello) #> Hello
lang = jp_tr
print(lang.section2.world) #> 世界
Now i want to programically create all the base classes to automatically create the structure. However i cannot find a method to add a class to another class programically and construct it in the same way.
import xml.etree.ElementTree as ET
import os
from pathlib import Path
for lang in os.listdir('.\\assets\local\\'):
lang = Path(language).stem
exec(f'class {lang}:pass')
for rs in ET.parse(fr'.\assets\local\{lang}.xml').getroot():
exec(f'class{lang}.{rs.tag} = class {rs.tag}:pass')
for c in rs:
exec(f'{rs.tag}.{c.attrib["name"]}=str("{c.text}")')
however en_gb.base = class base:pass is not a valid syntax and so I'm stuck.
After creation here is how it should look (as in the raw code):
class en_gb:
class section1:
hello = "Hello"
bye = "Goodbye"
class section2:
world = "World"
end = "!"
class jp_tr:
class section1:
hello = "こんにちは"
bye = "さようなら"
class section2:
world = "世界"
end = "!"
Edit: I have replaced the previous method with SimpleNamespace
class local(object):
def __init__(self, lang="en_gb"):
l = localization()
self = getattr(l.langs, lang)
def set_lang(self, lang:str):
self = getattr(l.langs, lang)
class localization:
def __init__(self, lang="en_gb"):
langs = {}
for language in list(Path('./assets/local/').glob('*.xml')):
l_name = Path(language).stem
tree = ElementTree.parse(f".\\assets\\local\\{l_name}.xml")
d = {}
for child in tree.getroot():
tmp = {}
for c in child:
tmp[c.attrib['name']] = c.text
d[child.tag] = SimpleNamespace(**tmp)
langs[l_name] = SimpleNamespace(**d)
self.langs = SimpleNamespace(**langs)
def __get__(self):
print("getting")
return self.langs
However i cannot change the language using the locals class.
l = localization()
print(l.langs.en_gb.section1.hello) ##this works
lg = local()
print(lg.section1.hello) ##this doesnt work
#this should be able to change on the flu
lg.set_lang('jp_tr')
print(lg.section1.hello) ##should now be こんにちは
exec(f'class {rs.tag}:pass')is wide open for code injection, can you not do with nested dicts ?a.b.cyou have to fiddle with symbolsa["b"]["c"], even with IDE optimization its still a much more fiddley task. I know eval() and exec() are bad ways of doing this but i cannot find anything close to a solution that does not require strings on top of strings.