diff --git a/README.md b/README.md index a14de32..6d24d3e 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,8 @@ log_level = debug max_memory = 1024 ; default install directory install_dir = /mnt/tank/games +; locale override, must be in RFC 1766 format (e.g. "en-US") +locale = en-US ; default settings to use (currently limited to WINE executable) [default] @@ -321,6 +323,8 @@ skip_update_check = true ; start parameters to use (in addition to the required ones) start_params = -windowed wine_executable = /path/to/proton/wine64 +; override language with two-letter language code +language = fr [AppName.env] ; environment variables to set for this game (mostly useful on linux) diff --git a/legendary/api/egs.py b/legendary/api/egs.py index 67d9396..886e4bd 100644 --- a/legendary/api/egs.py +++ b/legendary/api/egs.py @@ -22,7 +22,7 @@ class EPCAPI: _ecommerce_host = 'ecommerceintegration-public-service-ecomprod02.ol.epicgames.com' _datastorage_host = 'datastorage-public-service-liveegs.live.use1a.on.epicgames.com' - def __init__(self): + def __init__(self, lc='en', cc='US'): self.session = requests.session() self.log = logging.getLogger('EPCAPI') self.unauth_session = requests.session() @@ -33,6 +33,9 @@ class EPCAPI: self.access_token = None self.user = None + self.language_code = lc + self.country_code = cc + def resume_session(self, session): self.session.headers['Authorization'] = f'bearer {session["access_token"]}' r = self.session.get(f'https://{self._oauth_host}/account/api/oauth/verify') @@ -115,7 +118,7 @@ class EPCAPI: def get_game_info(self, namespace, catalog_item_id): r = self.session.get(f'https://{self._catalog_host}/catalog/api/shared/namespace/{namespace}/bulk/items', params=dict(id=catalog_item_id, includeDLCDetails=True, includeMainGameDetails=True, - country='US', locale='en')) + country=self.country_code, locale=self.language_code)) r.raise_for_status() return r.json().get(catalog_item_id, None) diff --git a/legendary/cli.py b/legendary/cli.py index 225823c..aed2df2 100644 --- a/legendary/cli.py +++ b/legendary/cli.py @@ -370,7 +370,8 @@ class LegendaryCLI: params, cwd, env = self.core.get_launch_parameters(app_name=app_name, offline=args.offline, extra_args=extra, user=args.user_name_override, - wine_bin=args.wine_bin, wine_pfx=args.wine_pfx) + wine_bin=args.wine_bin, wine_pfx=args.wine_pfx, + language=args.language) logger.info(f'Launching {app_name}...') if args.dry_run: @@ -679,6 +680,8 @@ def main(): help='Override username used when launching the game (only works with some titles)') launch_parser.add_argument('--dry-run', dest='dry_run', action='store_true', help='Print the command line that would have been used to launch the game and exit') + launch_parser.add_argument('--language', dest='language', action='store', metavar='', + help='Override language for game launch (defaults to system settings)') if os.name != 'nt': launch_parser.add_argument('--wine', dest='wine_bin', action='store', metavar='', default=os.environ.get('LGDRY_WINE_BINARY', None), diff --git a/legendary/core.py b/legendary/core.py index c3428d6..3e153fb 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -9,6 +9,7 @@ import shutil from base64 import b64decode from collections import defaultdict from datetime import datetime, timezone +from locale import getdefaultlocale from multiprocessing import Queue from random import choice as randchoice from requests.exceptions import HTTPError @@ -46,6 +47,7 @@ class LegendaryCore: self.lgd = LGDLFS() self.local_timezone = datetime.now().astimezone().tzinfo + self.language_code, self.country_code = ('en', 'US') # epic lfs only works on Windows right now if os.name == 'nt': @@ -53,6 +55,22 @@ class LegendaryCore: else: self.egl = None + def get_locale(self): + locale = self.lgd.config.get('Legendary', 'locale', fallback=getdefaultlocale()[0]) + + if locale: + try: + self.language_code, self.country_code = locale.split('-' if '-' in locale else '_') + self.log.debug(f'Set locale to {self.language_code}-{self.country_code}') + + # if egs is loaded make sure to override its language setting as well + if self.egs: + self.egs.language_code, self.egs.country_code = self.language_code, self.country_code + except Exception as e: + self.log.warning(f'Getting locale failed: {e!r}, falling back to using en-US.') + else: + self.log.warning(f'Could not determine locale, falling back to en-US') + def auth(self, username, password): """ Attempts direct non-web login, raises CaptchaError if manual login is required @@ -158,6 +176,8 @@ class LegendaryCore: def get_game_and_dlc_list(self, update_assets=True, platform_override=None, skip_ue=True) -> (List[Game], Dict[str, Game]): + # resolve locale + self.get_locale() _ret = [] _dlc = defaultdict(list) @@ -209,7 +229,8 @@ class LegendaryCore: def get_launch_parameters(self, app_name: str, offline: bool = False, user: str = None, extra_args: list = None, - wine_bin: str = None, wine_pfx: str = None) -> (list, str, dict): + wine_bin: str = None, wine_pfx: str = None, + language: str = None) -> (list, str, dict): install = self.lgd.get_installed_game(app_name) game = self.lgd.get_game_meta(app_name) @@ -260,11 +281,16 @@ class LegendaryCore: f.write(ovt) params.append(f'-epicovt={ovt_path}') + language_code = self.lgd.config.get(app_name, 'language', fallback=language) + if not language_code: # fall back to system or config language + self.get_locale() + language_code = self.language_code + params.extend([ '-EpicPortal', f'-epicusername={user_name}', f'-epicuserid={account_id}', - '-epiclocale=en' + f'-epiclocale={language_code}' ]) if extra_args: