Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/localize.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: localize

on:
push:
branches: [nightly]
paths: # prevents workflow from running unless files in these directories change
- 'sunshine/**' # only localizing files inside sunshine directory
workflow_dispatch:

jobs:
localize:
name: Update Localization
if: ${{ github.event.pull_request.merged }}
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 ./scripts
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 ./scripts/_locale.py --extract

- 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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 17 additions & 0 deletions crowdin.yml
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
]
156 changes: 156 additions & 0 deletions scripts/_locale.py
Original file line number Diff line number Diff line change
@@ -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() + ".po")}',
'--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()}.po')

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()}.po'),
'-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()}.po'),
'-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()
1 change: 1 addition & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Babel==2.9.1