mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
* l10n: move hard-coded UI text into string resources (#212) About 650 user-facing strings were written in Kotlin and Java, so no translation could reach them and the UI stayed half English in every language. They now live in values/strings.xml. - Dialogs, menus, status lines, toasts and errors shown to the user go through getString/stringResource. - Code that matched on English text now uses IDs: installer and loading stages, Decky stages, storage labels and the Back button's order labels. - Choice lists (upscalers, FPS limits, textures, themes, presets) carry @StringRes labels instead of English. - Robolectric tests read the app's resources (includeAndroidResources). - Six strings nothing used any more are dropped. Existing translations are untouched; the new strings show in English until they are translated. * Layouts: fit longer text in other languages, and small phones Found by screenshotting every page, tab and menu in each language on a phone, a tablet, a 640dp phone and a foldable. - Rail labels shrink to fit instead of being cut off (FitText). - Tab rows tighten when they overflow and scroll the chosen tab into view. - Menus fade over a down arrow when more entries are below, and open scrolled to the current choice. - On a small phone the desktop card's button moves under its title, emulator tiles go two across, the GPU support verdict wraps and the Updates page stacks the channels under the status. * l10n: translate every string into Spanish, Japanese, Korean and Chinese (#187) Simplified Chinese, Traditional Chinese (Taiwan) and Traditional Chinese (Hong Kong, reviewed for Hong Kong wording) are complete, as are Spanish, Japanese and Korean: no missing keys and no placeholder mismatches. * Language setting: follow the system, or a language picked in Setup; Steam uses it too (#187) - Setup -> Launcher starts with a Language row: "System default (...)" and each language by its own name. With nothing picked the app follows the system language; a pick applies at once and stays until System default is picked again. - Steam keeps its language in ~/.steam/registry.vdf under both Valve/Steam/language and Valve/Steamsteamglobal/language. Before each start of the client the guest helper droiddeck-steam-language writes both (and exportedsettings.json), and the client gets -language. SteamLanguage maps the app's language to Steam's names: english, spanish/latam, japanese, koreana, schinese, tchinese. * l10n: translate the strings main added since, and Chinese review fixes - Translate the 32 strings added on main since (Windows components, device details, Force SSBS, native Steam sleep) into all six languages. - zh-CN: busy labels say what is in progress again (正在取消, 正在导出 ...), as the other languages do. - zh-CN: "set up" is 配置, as Setup is; 设置 stays for Settings. - zh-TW: proton_compatible_note says 前綴 rather than "prefix".
139 lines
4.6 KiB
Python
139 lines
4.6 KiB
Python
import json
|
|
from pathlib import Path
|
|
import runpy
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
BIN = Path(__file__).resolve().parents[1] / 'linuxfs/overlay/usr/local/bin'
|
|
MODULE = runpy.run_path(str(BIN / 'droiddeck-steam-language'))
|
|
apply = MODULE['apply']
|
|
with_language = MODULE['with_language']
|
|
tokenize = MODULE['tokenize']
|
|
child = MODULE['child']
|
|
block_end = MODULE['block_end']
|
|
main = MODULE['main']
|
|
|
|
|
|
def value(text, *path):
|
|
tokens = tokenize(text)
|
|
start, end = 0, len(tokens)
|
|
for name in path[:-1]:
|
|
opening = child(tokens, start, end, name)
|
|
if opening is None:
|
|
return None
|
|
start, end = opening + 1, block_end(tokens, opening)
|
|
i = start
|
|
while i < end:
|
|
if i + 1 < end and tokens[i + 1] == '{':
|
|
i = block_end(tokens, i + 1) + 1
|
|
continue
|
|
if tokens[i].lower() == '"%s"' % path[-1].lower():
|
|
return tokens[i + 1].strip('"')
|
|
i += 2
|
|
return None
|
|
|
|
|
|
STEAM = ('Registry', 'HKCU', 'Software', 'Valve', 'Steam', 'language')
|
|
GLOBAL = ('Registry', 'HKCU', 'Software', 'Valve', 'Steamsteamglobal', 'language')
|
|
|
|
# As the client leaves it on a device (trimmed): both keys, other values beside them, and HKLM.
|
|
CLIENT = '''"Registry"
|
|
{
|
|
"HKLM"
|
|
{
|
|
"Software"
|
|
{
|
|
"Valve"
|
|
{
|
|
"Steam"
|
|
{
|
|
"InstallPath" "/root/.local/share/Steam"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"HKCU"
|
|
{
|
|
"Software"
|
|
{
|
|
"Valve"
|
|
{
|
|
"Steam"
|
|
{
|
|
"language" "english"
|
|
"AutoLoginUser" "someone"
|
|
"Apps"
|
|
{
|
|
"language" "not this one"
|
|
}
|
|
}
|
|
"Steamsteamglobal"
|
|
{
|
|
"language" "english"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
'''
|
|
|
|
|
|
class SteamLanguageTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.home = Path(self.temp.name)
|
|
(self.home / '.steam').mkdir()
|
|
self.registry = self.home / '.steam/registry.vdf'
|
|
|
|
def test_both_keys_change_and_everything_else_stays(self):
|
|
text = with_language(CLIENT, 'japanese')
|
|
self.assertEqual('japanese', value(text, *STEAM))
|
|
self.assertEqual('japanese', value(text, *GLOBAL))
|
|
self.assertEqual('someone', value(text, 'Registry', 'HKCU', 'Software', 'Valve', 'Steam', 'AutoLoginUser'))
|
|
self.assertEqual('not this one', value(text, 'Registry', 'HKCU', 'Software', 'Valve', 'Steam', 'Apps', 'language'))
|
|
self.assertEqual('/root/.local/share/Steam', value(text, 'Registry', 'HKLM', 'Software', 'Valve', 'Steam', 'InstallPath'))
|
|
self.assertEqual(1, text.count('"HKCU"'))
|
|
|
|
def test_a_first_run_gets_a_registry_the_client_reads(self):
|
|
self.assertTrue(apply(str(self.home), 'koreana'))
|
|
text = self.registry.read_text()
|
|
self.assertTrue(text.startswith('"Registry"'))
|
|
self.assertEqual('koreana', value(text, *STEAM))
|
|
self.assertEqual('koreana', value(text, *GLOBAL))
|
|
self.assertEqual({'language': 'koreana'}, json.loads((self.home / '.steam/exportedsettings.json').read_text()))
|
|
|
|
def test_missing_keys_are_added_whatever_their_case(self):
|
|
text = with_language('"registry"\n{\n\t"hkcu"\n\t{\n\t\t"software"\n\t\t{\n\t\t}\n\t}\n}\n', 'schinese')
|
|
self.assertEqual('schinese', value(text, *STEAM))
|
|
self.assertEqual('schinese', value(text, *GLOBAL))
|
|
self.assertEqual(1, text.lower().count('"hkcu"'))
|
|
self.assertEqual(1, text.lower().count('"software"'))
|
|
|
|
def test_a_value_is_replaced_not_duplicated_whatever_its_key_case(self):
|
|
text = with_language(CLIENT.replace('"language"\t\t"english"', '"Language"\t\t"english"'), 'latam')
|
|
self.assertEqual(2, text.lower().count('"language"\t\t"latam"'))
|
|
self.assertNotIn('english', text)
|
|
|
|
def test_an_unchanged_language_writes_nothing(self):
|
|
self.registry.write_text(CLIENT)
|
|
apply(str(self.home), 'spanish')
|
|
before = self.registry.stat().st_mtime_ns
|
|
self.assertFalse(apply(str(self.home), 'spanish'))
|
|
self.assertEqual(before, self.registry.stat().st_mtime_ns)
|
|
|
|
def test_other_exported_settings_are_kept(self):
|
|
(self.home / '.steam/exportedsettings.json').write_text('{"language": "english", "other": 1}')
|
|
apply(str(self.home), 'tchinese')
|
|
self.assertEqual({'language': 'tchinese', 'other': 1}, json.loads((self.home / '.steam/exportedsettings.json').read_text()))
|
|
|
|
def test_only_the_clients_names_are_accepted(self):
|
|
self.assertEqual(2, main(['x', str(self.home), 'korean']))
|
|
self.assertEqual(2, main(['x', str(self.home), 'Spanish']))
|
|
self.assertFalse(self.registry.exists())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|