From 045970bcc58ed88e6ff03451f3da9b5d40d49d18 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 00:20:25 -0500 Subject: [PATCH 01/10] Create requirements.txt --- locale/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 locale/requirements.txt diff --git a/locale/requirements.txt b/locale/requirements.txt new file mode 100644 index 00000000..9d236e72 --- /dev/null +++ b/locale/requirements.txt @@ -0,0 +1 @@ +Babel==2.9.1 From e28cc5e64597f81991b185dbebb5a95641b3421a Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 00:21:12 -0500 Subject: [PATCH 02/10] Create _locale.py --- locale/_locale.py | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 locale/_locale.py diff --git a/locale/_locale.py b/locale/_locale.py new file mode 100644 index 00000000..4a2ec2c1 --- /dev/null +++ b/locale/_locale.py @@ -0,0 +1,156 @@ +"""_locale.py + +Functions related to building, initializing, updating, and compiling localization translations. + +Borrowed from RetroArcher. +""" +# standard imports +import argparse +import datetime +import os +import subprocess + +project_name = 'Sunshine' + +script_dir = os.path.dirname(os.path.abspath(__file__)) +root_dir = os.path.dirname(script_dir) +locale_dir = os.path.join(root_dir, 'locale') +project_dir = os.path.join(root_dir, project_name.lower()) + +year = datetime.datetime.now().year + +# retroarcher target locales +target_locales = [ + 'de', # Deutsch + 'en', # English + 'en_GB', # English (United Kingdom) + 'en_US', # English (United States) + 'es', # español + 'fr', # français + 'it', # italiano + 'ru', # русский +] + + +def x_extract(): + """Executes `xgettext extraction` in subprocess.""" + + commands = [ + 'xgettext', + f'--default-domain={project_name.lower()}', + f'--output={os.path.join(locale_dir, project_name.lower() + ".pot")}', + '--language=C++', + '--boost', + '--from-code=utf-8', + '-F', + f'--msgid-bugs-address=github.com/{project_name.lower()}', + f'--copyright-holder={project_name}', + f'--package-name={project_name}', + '--package-version=v0' + ] + + pot_filepath = os.path.join(locale_dir, f'{project_name.lower()}.pot') + + extensions = ['cpp', 'h', 'm', 'mm'] + + # find input files + for root, dirs, files in os.walk(project_dir, topdown=True): + for name in files: + filename = os.path.join(root, name) + extension = filename.rsplit('.', 1)[-1] + if extension in extensions: # append input files + commands.append(filename) + + print(commands) + proc = subprocess.run(args=commands, cwd=root_dir) + + # fix header + body = "" + with open(file=pot_filepath, mode='r') as file: + for line in file.readlines(): + if line != '"Language: \\n"\n': # do not include this line + if line == '# SOME DESCRIPTIVE TITLE.\n': + body += f'# Translations template for {project_name}.\n' + elif line.startswith('#') and 'YEAR' in line: + body += line.replace('YEAR', str(year)) + elif line.startswith('#') and 'PACKAGE' in line: + body += line.replace('PACKAGE', project_name) + else: + body += line + + # rewrite pot file with updated header + with open(file=pot_filepath, mode='w+') as file: + file.write(body) + + +def babel_init(locale_code: str): + """Executes `pybabel init` in subprocess. + + :param locale_code: str - locale code + """ + commands = [ + 'pybabel', + 'init', + '-i', os.path.join(locale_dir, f'{project_name.lower()}.pot'), + '-d', locale_dir, + '-D', project_name.lower(), + '-l', locale_code + ] + + print(commands) + proc = subprocess.run(args=commands, cwd=root_dir) + + +def babel_update(): + """Executes `pybabel update` in subprocess.""" + commands = [ + 'pybabel', + 'update', + '-i', os.path.join(locale_dir, f'{project_name.lower()}.pot'), + '-d', locale_dir, + '-D', project_name.lower(), + '--update-header-comment' + ] + + print(commands) + proc = subprocess.run(args=commands, cwd=root_dir) + + +def babel_compile(): + """Executes `pybabel compile` in subprocess.""" + commands = [ + 'pybabel', + 'compile', + '-d', locale_dir, + '-D', project_name.lower() + ] + + print(commands) + proc = subprocess.run(args=commands, cwd=root_dir) + + +if __name__ == '__main__': + # Set up and gather command line arguments + parser = argparse.ArgumentParser( + description='Script helps update locale_id translations. Translations must be done manually.') + + parser.add_argument('--extract', action='store_true', help='Extract messages from c++ files.') + parser.add_argument('--init', action='store_true', help='Initialize any new locales specified in target locales.') + parser.add_argument('--update', action='store_true', help='Update existing locales.') + parser.add_argument('--compile', action='store_true', help='Compile translated locales.') + + args = parser.parse_args() + + if args.extract: + x_extract() + + if args.init: + for locale_id in target_locales: + if not os.path.isdir(os.path.join(locale_dir, locale_id)): + babel_init(locale_code=locale_id) + + if args.update: + babel_update() + + if args.compile: + babel_compile() From 3bd9f6b7103fb0c5faffd5a82ad5b38794445167 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 00:22:29 -0500 Subject: [PATCH 03/10] Ignore translation templates and compilations --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 6fb681e7..39afd65a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ cmake-build* /assets/web/fonts/fontawesome-free-web/scss/ /assets/web/fonts/fontawesome-free-web/sprites/ /assets/web/fonts/fontawesome-free-web/svgs/ + +# Translations +*.mo +*.pot From b3cdadca8681f6934446ef1c3a01b6d41737e915 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 00:26:53 -0500 Subject: [PATCH 04/10] Create localize.yml --- .github/workflows/localize.yml | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/localize.yml diff --git a/.github/workflows/localize.yml b/.github/workflows/localize.yml new file mode 100644 index 00000000..e9407c08 --- /dev/null +++ b/.github/workflows/localize.yml @@ -0,0 +1,42 @@ +name: localize + +on: + push: + branches: [nightly] + workflow_dispatch: + +jobs: + localize: + name: Update Localization + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Python 3.9 + uses: actions/setup-python@v3 # https://github.com/actions/setup-python + with: + python-version: '3.9' + + - name: Set up Python 3.9 Dependencies + run: | + cd ./locale + python -m pip install --upgrade pip setuptools + python -m pip install -r requirements.txt + + - name: Set up xgettext + run: | + sudo apt-get update -y && \ + sudo apt-get --reinstall install -y \ + gettext + + - name: Update Strings + run: | + python ./locale/_locale.py --extract --init --update + + - name: GitHub Commit & Push # push changes back into nightly + uses: actions-js/push@v1.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: nightly + message: localization updated by localize workflow From 88cf616a483462db86bf111c94e7a05573b652a2 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 13:46:18 -0500 Subject: [PATCH 05/10] Move _locale.py and requirements --- {locale => scripts}/_locale.py | 0 {locale => scripts}/requirements.txt | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {locale => scripts}/_locale.py (100%) rename {locale => scripts}/requirements.txt (100%) diff --git a/locale/_locale.py b/scripts/_locale.py similarity index 100% rename from locale/_locale.py rename to scripts/_locale.py diff --git a/locale/requirements.txt b/scripts/requirements.txt similarity index 100% rename from locale/requirements.txt rename to scripts/requirements.txt From 01155ef4a3f31beab812fae1b0847c70e34f6172 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 13:48:24 -0500 Subject: [PATCH 06/10] Update trigger events - Don't run if commits are all in 'locale' directory - Allows pushing changes back into nightly from this workflow without triggering and endless loop - Don't run job unless event is 'pull_request.merged' --- .github/workflows/localize.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/localize.yml b/.github/workflows/localize.yml index e9407c08..c587dcc9 100644 --- a/.github/workflows/localize.yml +++ b/.github/workflows/localize.yml @@ -3,11 +3,14 @@ name: localize on: push: branches: [nightly] + paths-ignore: + - 'locale/**' workflow_dispatch: jobs: localize: name: Update Localization + if: ${{ github.event.pull_request.merged }} runs-on: ubuntu-latest steps: - name: Checkout @@ -20,7 +23,7 @@ jobs: - name: Set up Python 3.9 Dependencies run: | - cd ./locale + cd ./scripts python -m pip install --upgrade pip setuptools python -m pip install -r requirements.txt @@ -32,7 +35,7 @@ jobs: - name: Update Strings run: | - python ./locale/_locale.py --extract --init --update + python ./scripts/_locale.py --extract --init --update - name: GitHub Commit & Push # push changes back into nightly uses: actions-js/push@v1.2 From f1d82a7d09d9f56b05b9a9a062095128972d8c2d Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 14:00:23 -0500 Subject: [PATCH 07/10] Update trigger conditions - Only run when changes are made on files inside 'sunshine' directory - Prevents workflow from running again when this workflow pushes changes back into 'locale' directory - Should be cleaner than using 'paths-ignore' --- .github/workflows/localize.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/localize.yml b/.github/workflows/localize.yml index c587dcc9..b6619833 100644 --- a/.github/workflows/localize.yml +++ b/.github/workflows/localize.yml @@ -3,8 +3,8 @@ name: localize on: push: branches: [nightly] - paths-ignore: - - 'locale/**' + paths: # prevents workflow from running again when this workflow pushes changes back into 'locale' directory + - 'sunshine/**' # only localizing files inside sunshine directory workflow_dispatch: jobs: From 84584c950b37ccc1498055c17bd317d3f6b2a9e8 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 11 Mar 2022 14:07:10 -0500 Subject: [PATCH 08/10] Update comment --- .github/workflows/localize.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/localize.yml b/.github/workflows/localize.yml index b6619833..0ca9e621 100644 --- a/.github/workflows/localize.yml +++ b/.github/workflows/localize.yml @@ -3,7 +3,7 @@ name: localize on: push: branches: [nightly] - paths: # prevents workflow from running again when this workflow pushes changes back into 'locale' directory + paths: # prevents workflow from running unless files in these directories change - 'sunshine/**' # only localizing files inside sunshine directory workflow_dispatch: From a014391ae7e415256d47bec9925e93be9caf9922 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 13 Mar 2022 16:29:51 -0400 Subject: [PATCH 09/10] Update for Crowdin Integration - Rename extracted template file to `sunshine.po` - Add `crowdin.yml` - Remove `--init` and `--update` from `localize.yml` - Crowdin will initialize new languages and update existing ones --- .github/workflows/localize.yml | 2 +- crowdin.yml | 17 +++++++++++++++++ scripts/_locale.py | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 crowdin.yml diff --git a/.github/workflows/localize.yml b/.github/workflows/localize.yml index 0ca9e621..eafc7430 100644 --- a/.github/workflows/localize.yml +++ b/.github/workflows/localize.yml @@ -35,7 +35,7 @@ jobs: - name: Update Strings run: | - python ./scripts/_locale.py --extract --init --update + python ./scripts/_locale.py --extract - name: GitHub Commit & Push # push changes back into nightly uses: actions-js/push@v1.2 diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..d014a00c --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,17 @@ +"base_path": "." +"base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only) +"preserve_hierarchy": false # flatten tree on crowdin + +"files" : [ + { + "source" : "/locale/*.po", + "translation" : "/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%", + "languages_mapping": { + "two_letters_code": { + # map non-two letter codes here, left side is crowdin designation, right side is babel designation + "en-GB": "en_GB", + "en-US": "en_US" + } + } + } +] diff --git a/scripts/_locale.py b/scripts/_locale.py index 4a2ec2c1..b26a0592 100644 --- a/scripts/_locale.py +++ b/scripts/_locale.py @@ -38,7 +38,7 @@ def x_extract(): commands = [ 'xgettext', f'--default-domain={project_name.lower()}', - f'--output={os.path.join(locale_dir, project_name.lower() + ".pot")}', + f'--output={os.path.join(locale_dir, project_name.lower() + ".po")}', '--language=C++', '--boost', '--from-code=utf-8', From 907d0bfcd50855c2078e72846128881183e178c8 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 13 Mar 2022 16:39:33 -0400 Subject: [PATCH 10/10] Fix `.po` file extension --- scripts/_locale.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/_locale.py b/scripts/_locale.py index b26a0592..82e172cc 100644 --- a/scripts/_locale.py +++ b/scripts/_locale.py @@ -49,7 +49,7 @@ def x_extract(): '--package-version=v0' ] - pot_filepath = os.path.join(locale_dir, f'{project_name.lower()}.pot') + pot_filepath = os.path.join(locale_dir, f'{project_name.lower()}.po') extensions = ['cpp', 'h', 'm', 'mm'] @@ -91,7 +91,7 @@ def babel_init(locale_code: str): commands = [ 'pybabel', 'init', - '-i', os.path.join(locale_dir, f'{project_name.lower()}.pot'), + '-i', os.path.join(locale_dir, f'{project_name.lower()}.po'), '-d', locale_dir, '-D', project_name.lower(), '-l', locale_code @@ -106,7 +106,7 @@ def babel_update(): commands = [ 'pybabel', 'update', - '-i', os.path.join(locale_dir, f'{project_name.lower()}.pot'), + '-i', os.path.join(locale_dir, f'{project_name.lower()}.po'), '-d', locale_dir, '-D', project_name.lower(), '--update-header-comment'