Skip to content

Commit

Permalink
feat(i18n): add ui localization (#2279)
Browse files Browse the repository at this point in the history
Co-authored-by: TheElixZammuto <6505622+TheElixZammuto@users.noreply.github.com>
  • Loading branch information
ReenigneArcher and TheElixZammuto committed Mar 22, 2024
1 parent 8316f44 commit 8777433
Show file tree
Hide file tree
Showing 29 changed files with 4,446 additions and 719 deletions.
12 changes: 10 additions & 2 deletions crowdin.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
"base_path": "."
"base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only)
"preserve_hierarchy": false # flatten tree on crowdin
"preserve_hierarchy": true # false will flatten tree on crowdin, but doesn't work with dest option
"pull_request_labels": [
"crowdin",
"l10n"
Expand All @@ -10,13 +10,21 @@
"files": [
{
"source": "/locale/*.po",
"dest": "/%original_file_name%",
"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"
}
}
},
"update_option": "update_as_unapproved"
},
{
"source": "/src_assets/common/assets/web/public/assets/locale/en.json",
"dest": "/sunshine.json",
"translation": "/src_assets/common/assets/web/public/assets/locale/%two_letters_code%.%file_extension%",
"update_option": "update_as_unapproved"
}
]
34 changes: 34 additions & 0 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,40 @@ editing the `conf` file in a text editor. Use the examples as reference.
`General <https://localhost:47990/config/#general>`__
-----------------------------------------------------

`locale <https://localhost:47990/config/#locale>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
The locale used for Sunshine's user interface.

**Choices**

.. table::
:widths: auto

======= ===========
Value Description
======= ===========
de German
en English
en-GB English (UK)
en-US English (United States)
es Spanish
fr French
it Italian
ru Russian
sv Swedish
zh Chinese (Simplified)
======= ===========

**Default**
``en``

**Example**
.. code-block:: text
locale = en
`sunshine_name <https://localhost:47990/config/#sunshine_name>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
84 changes: 58 additions & 26 deletions docs/source/contributing/localization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,42 +30,74 @@ localization there.

Extraction
----------
There should be minimal cases where strings need to be extracted from source code; however it may be necessary in some
situations. For example if a system tray icon is added it should be localized as it is user interfacing.

- Wrap the string to be extracted in a function as shown.
.. code-block:: cpp
.. tab:: UI

#include <boost/locale.hpp>
#include <string>
Sunshine uses `Vue I18n <https://vue-i18n.intlify.dev/>`__ for localizing the UI.
The following is a simple example of how to use it.

std::string msg = boost::locale::translate("Hello world!");
- Add the string to `src_assets/common/assets/web/public/assets/locale/en.json`, in English.
.. code-block:: json
.. tip:: More examples can be found in the documentation for
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.
{
"index": {
"welcome": "Hello, Sunshine!"
}
}
.. warning:: This is for information only. Contributors should never include manually updated template files, or
manually compiled language files in Pull Requests.
.. note:: The json keys should be sorted alphabetically. You can use `jsonabc <https://novicelab.org/jsonabc/>`__
to sort the keys.

Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
used by CrowdIn to generate language specific template files. The file is generated using the
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
any of the following paths are modified.
- Use the string in a Vue component.
.. code-block:: html

.. code-block:: yaml
<template>
<div>
<p>{{ $t('index.welcome') }}</p>
</div>
</template>

- 'src/**'
.. tip:: More formatting examples can be found in the
`Vue I18n guide <https://kazupon.github.io/vue-i18n/guide/formatting.html>`__.

When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.
.. tab:: C++

**Extract, initialize, and update**
.. code-block:: bash
There should be minimal cases where strings need to be extracted from C++ source code; however it may be necessary in
some situations. For example the system tray icon could be localized as it is user interfacing.

python ./scripts/_locale.py --extract --init --update
- Wrap the string to be extracted in a function as shown.
.. code-block:: cpp
**Compile**
.. code-block:: bash
#include <boost/locale.hpp>
#include <string>
python ./scripts/_locale.py --compile
std::string msg = boost::locale::translate("Hello world!");
.. tip:: More examples can be found in the documentation for
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.

.. warning:: This is for information only. Contributors should never include manually updated template files, or
manually compiled language files in Pull Requests.

Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
used by CrowdIn to generate language specific template files. The file is generated using the
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
any of the following paths are modified.

.. code-block:: yaml
- 'src/**'
When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.

**Extract, initialize, and update**
.. code-block:: bash
python ./scripts/_locale.py --extract --init --update
**Compile**
.. code-block:: bash
python ./scripts/_locale.py --compile
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"bootstrap": "5.3.3",
"vite": "4.5.2",
"vite-plugin-ejs": "1.6.4",
"vue": "3.4.5"
"vue": "3.4.5",
"vue-i18n": "9.10.2"
}
}
14 changes: 14 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ namespace config {
};

sunshine_t sunshine {
"en", // locale
2, // min_log_level
0, // flags
{}, // User file
Expand Down Expand Up @@ -1101,6 +1102,19 @@ namespace config {
config::sunshine.flags[config::flag::UPNP].flip();
}

string_restricted_f(vars, "locale", config::sunshine.locale, {
"de"sv, // German
"en"sv, // English
"en-GB"sv, // English (UK)
"en-US"sv, // English (US)
"es"sv, // Spanish
"fr"sv, // French
"it"sv, // Italian
"ru"sv, // Russian
"sv"sv, // Swedish
"zh"sv, // Chinese
});

std::string log_level_string;
string_f(vars, "min_log_level", log_level_string);

Expand Down
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ namespace config {
bool elevated;
};
struct sunshine_t {
std::string locale;
int min_log_level;
std::bitset<flag::FLAG_SIZE> flags;
std::string credentials_file;
Expand Down
19 changes: 19 additions & 0 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,24 @@ namespace confighttp {
}
}

void
getLocale(resp_https_t response, req_https_t request) {
// we need to return the locale whether authenticated or not

print_req(request);

pt::ptree outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;

pt::write_json(data, outputTree);
response->write(data.str());
});

outputTree.put("status", "true");
outputTree.put("locale", config::sunshine.locale);
}

void
saveConfig(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;
Expand Down Expand Up @@ -743,6 +761,7 @@ namespace confighttp {
server.resource["^/api/apps$"]["POST"] = saveApp;
server.resource["^/api/config$"]["GET"] = getConfig;
server.resource["^/api/config$"]["POST"] = saveConfig;
server.resource["^/api/configLocale$"]["GET"] = getLocale;
server.resource["^/api/restart$"]["POST"] = restart;
server.resource["^/api/password$"]["POST"] = savePassword;
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
Expand Down
12 changes: 6 additions & 6 deletions src_assets/common/assets/web/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> Home</a>
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> PIN</a>
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> Applications</a>
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> Configuration</a>
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> Change Password</a>
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> Troubleshooting</a>
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
</li>
</ul>
</div>
Expand Down
21 changes: 9 additions & 12 deletions src_assets/common/assets/web/ResourceCard.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
<template>
<div class="card p-2">
<div class="card-body">
<h2>Resources</h2>
<h2>{{ $t('resource_card.resources') }}</h2>
<br>
<p>
Resources for Sunshine!
</p>
<p>{{ $t('resource_card.resources_desc') }}</p>
<div class="card-group p-4 align-items-center">
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">LizardByte Website</a>
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">
{{ $t('resource_card.lizardbyte_website') }}</a>
<a class="btn btn-primary m-1" href="https://app.lizardbyte.dev/discord" target="_blank">
<i class="fab fa-fw fa-discord"></i> Discord</a>
<a class="btn btn-secondary m-1" href="https://github.com/LizardByte/Sunshine/discussions" target="_blank">
<i class="fab fa-fw fa-github"></i> Github Discussions</a>
<i class="fab fa-fw fa-github"></i> {{ $t('resource_card.github_discussions') }}</a>
</div>
</div>
</div>
<!-- Legal -->
<div class="card p-2 mt-4">
<div class="card-body">
<h2>Legal</h2>
<h2>{{ $t('resource_card.legal') }}</h2>
<br>
<p>
By continuing to use this software you agree to the terms and conditions in the following documents.
</p>
<p>{{ $t('resource_card.legal_desc') }}</p>
<div class="card-group p-4 align-items-center">
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
target="_blank">
<i class="fas fa-fw fa-file-alt"></i> License</a>
<i class="fas fa-fw fa-file-alt"></i> {{ $t('resource_card.license') }}</a>
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
target="_blank">
<i class="fas fa-fw fa-exclamation"></i> Third Party Notice</a>
<i class="fas fa-fw fa-exclamation"></i> {{ $t('resource_card.third_party_notice') }}</a>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 8777433

Please sign in to comment.