mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 12:26:15 +02:00
i18n: static pot to json compilation
Translations aren't embedded on every page load anymore; this saves roughly 55kB on every single request, because these can now be cached.
This commit is contained in:
parent
64b8195f62
commit
7f598058bf
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
|||
/public/node_modules
|
||||
node_modules
|
||||
.yarn
|
||||
.vagrant
|
||||
/vendor
|
||||
/.release
|
||||
embedded.txt
|
||||
|
|
|
|||
8
Vagrantfile
vendored
Normal file
8
Vagrantfile
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "debian/buster64"
|
||||
|
||||
config.vm.provision "shell", path: "buildfiles/provision-vagrant.sh"
|
||||
config.vm.network "forwarded_port", guest: 80, host: 8000
|
||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
config.vm.synced_folder ".", "/grocy", automount: true, owner: "www-data", group: "www-data"
|
||||
end
|
||||
30
buildfiles/generate-locales.php
Normal file
30
buildfiles/generate-locales.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
use Grocy\Services\LocalizationService;
|
||||
|
||||
/* This file statically generates json to handle
|
||||
* frontend translations.
|
||||
*/
|
||||
define("GROCY_DATAPATH", __DIR__ . '/../data');
|
||||
|
||||
// Load composer dependencies
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
// Load config files
|
||||
require_once GROCY_DATAPATH . '/config.php';
|
||||
require_once __DIR__ . '/../config-dist.php'; // For not in own config defined values we use the default ones
|
||||
|
||||
echo "Searching for localizations in " . __DIR__ . '/../localization/* \n';
|
||||
|
||||
$translations = array_filter(glob(__DIR__ . '/../localization/*'), 'is_dir');
|
||||
|
||||
// ensure the target directory is there
|
||||
if(!is_dir(__DIR__ . '/../public/js/locales/grocy/')) {
|
||||
mkdir(__DIR__ . '/../public/js/locales/grocy/', 0777, true);
|
||||
}
|
||||
|
||||
foreach($translations as $lang) {
|
||||
$culture = basename($lang);
|
||||
echo "Generating " . $culture . "...\n";
|
||||
$ls = LocalizationService::getInstance($culture, true);
|
||||
$ls->LoadLocalizations(false);
|
||||
file_put_contents(__DIR__ .'/../public/js/locales/grocy/'.$culture.'.json', $ls->GetPoAsJsonString());
|
||||
}
|
||||
22
buildfiles/grocy.nginx
Normal file
22
buildfiles/grocy.nginx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
root /grocy/public;
|
||||
index index.php;
|
||||
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
14
buildfiles/provision-vagrant.sh
Normal file
14
buildfiles/provision-vagrant.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apt update
|
||||
|
||||
# install php
|
||||
apt -y install lsb-release apt-transport-https ca-certificates
|
||||
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
|
||||
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list
|
||||
apt update
|
||||
|
||||
apt -y install php7.4-fpm php7.4-cli php7.4-{bcmath,bz2,intl,gd,mbstring,zip,sqlite} nginx
|
||||
|
||||
cp /grocy/buildfiles/grocy.nginx /etc/nginx/sites-enabled/default
|
||||
|
||||
systemctl enable nginx.service
|
||||
systemctl restart nginx.service
|
||||
|
|
@ -130,7 +130,14 @@ class BaseController
|
|||
$this->View->set('__n', function ($number, $singularForm, $pluralForm) use ($localizationService) {
|
||||
return $localizationService->__n($number, $singularForm, $pluralForm);
|
||||
});
|
||||
$this->View->set('GettextPo', $localizationService->GetPoAsJsonString());
|
||||
$this->View->set('GrocyLocale', $localizationService->Culture);
|
||||
|
||||
// only inclue strings loaded from the database on every request,
|
||||
// so that the rest can be cached and generated on build-time.
|
||||
// TODO: figure out why this is needed in the first place.
|
||||
$dynamicLocalizationService = new LocalizationService($localizationService->Culture, true);
|
||||
$dynamicLocalizationService->LoadDynamicLocalizations();
|
||||
$this->View->set('GettextPo', $dynamicLocalizationService->GetPoAsJsonString());
|
||||
|
||||
// TODO: Better handle this generically based on the current language (header in .po file?)
|
||||
$dir = 'ltr';
|
||||
|
|
|
|||
|
|
@ -152,7 +152,15 @@ function clean(cb)
|
|||
function build(cb)
|
||||
{
|
||||
// body omitted
|
||||
return parallel(js, css, vendor, viewjs, resourceFileCopy, copyLocales, done => { done(); cb(); })();
|
||||
return parallel(
|
||||
js,
|
||||
css,
|
||||
vendor,
|
||||
viewjs,
|
||||
resourceFileCopy,
|
||||
copyLocales,
|
||||
makeLocales,
|
||||
done => { done(); cb(); })();
|
||||
}
|
||||
|
||||
function publish(cb)
|
||||
|
|
@ -229,6 +237,11 @@ function resourceFileCopy(cb)
|
|||
)();
|
||||
}
|
||||
|
||||
async function makeLocales()
|
||||
{
|
||||
return subprocess.exec("php buildfiles/generate-locales.php");
|
||||
}
|
||||
|
||||
function copyLocales(cb)
|
||||
{
|
||||
return parallel(
|
||||
|
|
@ -290,4 +303,5 @@ function bundle(cb)
|
|||
.pipe(dest('.release'))
|
||||
}
|
||||
|
||||
export { build, js, vendor, viewjs, css, live, clean, resourceFileCopy, copyLocales, publish, release, bundle }
|
||||
|
||||
export { build, js, vendor, viewjs, css, live, clean, resourceFileCopy, copyLocales, publish, release, bundle, makeLocales }
|
||||
15
js/grocy.js
15
js/grocy.js
|
|
@ -44,12 +44,25 @@ class GrocyClass
|
|||
this.EditObjectProduct = config.EditObjectProduct;
|
||||
this.RecipePictureFileName = config.RecipePictureFileName;
|
||||
this.InstructionManualFileNameName = config.InstructionManualFileNameName;
|
||||
this.Locale = config.Locale;
|
||||
|
||||
this.Components = {};
|
||||
|
||||
// Init some classes
|
||||
this.Api = new GrocyApi(this);
|
||||
this.Translator = new Translator(config.GettextPo);
|
||||
|
||||
// Merge dynamic and static locales
|
||||
var strings = this.Api.LoadLanguageSync(this.Locale);
|
||||
if (strings == null)
|
||||
{
|
||||
console.error("Could not load locale " + this.Locale + ", fallback to en.");
|
||||
strings = this.Api.LoadLanguageSync("en");
|
||||
}
|
||||
Object.assign(strings.messages[""], config.GettextPo.messages[""]);
|
||||
this.Translator = new Translator(strings);
|
||||
|
||||
|
||||
|
||||
this.FrontendHelpers = new GrocyFrontendHelpers(this, this.Api);
|
||||
this.WakeLock = new WakeLock(this);
|
||||
this.UISound = new UISound(this);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,28 @@ class GrocyApi
|
|||
this.Grocy = Grocy;
|
||||
}
|
||||
|
||||
// This throws a deprecation warning in the console.
|
||||
// The "clean" solution would be to move all translations
|
||||
// To be promise-based async stuff, but Grocy not in a shape
|
||||
// to make this easily possible right now.
|
||||
// The introduction of a frontend framework like react or vue
|
||||
// will make this obsolete as well.
|
||||
LoadLanguageSync(locale)
|
||||
{
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open("GET", this.Grocy.FormatUrl('/js/locales/grocy/' + locale + '.json'), false);
|
||||
xmlhttp.send();
|
||||
// if Status OK or NOT MODIFIED
|
||||
if ((xmlhttp.status == 200 || xmlhttp.status == 304) && xmlhttp.readyState == 4)
|
||||
{
|
||||
return JSON.parse(xmlhttp.responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Get(apiFunction, success, error)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ use Gettext\Translator;
|
|||
|
||||
class LocalizationService
|
||||
{
|
||||
const DOMAIN = 'grocy/userstrings';
|
||||
|
||||
protected $Po;
|
||||
|
||||
protected $PoUserStrings;
|
||||
|
|
@ -62,11 +64,15 @@ class LocalizationService
|
|||
return $this->Po->toJsonString();
|
||||
}
|
||||
|
||||
public function __construct(string $culture)
|
||||
public function __construct(string $culture, $deferLoading = false)
|
||||
{
|
||||
$this->Culture = $culture;
|
||||
$this->PoUserStrings = new Translations();
|
||||
$this->PoUserStrings->setDomain(self::DOMAIN);
|
||||
|
||||
$this->LoadLocalizations($culture);
|
||||
if(!$deferLoading) {
|
||||
$this->LoadLocalizations($culture);
|
||||
}
|
||||
}
|
||||
|
||||
public function __n($number, $singularForm, $pluralForm)
|
||||
|
|
@ -110,7 +116,7 @@ class LocalizationService
|
|||
return $this->getDatabaseService()->GetDbConnection();
|
||||
}
|
||||
|
||||
private function LoadLocalizations()
|
||||
public function LoadLocalizations($includeDynamic = true)
|
||||
{
|
||||
$culture = $this->Culture;
|
||||
|
||||
|
|
@ -133,9 +139,6 @@ class LocalizationService
|
|||
}
|
||||
}
|
||||
|
||||
$this->PoUserStrings = new Translations();
|
||||
$this->PoUserStrings->setDomain('grocy/userstrings');
|
||||
|
||||
$this->Po = Translations::fromPoFile(__DIR__ . "/../localization/$culture/strings.po");
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/chore_assignment_types.po"))
|
||||
|
|
@ -178,7 +181,18 @@ class LocalizationService
|
|||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/demo_data.po"));
|
||||
}
|
||||
|
||||
if($includeDynamic)
|
||||
{
|
||||
$this->LoadDynamicLocalizations();
|
||||
}
|
||||
|
||||
$this->Translator = new Translator();
|
||||
$this->Translator->loadTranslations($this->Po);
|
||||
}
|
||||
|
||||
public function LoadDynamicLocalizations() {
|
||||
$quantityUnits = null;
|
||||
|
||||
try
|
||||
{
|
||||
$quantityUnits = $this->getDatabase()->quantity_units()->fetchAll();
|
||||
|
|
@ -200,10 +214,13 @@ class LocalizationService
|
|||
$this->PoUserStrings[] = $translation;
|
||||
}
|
||||
|
||||
if($this->Po == null)
|
||||
{
|
||||
$this->Po = new Translations();
|
||||
$this->Po->setDomain(self::DOMAIN);
|
||||
}
|
||||
|
||||
$this->Po = $this->Po->mergeWith($this->PoUserStrings);
|
||||
}
|
||||
|
||||
$this->Translator = new Translator();
|
||||
$this->Translator->loadTranslations($this->Po);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
Currency:'{{ GROCY_CURRENCY }}',
|
||||
CalendarFirstDayOfWeek: '{{ GROCY_CALENDAR_FIRST_DAY_OF_WEEK }}',
|
||||
CalendarShowWeekNumbers: {{ BoolToString(GROCY_CALENDAR_SHOW_WEEK_OF_YEAR) }},
|
||||
Locale: '{{ $GrocyLocale }}',
|
||||
GettextPo: {!! $GettextPo !!},
|
||||
FeatureFlags: {!! json_encode($featureFlags) !!},
|
||||
Webhooks: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user