Merge pull request #94 from SunshineStream/i10n
Initial support for localization
This commit is contained in:
@@ -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
|
||||||
@@ -18,3 +18,7 @@ cmake-build*
|
|||||||
/assets/web/fonts/fontawesome-free-web/scss/
|
/assets/web/fonts/fontawesome-free-web/scss/
|
||||||
/assets/web/fonts/fontawesome-free-web/sprites/
|
/assets/web/fonts/fontawesome-free-web/sprites/
|
||||||
/assets/web/fonts/fontawesome-free-web/svgs/
|
/assets/web/fonts/fontawesome-free-web/svgs/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|||||||
+17
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Babel==2.9.1
|
||||||
Reference in New Issue
Block a user