mirror of
https://github.com/grocy/grocy.git
synced 2026-04-06 21:06:15 +02:00
Add user-setting "locale"
Rename CULTURE to DEFAULT_LOCALE
This commit is contained in:
parent
c9dbda957e
commit
d5aadc17a6
|
|
@ -7,4 +7,5 @@ copy /Y localization\en\component_translations.po localization\en_GB\component_t
|
|||
copy /Y localization\en\chore_period_types.po localization\en_GB\chore_period_types.po
|
||||
copy /Y localization\en\chore_assignment_types.po localization\en_GB\chore_assignment_types.po
|
||||
copy /Y localization\en\permissions.po localization\en_GB\permissions.po
|
||||
copy /Y localization\en\locales.po localization\en_GB\locales.po
|
||||
popd
|
||||
|
|
|
|||
|
|
@ -48,3 +48,9 @@ file_filter = localization/<lang>/permissions.po
|
|||
source_file = localization/permissions.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[grocy.locales]
|
||||
file_filter = localization/<lang>/locales.po
|
||||
source_file = localization/locales.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ If you run grocy on Linux, there is also `update.sh` (remember to make the scrip
|
|||
## Localization
|
||||
grocy is fully localizable - the default language is English (integrated into code), a German localization is always maintained by me.
|
||||
You can easily help translating grocy at https://www.transifex.com/grocy/grocy, if your language is incomplete or not available yet.
|
||||
(Language can be changed in `data/config.php`, e. g. `Setting('CULTURE', 'it');`)
|
||||
(Language can be changed in `data/config.php`, e. g. `Setting('DEFAULT_LOCALE', 'it');`)
|
||||
|
||||
The [pre-release demo](https://demo-prerelease.grocy.info) is available for any translation which is at least 80 % complete and will pull the translations from Transifex 10 minutes past every hour, so you can have a kind of instant preview of your contributed translations. Thank you!
|
||||
|
||||
|
|
|
|||
20
app.php
20
app.php
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Grocy\Middleware\CorsMiddleware;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Container\ContainerInterface as Container;
|
||||
|
|
@ -62,18 +63,21 @@ if (!empty(GROCY_BASE_PATH))
|
|||
{
|
||||
$app->setBasePath(GROCY_BASE_PATH);
|
||||
}
|
||||
|
||||
if (GROCY_MODE === 'production')
|
||||
{
|
||||
$app->add(new \Grocy\Middleware\LocaleMiddleware($container));
|
||||
}
|
||||
else {
|
||||
define('GROCY_LOCALE', GROCY_DEFAULT_LOCALE);
|
||||
}
|
||||
$authMiddlewareClass = GROCY_AUTH_CLASS;
|
||||
$app->add(new $authMiddlewareClass($container, $app->getResponseFactory()));
|
||||
// Add default middleware
|
||||
$app->addRoutingMiddleware();
|
||||
$errorMiddleware = $app->addErrorMiddleware(true, false, false);
|
||||
$errorMiddleware->setDefaultErrorHandler(
|
||||
new \Grocy\Controllers\ExceptionController($app, $container)
|
||||
);
|
||||
if (GROCY_MODE === 'production')
|
||||
{
|
||||
$app->add(new \Grocy\Middleware\LocaleMiddleware($container));
|
||||
}
|
||||
else {
|
||||
define(GROCY_LOCALE, GROCY_CULTURE);
|
||||
}
|
||||
|
||||
$app->add(CorsMiddleware::class);
|
||||
$app->run();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Setting('MODE', 'production');
|
|||
|
||||
# Either "en" or "de" or the directory name of
|
||||
# one of the other available localization folders in the "/localization" directory
|
||||
Setting('CULTURE', 'en');
|
||||
Setting('DEFAULT_LOCALE', 'en');
|
||||
|
||||
# This is used to define the first day of a week for calendar views in the frontend,
|
||||
# leave empty to use the locale default
|
||||
|
|
|
|||
|
|
@ -44,4 +44,16 @@ class UsersController extends BaseController
|
|||
->where('parent IS NULL')->where('user_id', $args['userId']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function LocaleForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
return $this->renderPage($response, 'locale', [
|
||||
'languages' => array_filter(scandir(__DIR__.'/../localization'), function ($item){
|
||||
if($item == "." || $item == "..")
|
||||
return false;
|
||||
return is_dir(__DIR__.'/../localization/'.$item);
|
||||
})
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
79
localization/en/locales.po
Normal file
79
localization/en/locales.po
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: http://www.transifex.com/grocy/grocy/language/en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"Language: en\n"
|
||||
"X-Domain: grocy/locales\n"
|
||||
|
||||
msgid "cs"
|
||||
msgstr "Czech"
|
||||
|
||||
msgid "da"
|
||||
msgstr "Danish"
|
||||
|
||||
msgid "de"
|
||||
msgstr "German"
|
||||
|
||||
msgid "el_GR"
|
||||
msgstr "Greek"
|
||||
|
||||
msgid "en"
|
||||
msgstr "English"
|
||||
|
||||
msgid "en_GB"
|
||||
msgstr "English (Great Britain)"
|
||||
|
||||
msgid "es"
|
||||
msgstr "Spanish"
|
||||
|
||||
msgid "fr"
|
||||
msgstr "French"
|
||||
|
||||
msgid "hu"
|
||||
msgstr "Hungarian"
|
||||
|
||||
msgid "it"
|
||||
msgstr "Italian"
|
||||
|
||||
msgid "ja"
|
||||
msgstr "Japanese"
|
||||
|
||||
msgid "ko_KR"
|
||||
msgstr "Korean"
|
||||
|
||||
msgid "nl"
|
||||
msgstr "Dutch"
|
||||
|
||||
msgid "no"
|
||||
msgstr "Norwegian"
|
||||
|
||||
msgid "pl"
|
||||
msgstr "Polish"
|
||||
|
||||
msgid "pt_BR"
|
||||
msgstr "Portuguese (Brazil)"
|
||||
|
||||
msgid "pt_PT"
|
||||
msgstr "Portuguese (Portugal)"
|
||||
|
||||
msgid "ru"
|
||||
msgstr "Russian"
|
||||
|
||||
msgid "sk_SK"
|
||||
msgstr "Slovak"
|
||||
|
||||
msgid "sv_SE"
|
||||
msgstr "Swedish"
|
||||
|
||||
msgid "tr"
|
||||
msgstr "Turkish"
|
||||
|
||||
msgid "zh_TW"
|
||||
msgstr "Chinese (Taiwan)"
|
||||
79
localization/locales.pot
Normal file
79
localization/locales.pot
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: http://www.transifex.com/grocy/grocy/language/en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"Language: en\n"
|
||||
"X-Domain: grocy/locales\n"
|
||||
|
||||
msgid "cs"
|
||||
msgstr ""
|
||||
|
||||
msgid "da"
|
||||
msgstr ""
|
||||
|
||||
msgid "de"
|
||||
msgstr ""
|
||||
|
||||
msgid "el_GR"
|
||||
msgstr ""
|
||||
|
||||
msgid "en"
|
||||
msgstr ""
|
||||
|
||||
msgid "en_GB"
|
||||
msgstr ""
|
||||
|
||||
msgid "es"
|
||||
msgstr ""
|
||||
|
||||
msgid "fr"
|
||||
msgstr ""
|
||||
|
||||
msgid "hu"
|
||||
msgstr ""
|
||||
|
||||
msgid "it"
|
||||
msgstr ""
|
||||
|
||||
msgid "ja"
|
||||
msgstr ""
|
||||
|
||||
msgid "ko_KR"
|
||||
msgstr ""
|
||||
|
||||
msgid "nl"
|
||||
msgstr ""
|
||||
|
||||
msgid "no"
|
||||
msgstr ""
|
||||
|
||||
msgid "pl"
|
||||
msgstr ""
|
||||
|
||||
msgid "pt_BR"
|
||||
msgstr ""
|
||||
|
||||
msgid "pt_PT"
|
||||
msgstr ""
|
||||
|
||||
msgid "ru"
|
||||
msgstr ""
|
||||
|
||||
msgid "sk_SK"
|
||||
msgstr ""
|
||||
|
||||
msgid "sv_SE"
|
||||
msgstr ""
|
||||
|
||||
msgid "tr"
|
||||
msgstr ""
|
||||
|
||||
msgid "zh_TW"
|
||||
msgstr ""
|
||||
|
|
@ -1891,3 +1891,6 @@ msgstr ""
|
|||
|
||||
msgid "If you think this is a bug, please report it"
|
||||
msgstr ""
|
||||
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ class CorsMiddleware extends BaseMiddleware
|
|||
{
|
||||
public function __invoke(Request $request, RequestHandler $handler): Response
|
||||
{
|
||||
$response = $handler->handle($request);
|
||||
|
||||
//$routeContext = RouteContext::fromRequest($request);
|
||||
//$routingResults = $routeContext->getRoutingResults();
|
||||
//$methods = $routingResults->getAllowedMethods();
|
||||
|
|
@ -23,7 +21,6 @@ class CorsMiddleware extends BaseMiddleware
|
|||
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
|
||||
$response = $response->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
$response = $response->withHeader('Access-Control-Allow-Headers', '*');
|
||||
$response = $response->withStatus(204);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@
|
|||
namespace Grocy\Middleware;
|
||||
|
||||
|
||||
use Locale;
|
||||
use Grocy\Services\UsersService;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||
|
||||
class LocaleMiddleware extends BaseMiddleware
|
||||
{
|
||||
const LOCALE_COOKIE_NAME = 'LOCALE';
|
||||
|
||||
public function __invoke(Request $request, RequestHandler $handler): Response
|
||||
{
|
||||
|
|
@ -22,13 +21,16 @@ class LocaleMiddleware extends BaseMiddleware
|
|||
|
||||
protected function getLocale(Request $request)
|
||||
{
|
||||
$cookies = $request->getCookieParams();
|
||||
if (isset($cookies[self::LOCALE_COOKIE_NAME])) {
|
||||
$locale = $cookies[self::LOCALE_COOKIE_NAME];
|
||||
if (in_array($locale, scandir(__DIR__ . '/../localization'))) {
|
||||
return $locale;
|
||||
if(GROCY_AUTHENTICATED)
|
||||
{
|
||||
$locale = UsersService::getInstance()->GetUserSetting(GROCY_USER_ID, 'locale');
|
||||
if (isset($locale)) {
|
||||
if (in_array($locale, scandir(__DIR__ . '/../localization'))) {
|
||||
return $locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$langs = join(',', $request->getHeader('Accept-Language'));
|
||||
|
||||
// src: https://gist.github.com/spolischook/0cde9c6286415cddc088
|
||||
|
|
@ -60,6 +62,6 @@ class LocaleMiddleware extends BaseMiddleware
|
|||
return substr($locale, 0, 2);
|
||||
}
|
||||
}
|
||||
return GROCY_CULTURE;
|
||||
return GROCY_DEFAULT_LOCALE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -670,10 +670,14 @@ $(Grocy.UserPermissions).each(function (index, item)
|
|||
$('.permission-'+item.permission_name).addClass('disabled').addClass('not-allowed');
|
||||
}
|
||||
});
|
||||
Grocy.SetLanguage = function (lang) {
|
||||
var expires = new Date();
|
||||
// Expires "never" (= 30 years)
|
||||
expires.setDate(expires.getDate() + 365*30);
|
||||
document.cookie = "LOCALE=" + lang + "; SameSite=Lax; expires="+expires.toUTCString();
|
||||
location.reload();
|
||||
}
|
||||
$('a.link-return').each(function () {
|
||||
var base = $(this).data('href');
|
||||
if(base.contains('?'))
|
||||
{
|
||||
$(this).attr('href', base + '&returnto' + encodeURIComponent(location.pathname));
|
||||
}
|
||||
else{
|
||||
$(this).attr('href', base + '?returnto=' + encodeURIComponent(location.pathname));
|
||||
}
|
||||
|
||||
})
|
||||
|
|
|
|||
17
public/viewjs/locale.js
Normal file
17
public/viewjs/locale.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
$('#locale-save').on('click', function () {
|
||||
var value = $("input:radio[name ='language']:checked").val();
|
||||
var jsonData = {'value': value};
|
||||
Grocy.Api.Put('user/settings/locale', jsonData,
|
||||
function(result)
|
||||
{
|
||||
location.pathname = GetUriParam('returnto');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
if (!xhr.statusText.isEmpty())
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
12
routes.php
12
routes.php
|
|
@ -6,9 +6,7 @@ use Psr\Http\Message\ResponseInterface as Response;
|
|||
use Slim\Routing\RouteCollectorProxy;
|
||||
|
||||
use Grocy\Middleware\JsonMiddleware;
|
||||
use Grocy\Middleware\CorsMiddleware;
|
||||
|
||||
$authMiddlewareClass = GROCY_AUTH_CLASS;
|
||||
|
||||
$app->group('', function(RouteCollectorProxy $group)
|
||||
{
|
||||
|
|
@ -34,6 +32,7 @@ $app->group('', function(RouteCollectorProxy $group)
|
|||
$group->get('/users', '\Grocy\Controllers\UsersController:UsersList');
|
||||
$group->get('/user/{userId}', '\Grocy\Controllers\UsersController:UserEditForm');
|
||||
$group->get('/user/{userId}/permissions', '\Grocy\Controllers\UsersController:PermissionList');
|
||||
$group->get('/usersettings/locale', '\Grocy\Controllers\UsersController:LocaleForm');
|
||||
|
||||
// Stock routes
|
||||
if (GROCY_FEATURE_FLAG_STOCK)
|
||||
|
|
@ -136,7 +135,7 @@ $app->group('', function(RouteCollectorProxy $group)
|
|||
$group->get('/api', '\Grocy\Controllers\OpenApiController:DocumentationUi');
|
||||
$group->get('/manageapikeys', '\Grocy\Controllers\OpenApiController:ApiKeysList');
|
||||
$group->get('/manageapikeys/new', '\Grocy\Controllers\OpenApiController:CreateNewApiKey');
|
||||
})->add(new $authMiddlewareClass($container, $app->getResponseFactory()));
|
||||
});
|
||||
|
||||
$app->group('/api', function(RouteCollectorProxy $group)
|
||||
{
|
||||
|
|
@ -259,11 +258,10 @@ $app->group('/api', function(RouteCollectorProxy $group)
|
|||
$group->get('/calendar/ical', '\Grocy\Controllers\CalendarApiController:Ical')->setName('calendar-ical');
|
||||
$group->get('/calendar/ical/sharing-link', '\Grocy\Controllers\CalendarApiController:IcalSharingLink');
|
||||
}
|
||||
})->add(JsonMiddleware::class)
|
||||
->add(new $authMiddlewareClass($container, $app->getResponseFactory()));
|
||||
})->add(JsonMiddleware::class);
|
||||
|
||||
// Handle CORS preflight OPTIONS requests
|
||||
$app->options('/api/{routes:.+}', function(Request $request, Response $response): Response
|
||||
{
|
||||
return $response;
|
||||
})->add(CorsMiddleware::class);
|
||||
return $response->withStatus(204);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class DatabaseService
|
|||
{
|
||||
if (GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease')
|
||||
{
|
||||
return GROCY_DATAPATH . '/grocy_' . GROCY_CULTURE . '.db';
|
||||
return GROCY_DATAPATH . '/grocy_' . GROCY_DEFAULT_LOCALE . '.db';
|
||||
}
|
||||
|
||||
return GROCY_DATAPATH . '/grocy.db';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class DemoDataGeneratorService extends BaseService
|
|||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->LocalizationService = new LocalizationService(GROCY_CULTURE);
|
||||
$this->LocalizationService = new LocalizationService(GROCY_DEFAULT_LOCALE);
|
||||
}
|
||||
|
||||
protected $LocalizationService;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ class LocalizationService
|
|||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/strings.pot'));
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/userfield_types.pot'));
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/permissions.pot'));
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/locales.pot'));
|
||||
|
||||
|
||||
if (GROCY_MODE !== 'production')
|
||||
{
|
||||
|
|
@ -96,6 +98,10 @@ class LocalizationService
|
|||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/permissions.po"));
|
||||
}
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/locales.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/locales.po"));
|
||||
}
|
||||
if (GROCY_MODE !== 'production' && file_exists(__DIR__ . "/../localization/$culture/demo_data.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/demo_data.po"));
|
||||
|
|
|
|||
|
|
@ -416,6 +416,9 @@
|
|||
@if(GROCY_FEATURE_FLAG_TASKS)
|
||||
<a class="dropdown-item discrete-link permission-TASKS" href="{{ $U('/taskssettings') }}"><i class="fas fa-tasks"></i> {{ $__t('Tasks settings') }}</a>
|
||||
@endif
|
||||
<a data-href="{{ $U('/usersettings/locale') }}" class="dropdown-item discrete-link link-return">
|
||||
<i class="fas fa-globe"></i> {{ $__t('Language') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
@if(GROCY_SHOW_AUTH_VIEWS)
|
||||
<a class="dropdown-item discrete-link permission-USERS_READ" href="{{ $U('/users') }}"><i class="fas fa-users"></i> {{ $__t('Manage users') }}</a>
|
||||
|
|
|
|||
30
views/locale.blade.php
Normal file
30
views/locale.blade.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title', $__t('Language'))
|
||||
@section('activeNav', '')
|
||||
@section('viewJsName', 'locale')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row mt-3">
|
||||
<div class="col">
|
||||
<ul>
|
||||
@foreach($languages as $lang)
|
||||
<li>
|
||||
<label>
|
||||
<input name="language" type="radio" class="change-language" value="{{ $lang }}" @if(GROCY_LOCALE == $lang) checked @endif>
|
||||
<i class="flag-icon flag-icon-{{$lang}}"></i> <span
|
||||
class="txt-lang-name">{{ $__t($lang) }}</span>
|
||||
</label>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<button id="locale-save" class="btn btn-success" type="submit">{{ $__t('Save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Loading…
Reference in New Issue
Block a user