From 4e1f6e9ac74724b0538e08425133916f295f870e Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Fri, 2 Jul 2021 18:45:03 +0200 Subject: [PATCH 01/16] Moved legacy JavaScript to its own folder --- js/grocy.js | 32 +++++++++---------- js/{ => legacy}/components/BasePicker.js | 0 js/{ => legacy}/components/barcodescanner.js | 0 js/{ => legacy}/components/batterycard.js | 0 js/{ => legacy}/components/calendarcard.js | 0 js/{ => legacy}/components/chorecard.js | 0 js/{ => legacy}/components/datetimepicker.js | 0 js/{ => legacy}/components/datetimepicker2.js | 0 js/{ => legacy}/components/index.js | 0 js/{ => legacy}/components/locationpicker.js | 0 js/{ => legacy}/components/numberpicker.js | 0 .../components/productamountpicker.js | 0 js/{ => legacy}/components/productcard.js | 0 js/{ => legacy}/components/productpicker.js | 0 js/{ => legacy}/components/recipepicker.js | 0 .../components/shoppinglocationpicker.js | 0 js/{ => legacy}/components/userfieldsform.js | 0 js/{ => legacy}/components/userpicker.js | 0 js/{ => legacy}/configs/datatable.js | 0 js/{ => legacy}/configs/globalstate.js | 0 js/{ => legacy}/configs/lazy.js | 0 js/{ => legacy}/configs/permissions.js | 0 js/{ => legacy}/configs/timeago.js | 0 js/{ => legacy}/helpers/clock.js | 0 js/{ => legacy}/helpers/dropdown.js | 0 js/{ => legacy}/helpers/embeds.js | 0 js/{ => legacy}/helpers/extensions.js | 0 js/{ => legacy}/helpers/frontend.js | 0 js/{ => legacy}/helpers/global.js | 0 js/{ => legacy}/helpers/input.js | 0 js/{ => legacy}/helpers/messagebag.js | 0 js/{ => legacy}/helpers/numberdisplay.js | 0 js/{ => legacy}/helpers/qrcode.js | 0 js/{ => legacy}/helpers/string.js | 0 js/{ => legacy}/lib/UISound.js | 0 js/{ => legacy}/lib/WakeLock.js | 0 js/{ => legacy}/lib/api.js | 0 js/{ => legacy}/lib/legacy.js | 0 js/{ => legacy}/lib/nightmode.js | 0 js/{ => legacy}/lib/proxy.js | 0 js/{ => legacy}/vendor.js | 0 js/{ => legacy}/viewjs/about.js | 0 .../viewjs/barcodescannertesting.js | 0 js/{ => legacy}/viewjs/batteries.js | 0 js/{ => legacy}/viewjs/batteriesjournal.js | 0 js/{ => legacy}/viewjs/batteriesoverview.js | 0 js/{ => legacy}/viewjs/batteriessettings.js | 0 js/{ => legacy}/viewjs/batteryform.js | 0 js/{ => legacy}/viewjs/batterytracking.js | 0 js/{ => legacy}/viewjs/calendar.js | 0 js/{ => legacy}/viewjs/choreform.js | 0 js/{ => legacy}/viewjs/chores.js | 0 js/{ => legacy}/viewjs/choresjournal.js | 0 js/{ => legacy}/viewjs/choresoverview.js | 0 js/{ => legacy}/viewjs/choressettings.js | 0 js/{ => legacy}/viewjs/choretracking.js | 0 js/{ => legacy}/viewjs/consume.js | 0 js/{ => legacy}/viewjs/equipment.js | 0 js/{ => legacy}/viewjs/equipmentform.js | 0 js/{ => legacy}/viewjs/index.js | 0 js/{ => legacy}/viewjs/inventory.js | 0 .../viewjs/locationcontentsheet.js | 0 js/{ => legacy}/viewjs/locationform.js | 0 js/{ => legacy}/viewjs/locations.js | 0 js/{ => legacy}/viewjs/login.js | 0 js/{ => legacy}/viewjs/manageapikeys.js | 0 js/{ => legacy}/viewjs/mealplan.js | 0 js/{ => legacy}/viewjs/openapiui.js | 0 js/{ => legacy}/viewjs/productbarcodeform.js | 0 js/{ => legacy}/viewjs/productform.js | 0 js/{ => legacy}/viewjs/productgroupform.js | 0 js/{ => legacy}/viewjs/productgroups.js | 0 js/{ => legacy}/viewjs/products.js | 0 js/{ => legacy}/viewjs/purchase.js | 0 .../viewjs/quantityunitconversionform.js | 0 js/{ => legacy}/viewjs/quantityunitform.js | 0 .../viewjs/quantityunitpluraltesting.js | 0 js/{ => legacy}/viewjs/quantityunits.js | 0 js/{ => legacy}/viewjs/recipeform.js | 0 js/{ => legacy}/viewjs/recipeposform.js | 0 js/{ => legacy}/viewjs/recipes.js | 0 js/{ => legacy}/viewjs/recipessettings.js | 0 js/{ => legacy}/viewjs/shoppinglist.js | 0 js/{ => legacy}/viewjs/shoppinglistform.js | 0 .../viewjs/shoppinglistitemform.js | 0 .../viewjs/shoppinglistsettings.js | 0 .../viewjs/shoppinglocationform.js | 0 js/{ => legacy}/viewjs/shoppinglocations.js | 0 js/{ => legacy}/viewjs/stockentries.js | 0 js/{ => legacy}/viewjs/stockentryform.js | 0 js/{ => legacy}/viewjs/stockjournal.js | 0 js/{ => legacy}/viewjs/stockjournalsummary.js | 0 js/{ => legacy}/viewjs/stockoverview.js | 0 js/{ => legacy}/viewjs/stocksettings.js | 0 js/{ => legacy}/viewjs/taskcategories.js | 0 js/{ => legacy}/viewjs/taskcategoryform.js | 0 js/{ => legacy}/viewjs/taskform.js | 0 js/{ => legacy}/viewjs/tasks.js | 0 js/{ => legacy}/viewjs/taskssettings.js | 0 js/{ => legacy}/viewjs/transfer.js | 0 js/{ => legacy}/viewjs/userentities.js | 0 js/{ => legacy}/viewjs/userentityform.js | 0 js/{ => legacy}/viewjs/userfieldform.js | 0 js/{ => legacy}/viewjs/userfields.js | 0 js/{ => legacy}/viewjs/userform.js | 0 js/{ => legacy}/viewjs/userobjectform.js | 0 js/{ => legacy}/viewjs/userobjects.js | 0 js/{ => legacy}/viewjs/userpermissions.js | 0 js/{ => legacy}/viewjs/users.js | 0 js/{ => legacy}/viewjs/usersettings.js | 0 110 files changed, 16 insertions(+), 16 deletions(-) rename js/{ => legacy}/components/BasePicker.js (100%) rename js/{ => legacy}/components/barcodescanner.js (100%) rename js/{ => legacy}/components/batterycard.js (100%) rename js/{ => legacy}/components/calendarcard.js (100%) rename js/{ => legacy}/components/chorecard.js (100%) rename js/{ => legacy}/components/datetimepicker.js (100%) rename js/{ => legacy}/components/datetimepicker2.js (100%) rename js/{ => legacy}/components/index.js (100%) rename js/{ => legacy}/components/locationpicker.js (100%) rename js/{ => legacy}/components/numberpicker.js (100%) rename js/{ => legacy}/components/productamountpicker.js (100%) rename js/{ => legacy}/components/productcard.js (100%) rename js/{ => legacy}/components/productpicker.js (100%) rename js/{ => legacy}/components/recipepicker.js (100%) rename js/{ => legacy}/components/shoppinglocationpicker.js (100%) rename js/{ => legacy}/components/userfieldsform.js (100%) rename js/{ => legacy}/components/userpicker.js (100%) rename js/{ => legacy}/configs/datatable.js (100%) rename js/{ => legacy}/configs/globalstate.js (100%) rename js/{ => legacy}/configs/lazy.js (100%) rename js/{ => legacy}/configs/permissions.js (100%) rename js/{ => legacy}/configs/timeago.js (100%) rename js/{ => legacy}/helpers/clock.js (100%) rename js/{ => legacy}/helpers/dropdown.js (100%) rename js/{ => legacy}/helpers/embeds.js (100%) rename js/{ => legacy}/helpers/extensions.js (100%) rename js/{ => legacy}/helpers/frontend.js (100%) rename js/{ => legacy}/helpers/global.js (100%) rename js/{ => legacy}/helpers/input.js (100%) rename js/{ => legacy}/helpers/messagebag.js (100%) rename js/{ => legacy}/helpers/numberdisplay.js (100%) rename js/{ => legacy}/helpers/qrcode.js (100%) rename js/{ => legacy}/helpers/string.js (100%) rename js/{ => legacy}/lib/UISound.js (100%) rename js/{ => legacy}/lib/WakeLock.js (100%) rename js/{ => legacy}/lib/api.js (100%) rename js/{ => legacy}/lib/legacy.js (100%) rename js/{ => legacy}/lib/nightmode.js (100%) rename js/{ => legacy}/lib/proxy.js (100%) rename js/{ => legacy}/vendor.js (100%) rename js/{ => legacy}/viewjs/about.js (100%) rename js/{ => legacy}/viewjs/barcodescannertesting.js (100%) rename js/{ => legacy}/viewjs/batteries.js (100%) rename js/{ => legacy}/viewjs/batteriesjournal.js (100%) rename js/{ => legacy}/viewjs/batteriesoverview.js (100%) rename js/{ => legacy}/viewjs/batteriessettings.js (100%) rename js/{ => legacy}/viewjs/batteryform.js (100%) rename js/{ => legacy}/viewjs/batterytracking.js (100%) rename js/{ => legacy}/viewjs/calendar.js (100%) rename js/{ => legacy}/viewjs/choreform.js (100%) rename js/{ => legacy}/viewjs/chores.js (100%) rename js/{ => legacy}/viewjs/choresjournal.js (100%) rename js/{ => legacy}/viewjs/choresoverview.js (100%) rename js/{ => legacy}/viewjs/choressettings.js (100%) rename js/{ => legacy}/viewjs/choretracking.js (100%) rename js/{ => legacy}/viewjs/consume.js (100%) rename js/{ => legacy}/viewjs/equipment.js (100%) rename js/{ => legacy}/viewjs/equipmentform.js (100%) rename js/{ => legacy}/viewjs/index.js (100%) rename js/{ => legacy}/viewjs/inventory.js (100%) rename js/{ => legacy}/viewjs/locationcontentsheet.js (100%) rename js/{ => legacy}/viewjs/locationform.js (100%) rename js/{ => legacy}/viewjs/locations.js (100%) rename js/{ => legacy}/viewjs/login.js (100%) rename js/{ => legacy}/viewjs/manageapikeys.js (100%) rename js/{ => legacy}/viewjs/mealplan.js (100%) rename js/{ => legacy}/viewjs/openapiui.js (100%) rename js/{ => legacy}/viewjs/productbarcodeform.js (100%) rename js/{ => legacy}/viewjs/productform.js (100%) rename js/{ => legacy}/viewjs/productgroupform.js (100%) rename js/{ => legacy}/viewjs/productgroups.js (100%) rename js/{ => legacy}/viewjs/products.js (100%) rename js/{ => legacy}/viewjs/purchase.js (100%) rename js/{ => legacy}/viewjs/quantityunitconversionform.js (100%) rename js/{ => legacy}/viewjs/quantityunitform.js (100%) rename js/{ => legacy}/viewjs/quantityunitpluraltesting.js (100%) rename js/{ => legacy}/viewjs/quantityunits.js (100%) rename js/{ => legacy}/viewjs/recipeform.js (100%) rename js/{ => legacy}/viewjs/recipeposform.js (100%) rename js/{ => legacy}/viewjs/recipes.js (100%) rename js/{ => legacy}/viewjs/recipessettings.js (100%) rename js/{ => legacy}/viewjs/shoppinglist.js (100%) rename js/{ => legacy}/viewjs/shoppinglistform.js (100%) rename js/{ => legacy}/viewjs/shoppinglistitemform.js (100%) rename js/{ => legacy}/viewjs/shoppinglistsettings.js (100%) rename js/{ => legacy}/viewjs/shoppinglocationform.js (100%) rename js/{ => legacy}/viewjs/shoppinglocations.js (100%) rename js/{ => legacy}/viewjs/stockentries.js (100%) rename js/{ => legacy}/viewjs/stockentryform.js (100%) rename js/{ => legacy}/viewjs/stockjournal.js (100%) rename js/{ => legacy}/viewjs/stockjournalsummary.js (100%) rename js/{ => legacy}/viewjs/stockoverview.js (100%) rename js/{ => legacy}/viewjs/stocksettings.js (100%) rename js/{ => legacy}/viewjs/taskcategories.js (100%) rename js/{ => legacy}/viewjs/taskcategoryform.js (100%) rename js/{ => legacy}/viewjs/taskform.js (100%) rename js/{ => legacy}/viewjs/tasks.js (100%) rename js/{ => legacy}/viewjs/taskssettings.js (100%) rename js/{ => legacy}/viewjs/transfer.js (100%) rename js/{ => legacy}/viewjs/userentities.js (100%) rename js/{ => legacy}/viewjs/userentityform.js (100%) rename js/{ => legacy}/viewjs/userfieldform.js (100%) rename js/{ => legacy}/viewjs/userfields.js (100%) rename js/{ => legacy}/viewjs/userform.js (100%) rename js/{ => legacy}/viewjs/userobjectform.js (100%) rename js/{ => legacy}/viewjs/userobjects.js (100%) rename js/{ => legacy}/viewjs/userpermissions.js (100%) rename js/{ => legacy}/viewjs/users.js (100%) rename js/{ => legacy}/viewjs/usersettings.js (100%) diff --git a/js/grocy.js b/js/grocy.js index a2b4b97c..06258065 100644 --- a/js/grocy.js +++ b/js/grocy.js @@ -1,26 +1,26 @@ // todo: axe some stuff from here for better tree-shaking. also brr, side effects. -import './vendor'; -import { GrocyApi } from './lib/api'; -import { RefreshContextualTimeago } from './configs/timeago'; -import { setDatatableDefaults } from './configs/datatable'; -import { GrocyFrontendHelpers } from './helpers/frontend'; -import { setInitialGlobalState } from './configs/globalstate'; -import { WakeLock } from './lib/WakeLock'; -import { UISound } from './lib/UISound'; -import { Nightmode } from './lib/nightmode'; -import { HeaderClock } from './helpers/clock'; -import { BoolVal } from './helpers/extensions'; +import './legacy/vendor'; +import { GrocyApi } from './legacy/lib/api'; +import { RefreshContextualTimeago } from './legacy/configs/timeago'; +import { setDatatableDefaults } from './legacy/configs/datatable'; +import { GrocyFrontendHelpers } from './legacy/helpers/frontend'; +import { setInitialGlobalState } from './legacy/configs/globalstate'; +import { WakeLock } from './legacy/lib/WakeLock'; +import { UISound } from './legacy/lib/UISound'; +import { Nightmode } from './legacy/lib/nightmode'; +import { HeaderClock } from './legacy/helpers/clock'; +import { BoolVal } from './legacy/helpers/extensions'; import Translator from 'gettext-translator'; import { register as timeagoRegisterLang } from 'timeago.js'; import * as timeagoLangs from 'timeago.js/lib/lang'; -import { WindowMessageBag } from './helpers/messagebag'; -import * as components from './components'; +import { WindowMessageBag } from './legacy/helpers/messagebag'; +import * as components from './legacy/components'; import * as uuid from 'uuid'; -import * as views from './viewjs'; -import { GrocyProxy } from './lib/proxy'; //import { $ } from 'jquery'; +import * as views from './legacy/viewjs'; +import { GrocyProxy } from './legacy/lib/proxy'; //import { $ } from 'jquery'; import moment from 'moment'; -import './helpers/string'; +import './legacy/helpers/string'; class GrocyClass { diff --git a/js/components/BasePicker.js b/js/legacy/components/BasePicker.js similarity index 100% rename from js/components/BasePicker.js rename to js/legacy/components/BasePicker.js diff --git a/js/components/barcodescanner.js b/js/legacy/components/barcodescanner.js similarity index 100% rename from js/components/barcodescanner.js rename to js/legacy/components/barcodescanner.js diff --git a/js/components/batterycard.js b/js/legacy/components/batterycard.js similarity index 100% rename from js/components/batterycard.js rename to js/legacy/components/batterycard.js diff --git a/js/components/calendarcard.js b/js/legacy/components/calendarcard.js similarity index 100% rename from js/components/calendarcard.js rename to js/legacy/components/calendarcard.js diff --git a/js/components/chorecard.js b/js/legacy/components/chorecard.js similarity index 100% rename from js/components/chorecard.js rename to js/legacy/components/chorecard.js diff --git a/js/components/datetimepicker.js b/js/legacy/components/datetimepicker.js similarity index 100% rename from js/components/datetimepicker.js rename to js/legacy/components/datetimepicker.js diff --git a/js/components/datetimepicker2.js b/js/legacy/components/datetimepicker2.js similarity index 100% rename from js/components/datetimepicker2.js rename to js/legacy/components/datetimepicker2.js diff --git a/js/components/index.js b/js/legacy/components/index.js similarity index 100% rename from js/components/index.js rename to js/legacy/components/index.js diff --git a/js/components/locationpicker.js b/js/legacy/components/locationpicker.js similarity index 100% rename from js/components/locationpicker.js rename to js/legacy/components/locationpicker.js diff --git a/js/components/numberpicker.js b/js/legacy/components/numberpicker.js similarity index 100% rename from js/components/numberpicker.js rename to js/legacy/components/numberpicker.js diff --git a/js/components/productamountpicker.js b/js/legacy/components/productamountpicker.js similarity index 100% rename from js/components/productamountpicker.js rename to js/legacy/components/productamountpicker.js diff --git a/js/components/productcard.js b/js/legacy/components/productcard.js similarity index 100% rename from js/components/productcard.js rename to js/legacy/components/productcard.js diff --git a/js/components/productpicker.js b/js/legacy/components/productpicker.js similarity index 100% rename from js/components/productpicker.js rename to js/legacy/components/productpicker.js diff --git a/js/components/recipepicker.js b/js/legacy/components/recipepicker.js similarity index 100% rename from js/components/recipepicker.js rename to js/legacy/components/recipepicker.js diff --git a/js/components/shoppinglocationpicker.js b/js/legacy/components/shoppinglocationpicker.js similarity index 100% rename from js/components/shoppinglocationpicker.js rename to js/legacy/components/shoppinglocationpicker.js diff --git a/js/components/userfieldsform.js b/js/legacy/components/userfieldsform.js similarity index 100% rename from js/components/userfieldsform.js rename to js/legacy/components/userfieldsform.js diff --git a/js/components/userpicker.js b/js/legacy/components/userpicker.js similarity index 100% rename from js/components/userpicker.js rename to js/legacy/components/userpicker.js diff --git a/js/configs/datatable.js b/js/legacy/configs/datatable.js similarity index 100% rename from js/configs/datatable.js rename to js/legacy/configs/datatable.js diff --git a/js/configs/globalstate.js b/js/legacy/configs/globalstate.js similarity index 100% rename from js/configs/globalstate.js rename to js/legacy/configs/globalstate.js diff --git a/js/configs/lazy.js b/js/legacy/configs/lazy.js similarity index 100% rename from js/configs/lazy.js rename to js/legacy/configs/lazy.js diff --git a/js/configs/permissions.js b/js/legacy/configs/permissions.js similarity index 100% rename from js/configs/permissions.js rename to js/legacy/configs/permissions.js diff --git a/js/configs/timeago.js b/js/legacy/configs/timeago.js similarity index 100% rename from js/configs/timeago.js rename to js/legacy/configs/timeago.js diff --git a/js/helpers/clock.js b/js/legacy/helpers/clock.js similarity index 100% rename from js/helpers/clock.js rename to js/legacy/helpers/clock.js diff --git a/js/helpers/dropdown.js b/js/legacy/helpers/dropdown.js similarity index 100% rename from js/helpers/dropdown.js rename to js/legacy/helpers/dropdown.js diff --git a/js/helpers/embeds.js b/js/legacy/helpers/embeds.js similarity index 100% rename from js/helpers/embeds.js rename to js/legacy/helpers/embeds.js diff --git a/js/helpers/extensions.js b/js/legacy/helpers/extensions.js similarity index 100% rename from js/helpers/extensions.js rename to js/legacy/helpers/extensions.js diff --git a/js/helpers/frontend.js b/js/legacy/helpers/frontend.js similarity index 100% rename from js/helpers/frontend.js rename to js/legacy/helpers/frontend.js diff --git a/js/helpers/global.js b/js/legacy/helpers/global.js similarity index 100% rename from js/helpers/global.js rename to js/legacy/helpers/global.js diff --git a/js/helpers/input.js b/js/legacy/helpers/input.js similarity index 100% rename from js/helpers/input.js rename to js/legacy/helpers/input.js diff --git a/js/helpers/messagebag.js b/js/legacy/helpers/messagebag.js similarity index 100% rename from js/helpers/messagebag.js rename to js/legacy/helpers/messagebag.js diff --git a/js/helpers/numberdisplay.js b/js/legacy/helpers/numberdisplay.js similarity index 100% rename from js/helpers/numberdisplay.js rename to js/legacy/helpers/numberdisplay.js diff --git a/js/helpers/qrcode.js b/js/legacy/helpers/qrcode.js similarity index 100% rename from js/helpers/qrcode.js rename to js/legacy/helpers/qrcode.js diff --git a/js/helpers/string.js b/js/legacy/helpers/string.js similarity index 100% rename from js/helpers/string.js rename to js/legacy/helpers/string.js diff --git a/js/lib/UISound.js b/js/legacy/lib/UISound.js similarity index 100% rename from js/lib/UISound.js rename to js/legacy/lib/UISound.js diff --git a/js/lib/WakeLock.js b/js/legacy/lib/WakeLock.js similarity index 100% rename from js/lib/WakeLock.js rename to js/legacy/lib/WakeLock.js diff --git a/js/lib/api.js b/js/legacy/lib/api.js similarity index 100% rename from js/lib/api.js rename to js/legacy/lib/api.js diff --git a/js/lib/legacy.js b/js/legacy/lib/legacy.js similarity index 100% rename from js/lib/legacy.js rename to js/legacy/lib/legacy.js diff --git a/js/lib/nightmode.js b/js/legacy/lib/nightmode.js similarity index 100% rename from js/lib/nightmode.js rename to js/legacy/lib/nightmode.js diff --git a/js/lib/proxy.js b/js/legacy/lib/proxy.js similarity index 100% rename from js/lib/proxy.js rename to js/legacy/lib/proxy.js diff --git a/js/vendor.js b/js/legacy/vendor.js similarity index 100% rename from js/vendor.js rename to js/legacy/vendor.js diff --git a/js/viewjs/about.js b/js/legacy/viewjs/about.js similarity index 100% rename from js/viewjs/about.js rename to js/legacy/viewjs/about.js diff --git a/js/viewjs/barcodescannertesting.js b/js/legacy/viewjs/barcodescannertesting.js similarity index 100% rename from js/viewjs/barcodescannertesting.js rename to js/legacy/viewjs/barcodescannertesting.js diff --git a/js/viewjs/batteries.js b/js/legacy/viewjs/batteries.js similarity index 100% rename from js/viewjs/batteries.js rename to js/legacy/viewjs/batteries.js diff --git a/js/viewjs/batteriesjournal.js b/js/legacy/viewjs/batteriesjournal.js similarity index 100% rename from js/viewjs/batteriesjournal.js rename to js/legacy/viewjs/batteriesjournal.js diff --git a/js/viewjs/batteriesoverview.js b/js/legacy/viewjs/batteriesoverview.js similarity index 100% rename from js/viewjs/batteriesoverview.js rename to js/legacy/viewjs/batteriesoverview.js diff --git a/js/viewjs/batteriessettings.js b/js/legacy/viewjs/batteriessettings.js similarity index 100% rename from js/viewjs/batteriessettings.js rename to js/legacy/viewjs/batteriessettings.js diff --git a/js/viewjs/batteryform.js b/js/legacy/viewjs/batteryform.js similarity index 100% rename from js/viewjs/batteryform.js rename to js/legacy/viewjs/batteryform.js diff --git a/js/viewjs/batterytracking.js b/js/legacy/viewjs/batterytracking.js similarity index 100% rename from js/viewjs/batterytracking.js rename to js/legacy/viewjs/batterytracking.js diff --git a/js/viewjs/calendar.js b/js/legacy/viewjs/calendar.js similarity index 100% rename from js/viewjs/calendar.js rename to js/legacy/viewjs/calendar.js diff --git a/js/viewjs/choreform.js b/js/legacy/viewjs/choreform.js similarity index 100% rename from js/viewjs/choreform.js rename to js/legacy/viewjs/choreform.js diff --git a/js/viewjs/chores.js b/js/legacy/viewjs/chores.js similarity index 100% rename from js/viewjs/chores.js rename to js/legacy/viewjs/chores.js diff --git a/js/viewjs/choresjournal.js b/js/legacy/viewjs/choresjournal.js similarity index 100% rename from js/viewjs/choresjournal.js rename to js/legacy/viewjs/choresjournal.js diff --git a/js/viewjs/choresoverview.js b/js/legacy/viewjs/choresoverview.js similarity index 100% rename from js/viewjs/choresoverview.js rename to js/legacy/viewjs/choresoverview.js diff --git a/js/viewjs/choressettings.js b/js/legacy/viewjs/choressettings.js similarity index 100% rename from js/viewjs/choressettings.js rename to js/legacy/viewjs/choressettings.js diff --git a/js/viewjs/choretracking.js b/js/legacy/viewjs/choretracking.js similarity index 100% rename from js/viewjs/choretracking.js rename to js/legacy/viewjs/choretracking.js diff --git a/js/viewjs/consume.js b/js/legacy/viewjs/consume.js similarity index 100% rename from js/viewjs/consume.js rename to js/legacy/viewjs/consume.js diff --git a/js/viewjs/equipment.js b/js/legacy/viewjs/equipment.js similarity index 100% rename from js/viewjs/equipment.js rename to js/legacy/viewjs/equipment.js diff --git a/js/viewjs/equipmentform.js b/js/legacy/viewjs/equipmentform.js similarity index 100% rename from js/viewjs/equipmentform.js rename to js/legacy/viewjs/equipmentform.js diff --git a/js/viewjs/index.js b/js/legacy/viewjs/index.js similarity index 100% rename from js/viewjs/index.js rename to js/legacy/viewjs/index.js diff --git a/js/viewjs/inventory.js b/js/legacy/viewjs/inventory.js similarity index 100% rename from js/viewjs/inventory.js rename to js/legacy/viewjs/inventory.js diff --git a/js/viewjs/locationcontentsheet.js b/js/legacy/viewjs/locationcontentsheet.js similarity index 100% rename from js/viewjs/locationcontentsheet.js rename to js/legacy/viewjs/locationcontentsheet.js diff --git a/js/viewjs/locationform.js b/js/legacy/viewjs/locationform.js similarity index 100% rename from js/viewjs/locationform.js rename to js/legacy/viewjs/locationform.js diff --git a/js/viewjs/locations.js b/js/legacy/viewjs/locations.js similarity index 100% rename from js/viewjs/locations.js rename to js/legacy/viewjs/locations.js diff --git a/js/viewjs/login.js b/js/legacy/viewjs/login.js similarity index 100% rename from js/viewjs/login.js rename to js/legacy/viewjs/login.js diff --git a/js/viewjs/manageapikeys.js b/js/legacy/viewjs/manageapikeys.js similarity index 100% rename from js/viewjs/manageapikeys.js rename to js/legacy/viewjs/manageapikeys.js diff --git a/js/viewjs/mealplan.js b/js/legacy/viewjs/mealplan.js similarity index 100% rename from js/viewjs/mealplan.js rename to js/legacy/viewjs/mealplan.js diff --git a/js/viewjs/openapiui.js b/js/legacy/viewjs/openapiui.js similarity index 100% rename from js/viewjs/openapiui.js rename to js/legacy/viewjs/openapiui.js diff --git a/js/viewjs/productbarcodeform.js b/js/legacy/viewjs/productbarcodeform.js similarity index 100% rename from js/viewjs/productbarcodeform.js rename to js/legacy/viewjs/productbarcodeform.js diff --git a/js/viewjs/productform.js b/js/legacy/viewjs/productform.js similarity index 100% rename from js/viewjs/productform.js rename to js/legacy/viewjs/productform.js diff --git a/js/viewjs/productgroupform.js b/js/legacy/viewjs/productgroupform.js similarity index 100% rename from js/viewjs/productgroupform.js rename to js/legacy/viewjs/productgroupform.js diff --git a/js/viewjs/productgroups.js b/js/legacy/viewjs/productgroups.js similarity index 100% rename from js/viewjs/productgroups.js rename to js/legacy/viewjs/productgroups.js diff --git a/js/viewjs/products.js b/js/legacy/viewjs/products.js similarity index 100% rename from js/viewjs/products.js rename to js/legacy/viewjs/products.js diff --git a/js/viewjs/purchase.js b/js/legacy/viewjs/purchase.js similarity index 100% rename from js/viewjs/purchase.js rename to js/legacy/viewjs/purchase.js diff --git a/js/viewjs/quantityunitconversionform.js b/js/legacy/viewjs/quantityunitconversionform.js similarity index 100% rename from js/viewjs/quantityunitconversionform.js rename to js/legacy/viewjs/quantityunitconversionform.js diff --git a/js/viewjs/quantityunitform.js b/js/legacy/viewjs/quantityunitform.js similarity index 100% rename from js/viewjs/quantityunitform.js rename to js/legacy/viewjs/quantityunitform.js diff --git a/js/viewjs/quantityunitpluraltesting.js b/js/legacy/viewjs/quantityunitpluraltesting.js similarity index 100% rename from js/viewjs/quantityunitpluraltesting.js rename to js/legacy/viewjs/quantityunitpluraltesting.js diff --git a/js/viewjs/quantityunits.js b/js/legacy/viewjs/quantityunits.js similarity index 100% rename from js/viewjs/quantityunits.js rename to js/legacy/viewjs/quantityunits.js diff --git a/js/viewjs/recipeform.js b/js/legacy/viewjs/recipeform.js similarity index 100% rename from js/viewjs/recipeform.js rename to js/legacy/viewjs/recipeform.js diff --git a/js/viewjs/recipeposform.js b/js/legacy/viewjs/recipeposform.js similarity index 100% rename from js/viewjs/recipeposform.js rename to js/legacy/viewjs/recipeposform.js diff --git a/js/viewjs/recipes.js b/js/legacy/viewjs/recipes.js similarity index 100% rename from js/viewjs/recipes.js rename to js/legacy/viewjs/recipes.js diff --git a/js/viewjs/recipessettings.js b/js/legacy/viewjs/recipessettings.js similarity index 100% rename from js/viewjs/recipessettings.js rename to js/legacy/viewjs/recipessettings.js diff --git a/js/viewjs/shoppinglist.js b/js/legacy/viewjs/shoppinglist.js similarity index 100% rename from js/viewjs/shoppinglist.js rename to js/legacy/viewjs/shoppinglist.js diff --git a/js/viewjs/shoppinglistform.js b/js/legacy/viewjs/shoppinglistform.js similarity index 100% rename from js/viewjs/shoppinglistform.js rename to js/legacy/viewjs/shoppinglistform.js diff --git a/js/viewjs/shoppinglistitemform.js b/js/legacy/viewjs/shoppinglistitemform.js similarity index 100% rename from js/viewjs/shoppinglistitemform.js rename to js/legacy/viewjs/shoppinglistitemform.js diff --git a/js/viewjs/shoppinglistsettings.js b/js/legacy/viewjs/shoppinglistsettings.js similarity index 100% rename from js/viewjs/shoppinglistsettings.js rename to js/legacy/viewjs/shoppinglistsettings.js diff --git a/js/viewjs/shoppinglocationform.js b/js/legacy/viewjs/shoppinglocationform.js similarity index 100% rename from js/viewjs/shoppinglocationform.js rename to js/legacy/viewjs/shoppinglocationform.js diff --git a/js/viewjs/shoppinglocations.js b/js/legacy/viewjs/shoppinglocations.js similarity index 100% rename from js/viewjs/shoppinglocations.js rename to js/legacy/viewjs/shoppinglocations.js diff --git a/js/viewjs/stockentries.js b/js/legacy/viewjs/stockentries.js similarity index 100% rename from js/viewjs/stockentries.js rename to js/legacy/viewjs/stockentries.js diff --git a/js/viewjs/stockentryform.js b/js/legacy/viewjs/stockentryform.js similarity index 100% rename from js/viewjs/stockentryform.js rename to js/legacy/viewjs/stockentryform.js diff --git a/js/viewjs/stockjournal.js b/js/legacy/viewjs/stockjournal.js similarity index 100% rename from js/viewjs/stockjournal.js rename to js/legacy/viewjs/stockjournal.js diff --git a/js/viewjs/stockjournalsummary.js b/js/legacy/viewjs/stockjournalsummary.js similarity index 100% rename from js/viewjs/stockjournalsummary.js rename to js/legacy/viewjs/stockjournalsummary.js diff --git a/js/viewjs/stockoverview.js b/js/legacy/viewjs/stockoverview.js similarity index 100% rename from js/viewjs/stockoverview.js rename to js/legacy/viewjs/stockoverview.js diff --git a/js/viewjs/stocksettings.js b/js/legacy/viewjs/stocksettings.js similarity index 100% rename from js/viewjs/stocksettings.js rename to js/legacy/viewjs/stocksettings.js diff --git a/js/viewjs/taskcategories.js b/js/legacy/viewjs/taskcategories.js similarity index 100% rename from js/viewjs/taskcategories.js rename to js/legacy/viewjs/taskcategories.js diff --git a/js/viewjs/taskcategoryform.js b/js/legacy/viewjs/taskcategoryform.js similarity index 100% rename from js/viewjs/taskcategoryform.js rename to js/legacy/viewjs/taskcategoryform.js diff --git a/js/viewjs/taskform.js b/js/legacy/viewjs/taskform.js similarity index 100% rename from js/viewjs/taskform.js rename to js/legacy/viewjs/taskform.js diff --git a/js/viewjs/tasks.js b/js/legacy/viewjs/tasks.js similarity index 100% rename from js/viewjs/tasks.js rename to js/legacy/viewjs/tasks.js diff --git a/js/viewjs/taskssettings.js b/js/legacy/viewjs/taskssettings.js similarity index 100% rename from js/viewjs/taskssettings.js rename to js/legacy/viewjs/taskssettings.js diff --git a/js/viewjs/transfer.js b/js/legacy/viewjs/transfer.js similarity index 100% rename from js/viewjs/transfer.js rename to js/legacy/viewjs/transfer.js diff --git a/js/viewjs/userentities.js b/js/legacy/viewjs/userentities.js similarity index 100% rename from js/viewjs/userentities.js rename to js/legacy/viewjs/userentities.js diff --git a/js/viewjs/userentityform.js b/js/legacy/viewjs/userentityform.js similarity index 100% rename from js/viewjs/userentityform.js rename to js/legacy/viewjs/userentityform.js diff --git a/js/viewjs/userfieldform.js b/js/legacy/viewjs/userfieldform.js similarity index 100% rename from js/viewjs/userfieldform.js rename to js/legacy/viewjs/userfieldform.js diff --git a/js/viewjs/userfields.js b/js/legacy/viewjs/userfields.js similarity index 100% rename from js/viewjs/userfields.js rename to js/legacy/viewjs/userfields.js diff --git a/js/viewjs/userform.js b/js/legacy/viewjs/userform.js similarity index 100% rename from js/viewjs/userform.js rename to js/legacy/viewjs/userform.js diff --git a/js/viewjs/userobjectform.js b/js/legacy/viewjs/userobjectform.js similarity index 100% rename from js/viewjs/userobjectform.js rename to js/legacy/viewjs/userobjectform.js diff --git a/js/viewjs/userobjects.js b/js/legacy/viewjs/userobjects.js similarity index 100% rename from js/viewjs/userobjects.js rename to js/legacy/viewjs/userobjects.js diff --git a/js/viewjs/userpermissions.js b/js/legacy/viewjs/userpermissions.js similarity index 100% rename from js/viewjs/userpermissions.js rename to js/legacy/viewjs/userpermissions.js diff --git a/js/viewjs/users.js b/js/legacy/viewjs/users.js similarity index 100% rename from js/viewjs/users.js rename to js/legacy/viewjs/users.js diff --git a/js/viewjs/usersettings.js b/js/legacy/viewjs/usersettings.js similarity index 100% rename from js/viewjs/usersettings.js rename to js/legacy/viewjs/usersettings.js From e95d86d84519693034c0d6c40aa0a897a05a89cd Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Fri, 2 Jul 2021 23:03:18 +0200 Subject: [PATCH 02/16] Setup typescript, vue3, primeVue --- .eslintrc.js | 36 +- .vscode/extensions.json | 5 +- .vscode/settings.json | 8 + Makefile | 5 + js/App.vue | 14 + js/grocy.js | 1 + js/main.ts | 14 + js/pages/Stock/Overview.vue | 11 + ....vue?vue&type=template&id=31afdab6&lang.js | 6 + js/router.ts | 19 + js/shims-vue.d.ts | 7 + package.json | 16 +- php/Controllers/SystemController.php | 4 +- php/Middleware/AuthMiddleware.php | 9 + php/Views/vue.blade.php | 38 ++ rollup.vue.js | 45 ++ scss/grocy.scss | 14 +- tsconfig.json | 18 + yarn.lock | 499 +++++++++++++++++- 19 files changed, 749 insertions(+), 20 deletions(-) create mode 100644 js/App.vue create mode 100644 js/main.ts create mode 100644 js/pages/Stock/Overview.vue create mode 100644 js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js create mode 100644 js/router.ts create mode 100644 js/shims-vue.d.ts create mode 100644 php/Views/vue.blade.php create mode 100644 rollup.vue.js create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 6133755e..13f85238 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,5 @@ +/* eslint-disable indent */ +// eslint-disable-next-line no-undef module.exports = { env: { browser: true, @@ -5,14 +7,15 @@ module.exports = { }, extends: [ 'plugin:vue/essential', - "eslint:recommended" + "eslint:recommended", ], parserOptions: { ecmaVersion: 12, sourceType: 'module' }, plugins: [ - 'vue' + 'vue', + '@typescript-eslint', ], globals: { // from vendor.js: @@ -28,5 +31,30 @@ module.exports = { "indent": ["error", "tab"], "brace-style": ["error", "allman", { "allowSingleLine": true }], "semi": ["error", "always", { "omitLastInOneLineBlock": true }], - } -} + }, + overrides: [ + { + files: ["*.ts", "*.tsx"], + parser: '@typescript-eslint/parser', + extends: [ + 'plugin:vue/essential', + "eslint:recommended", + 'plugin:@typescript-eslint/recommended', + ], + parserOptions: { + project: "./tsconfig.json", + } + }, + { + files: ["*.vue"], + parser: "vue-eslint-parser", + parserOptions: { + parser: '@typescript-eslint/parser', + vueFeatures: { + filter: false, + interpolationAsNonHTML: true + } + } + } + ] +}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 73cca8d5..d45276a7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,6 @@ { - "recommendations": [] + "recommendations": [ + "octref.vetur", + "dbaeumer.vscode-eslint" + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6eb0adfc..64ab714e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,4 +10,12 @@ "php-cs-fixer.formatHtml": true, "php-cs-fixer.autoFixBySemicolon": true, "php-cs-fixer.onsave": true, + "eslint.validate": [ + "javascript", + "typescript", + "vue" + ], + "eslint.options": { + "useEslintrc": true, + } } \ No newline at end of file diff --git a/Makefile b/Makefile index cd6adaa1..39813311 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,7 @@ watch: ${SASS} ${SASSFLAGS} --watch ${SASS_INPUT} ${TMPSASS} & \ ${POSTCSS} ${TMPSASS} --config . -o ${SASS_OUTPUT} --watch & \ ${ROLLUP} --watch --no-watch.clearScreen --config ${RFLAGS} & \ + ${ROLLUP} --watch --no-watch-clearScreen --config rollup.vue.js ${RFLAGS} & \ ${PHP} ${RUNFLAGS} & \ trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT & \ wait @@ -141,6 +142,10 @@ css: yarn.lock | $(OBJDIRS) ${SASS} ${SASSFLAGS} ${SASS_INPUT} ${SASS_OUTPUT} ${POSTCSS} --config . ${SASS_OUTPUT} -r +.PHONY=frontend +frontend: + ${ROLLUP} --config rollup.vue.js ${RFLAGS} + # To bundle all resources, there are a few prerequisites: # First, the public output folders need to be created. diff --git a/js/App.vue b/js/App.vue new file mode 100644 index 00000000..154f02ac --- /dev/null +++ b/js/App.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/js/grocy.js b/js/grocy.js index 06258065..935bdbbe 100644 --- a/js/grocy.js +++ b/js/grocy.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ // todo: axe some stuff from here for better tree-shaking. also brr, side effects. import './legacy/vendor'; import { GrocyApi } from './legacy/lib/api'; diff --git a/js/main.ts b/js/main.ts new file mode 100644 index 00000000..398689e4 --- /dev/null +++ b/js/main.ts @@ -0,0 +1,14 @@ +import { createApp } from 'vue'; +import router from './router'; +import App from './App.vue'; + +// PrimeVue components +import PrimeVue from 'primevue/config'; + +const app = createApp(App); + +app.use(PrimeVue); +app.use(router); + + +app.mount("#app"); diff --git a/js/pages/Stock/Overview.vue b/js/pages/Stock/Overview.vue new file mode 100644 index 00000000..64bf5db9 --- /dev/null +++ b/js/pages/Stock/Overview.vue @@ -0,0 +1,11 @@ + + + diff --git a/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js b/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js new file mode 100644 index 00000000..2b553682 --- /dev/null +++ b/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js @@ -0,0 +1,6 @@ +import { openBlock as _openBlock, createBlock as _createBlock } from "vue"; + +export function render(_ctx, _cache, $props, $setup, $data, $options) +{ + return (_openBlock(), _createBlock("p", null, " Hi! ")); +} \ No newline at end of file diff --git a/js/router.ts b/js/router.ts new file mode 100644 index 00000000..affa3a78 --- /dev/null +++ b/js/router.ts @@ -0,0 +1,19 @@ +import { createRouter, createWebHashHistory } from 'vue-router'; +import StockOverview from './pages/Stock/Overview.vue'; +const routes = [ + { + path: '/', + name: 'Home', + component: StockOverview + }, +]; +const router = createRouter({ + history: createWebHashHistory(), + routes +}); +export default router; +//# sourceMappingURL=router.js.map +//# sourceMappingURL=router.js.map +//# sourceMappingURL=router.js.map +//# sourceMappingURL=router.js.map +//# sourceMappingURL=router.js.map \ No newline at end of file diff --git a/js/shims-vue.d.ts b/js/shims-vue.d.ts new file mode 100644 index 00000000..94beb246 --- /dev/null +++ b/js/shims-vue.d.ts @@ -0,0 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/ban-types */ +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent<{}, {}, any>; + export default component; +} \ No newline at end of file diff --git a/package.json b/package.json index 7a255c25..690433ff 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "jquery-serializejson": "^2.9.0", "moment": "^2.27.0", "nosleep.js": "^0.11.0", + "primeicons": "^4.1.0", + "primevue": "^3.5.1", "sass": "^1.35.1", "sprintf-js": "^1.1.2", "startbootstrap-sb-admin": "4.0.0", @@ -43,7 +45,9 @@ "timeago": "^1.6.7", "timeago.js": "^4.0.2", "toastr": "^2.1.4", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "vue": "next", + "vue-router": "4" }, "devDependencies": { "@babel/core": "^7.14.6", @@ -52,6 +56,9 @@ "@rollup/plugin-commonjs": "^19.0.0", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-replace": "^2.4.2", + "@typescript-eslint/eslint-plugin": "^4.28.1", + "@typescript-eslint/parser": "^4.28.1", + "@vue/compiler-sfc": "^3.1.4", "autoprefixer": "^10.2.6", "babel-core": "^6.26.3", "babel-preset-es2015": "^6.24.1", @@ -72,7 +79,12 @@ "postcss-import": "^14.0.2", "rollup": "^2.52.1", "rollup-plugin-postcss": "^4.0.0", + "rollup-plugin-typescript2": "^0.30.0", + "rollup-plugin-vue": "latest", "standard": "^16.0.3", - "uglify-js": "^3.13.10" + "tslib": "^2.3.0", + "typescript": "^4.3.5", + "uglify-js": "^3.13.10", + "vue-eslint-parser": "^7.7.2" } } diff --git a/php/Controllers/SystemController.php b/php/Controllers/SystemController.php index 2018904a..49c38cd1 100644 --- a/php/Controllers/SystemController.php +++ b/php/Controllers/SystemController.php @@ -32,7 +32,9 @@ public function Root(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Htt $demoDataGeneratorService->PopulateDemoData(); } - return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl($this->GetEntryPageRelative())); + // yolo, this is a SPA now. + return $this->render($request, $response, 'vue'); + //return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl($this->GetEntryPageRelative())); } public function __construct(\DI\Container $container) diff --git a/php/Middleware/AuthMiddleware.php b/php/Middleware/AuthMiddleware.php index 3b3d75f0..3b73790d 100644 --- a/php/Middleware/AuthMiddleware.php +++ b/php/Middleware/AuthMiddleware.php @@ -28,6 +28,15 @@ public function __invoke(Request $request, RequestHandler $handler): Response if ($routeName === 'root') { + if (GROCY_MODE === 'dev' || GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease' || GROCY_IS_EMBEDDED_INSTALL || GROCY_DISABLE_AUTH) + { + $sessionService = SessionService::getInstance(); + $user = $sessionService->GetDefaultUser(); + + define('GROCY_AUTHENTICATED', true); + define('GROCY_USER_USERNAME', $user->username); + define('GROCY_USER_PICTURE_FILE_NAME', $user->picture_file_name); + } return $handler->handle($request); } elseif ($routeName === 'login') diff --git a/php/Views/vue.blade.php b/php/Views/vue.blade.php new file mode 100644 index 00000000..e8db5769 --- /dev/null +++ b/php/Views/vue.blade.php @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + grocy + + + + + + +
+ + + + + diff --git a/rollup.vue.js b/rollup.vue.js new file mode 100644 index 00000000..e665f2b5 --- /dev/null +++ b/rollup.vue.js @@ -0,0 +1,45 @@ +/* eslint-disable no-undef */ +import vue from 'rollup-plugin-vue'; +import commonjs from '@rollup/plugin-commonjs'; +import resolve from '@rollup/plugin-node-resolve'; +import babel from '@rollup/plugin-babel'; +import postcss from 'rollup-plugin-postcss'; +import replace from '@rollup/plugin-replace'; +import typescript from 'rollup-plugin-typescript2'; + +import path from 'path'; + + +const env = process.env.NODE_ENV || 'development'; + +export default { + input: [ + 'js/main.ts' + ], + output: { + dir: 'public/dist', + format: 'umd', + name: 'VueGrocy', + sourcemap: true, + }, + plugins: [ + resolve({ browser: true, preferBuiltins: true }), + vue({ + target: "browser" + }), + typescript(), + commonjs(), + babel({ + babelHelpers: 'bundled', + exclude: 'node_modules/**' + }), + postcss({ // will load postcss.config.js + extract: path.resolve('public/dist/app.css'), + minimize: env !== 'development', + }), + replace({ + 'process.env.NODE_ENV': JSON.stringify(env), + preventAssignment: true, + }) + ] +}; diff --git a/scss/grocy.scss b/scss/grocy.scss index 38caedd5..f445c09e 100755 --- a/scss/grocy.scss +++ b/scss/grocy.scss @@ -61,4 +61,16 @@ // end third-party // TODO: does it need to be at the end? -@import "night-mode"; \ No newline at end of file +@import "night-mode"; + +// now comes the fun part, because whatever is set above, primeVue will now override. +.theme-day { + @import 'primevue/resources/themes/saga-green/theme.css'; // this will be the day theme. +} +.theme-night { + @import 'primevue/resources/themes/arya-green/theme.css'; // this will be the night theme. +} + +@import 'primevue/resources/primevue.css'; // core css +@import 'primeicons/primeicons.css'; // icons + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..993a7c85 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "include": [ + "./js/**/*.js", + "./js/**/*.ts", + "./js/**/*.vue", + ], + "compilerOptions": { + "target": "esnext", + "module": "esnext", + // this enables stricter inference for data properties on `this` + "strict": true, + "jsx": "preserve", + "moduleResolution": "node", + "allowJs": true, + "sourceMap": true, + "outDir": "./obj", + }, +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f85adbfa..bfce7b71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -338,7 +338,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.14.5, @babel/parser@npm:^7.14.6, @babel/parser@npm:^7.14.7": +"@babel/parser@npm:^7.12.0, @babel/parser@npm:^7.13.9, @babel/parser@npm:^7.14.5, @babel/parser@npm:^7.14.6, @babel/parser@npm:^7.14.7": version: 7.14.7 resolution: "@babel/parser@npm:7.14.7" bin: @@ -1223,7 +1223,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.14.5, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.12.0, @babel/types@npm:^7.13.0, @babel/types@npm:^7.14.5, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.14.5 resolution: "@babel/types@npm:7.14.5" dependencies: @@ -1502,7 +1502,7 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^4.0.0": +"@rollup/pluginutils@npm:^4.0.0, @rollup/pluginutils@npm:^4.1.0": version: 4.1.0 resolution: "@rollup/pluginutils@npm:4.1.0" dependencies: @@ -1955,7 +1955,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*": +"@types/estree@npm:*, @types/estree@npm:^0.0.48": version: 0.0.48 resolution: "@types/estree@npm:0.0.48" checksum: 1f948e0a7730254ec77f543e4ac2c101f90147d21202d16b22b524e6e3e5d5a770d003fac3e218499d25f5db126f12c774bb16d8993114c7883496d358e1b7af @@ -1997,6 +1997,13 @@ __metadata: languageName: node linkType: hard +"@types/json-schema@npm:^7.0.7": + version: 7.0.7 + resolution: "@types/json-schema@npm:7.0.7" + checksum: b9d2c509fa4e0b82f58e73f5e6ab76c60ff1884ba41bb82f37fb1cece226d4a3e5a62fedf78a43da0005373a6713d9abe61c1e592906402c41c08ad6ab26d52b + languageName: node + linkType: hard + "@types/json5@npm:^0.0.29": version: 0.0.29 resolution: "@types/json5@npm:0.0.29" @@ -2128,6 +2135,209 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:^4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/eslint-plugin@npm:4.28.1" + dependencies: + "@typescript-eslint/experimental-utils": 4.28.1 + "@typescript-eslint/scope-manager": 4.28.1 + debug: ^4.3.1 + functional-red-black-tree: ^1.0.1 + regexpp: ^3.1.0 + semver: ^7.3.5 + tsutils: ^3.21.0 + peerDependencies: + "@typescript-eslint/parser": ^4.0.0 + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 79a794ff44794c3260d0b57f8b9a25e46e87a93eb151c2e2e1c8822c8fb373fe07207edf3c8cd6e21864142214e36d233fc5502d60a2a979eb16e02fce0d9fe6 + languageName: node + linkType: hard + +"@typescript-eslint/experimental-utils@npm:4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/experimental-utils@npm:4.28.1" + dependencies: + "@types/json-schema": ^7.0.7 + "@typescript-eslint/scope-manager": 4.28.1 + "@typescript-eslint/types": 4.28.1 + "@typescript-eslint/typescript-estree": 4.28.1 + eslint-scope: ^5.1.1 + eslint-utils: ^3.0.0 + peerDependencies: + eslint: "*" + checksum: 8e10696825812dee45dc7fd0292eefd527c59886b9fb922dd2da49201da6b1746d4b48abfdc32c33c1489a4eb7409df56c7371c76e5ea723c4e6d99457ec98a0 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:^4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/parser@npm:4.28.1" + dependencies: + "@typescript-eslint/scope-manager": 4.28.1 + "@typescript-eslint/types": 4.28.1 + "@typescript-eslint/typescript-estree": 4.28.1 + debug: ^4.3.1 + peerDependencies: + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: ab7c7fef7477cd1506041cba0ba36ad003c74214f8a96836a742309cc471b12979b622edf98915b0fad45a257466b291ca9924ae5c7b38316a5fd23d1d634070 + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/scope-manager@npm:4.28.1" + dependencies: + "@typescript-eslint/types": 4.28.1 + "@typescript-eslint/visitor-keys": 4.28.1 + checksum: 2c2f4858f497bf01ba878fe5ec6181a1aa43a57750b52bef8b6e3c3ba65ea28f4209f4b794e8dec9b2f7b39c54a830d2004231bae2aa9e9b8d9a1623be4ce917 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/types@npm:4.28.1" + checksum: 8748138865df0a167b6260158fcaa3fc26e3f442065168f762b63e64e7dff065a1175822df0e0ecd0d555cac2756472ae0de64b59ecbaee484be9c4c8409da90 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/typescript-estree@npm:4.28.1" + dependencies: + "@typescript-eslint/types": 4.28.1 + "@typescript-eslint/visitor-keys": 4.28.1 + debug: ^4.3.1 + globby: ^11.0.3 + is-glob: ^4.0.1 + semver: ^7.3.5 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 26dfe831a9d5b915c9c4e9c110c0c815ce3f8a9890e61ed46d187f967d3192047e793d7dc771f35cf6828a9f4b9ddf20f06b4f0418b3346fa11617facd1d1969 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:4.28.1": + version: 4.28.1 + resolution: "@typescript-eslint/visitor-keys@npm:4.28.1" + dependencies: + "@typescript-eslint/types": 4.28.1 + eslint-visitor-keys: ^2.0.0 + checksum: 85c22a0374952a258cfbce3fb1802fc41545f4c97319a057c615b4c6c5cb9efa587e7574adc71d0fa7b35790b5d1d5e17580cc27d57f819627906acf292f810c + languageName: node + linkType: hard + +"@vue/compiler-core@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/compiler-core@npm:3.1.4" + dependencies: + "@babel/parser": ^7.12.0 + "@babel/types": ^7.12.0 + "@vue/shared": 3.1.4 + estree-walker: ^2.0.1 + source-map: ^0.6.1 + checksum: 78b3eaa2811a5c1223c8fa848938d3b29d1ee0c91941058969b0d660be8ac114dc53857054e036dc7a12c35b757b7901182234fc34ab9e441c380266e5cb6917 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/compiler-dom@npm:3.1.4" + dependencies: + "@vue/compiler-core": 3.1.4 + "@vue/shared": 3.1.4 + checksum: 7904fdd56b8766b70ad14dcd8d656b522c629dd1a0873156baf9fb71c998b97c145e85d8707e52d97c69c2eac35f37d28fbf9fdc109ab088b86048f6d9a47951 + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:^3.1.4": + version: 3.1.4 + resolution: "@vue/compiler-sfc@npm:3.1.4" + dependencies: + "@babel/parser": ^7.13.9 + "@babel/types": ^7.13.0 + "@types/estree": ^0.0.48 + "@vue/compiler-core": 3.1.4 + "@vue/compiler-dom": 3.1.4 + "@vue/compiler-ssr": 3.1.4 + "@vue/shared": 3.1.4 + consolidate: ^0.16.0 + estree-walker: ^2.0.1 + hash-sum: ^2.0.0 + lru-cache: ^5.1.1 + magic-string: ^0.25.7 + merge-source-map: ^1.1.0 + postcss: ^8.1.10 + postcss-modules: ^4.0.0 + postcss-selector-parser: ^6.0.4 + source-map: ^0.6.1 + peerDependencies: + vue: 3.1.4 + checksum: 1200de53f0dec13159bc72dbbabc9e3a04160027fd9bfedf6bc1e240eccb2293c1de19bb30dff9c6b8c310a371bb56a72968b4f74cfb5078ca7749b45708bedf + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/compiler-ssr@npm:3.1.4" + dependencies: + "@vue/compiler-dom": 3.1.4 + "@vue/shared": 3.1.4 + checksum: 8db0b0e3c3d7b53621947f0b4916457f121bed0108c75db474561c991fbde696bdd03c32a132a457941a9e821351dd49cf16a7fb99566168d8857db196858fbd + languageName: node + linkType: hard + +"@vue/devtools-api@npm:^6.0.0-beta.14": + version: 6.0.0-beta.15 + resolution: "@vue/devtools-api@npm:6.0.0-beta.15" + checksum: 6e76f02a9aa34cd8712c4897569ebf69f6ddb387e44d5162a98a1b5dc4528c655689eade4453b88f2479ba75e58fb10aa7333c8bb6f4f6e0e9e91fe6f8b780eb + languageName: node + linkType: hard + +"@vue/reactivity@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/reactivity@npm:3.1.4" + dependencies: + "@vue/shared": 3.1.4 + checksum: 6c82d4dbf269a701c4e14bcf74d86a53d7ac871d2ebc142aab6dce85d18b667b378f1e3911eb537e1537cc624841e5d516e84c8adc60f0892e4718c5113fac0b + languageName: node + linkType: hard + +"@vue/runtime-core@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/runtime-core@npm:3.1.4" + dependencies: + "@vue/reactivity": 3.1.4 + "@vue/shared": 3.1.4 + checksum: f2ed8ca9d47b71751505f5ad0010d2cb7e12abfad91f005d79a5bc2997c91b33bf0bf735d0cc7981c3dfc45e9ee2a54d0ebd38fce7c1448006df6c2a3ea836db + languageName: node + linkType: hard + +"@vue/runtime-dom@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/runtime-dom@npm:3.1.4" + dependencies: + "@vue/runtime-core": 3.1.4 + "@vue/shared": 3.1.4 + csstype: ^2.6.8 + checksum: 53dfa4fa52a352ef27c081f15856460b134146512994585513a9d6925ebdf8e612e56e214dc746aa8ef6cd315c582ffc760bbfe7dcb5642eb77cd5fddd5cdd72 + languageName: node + linkType: hard + +"@vue/shared@npm:3.1.4": + version: 3.1.4 + resolution: "@vue/shared@npm:3.1.4" + checksum: 3fb7e5c171d447d7f2a75d045a6a780359200dad2ff7b62ff8d4f66588c871f32822a06c1cc7ff13ff2a3f50f5e872118ed8837a48c22415ac5e152e6cf9717e + languageName: node + linkType: hard + "@yarnpkg/core@npm:^2.4.0": version: 2.4.0 resolution: "@yarnpkg/core@npm:2.4.0" @@ -3276,6 +3486,13 @@ __metadata: languageName: node linkType: hard +"bluebird@npm:^3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 4f2288662f3d4eadbb82d4daa4a7d7976a28fa3c7eb4102c9b4033b03e5be4574ba123ac52a7c103cde4cb7b2d2afc1dbe41817ca15a29ff21ecd258d0286047 + languageName: node + linkType: hard + "boolbase@npm:^1.0.0, boolbase@npm:~1.0.0": version: 1.0.0 resolution: "boolbase@npm:1.0.0" @@ -3952,6 +4169,15 @@ __metadata: languageName: node linkType: hard +"consolidate@npm:^0.16.0": + version: 0.16.0 + resolution: "consolidate@npm:0.16.0" + dependencies: + bluebird: ^3.7.2 + checksum: b75f222ef398c055a1ce17117d3803887db75aa076c3053b4aa1c3d9cded1191026b9848b5eefe492e2d7b1496844aef249a3a902e20b3034963a5b32ed3a105 + languageName: node + linkType: hard + "contains-path@npm:^0.1.0": version: 0.1.0 resolution: "contains-path@npm:0.1.0" @@ -4315,6 +4541,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^2.6.8": + version: 2.6.17 + resolution: "csstype@npm:2.6.17" + checksum: 82128d8c8b515a874eeb734f6ed3a32871180f37c7c5665951bfc9932415f6a15298d10fe7f973553f528a5c6cd478ad5a4bd55f213171b9e08ffe13d58fded7 + languageName: node + linkType: hard + "cwise-compiler@npm:^1.1.2": version: 1.1.3 resolution: "cwise-compiler@npm:1.1.3" @@ -5190,6 +5423,17 @@ __metadata: languageName: node linkType: hard +"eslint-utils@npm:^3.0.0": + version: 3.0.0 + resolution: "eslint-utils@npm:3.0.0" + dependencies: + eslint-visitor-keys: ^2.0.0 + peerDependencies: + eslint: ">=5" + checksum: 035451529f016e28edd26e8951f15e28a6a4e58adff67bd0cb494879f360080750b9c779e46561369aec0657ac2b89dd8b0aa38476e8cdf50e635aa872fa27b6 + languageName: node + linkType: hard + "eslint-visitor-keys@npm:^1.1.0, eslint-visitor-keys@npm:^1.3.0": version: 1.3.0 resolution: "eslint-visitor-keys@npm:1.3.0" @@ -5534,6 +5778,17 @@ __metadata: languageName: node linkType: hard +"find-cache-dir@npm:^3.3.1": + version: 3.3.1 + resolution: "find-cache-dir@npm:3.3.1" + dependencies: + commondir: ^1.0.1 + make-dir: ^3.0.2 + pkg-dir: ^4.1.0 + checksum: b1e23226ee89fba89646aa5f72d084c6d04bb64f6d523c9cb2d57a1b5280fcac39e92fd5be572e2fae8a83aa70bc5b797ce33a826b9a4b92373cc38e66d4aa64 + languageName: node + linkType: hard + "find-parent-dir@npm:^0.3.0": version: 0.3.1 resolution: "find-parent-dir@npm:0.3.1" @@ -5559,6 +5814,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^4.0.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: ^5.0.0 + path-exists: ^4.0.0 + checksum: d612d28e02eaca6cd7128fc9bc9b456e2547a3f9875b2b2ae2dbdc6b8cec52bc2885efcb3ac6c18954e838f4c8e20565d196784b190e1d38565f9dc39aade722 + languageName: node + linkType: hard + "flat-cache@npm:^2.0.1": version: 2.0.1 resolution: "flat-cache@npm:2.0.1" @@ -5643,6 +5908,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:8.1.0": + version: 8.1.0 + resolution: "fs-extra@npm:8.1.0" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^4.0.0 + universalify: ^0.1.0 + checksum: 056a96d4f55ab8728b021e251175a4a6440d1edb5845e6c2e8e010019bde3e63de188a0eb99386c21c71804ca1a571cd6e08f507971f10a2bc4f4f7667720fa4 + languageName: node + linkType: hard + "fs-extra@npm:^9.0.0": version: 9.1.0 resolution: "fs-extra@npm:9.1.0" @@ -5934,7 +6210,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"globby@npm:^11.0.0, globby@npm:^11.0.1": +"globby@npm:^11.0.0, globby@npm:^11.0.1, globby@npm:^11.0.3": version: 11.0.4 resolution: "globby@npm:11.0.4" dependencies: @@ -6020,6 +6296,9 @@ fsevents@~2.3.2: "@rollup/plugin-eslint": ^8.0.1 "@rollup/plugin-node-resolve": ^13.0.0 "@rollup/plugin-replace": ^2.4.2 + "@typescript-eslint/eslint-plugin": ^4.28.1 + "@typescript-eslint/parser": ^4.28.1 + "@vue/compiler-sfc": ^3.1.4 animate.css: ^3.7.2 autoprefixer: ^10.2.6 babel-core: ^6.26.3 @@ -6060,8 +6339,12 @@ fsevents@~2.3.2: postcss: ^8.3.5 postcss-cli: ^8.3.1 postcss-import: ^14.0.2 + primeicons: ^4.1.0 + primevue: ^3.5.1 rollup: ^2.52.1 rollup-plugin-postcss: ^4.0.0 + rollup-plugin-typescript2: ^0.30.0 + rollup-plugin-vue: latest sass: ^1.35.1 sprintf-js: ^1.1.2 standard: ^16.0.3 @@ -6072,8 +6355,13 @@ fsevents@~2.3.2: timeago: ^1.6.7 timeago.js: ^4.0.2 toastr: ^2.1.4 + tslib: ^2.3.0 + typescript: ^4.3.5 uglify-js: ^3.13.10 uuid: ^8.3.2 + vue: next + vue-eslint-parser: ^7.7.2 + vue-router: 4 languageName: unknown linkType: soft @@ -6170,6 +6458,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"hash-sum@npm:^2.0.0": + version: 2.0.0 + resolution: "hash-sum@npm:2.0.0" + checksum: 9d833f05e8b9cb318ff0154c3864b0d2aaf4bd675a4b65377fb4d12eed13505423deb45e85b484affd71da3d3c8b8fe30e83bf81ca9a1dd6f3523043d9281e34 + languageName: node + linkType: hard + "hex-color-regex@npm:^1.1.0": version: 1.1.0 resolution: "hex-color-regex@npm:1.1.0" @@ -7098,6 +7393,18 @@ fsevents@~2.3.2: languageName: node linkType: hard +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.6 + dependenciesMeta: + graceful-fs: + optional: true + checksum: a40b7b64da41c84b0dc7ad753737ba240bb0dc50a94be20ec0b73459707dede69a6f89eb44b4d29e6994ed93ddf8c9b6e57f6b1f09dd707567959880ad6cee7f + languageName: node + linkType: hard + "jsonfile@npm:^6.0.1": version: 6.1.0 resolution: "jsonfile@npm:6.1.0" @@ -7285,6 +7592,15 @@ fsevents@~2.3.2: languageName: node linkType: hard +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: ^4.1.0 + checksum: c58f49d45c8672d0a290dea0ce41fcb27205b3f2d61452ba335ef3b42ad36c10c31b1f061b46d96dd4b81e9a00e8a2897bc124d75623b80a9f6d36b1e754a6b5 + languageName: node + linkType: hard + "lodash.assign@npm:^4.2.0": version: 4.2.0 resolution: "lodash.assign@npm:4.2.0" @@ -7728,7 +8044,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"make-dir@npm:^3.0.0": +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -7783,6 +8099,15 @@ fsevents@~2.3.2: languageName: node linkType: hard +"merge-source-map@npm:^1.1.0": + version: 1.1.0 + resolution: "merge-source-map@npm:1.1.0" + dependencies: + source-map: ^0.6.1 + checksum: fc9701ad15e346905a52e5d2618730ec785e54c7938dd914885ccfea35ec1e34b3ea1a6a893952ad61d3884233bc6f79a0fe3ce20f00becd493b251f8e73aead + languageName: node + linkType: hard + "merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -8492,6 +8817,15 @@ fsevents@~2.3.2: languageName: node linkType: hard +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: ^2.2.0 + checksum: 57f9abef0b29f02ff88c0936a392c9a1fbdd08169e636e0d85b7407c108014d71578c0c6fe93fa49b5bf3857b20d6f16b96389e2b356f7f599d4d2150505844f + languageName: node + linkType: hard + "p-map@npm:2.1.0": version: 2.1.0 resolution: "p-map@npm:2.1.0" @@ -8632,6 +8966,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 6ab15000c5bea4f3e6e6b651983276e27ee42907ea29f5bd68f0d5c425c22f1664ab53c355099723f59b0bfd31aa52d29ea499e1843bf62543e045698f4c77b2 + languageName: node + linkType: hard + "path-is-absolute@npm:^1.0.0, path-is-absolute@npm:^1.0.1": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -8757,6 +9098,15 @@ fsevents@~2.3.2: languageName: node linkType: hard +"pkg-dir@npm:^4.1.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: ^4.0.0 + checksum: 1956ebf3cf5cc36a5d20e93851fcadd5a786774eb08667078561e72e0ab8ace91fc36a028d5305f0bfe7c89f9bf51886e2a3c8cb2c2620accfa3feb8da3c256b + languageName: node + linkType: hard + "pkg-up@npm:^2.0.0": version: 2.0.0 resolution: "pkg-up@npm:2.0.0" @@ -9580,7 +9930,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"postcss@npm:^8.3.5": +"postcss@npm:^8.1.10, postcss@npm:^8.3.5": version: 8.3.5 resolution: "postcss@npm:8.3.5" dependencies: @@ -9619,6 +9969,20 @@ fsevents@~2.3.2: languageName: node linkType: hard +"primeicons@npm:^4.1.0": + version: 4.1.0 + resolution: "primeicons@npm:4.1.0" + checksum: 505e3d9bb5540977186562acf4433f3d88d6e96a7d768bb7cfb05e87f338bd133e2275711463630b2eea4a1a06c43f0fef123ba74c23f84474687c21f0350a60 + languageName: node + linkType: hard + +"primevue@npm:^3.5.1": + version: 3.5.1 + resolution: "primevue@npm:3.5.1" + checksum: cfd8ad6a847fd15a1834ace6181ec8f35bc6f94bb0bb830d39178347861c8f12dd50ba98b2530662cba36882b5e0eedc79408e706f2c24652504b1f45ee665db + languageName: node + linkType: hard + "private@npm:^0.1.6, private@npm:^0.1.8": version: 0.1.8 resolution: "private@npm:0.1.8" @@ -10156,7 +10520,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0": +"resolve@1.20.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0": version: 1.20.0 resolution: "resolve@npm:1.20.0" dependencies: @@ -10166,7 +10530,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.7#builtin, resolve@patch:resolve@^1.10.0#builtin, resolve@patch:resolve@^1.10.1#builtin, resolve@patch:resolve@^1.13.1#builtin, resolve@patch:resolve@^1.14.2#builtin, resolve@patch:resolve@^1.17.0#builtin, resolve@patch:resolve@^1.18.1#builtin, resolve@patch:resolve@^1.19.0#builtin, resolve@patch:resolve@^1.20.0#builtin": +"resolve@patch:resolve@1.20.0#builtin, resolve@patch:resolve@^1.1.7#builtin, resolve@patch:resolve@^1.10.0#builtin, resolve@patch:resolve@^1.10.1#builtin, resolve@patch:resolve@^1.13.1#builtin, resolve@patch:resolve@^1.14.2#builtin, resolve@patch:resolve@^1.17.0#builtin, resolve@patch:resolve@^1.18.1#builtin, resolve@patch:resolve@^1.19.0#builtin, resolve@patch:resolve@^1.20.0#builtin": version: 1.20.0 resolution: "resolve@patch:resolve@npm%3A1.20.0#builtin::version=1.20.0&hash=3388aa" dependencies: @@ -10302,6 +10666,35 @@ fsevents@~2.3.2: languageName: node linkType: hard +"rollup-plugin-typescript2@npm:^0.30.0": + version: 0.30.0 + resolution: "rollup-plugin-typescript2@npm:0.30.0" + dependencies: + "@rollup/pluginutils": ^4.1.0 + find-cache-dir: ^3.3.1 + fs-extra: 8.1.0 + resolve: 1.20.0 + tslib: 2.1.0 + peerDependencies: + rollup: ">=1.26.3" + typescript: ">=2.4.0" + checksum: 3bf7a4d398f53461d6486ddb92f3649e646a6d1f89340090bb242764e9ba09e97d48659ffa9135e3c8b898d9ed3bcca9269e0d6beaef4e44659ae06791f79bea + languageName: node + linkType: hard + +"rollup-plugin-vue@npm:latest": + version: 6.0.0 + resolution: "rollup-plugin-vue@npm:6.0.0" + dependencies: + debug: ^4.1.1 + hash-sum: ^2.0.0 + rollup-pluginutils: ^2.8.2 + peerDependencies: + "@vue/compiler-sfc": "*" + checksum: 3676295e80a5dce8540d9e4fe7ce56fbae92213b85d48d6d4a88b2fd834f54dcb5ff3f04ee70f6b60db8075ce16c4c1fd29de6381604daa6f36608366c01b841 + languageName: node + linkType: hard + "rollup-pluginutils@npm:^2.8.2": version: 2.8.2 resolution: "rollup-pluginutils@npm:2.8.2" @@ -11811,20 +12204,38 @@ fsevents@~2.3.2: languageName: node linkType: hard -"tslib@npm:^1, tslib@npm:^1.10.0, tslib@npm:^1.11.2, tslib@npm:^1.13.0, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:2.1.0": + version: 2.1.0 + resolution: "tslib@npm:2.1.0" + checksum: d8f5bdd067611651c6b846c2388f4dc8ba1f5af124e66105f5263d1ad56da17f4b8c6566887ca2f205c5a9758451871ceca87d5d06087af2dca1699c5e33db69 + languageName: node + linkType: hard + +"tslib@npm:^1, tslib@npm:^1.10.0, tslib@npm:^1.11.2, tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: f44fe7f216946b17d3e3074df3746372703cf24e9127b4c045511456e8e4bf25515fb0a1bb3937676cc305651c5d4fcb6377b0588a4c6a957e748c4c28905d17 languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0": +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0": version: 2.3.0 resolution: "tslib@npm:2.3.0" checksum: 7b4fc9feff0f704743c3760f5d8d708f6417fac6458159e63df3a6b1100f0736e3b99edb9fe370f274ad15160a1f49ff05cb49402534c818ff552c48e38c3e6e languageName: node linkType: hard +"tsutils@npm:^3.21.0": + version: 3.21.0 + resolution: "tsutils@npm:3.21.0" + dependencies: + tslib: ^1.8.1 + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + checksum: a10e746258ca9c8e5cdd5e363259b4e353a6729b432f1b30455b9d84ff3fd2f12a44fedafd13872518b0e951fa8cdf56a5b35908bc91d5bf5e7d342548427f2e + languageName: node + linkType: hard + "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -11908,6 +12319,26 @@ fsevents@~2.3.2: languageName: node linkType: hard +typescript@^4.3.5: + version: 4.3.5 + resolution: "typescript@npm:4.3.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: d9a8e78d72dd19896e6bfa73ab2a0fcea6eca2700d1d6e7c33f67a970af54a3e0fed8f715a8c4e6a0ff7fc0995067b394b2003518ab0aa84cd396377e54b760c + languageName: node + linkType: hard + +"typescript@patch:typescript@^4.3.5#builtin": + version: 4.3.5 + resolution: "typescript@patch:typescript@npm%3A4.3.5#builtin::version=4.3.5&hash=ddfc1b" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 7f0b8343f71ecac18095be1476b398aca420ab60dc207cc1efe078f381eef5527b80a518297720257114cdbda65612f8839e4b63e85dc95e67ac5cbbade8bdf0 + languageName: node + linkType: hard + "uglify-js@npm:^3.13.10": version: 3.13.10 resolution: "uglify-js@npm:3.13.10" @@ -12001,6 +12432,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 420fc6547357782c700d53e9a92506a8e95345b13e97684c8f9ab75237912ec2ebb6af8ac10d4f7406b7b6bd21c58f6c5c0811414fb0b4091b78b4743fa6806e + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.0 resolution: "universalify@npm:2.0.0" @@ -12164,6 +12602,45 @@ fsevents@~2.3.2: languageName: node linkType: hard +"vue-eslint-parser@npm:^7.7.2": + version: 7.7.2 + resolution: "vue-eslint-parser@npm:7.7.2" + dependencies: + debug: ^4.1.1 + eslint-scope: ^5.1.1 + eslint-visitor-keys: ^1.1.0 + espree: ^6.2.1 + esquery: ^1.4.0 + lodash: ^4.17.21 + semver: ^6.3.0 + peerDependencies: + eslint: ">=5.0.0" + checksum: 5a89f6b990a4a66cd585add662cb4985e3ea0fdc6a3007aa056322145a5c054aec7d2c271a2730be69636f5c60ef896fb51f0678f7760945c7a998282da2f1f8 + languageName: node + linkType: hard + +"vue-router@npm:4": + version: 4.0.10 + resolution: "vue-router@npm:4.0.10" + dependencies: + "@vue/devtools-api": ^6.0.0-beta.14 + peerDependencies: + vue: ^3.0.0 + checksum: c6b43db0ef1fa5aa1f55d68fb675ab32fd48665fd96336bc136f900b4e525662a4f78f5f34c70ac912083322c7a173fac83e70db300a55d6dd8b4bbb17e753e1 + languageName: node + linkType: hard + +"vue@npm:next": + version: 3.1.4 + resolution: "vue@npm:3.1.4" + dependencies: + "@vue/compiler-dom": 3.1.4 + "@vue/runtime-dom": 3.1.4 + "@vue/shared": 3.1.4 + checksum: 5b256d788c12cbd74ba3ca7a83456696cbacc2b7ef02e54a9f587c7bf5c42d7892e8d896aac96554056263c87f21e956a9636696bbd0843b05a92bc040fe3ffa + languageName: node + linkType: hard + "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" From cb57a49e1c9c8e974aabc0d246e4918c3cbe34f4 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Fri, 2 Jul 2021 23:27:36 +0200 Subject: [PATCH 03/16] Remove build artifact --- .../Overview.vue?vue&type=template&id=31afdab6&lang.js | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js diff --git a/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js b/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js deleted file mode 100644 index 2b553682..00000000 --- a/js/pages/Stock/Overview.vue?vue&type=template&id=31afdab6&lang.js +++ /dev/null @@ -1,6 +0,0 @@ -import { openBlock as _openBlock, createBlock as _createBlock } from "vue"; - -export function render(_ctx, _cache, $props, $setup, $data, $options) -{ - return (_openBlock(), _createBlock("p", null, " Hi! ")); -} \ No newline at end of file From ba9dd1f35396e741506837030ccf599fa2e6b1bf Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Sat, 3 Jul 2021 18:20:04 +0200 Subject: [PATCH 04/16] Basic grocy template --- Makefile | 31 +++- artwork/not-grocy-user-white.afdesign | Bin 0 -> 20387 bytes artwork/not-grocy-user-white.svg | 7 + artwork/not-grocy-white-stencil.afdesign | Bin 0 -> 50594 bytes artwork/not-grocy-white-stencil.png | Bin 0 -> 49935 bytes artwork/not-grocy-white-stencil.svg | 12 ++ js/App.vue | 206 ++++++++++++++++++++++- js/components/App/Config.vue | 139 +++++++++++++++ js/components/App/Footer.vue | 15 ++ js/components/App/Menu.vue | 26 +++ js/components/App/Profile.vue | 41 +++++ js/components/App/QuickUserSettings.vue | 131 ++++++++++++++ js/components/App/Submenu.vue | 83 +++++++++ js/components/App/TopBar.vue | 78 +++++++++ js/components/App/UserSettingsMenu.vue | 37 ++++ js/customTypes.d.ts | 6 + js/lib/breakpoints.ts | 34 ++++ js/main.ts | 16 ++ js/router.ts | 7 +- js/shims-vue.d.ts | 5 + js/types/Menu.ts | 11 ++ package.json | 5 +- patches/primevue.diff | 26 +++ php/Views/vue.blade.php | 2 +- public/img/not-grocy-user-white.svg | 7 + public/img/not-grocy-white-stencil.svg | 12 ++ public/img/not-grocy-white.svg | 12 ++ scss/_main.scss | 2 +- scss/grocy.scss | 79 +++++---- scss/sigma/.gitignore | 2 + scss/sigma/_overrides.scss | 140 +++++++++++++++ scss/sigma/_variables.scss | 47 ++++++ scss/sigma/flags/flag_placeholder.png | Bin 0 -> 96 bytes scss/sigma/flags/flags.css | 1 + scss/sigma/flags/flags_responsive.png | Bin 0 -> 55194 bytes scss/sigma/layout.scss | 33 ++++ scss/sigma/sass/_config.scss | 151 +++++++++++++++++ scss/sigma/sass/_content.scss | 4 + scss/sigma/sass/_dashboard.scss | 186 ++++++++++++++++++++ scss/sigma/sass/_footer.scss | 13 ++ scss/sigma/sass/_layout.scss | 14 ++ scss/sigma/sass/_main.scss | 34 ++++ scss/sigma/sass/_menu.scss | 189 +++++++++++++++++++++ scss/sigma/sass/_mixins.scss | 43 +++++ scss/sigma/sass/_profile.scss | 133 +++++++++++++++ scss/sigma/sass/_responsive.scss | 97 +++++++++++ scss/sigma/sass/_sidebar.scss | 32 ++++ scss/sigma/sass/_splash.scss | 47 ++++++ scss/sigma/sass/_topbar.scss | 127 ++++++++++++++ scss/sigma/sass/_typography.scss | 63 +++++++ scss/sigma/sass/_utils.scss | 26 +++ yarn.lock | 35 +++- 52 files changed, 2396 insertions(+), 51 deletions(-) create mode 100644 artwork/not-grocy-user-white.afdesign create mode 100644 artwork/not-grocy-user-white.svg create mode 100644 artwork/not-grocy-white-stencil.afdesign create mode 100644 artwork/not-grocy-white-stencil.png create mode 100644 artwork/not-grocy-white-stencil.svg create mode 100644 js/components/App/Config.vue create mode 100644 js/components/App/Footer.vue create mode 100644 js/components/App/Menu.vue create mode 100644 js/components/App/Profile.vue create mode 100644 js/components/App/QuickUserSettings.vue create mode 100644 js/components/App/Submenu.vue create mode 100644 js/components/App/TopBar.vue create mode 100644 js/components/App/UserSettingsMenu.vue create mode 100644 js/customTypes.d.ts create mode 100644 js/lib/breakpoints.ts create mode 100644 js/types/Menu.ts create mode 100644 patches/primevue.diff create mode 100644 public/img/not-grocy-user-white.svg create mode 100644 public/img/not-grocy-white-stencil.svg create mode 100644 public/img/not-grocy-white.svg create mode 100644 scss/sigma/.gitignore create mode 100644 scss/sigma/_overrides.scss create mode 100644 scss/sigma/_variables.scss create mode 100755 scss/sigma/flags/flag_placeholder.png create mode 100755 scss/sigma/flags/flags.css create mode 100755 scss/sigma/flags/flags_responsive.png create mode 100644 scss/sigma/layout.scss create mode 100644 scss/sigma/sass/_config.scss create mode 100644 scss/sigma/sass/_content.scss create mode 100644 scss/sigma/sass/_dashboard.scss create mode 100644 scss/sigma/sass/_footer.scss create mode 100644 scss/sigma/sass/_layout.scss create mode 100644 scss/sigma/sass/_main.scss create mode 100644 scss/sigma/sass/_menu.scss create mode 100644 scss/sigma/sass/_mixins.scss create mode 100644 scss/sigma/sass/_profile.scss create mode 100644 scss/sigma/sass/_responsive.scss create mode 100644 scss/sigma/sass/_sidebar.scss create mode 100644 scss/sigma/sass/_splash.scss create mode 100644 scss/sigma/sass/_topbar.scss create mode 100644 scss/sigma/sass/_typography.scss create mode 100644 scss/sigma/sass/_utils.scss diff --git a/Makefile b/Makefile index 39813311..d8bf0453 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,8 @@ UGLIFY_INPUT=public/dist/grocy.js UGLIFY_OUTPUT=public/dist/grocy.min.js OBJDIRS := public/dist public/js public/css public/js/locales TMPSASS=public/dist/grocy.tmp.css +ARTSOURCES := $(wildcard artwork/*.svg) +ARTOBJS := $(addprefix public/img/, $(notdir $(ARTSOURCES))) .DEFAULT_GOAL := build # disable default suffixes @@ -123,8 +125,8 @@ watch: touch ${TMPSASS} ${SASS} ${SASSFLAGS} --watch ${SASS_INPUT} ${TMPSASS} & \ ${POSTCSS} ${TMPSASS} --config . -o ${SASS_OUTPUT} --watch & \ - ${ROLLUP} --watch --no-watch.clearScreen --config ${RFLAGS} & \ - ${ROLLUP} --watch --no-watch-clearScreen --config rollup.vue.js ${RFLAGS} & \ + ${ROLLUP} --watch --no-watch.clearScreen --config rollup.config.js ${RFLAGS} & \ + ${ROLLUP} --watch --no-watch.clearScreen --config rollup.vue.js ${RFLAGS} & \ ${PHP} ${RUNFLAGS} & \ trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT & \ wait @@ -138,10 +140,23 @@ js: yarn.lock | $(OBJDIRS) # build and bundle SASS files. .PHONY=css -css: yarn.lock | $(OBJDIRS) +css: scss/sigma/_day.scss scss/sigma/_night.scss yarn.lock | $(OBJDIRS) ${SASS} ${SASSFLAGS} ${SASS_INPUT} ${SASS_OUTPUT} ${POSTCSS} --config . ${SASS_OUTPUT} -r +# Both themes need to be wrapped for now (makes changing easier...) +# CSS is perfectly fine SCSS with a different file ending. +# So copy files, and .gitignore them, so that scoped @use works. +scss/sigma/_day.scss: node_modules/primevue/resources/themes/saga-green/theme.css + cp node_modules/primevue/resources/themes/saga-green/theme.css scss/sigma/_day.scss + +scss/sigma/_night.scss: node_modules/primevue/resources/themes/arya-green/theme.css + cp node_modules/primevue/resources/themes/arya-green/theme.css scss/sigma/_night.scss + +node_modules/primevue/resources/themes/saga-green/theme.css: yarn.lock + +node_modules/primevue/resources/themes/arya-green/theme.css: yarn.lock + .PHONY=frontend frontend: ${ROLLUP} --config rollup.vue.js ${RFLAGS} @@ -154,8 +169,9 @@ frontend: # # The resources target depends on all i18n copy targets # defined below, which do the actual magic. +# TODO: better dependency expression. .PHONY=resources -resources: public/webfonts public/dist/font public/js/locales/summernote public/js/locales/bootstrap-select public/js/locales/fullcalendar public/js/locales/fullcalendar-core public/js/swagger-ui.js +resources: public/webfonts public/dist/font public/dist/fonts public/js/locales/summernote public/js/locales/bootstrap-select public/js/locales/fullcalendar public/js/locales/fullcalendar-core public/js/swagger-ui.js $(ARTOBJS) public/dist: mkdir -p public/dist @@ -172,6 +188,9 @@ public/webfonts: | yarn.lock $(OBJDIRS) public/dist/font: | yarn.lock $(OBJDIRS) cp -r node_modules/summernote/dist/font public/dist/font +public/dist/fonts: | yarn.lock $(OBJDIRS) + cp -r node_modules/primeicons/fonts public/dist/fonts + public/js/locales/summernote: | yarn.lock $(OBJDIRS) cp -r node_modules/summernote/dist/lang public/js/locales/summernote @@ -188,6 +207,10 @@ public/js/swagger-ui.js: node_modules/swagger-ui-dist/swagger-ui.js | yarn.lock cp -r node_modules/swagger-ui-dist/*.js node_modules/swagger-ui-dist/*.js.map public/js cp -r node_modules/swagger-ui-dist/*.css node_modules/swagger-ui-dist/*.css.map public/css +public/img/%.svg: artwork/%.svg + cp $< $@ + + node_modules/swagger-ui-dist/swagger-ui.js: yarn.lock # This doesn't just generate en.json, but all locale files. diff --git a/artwork/not-grocy-user-white.afdesign b/artwork/not-grocy-user-white.afdesign new file mode 100644 index 0000000000000000000000000000000000000000..5aaae7842cbd8743c069a0ef943fdebe41d5d599 GIT binary patch literal 20387 zcmdqH^;=Y3)Brj`_lR^4LzgrHl0$b%3JB62(lLPINC*reEh!}>AfPCNG>Rh95+fo? zNl3@cJ-*-fJ@=>k58VCCoY-fbwbx#I^*$hifjT9K4CEgYY|5+W@BWtq^k2Kn`9J5% z|God8I}oT)9q0V-nh5wqfWK*QKqv`t=GFG{73`3>0_jd~g9?W??5yZ)Mm&>1G1Mz^ z+n$97$9E$Y^W08bb3Kp~+?hMl)aTWi5W(mvOZ@jHO*AhLY$KEB^LIeoqpP)+yCAoFHZZ{jL`!Pe@OD zBH6^jQuUDCcH2~g;!C1f%bv5EH4-GE#}5JtA2*S4I`>hE335+XOIo4>;lDejE7WN$ zwN1fdud~xQo{%tM*_<7)W`0wDKn?w@xX7u6A5a&Id%7@&C)QA+9Pl-y71EtJPELv) z##8*!TbDYM8&@+{zFZX~tOg4Fjj1i?rnu&g>tig*DKa9t6LYWo`-osAiN)`}wdbic z(~szFb>Dy9ez7wV^d-rJ#iLbH^ZM(?7>9VX-`aQ)ilsn2c}vAa`u3OB(c=ce(648` zca!MO5~t=+TvLZLKPFjP8P=1Zd<7Bh=?~<2$Ft3} zIV@(zSJ@wTdX}t0UB^A-;!_kfD(wnNBqN_~(k4!3 zLoaqsy};(6!7SE_!uK4ol>A<&+W{R?B($o!YB%Oq6p3DDzSoT!qB(AA=aGAV!}Y6e zs|$(-OP9pkbmuG)FD~agm0ZBAHb#>BzduYo^O9A$|$F=*Dkr;Dn|zH*uiQe(>$F(fTzrmT3GweM-EAcdnCNolLA zi~N;WvDoX;urqKOZ5-n%$&n}Mi2V*~hD8`p%Wwt^Y zgu-ee_rPKb?}zYVHlA_LpmZLZ2{B0$77&-f4UIc4U=G3k1b=c~PO*`4a^Kw+p$%73ld?}jE$ZWlN{K0h&RxeXk4Z=W}K96RSJax~?QN{72Y?~MDET~bL1 z7{XMC^;mx#j`~SnfoBz#=D+^e0Bi91z>RSMTESVB`xBqoZvIPkIFXAy824!CI2|6+bbq1Cgr!q zYcIxr5s*49m4&w#-HkaL`du*K`_no;7cDlOe&qXebeanos+YU*I>CT44ly$Aw5RDx zkUL&sAj{Jwh)gIsDNfx|l$-A@4_B-+>;B#MIFy3V^LyCWpU*%1Q{xuZk7D+j#-DD6 zM%O{Xu<}LxhaW#{mG^dgm#%RTqHaoEr)(OLCn9^2KGtAMomIzEAq>|X;C113rN4JH z8mRstLQM^hYfefXIyb+NFcBj=1fkmr2VIGloHny8RWDtvMA+$%K8b1F~lQ^`z0sn3X!g>Vo!p!=n#$rE<2LpZDP<`PK2(?dG@7gStG| zZO>b!;kV`_Sh9*R$iodcH!}W~eaP-%3jdg$Gw&^> zCHaj-Msv)!l|@E&biwrF@pu~l4*SE&)={b`jNs>w5(svS@*EbVrrx~okWbCGePL^!lK$N2Z~-boQa{hw>opv3uw!ZgQ#NNnKlB#V=#+mRWB42I&L$bc}1h zQk^6fsas^f$l|+6pFK`g@`~%{&Ct|Q;K{~RB`GF!ghYn5VDwYAS7o*Q0*mJ8Zm41b z=I{o{dyFo<&i29B$9pwY)Z z?l1q2Y=lbjZGhKwGP%@^*S!83mv7aOV)!>2weLOnJ>}|dI9dpslF@+7-*cr!Ahk`~ zUcgF*a$YQXBel^_@-d5tnGBC*6Rss~sKG|*Q?3=#Z7eycIjF}(ac6xozV%ACk3Nc9 z(n}pNA;ecSFvRv{Jj+S!vg^HsReW#$)7M z8jmJSJ;e;~pX&IuROH7FE>K^I*u5#Ycf2VwP59W9Xq<83B{Uc9uFIO(JYVF@gXf%oBQvlu`xwuQfcQu@>zgN^pFEuLmwYoM^OLb= zUEeI#!u4UTZ7EZ5?r7?$>L=roHK*c|iOZ+9>=h+!*6~5;o&{Cf3ke$ov#RrpALEI5 zi+{(ibCJB(JyrKXDaPPt{gyS9zWKUqe9o!H*Q@ZkS@dL}1dUlr(n0d4w$Sm7d#^V% z`o+M(iUsf1L#38N&&qF>7|P#?>-su+sAeqi^_?nJ`pbCD17v3pPvnp`x;u(iK+K8d-4waHA-*uq(8gt3Z>s+fSAE!Dp({CTTi(#rO0%cc zg$}mK(xk%LlM&s7?1fq z&WAk+(s~PY(Y^egz)AMg9qAw)%Ib;6G?Gg&HQ&1s>!HBloy@%o z3Ywc}jZkxc3A=i<$nL2V*2TYp0{0L~PaPSyNT@#~Ji3ZE)0mu`RNyy#T;IF0v4rnmLRVE) zF+F~zPxz3EyepaUW7sKjk|k^E=(A9&KjD?#&7Xu%_#z9&a@~t{w99F8sy5x`G|Sb8<( zG)IQOzd7z~g(8VkheF8 z^sjo=q^r-i0wBWrqk;hng|Db}zH0xS-76n`px-vxio9~!=SiBJ)RViZGpk3cFq2~b z%Z7$HOQIm#H~2}@6k@zJ4D;Pd(^K>76y?IlP}K=TFFf1VtcG6wG5-2^uHyh>)WY{i zCG5MM0ITY}9@82g_2Zn$^w206lc=`CJ^ZB6Q^uPHM#`5XE(1TwdBw>x-=ht#Ydb_; zDh7{L*n6*(6xDkeTrG9iSrTq-@6u3RCum8@c22F7Smxr}!gQ`8r>sBe>wQYRC<;TN z)v^LrSj>-SPd(_qWM~x=Y^($|k#;#d%hrgzR@Zy??%dlr01>Byv5i{3bzk7dKoPcOy{Ctf&0; zbuB@`FF#+7y4=>)1KJI?EfKML=NA@fXZH=PYm3Br8)bxebPBu%4}BB8A@oM^_hl2h zUBb$ZoEMjw`#j~FWi9E4El25u#BC+z@Ka81B)KbO>79(svZhj{QhPcXGOzED)zBTF zsQod)Es2N+r&$^&PpGNL*s$NVDdAW(n%kMkxxSX1+MvNtnbLVTh)L-$%N`WL)w*Gc zkg&2u%0jZ$WGZ@$J;S|x|3I7;xuLGVRc6)?cc|&dkwS9T7`{sLJxuS7*d1P(fzt+v7K^tmn0W{YT-lTsJqmaRIa^QsFqr85z z3sWrc7$p%<6C9rBt)9i&Ii^pp%X_KjMzgY|p$DD3S z-^$%=ju06qAVik+v$ui@96u7Qr=(ZAB+r{iw)^%Q{)*VOwBIVXwLHU>9rFYs{kfzD z)y3*>H(71G97MG8P+nj4dqMEIPjht<)%YnM@eLeVcf_C0TZFp0EtYi=ZrQ>Yw>UZy zC$|4%bN3{x4So^pJo+yXX51jsayj^0roQ&EcewxDcbV=v?}flii6XI|`1=0-bH~j9 zvgm4p<}vgG=EC5JR_$twQq%y^T1)caaCWmJ#n`{q;R<`}L(CV`_{PT8-)neVFKVs& zUbroWpfN)~in;I^%zA77htSObT=yBC!K&?9j;XJwase{SvRao1!;hCmW+5zUxZ)YC z+n!AkrFC6OtP00gb2|@crNEdt@t7`s+q$nC%@y7x2I-oi7wUeW#A3?x+*0&Skw{bg z(-}{a>Od9#s_kZ;g-=YbqOu@Q~(X47TVYE**;H0EAi=@iB+>a03uoL?O#U$ zZQNsgWKCY)8iDdEfa=aT8}-PP`Xv1Dc!G|zvBp)X&)z+heZ zl0-a224K_k5HD2 z%@+2a^{WGUn-?{Gl>EgBB01m;W)Low<3{FpV#OYGIVH2ho(82HDbvN9mcc@smX*-H z)Yd+$ zEq4oG&()xvBWs=`_&ng4Biz*xanzuh3WT!V4$>X;e}OQW`rta|NZ`Kt++O9SVQ)P3 zDT)_*YY~e|7xLBx(-UVrrQEJH-B!&Q+ljgGn2&D>R$3v@Ul3A>Z@*B}zr+g#_CLPr zl>xI|o681LJ8vY2${X zCAu7%WsRa1gpGyg+YGq`hT%O_WlR%0Qg(IRcBYb8=W92AMx?}tAHgv6tIuAL=#^Z+QHt5@@_);k2!d8NNYcJpYt;(F#mCIfiNIy-~(+ z$~AzOyIn)2g(<20$ZoOUTP{5_VtMrt9EnqRb7DCQQ?tvN~&&)g)t(I?sTvLa;bNt+c4VpK7a)_?MRKbdZZRHv@Cdx*p~ z^>8vj+3Le)y5~QwJO`^cy^GmwsKGa%6HEy$c*TxCWbtbGc0P?a_kL#a-V~f!fW3r@D1dv52cZT(ndg2BpOuQWUgs9iI-Tfp%SPJa+n9P z#dnig)Id~fKpmV8gmnfngN9H2EiFg;hh0RGC-*(IpNZTl9{-SV^Q0@nfgQS<|QkK6T>Pcma4`~z`MD*v4rXqNt$M93gry@EB|Zx4OY9Ny9P)g1pk4Ef|$v=4GAGMNaovEK6JNdFeaBJb;lkfQw!oh8^$zfoRLgxPl= zjw03AOK!ut0@XD)A;UXWi9_d27lW<2^pr1^O$XKZJ+91uaK7VRRF*2!4G4>83(K$?nZ##rKz%u-)!73 z+w5nFlKaKNI%p<>>sWuLb(KtqQt)EH&DljB9uW6}RSo%O4F?~V8eST@P8Lwyx-BuC z39Z!OQat}#6mzXDZ1HnH%#N#fxFc^uoccT@N4rI=P-2m>BJzRzy6CMBq65CV9~K|L zA}f)J23kk69AQVPDP2^tFybT_U&I%!MFNG8IB|fKjy85 zEQeX5(Nd9O!()F-?0Z&0D+O=mKTy$1V`VkN(Pofbq0D~mS5xr7mm`Q|Z>}ldZD(fM z7rui0<9=eo{0#PF*(E@{i+AO$PUVIKUh2XRix!=iY`Gs~drB3(VliXJvU^lp)L!7s zsr6aVl||z2hd)ZrUO8f$PwmxPsRKf?n%xi&N<~sbc;=jggKs0-6Q4R{9hnk7a4wTr zQga&a1%R}io?0!o4`#NB6#P7Cjx;cTDA50k6qfd~0`W85eeH$cs82(GyNiT`srDUM zEOliCov$@!_88^!tzs*f(dv4G=%qC`q|Z`sOrmr7A!oOE)Qk~TI4_36i zOglOAu3{Q_I$5JDJG}J#b6-*?Vjt1yF(725F@N-k`o+_1b0~I|tXO_N zXVku;r^fadsB1j3s&stT^~kDef9TNR+hh7~R6g9&MIG?(;05BU#+on@a;1UrIq~>7%wX`_z*=g zpE>AsZ$+`aLt`;@a- z-|xhj?WA~#Qn$Xw$CCQ$q&nq+b!>(AUerE#<@q<61+Ug^qF_d9E!aCGpnJWToh!A3=BiFH+5Xw!p#kb3`~isxXvoX?#;&5_KU^_*W8 zLHxLBUy97v3M#>ox171D2tec2wv+*OZ2xQjx1Ia{wcQK;UpVRi&XE6Zb^o_-*ZF@g z{%?yoC{PpvAp`mXJAQp61>Fm96~A{k^xueUX_vrf&OnlnGd)IKQ=Yip{8J^ z0G?6n=_1YlXHOXdBKxAox4xGpXb!|gHAew=He{ik{4I2o=4bnrZTSgS`EJe28 zv5v)^?Y?sR@Rw@NIqA_HGrR8%Iw%PRS*2HP<_%(usBni(9aV{Cwc>`c;!>`qaPG8K zRmyYo4LG<+=sJIp^4%Zc&wsf;XZz0Qd5mVi0P^6sCtIq=k=&G{QM*fRpH+S>?|L-8 zDP`A< z=83JjydkJ6Ar;MmT0*@ooLw%uigt1icV=>)6^r{bDbwoD*4%*&!Rk9({k0>1p)b_9 ziN5x@_)mbN2)kHj z@*t7gW;a?FtB#e#ewI_E1=ILCUSvHwdvgf6$zn6p!wLEW@+bXcEOcu&NEx#}0)W?{ z=V&8T_at#P2Z8ufEN5tZ|4Jz}*84!FPa1n4TY#Mws_u}5X1Cwx2bpIe2aK`N37?@{ zJfDAk7F}h-2szX5F$a?D`Qc`A%q`RPCo>8_x(u=nhbKf&AaN|GGJ9iYg@~01c@SIC zv#k-$Lz8T#{c+K4`}xb=6Mx4G8PyZsW{EkDIhHxEwr7YHW(LlP|*{OicT2BVsa*jZVq+w1`6JkoWDHM$NgMG z++-^T%7OG}g~q%BkRm$@ zt4Xh>L$kCcMPM!T=Qy`V@eF+jE@$a=xNVK2q_=_}WI=$&FsK^y#&_ zRB2RmO-^3t_2&q^rN`(i`08i)>UXpa)87*{IT~{MJq_d zXFmV1tKoGqLM}4EoYH!3YhbG4Qqk98Z;%5_@YO7s|M^$gzAy6fKCD8bTsQ=WWK{)KBG_XA&X&upZ$9F1pNrU+6-SklH9Ao`#tW7`~DQZ zN(yU@D(!9X;9X^d?c)Pw)G(HsNOn$`leho!-wqzQcskldr|C*{2JWJc?2&`x5!&{= zLD9%dGi1+;V_7{6KN7i32ir|UhrpX{o;9b#_B+rD!b83z@zL;%;vNaOf6=d9f5n3Z z)Gl&B@;|cH;m!VppCk5v*QKA{a4v@xUC6XoY}x&y)4}htq8r!R0rJpIBdTX z=6?x`%)({BSN&j;?LT5uQ+m|)oU{j)va+kr@}HnB;3jpa7rBUWrx4e3Byv$|bv)h( zS9?Wk#;Ig``ikLX!MSx*2CartXCp^+>pm99MP}Hubo7#sQ#Qg&4K87PaRS@7`55mQHT2XK^)trjagV}YIxOfr zS?}LEw9PeGF9$4o23Xxm5YllwJosa;&3M`d`m~qVWp`m#1_=u?MaIMrtPt9=n3(_E zzKBBT-D2!6CDf)~xnH_pmD($H?a_{JIS0aa~dh~X5%YZ5u| z1r`*UrqaotW%4RIyldh`?Z{xF%JkxZPOCWFI0K#ZkCAnnSzDi-u2nLe7PQL2ml;&f z!r=$)#+vFTAcvpq?AubqdnlO;XRR4ux`5_PhAcZjbO0&>$j9-VCja-J7VEf+#2jeA>%Pe~j(GgJty<3)R zicl^}CKCP07OmwiLc)kZe@DtXhl z`-^n6YJKdT!V>hH0Q}vx$^h!Lk!c74zQ`%4cg5JPvAC-xKKV5nkl-){E@kGdP7+S~ zvj@X+jBzTAke=QrT3WKHEN6;E-4NSxK zj~8b7^cGS^MDQjV$I1AoIs?s*qTiDW9e7SW(^hG|YstpDnuCsT+u6b({P^L=Y3O*S zo#H=CS=s+&{9Q-0(O)JQsrCTFmrv-4bM)y_i+LI1NE0wrkzJkRk%TwAtFZu3Wh&a_ z)#-ACAbgpLksMQX4wpE6-B>MgioUEm;d)inJ@5wGo`jc zi4(K~@t@3v+2_Z|fvhk7S|iO`1O6S&1{i*Gq$oND5n!K+hr=Dw=xlGNq6MZ%k(Hsm zWq~c;)p*Jp2%;_;wl9)pz1g|Z0Bg0_@v*poyX>M*hZ$EyXmti2`8YjA80DZ9#1|U> zs{Vb5PXWe}g3msVuy21VW2`e^1q<@r@yTBAU*%^l9_g4Ti0vga7R7>3Pq5Nruj)AU zgop!~_N<&;*^D3oBzQmM_OwQ{d5s}RMiACYcglz3RkN?Qtw{(;_-n3TWan*hE63c9 zxR}O)5=m(!P80_|wNbcVl^_Y8a7H=?9!24-QBXVIa~>QUmL9;5e@%bNxo2sx!$Eb) z8W>@)qgQeRJdwFg6GIc>lj_$5{Y2DcC}h+z*AyjO-JWnM-^=dlGw|yUR^p*Ol&>xp z@H6O$*VV)h(*88Gkp&7_Ohl#EVHusLj!_8sviZdV!s`XHX+2Cc)oBI>^WmL^wXCiX zrJzfASEoF8*G4wb3i2u8&=2Tuc9kvl#`7y7#%EoZtoqSgWJJ+yk7uq3ekg(jMo^Z1R&4AVP4&5e-lLP{4JnQ z&FyF%K%(BQ^Gl<8+vH-eggS}?J#oS&%Sz^E(G_Z? z{HmPbwsyXo(XrSx^nn8#iQQ!Uwn%w8zFsrM1|Tak`HC;zkO`CxEHEKN35~0(6NnZK z-c*8ip~l{=x4iBFFzILovQy0iicPs&T8@{$f_e+n2iNLQam-8oket5wWmYRk53>52a~Ugewh{jeawkT*`p`5XO!lTf-%~RGuT5Ce zR%DB?G}zRL6hbIIw#Gx1p_fJgfUP5U2E5}fBhCYrp)~;hH&`JsnCDodhahI7w+h_tM)02}SBMRzr|wzkLENllKKH&YkwktjJt{3uTZ%N+Cla z#~l$b1qcx`WOYRXdhyhg zGlH(GR_)tl9$uj3mD(Fp(6f^&wfjIWcjqQ!gnWxZ!mAooTTzX4UAq!WgrfZo+nGb! zEQ6>0xez}{xGD0SKo)qfT>LhqhSA$TDl2DFW{=@u#NwMHWy~BfCqNYMM2MY@x}Rw5 zJWJ0dOf`N;_y%BMI%^9?CcLU6qY2+ws)3p?dWO^DT(RoJ3wd!LiPQ#Vbnu*%JvF9G z7T6U4W})=gNDWcmo;ognT^hmgy~u!Xha#ph*o1I0iNCos>D$OROJrkPwsULLgDB^G zTNjwnW*|D4>ai%DN) zZ534gz(zJ{)@QQJlpA1E7_60PZ;SX%7dQetO{n+f2&CWhEI*EKtE=Sc_(Y^u4VeAN z@>fYcv~Q-KHpz$xRdZXD^i-NMHS;$UXWDr{%4TFuM0?FzQb|$vg>UD~{b<4R7XFHx zosCqr*8n+te<5UjEI-09_eXTt|liJOG^RlFCePh$rU;*|E_(WA%D0q zOC$w06@j~`9_assQig^2>2Kf@cMK{Op@Om+_iyZdLeISc0@fS+Any;bweJMXJBzMZ zFF@A?650%8P4HBD+kq@e+ zz5uD3%vTzG2C5svbML0|d`73i26G${5;z%@wmYOut_O0e_Fd(jRab1Cm`?&F#VZ02 z4wbm$1HA2t7=O1$Yko|*p>pupXOhkk>cQYk;Kse3A)2TK0)={FtOvzO5MxVSAeXsF zAw6W5N1#dTHTTnR;#PhZ^Z*+w6&WLZ`i`AIpDGR0@7LupVBG*IgDKQIUlfA)L9EjO zrcHaaYaugFWlXFlO!f;20fNcXP`q%6|Q7=MxberZvQ z6CaLu(rhm-2IVNgBZit^v#I@cgOr^qmLB}dUO~pCU`Pb2Wr!DzyDQW{!&wbb{SbgX zXXj)s)_up;pw6nCQjxhwmJ1wf_d(T+t$(PCEo=SKZvr&hBDb|v&cizsmz34wqA>$u}^yE|9+Fc8;Js@~Pi6o>yd_9+NS)klwdckVJ4=U_% z@7*`U`cQ^R6E7B;N7XxXna_$>IzT{yGJ!d=U>PO=CiT|n#gh|}xnXVGDj=P*ixkRc zEmu#oYSzOf<{k|J=4pME0sEQZ&yPsl!TVU<67NqWU1fptsyg^I{WgI4wqD7MwBTi% zt_CGR`{cQ{{}v{R%1+16VTiX-yz)vL@pSyFg&Iur>=*!r`&@qtC3o-T=#L z{|3CfTlP#^>9^*lgT9xnTTSP+y}#Hd;)@ApAn5enq3wAS$dziLf5P}jvwq{z!*B7l zEhgVpWcdHREC5yl=-Ph2W6*KOusa0It$G#T<$S@)%MV&;ayR=T!T85L<-kP8+|L9m z&-CX9(;c$fc_2XbWrq}-c4g>{D1oYiUX>nvb68QIZ&UmaDH|7Qn9$-d!x}4SQXv}L zmHg5Q98RiY*AkmfP7iup*j&+d2? z8G_o4V6F7sr=JrsOQ6&d6RpWP1EEIJKh?gIZ|L{>(1K_4(*Tm+)$HJ&Cogxif1#8T zWQhSh?m~f1eaGRTCtw;tcu4?5Eb$XL#(6~ilz!^kK z7hpG>#E47FwiQv<5FNYgy7sP+vc`FO1&X-_XD+ZPuLLBA-&o%Z8p-N>r_cYGKR)k7 z5Ex^ONXIc)A$# zg%^igb0oGJ|D{Jj{6@1@6u)uM%a-6nal%7As5#zHb(P>+__>QHAM|@L475D0u6hE_ z2%X57iXK{9K&o z3uhdMJk+CnIZmX3*8_C{`R4`*T_hEZKIg)c;3fgR)ch;+^Yfq$sHZoOiqcSe|eLOb3FsDlLhEdR_vvQ1_Y{2sNX{EscBJ5ms5GR73Vq zMffqrdM!UeJY&DTpokkFn+56H+kgx$j{AJMa2BxEDrYX%rPZ8Ci)y8fwY1-8g=-2O z4R{%eXy~K+6Q5((LO_VOa~cOp<{Me_qzSoz3g}I$ zEkSz3st+|bo+#QvCmcp*-~2YfRDmz-nB?Ps2kNT8j|I45Hy&GJ5LfNf*m9w2k2k+x ze7FN@Kz&u(Ho;W!+yu6vLn}TIIj0nZLZ#Yy^d?a+1Xg&Jl=$Da5ph?`P%ly}E?Ygr zAgBi#u{(E4vnTKKl-0D*^dR)?6*FQowhd>=v8=(}9|RQtSYxW*MP2X8d*V|SS_qQS zWIF-Lhs`$wRkko__qkIIpSe#l@g7|u$C;%9Q0SbG3NG(?^yW|3UtpkiAXZ;oxxa!z z9DTfoTgaKToaLBRqy}o(;s3FsVc zgPq2u55`L?Dp3dez+L1IAZSgWAs#*RVMgfi{riWzGgHM7?&?G3y~aXGU5sVlsB8e< zG?EC!4Sz!9PZHM5kqQ*bY{=P*%r|>ZKd;w60mtm0nqK)RH8Cl(m>U~`#UxWJ>fM9XRnaY4NXksYOXASH@g*qy{jMh zILP~cdJjdx{`Ge*Jwo2`Ovx_Oh!eag1({mNb9($ns z4mU!tu+m~g(?_2{TO9_zTb{BO#+%nziafMX={5Lq|guE;T?On2~xH zjsl=`pqk@D3eY1;bXIy~xTE%}=7d~L--{!X2=`^7RTtCF5f1Hqo+CFyb1ypT8A#?2 zX;ag`6_nr^IjJZ0CY55-)B}}*nuuqnXpopHzX-;zC(8GiqLToLD9>R-@pZUnA-vca zq4I~ZM}#zW1StI*N@;XD7ooSAO^p>>LpalsWP%u`>c69a(DRPDRV~w1w_&M-0;=>-APf#~;7FBm z?o;c9R&hpBDJTMuHSSY-liuJGs&<0~$?X*YJb}_-bFtM}O)T$Pn=Ge*z!%bhV?`2$ zy0gxzor?>e<;%O+`FLbZX3Xm36JYjH?!3SX(Fc+QYU_K=`w~9GT6y>6aP-(mD9zEw z?{Uwms^80j0`!Ea{qbDMC7^9e2a=xhFi~?8!|Z2!62$K)L|N}UO2Lo8D#Sp_#M>ed z#V=>tg{kaqJU3m!Q3X?!?JEbe@`J9eQ52{Wy`y!Q63ycPDihu4Q`SJqy;SUQ0=*=8 z=5y57g!>)Pg66(BG%p;y3!f#9i2NB?Rrm*rr%({9w%oq{(%lw9ThbI4Ot^g=GdPy!@p-HY$$8E5D<<=_9-BP-@| z^%lS?#jsm@!$Q?X-q*SHgc?cSX_ZFVe7R3Bck6_7*5r`HDqoe+qlT8S@B{iR8S*?( ze3tp^Mt3@+)gY-ozk><~uWP>Wm5@J;93!zO$oHHoFHW0mw9O^&#_KO&6!y4~Y@pz}Xp^ zD(WN?5NqnF4_MKjjL!-Pm7cwE*ps&xs#Zb~?38HgT7D%ojJmax28jN%5tP`-wcLV9 z1qz(O_}4I}62nrq+@a|Z>`hIlOLCpP{J`a|KLXNB-wpjz^YinVpNbER-^By9#3t%4D9rD>5&v=Rn#I z0eVtQ(POGaR_B%h?mP13|K>3I}=Bq zohbJ7qTS~?$u}tEeOz-vGL+Kz*J14DsqQIHM4LHAd}W^XJxWmW9)@>#LifTul03iJ z@PX1PTli5!!_wHdP!YugglUI<`P54v=KiX%?SE5e#c}xz^K{%J?pF%DCkAC{&2-;T z`q6$GFf<3eYmtx+Ox3RCc_j5{RPgF*m|A+$Fw576jXw%O%47NXKAE7~^38poW7nJc zGl+CG!{z$f9v0;W8G&wNXazPd^DXP#G}*x@_BL`+ zTQNPVW`tO;w1ERHlX$CW))|R}4=WE0Xcg`Y2Hxr~PAtl8H3Aj#6ltwhCRUfT0$I^E z)i!k7Mv2agC>)YnsMYA|&e|%qYfxw<|70oFu_GzJ@($|Co0KOV7l|9nnVh2` z^ydqJ#$B)mt%8q}w-~T@VXb9h+#!2XxFdiu+I!5AM+6(TEuyj@;z-!=9L*2U7h*(D z1Xe7gn+pi4%n`0THdT>@B!75JcrrTlbU~c_&*(he)TpQJQAysUk_Jl9;Vd2TDjqx9 z;%sx)3q?j(z-Ch_81sJK(A5kj3OrPM4h|z-CanTo+uuXcy`%;8(7W3J;ndhW00HY> z9=L2OS4o^t!U^EsS^H>5j|judNCZfzni|^)c&>EZGpMRHAVH_}$(?>?3yZVEky|V4 z{OF29qGgSA?-oqTeYB$;aoH0qbStthvKo1-pdpl>*Kaz)cdz6`={t9wzFl z!AKa-2mHWoz(cF+@+vT&enl(13*!cYRmwddY_scz6ZN<5e_Hi>vWgG1g^CA)4w*!v zV$kJXB)gq-rn~TJC6++JZYs^_g_Hn!+_=j-K%WaZAvwrXsL9k5E!MtIfYnq5oPrP0 z#Ny=&_2siQMPmB4G8CR`U!eS2gHP#1sUe(UE)-XRJc83e=1a&@!>46;V5~Xf%ljaEo2HBijsh=@HOjX zCjSY-o##vzE+mBdt^U&|{sYI)|Eb_w3HAgfp*JlTGb=9$bvXAIg6pb4!23i|U0xi;L7o7E_9H+&7knhw;sFof03sPU_Fcl9Hx3 zc3_Xz>b^ggt|JG&c~grI=}U)sjz{W2T|?ungYjeePfJeq^0>_C9Cr zwf0%x@BVUOPpg@MKV~mQzl`yLED3hCl}ts5Ad$87_`3KA+YalylJ+l%@y&4w*;>_p}IhZgeKgn5ok{XHhW$y+3ebG-`0W(_{>zHgcq zq$aSZ!^2^nQk!6Ste?;&1e-}H_6I=-IcJTiV*p&hcgdL;oALcd2x)1i2;6ZER5!eg@w@h{%k)Z zYa|P0p+$&A&?6)ImQR}I`io}rZI4o9-_4Fq-KADZK}7WzWtdK**RJvH*Dl;Lbkk8- z&UO{_aPxU{_y8)asvrT{7#xxMqm1t_sjy^o&G_Pa;+DN`aCb(AD_jKq05ujJZ+c>> z+Y&`0wQA?34-@Gbiv4IAlt{6+D`2#)RDdVDFuD)}_oTQXwc_AA`bx)XJ$*@uM23z_-BWsAf7Mm8hfw*8eYYiC!=LdUkp z&!>K=boh#ERSdR)anrSE74CYIA>?^Z+_JBBvqk7vu*EfClzsQY;qMNW2Mryyp z)R@VO1N^w(>|kYutLm>nsyAWLB&fZ{`#(_TcQ&ogo(XMurK|T=cPPTsBr6?K);(6v z9d_Yyr!AdLsyklYJ>E;GOk_f*2MiA%G7;Z)sijE#gW|JGV-{_9S7vRBwL%-1O;Y^DGkL8IQl_8y+aSf9qC~<*69BVOeL{P7T{}z>Epq zxwas8uSzmE6|&q^!{=CThBp?%bW-b+mY#}*XM0X-dPR6@>Iqy`NzA+(+bdh!Y^rX9 zCSQHgkj;?7!}Y}T8XdTD;;WVwp>uOS2k_m;!KB9>lWi|lde~`zJMvQ%IT2^H&~IxW zV$YDc0YO**M6S<(xf8Lxe-I`ZJxhfeU0-Bn(@vN-*`vnIb;$Ynb}RaTC{r@%-$W!s zjnNKT^hxn@i57xay#Paf^1^d8gtf#B68G|FY{DId^iwhPVv|$RQ9LhPjY$WOdN;Hr zE#3SpygnsRlDMv!L}#nKSR46se+}?;)3s2$s)E;WKPE>F)#kT<#)<-|3%MHt$
-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
+
+
+    
+        
+    
+
diff --git a/artwork/not-grocy-white-stencil.afdesign b/artwork/not-grocy-white-stencil.afdesign
new file mode 100644
index 0000000000000000000000000000000000000000..e4ef18bbf471ee6aa2256edb7615f4f1649bdd96
GIT binary patch
literal 50594
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

literal 0
HcmV?d00001

diff --git a/artwork/not-grocy-white-stencil.png b/artwork/not-grocy-white-stencil.png
new file mode 100644
index 0000000000000000000000000000000000000000..5832752aa4fb1b80759cae8a352dfeae593af0b5
GIT binary patch
literal 49935
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 literal 0 HcmV?d00001 diff --git a/artwork/not-grocy-white-stencil.svg b/artwork/not-grocy-white-stencil.svg new file mode 100644 index 00000000..5b7fee51 --- /dev/null +++ b/artwork/not-grocy-white-stencil.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/js/App.vue b/js/App.vue index 154f02ac..1f4e9ac1 100644 --- a/js/App.vue +++ b/js/App.vue @@ -1,14 +1,216 @@ \ No newline at end of file diff --git a/js/components/App/Config.vue b/js/components/App/Config.vue new file mode 100644 index 00000000..955c1e42 --- /dev/null +++ b/js/components/App/Config.vue @@ -0,0 +1,139 @@ + + \ No newline at end of file diff --git a/js/components/App/Footer.vue b/js/components/App/Footer.vue new file mode 100644 index 00000000..8dc1561d --- /dev/null +++ b/js/components/App/Footer.vue @@ -0,0 +1,15 @@ + + + \ No newline at end of file diff --git a/js/components/App/Menu.vue b/js/components/App/Menu.vue new file mode 100644 index 00000000..6de9622e --- /dev/null +++ b/js/components/App/Menu.vue @@ -0,0 +1,26 @@ + + + \ No newline at end of file diff --git a/js/components/App/Profile.vue b/js/components/App/Profile.vue new file mode 100644 index 00000000..28bcc290 --- /dev/null +++ b/js/components/App/Profile.vue @@ -0,0 +1,41 @@ + + + \ No newline at end of file diff --git a/js/components/App/QuickUserSettings.vue b/js/components/App/QuickUserSettings.vue new file mode 100644 index 00000000..36c04523 --- /dev/null +++ b/js/components/App/QuickUserSettings.vue @@ -0,0 +1,131 @@ + + + diff --git a/js/components/App/Submenu.vue b/js/components/App/Submenu.vue new file mode 100644 index 00000000..00d31eb2 --- /dev/null +++ b/js/components/App/Submenu.vue @@ -0,0 +1,83 @@ + + + \ No newline at end of file diff --git a/js/components/App/TopBar.vue b/js/components/App/TopBar.vue new file mode 100644 index 00000000..7c3d9658 --- /dev/null +++ b/js/components/App/TopBar.vue @@ -0,0 +1,78 @@ + + + diff --git a/js/components/App/UserSettingsMenu.vue b/js/components/App/UserSettingsMenu.vue new file mode 100644 index 00000000..e4a2f773 --- /dev/null +++ b/js/components/App/UserSettingsMenu.vue @@ -0,0 +1,37 @@ + + + \ No newline at end of file diff --git a/js/customTypes.d.ts b/js/customTypes.d.ts new file mode 100644 index 00000000..8a45ac8d --- /dev/null +++ b/js/customTypes.d.ts @@ -0,0 +1,6 @@ +/*declare module '@vue/runtime-core' { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface ComponentCustomProperties { + + } +}*/ \ No newline at end of file diff --git a/js/lib/breakpoints.ts b/js/lib/breakpoints.ts new file mode 100644 index 00000000..3a7fdab7 --- /dev/null +++ b/js/lib/breakpoints.ts @@ -0,0 +1,34 @@ +import { computed, onMounted, onUnmounted, ref, ComputedRef } from "vue"; + +interface ViewportSize { width: ComputedRef, height: ComputedRef, breakpoint: ComputedRef<"xs" | "md" | "lg" | "unknown"> } + +export default function () : ViewportSize +{ + const windowWidth = ref(window.innerWidth); + const windowHeight = ref(window.innerHeight); + + const sizeChanged = () => + { + windowWidth.value = window.innerWidth; + windowHeight.value = window.innerHeight; + }; + + onMounted(() => window.addEventListener('resize', sizeChanged)); + onUnmounted(() => window.removeEventListener('resize', sizeChanged)); + + // TODO: check breakpoints + const breakpoint = computed(() => + { + if (windowWidth.value < 550) return 'xs'; + if (windowWidth.value > 549 && windowWidth.value < 1200) return 'md'; + if (windowWidth.value > 1199) return 'lg'; + return 'unknown'; + }); + + const width = computed(() => windowWidth.value); + const height = computed(() => windowHeight.value); + + return { width, height, breakpoint }; +} + +export { ViewportSize }; \ No newline at end of file diff --git a/js/main.ts b/js/main.ts index 398689e4..3743e30f 100644 --- a/js/main.ts +++ b/js/main.ts @@ -5,10 +5,26 @@ import App from './App.vue'; // PrimeVue components import PrimeVue from 'primevue/config'; +import ToastService from 'primevue/toastservice'; + +// some globally registered components +import InputText from 'primevue/inputtext'; +import RadioButton from 'primevue/radiobutton'; +import ToggleButton from 'primevue/togglebutton'; +import InputSwitch from 'primevue/inputswitch'; +import CheckBox from 'primevue/checkbox'; + const app = createApp(App); app.use(PrimeVue); +app.use(ToastService); app.use(router); +app.component('InputText', InputText); +app.component('RadioButton', RadioButton); +app.component('ToggleButton', ToggleButton); +app.component('InputSwitch', InputSwitch); +app.component('CheckBox', CheckBox); + app.mount("#app"); diff --git a/js/router.ts b/js/router.ts index affa3a78..276e089c 100644 --- a/js/router.ts +++ b/js/router.ts @@ -11,9 +11,4 @@ const router = createRouter({ history: createWebHashHistory(), routes }); -export default router; -//# sourceMappingURL=router.js.map -//# sourceMappingURL=router.js.map -//# sourceMappingURL=router.js.map -//# sourceMappingURL=router.js.map -//# sourceMappingURL=router.js.map \ No newline at end of file +export default router; \ No newline at end of file diff --git a/js/shims-vue.d.ts b/js/shims-vue.d.ts index 94beb246..a7468037 100644 --- a/js/shims-vue.d.ts +++ b/js/shims-vue.d.ts @@ -4,4 +4,9 @@ declare module '*.vue' { import type { DefineComponent } from 'vue'; const component: DefineComponent<{}, {}, any>; export default component; +} + +declare module 'primevue/toastservice/toastservice.esm' { + import { Plugin } from 'vue'; + export const install: Plugin; } \ No newline at end of file diff --git a/js/types/Menu.ts b/js/types/Menu.ts new file mode 100644 index 00000000..2573e0b4 --- /dev/null +++ b/js/types/Menu.ts @@ -0,0 +1,11 @@ +export interface MenuItemClickEvent { originalEvent: Event, item: MenuItem } +export interface MenuItem { + label: string, + icon?: string, + to?: string, + items?: Array, + visible?: (boolean | (() => boolean)), + disabled?: boolean, + url?: string, + command?: ((arg0: MenuItemClickEvent) => null), +} \ No newline at end of file diff --git a/package.json b/package.json index 690433ff..af97dd56 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@fullcalendar/moment": "4.4.2", "@fullcalendar/timegrid": "4.4.2", "@rollup/plugin-eslint": "^8.0.1", + "@types/luxon": "^1.27.0", "animate.css": "^3.7.2", "bootbox": "^5.5.2", "bootstrap": "^4.5.2", @@ -32,10 +33,12 @@ "jquery": "3.5.1", "jquery-lazy": "^1.7.11", "jquery-serializejson": "^2.9.0", + "luxon": "^1.27.0", "moment": "^2.27.0", "nosleep.js": "^0.11.0", + "primeflex": "^2.0.0", "primeicons": "^4.1.0", - "primevue": "^3.5.1", + "primevue": "patch:primevue@3.5.1#./patches/primevue.diff", "sass": "^1.35.1", "sprintf-js": "^1.1.2", "startbootstrap-sb-admin": "4.0.0", diff --git a/patches/primevue.diff b/patches/primevue.diff new file mode 100644 index 00000000..8cb4c711 --- /dev/null +++ b/patches/primevue.diff @@ -0,0 +1,26 @@ +diff --git a/confirmationservice/ConfirmationService.d.ts b/confirmationservice/ConfirmationService.d.ts +index de2ab1d8b4c83981eeb146e56a960816452e4fa6..cd921ea8c9d5f74dec8f7a1af5c48d5adf0de7a4 100644 +--- a/confirmationservice/ConfirmationService.d.ts ++++ b/confirmationservice/ConfirmationService.d.ts +@@ -1,6 +1,7 @@ + import Vue, { PluginFunction } from 'vue'; + +-export const install: PluginFunction<{}>; ++declare const install: PluginFunction<{}>; ++export default install; + + interface ConfirmationServiceMethods { + require(options: any): any; +diff --git a/toastservice/ToastService.d.ts b/toastservice/ToastService.d.ts +index 88a375aa3847a56796e586ec6c6cd1d527da0b67..8459f689a4265528e4ccaf917bf43c7e61835bbd 100755 +--- a/toastservice/ToastService.d.ts ++++ b/toastservice/ToastService.d.ts +@@ -1,6 +1,7 @@ + import Vue, { PluginFunction } from 'vue'; + +-export const install: PluginFunction<{}>; ++declare const install: PluginFunction<{}>; ++export default install; + + interface ToastServiceMethods { + add(message: any): any; diff --git a/php/Views/vue.blade.php b/php/Views/vue.blade.php index e8db5769..31383393 100644 --- a/php/Views/vue.blade.php +++ b/php/Views/vue.blade.php @@ -29,7 +29,7 @@ - +
diff --git a/public/img/not-grocy-user-white.svg b/public/img/not-grocy-user-white.svg new file mode 100644 index 00000000..7ada3f07 --- /dev/null +++ b/public/img/not-grocy-user-white.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/img/not-grocy-white-stencil.svg b/public/img/not-grocy-white-stencil.svg new file mode 100644 index 00000000..5b7fee51 --- /dev/null +++ b/public/img/not-grocy-white-stencil.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/img/not-grocy-white.svg b/public/img/not-grocy-white.svg new file mode 100644 index 00000000..5b7fee51 --- /dev/null +++ b/public/img/not-grocy-white.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scss/_main.scss b/scss/_main.scss index d012e474..175b4061 100644 --- a/scss/_main.scss +++ b/scss/_main.scss @@ -56,7 +56,7 @@ a.discrete-link { top: 0; left: 0; overflow: auto; - background-color: $white; + background-color: #fff; width: 100%; } diff --git a/scss/grocy.scss b/scss/grocy.scss index f445c09e..7e8be308 100755 --- a/scss/grocy.scss +++ b/scss/grocy.scss @@ -1,14 +1,50 @@ // This is the main not-grocy stylesheet. // Please do not place any CSS directly in here but import from other files. -// import custom variables +@import 'primeflex/src/_variables.scss'; +@import 'primeflex/src/_grid.scss'; +@import 'primeflex/src/_formlayout.scss'; +@import 'primeflex/src/_display.scss'; +@import 'primeflex/src/_text.scss'; +@import 'primeflex/src/flexbox/_flexbox.scss'; +@import 'primeflex/src/_spacing.scss'; +@import 'primeflex/src/_elevation.scss'; + @import "variables"; -// import bootstrap (as a whole) -@import "bootstrap/scss/bootstrap"; -// startbootstrap -@import "startbootstrap-sb-admin/scss/sb-admin"; +.use-bootstrap { + // import custom variables + // import bootstrap (as a whole) + @import "bootstrap/scss/bootstrap"; + + @import "customizations/bootstrap"; + @import "customizations/sbadmin2"; + + // bootstrap-combobox + @import "@danielfarrell/bootstrap-combobox/css/bootstrap-combobox.css"; + @import "customizations/bs-combobox"; + + // bootstrap-datatables + @import "datatables.net-bs4/css/dataTables.bootstrap4.css"; + @import "datatables.net-colreorder-bs4/css/colReorder.bootstrap4.css"; + @import "datatables.net-rowgroup-bs4/css/rowGroup.bootstrap4.css"; + @import "datatables.net-select-bs4/css/select.bootstrap4.css"; + @import "customizations/datatables"; + + // bootstrap-select + @import "bootstrap-select/sass/bootstrap-select"; + + // tempusdominus + @import "tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4"; + @import "customizations/tempusdominus"; + + // summernote + @import "summernote/src/styles/summernote-bs4"; + + // TODO: does it need to be at the end? + @import "night-mode"; +} // main grocy-style @import "main"; @import "barcodes"; @@ -19,34 +55,10 @@ @import "@fortawesome/fontawesome-free/css/all.css"; @import "customizations/fontawesome"; - -@import "customizations/bootstrap"; -@import "customizations/sbadmin2"; - -// bootstrap-combobox -@import "@danielfarrell/bootstrap-combobox/css/bootstrap-combobox.css"; -@import "customizations/bs-combobox"; - -// bootstrap-datatables -@import "datatables.net-bs4/css/dataTables.bootstrap4.css"; -@import "datatables.net-colreorder-bs4/css/colReorder.bootstrap4.css"; -@import "datatables.net-rowgroup-bs4/css/rowGroup.bootstrap4.css"; -@import "datatables.net-select-bs4/css/select.bootstrap4.css"; -@import "customizations/datatables"; - -// bootstrap-select -@import "bootstrap-select/sass/bootstrap-select"; - // toastr @import "toastr/toastr"; @import "customizations/toastr"; -// tempusdominus -@import "tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4"; -@import "customizations/tempusdominus"; - -// summernote -@import "summernote/src/styles/summernote-bs4"; // popper.js @import "customizations/popperjs"; @@ -60,17 +72,16 @@ // end third-party -// TODO: does it need to be at the end? -@import "night-mode"; - // now comes the fun part, because whatever is set above, primeVue will now override. .theme-day { - @import 'primevue/resources/themes/saga-green/theme.css'; // this will be the day theme. + @import 'sigma/day'; // this will be the day theme. } .theme-night { - @import 'primevue/resources/themes/arya-green/theme.css'; // this will be the night theme. + @import 'sigma/night'; // this will be the night theme. } @import 'primevue/resources/primevue.css'; // core css @import 'primeicons/primeicons.css'; // icons +@import './sigma/layout.scss'; +@import 'primeicons/primeicons.css'; diff --git a/scss/sigma/.gitignore b/scss/sigma/.gitignore new file mode 100644 index 00000000..a09ed652 --- /dev/null +++ b/scss/sigma/.gitignore @@ -0,0 +1,2 @@ +_day.scss +_night.scss \ No newline at end of file diff --git a/scss/sigma/_overrides.scss b/scss/sigma/_overrides.scss new file mode 100644 index 00000000..45f8e540 --- /dev/null +++ b/scss/sigma/_overrides.scss @@ -0,0 +1,140 @@ +body { + transition: background-color .2s ease; + + &.theme-night { + color: rgba(255, 255, 255, 0.87); // text-color + background-color: #121212; // surface-b + + .layout-footer { + background-color: #1e1e1e; // surface-a + } + + .user-settings-menu { + > li { + > a { + border-top: 1px solid $menuitemDarkBorderColor; + } + + &:last-child { + > a { + border-bottom: 1px solid $menuitemDarkBorderColor; + } + } + + ul { + background-color: $menuitemDarkActiveBgColor; + } + } + + li { + a { + color: $menuitemDarkColor; + + &.router-link-active { + color: $menuitemDarkActiveColor; + } + + &:hover { + color: $menuitemDarkHoverColor; + } + } + + &.active-menuitem { + > a { + background-color: $menuitemDarkActiveBgColor; + color: $menuitemDarkActiveColor; + } + } + } + } + } + + &.theme-day { + .user-settings-menu { + > li { + > a { + border-top: 1px solid $menuitemBorderColor; + } + + &:last-child { + > a { + border-bottom: 1px solid $menuitemBorderColor; + } + } + + ul { + background-color: $menuitemActiveBgColor; + } + } + + li { + a { + color: $menuitemColor; + + &.router-link-active { + color: $menuitemActiveColor; + } + + &:hover { + color: $menuitemHoverColor; + } + } + + &.active-menuitem { + > a { + background-color: $menuitemActiveBgColor; + color: $menuitemActiveColor; + } + } + } + } + } +} + +.layout-logo img { + max-width: 80%; + max-height: 7.5rem; +} + +.p-text-help { + color: $textSecondaryColor; +} + +#userSettingsDropDown { + width: 100%; + @media screen and (min-width: $md) { + width: 370px; + } +} + + +.layout-menu-button { + &:focus { + @include focused-inset(); + } + img { + max-width: 100px; + float: left; + margin-top: -50px; + margin-left: 20px; + transition: opacity .2s ease-out; + opacity: 0; + + &.showing { + opacity: 1; + } + } + span { + display: block; + float: left; + } + + width: 80px; + clear: left; +} + +.layout-topbar-icons .p-link { + &:focus { + @include focused-inset(); + } +} \ No newline at end of file diff --git a/scss/sigma/_variables.scss b/scss/sigma/_variables.scss new file mode 100644 index 00000000..f641c126 --- /dev/null +++ b/scss/sigma/_variables.scss @@ -0,0 +1,47 @@ +/* General */ +$fontSize:14px; +$bodyBgColor:#edf0f5; +$textColor:#333333; +$textSecondaryColor:#707070; +$borderRadius:3px; +$dividerColor:#e3e3e3; +$transitionDuration:.2s; +$maskBgColor:#424242; +$focusShadowColor:#a0ff8d; + +/* Menu Common */ +$menuitemBadgeBgColor:#2a602c; +$menuitemBadgeColor:#ffffff; +$submenuFontSize:13px; +$menuitemActiveRouteColor:#bbebb1; + +/* Menu Light */ +$menuBgColorFirst:#f3f4f9; +$menuBgColorLast:#d7dbe8; +$menuitemColor:#242823; +$menuitemHoverColor:#2a602c; +$menuitemActiveColor:#2a602c; +$menuitemActiveBgColor:#ffffff; +$menuitemBorderColor:rgba(210, 224, 207, 0.6); + +/* Menu Dark */ +$menuDarkBgColorFirst:#585f56; +$menuDarkBgColorLast:#414741; +$menuitemDarkColor:#ffffff; +$menuitemDarkHoverColor:#4CAF50; +$menuitemDarkActiveColor:#bbebb1; +$menuitemDarkActiveBgColor:#2f352e; +$menuitemDarkBorderColor:rgba(54, 65, 52, 0.6); + +/* Topbar */ +$topbarLeftBgColor:#4CAF50; +$topbarRightBgColor:#6ebe71; +$topbarItemBadgeBgColor:#ef6262; +$topbarItemBadgeColor:#ffffff; +$topbarItemColor:#ffffff; +$topbarItemHoverColor:#ccfbc3; +$topbarSearchInputBorderBottomColor:#ffffff; +$topbarSearchInputColor:#ffffff; + +/* Footer */ +$footerBgColor:#ffffff; \ No newline at end of file diff --git a/scss/sigma/flags/flag_placeholder.png b/scss/sigma/flags/flag_placeholder.png new file mode 100755 index 0000000000000000000000000000000000000000..febd52467c21c52c51e3d39249c3c438e6daa3a4 GIT binary patch literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^IzTMP$P6TNf8;#@Qp^E9A+A1|g}%pcSOK|=B|(0{ p3=Yq3qyahHo-U3d6>-TDWkAj)21aA?8Ja*AgQu&X%Q~loCIHB+754xD literal 0 HcmV?d00001 diff --git a/scss/sigma/flags/flags.css b/scss/sigma/flags/flags.css new file mode 100755 index 00000000..592d5757 --- /dev/null +++ b/scss/sigma/flags/flags.css @@ -0,0 +1 @@ +span.flag{width:44px;height:30px;display:inline-block;}img.flag{width:30px}.flag{background:url(flags_responsive.png) no-repeat;background-size:100%;vertical-align: middle;}.flag-ad{background-position:0 .413223%}.flag-ae{background-position:0 .826446%}.flag-af{background-position:0 1.239669%}.flag-ag{background-position:0 1.652893%}.flag-ai{background-position:0 2.066116%}.flag-al{background-position:0 2.479339%}.flag-am{background-position:0 2.892562%}.flag-an{background-position:0 3.305785%}.flag-ao{background-position:0 3.719008%}.flag-aq{background-position:0 4.132231%}.flag-ar{background-position:0 4.545455%}.flag-as{background-position:0 4.958678%}.flag-at{background-position:0 5.371901%}.flag-au{background-position:0 5.785124%}.flag-aw{background-position:0 6.198347%}.flag-az{background-position:0 6.61157%}.flag-ba{background-position:0 7.024793%}.flag-bb{background-position:0 7.438017%}.flag-bd{background-position:0 7.85124%}.flag-be{background-position:0 8.264463%}.flag-bf{background-position:0 8.677686%}.flag-bg{background-position:0 9.090909%}.flag-bh{background-position:0 9.504132%}.flag-bi{background-position:0 9.917355%}.flag-bj{background-position:0 10.330579%}.flag-bm{background-position:0 10.743802%}.flag-bn{background-position:0 11.157025%}.flag-bo{background-position:0 11.570248%}.flag-br{background-position:0 11.983471%}.flag-bs{background-position:0 12.396694%}.flag-bt{background-position:0 12.809917%}.flag-bv{background-position:0 13.22314%}.flag-bw{background-position:0 13.636364%}.flag-by{background-position:0 14.049587%}.flag-bz{background-position:0 14.46281%}.flag-ca{background-position:0 14.876033%}.flag-cc{background-position:0 15.289256%}.flag-cd{background-position:0 15.702479%}.flag-cf{background-position:0 16.115702%}.flag-cg{background-position:0 16.528926%}.flag-ch{background-position:0 16.942149%}.flag-ci{background-position:0 17.355372%}.flag-ck{background-position:0 17.768595%}.flag-cl{background-position:0 18.181818%}.flag-cm{background-position:0 18.595041%}.flag-cn{background-position:0 19.008264%}.flag-co{background-position:0 19.421488%}.flag-cr{background-position:0 19.834711%}.flag-cu{background-position:0 20.247934%}.flag-cv{background-position:0 20.661157%}.flag-cx{background-position:0 21.07438%}.flag-cy{background-position:0 21.487603%}.flag-cz{background-position:0 21.900826%}.flag-de{background-position:0 22.31405%}.flag-dj{background-position:0 22.727273%}.flag-dk{background-position:0 23.140496%}.flag-dm{background-position:0 23.553719%}.flag-do{background-position:0 23.966942%}.flag-dz{background-position:0 24.380165%}.flag-ec{background-position:0 24.793388%}.flag-ee{background-position:0 25.206612%}.flag-eg{background-position:0 25.619835%}.flag-eh{background-position:0 26.033058%}.flag-er{background-position:0 26.446281%}.flag-es{background-position:0 26.859504%}.flag-et{background-position:0 27.272727%}.flag-fi{background-position:0 27.68595%}.flag-fj{background-position:0 28.099174%}.flag-fk{background-position:0 28.512397%}.flag-fm{background-position:0 28.92562%}.flag-fo{background-position:0 29.338843%}.flag-fr{background-position:0 29.752066%}.flag-ga{background-position:0 30.165289%}.flag-gd{background-position:0 30.578512%}.flag-ge{background-position:0 30.991736%}.flag-gf{background-position:0 31.404959%}.flag-gh{background-position:0 31.818182%}.flag-gi{background-position:0 32.231405%}.flag-gl{background-position:0 32.644628%}.flag-gm{background-position:0 33.057851%}.flag-gn{background-position:0 33.471074%}.flag-gp{background-position:0 33.884298%}.flag-gq{background-position:0 34.297521%}.flag-gr{background-position:0 34.710744%}.flag-gs{background-position:0 35.123967%}.flag-gt{background-position:0 35.53719%}.flag-gu{background-position:0 35.950413%}.flag-gw{background-position:0 36.363636%}.flag-gy{background-position:0 36.77686%}.flag-hk{background-position:0 37.190083%}.flag-hm{background-position:0 37.603306%}.flag-hn{background-position:0 38.016529%}.flag-hr{background-position:0 38.429752%}.flag-ht{background-position:0 38.842975%}.flag-hu{background-position:0 39.256198%}.flag-id{background-position:0 39.669421%}.flag-ie{background-position:0 40.082645%}.flag-il{background-position:0 40.495868%}.flag-in{background-position:0 40.909091%}.flag-io{background-position:0 41.322314%}.flag-iq{background-position:0 41.735537%}.flag-ir{background-position:0 42.14876%}.flag-is{background-position:0 42.561983%}.flag-it{background-position:0 42.975207%}.flag-jm{background-position:0 43.38843%}.flag-jo{background-position:0 43.801653%}.flag-jp{background-position:0 44.214876%}.flag-ke{background-position:0 44.628099%}.flag-kg{background-position:0 45.041322%}.flag-kh{background-position:0 45.454545%}.flag-ki{background-position:0 45.867769%}.flag-km{background-position:0 46.280992%}.flag-kn{background-position:0 46.694215%}.flag-kp{background-position:0 47.107438%}.flag-kr{background-position:0 47.520661%}.flag-kw{background-position:0 47.933884%}.flag-ky{background-position:0 48.347107%}.flag-kz{background-position:0 48.760331%}.flag-la{background-position:0 49.173554%}.flag-lb{background-position:0 49.586777%}.flag-lc{background-position:0 50%}.flag-li{background-position:0 50.413223%}.flag-lk{background-position:0 50.826446%}.flag-lr{background-position:0 51.239669%}.flag-ls{background-position:0 51.652893%}.flag-lt{background-position:0 52.066116%}.flag-lu{background-position:0 52.479339%}.flag-lv{background-position:0 52.892562%}.flag-ly{background-position:0 53.305785%}.flag-ma{background-position:0 53.719008%}.flag-mc{background-position:0 54.132231%}.flag-md{background-position:0 54.545455%}.flag-me{background-position:0 54.958678%}.flag-mg{background-position:0 55.371901%}.flag-mh{background-position:0 55.785124%}.flag-mk{background-position:0 56.198347%}.flag-ml{background-position:0 56.61157%}.flag-mm{background-position:0 57.024793%}.flag-mn{background-position:0 57.438017%}.flag-mo{background-position:0 57.85124%}.flag-mp{background-position:0 58.264463%}.flag-mq{background-position:0 58.677686%}.flag-mr{background-position:0 59.090909%}.flag-ms{background-position:0 59.504132%}.flag-mt{background-position:0 59.917355%}.flag-mu{background-position:0 60.330579%}.flag-mv{background-position:0 60.743802%}.flag-mw{background-position:0 61.157025%}.flag-mx{background-position:0 61.570248%}.flag-my{background-position:0 61.983471%}.flag-mz{background-position:0 62.396694%}.flag-na{background-position:0 62.809917%}.flag-nc{background-position:0 63.22314%}.flag-ne{background-position:0 63.636364%}.flag-nf{background-position:0 64.049587%}.flag-ng{background-position:0 64.46281%}.flag-ni{background-position:0 64.876033%}.flag-nl{background-position:0 65.289256%}.flag-no{background-position:0 65.702479%}.flag-np{background-position:0 66.115702%}.flag-nr{background-position:0 66.528926%}.flag-nu{background-position:0 66.942149%}.flag-nz{background-position:0 67.355372%}.flag-om{background-position:0 67.768595%}.flag-pa{background-position:0 68.181818%}.flag-pe{background-position:0 68.595041%}.flag-pf{background-position:0 69.008264%}.flag-pg{background-position:0 69.421488%}.flag-ph{background-position:0 69.834711%}.flag-pk{background-position:0 70.247934%}.flag-pl{background-position:0 70.661157%}.flag-pm{background-position:0 71.07438%}.flag-pn{background-position:0 71.487603%}.flag-pr{background-position:0 71.900826%}.flag-pt{background-position:0 72.31405%}.flag-pw{background-position:0 72.727273%}.flag-py{background-position:0 73.140496%}.flag-qa{background-position:0 73.553719%}.flag-re{background-position:0 73.966942%}.flag-ro{background-position:0 74.380165%}.flag-rs{background-position:0 74.793388%}.flag-ru{background-position:0 75.206612%}.flag-rw{background-position:0 75.619835%}.flag-sa{background-position:0 76.033058%}.flag-sb{background-position:0 76.446281%}.flag-sc{background-position:0 76.859504%}.flag-sd{background-position:0 77.272727%}.flag-se{background-position:0 77.68595%}.flag-sg{background-position:0 78.099174%}.flag-sh{background-position:0 78.512397%}.flag-si{background-position:0 78.92562%}.flag-sj{background-position:0 79.338843%}.flag-sk{background-position:0 79.752066%}.flag-sl{background-position:0 80.165289%}.flag-sm{background-position:0 80.578512%}.flag-sn{background-position:0 80.991736%}.flag-so{background-position:0 81.404959%}.flag-sr{background-position:0 81.818182%}.flag-ss{background-position:0 82.231405%}.flag-st{background-position:0 82.644628%}.flag-sv{background-position:0 83.057851%}.flag-sy{background-position:0 83.471074%}.flag-sz{background-position:0 83.884298%}.flag-tc{background-position:0 84.297521%}.flag-td{background-position:0 84.710744%}.flag-tf{background-position:0 85.123967%}.flag-tg{background-position:0 85.53719%}.flag-th{background-position:0 85.950413%}.flag-tj{background-position:0 86.363636%}.flag-tk{background-position:0 86.77686%}.flag-tl{background-position:0 87.190083%}.flag-tm{background-position:0 87.603306%}.flag-tn{background-position:0 88.016529%}.flag-to{background-position:0 88.429752%}.flag-tp{background-position:0 88.842975%}.flag-tr{background-position:0 89.256198%}.flag-tt{background-position:0 89.669421%}.flag-tv{background-position:0 90.082645%}.flag-tw{background-position:0 90.495868%}.flag-ty{background-position:0 90.909091%}.flag-tz{background-position:0 91.322314%}.flag-ua{background-position:0 91.735537%}.flag-ug{background-position:0 92.14876%}.flag-gb,.flag-uk{background-position:0 92.561983%}.flag-um{background-position:0 92.975207%}.flag-us{background-position:0 93.38843%}.flag-uy{background-position:0 93.801653%}.flag-uz{background-position:0 94.214876%}.flag-va{background-position:0 94.628099%}.flag-vc{background-position:0 95.041322%}.flag-ve{background-position:0 95.454545%}.flag-vg{background-position:0 95.867769%}.flag-vi{background-position:0 96.280992%}.flag-vn{background-position:0 96.694215%}.flag-vu{background-position:0 97.107438%}.flag-wf{background-position:0 97.520661%}.flag-ws{background-position:0 97.933884%}.flag-ye{background-position:0 98.347107%}.flag-za{background-position:0 98.760331%}.flag-zm{background-position:0 99.173554%}.flag-zr{background-position:0 99.586777%}.flag-zw{background-position:0 100%} diff --git a/scss/sigma/flags/flags_responsive.png b/scss/sigma/flags/flags_responsive.png new file mode 100755 index 0000000000000000000000000000000000000000..c27ce213fcad70eb18000e762fbcef2c08885fcf GIT binary patch literal 55194 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%0D6CrSxHv-bfoyIFjclhNCO|DLs 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 literal 0 HcmV?d00001 diff --git a/scss/sigma/layout.scss b/scss/sigma/layout.scss new file mode 100644 index 00000000..cc7084b5 --- /dev/null +++ b/scss/sigma/layout.scss @@ -0,0 +1,33 @@ +/* This file, and all contents of the directory /scss/sigma + are taken from primevue-sigma, a free Admin template + Licensed under the MIT license. Some edits have been made by the + not-grocy authors. You can obtain individual line-by-line authorship + and copyright by using `git blame `. + +MIT License + +Copyright (c) 2019 cagataycivici + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +@import "./_variables"; +@import "./sass/_layout"; +@import "./_overrides"; \ No newline at end of file diff --git a/scss/sigma/sass/_config.scss b/scss/sigma/sass/_config.scss new file mode 100644 index 00000000..c9175f2d --- /dev/null +++ b/scss/sigma/sass/_config.scss @@ -0,0 +1,151 @@ +.layout-config { + position: fixed; + top: 50px; + padding: 0; + right: 0; + display: block; + width: 20em; + z-index: 998; + height: calc(100% - 50px); + transform: translate3d(20em, 0px, 0px); + transition: transform $transitionDuration; + backface-visibility: hidden; + box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.24); + color: $textColor; + background-color: #ffffff; + + &.layout-config-active { + transform: translate3d(0px, 0px, 0px); + + .layout-config-content { + .layout-config-button { + i { + transform: rotate(360deg); + } + } + } + } + + .layout-config-button { + display: block; + position: absolute; + width: 52px; + height: 52px; + line-height: 52px; + background-color: $menuitemBadgeBgColor; + text-align: center; + color: $menuitemBadgeColor; + top: 230px; + left: -52px; + z-index: -1; + overflow: hidden; + cursor: pointer; + transition: background-color $transitionDuration; + box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.24); + + i { + font-size: 32px; + line-height: inherit; + cursor: pointer; + transform: rotate(0deg); + transition: color $transitionDuration, transform 1s; + } + } + + .layout-config-close { + position: absolute; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + right: 20px; + top: 10px; + z-index: 1; + background-color: #e54a51; + transition: background-color $transitionDuration; + border-radius: 50%; + + i { + color: #ffffff; + line-height: inherit; + font-size: 12px; + } + + &:hover { + background-color: #ea6e73; + } + } + + .layout-config-content { + position: relative; + overflow: auto; + height: 100%; + padding: 1em; + padding-bottom: 100px; + + .layout-themes { + display: flex; + flex-wrap: wrap; + + > div { + padding: .25rem; + } + + + a { + width: 2rem; + height: 2rem; + border-radius: 50%; + display: block; + position: relative; + align-items: center; + justify-content: center; + transition: transform $transitionDuration; + box-shadow: 0 .125rem .25rem rgba(0,0,0,.075); + + i { + font-size: 1rem; + position: absolute; + color: $menuitemBadgeColor; + top: 50%; + left: 50%; + margin-left: -.5rem; + margin-top: -.5rem; + } + + &:hover { + transform: scale(1.1); + } + } + } + } + + .p-col { + text-align: center; + } + + p { + line-height: 1.5; + margin-top: 0; + color: $textSecondaryColor; + } +} + +.blocked-scroll-config{ + overflow: hidden; +} + +@media screen and (max-width: 1024px) { + .layout-config { + top: 50px; + transform: translate3d(100%, 0px, 0px); + + &.layout-config-active { + transform: translate3d(0px, 0px, 0px); + } + + .layout-config-close { + right: 10px; + } + } +} diff --git a/scss/sigma/sass/_content.scss b/scss/sigma/sass/_content.scss new file mode 100644 index 00000000..d198f6fe --- /dev/null +++ b/scss/sigma/sass/_content.scss @@ -0,0 +1,4 @@ +.layout-main { + transition: margin-left $transitionDuration; + padding: 70px 2rem 2rem 2rem; +} diff --git a/scss/sigma/sass/_dashboard.scss b/scss/sigma/sass/_dashboard.scss new file mode 100644 index 00000000..7203f60d --- /dev/null +++ b/scss/sigma/sass/_dashboard.scss @@ -0,0 +1,186 @@ +/* Dashboard */ +.dashboard { + + .summary { + position: relative; + + .title { + font-size: 20px; + } + + .detail { + color: $textSecondaryColor; + display: block; + margin-top: 10px; + } + + .count { + color: #ffffff; + position: absolute; + top: 10px; + right: 10px; + font-size: 24px; + padding: 7px 14px; + border-radius: $borderRadius; + + &.visitors { + background-color: #20d077; + } + + &.purchases { + background-color: #f9c851; + } + + &.revenue { + background-color: #007be5; + } + } + } + + .highlight-box { + height: 100px; + display: flex; + @include clearfix(); + + .initials { + height: 100%; + width: 50%; + text-align: center; + padding: 1em; + + > span { + font-size: 48px; + } + } + + .highlight-details { + height: 100%; + background-color: #ffffff; + width: 50%; + padding: 1em; + + i { + font-size: 18px; + vertical-align: middle; + margin-right: .5em; + } + + .count { + color: $textSecondaryColor; + font-size: 36px; + margin-top: 4px; + display: block; + } + } + } + + .task-list { + list-style-type: none; + margin: 0; + padding: 0; + + li { + padding: .5em .25em; + border-bottom: 1px solid $dividerColor; + @include clearfix(); + } + + .p-checkbox { + vertical-align: middle; + margin-right: .5em; + } + + .task-name { + vertical-align: middle; + } + + i { + float: right; + font-size: 24px; + color: $textSecondaryColor; + } + + .p-panel-content { + min-height: 256px; + } + } + + .contact-form { + .p-panel-content { + min-height: 256px; + } + } + + .contacts { + + ul { + list-style-type: none; + padding: 0; + margin: 0; + + li { + border-bottom: 1px solid $dividerColor; + + button { + padding: 9px; + width: 100%; + box-sizing: border-box; + text-decoration: none; + position: relative; + display: block; + border-radius: 2px; + transition: background-color .2s; + + .name { + position: absolute; + right: 10px; + top: 10px; + font-size: 18px; + } + + .email { + position: absolute; + right: 10px; + top: 30px; + font-size: 14px; + color: $textSecondaryColor; + } + + &:hover { + cursor: pointer; + background-color: #eeeeee; + } + } + + &:last-child { + border: 0; + } + } + } + + .p-panel-content { + min-height: 256px; + } + } + + .activity-list { + list-style-type: none; + padding: 0; + margin: 0; + + li { + border-bottom: 1px solid $dividerColor; + padding: 16px 8px; + + .count { + font-size: 24px; + color: #ffffff; + background-color: #007be5; + font-weight: 700; + padding: .25em .5em; + display: inline-block; + border-radius: $borderRadius; + } + } + } +} diff --git a/scss/sigma/sass/_footer.scss b/scss/sigma/sass/_footer.scss new file mode 100644 index 00000000..f0cb0fe4 --- /dev/null +++ b/scss/sigma/sass/_footer.scss @@ -0,0 +1,13 @@ +.layout-footer { + transition: margin-left $transitionDuration; + background-color: $footerBgColor; + padding: 1em 2em; + + img { + vertical-align: middle; + } + + .footer-text { + vertical-align: middle; + } +} diff --git a/scss/sigma/sass/_layout.scss b/scss/sigma/sass/_layout.scss new file mode 100644 index 00000000..f6bdf650 --- /dev/null +++ b/scss/sigma/sass/_layout.scss @@ -0,0 +1,14 @@ +@import "./_mixins"; +@import "./_splash"; +@import "./_main"; +@import "./_topbar"; +@import "./_sidebar"; +@import "./_profile"; +@import "./_menu"; +@import "./_config"; +@import "./_content"; +@import "./_footer"; +@import "./_responsive"; +@import "./_utils"; +@import "./_typography"; +@import "./_dashboard"; diff --git a/scss/sigma/sass/_main.scss b/scss/sigma/sass/_main.scss new file mode 100644 index 00000000..08073877 --- /dev/null +++ b/scss/sigma/sass/_main.scss @@ -0,0 +1,34 @@ +* { + box-sizing: border-box; +} + +html { + height: 100%; + font-size: $fontSize; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: $fontSize; + color: $textColor; + background-color: $bodyBgColor; + margin: 0; + padding: 0; + min-height: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + text-decoration: none; + color: #2196f3; +} + +.layout-wrapper { + padding: 0; + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 100vh; +} + diff --git a/scss/sigma/sass/_menu.scss b/scss/sigma/sass/_menu.scss new file mode 100644 index 00000000..f06f76cb --- /dev/null +++ b/scss/sigma/sass/_menu.scss @@ -0,0 +1,189 @@ +.layout-menu-container { + padding-bottom: 120px; +} + +.layout-menu { + list-style-type: none; + margin: 0; + padding: 0; + + li { + a { + cursor: pointer; + position: relative; + text-decoration: none; + display: flex; + align-items: center; + transition: color $transitionDuration; + + i { + font-size: 16px; + } + + span { + margin-left: 0.5em; + } + + .menuitem-toggle-icon { + margin-left: auto; + } + + &:focus { + @include focused-inset(); + } + } + + &.active-menuitem { + > a { + .menuitem-toggle-icon { + @include icon-override('\e933'); + } + } + } + } + + > li { + > a { + padding: 1em; + + span { + font-size: $fontSize; + } + } + + &:last-child { + > a { + border-bottom: 1px solid $menuitemBorderColor; + } + } + + ul { + list-style-type: none; + margin: 0; + padding: 0; + padding-left: 1.5em; + + li { + a { + cursor: pointer; + padding: 0.75em 1em; + border-top: 0 none; + + span { + font-size: $submenuFontSize; + } + } + + &:last-child { + padding-bottom: 1em; + } + } + + &.layout-submenu-wrapper-enter-from, + &.layout-submenu-wrapper-leave-to { + max-height: 0; + } + + &.layout-submenu-wrapper-enter-to, + &.layout-submenu-wrapper-leave-from { + max-height: 1000px; + } + + &.layout-submenu-wrapper-leave-active { + overflow: hidden; + transition: max-height 0.45s cubic-bezier(0, 1, 0, 1); + } + + &.layout-submenu-wrapper-enter-active { + overflow: hidden; + transition: max-height 1s ease-in-out; + } + } + } +} + +.layout-sidebar-light { + @include linear-gradient($menuBgColorFirst, $menuBgColorLast); + + .layout-menu { + > li { + > a { + border-top: 1px solid $menuitemBorderColor; + } + + &:last-child { + > a { + border-bottom: 1px solid $menuitemBorderColor; + } + } + + ul { + background-color: $menuitemActiveBgColor; + } + } + + li { + a { + color: $menuitemColor; + + &.router-link-active { + color: $menuitemActiveColor; + } + + &:hover { + color: $menuitemHoverColor; + } + } + + &.active-menuitem { + > a { + background-color: $menuitemActiveBgColor; + color: $menuitemActiveColor; + } + } + } + } +} + +.layout-sidebar-dark { + @include linear-gradient($menuDarkBgColorFirst, $menuDarkBgColorLast); + + .layout-menu { + > li { + > a { + border-top: 1px solid $menuitemDarkBorderColor; + } + + &:last-child { + > a { + border-bottom: 1px solid $menuitemDarkBorderColor; + } + } + + ul { + background-color: $menuitemDarkActiveBgColor; + } + } + + li { + a { + color: $menuitemDarkColor; + + &.router-link-active { + color: $menuitemDarkActiveColor; + } + + &:hover { + color: $menuitemDarkHoverColor; + } + } + + &.active-menuitem { + > a { + background-color: $menuitemDarkActiveBgColor; + color: $menuitemDarkActiveColor; + } + } + } + } +} diff --git a/scss/sigma/sass/_mixins.scss b/scss/sigma/sass/_mixins.scss new file mode 100644 index 00000000..e1a5fe01 --- /dev/null +++ b/scss/sigma/sass/_mixins.scss @@ -0,0 +1,43 @@ +@mixin icon-override($icon) { + &:before { + content: $icon; + } +} + +@mixin linear-gradient($top, $bottom){ + background: $top; /* Old browsers */ + background: linear-gradient(to bottom, $top 0%,$bottom 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#000000',GradientType=0 ); /* IE6-9 */ +} + +@mixin linear-gradient-left($left, $right){ + background: $left; /* Old browsers */ + background: linear-gradient(to right, $left 0%,$right 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr=$left, endColorstr=$right,GradientType=1 ); /* IE6-9 */ +} + +@mixin opacity($opacity) { + opacity: $opacity; + $opacity-ie: $opacity * 100; + filter: alpha(opacity=$opacity-ie); +} + +@mixin focused() { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2em $focusShadowColor; +} + +@mixin focused-inset() { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.2em $focusShadowColor; +} + +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} diff --git a/scss/sigma/sass/_profile.scss b/scss/sigma/sass/_profile.scss new file mode 100644 index 00000000..2886083a --- /dev/null +++ b/scss/sigma/sass/_profile.scss @@ -0,0 +1,133 @@ +.layout-profile { + text-align: center; + padding: 20px 0; + + img { + width: 56px; + margin: 10px; + border-radius: 56px; + background-color: #1e4620; + } + + .layout-profile-link { + cursor: pointer; + display: block; + width: 100%; + margin-bottom: 0.75em; + transition: color $transitionDuration; + + i { + display: inline-block; + font-size: 16px; + vertical-align: middle; + margin-left: 0.5em; + } + } + + ul { + list-style-type: none; + padding: 0; + margin: 0; + + li { + button { + width: 100%; + padding: 1em; + border: 0 none; + border-radius: 0; + cursor: pointer; + transition: color $transitionDuration; + + &:hover { + color: $menuitemHoverColor; + } + + span { + margin-left: 0.25em; + vertical-align: middle; + } + + i { + vertical-align: middle; + } + } + } + + &.layout-submenu-wrapper-enter-from, + &.layout-submenu-wrapper-leave-to { + max-height: 0; + } + + &.layout-submenu-wrapper-enter-to, + &.layout-submenu-wrapper-leave-from { + max-height: 1000px; + } + + &.layout-submenu-wrapper-leave-active { + overflow: hidden; + transition: max-height 0.45s cubic-bezier(0, 1, 0, 1); + } + + &.layout-submenu-wrapper-enter-active { + overflow: hidden; + transition: max-height 1s ease-in-out; + } + } +} + +.layout-sidebar .layout-profile { + padding: 0; + padding-bottom: 1rem; +} + +.layout-sidebar-light { + .layout-profile { + .layout-profile-link { + color: $menuitemColor; + + &:hover { + color: $menuitemHoverColor; + } + } + + ul { + background-color: $menuitemActiveBgColor; + + li { + button { + color: $menuitemColor; + + &:hover { + color: $menuitemHoverColor; + } + } + } + } + } +} + +.layout-sidebar-dark { + .layout-profile { + .layout-profile-link { + color: $menuitemDarkColor; + + &:hover { + color: $menuitemDarkHoverColor; + } + } + + ul { + background-color: $menuitemDarkActiveBgColor; + + li { + button { + color: $menuitemDarkColor; + + &:hover { + color: $menuitemDarkHoverColor; + } + } + } + } + } +} diff --git a/scss/sigma/sass/_responsive.scss b/scss/sigma/sass/_responsive.scss new file mode 100644 index 00000000..be6204a3 --- /dev/null +++ b/scss/sigma/sass/_responsive.scss @@ -0,0 +1,97 @@ +@media (min-width: 1025px) { + .layout-wrapper { + &.layout-overlay { + .layout-topbar { + left: 0; + } + + .layout-main, .layout-footer { + margin-left: 0; + } + + &.layout-overlay-sidebar-active { + .layout-sidebar { + left: 0; + } + + .layout-topbar { + left: 250px; + } + } + + &.layout-mobile-sidebar-active { + .layout-sidebar { + left: 0; + } + + .layout-topbar { + left: 250px; + } + } + } + + &.layout-static { + .layout-topbar { + left: 250px; + } + + .layout-main, .layout-footer { + margin-left: 250px; + } + + &.layout-static-sidebar-inactive { + .layout-topbar { + left: 0; + } + + .layout-main, .layout-footer { + margin-left: 0; + } + } + } + } +} + +@media (max-width: 1024px) { + .layout-wrapper { + .layout-topbar { + left: 0; + } + + .layout-main, .layout-footer { + margin-left: 0; + } + + .layout-sidebar { + transition: left $transitionDuration; + left: -250px; + margin-top: 50px; + } + + .layout-mask { + display: none; + position: fixed; + width: 100%; + height: 100%; + top: 50px; + left: 0; + z-index: 998; + background-color: $maskBgColor; + @include opacity(0.7); + } + + &.layout-mobile-sidebar-active { + .layout-sidebar { + left: -0; + } + + .layout-mask { + display: block; + } + } + } + + .body-overflow-hidden { + overflow: hidden; + } +} diff --git a/scss/sigma/sass/_sidebar.scss b/scss/sigma/sass/_sidebar.scss new file mode 100644 index 00000000..7d88534f --- /dev/null +++ b/scss/sigma/sass/_sidebar.scss @@ -0,0 +1,32 @@ +.layout-sidebar { + position: fixed; + width: 250px; + height: 100%; + z-index: 999; + overflow-y: auto; + user-select: none; + transition: transform $transitionDuration; + box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.16); + + &.layout-sidebar-enter-from, + &.layout-sidebar-leave-to { + transform: translateX(-100%); + } + + .layout-logo { + text-align: center; + } + + .menuitem-badge { + display: inline-block; + margin-left: 4px; + font-size: 10px; + width: 16px; + height: 16px; + line-height: 16px; + text-align: center; + color: $menuitemBadgeColor; + background-color: $menuitemBadgeBgColor; + border-radius: 50%; + } +} diff --git a/scss/sigma/sass/_splash.scss b/scss/sigma/sass/_splash.scss new file mode 100644 index 00000000..df8f1b0c --- /dev/null +++ b/scss/sigma/sass/_splash.scss @@ -0,0 +1,47 @@ +.splash-screen { + width: 100%; + height: 100%; + position: fixed; + @include linear-gradient-left($topbarLeftBgColor, $topbarRightBgColor); + + .splash-container { + width: 40px; + height: 40px; + margin: 0px auto; + position: absolute; + left: 50%; + top: 50%; + margin-left: -20px; + margin-top: -20px; + + } + + .splash-double-bounce1, .splash-double-bounce2 { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: $topbarItemColor; + opacity: 0.6; + position: absolute; + top: 0; + left: 0; + animation: splash-bounce 2.0s infinite ease-in-out; + } + + .splash-double-bounce2 { + animation-delay: -1.0s; + } + + @-webkit-keyframes splash-bounce { + 0%, 100% { -webkit-transform: scale(0.0) } + 50% { -webkit-transform: scale(1.0) } + } + + @keyframes splash-bounce { + 0%, 100% { + transform: scale(0.0); + } 50% { + transform: scale(1.0); + } + } +} diff --git a/scss/sigma/sass/_topbar.scss b/scss/sigma/sass/_topbar.scss new file mode 100644 index 00000000..a1f392c2 --- /dev/null +++ b/scss/sigma/sass/_topbar.scss @@ -0,0 +1,127 @@ +.layout-topbar { + position: fixed; + height: 50px; + padding: 1em 2em 0em 2em; + color: #ffffff; + z-index: 999; + right: 0; + @include clearfix(); + @include linear-gradient-left($topbarLeftBgColor,$topbarRightBgColor); + transition: left $transitionDuration; + + .layout-topbar-icons { + float: right; + display: block; + animation-duration: .5s; + + button { + position: relative; + color: $topbarItemColor; + margin-left: 20px; + display: inline-block; + text-decoration: none; + transition: color $transitionDuration; + + &:hover { + color: $topbarItemHoverColor; + } + + &:focus { + @include focused(); + } + + span { + &.layout-topbar-icon { + font-size: 1.5em; + } + + &.layout-topbar-item-text { + font-size: 20px; + display: none; + } + + &.layout-topbar-badge { + position: absolute; + font-size: 10px; + right: -5px; + top: -5px; + width: 16px; + height: 16px; + text-align: center; + line-height: 16px; + color: $topbarItemBadgeColor; + background-color: $topbarItemBadgeBgColor; + border-radius: 50%; + } + } + } + + .layout-topbar-search { + padding: 0; + position: relative; + display: inline-block; + top: -4px; + + input { + display: inline-block; + border: 0 none; + font-size: $fontSize; + background: transparent; + border-bottom: 2px solid $topbarSearchInputBorderBottomColor; + outline: 0 none; + box-shadow: none; + color: $topbarSearchInputColor; + width: 100px; + padding: 1px 20px 1px 1px; + margin: 0px; + border-radius: 2px; + + &::-webkit-input-placeholder { color:$topbarSearchInputColor; opacity: .7; transition: opacity $transitionDuration;} + &:-moz-placeholder { color:$topbarSearchInputColor; opacity: .7; transition: opacity $transitionDuration;} + &::-moz-placeholder { color:$topbarSearchInputColor; opacity: .7; transition: opacity $transitionDuration;} + &:-ms-input-placeholder { color:$topbarSearchInputColor; opacity: .7; transition: opacity $transitionDuration;} + } + + .layout-topbar-search-icon { + font-size: 18px; + position: absolute; + top: -1px; + right: 0px; + } + + &:hover { + input { + border-bottom-color: $topbarItemHoverColor; + &::-webkit-input-placeholder { opacity: 1 } + &:-moz-placeholder {opacity: 1} + &::-moz-placeholder {opacity: 1} + &:-ms-input-placeholder {opacity: 1} + } + + .layout-topbar-search-icon { + color: $topbarItemHoverColor; + } + } + } + } + + .layout-menu-button { + cursor: pointer; + display: inline-block; + text-decoration: none; + color: $topbarItemColor; + transition: color $transitionDuration; + + span { + font-size: 1.5em; + } + + &:hover { + color: $topbarItemHoverColor; + } + } + + button { + cursor: pointer; + } +} diff --git a/scss/sigma/sass/_typography.scss b/scss/sigma/sass/_typography.scss new file mode 100644 index 00000000..81d90a50 --- /dev/null +++ b/scss/sigma/sass/_typography.scss @@ -0,0 +1,63 @@ +h1, h2, h3, h4, h5, h6 { + margin: 1.5rem 0 1rem 0; + font-family: inherit; + font-weight: normal; + line-height: 1.2; + color: inherit; + + &:first-child { + margin-top: 0; + } +} + +h1 { + font-size: 2.5rem; +} + +h2 { + font-size: 2rem; +} + +h3 { + font-size: 1.75rem; +} + +h4 { + font-size: 1.5rem; +} + +h5 { + font-size: 1.25rem; +} + +h6 { + font-size: 1rem; +} + +mark { + background: #FFF8E1; + padding: .25rem .4rem; + border-radius: $borderRadius; + font-family: monospace; +} + +blockquote { + margin: 1rem 0; + padding: 0 2rem; + border-left: 4px solid #90A4AE; +} + +hr { + border-top: solid $dividerColor; + border-width: 1px 0 0 0; + margin: 1rem 0; +} + +p { + margin: 0 0 1rem 0; + line-height: 1.5; + + &:last-child { + margin-bottom: 0; + } +} diff --git a/scss/sigma/sass/_utils.scss b/scss/sigma/sass/_utils.scss new file mode 100644 index 00000000..a6c1f898 --- /dev/null +++ b/scss/sigma/sass/_utils.scss @@ -0,0 +1,26 @@ +/* Typography */ +.card { + background-color: #ffffff; + padding: 1em; + margin-bottom: 16px; + border-radius: $borderRadius; + + &.card-w-title { + padding-bottom: 2em; + } +} + +/* Code Highlight */ +.docs { + pre[class*="language-"] { + padding: 0 !important; + background: transparent; + overflow: visible; + + > code { + border-left: 0 none; + box-shadow: none !important; + font-size: 14px; + } + } +} diff --git a/yarn.lock b/yarn.lock index bfce7b71..d03c31af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2054,6 +2054,13 @@ __metadata: languageName: node linkType: hard +"@types/luxon@npm:^1.27.0": + version: 1.27.0 + resolution: "@types/luxon@npm:1.27.0" + checksum: ebce0d4c671086c18517784abd5e49e836024d32eddcf189e03ecc1ef4547dadd194895461b53ea34c230e9eaf38f2d2c87183322e3dbcd5e0d658c6f9938995 + languageName: node + linkType: hard + "@types/minimatch@npm:^3.0.3": version: 3.0.4 resolution: "@types/minimatch@npm:3.0.4" @@ -6296,6 +6303,7 @@ fsevents@~2.3.2: "@rollup/plugin-eslint": ^8.0.1 "@rollup/plugin-node-resolve": ^13.0.0 "@rollup/plugin-replace": ^2.4.2 + "@types/luxon": ^1.27.0 "@typescript-eslint/eslint-plugin": ^4.28.1 "@typescript-eslint/parser": ^4.28.1 "@vue/compiler-sfc": ^3.1.4 @@ -6333,14 +6341,16 @@ fsevents@~2.3.2: jquery-lazy: ^1.7.11 jquery-serializejson: ^2.9.0 lodash.clonedeep: ^4.5.0 + luxon: ^1.27.0 moment: ^2.27.0 node-sass-tilde-importer: ^1.0.2 nosleep.js: ^0.11.0 postcss: ^8.3.5 postcss-cli: ^8.3.1 postcss-import: ^14.0.2 + primeflex: ^2.0.0 primeicons: ^4.1.0 - primevue: ^3.5.1 + primevue: "patch:primevue@3.5.1#./patches/primevue.diff" rollup: ^2.52.1 rollup-plugin-postcss: ^4.0.0 rollup-plugin-typescript2: ^0.30.0 @@ -8028,6 +8038,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"luxon@npm:^1.27.0": + version: 1.27.0 + resolution: "luxon@npm:1.27.0" + checksum: c60f73b6b94bdd4e43c731133aa746388b907a26a5a426478c2520b263bec1dae4dc6e269a3410374789551309c65af5d245fae4cef61b72c3ad64a4c3e8b2b9 + languageName: node + linkType: hard + "macos-release@npm:^2.2.0": version: 2.5.0 resolution: "macos-release@npm:2.5.0" @@ -9969,6 +9986,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"primeflex@npm:^2.0.0": + version: 2.0.0 + resolution: "primeflex@npm:2.0.0" + checksum: 8b7f7af50cb7ba9c0b57f57a312d85bb134eb867026a0238cef76b6d801dfe66b29a4a58b5b93e811a097424503cc9f23a0bdd67dbfa9dae5510f54240139123 + languageName: node + linkType: hard + "primeicons@npm:^4.1.0": version: 4.1.0 resolution: "primeicons@npm:4.1.0" @@ -9976,13 +10000,20 @@ fsevents@~2.3.2: languageName: node linkType: hard -"primevue@npm:^3.5.1": +primevue@3.5.1: version: 3.5.1 resolution: "primevue@npm:3.5.1" checksum: cfd8ad6a847fd15a1834ace6181ec8f35bc6f94bb0bb830d39178347861c8f12dd50ba98b2530662cba36882b5e0eedc79408e706f2c24652504b1f45ee665db languageName: node linkType: hard +"primevue@patch:primevue@3.5.1#./patches/primevue.diff::locator=grocy%40workspace%3A.": + version: 3.5.1 + resolution: "primevue@patch:primevue@npm%3A3.5.1#./patches/primevue.diff::version=3.5.1&hash=527256&locator=grocy%40workspace%3A." + checksum: 96c8c3de830330fff31faa535f42327425848906965185a6bb32e1a472c52358803c0beea19a530278b790f9e4a554e4fbbc3dc8663f6a9d5396f023ce5086db + languageName: node + linkType: hard + "private@npm:^0.1.6, private@npm:^0.1.8": version: 0.1.8 resolution: "private@npm:0.1.8" From 0e31f4d9cbfe82a32e21b4f44c3b18bc81787376 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Sat, 3 Jul 2021 22:20:16 +0200 Subject: [PATCH 05/16] i18n: Begin move to vue-i18n. I have commited the script I used to convert the gettext files. --- Makefile | 16 +- buildfiles/convert-locales.php | 67 ++ js/App.vue | 3 +- js/components/App/Footer.vue | 7 +- js/components/App/QuickUserSettings.vue | 35 +- js/components/App/Submenu.vue | 6 +- js/locale.ts | 43 ++ js/main.ts | 5 + js/pages/Stock/Overview.vue | 2 +- locale/cs.json | 911 ++++++++++++++++++++++++ locale/da.json | 855 ++++++++++++++++++++++ locale/de.json | 899 +++++++++++++++++++++++ locale/el_GR.json | 911 ++++++++++++++++++++++++ locale/en.json | 611 ++++++++++++++++ locale/en_GB.json | 911 ++++++++++++++++++++++++ locale/es.json | 911 ++++++++++++++++++++++++ locale/fi.json | 911 ++++++++++++++++++++++++ locale/fr.json | 911 ++++++++++++++++++++++++ locale/he_IL.json | 911 ++++++++++++++++++++++++ locale/hu.json | 911 ++++++++++++++++++++++++ locale/it.json | 911 ++++++++++++++++++++++++ locale/ja.json | 846 ++++++++++++++++++++++ locale/ko_KR.json | 885 +++++++++++++++++++++++ locale/nl.json | 911 ++++++++++++++++++++++++ locale/no.json | 855 ++++++++++++++++++++++ locale/pl.json | 911 ++++++++++++++++++++++++ locale/pt_BR.json | 855 ++++++++++++++++++++++ locale/pt_PT.json | 768 ++++++++++++++++++++ locale/ru.json | 850 ++++++++++++++++++++++ locale/sk_SK.json | 885 +++++++++++++++++++++++ locale/sv_SE.json | 885 +++++++++++++++++++++++ locale/ta.json | 911 ++++++++++++++++++++++++ locale/tr.json | 524 ++++++++++++++ locale/zh_CN.json | 911 ++++++++++++++++++++++++ locale/zh_TW.json | 911 ++++++++++++++++++++++++ package.json | 5 +- php/Services/LocalizationService.php | 3 +- public/.gitignore | 1 + rollup.vue.js | 2 + yarn.lock | 168 ++++- 40 files changed, 22799 insertions(+), 36 deletions(-) create mode 100644 buildfiles/convert-locales.php create mode 100644 js/locale.ts create mode 100644 locale/cs.json create mode 100644 locale/da.json create mode 100644 locale/de.json create mode 100644 locale/el_GR.json create mode 100644 locale/en.json create mode 100644 locale/en_GB.json create mode 100644 locale/es.json create mode 100644 locale/fi.json create mode 100644 locale/fr.json create mode 100644 locale/he_IL.json create mode 100644 locale/hu.json create mode 100644 locale/it.json create mode 100644 locale/ja.json create mode 100644 locale/ko_KR.json create mode 100644 locale/nl.json create mode 100644 locale/no.json create mode 100644 locale/pl.json create mode 100644 locale/pt_BR.json create mode 100644 locale/pt_PT.json create mode 100644 locale/ru.json create mode 100644 locale/sk_SK.json create mode 100644 locale/sv_SE.json create mode 100644 locale/ta.json create mode 100644 locale/tr.json create mode 100644 locale/zh_CN.json create mode 100644 locale/zh_TW.json diff --git a/Makefile b/Makefile index d8bf0453..eecec615 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ YARN=yarn PHP=php COMPOSER=composer POSTCSS=npx postcss +I18NEXTRACT=yarn run vue-i18n-extract # Configure some default flags for the tooling. Set include paths. SASSFLAGS=-I node_modules/ --color --quiet-deps @@ -57,10 +58,12 @@ SASS_INPUT=scss/grocy.scss SASS_OUTPUT=public/dist/grocy.css UGLIFY_INPUT=public/dist/grocy.js UGLIFY_OUTPUT=public/dist/grocy.min.js -OBJDIRS := public/dist public/js public/css public/js/locales +OBJDIRS := public/dist public/js public/css public/js/locales public/locale TMPSASS=public/dist/grocy.tmp.css ARTSOURCES := $(wildcard artwork/*.svg) ARTOBJS := $(addprefix public/img/, $(notdir $(ARTSOURCES))) +LANGS := $(wildcard locale/*.json) +LANGOBJS := $(addprefix public/locale/, $(notdir $(LANGS))) .DEFAULT_GOAL := build # disable default suffixes @@ -171,7 +174,7 @@ frontend: # defined below, which do the actual magic. # TODO: better dependency expression. .PHONY=resources -resources: public/webfonts public/dist/font public/dist/fonts public/js/locales/summernote public/js/locales/bootstrap-select public/js/locales/fullcalendar public/js/locales/fullcalendar-core public/js/swagger-ui.js $(ARTOBJS) +resources: public/webfonts public/dist/font public/dist/fonts public/js/locales/summernote public/js/locales/bootstrap-select public/js/locales/fullcalendar public/js/locales/fullcalendar-core public/js/swagger-ui.js $(ARTOBJS) $(LANGOBJS) public/dist: mkdir -p public/dist @@ -181,6 +184,8 @@ public/css: mkdir -p public/css public/js/locales: mkdir -p public/js/locales +public/locale: + mkdir -p public/locale public/webfonts: | yarn.lock $(OBJDIRS) cp -r node_modules/@fortawesome/fontawesome-free/webfonts public/webfonts @@ -210,6 +215,8 @@ public/js/swagger-ui.js: node_modules/swagger-ui-dist/swagger-ui.js | yarn.lock public/img/%.svg: artwork/%.svg cp $< $@ +public/locale/%.json: locale/%.json | $(OBJDIRS) + cp $< $@ node_modules/swagger-ui-dist/swagger-ui.js: yarn.lock @@ -221,6 +228,10 @@ node_modules/swagger-ui-dist/swagger-ui.js: yarn.lock public/js/locales/grocy/en.json: vendor localization/strings.pot | $(OBJDIRS) ${PHP} buildfiles/generate-locales.php +.PHONY=extract +extract: + ${I18NEXTRACT} report -v './js/**/*.?(js|vue|ts)' --languageFiles './locale/*.json' -a + .PHONY=run run: build ${PHP} ${RUNFLAGS} @@ -268,6 +279,7 @@ clean: -rm -rf public/js -rm -rf public/css -rm -rf public/js/locales + -rm -rf public/locale -rm -rf release/ -rm -rf vendor/ -rm -rf node_modules diff --git a/buildfiles/convert-locales.php b/buildfiles/convert-locales.php new file mode 100644 index 00000000..affc4544 --- /dev/null +++ b/buildfiles/convert-locales.php @@ -0,0 +1,67 @@ +LoadLocalizations(false); + + $lf = fopen(REPO_BASE . "/locale/" . $culture . ".json", "w"); + + fwrite($lf, "{\n"); + + $isFirst = true; + foreach($ls->Po as $translation) { + if(!$isFirst) { + fwrite($lf, ",\n"); + } + $isFirst = false; + $orig = clean($translation->getOriginal()); + $hasPlural = $translation->hasPlural(); + if($hasPlural) { + $orig .= ' | ' . clean($translation->getPlural()); + } + + $trans = clean($translation->getTranslation()); + if($hasPlural) { + $plTrans = $translation->getPluralTranslations(); + foreach($plTrans as $pTrans) { + $trans .= ' | ' . clean($pTrans); + } + } + + fwrite($lf, ' "'.$orig.'" : "'.$trans.'"'); + } + fwrite($lf, "\n}\n"); +} diff --git a/js/App.vue b/js/App.vue index 1f4e9ac1..7fe99ac7 100644 --- a/js/App.vue +++ b/js/App.vue @@ -31,7 +31,6 @@ import AppProfile from './components/App/Profile.vue'; import AppMenu from './components/App/Menu.vue'; import AppFooter from './components/App/Footer.vue'; import ScrollPanel from 'primevue/scrollpanel'; - import { MenuItem, MenuItemClickEvent } from './types/Menu'; import { defineComponent } from 'vue'; @@ -48,7 +47,7 @@ export default defineComponent({ menuActive: false, menuClick: false, menu : > [ - { label: 'Stockoverview', icon: 'fas fa-box', to: '/'}, + { label: 'Stock overview', icon: 'fas fa-box', to: '/'}, { label: 'Shopping list', icon: 'fas fa-shopping-cart', to: '/shoppinglist'}, { label: 'Recipes', icon: 'fas fa-pizza-slice', to: '/recipes'}, { label: 'Meal plan', icon: 'fas fa-map', to: '/mealplan'}, diff --git a/js/components/App/Footer.vue b/js/components/App/Footer.vue index 8dc1561d..2beb7ff3 100644 --- a/js/components/App/Footer.vue +++ b/js/components/App/Footer.vue @@ -1,8 +1,11 @@ diff --git a/js/components/App/QuickUserSettings.vue b/js/components/App/QuickUserSettings.vue index 36c04523..03d13c94 100644 --- a/js/components/App/QuickUserSettings.vue +++ b/js/components/App/QuickUserSettings.vue @@ -2,54 +2,63 @@

-
+
-
+

-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
This activates night mode between {{ nightModeStart }} and {{ nightModeEnd }} the nextsame day.
+
+ + + + + + + + +

-
+
-
+
diff --git a/js/components/App/Submenu.vue b/js/components/App/Submenu.vue index 00d31eb2..c5983e40 100644 --- a/js/components/App/Submenu.vue +++ b/js/components/App/Submenu.vue @@ -6,16 +6,16 @@ - {{item.label}} + {{ $t(item.label) }} {{item.badge}}
- {{item.label}} + {{ $t(item.label) }} - {{item.badge}} + {{ item.badge }} diff --git a/js/locale.ts b/js/locale.ts new file mode 100644 index 00000000..2f9804a4 --- /dev/null +++ b/js/locale.ts @@ -0,0 +1,43 @@ +import { nextTick } from 'vue'; +import { createI18n, I18n } from 'vue-i18n'; + +export const ALL_LANGS = ['cs', 'da', 'de', 'el_GR', 'en', 'en_GB', 'es', 'fi', 'fr', 'he_IL', 'hu', 'it', 'ja', 'ko_KR', 'nl', 'no', 'pl', 'pt_BR', 'pt_PT', 'ru', 'sk_SK', 'sv_SE', 'ta', 'tr', 'zh_CN', 'zh_TW']; + +// Reason: the type just isn't really there. It's any in the repo. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function setupI18n(options: any = { locale: 'en' }): I18n +{ + // force "format fallback messages" + options.formatFallbackMessages = true; + options.legacy = false; + options.globalInjection = true; + + const i18n = createI18n(options); + setI18nLanguage(i18n, options.locale); + return i18n; +} + +export function setI18nLanguage(i18n: I18n, locale: string) : void +{ + i18n.global.locale.value = locale; + /** + * NOTE: + * If you need to specify the language setting for headers, such as the `fetch` API, set it here. + * The following is an example for axios. + * + * axios.defaults.headers.common['Accept-Language'] = locale + */ + document.querySelector('html')?.setAttribute('lang', locale); +} + +export async function loadLocaleMessages(i18n : I18n, locale : string) : Promise +{ + // load locale messages with dynamic import + // this gets 1:1 translated into a network call, so.... + const messages = await (await fetch(`/locale/${locale}.json`)).json(); + + // set locale and locale message + i18n.global.setLocaleMessage(locale, messages); + + return nextTick(); +} \ No newline at end of file diff --git a/js/main.ts b/js/main.ts index 3743e30f..74154f1d 100644 --- a/js/main.ts +++ b/js/main.ts @@ -1,4 +1,5 @@ import { createApp } from 'vue'; +import { loadLocaleMessages, setI18nLanguage, setupI18n } from './locale'; import router from './router'; import App from './App.vue'; @@ -15,10 +16,12 @@ import InputSwitch from 'primevue/inputswitch'; import CheckBox from 'primevue/checkbox'; const app = createApp(App); +const i18n = setupI18n(); app.use(PrimeVue); app.use(ToastService); app.use(router); +app.use(i18n); app.component('InputText', InputText); app.component('RadioButton', RadioButton); @@ -26,5 +29,7 @@ app.component('ToggleButton', ToggleButton); app.component('InputSwitch', InputSwitch); app.component('CheckBox', CheckBox); +// test! +loadLocaleMessages(i18n, "de").then(() => setI18nLanguage(i18n, "de")); app.mount("#app"); diff --git a/js/pages/Stock/Overview.vue b/js/pages/Stock/Overview.vue index 64bf5db9..5b122728 100644 --- a/js/pages/Stock/Overview.vue +++ b/js/pages/Stock/Overview.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/js/components/App/Profile.vue b/js/components/App/Profile.vue index 28bcc290..d18eff9c 100644 --- a/js/components/App/Profile.vue +++ b/js/components/App/Profile.vue @@ -3,17 +3,17 @@
    -
  • -
  • +
  • +
@@ -22,6 +22,7 @@ diff --git a/js/components/App/TopBar.vue b/js/components/App/TopBar.vue index 7c3d9658..6ab9271a 100644 --- a/js/components/App/TopBar.vue +++ b/js/components/App/TopBar.vue @@ -7,6 +7,7 @@
+
- +
@@ -25,8 +26,11 @@ import { defineComponent, ref } from 'vue'; import QuickUserSettings from './QuickUserSettings.vue'; import UserSettingsMenu from './UserSettingsMenu.vue'; +import HeaderClock from './HeaderClock.vue'; import { MenuItem } from '../../types/Menu'; +import { useStore } from '../../store'; +import ViewportSize from '../../lib/breakpoints'; export default defineComponent({ data() @@ -58,6 +62,10 @@ export default defineComponent({ onMenuItemClick(event: Event) : void { this.$emit('menuitem-click', event); + }, + onCheckNightmode() : void + { + this.$emit('check-nightmode'); } }, setup() @@ -68,11 +76,23 @@ export default defineComponent({ const userSettingsMenu = ref>(); const toggleUserSettingsMenu = (event: Event) : void => { userSettingsMenu.value?.toggle(event) } ; - return { userSettings, userSettingsMenu, toggleUserSettings, toggleUserSettingsMenu }; + const store = useStore(); + + return { store, userSettings, userSettingsMenu, toggleUserSettings, toggleUserSettingsMenu }; + }, + computed: { + showHeaderClock() : boolean | undefined + { + // Don't mount that on small screens. + // TODO: doesn't reevaluate correctly when viewportsize changes. + const { breakpoint } = ViewportSize(); + return this.store.state.Settings.User?.HeaderClock && breakpoint.value != "sm" && breakpoint.value != "xs"; + } }, components: { 'QuickUserSettings': QuickUserSettings, 'UserSettingsMenu' : UserSettingsMenu, + 'HeaderClock': HeaderClock } }); diff --git a/js/lib/breakpoints.ts b/js/lib/breakpoints.ts index 3a7fdab7..1e1f16fb 100644 --- a/js/lib/breakpoints.ts +++ b/js/lib/breakpoints.ts @@ -1,6 +1,6 @@ import { computed, onMounted, onUnmounted, ref, ComputedRef } from "vue"; -interface ViewportSize { width: ComputedRef, height: ComputedRef, breakpoint: ComputedRef<"xs" | "md" | "lg" | "unknown"> } +interface ViewportSize { width: ComputedRef, height: ComputedRef, breakpoint: ComputedRef<"xs" | "sm" | "md" | "lg" | "xl"> } export default function () : ViewportSize { @@ -19,10 +19,11 @@ export default function () : ViewportSize // TODO: check breakpoints const breakpoint = computed(() => { - if (windowWidth.value < 550) return 'xs'; - if (windowWidth.value > 549 && windowWidth.value < 1200) return 'md'; - if (windowWidth.value > 1199) return 'lg'; - return 'unknown'; + if (windowWidth.value > 576 && windowWidth.value <= 768) return 'sm'; + if (windowWidth.value > 768 && windowWidth.value <= 992) return 'md'; + if (windowWidth.value > 992 && windowWidth.value <= 1200) return 'lg'; + if (windowWidth.value > 1200) return 'xl'; + return 'xs'; }); const width = computed(() => windowWidth.value); diff --git a/js/main.ts b/js/main.ts index 74154f1d..67cf5438 100644 --- a/js/main.ts +++ b/js/main.ts @@ -1,7 +1,10 @@ import { createApp } from 'vue'; import { loadLocaleMessages, setI18nLanguage, setupI18n } from './locale'; import router from './router'; +import { store, key } from './store'; +import { LOAD_CONFIG } from './store/mutations'; import App from './App.vue'; +import api from './api'; // PrimeVue components import PrimeVue from 'primevue/config'; @@ -22,6 +25,7 @@ app.use(PrimeVue); app.use(ToastService); app.use(router); app.use(i18n); +app.use(store, key); app.component('InputText', InputText); app.component('RadioButton', RadioButton); @@ -29,7 +33,12 @@ app.component('ToggleButton', ToggleButton); app.component('InputSwitch', InputSwitch); app.component('CheckBox', CheckBox); -// test! -loadLocaleMessages(i18n, "de").then(() => setI18nLanguage(i18n, "de")); +// load configs +api.System.GetConfig().then((config) => +{ + store.commit(LOAD_CONFIG, config); -app.mount("#app"); + loadLocaleMessages(i18n, store.state.Settings.Locale).then(() => setI18nLanguage(i18n, store.state.Settings.Locale)); + + app.mount("#app"); +}); \ No newline at end of file diff --git a/js/store/actions.ts b/js/store/actions.ts new file mode 100644 index 00000000..e5e20931 --- /dev/null +++ b/js/store/actions.ts @@ -0,0 +1,9 @@ + +export const SAVE_USER_SETTING_NIGHTMODE = 'SAVE_USER_SETTING_NIGHTMODE'; +export const SAVE_USER_SETTING_AUTORELOAD = 'SAVE_USER_SETTING_AUTORELOAD'; +export const SAVE_USER_SETTING_FULLSCREENLOCK = 'SAVE_USER_SETTING_FULLSCREENLOCK'; +export const SAVE_USER_SETTING_SCREENLOCK = 'SAVE_USER_SETTING_SCREENLOCK'; +export const SAVE_USER_SETTING_HEADERCLOCK = 'SAVE_USER_SETTING_HEADERCLOCK'; +export const SAVE_USER_SETTING_AUTONIGHTMODE = 'SAVE_USER_SETTING_AUTONIGHTMODE'; +export const SAVE_USER_SETTING_AUTONIGHTMODE_RANGE = 'SAVE_USER_SETTING_AUTONIGHTMODE_RANGE'; +export const SAVE_USER_SETTING_AUTONIGHTMODE_INVERT = 'SAVE_USER_SETTING_AUTONIGHTMODE_INVERT'; \ No newline at end of file diff --git a/js/store/index.ts b/js/store/index.ts new file mode 100644 index 00000000..f8454623 --- /dev/null +++ b/js/store/index.ts @@ -0,0 +1,19 @@ +import { InjectionKey } from 'vue'; +import { createStore, useStore as baseUseStore, Store } from 'vuex'; +import { Settings, User } from './modules'; +import { RootState } from './interfaces'; + +// define injection key +export const key: InjectionKey> = Symbol(); + +export const store = createStore({ + modules: { + Settings: Settings, + User: User, + } +}); + +export function useStore() : Store +{ + return baseUseStore(key); +} \ No newline at end of file diff --git a/js/store/interfaces.ts b/js/store/interfaces.ts new file mode 100644 index 00000000..6321a23a --- /dev/null +++ b/js/store/interfaces.ts @@ -0,0 +1,9 @@ +import { SettingsState } from './modules/settings'; +import { UserState } from './modules/user'; + +// define your typings for the store state +export interface RootState { + Settings: SettingsState, + User: UserState, +} + diff --git a/js/store/modules/index.ts b/js/store/modules/index.ts new file mode 100644 index 00000000..a86ccbff --- /dev/null +++ b/js/store/modules/index.ts @@ -0,0 +1,3 @@ +export * from './settings'; +export * from './usersettings'; +export * from './user'; \ No newline at end of file diff --git a/js/store/modules/settings.ts b/js/store/modules/settings.ts new file mode 100644 index 00000000..bf429339 --- /dev/null +++ b/js/store/modules/settings.ts @@ -0,0 +1,52 @@ +import { Module } from "vuex"; +import { RootState } from '../interfaces'; +import { UserSettings, UserSettingsState } from "./usersettings"; +import { LOAD_CONFIG } from "../mutations"; + +// removed: +// UserSettings +// FeatureFlags +// QuantityUnits +// QuantityUnitConversions + +export interface SettingsState { + CalendarShowWeekNumbers: boolean; + CalendarFirstDayOfWeek: string; + MealPlanFirstDayOfWeek: string; + Currency: string; + Locale: string; + Version: string; + User?: UserSettingsState; // ??? +} + +const Settings: Module = { + state() + { + return { + CalendarShowWeekNumbers: true, + CalendarFirstDayOfWeek: "", + Currency: "", + MealPlanFirstDayOfWeek: "", + Locale: "", + Version: "" + }; + }, + mutations: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [LOAD_CONFIG](state, newConfig: any) + { + state.CalendarFirstDayOfWeek = newConfig.CalendarFirstDayOfWeek; + state.CalendarShowWeekNumbers = newConfig.CalendarShowWeekNumbers; + state.MealPlanFirstDayOfWeek = newConfig.MealPlanFirstDayOfWeek; + state.Currency = newConfig.Currency; + state.Locale = newConfig.Locale; + state.Version = newConfig.Version; + } + }, + modules: { + User: UserSettings + } + +}; + +export { Settings }; \ No newline at end of file diff --git a/js/store/modules/user.ts b/js/store/modules/user.ts new file mode 100644 index 00000000..aef90a9e --- /dev/null +++ b/js/store/modules/user.ts @@ -0,0 +1,34 @@ +import { Module } from "vuex"; +import { RootState } from '../interfaces'; +import { LOAD_CONFIG } from "../mutations"; + +// omitted for now: +// Permissions + +export interface UserState { + Id: number; + Username: string; + PictureFileName: string | null; +} + +const User: Module = { + state() + { + return { + Id: -1, + Username: "", + PictureFileName: null + }; + }, + mutations: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [LOAD_CONFIG](state, newConfig: any) + { + state.Id = newConfig.User.Id; + state.Username = newConfig.User.Username; + state.PictureFileName = newConfig.User.PictureFileName; + } + } +}; + +export { User }; \ No newline at end of file diff --git a/js/store/modules/usersettings.ts b/js/store/modules/usersettings.ts new file mode 100644 index 00000000..bad5d2f7 --- /dev/null +++ b/js/store/modules/usersettings.ts @@ -0,0 +1,320 @@ +import { Module } from "vuex"; +import { RootState } from '../interfaces'; +import { LOAD_CONFIG, UPDATE_USER_SETTING_NIGHTMODE, UPDATE_USER_SETTING_AUTORELOAD, UPDATE_USER_SETTING_FULLSCREENLOCK, UPDATE_USER_SETTING_SCREENLOCK, UPDATE_USER_SETTING_HEADERCLOCK, UPDATE_USER_SETTING_AUTONIGHTMODE, UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE, UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT, UPDATE_USER_SETTING_UNSAVED_AUTONIGHTMODE_RANGE } from "../mutations"; +import { SAVE_USER_SETTING_NIGHTMODE, SAVE_USER_SETTING_AUTORELOAD, SAVE_USER_SETTING_FULLSCREENLOCK, SAVE_USER_SETTING_SCREENLOCK, SAVE_USER_SETTING_HEADERCLOCK, SAVE_USER_SETTING_AUTONIGHTMODE, SAVE_USER_SETTING_AUTONIGHTMODE_RANGE, SAVE_USER_SETTING_AUTONIGHTMODE_INVERT} from "../actions"; +import api from '../../api'; + +// removed: +// datatables_* foo. + +export interface UserSettingsState { + NightMode: boolean, + AutoNightMode: boolean, + AutoNightModeRange: Array, + AutoNightModeInvert: boolean, + KeepScreenOn: boolean, + KeepScreenOnWhenFullscreen: boolean, + PresetProductLocation: number, + PresetProductGroup: number, + PresetProductQuId: number, + DecimalPlacesAmount: number, + DecimalPlacesPrices: number, + DueSoonDays: number, + DefaultPurchaseAmount: number, + DefaultConsumeAmount: number, + DefaultConsumeAmountUsesQuickConsumeAmount: boolean, + ScanModeConsume: boolean, + ScanModePurchase: boolean, + ShowIconWhenProductOnShoppinglist: boolean, + ShowPurchasedDateOnPurchase: boolean, + ShowWarningOnPurchaseWhenDueDateIsEarlierThanNext: boolean, + ShoppingListToStockWorkflowAutoSubmit: boolean, + ShoppingListShowCalendar: boolean, + RecipeIngredientsGroupByProductGroup: boolean, + ChoresDueSoonDays: number, + BatteriesDueSoonDays: number, + TasksDueSoonDays: number, + AutoReload: boolean, + HeaderClock: boolean, + QuaggaWorkers: number, + QuaggaHalfsample: boolean, + QuaggaPatchsize: string, + QuaggaFrequency: number, + QuaggaDebug: boolean, + + // For throtteling + UnsavedAutoNightModeRange: Array +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function ensureBool(whatever: any, fallback = false): boolean +{ + if (typeof whatever === "string") + { + return whatever === "1"; + } + if (typeof whatever === "boolean") + { + return whatever; + } + if (typeof whatever === "number") + { + return whatever > 0; + } + + return fallback; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function ensureInt(whatever: any, fallback = 0): number +{ + if (typeof whatever === "string") + { + return parseInt(whatever); + } + if (typeof whatever === "number") + { + return whatever; + } + + return fallback; +} + +const UserSettings: Module = { + state(): UserSettingsState + { + return { + NightMode: false, + AutoNightMode: true, + AutoNightModeRange: [8.0, 20.0], + UnsavedAutoNightModeRange: [8.0, 20.0], + AutoNightModeInvert: true, + KeepScreenOn: false, + KeepScreenOnWhenFullscreen: true, + PresetProductLocation: -1, + PresetProductGroup: -1, + PresetProductQuId: -1, + DecimalPlacesAmount: 4, + DecimalPlacesPrices: 2, + DueSoonDays: 5, + DefaultPurchaseAmount: 0, + DefaultConsumeAmount: 1, + DefaultConsumeAmountUsesQuickConsumeAmount: true, + ScanModeConsume: false, + ScanModePurchase: false, + ShowIconWhenProductOnShoppinglist: true, + ShowPurchasedDateOnPurchase: false, + ShowWarningOnPurchaseWhenDueDateIsEarlierThanNext: true, + ShoppingListToStockWorkflowAutoSubmit: false, + ShoppingListShowCalendar: false, + RecipeIngredientsGroupByProductGroup: false, + ChoresDueSoonDays: 5, + BatteriesDueSoonDays: 5, + TasksDueSoonDays: 5, + AutoReload: true, + HeaderClock: false, + QuaggaWorkers: 4, + QuaggaHalfsample: false, + QuaggaPatchsize: "medium", + QuaggaFrequency: 10, + QuaggaDebug: true + }; + }, + mutations: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [LOAD_CONFIG](state, newConfig: any) + { + state.NightMode = ensureBool(newConfig.User.Settings.night_mode_enabled); + state.AutoNightMode = ensureBool(newConfig.User.Settings.auto_night_mode_enabled); + + const from: Array = newConfig.User.Settings.auto_night_mode_time_range_from.split(":"); + const to: Array = newConfig.User.Settings.auto_night_mode_time_range_to.split(":"); + let start: number, end: number; + start = parseFloat(from[0]) + parseFloat(from[1]) / 60; + end = parseFloat(to[0]) + parseFloat(to[1]) / 60; + if (end < start) + { + const tmp = start; + start = end; + end = tmp; + } + + state.AutoNightModeRange = [start, end]; + state.UnsavedAutoNightModeRange = [start, end]; + state.AutoNightModeInvert = ensureBool(newConfig.User.Settings.auto_night_mode_time_range_goes_over_midnight); + state.KeepScreenOn = newConfig.User.Settings.keep_screen_on; + state.KeepScreenOnWhenFullscreen = newConfig.User.Settings.keep_screen_on_when_fullscreen_card; + state.PresetProductLocation = newConfig.User.Settings.product_presets_location_id; + state.PresetProductGroup = newConfig.User.Settings.product_presets_product_group_id; + state.PresetProductQuId = newConfig.User.Settings.product_presets_qu_id; + state.DecimalPlacesAmount = newConfig.User.Settings.stock_decimal_places_amounts; + state.DecimalPlacesPrices = newConfig.User.Settings.stock_decimal_places_prices; + state.DueSoonDays = newConfig.User.Settings.stock_due_soon_days; + state.DefaultPurchaseAmount = newConfig.User.Settings.stock_default_purchase_amount; + state.DefaultConsumeAmount = ensureInt(newConfig.User.Settings.stock_default_consume_amount); + state.DefaultConsumeAmountUsesQuickConsumeAmount = ensureBool(newConfig.User.Settings.stock_default_consume_amount_use_quick_consume_amount); + state.ScanModeConsume = ensureBool(newConfig.User.Settings.scan_mode_consume_enabled); + state.ScanModePurchase = ensureBool(newConfig.User.Settings.scan_mode_purchase_enabled); + state.ShowIconWhenProductOnShoppinglist = newConfig.User.Settings.show_icon_on_stock_overview_page_when_product_is_on_shopping_list; + state.ShowPurchasedDateOnPurchase = newConfig.User.Settings.show_purchased_date_on_purchase; + state.ShowWarningOnPurchaseWhenDueDateIsEarlierThanNext = newConfig.User.Settings.show_warning_on_purchase_when_due_date_is_earlier_than_next; + state.ShoppingListToStockWorkflowAutoSubmit = newConfig.User.Settings.shopping_list_to_stock_workflow_auto_submit_when_prefilled; + state.ShoppingListShowCalendar = newConfig.User.Settings.shopping_list_show_calendar; + state.RecipeIngredientsGroupByProductGroup = newConfig.User.Settings.recipe_ingredients_group_by_product_group; + state.ChoresDueSoonDays = newConfig.User.Settings.chores_due_soon_days; + state.BatteriesDueSoonDays = newConfig.User.Settings.batteries_due_soon_days; + state.TasksDueSoonDays = newConfig.User.Settings.tasks_due_soon_days; + state.AutoReload = newConfig.User.Settings.auto_reload_on_db_change; + state.HeaderClock = ensureBool(newConfig.User.Settings.show_clock_in_header); + state.QuaggaWorkers = newConfig.User.Settings.quagga2_numofworkers; + state.QuaggaHalfsample = newConfig.User.Settings.quagga2_halfsample; + state.QuaggaPatchsize = newConfig.User.Settings.quagga2_patchsize; + state.QuaggaFrequency = newConfig.User.Settings.quagga2_frequency; + state.QuaggaDebug = newConfig.User.Settings.quagga2_debug; + }, + [UPDATE_USER_SETTING_NIGHTMODE](state, newValue: boolean) + { + state.NightMode = newValue; + }, + [UPDATE_USER_SETTING_AUTORELOAD](state, newValue: boolean) + { + state.AutoReload = newValue; + }, + [UPDATE_USER_SETTING_FULLSCREENLOCK](state, newValue: boolean) + { + state.KeepScreenOnWhenFullscreen = newValue; + }, + [UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE](state, newValue: Array) + { + state.AutoNightModeRange[0] = newValue[0]; + state.AutoNightModeRange[1] = newValue[1]; + }, + [UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT](state, newValue: boolean) + { + state.AutoNightModeInvert = newValue; + }, + [UPDATE_USER_SETTING_AUTONIGHTMODE](state, newValue: boolean) + { + state.AutoNightMode = newValue; + }, + [UPDATE_USER_SETTING_FULLSCREENLOCK](state, newValue: boolean) + { + state.KeepScreenOnWhenFullscreen = newValue; + }, + [UPDATE_USER_SETTING_SCREENLOCK](state, newValue: boolean) + { + state.KeepScreenOn = newValue; + }, + [UPDATE_USER_SETTING_HEADERCLOCK](state, newValue: boolean) + { + state.HeaderClock = newValue; + }, + [UPDATE_USER_SETTING_UNSAVED_AUTONIGHTMODE_RANGE](state, newValue: Array) + { + state.UnsavedAutoNightModeRange[0] = newValue[0]; + state.UnsavedAutoNightModeRange[1] = newValue[1]; + } + }, + actions: { + [SAVE_USER_SETTING_NIGHTMODE](state, newValue: boolean) + { + const oldValue = state.state.NightMode; + state.commit(UPDATE_USER_SETTING_NIGHTMODE, newValue); + + return api.Settings.SaveUserSetting('night_mode_enabled', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_NIGHTMODE, oldValue); + }); + }, + [SAVE_USER_SETTING_AUTONIGHTMODE](state, newValue: boolean) + { + const oldValue = state.state.AutoNightMode; + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE, newValue); + return api.Settings.SaveUserSetting('auto_night_mode_enabled', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE, oldValue); + }); + }, + [SAVE_USER_SETTING_AUTONIGHTMODE_RANGE](state, newValue: Array | null) + { + let target: Array; + + if (newValue != null) + target = newValue; + else + target = state.state.UnsavedAutoNightModeRange; + + const oldValue = state.state.AutoNightModeRange; + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE, target); + + // some processing is required for the API. + const fromHours = Math.floor(target[0]); + const fromMinutes = Math.floor((target[0] - fromHours) * 60); + const toHours = Math.floor(target[1]); + const toMinutes = Math.floor((target[1] - toHours) * 60); + const fromString = `${fromHours}:${fromMinutes}`; + const toStr = `${toHours}:${toMinutes}`; + + // requires two calls. if either of them fail, restore old state. + const afrom = api.Settings.SaveUserSetting('auto_night_mode_time_range_from', fromString).catch(() => + { + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE, oldValue); + }); + const ato = api.Settings.SaveUserSetting('auto_night_mode_time_range_to', toStr).catch(() => + { + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE, oldValue); + }); + + return Promise.all([afrom, ato]); + }, + [SAVE_USER_SETTING_AUTONIGHTMODE_INVERT](state, newValue: boolean) + { + const oldValue = state.state.AutoNightModeInvert; + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT, newValue); + return api.Settings.SaveUserSetting('auto_night_mode_time_range_goes_over_midnight', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT, oldValue); + }); + }, + [SAVE_USER_SETTING_AUTORELOAD](state, newValue: boolean) + { + const oldValue = state.state.AutoReload; + state.commit(UPDATE_USER_SETTING_AUTORELOAD, newValue); + api.Settings.SaveUserSetting('auto_reload_on_db_change', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_AUTORELOAD, oldValue); + }); + }, + [SAVE_USER_SETTING_FULLSCREENLOCK](state, newValue: boolean) + { + const oldValue = state.state.KeepScreenOnWhenFullscreen; + state.commit(UPDATE_USER_SETTING_FULLSCREENLOCK, newValue); + api.Settings.SaveUserSetting('keep_screen_on_when_fullscreen_card', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_FULLSCREENLOCK, oldValue); + }); + }, + [SAVE_USER_SETTING_SCREENLOCK](state, newValue: boolean) + { + const oldValue = state.state.AutoReload; + state.commit(UPDATE_USER_SETTING_SCREENLOCK, newValue); + api.Settings.SaveUserSetting('keep_screen_on', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_SCREENLOCK, oldValue); + }); + }, + [SAVE_USER_SETTING_HEADERCLOCK](state, newValue: boolean) + { + const oldValue = state.state.KeepScreenOn; + state.commit(UPDATE_USER_SETTING_HEADERCLOCK, newValue); + return api.Settings.SaveUserSetting('show_clock_in_header', newValue).catch(() => + { + state.commit(UPDATE_USER_SETTING_HEADERCLOCK, oldValue); + }); + }, + + } + +}; + +export { UserSettings }; \ No newline at end of file diff --git a/js/store/mutations.ts b/js/store/mutations.ts new file mode 100644 index 00000000..5057e54b --- /dev/null +++ b/js/store/mutations.ts @@ -0,0 +1,12 @@ +// State mutations + +export const LOAD_CONFIG = 'LOAD_CONFIG'; +export const UPDATE_USER_SETTING_NIGHTMODE = 'UPDATE_USER_SETTING_NIGHTMODE'; +export const UPDATE_USER_SETTING_AUTORELOAD = 'UPDATE_USER_SETTING_AUTORELOAD'; +export const UPDATE_USER_SETTING_FULLSCREENLOCK = 'UPDATE_USER_SETTING_FULLSCREENLOCK'; +export const UPDATE_USER_SETTING_SCREENLOCK = 'UPDATE_USER_SETTING_SCREENLOCK'; +export const UPDATE_USER_SETTING_HEADERCLOCK = 'UPDATE_USER_SETTING_HEADERCLOCK'; +export const UPDATE_USER_SETTING_AUTONIGHTMODE = 'UPDATE_USER_SETTING_AUTONIGHTMODE'; +export const UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE = 'UPDATE_USER_SETTING_AUTONIGHTMODE_RANGE'; +export const UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT = 'UPDATE_USER_SETTING_AUTONIGHTMODE_INVERT'; +export const UPDATE_USER_SETTING_UNSAVED_AUTONIGHTMODE_RANGE = 'UPDATE_USER_SETTING_UNSAVED_AUTONIGHTMODE_RANGE'; \ No newline at end of file diff --git a/package.json b/package.json index d6a13f65..5f640602 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "uuid": "^8.3.2", "vue": "next", "vue-i18n": "next", - "vue-router": "4" + "vue-router": "4", + "vuex": "next" }, "devDependencies": { "@babel/core": "^7.14.6", diff --git a/php/Controllers/SystemApiController.php b/php/Controllers/SystemApiController.php index 8d7dd323..35613d49 100644 --- a/php/Controllers/SystemApiController.php +++ b/php/Controllers/SystemApiController.php @@ -75,7 +75,9 @@ public function GetGrocyConfig(\Psr\Http\Message\ServerRequestInterface $request if(GROCY_AUTHENTICATED) { $user = $this->getSessionService()->GetDefaultUser(); $GrocyConfig["User"]["Settings"] = array_merge($GrocyConfig["User"]["Settings"], $this->getUsersService()->GetUserSettings($user->id)); - $GrocyConfig["User"]["Permissions"] = array_merge($GrocyConfig["User"]["Permissions"], User::PermissionList()); + $perms = User::PermissionList(); + foreach($perms as $value) + $GrocyConfig["User"]["Permissions"][$value->permission_name] = $value->has_permission; $GrocyConfig["User"]["Id"] = $user->id; diff --git a/scss/sigma/_overrides.scss b/scss/sigma/_overrides.scss index 45f8e540..de2f4c5f 100644 --- a/scss/sigma/_overrides.scss +++ b/scss/sigma/_overrides.scss @@ -137,4 +137,9 @@ body { &:focus { @include focused-inset(); } +} + +.layout-headerclock { + float: left; + font-size: 20px; } \ No newline at end of file diff --git a/scss/sigma/sass/_menu.scss b/scss/sigma/sass/_menu.scss index f06f76cb..613567f6 100644 --- a/scss/sigma/sass/_menu.scss +++ b/scss/sigma/sass/_menu.scss @@ -18,6 +18,8 @@ i { font-size: 16px; + width: 2em; + text-align: center; } span { diff --git a/yarn.lock b/yarn.lock index a6e6506e..d11a5b3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2382,7 +2382,7 @@ __metadata: languageName: node linkType: hard -"@vue/devtools-api@npm:^6.0.0-beta.14, @vue/devtools-api@npm:^6.0.0-beta.7": +"@vue/devtools-api@npm:^6.0.0-beta.11, @vue/devtools-api@npm:^6.0.0-beta.14, @vue/devtools-api@npm:^6.0.0-beta.7": version: 6.0.0-beta.15 resolution: "@vue/devtools-api@npm:6.0.0-beta.15" checksum: 6e76f02a9aa34cd8712c4897569ebf69f6ddb387e44d5162a98a1b5dc4528c655689eade4453b88f2479ba75e58fb10aa7333c8bb6f4f6e0e9e91fe6f8b780eb @@ -6482,6 +6482,7 @@ fsevents@~2.3.2: vue-i18n: next vue-i18n-extract: ^1.2.3 vue-router: 4 + vuex: next languageName: unknown linkType: soft @@ -12818,6 +12819,17 @@ typescript@^4.3.5: languageName: node linkType: hard +"vuex@npm:next": + version: 4.0.2 + resolution: "vuex@npm:4.0.2" + dependencies: + "@vue/devtools-api": ^6.0.0-beta.11 + peerDependencies: + vue: ^3.0.2 + checksum: cb98e64928a9da876e4df12e0aaa92a70e93a391356c8a9458fd8380b8ed6a850991cc78888535a77724062d5ced38e5a2d0e50278abb56a8b9b076b173187c1 + languageName: node + linkType: hard + "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" From 2a5ff20be2eab9ad4fe0c8e024efa04e5b89df71 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Sun, 4 Jul 2021 21:08:55 +0200 Subject: [PATCH 11/16] Basic Stockoverview page --- Makefile | 2 +- buildfiles/convert-locales.php | 18 +- js/api/StockApi.ts | 51 +++++ js/api/index.ts | 4 + js/main.ts | 24 ++- js/pages/Stock/Overview.vue | 247 ++++++++++++++++++++++++- locale/cs.json | 52 +++--- locale/da.json | 52 +++--- locale/de.json | 42 ++--- locale/el_GR.json | 52 +++--- locale/en.json | 18 +- locale/en_GB.json | 52 +++--- locale/es.json | 52 +++--- locale/fi.json | 54 +++--- locale/fr.json | 52 +++--- locale/he_IL.json | 52 +++--- locale/hu.json | 54 +++--- locale/it.json | 54 +++--- locale/ja.json | 52 +++--- locale/ko_KR.json | 52 +++--- locale/nl.json | 52 +++--- locale/no.json | 52 +++--- locale/pl.json | 52 +++--- locale/pt_BR.json | 54 +++--- locale/pt_PT.json | 49 +++-- locale/ru.json | 52 +++--- locale/sk_SK.json | 52 +++--- locale/sv_SE.json | 52 +++--- locale/ta.json | 52 +++--- locale/tr.json | 45 ++--- locale/zh_CN.json | 52 +++--- locale/zh_TW.json | 52 +++--- php/Controllers/StockApiController.php | 10 + php/routes.php | 1 + scss/sigma/_overrides.scss | 58 ++++++ 35 files changed, 979 insertions(+), 742 deletions(-) create mode 100644 js/api/StockApi.ts diff --git a/Makefile b/Makefile index eecec615..b1f22ec3 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ help: # Install all dependencies, then build the public/ folder. .PHONY=build -build: vendor js css public/js/locales/grocy/en.json resources +build: vendor js css $(LANGOBJS) public/js/locales/grocy/en.json resources .PHONY=minify minify: build diff --git a/buildfiles/convert-locales.php b/buildfiles/convert-locales.php index affc4544..5c18f206 100644 --- a/buildfiles/convert-locales.php +++ b/buildfiles/convert-locales.php @@ -1,12 +1,14 @@ Po as $translation) { + $hasPlural = $translation->hasPlural(); if(!$isFirst) { fwrite($lf, ",\n"); } $isFirst = false; - $orig = clean($translation->getOriginal()); - $hasPlural = $translation->hasPlural(); + $orig = clean($translation->getOriginal(), $hasPlural); if($hasPlural) { - $orig .= ' | ' . clean($translation->getPlural()); + $orig .= ' | ' . clean($translation->getPlural(), $hasPlural); } - $trans = clean($translation->getTranslation()); + $trans = clean($translation->getTranslation(), $hasPlural); if($hasPlural) { $plTrans = $translation->getPluralTranslations(); foreach($plTrans as $pTrans) { - $trans .= ' | ' . clean($pTrans); + $trans .= ' | ' . clean($pTrans, $hasPlural); } } diff --git a/js/api/StockApi.ts b/js/api/StockApi.ts new file mode 100644 index 00000000..de769cf0 --- /dev/null +++ b/js/api/StockApi.ts @@ -0,0 +1,51 @@ +import BaseApi from "./BaseApi"; + +class StockApi extends BaseApi +{ + fetchOptions: RequestInit; + + constructor(baseUrl: string) + { + super(baseUrl); + this.fetchOptions = { + cache: 'no-cache', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + redirect: 'follow', + referrerPolicy: 'no-referrer' + }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async GetAll(): Promise + { + // I'm sure there is a way, I just don't know it. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const options = {}; + Object.assign(options, this.fetchOptions); + options.method = "GET"; + + const endpoint = this.baseUrl + '/stock'; + + return this.execute(endpoint, options); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async Overview(): Promise + { + // I'm sure there is a way, I just don't know it. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const options = {}; + Object.assign(options, this.fetchOptions); + options.method = "GET"; + + const endpoint = this.baseUrl + '/stock/overview'; + + return this.execute(endpoint, options); + } +} + +export default StockApi; \ No newline at end of file diff --git a/js/api/index.ts b/js/api/index.ts index d4c786d0..d31c035b 100644 --- a/js/api/index.ts +++ b/js/api/index.ts @@ -1,12 +1,14 @@ import BaseApi from './BaseApi'; import SettingsApi from './SettingsApi'; import SystemApi from './SystemApi'; +import StockApi from './StockApi'; class GrocyApi extends BaseApi { fetchOptions: RequestInit; Settings: SettingsApi; System: SystemApi; + Stock: StockApi; constructor(baseUrl: string) { @@ -24,6 +26,7 @@ class GrocyApi extends BaseApi this.Settings = new SettingsApi(baseUrl); this.System = new SystemApi(baseUrl); + this.Stock = new StockApi(baseUrl); } SetBase(newBase: string) : void @@ -31,6 +34,7 @@ class GrocyApi extends BaseApi super.SetBase(newBase); this.Settings.SetBase(newBase); this.System.SetBase(newBase); + this.Stock.SetBase(newBase); } } diff --git a/js/main.ts b/js/main.ts index 67cf5438..8f0344db 100644 --- a/js/main.ts +++ b/js/main.ts @@ -17,6 +17,11 @@ import RadioButton from 'primevue/radiobutton'; import ToggleButton from 'primevue/togglebutton'; import InputSwitch from 'primevue/inputswitch'; import CheckBox from 'primevue/checkbox'; +import Card from 'primevue/card'; +import Button from 'primevue/button'; +import DataTable from 'primevue/datatable'; +import Column from 'primevue/column'; +import ContextMenu from 'primevue/contextmenu'; const app = createApp(App); const i18n = setupI18n(); @@ -32,13 +37,24 @@ app.component('RadioButton', RadioButton); app.component('ToggleButton', ToggleButton); app.component('InputSwitch', InputSwitch); app.component('CheckBox', CheckBox); +app.component('Card', Card); +app.component('Button', Button); +app.component('DataTable', DataTable); +app.component('Column', Column); +app.component('ContextMenu', ContextMenu); // load configs api.System.GetConfig().then((config) => { store.commit(LOAD_CONFIG, config); - - loadLocaleMessages(i18n, store.state.Settings.Locale).then(() => setI18nLanguage(i18n, store.state.Settings.Locale)); - - app.mount("#app"); + const promises = [loadLocaleMessages(i18n, "en")]; + if (store.state.Settings.Locale != "en") + { + promises.push(loadLocaleMessages(i18n, store.state.Settings.Locale)); + } + Promise.all(promises).then(() => + { + setI18nLanguage(i18n, store.state.Settings.Locale); + app.mount("#app"); + }); }); \ No newline at end of file diff --git a/js/pages/Stock/Overview.vue b/js/pages/Stock/Overview.vue index 5b122728..aedac4fe 100644 --- a/js/pages/Stock/Overview.vue +++ b/js/pages/Stock/Overview.vue @@ -1,11 +1,252 @@ diff --git a/locale/cs.json b/locale/cs.json index bd005eb8..eeb90714 100644 --- a/locale/cs.json +++ b/locale/cs.json @@ -1,12 +1,12 @@ { "Stock overview": "Přehled zásob", - "{string0} product expires | {string0} products expiring": "{string0} produkt se zkazí | {string0} produkty se zkazí | {string0} produktů se zkazí | {string0} produktů se zkazí", - "within the next day | within the next {string0} days": "během následujícího dne | během následujících {string0} dní | během následujících {string0} dní | během následujících {string0} dní", - "{string0} product is already expired | {string0} products are already expired": "{string0} produkt má prošlou záruku | {string0} produkty jsou po záruce | {string0} produktů je po záruce | {string0} produktů je po záruce", - "{string0} product is overdue | {string0} products are overdue": "{string0} produkt je po době spotřeby | {string0} produkty jsou po době spotřeby | {string0} produktů je po době spotřeby | {string0} produktů je po době spotřeby", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produkt nemá dostatečnou minimální zásobu | {string0} produkty nemají dostatečnou minimální zásobu | {string0} produktů nemá dostatečnou minimální zásobu | {string0} produktů nemá dostatečnou minimální zásobu", + "{count} product expires | {count} products expiring": "{count} produkt se zkazí | {count} produkty se zkazí | {count} produktů se zkazí | {count} produktů se zkazí", + "within the next day | within the next {count} days": "během následujícího dne | během následujících {count} dní | během následujících {count} dní | během následujících {count} dní", + "{count} product is already expired | {count} products are already expired": "{count} produkt má prošlou záruku | {count} produkty jsou po záruce | {count} produktů je po záruce | {count} produktů je po záruce", + "{count} product is overdue | {count} products are overdue": "{count} produkt je po době spotřeby | {count} produkty jsou po době spotřeby | {count} produktů je po době spotřeby | {count} produktů je po době spotřeby", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produkt nemá dostatečnou minimální zásobu | {count} produkty nemají dostatečnou minimální zásobu | {count} produktů nemá dostatečnou minimální zásobu | {count} produktů nemá dostatečnou minimální zásobu", "Product": "Produkt", - "{string0} Product | {string0} Products": "{string0} produkt | {string0} produktů | {string0} produktů | {string0} produktů", + "{count} Product | {count} Products": "{count} produkt | {count} produktů | {count} produktů | {count} produktů", "Amount": "Množství", "Logout": "Odhlásit se", "Chores overview": "Přehled povinností", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Splnění požadavků", "Put missing products on shopping list": "Vložit chybějící produkty do nákupního seznamu", "Enough in stock": "Dostatečná zásoba", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Nedostatečná zásoba, chybí {string0} surovina, které je ale již na nákupním seznamu | Nedostatečná zásoba, chybí {string0} suroviny, které jsou ale již na nákupním seznamu | Nedostatečná zásoba, chybí {string0} surovin, které jsou ale již na nákupním seznamu | Nedostatečná zásoba, chybí {string0} surovin, které jsou ale již na nákupním seznamu", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Nedostatečná zásoba, chybí {count} surovina, které je ale již na nákupním seznamu | Nedostatečná zásoba, chybí {count} suroviny, které jsou ale již na nákupním seznamu | Nedostatečná zásoba, chybí {count} surovin, které jsou ale již na nákupním seznamu | Nedostatečná zásoba, chybí {count} surovin, které jsou ale již na nákupním seznamu", "Expand to fullscreen": "Rozšířit na celou obrazovku", "Ingredients": "Suroviny", "Preparation": "Příprava", @@ -177,11 +177,11 @@ "No price history available": "K dispozici není žádná historie cen", "Price": "Cena", "Unit": "Jednotka", - "{string0} Unit | {string0} Units": "{string0} jednotka | {string0} jednotek | {string0} jednotek | {string0} jednotek", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} povinnost by měla být splněna | {string0} povinnosti by měly být splněny | {string0} povinností by mělo být splněno | {string0} povinností by měly být splněny", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} povinnost už měla být splněna | {string0} povinnosti již mělo být splněno | {string0} povinností již mělo být splněno | {string0} povinností již mělo být splněno", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} baterie je potřeba vyměnit | {string0} baterií je potřeba vyměnit | {string0} baterií je potřeba vyměnit | {string0} baterií je potřeba vyměnit", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} baterie již měla být vyměněna | {string0} baterií již mělo být vyměněno | {string0} baterií již mělo být vyměněno | {string0} baterií již mělo být vyměněno", + "{count} Unit | {count} Units": "{count} jednotka | {count} jednotek | {count} jednotek | {count} jednotek", + "{count} chore is due to be done | {count} chores are due to be done": "{count} povinnost by měla být splněna | {count} povinnosti by měly být splněny | {count} povinností by mělo být splněno | {count} povinností by měly být splněny", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} povinnost už měla být splněna | {count} povinnosti již mělo být splněno | {count} povinností již mělo být splněno | {count} povinností již mělo být splněno", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} baterie je potřeba vyměnit | {count} baterií je potřeba vyměnit | {count} baterií je potřeba vyměnit | {count} baterií je potřeba vyměnit", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} baterie již měla být vyměněna | {count} baterií již mělo být vyměněno | {count} baterií již mělo být vyměněno | {count} baterií již mělo být vyměněno", "in singular form": "v jednotném čísle", "Quantity unit": "Měrná jednotka", "Only check if any amount is in stock": "Zkontrolovat pouze zda je v zásobě jakékoliv množství", @@ -204,8 +204,8 @@ "Category": "Kategorie", "Edit task": "Upravit úkol", "Are you sure to delete task \"{string0}\"?": "Opravdu chcete smazat úkol \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} úkol má být splněný | {string0} úkolů má být splněno | {string0} úkolů má být splněno | {string0} úkolů má být splněno", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} úkol již měl být splněn | {string0} úkoly již měly být splněny | {string0} úkolů již mělo být splněno | {string0} úkolů již mělo být splněno", + "{count} task is due to be done | {count} tasks are due to be done": "{count} úkol má být splněný | {count} úkolů má být splněno | {count} úkolů má být splněno | {count} úkolů má být splněno", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} úkol již měl být splněn | {count} úkoly již měly být splněny | {count} úkolů již mělo být splněno | {count} úkolů již mělo být splněno", "Edit task category": "Upravit kategorii úkolu", "Create task category": "Vytvořit kategorii úkolů", "Product groups": "Skupiny produktů", @@ -351,7 +351,7 @@ "Plural count": "Počet možností množných čísel: ", "Plural rule": "Pravidlo množného čísla", "in plural form": "v množném čísle", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Nedostatečná zásoba, chybí {string0} surovina | Nedostatečná zásoba, chybí {string0} suroviny | Nedostatečná zásoba, chybí {string0} surovin | Nedostatečná zásoba, chybí {string0} surovin", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Nedostatečná zásoba, chybí {count} surovina | Nedostatečná zásoba, chybí {count} suroviny | Nedostatečná zásoba, chybí {count} surovin | Nedostatečná zásoba, chybí {count} surovin", "Not enough in stock, but already on the shopping list": "Nedostatečná zásoba, ale již na nákupním seznamu", "Not enough in stock": "Nedostatečná zásoba", "Expiring soon days": "Trvanlivost brzy skončí", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Spotřebovat {string1} z {string2}", "Meal plan": "Jídelníček", "Add recipe on {string0}": "Přidat recept do {string0}", - "{string0} serving | {string0} servings": "{string0} porce | {string0} porcí | {string0} porcí | {string0} porcí", + "{count} serving | {count} servings": "{count} porce | {count} porcí | {count} porcí | {count} porcí", "Week costs": "Týdenní útrata", "Configuration": "Nastavení", "A predefined list of values, one per line": "Předdefinovaný seznam hodnot, jedna na řádek", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "To znamená, že další splnění povinnosti bude zadáno dalšímu uživateli, řazeno dle abecedy", "Assign to": "Přiřadit k", "This assignment type requires that at least one is assigned": "Typ přiřazení vyžaduje nejméně jednoho přiřazeného", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0}povinost je zadána mi | {string0} povinností je zadáno mi | {string0}povinností je zadáno mi | {string0} povinnosti jsou zadány mi", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count}povinost je zadána mi | {count} povinností je zadáno mi | {count}povinností je zadáno mi | {count} povinnosti jsou zadány mi", "Assigned to me": "Přiřazeno mě", "assigned to {string0}": "přiřazeno k {string0}", "Assignment": "Přidělení", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Při přesunutí produktu z mrazničky (produkt rozmražen), bude nastavené datum doby spotřeby dnes + tento počet dní", "Default due days after thawing": "Výchozí počet dní spotřeby po rozmrznutí", "Next due date": "Další datum spotřeby", - "{string0} product is due | {string0} products are due": "{string0}produkt po době spotřeby | {string0} produtky po době spotřeby | {string0} produktů po době spotřeby | {string0} produktů po době spotřeby", + "{count} product is due | {count} products are due": "{count}produkt po době spotřeby | {count} produtky po době spotřeby | {count} produktů po době spotřeby | {count} produktů po době spotřeby", "Due date": "Datum spotřeby", "Never overdue": "Žádné datum spotřeby", - "{string0} product is expired | {string0} products are expired": "{string0} produkt expiroval | {string0} produkty expirovaly | {string0} produktů expirovalo | {string0} produktů expirovalo", + "{count} product is expired | {count} products are expired": "{count} produkt expiroval | {count} produkty expirovaly | {count} produktů expirovalo | {count} produktů expirovalo", "Expired": "Expirováno", "Due soon days": "", "Add overdue/expired products": "Přidat trvanlivost/expiraci produktu", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Hebrejsky (Izraeil)", "Tamil": "", "Finnish": "Finsky", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/da.json b/locale/da.json index cff42bad..08b48532 100644 --- a/locale/da.json +++ b/locale/da.json @@ -1,12 +1,12 @@ { "Stock overview": "Lageroversigt", - "{string0} product expires | {string0} products expiring": "{string0} produkt udløber | {string0} varer udløber", - "within the next day | within the next {string0} days": "indenfor den næstkommende dag | indenfor de næste {string0} dage", - "{string0} product is already expired | {string0} products are already expired": "{string0} vare er allerede udløbet | {string0} varer er allerede udløbet", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} vare er under den definerede minimumsbeholdning | {string0} varer er under den definerede minimumsbeholdning", + "{count} product expires | {count} products expiring": "{count} produkt udløber | {count} varer udløber", + "within the next day | within the next {count} days": "indenfor den næstkommende dag | indenfor de næste {count} dage", + "{count} product is already expired | {count} products are already expired": "{count} vare er allerede udløbet | {count} varer er allerede udløbet", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} vare er under den definerede minimumsbeholdning | {count} varer er under den definerede minimumsbeholdning", "Product": "Vare", - "{string0} Product | {string0} Products": "{string0} Vare | {string0} Varer", + "{count} Product | {count} Products": "{count} Vare | {count} Varer", "Amount": "Mængde", "Logout": "Log ud", "Chores overview": "Pligtoversigt", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Skal udfyldes", "Put missing products on shopping list": "Sæt manglende varer på en indkøbsliste", "Enough in stock": "Der er nok i beholdningen", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Ikke nok i beholdningen, {string0} ingrediens mangler men er allerede på indkøbslisten | Ikke nok i beholdningen, {string0} ingredienser mangler men er allerede på indkøbslisten", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Ikke nok i beholdningen, {count} ingrediens mangler men er allerede på indkøbslisten | Ikke nok i beholdningen, {count} ingredienser mangler men er allerede på indkøbslisten", "Expand to fullscreen": "Udvid til fuldskærm", "Ingredients": "Ingredienser", "Preparation": "Tilberedning", @@ -177,11 +177,11 @@ "No price history available": "Ingen prishistorik tilgængelig", "Price": "Pris", "Unit": "Enhed", - "{string0} Unit | {string0} Units": "{string0} Enhed | {string0} Enheder ", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} pligt forfalder nu | {string0} pligter forfalder nu", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} pligt er forfalden | {string0} pligter er forfaldne", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batteri skal oplades nu | {string0} batterier skal oplades", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batteri skulle have været opladet | {string0} batterier skulle have være opladet", + "{count} Unit | {count} Units": "{count} Enhed | {count} Enheder ", + "{count} chore is due to be done | {count} chores are due to be done": "{count} pligt forfalder nu | {count} pligter forfalder nu", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} pligt er forfalden | {count} pligter er forfaldne", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batteri skal oplades nu | {count} batterier skal oplades", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batteri skulle have været opladet | {count} batterier skulle have være opladet", "in singular form": "i ental", "Quantity unit": "Mængdeenhed", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Kategori", "Edit task": "Rediger opgave", "Are you sure to delete task \"{string0}\"?": "Er du sikker på at du vil slette opgaven \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} opgaven forfalder nu | {string0} opgaver forfalder nu", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} opgaver er forfaldne | {string0} opgaver er forfaldne", + "{count} task is due to be done | {count} tasks are due to be done": "{count} opgaven forfalder nu | {count} opgaver forfalder nu", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} opgaver er forfaldne | {count} opgaver er forfaldne", "Edit task category": "Rediger opgavekategori", "Create task category": "Opret opgavekategori", "Product groups": "Varegrupper", @@ -351,7 +351,7 @@ "Plural count": "Flertalsantal", "Plural rule": "Flertalsregel", "in plural form": "i flertal", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Ikke nok i beholdningen, {string0} ingrediens mangler | Ikke nok i beholdningen, {string0} ingredienser mangler", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Ikke nok i beholdningen, {count} ingrediens mangler | Ikke nok i beholdningen, {count} ingredienser mangler", "Not enough in stock, but already on the shopping list": "Ikke nok på lager, men allerede på indkøbslisten", "Not enough in stock": "Ikke nok på lager", "Expiring soon days": "Udløber snart - antal dage", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Forbrug {string1} af {string2}", "Meal plan": "Måltidsplan", "Add recipe on {string0}": "Tilføj opskrift på {string0}", - "{string0} serving | {string0} servings": "{string0} portion | {string0} portioner", + "{count} serving | {count} servings": "{count} portion | {count} portioner", "Week costs": "Ugentlige omkostninger", "Configuration": "Konfiguration", "A predefined list of values, one per line": "En prædefineret liste af værdier, en per linje", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Det betyder at den næste udførelse af denne pligt vil blive tildelt til den næste i alfabetisk rækkefølge", "Assign to": "Tildel til", "This assignment type requires that at least one is assigned": "Denne opgavetype kræver, at der tildeles mindst en", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0}pligt er tildelt til mig | {string0}pligter er tildelt til mig", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count}pligt er tildelt til mig | {count}pligter er tildelt til mig", "Assigned to me": "Tildelt til mig", "assigned to {string0}": "tildelt til {string0}", "Assignment": "Opgave", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -830,22 +830,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/de.json b/locale/de.json index 7ac8ae2e..223a538d 100644 --- a/locale/de.json +++ b/locale/de.json @@ -1,12 +1,12 @@ { "Stock overview": "Bestand", - "{string0} product expires | {string0} products expiring": "{string0} läuft ab | {string0} Produkte laufen ab", - "within the next day | within the next {string0} days": "innerhalb des nächsten Tages | innerhalb der nächsten {string0} Tage", - "{string0} product is already expired | {string0} products are already expired": "{string0} Produkt ist bereits abgelaufen | {string0} Produkte sind bereits abgelaufen", - "{string0} product is overdue | {string0} products are overdue": "{string0} Produkt ist überfällig | {string0} Produkte sind überfällig", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} Produkt ist unter Mindestbestand | {string0} Produkte sind unter Mindestbestand", + "{count} product expires | {count} products expiring": "{count} läuft ab | {count} Produkte laufen ab", + "within the next day | within the next {count} days": "innerhalb des nächsten Tages | innerhalb der nächsten {count} Tage", + "{count} product is already expired | {count} products are already expired": "{count} Produkt ist bereits abgelaufen | {count} Produkte sind bereits abgelaufen", + "{count} product is overdue | {count} products are overdue": "{count} Produkt ist überfällig | {count} Produkte sind überfällig", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} Produkt ist unter Mindestbestand | {count} Produkte sind unter Mindestbestand", "Product": "Produkt", - "{string0} Product | {string0} Products": "{string0} Produkt | {string0} Produkte", + "{count} Product | {count} Products": "{count} Produkt | {count} Produkte", "Amount": "Menge", "Logout": "Abmelden", "Chores overview": "Hausarbeiten", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Bedarf im Bestand", "Put missing products on shopping list": "Fehlende Produkte auf den Einkaufszettel setzen", "Enough in stock": "Bestand reicht aus", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Bestand nicht ausreichend, {string0} Zutat fehlt, steht aber bereits auf dem Einkaufszettel | Bestand nicht ausreichend, {string0} Zutaten fehlen, stehen aber bereits auf dem Einkaufszettel", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Bestand nicht ausreichend, {count} Zutat fehlt, steht aber bereits auf dem Einkaufszettel | Bestand nicht ausreichend, {count} Zutaten fehlen, stehen aber bereits auf dem Einkaufszettel", "Expand to fullscreen": "Auf ganzen Bildschirm vergrößern", "Ingredients": "Zutaten", "Preparation": "Zubereitung", @@ -177,11 +177,11 @@ "No price history available": "Keine Preisdaten verfügbar", "Price": "Preis", "Unit": "Einheit", - "{string0} Unit | {string0} Units": "{string0} Einheit | {string0} Einheiten", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} Hausarbeit steht an | {string0} Hausarbeiten stehen an", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} Hausarbeit ist überfällig | {string0} Hausarbeiten sind überfällig", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} Batterie muss geladen werden | {string0} Batterien müssen geladen werden", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} Batterie ist überfällig | {string0} Batterien sind überfällig", + "{count} Unit | {count} Units": "{count} Einheit | {count} Einheiten", + "{count} chore is due to be done | {count} chores are due to be done": "{count} Hausarbeit steht an | {count} Hausarbeiten stehen an", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} Hausarbeit ist überfällig | {count} Hausarbeiten sind überfällig", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} Batterie muss geladen werden | {count} Batterien müssen geladen werden", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} Batterie ist überfällig | {count} Batterien sind überfällig", "in singular form": "in der Einzahl", "Quantity unit": "Mengeneinheit", "Only check if any amount is in stock": "Nur prüfen, ob eine beliebige Menge im Bestand verfügbar ist", @@ -204,8 +204,8 @@ "Category": "Kategorie", "Edit task": "Aufgabe bearbeiten", "Are you sure to delete task \"{string0}\"?": "Aufgabe \"{string0}\" wirklich löschen?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} Aufgabe steht an | {string0} Aufgaben stehen an", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} Aufgabe ist überfällig | {string0} Aufgaben sind überfällig", + "{count} task is due to be done | {count} tasks are due to be done": "{count} Aufgabe steht an | {count} Aufgaben stehen an", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} Aufgabe ist überfällig | {count} Aufgaben sind überfällig", "Edit task category": "Aufgabenkategorie bearbeiten", "Create task category": "Aufgabenkategorie erstellen", "Product groups": "Produktgruppen", @@ -351,7 +351,7 @@ "Plural count": "Plural Anzahl", "Plural rule": "Plural Regel", "in plural form": "in der Mehrzahl", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Nicht ausreichend im Bestand, {string0} Zutat fehlt | Nicht ausreichend im Bestand, {string0} Zutaten fehlen", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Nicht ausreichend im Bestand, {count} Zutat fehlt | Nicht ausreichend im Bestand, {count} Zutaten fehlen", "Not enough in stock, but already on the shopping list": "Bestand nicht ausreichend, steht aber bereits auf dem Einkaufszettel", "Not enough in stock": "Nicht ausreichend im Bestand", "Expiring soon days": "\"Bald ablaufend\"-Tage", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Verbrauche {string1} {string2}", "Meal plan": "Speiseplan", "Add recipe on {string0}": "Rezept am {string0} hinzufügen", - "{string0} serving | {string0} servings": "{string0} Portion | {string0} Portionen", + "{count} serving | {count} servings": "{count} Portion | {count} Portionen", "Week costs": "Wochenkosten", "Configuration": "Konfiguration", "A predefined list of values, one per line": "Eine vordefinierte Liste von Werten (einer pro Zeile)", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Das bedeutet, dass die nächste Ausführung der Hausarbeit dem nächsten in alphabetischer Reihenfolge zugewiesen wird", "Assign to": "Zuweisen an", "This assignment type requires that at least one is assigned": "Diese Zuordnungsart setzt voraus, dass mindestens eine Person zugeordnet ist", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} ist mir zugewiesen | {string0} Hausarbeiten sind mir zugewiesen", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} ist mir zugewiesen | {count} Hausarbeiten sind mir zugewiesen", "Assigned to me": "Mir zugewiesen", "assigned to {string0}": "zugewiesen an {string0}", "Assignment": "Zuweisung", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Wenn dieses Produkt von einem Gefrier-Standort umgelagert (sprich aufgetaut) wird, wird das Fälligkeitsdatum durch heute + diese Anzahl von Tagen ersetzt", "Default due days after thawing": "Standard-Fälligkeitstage nach dem Auftauen", "Next due date": "Nächstes Fälligkeitsdatum", - "{string0} product is due | {string0} products are due": "{string0} Produkte ist fällig | {string0} Produkte sind fällig", + "{count} product is due | {count} products are due": "{count} Produkte ist fällig | {count} Produkte sind fällig", "Due date": "Fälligkeitsdatum", "Never overdue": "Nie überfällig", - "{string0} product is expired | {string0} products are expired": "{string0} Produkt ist abgelaufen | {string0} Produkte sind abgelaufen", + "{count} product is expired | {count} products are expired": "{count} Produkt ist abgelaufen | {count} Produkte sind abgelaufen", "Expired": "Abgelaufen", "Due soon days": "\"Bald fällig\" Tage", "Add overdue/expired products": "Überfällige/Abgelaufene Produkte hinzufügen", @@ -682,7 +682,7 @@ "timeago_locale": "de", "timeago_nan": "vor NaN Jahren", "moment_locale": "de", - "datatables_localization": "{\"sEmptyTable\":\"Keine Daten in der Tabelle vorhanden\",\"sInfo\":\"_START_ bis _END_ von _TOTAL_ Einträgen\",\"sInfoEmpty\":\"Keine Daten vorhanden\",\"sInfoFiltered\":\"(gefiltert von _MAX_ Einträgen)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\".\",\"sLengthMenu\":\"_MENU_ Einträge anzeigen\",\"sLoadingRecords\":\"Wird geladen ..\",\"sProcessing\":\"Bitte warten ..\",\"sSearch\":\"Suchen\",\"sZeroRecords\":\"Keine Einträge vorhanden\",\"oPaginate\":{\"sFirst\":\"Erste\",\"sPrevious\":\"Zurück\",\"sNext\":\"Nächste\",\"sLast\":\"Letzte\"},\"oAria\":{\"sSortAscending\":\": aktivieren, um Spalte aufsteigend zu sortieren\",\"sSortDescending\":\": aktivieren, um Spalte absteigend zu sortieren\"},\"select\":{\"rows\":{\"0\":\"Zum Auswählen auf eine Zeile klicken\",\"1\":\"1 Zeile ausgewählt\",\"_\":\"{num0} Zeilen ausgewählt\"}},\"buttons\":{\"print\":\"Drucken\",\"colvis\":\"Spalten\",\"copy\":\"Kopieren\",\"copyTitle\":\"In Zwischenablage kopieren\",\"copyKeys\":\"Taste ctrl oder + C um Tabelle
in Zwischenspeicher zu kopieren.

Um abzubrechen die Nachricht anklicken oder Escape drücken.\",\"copySuccess\":{\"1\":\"1 Spalte kopiert\",\"_\":\"{num0} Spalten kopiert\"}}}", + "datatables_localization": "{\"sEmptyTable\":\"Keine Daten in der Tabelle vorhanden\",\"sInfo\":\"_START_ bis _END_ von _TOTAL_ Einträgen\",\"sInfoEmpty\":\"Keine Daten vorhanden\",\"sInfoFiltered\":\"(gefiltert von _MAX_ Einträgen)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\".\",\"sLengthMenu\":\"_MENU_ Einträge anzeigen\",\"sLoadingRecords\":\"Wird geladen ..\",\"sProcessing\":\"Bitte warten ..\",\"sSearch\":\"Suchen\",\"sZeroRecords\":\"Keine Einträge vorhanden\",\"oPaginate\":{\"sFirst\":\"Erste\",\"sPrevious\":\"Zurück\",\"sNext\":\"Nächste\",\"sLast\":\"Letzte\"},\"oAria\":{\"sSortAscending\":\": aktivieren, um Spalte aufsteigend zu sortieren\",\"sSortDescending\":\": aktivieren, um Spalte absteigend zu sortieren\"},\"select\":{\"rows\":{\"0\":\"Zum Auswählen auf eine Zeile klicken\",\"1\":\"1 Zeile ausgewählt\",\"_\":\"{string0} Zeilen ausgewählt\"}},\"buttons\":{\"print\":\"Drucken\",\"colvis\":\"Spalten\",\"copy\":\"Kopieren\",\"copyTitle\":\"In Zwischenablage kopieren\",\"copyKeys\":\"Taste ctrl oder + C um Tabelle
in Zwischenspeicher zu kopieren.

Um abzubrechen die Nachricht anklicken oder Escape drücken.\",\"copySuccess\":{\"1\":\"1 Spalte kopiert\",\"_\":\"{string0} Spalten kopiert\"}}}", "summernote_locale": "de-DE", "fullcalendar_locale": "de", "bootstrap-select_locale": "de_DE", @@ -889,7 +889,7 @@ "Automatically reload data": "Automatisch aktualisieren", "Night mode": "Nachtmodus", "Auto": "Auto", - "Automatic night mode range": "Zeitraum für Automatischen Nachtmodus", + "Automatic night mode range": "Zeitraum für automatischen Nachtmodus", "Invert": "Umkehren", "This activates night mode between {start} and {end} the next day.": "Der Nachtmodus ist zwischen {start} und {end} am nächsten Tag aktiv.", "This activates night mode between {start} and {end} the same day.": "Der Nachtmodus ist zwischen {start} und {end} am selben Tag aktiv.", diff --git a/locale/el_GR.json b/locale/el_GR.json index 32d7cdb4..8ad13aee 100644 --- a/locale/el_GR.json +++ b/locale/el_GR.json @@ -1,12 +1,12 @@ { "Stock overview": "Επισκόπηση αποθεμάτων", - "{string0} product expires | {string0} products expiring": "{string0} προϊόν λήγει | {string0} προϊόντα λήγουν", - "within the next day | within the next {string0} days": "μέσα στην επόμενη ημέρα | μέσα στις επόμενες {string0} ημέρες", - "{string0} product is already expired | {string0} products are already expired": "{string0} προϊόν έχει ήδη λήξει | {string0} προϊόντα έχουν ήδη λήξει", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} προϊόν είναι κάτω από το ορισμένο ελάχιστο απόθεμα | {string0} προϊόντα είναι κάτω από το ορισμένο ελάχιστο απόθεμα", + "{count} product expires | {count} products expiring": "{count} προϊόν λήγει | {count} προϊόντα λήγουν", + "within the next day | within the next {count} days": "μέσα στην επόμενη ημέρα | μέσα στις επόμενες {count} ημέρες", + "{count} product is already expired | {count} products are already expired": "{count} προϊόν έχει ήδη λήξει | {count} προϊόντα έχουν ήδη λήξει", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} προϊόν είναι κάτω από το ορισμένο ελάχιστο απόθεμα | {count} προϊόντα είναι κάτω από το ορισμένο ελάχιστο απόθεμα", "Product": "Προϊόν", - "{string0} Product | {string0} Products": "{string0} Προϊόν | {string0} Προϊόντα", + "{count} Product | {count} Products": "{count} Προϊόν | {count} Προϊόντα", "Amount": "Ποσό", "Logout": "Αποσύνδεση", "Chores overview": "Επισκόπηση μικροδουλειών", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Οι απαιτήσεις πληρούνται", "Put missing products on shopping list": "Βάλτε τα προϊόντα που λείπουν στη λίστα αγορών", "Enough in stock": "Αρκετά σε απόθεμα", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Not enough in stock, {string0} ingredient missing but already on the shopping list | Δεν υπάρχει αρκετό απόθεμα, λείπουν τα συστατικά του {string0} αλλά ήδη βρίσκονται στη λίστα αγορών", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Not enough in stock, {count} ingredient missing but already on the shopping list | Δεν υπάρχει αρκετό απόθεμα, λείπουν τα συστατικά του {count} αλλά ήδη βρίσκονται στη λίστα αγορών", "Expand to fullscreen": "Ανάπτυξη σε πλήρη οθόνη", "Ingredients": "Συστατικά", "Preparation": "Παρασκευή", @@ -177,11 +177,11 @@ "No price history available": "Δεν υπάρχει διαθέσιμο ιστορικό τιμών", "Price": "Τιμή", "Unit": "Μονάδα", - "{string0} Unit | {string0} Units": "{string0}Μονάδα | {string0}Μονάδες", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} δουλειά πρόκειται να γίνει | {string0} δουλειές πρόκειται να γίνουν", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} δουλειά έχει καθυστερήσει να γίνει | {string0} δουλειές έχουν καθυστερήσει να γίνουν", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} μπαταρία πρέπει να φορτιστεί | {string0} μπαταρίες πρέπει να φορτιστούν", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} μπαταρία έχει καθυστερήσει να φορτιστεί | {string0} μπαταρίες έχουν καθυστερήσει να φορτιστούν", + "{count} Unit | {count} Units": "{count}Μονάδα | {count}Μονάδες", + "{count} chore is due to be done | {count} chores are due to be done": "{count} δουλειά πρόκειται να γίνει | {count} δουλειές πρόκειται να γίνουν", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} δουλειά έχει καθυστερήσει να γίνει | {count} δουλειές έχουν καθυστερήσει να γίνουν", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} μπαταρία πρέπει να φορτιστεί | {count} μπαταρίες πρέπει να φορτιστούν", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} μπαταρία έχει καθυστερήσει να φορτιστεί | {count} μπαταρίες έχουν καθυστερήσει να φορτιστούν", "in singular form": "σε μοναδική μορφή", "Quantity unit": "Μονάδα ποσότητας", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Κατηγορία", "Edit task": "Επεξεργασία εργασίας", "Are you sure to delete task \"{string0}\"?": "Είστε βέβαιοι ότι θα διαγράψετε την εργασία \"% s\";", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} εργασία πρέπει να γίνει | {string0} εργασίες πρέπει να γίνουν", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} καθήκον που πρέπει να γίνει | {string0} καθήκοντα έχουν καθυστερήσει να γίνουν", + "{count} task is due to be done | {count} tasks are due to be done": "{count} εργασία πρέπει να γίνει | {count} εργασίες πρέπει να γίνουν", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} καθήκον που πρέπει να γίνει | {count} καθήκοντα έχουν καθυστερήσει να γίνουν", "Edit task category": "Επεξεργασία κατηγορίας εργασιών", "Create task category": "Δημιουργία κατηγορίας εργασιών", "Product groups": "Ομάδες προϊόντων", @@ -351,7 +351,7 @@ "Plural count": "Πληθυντικός αριθμός", "Plural rule": "Πληθυντικός κανόνας", "in plural form": "σε πληθυντικό σχήμα", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Δεν υπάρχει αρκετό απόθεμα, λείπει {string0} συστατικό | Δεν υπάρχει αρκετό απόθεμα, λείπουν {string0} συστατικά", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Δεν υπάρχει αρκετό απόθεμα, λείπει {count} συστατικό | Δεν υπάρχει αρκετό απόθεμα, λείπουν {count} συστατικά", "Not enough in stock, but already on the shopping list": "Δεν υπάρχει αρκετό απόθεμα, αλλά ήδη στη λίστα αγορών", "Not enough in stock": "Δεν υπάρχει αρκετό απόθεμα", "Expiring soon days": "Λήγει σύντομα ημέρες", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Καταναλώστε% 1 $ s από% 2 $ s", "Meal plan": "Προτίμηση γεύματος", "Add recipe on {string0}": "Προσθέστε συνταγή στο% s", - "{string0} serving | {string0} servings": "Μερίδα {string0} | {string0} Μερίδες", + "{count} serving | {count} servings": "Μερίδα {count} | {count} Μερίδες", "Week costs": "Κόστος εβδομάδας", "Configuration": "Διαμόρφωση", "A predefined list of values, one per line": "Μια προκαθορισμένη λίστα τιμών, μία ανά γραμμή", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Παρακολούθηση χρόνουΑυτό σημαίνει ότι η επόμενη εκτέλεση αυτής της δουλειάς θα ανατεθεί στην επόμενη με αλφαβητική σειρά", "Assign to": "Ανάθεση σε", "This assignment type requires that at least one is assigned": "Αυτός ο τύπος ανάθεσης απαιτεί να αντιστοιχιστεί τουλάχιστον ένας", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} δουλειά μου έχει ανατεθεί | {string0} δουλειές μου έχουν ανατεθεί", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} δουλειά μου έχει ανατεθεί | {count} δουλειές μου έχουν ανατεθεί", "Assigned to me": "Ανατέθηκε σε μένα", "assigned to {string0}": "εκχωρήθηκε στο% s", "Assignment": "ΑΝΑΘΕΣΗ ΕΡΓΑΣΙΑΣ", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/en.json b/locale/en.json index 074139e4..abcf0869 100644 --- a/locale/en.json +++ b/locale/en.json @@ -585,27 +585,23 @@ "Polish": "Polish", "DemoSupermarket1": "Walmart", "DemoSupermarket2": "Kroger", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on": "", "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, "This activates night mode between {start} and {end} the same day": { "": "" - } + }, + "{count} Product | {count} Products": "", + "{string0} total value": "" } \ No newline at end of file diff --git a/locale/en_GB.json b/locale/en_GB.json index bb574a3c..169a6ae8 100644 --- a/locale/en_GB.json +++ b/locale/en_GB.json @@ -1,12 +1,12 @@ { "Stock overview": "Stock overview", - "{string0} product expires | {string0} products expiring": "{string0} product expires | {string0} products expiring", - "within the next day | within the next {string0} days": "within the next day | within the next {string0} days", - "{string0} product is already expired | {string0} products are already expired": "{string0} product is already expired | {string0} products are already expired", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount", + "{count} product expires | {count} products expiring": "{count} product expires | {count} products expiring", + "within the next day | within the next {count} days": "within the next day | within the next {count} days", + "{count} product is already expired | {count} products are already expired": "{count} product is already expired | {count} products are already expired", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount", "Product": "Product", - "{string0} Product | {string0} Products": "{string0} Product | {string0} Products", + "{count} Product | {count} Products": "", "Amount": "Amount", "Logout": "Logout", "Chores overview": "Chores overview", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Requirements fulfilled", "Put missing products on shopping list": "Put missing products on shopping list", "Enough in stock": "Enough in stock", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list", "Expand to fullscreen": "Expand to fullscreen", "Ingredients": "Ingredients", "Preparation": "Preparation", @@ -177,11 +177,11 @@ "No price history available": "No price history available", "Price": "Price", "Unit": "Unit", - "{string0} Unit | {string0} Units": "{string0} Unit | {string0} Units", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} chore is due to be done | {string0} chores are due to be done", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} chore is overdue to be done | {string0} chores are overdue to be done", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} battery is due to be charged | {string0} batteries are due to be charged", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged", + "{count} Unit | {count} Units": "{count} Unit | {count} Units", + "{count} chore is due to be done | {count} chores are due to be done": "{count} chore is due to be done | {count} chores are due to be done", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} chore is overdue to be done | {count} chores are overdue to be done", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} battery is due to be charged | {count} batteries are due to be charged", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} battery is overdue to be charged | {count} batteries are overdue to be charged", "in singular form": "in singular form", "Quantity unit": "Quantity unit", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Category", "Edit task": "Edit task", "Are you sure to delete task \"{string0}\"?": "Are you sure to delete task \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} task is due to be done | {string0} tasks are due to be done", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} task is overdue to be done | {string0} tasks are overdue to be done", + "{count} task is due to be done | {count} tasks are due to be done": "{count} task is due to be done | {count} tasks are due to be done", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} task is overdue to be done | {count} tasks are overdue to be done", "Edit task category": "Edit task category", "Create task category": "Create task category", "Product groups": "Product groups", @@ -351,7 +351,7 @@ "Plural count": "Plural count", "Plural rule": "Plural rule", "in plural form": "in plural form", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing", "Not enough in stock, but already on the shopping list": "Not enough in stock, but already on the shopping list", "Not enough in stock": "Not enough in stock", "Expiring soon days": "Expiring soon days", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consume {string1} of {string2}", "Meal plan": "Meal plan", "Add recipe on {string0}": "Add recipe on {string0}", - "{string0} serving | {string0} servings": "{string0} serving | {string0} servings", + "{count} serving | {count} servings": "{count} serving | {count} servings", "Week costs": "Week costs", "Configuration": "Configuration", "A predefined list of values, one per line": "A predefined list of values, one per line", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "This means the next execution of this chore will be assigned to the next one in alphabetical order", "Assign to": "Assign to", "This assignment type requires that at least one is assigned": "This assignment type requires that at least one is assigned", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} chore is assigned to me | {string0} chores are assigned to me", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} chore is assigned to me | {count} chores are assigned to me", "Assigned to me": "Assigned to me", "assigned to {string0}": "assigned to {string0}", "Assignment": "Assignment", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/es.json b/locale/es.json index 26ca75b9..50efa5d6 100644 --- a/locale/es.json +++ b/locale/es.json @@ -1,12 +1,12 @@ { "Stock overview": "Resumen de inventario", - "{string0} product expires | {string0} products expiring": "{string0} producto caducará | {string0} productos caducarán", - "within the next day | within the next {string0} days": "mañana | en los próximos {string0} días", - "{string0} product is already expired | {string0} products are already expired": "{string0} producto ha caducado | {string0} productos han caducado", - "{string0} product is overdue | {string0} products are overdue": "{string0} producto ha superado su fecha de consumo preferente | {string0} productos han superado sus fechas de consumo preferente", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} producto por debajo del mínimo de existencias definido | {string0} productos por debajo del mínimo de existencias definido", + "{count} product expires | {count} products expiring": "{count} producto caducará | {count} productos caducarán", + "within the next day | within the next {count} days": "mañana | en los próximos {count} días", + "{count} product is already expired | {count} products are already expired": "{count} producto ha caducado | {count} productos han caducado", + "{count} product is overdue | {count} products are overdue": "{count} producto ha superado su fecha de consumo preferente | {count} productos han superado sus fechas de consumo preferente", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} producto por debajo del mínimo de existencias definido | {count} productos por debajo del mínimo de existencias definido", "Product": "Producto", - "{string0} Product | {string0} Products": "{string0} Producto | {string0} Productos", + "{count} Product | {count} Products": "{count} Producto | {count} Productos", "Amount": "Cantidad", "Logout": "Cerrar sesión", "Chores overview": "Resumen de tareas del hogar", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Requisitos satisfechos", "Put missing products on shopping list": "Añadir productos faltantes a la lista de la compra", "Enough in stock": "Existencias suficientes", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "No hay existencias suficientes: falta {string0} ingrediente pero ya está en la lista de la compra | No hay existencias suficientes: faltan {string0} ingredientes, pero ya están en la lista de la compra", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "No hay existencias suficientes: falta {count} ingrediente pero ya está en la lista de la compra | No hay existencias suficientes: faltan {count} ingredientes, pero ya están en la lista de la compra", "Expand to fullscreen": "Pantalla completa", "Ingredients": "Ingredientes", "Preparation": "Preparación", @@ -177,11 +177,11 @@ "No price history available": "No hay histórico de precios disponible", "Price": "Precio", "Unit": "Unidad", - "{string0} Unit | {string0} Units": "{string0} Unidad | {string0} Unidades", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} tarea del hogar debe hacerse | {string0} tareas del hogar deben hacerse", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} tarea del hogar debería haberse hecho | {string0} tareas del hogar deberían haberse hecho", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batería debe cargarse | {string0} baterías deben cargarse", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batería debería haberse cargado | {string0} baterías deberían haberse cargado", + "{count} Unit | {count} Units": "{count} Unidad | {count} Unidades", + "{count} chore is due to be done | {count} chores are due to be done": "{count} tarea del hogar debe hacerse | {count} tareas del hogar deben hacerse", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} tarea del hogar debería haberse hecho | {count} tareas del hogar deberían haberse hecho", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batería debe cargarse | {count} baterías deben cargarse", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batería debería haberse cargado | {count} baterías deberían haberse cargado", "in singular form": "en singular", "Quantity unit": "Unidad", "Only check if any amount is in stock": "Solo comprobar si hay existencias", @@ -204,8 +204,8 @@ "Category": "Categoría", "Edit task": "Modificar tarea", "Are you sure to delete task \"{string0}\"?": "¿Está seguro de querer eliminar la tarea \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} tarea debe hacerse | {string0} tareas deben hacerse", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} tarea debería haberse hecho | {string0} tareas deberían haberse hecho", + "{count} task is due to be done | {count} tasks are due to be done": "{count} tarea debe hacerse | {count} tareas deben hacerse", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} tarea debería haberse hecho | {count} tareas deberían haberse hecho", "Edit task category": "Modificar la categoría de la tarea", "Create task category": "Crear categoría de tarea", "Product groups": "Grupos de producto", @@ -351,7 +351,7 @@ "Plural count": "Cuenta del plural", "Plural rule": "Regla del plural", "in plural form": "en plural", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "No hay existencias suficientes: falta {string0} ingrediente | No hay existencias suficientes: faltan {string0} ingredientes", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "No hay existencias suficientes: falta {count} ingrediente | No hay existencias suficientes: faltan {count} ingredientes", "Not enough in stock, but already on the shopping list": "No hay existencias suficientes, pero ya están en la lista de la compra", "Not enough in stock": "No hay existencias suficientes", "Expiring soon days": "Expira en los próximos días", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consumir {string1} de {string2}", "Meal plan": "Plan de comidas", "Add recipe on {string0}": "Añadir receta en {string0}", - "{string0} serving | {string0} servings": "{string0} raciones | {string0} raciones", + "{count} serving | {count} servings": "{count} raciones | {count} raciones", "Week costs": "Coste semanal", "Configuration": "Configuración", "A predefined list of values, one per line": "Una lista de valores predefinidos, uno por línea", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Esto significa que la próxima ejecución de esta tarea del hogar se asignará al siguiente en orden alfabético", "Assign to": "Asignar a", "This assignment type requires that at least one is assigned": "Este tipo de asignación requiere que se asigne al menos a uno", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} tarea del hogar asignada a mí | {string0} tareas del hogar asignadas a mí", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} tarea del hogar asignada a mí | {count} tareas del hogar asignadas a mí", "Assigned to me": "Asignada a mí", "assigned to {string0}": "asignada a {string0}", "Assignment": "Asignación", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Al mover este producto desde una ubicación congelador (al descongelarlo), la fecha límite será reemplazada por la fecha de hoy más este número de días", "Default due days after thawing": "Número predeterminado de días hasta la fecha límite después de descongelado", "Next due date": "Próxima fecha límite", - "{string0} product is due | {string0} products are due": "{string0} producto ha superado su fecha de consumo preferente | {string0} productos han superado sus fechas de consumo preferente", + "{count} product is due | {count} products are due": "{count} producto ha superado su fecha de consumo preferente | {count} productos han superado sus fechas de consumo preferente", "Due date": "Fecha límite", "Never overdue": "Sin fecha límite", - "{string0} product is expired | {string0} products are expired": "{string0} producto está caducado | {string0} productos están caducados", + "{count} product is expired | {count} products are expired": "{count} producto está caducado | {count} productos están caducados", "Expired": "Caducado", "Due soon days": "Días para próximo a fecha límite", "Add overdue/expired products": "Añadir productos caducados o que han superado su fecha de consumo preferente", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Hebreo (Israel)", "Tamil": "Tamil", "Finnish": "Finés", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/fi.json b/locale/fi.json index 2e22178e..74e5711c 100644 --- a/locale/fi.json +++ b/locale/fi.json @@ -1,12 +1,12 @@ { "Stock overview": "Varaston yleiskatsaus", - "{string0} product expires | {string0} products expiring": "{string0}tuote vanhentuu | {string0} tuotetta vanhentumassa", - "within the next day | within the next {string0} days": "seuraavan päivän sisällä | {string0}päivän sisällä", - "{string0} product is already expired | {string0} products are already expired": "{string0}tuotetta on jo vanhentunut | {string0}tuotetta on jo vanhentunut", - "{string0} product is overdue | {string0} products are overdue": "{string0} tuote on myöhässä | {string0} tuotetta on myöhässä", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0}tuote on alle varaston minimirajan | {string0}tuotetta on alle minimirajan varastossa", + "{count} product expires | {count} products expiring": "{count}tuote vanhentuu | {count} tuotetta vanhentumassa", + "within the next day | within the next {count} days": "seuraavan päivän sisällä | {count}päivän sisällä", + "{count} product is already expired | {count} products are already expired": "{count}tuotetta on jo vanhentunut | {count}tuotetta on jo vanhentunut", + "{count} product is overdue | {count} products are overdue": "{count} tuote on myöhässä | {count} tuotetta on myöhässä", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count}tuote on alle varaston minimirajan | {count}tuotetta on alle minimirajan varastossa", "Product": "Tuote", - "{string0} Product | {string0} Products": "{string0}Tuote | {string0}Tuotetta", + "{count} Product | {count} Products": "{count}Tuote | {count}Tuotetta", "Amount": "Määrä", "Logout": "Kirjaudu ulos", "Chores overview": "Kotityö yleiskatsaus", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Vaatimukset täytetty", "Put missing products on shopping list": "Lisää puuttuvat tuotteet ostoslistalle", "Enough in stock": "Tarpeeksi varastossa", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Varastosta puuttuu {string0} ainesosa, mutta se on jo ostoslistalla. | Varastosta puuttuu {string0} ainesosia, mutta ne ovat jo ostoslistalla", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Varastosta puuttuu {count} ainesosa, mutta se on jo ostoslistalla. | Varastosta puuttuu {count} ainesosia, mutta ne ovat jo ostoslistalla", "Expand to fullscreen": "Käytä kokonäytön tilaa", "Ingredients": "Ainesosat", "Preparation": "Valmistelu", @@ -177,11 +177,11 @@ "No price history available": "Ei hintahistoriaa saatavilla", "Price": "Hinta", "Unit": "Yksikkö", - "{string0} Unit | {string0} Units": "{string0} kappale | {string0} kappaletta", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} kotityön määräpäivä | {string0} kotitöiden määräpäivä", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} myöhästynyt kotityö | {string0} kotityö myöhässä", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} paristo on lataamatta | {string0} paristoa lataamatta", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} pariston lataus myöhässä | {string0} pariston latausta myöhässä", + "{count} Unit | {count} Units": "{count} kappale | {count} kappaletta", + "{count} chore is due to be done | {count} chores are due to be done": "{count} kotityön määräpäivä | {count} kotitöiden määräpäivä", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} myöhästynyt kotityö | {count} kotityö myöhässä", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} paristo on lataamatta | {count} paristoa lataamatta", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} pariston lataus myöhässä | {count} pariston latausta myöhässä", "in singular form": "yksikössä", "Quantity unit": "Määräyksikkö", "Only check if any amount is in stock": "Tarkista vain onko varastossa ollenkaan", @@ -204,8 +204,8 @@ "Category": "Kategoria", "Edit task": "Muokkaa tehtävää", "Are you sure to delete task \"{string0}\"?": "Haluatko varmasti poistaa tehtävän \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} tehtävän määräpäivä  | {string0} tehtävän määräpäivä ", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} tehtävä myöhässä | {string0} tehtävää myöhässä", + "{count} task is due to be done | {count} tasks are due to be done": "{count} tehtävän määräpäivä  | {count} tehtävän määräpäivä ", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} tehtävä myöhässä | {count} tehtävää myöhässä", "Edit task category": "Muuta tehtäväluokitusta", "Create task category": "Luo tehtäväluokitus", "Product groups": "Tuoteryhmät", @@ -351,7 +351,7 @@ "Plural count": "Monikko määrä", "Plural rule": "Monikkosääntö", "in plural form": "monikossa", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Ei tarpeeksi varastossa, {string0} ainesosa puuttuu | Ei tarpeeksi varastossa, {string0} ainesosia puuttuu", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Ei tarpeeksi varastossa, {count} ainesosa puuttuu | Ei tarpeeksi varastossa, {count} ainesosia puuttuu", "Not enough in stock, but already on the shopping list": "Ei tarpeeksi varastossa, mutta lisätty jo kauppalistaan", "Not enough in stock": "Ei tarpeeksi varastossa", "Expiring soon days": "Viimeiset käyttöpäivät", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Käytä {string1} tuotteesta {string2}", "Meal plan": "Ateriasuunnitelma", "Add recipe on {string0}": "Lisää resepti {string0} päivälle", - "{string0} serving | {string0} servings": "{string0} annos | {string0} annosta", + "{count} serving | {count} servings": "{count} annos | {count} annosta", "Week costs": "Viikkokustannus", "Configuration": "Asetukset", "A predefined list of values, one per line": "Etukäteen määritelty lista, yksi arvo per rivi", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Tämä tarkoittaa, että tämän kotityön seuraava suoritus annetaan toimeksi seuraavalle aakkosjärjestyksessä", "Assign to": "Toimeksiannetaan tekijälle", "This assignment type requires that at least one is assigned": "Tämä toimeksiannon tapa vaatii vähintään yhden tekijän", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} kotityö toimeksiannettu minulle | {string0} kotityötä toimeksiannettu minulle", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} kotityö toimeksiannettu minulle | {count} kotityötä toimeksiannettu minulle", "Assigned to me": "Toimeksiannettu minulle", "assigned to {string0}": "Annetty toimeksi tekijälle: {string0}", "Assignment": "Toimeksianto", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Poistettaessa tämä tuote pakastin-sijainnista (eli sulatettaessa), parasta ennen -päiväksi asetetaan tänään + tämä määrä päiviä", "Default due days after thawing": "Oletusmäärä säilyvyyspäiviä sulatuksen jälkeen", "Next due date": "Seuraava parasta ennen -päivä", - "{string0} product is due | {string0} products are due": "{string0} tuote on vanhenemassa | {string0} tuotetta on vanhenemassa", + "{count} product is due | {count} products are due": "{count} tuote on vanhenemassa | {count} tuotetta on vanhenemassa", "Due date": "Parasta ennen -päivä", "Never overdue": "Ei vanhene koskaan", - "{string0} product is expired | {string0} products are expired": "{string0} tuote on vanhentunut | {string0} tuotetta on vanhentunut", + "{count} product is expired | {count} products are expired": "{count} tuote on vanhentunut | {count} tuotetta on vanhentunut", "Expired": "Vanhentunut", "Due soon days": "Pian vanhenemassa -päiviä", "Add overdue/expired products": "Lisää vanhenevat/vanhentuneet tuotteet", @@ -682,7 +682,7 @@ "timeago_locale": "fi", "timeago_nan": "NaN vuotta", "moment_locale": "fi", - "datatables_localization": "{\"sEmptyTable\":\"Ei näytettäviä tuloksia.\",\"sInfo\":\"Näytetään rivit _START_ - _END_ (yhteensä _TOTAL_ )\",\"sInfoEmpty\":\"Näytetään 0 - 0 (yhteensä 0)\",\"sInfoFiltered\":\"(suodatettu _MAX_ tuloksen joukosta)\",\"sInfoThousands\":\",\",\"sLengthMenu\":\"Näytä kerralla _MENU_ riviä\",\"sLoadingRecords\":\"Ladataan...\",\"sProcessing\":\"Hetkinen...\",\"sSearch\":\"Etsi:\",\"sZeroRecords\":\"Tietoja ei löytynyt\",\"oPaginate\":{\"sFirst\":\"Ensimmäinen\",\"sLast\":\"Viimeinen\",\"sNext\":\"Seuraava\",\"sPrevious\":\"Edellinen\"},\"oAria\":{\"sSortAscending\":\": lajittele sarake nousevasti\",\"sSortDescending\":\": lajittele sarake laskevasti\"},\"select\":{\"rows\":{\"0\":\"Klikkaa riviä valitaksesi sen\",\"1\":\"Valittuna vain yksi rivi\",\"_\":\"Valittuna {num0} riviä\"}},\"buttons\":{\"copy\":\"Kopioi\",\"copySuccess\":{\"1\":\"Yksi rivi kopioitu leikepöydälle\",\"_\":\"{num0} riviä kopioitu leikepöydälle\"},\"copyTitle\":\"Kopioi leikepöydälle\",\"copyKeys\":\"Paina ctrl tai + C kopioidaksesi taulukon arvot
leikepöydälle.

Peruuttaaksesi klikkaa tähän tai Esc.\"}}", + "datatables_localization": "{\"sEmptyTable\":\"Ei näytettäviä tuloksia.\",\"sInfo\":\"Näytetään rivit _START_ - _END_ (yhteensä _TOTAL_ )\",\"sInfoEmpty\":\"Näytetään 0 - 0 (yhteensä 0)\",\"sInfoFiltered\":\"(suodatettu _MAX_ tuloksen joukosta)\",\"sInfoThousands\":\",\",\"sLengthMenu\":\"Näytä kerralla _MENU_ riviä\",\"sLoadingRecords\":\"Ladataan...\",\"sProcessing\":\"Hetkinen...\",\"sSearch\":\"Etsi:\",\"sZeroRecords\":\"Tietoja ei löytynyt\",\"oPaginate\":{\"sFirst\":\"Ensimmäinen\",\"sLast\":\"Viimeinen\",\"sNext\":\"Seuraava\",\"sPrevious\":\"Edellinen\"},\"oAria\":{\"sSortAscending\":\": lajittele sarake nousevasti\",\"sSortDescending\":\": lajittele sarake laskevasti\"},\"select\":{\"rows\":{\"0\":\"Klikkaa riviä valitaksesi sen\",\"1\":\"Valittuna vain yksi rivi\",\"_\":\"Valittuna {string0} riviä\"}},\"buttons\":{\"copy\":\"Kopioi\",\"copySuccess\":{\"1\":\"Yksi rivi kopioitu leikepöydälle\",\"_\":\"{string0} riviä kopioitu leikepöydälle\"},\"copyTitle\":\"Kopioi leikepöydälle\",\"copyKeys\":\"Paina ctrl tai + C kopioidaksesi taulukon arvot
leikepöydälle.

Peruuttaaksesi klikkaa tähän tai Esc.\"}}", "summernote_locale": "fi-FI", "fullcalendar_locale": "fi", "bootstrap-select_locale": "fi_FI", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Heprea (Israel)", "Tamil": "Tamili", "Finnish": "Suomi", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/fr.json b/locale/fr.json index ce7211d8..bbf2ff49 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -1,12 +1,12 @@ { "Stock overview": "Aperçu du stock", - "{string0} product expires | {string0} products expiring": "{string0} produit périmé | {string0} produits périmés", - "within the next day | within the next {string0} days": "d'ici demain | d'ici {string0} jours", - "{string0} product is already expired | {string0} products are already expired": "{string0} produit est déjà périmé | {string0} produits sont déjà périmés", - "{string0} product is overdue | {string0} products are overdue": "{string0} produit arrive à échéance | {string0} produits arrivent à échéance", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produit est en quantité inférieure à la limite définie | {string0} produits sont en quantité inférieure à la limite définie", + "{count} product expires | {count} products expiring": "{count} produit périmé | {count} produits périmés", + "within the next day | within the next {count} days": "d'ici demain | d'ici {count} jours", + "{count} product is already expired | {count} products are already expired": "{count} produit est déjà périmé | {count} produits sont déjà périmés", + "{count} product is overdue | {count} products are overdue": "{count} produit arrive à échéance | {count} produits arrivent à échéance", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produit est en quantité inférieure à la limite définie | {count} produits sont en quantité inférieure à la limite définie", "Product": "Produit", - "{string0} Product | {string0} Products": "{string0} Produit | {string0} Produits", + "{count} Product | {count} Products": "{count} Produit | {count} Produits", "Amount": "Quantité", "Logout": "Déconnexion", "Chores overview": "Aperçu des corvées", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Prérequis remplis", "Put missing products on shopping list": "Ajouter les produits manquants dans la liste de courses", "Enough in stock": "Il y en a assez en stock", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Pas assez en stock, {string0} ingrédient manquant mais déjà ajouté à la liste de courses | Pas assez en stock, {string0} ingrédients manquants mais déjà ajoutés à la liste de courses", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Pas assez en stock, {count} ingrédient manquant mais déjà ajouté à la liste de courses | Pas assez en stock, {count} ingrédients manquants mais déjà ajoutés à la liste de courses", "Expand to fullscreen": "Mettre en plein écran", "Ingredients": "Ingrédients", "Preparation": "Préparation", @@ -177,11 +177,11 @@ "No price history available": "Aucun historique disponible", "Price": "Prix", "Unit": "Unité", - "{string0} Unit | {string0} Units": "{string0} unité | {string0} unités", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} corvée reste à faire | {string0} corvées restent à faire", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} corvée n'a pas été réalisée à temps | {string0} corvées n'ont pas été réalisées à temps", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} pile doit être rechargée | {string0} piles doivent être rechargées", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} pile aurait dû être chargée | {string0} piles auraient dû être rechargées", + "{count} Unit | {count} Units": "{count} unité | {count} unités", + "{count} chore is due to be done | {count} chores are due to be done": "{count} corvée reste à faire | {count} corvées restent à faire", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} corvée n'a pas été réalisée à temps | {count} corvées n'ont pas été réalisées à temps", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} pile doit être rechargée | {count} piles doivent être rechargées", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} pile aurait dû être chargée | {count} piles auraient dû être rechargées", "in singular form": "Au singulier", "Quantity unit": "Format", "Only check if any amount is in stock": "Vérifier seulement qu'une quantité non-nulle est en stock", @@ -204,8 +204,8 @@ "Category": "Catégorie", "Edit task": "Modifier la tâche", "Are you sure to delete task \"{string0}\"?": "Voulez-vous vraiment supprimer la tâche \"{string0}\" ?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} tâche est due | {string0} tâches sont dues", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} tâche en retard | {string0} tâches en retard", + "{count} task is due to be done | {count} tasks are due to be done": "{count} tâche est due | {count} tâches sont dues", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} tâche en retard | {count} tâches en retard", "Edit task category": "Modifier la catégorie de tâche", "Create task category": "Créer une catégorie de tâche", "Product groups": "Groupes de produit", @@ -351,7 +351,7 @@ "Plural count": "Nombre de pluriel", "Plural rule": "Règle pour la forme plurielle", "in plural form": "Au pluriel", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Pas assez en stock, il vous manque {string0} ingrédient | Pas assez en stock, il vous manque {string0} ingrédients", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Pas assez en stock, il vous manque {count} ingrédient | Pas assez en stock, il vous manque {count} ingrédients", "Not enough in stock, but already on the shopping list": "Pas assez en stock, mais déjà sur la liste de courses.", "Not enough in stock": "Pas assez en stock", "Expiring soon days": "Jours avant péremption", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consommer {string1} de {string2}", "Meal plan": "Prévisions des menus", "Add recipe on {string0}": "Ajouter une recette dans {string0}", - "{string0} serving | {string0} servings": "{string0} part / portion | {string0} parts / portions", + "{count} serving | {count} servings": "{count} part / portion | {count} parts / portions", "Week costs": "Budget hebdo", "Configuration": "Configuration", "A predefined list of values, one per line": "Liste prédéfinie, une valeur par ligne", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Signifie que la prochaine exécution de cette corvée sera assigné au prochain par ordre alphabétique", "Assign to": "Assigné à", "This assignment type requires that at least one is assigned": "Ce type de tâche nécessite au moins une personne assignée", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} corvée m'est assignée | {string0} corvées me sont assignées", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} corvée m'est assignée | {count} corvées me sont assignées", "Assigned to me": "Assigné à moi", "assigned to {string0}": "assigné à {string0}", "Assignment": "Tâche", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Lorsque ce produit est sorti du congélateur (décongélation), la date de durabilité minimale sera remplacée par la date du jour + ce nombre de jours", "Default due days after thawing": "Date de péremption par défaut après décongélation en jours", "Next due date": "Prochaine date de péremption", - "{string0} product is due | {string0} products are due": "{string0}produit est périmé | {string0}des produits sont exprimés", + "{count} product is due | {count} products are due": "{count}produit est périmé | {count}des produits sont exprimés", "Due date": "Date de péremption", "Never overdue": "Pas de date de péremption", - "{string0} product is expired | {string0} products are expired": "{string0} produit est périmé | {string0} produits sont périmés", + "{count} product is expired | {count} products are expired": "{count} produit est périmé | {count} produits sont périmés", "Expired": "Périmé", "Due soon days": "Date de péremption proche", "Add overdue/expired products": "Ajouter les produits dépassés/périmés", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Hébreux (Israël)", "Tamil": "Tamoul", "Finnish": "Finnois", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/he_IL.json b/locale/he_IL.json index c153c267..8056c151 100644 --- a/locale/he_IL.json +++ b/locale/he_IL.json @@ -1,12 +1,12 @@ { "Stock overview": "סקירת מלאי", - "{string0} product expires | {string0} products expiring": "תוקפו של מוצר יפוג | תוקפם של {string0} מוצרים יפוג | תוקפם של {string0} מוצרים יפוג | תוקפם של {string0} מוצרים יפוג", - "within the next day | within the next {string0} days": "תוך יום | תוך יומיים | תוך {string0} ימים | תוך {string0} ימים", - "{string0} product is already expired | {string0} products are already expired": "תוקפו של מוצר אחד כבר פג | תוקפם של {string0} מוצרים כבר פג | תוקפם של {string0} מוצרים כבר פג | תוקפם של {string0} מוצרים כבר פג", - "{string0} product is overdue | {string0} products are overdue": "התוקף של אחד המוצרים פג | התוקף של {string0} מוצרים פג | התוקף של {string0} מוצרים פג | התוקף של {string0} מוצרים פג", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "מוצר אחד אזל מתחת לכמות המלאי המזערית | {string0} מוצרים אזלו מעבר לכמות המלאי המזערית | {string0} מוצרים אזלו מעבר לכמות המלאי המזערית | {string0} מוצרים אזלו מעבר לכמות המלאי המזערית", + "{count} product expires | {count} products expiring": "תוקפו של מוצר יפוג | תוקפם של {count} מוצרים יפוג | תוקפם של {count} מוצרים יפוג | תוקפם של {count} מוצרים יפוג", + "within the next day | within the next {count} days": "תוך יום | תוך יומיים | תוך {count} ימים | תוך {count} ימים", + "{count} product is already expired | {count} products are already expired": "תוקפו של מוצר אחד כבר פג | תוקפם של {count} מוצרים כבר פג | תוקפם של {count} מוצרים כבר פג | תוקפם של {count} מוצרים כבר פג", + "{count} product is overdue | {count} products are overdue": "התוקף של אחד המוצרים פג | התוקף של {count} מוצרים פג | התוקף של {count} מוצרים פג | התוקף של {count} מוצרים פג", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "מוצר אחד אזל מתחת לכמות המלאי המזערית | {count} מוצרים אזלו מעבר לכמות המלאי המזערית | {count} מוצרים אזלו מעבר לכמות המלאי המזערית | {count} מוצרים אזלו מעבר לכמות המלאי המזערית", "Product": "מוצר", - "{string0} Product | {string0} Products": "מוצר אחד | {string0} מוצרים | {string0} מוצרים | {string0} מוצרים", + "{count} Product | {count} Products": "מוצר אחד | {count} מוצרים | {count} מוצרים | {count} מוצרים", "Amount": "כמות", "Logout": "יציאה", "Chores overview": "סקירת מטלות", @@ -144,7 +144,7 @@ "Requirements fulfilled": "הדרישות מולאו", "Put missing products on shopping list": "הוספת מוצרים חסרים לרשימת הקניות", "Enough in stock": "מספיק במלאי", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "אין מספיק במלאי, רכיב אחד חסר אבל כבר ברשימת הקניות | אין מספיק במלאי, {string0} רכיבים חסרים אבל כבר ברשימת הקניות | אין מספיק במלאי, {string0} רכיבים חסרים אבל כבר ברשימת הקניות | אין מספיק במלאי, {string0} רכיבים חסרים אבל כבר ברשימת הקניות", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "אין מספיק במלאי, רכיב אחד חסר אבל כבר ברשימת הקניות | אין מספיק במלאי, {count} רכיבים חסרים אבל כבר ברשימת הקניות | אין מספיק במלאי, {count} רכיבים חסרים אבל כבר ברשימת הקניות | אין מספיק במלאי, {count} רכיבים חסרים אבל כבר ברשימת הקניות", "Expand to fullscreen": "הרחבה למסך מלא", "Ingredients": "רכיבים", "Preparation": "הכנה", @@ -177,11 +177,11 @@ "No price history available": "אין היסטוריים מחירים זמינה", "Price": "מחיר", "Unit": "יחידה", - "{string0} Unit | {string0} Units": "יחידה אחת | {string0} יחידות | {string0} יחידות | {string0} יחידות", - "{string0} chore is due to be done | {string0} chores are due to be done": "מטלה אחת אמורה להסתיים | {string0} מטלות אמורות להסתיים | {string0} מטלות אמורות להסתיים | {string0} מטלות אמורות להסתיים", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "מטלה אחת חרגה ממועד הסיום | {string0} מטלות חרגו ממועד הסיום | {string0} מטלות חרגו ממועד הסיום | {string0} מטלות חרגו ממועד הסיום", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "סוללה אחת שיש לטעון בקרוב | {string0} סוללות שיש לטעון בקרוב | {string0} סוללות שיש לטעון בקרוב | {string0} סוללות שיש לטעון בקרוב", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "סוללה אחת כבר אמורה הייתה להיטען | {string0} סוללות כבר היו אמורות להיטען | {string0} סוללות כבר היו אמורות להיטען | {string0} סוללות כבר היו אמורות להיטען", + "{count} Unit | {count} Units": "יחידה אחת | {count} יחידות | {count} יחידות | {count} יחידות", + "{count} chore is due to be done | {count} chores are due to be done": "מטלה אחת אמורה להסתיים | {count} מטלות אמורות להסתיים | {count} מטלות אמורות להסתיים | {count} מטלות אמורות להסתיים", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "מטלה אחת חרגה ממועד הסיום | {count} מטלות חרגו ממועד הסיום | {count} מטלות חרגו ממועד הסיום | {count} מטלות חרגו ממועד הסיום", + "{count} battery is due to be charged | {count} batteries are due to be charged": "סוללה אחת שיש לטעון בקרוב | {count} סוללות שיש לטעון בקרוב | {count} סוללות שיש לטעון בקרוב | {count} סוללות שיש לטעון בקרוב", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "סוללה אחת כבר אמורה הייתה להיטען | {count} סוללות כבר היו אמורות להיטען | {count} סוללות כבר היו אמורות להיטען | {count} סוללות כבר היו אמורות להיטען", "in singular form": "בצורת יחיד", "Quantity unit": "יחידת כמות", "Only check if any amount is in stock": "לסמן רק אם יש כמות כלשהי במלאי", @@ -204,8 +204,8 @@ "Category": "קטגוריה", "Edit task": "עריכת משימה", "Are you sure to delete task \"{string0}\"?": "למחוק את המשימה „{string0}”?", - "{string0} task is due to be done | {string0} tasks are due to be done": "משימה אחת אמורה להסתיים | {string0} משימות אמורות להסתיים | {string0} משימות אמורות להסתיים | {string0} משימות אמורות להסתיים", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "משימה אחת חרגה ממועד הסיום | {string0} משימות חרגו ממועד הסיום | {string0} משימות חרגו ממועד הסיום | {string0} משימות חרגו ממועד הסיום", + "{count} task is due to be done | {count} tasks are due to be done": "משימה אחת אמורה להסתיים | {count} משימות אמורות להסתיים | {count} משימות אמורות להסתיים | {count} משימות אמורות להסתיים", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "משימה אחת חרגה ממועד הסיום | {count} משימות חרגו ממועד הסיום | {count} משימות חרגו ממועד הסיום | {count} משימות חרגו ממועד הסיום", "Edit task category": "עריכת קטגוריית משימות", "Create task category": "יצירת קטגוריית משימות", "Product groups": "קבוצות מוצרים", @@ -351,7 +351,7 @@ "Plural count": "ספירת ריבוי", "Plural rule": "כלל ריבוי", "in plural form": "בצורת ריבוי", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "אין מספיק במלאי, רכיב אחד חסר | אין מספיק במלאי, {string0} רכיבים חסרים | אין מספיק במלאי, {string0} רכיבים חסרים | אין מספיק במלאי, {string0} רכיבים חסרים", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "אין מספיק במלאי, רכיב אחד חסר | אין מספיק במלאי, {count} רכיבים חסרים | אין מספיק במלאי, {count} רכיבים חסרים | אין מספיק במלאי, {count} רכיבים חסרים", "Not enough in stock, but already on the shopping list": "אין מספיק במלאי, אבל כבר מופיע ברשימת הקניות", "Not enough in stock": "אין מספיק במלאי", "Expiring soon days": "ימים לתפוגה בקרוב", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "צריכה של {string1} מתוך {string2}", "Meal plan": "תכנית ארוחות", "Add recipe on {string0}": "הוספת מתכון תחת {string0}", - "{string0} serving | {string0} servings": "מנה אחת | {string0} מנות | {string0} מנות | {string0} מנות", + "{count} serving | {count} servings": "מנה אחת | {count} מנות | {count} מנות | {count} מנות", "Week costs": "עלויות שבועיות", "Configuration": "הגדרות", "A predefined list of values, one per line": "רשימה מוגדרת מראש של ערכים, אחד בשורה", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "משמעות הדבר היא שההוצאה הבאה לפועל של המטלה הזאת תוקצה לבא בתור בסדר האלפבית", "Assign to": "הקצאה אל", "This assignment type requires that at least one is assigned": "סוג הקצאה זו דורש לפחות מוקצה אחד", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "מטלה אחת מוקצית אלי | {string0} מטלות מוקצות אלי | {string0} מטלות מוקצות אלי | {string0} מטלות מוקצות אלי", + "{count} chore is assigned to me | {count} chores are assigned to me": "מטלה אחת מוקצית אלי | {count} מטלות מוקצות אלי | {count} מטלות מוקצות אלי | {count} מטלות מוקצות אלי", "Assigned to me": "מוקצה אלי", "assigned to {string0}": "מוקצה אל {string0}", "Assignment": "הקצאה", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "בהוצאת מוצר מהקפאה (הפשרה), מועד התוקף יוחלף ביום הנוכחי + כמות זו של ימים", "Default due days after thawing": "ימי תוקף כבררת מחדל לאחר הפשרה", "Next due date": "תאריך סיום התוקף הבא", - "{string0} product is due | {string0} products are due": "תוקפו של מוצר אחד יפוג | תוקפם של {string0} מוצרים יפוג | תוקפם של {string0} מוצרים יפוג | תוקפם של {string0} מוצרים יפוג", + "{count} product is due | {count} products are due": "תוקפו של מוצר אחד יפוג | תוקפם של {count} מוצרים יפוג | תוקפם של {count} מוצרים יפוג | תוקפם של {count} מוצרים יפוג", "Due date": "תאריך סיום התוקף", "Never overdue": "אין תפוגה", - "{string0} product is expired | {string0} products are expired": "פג תוקפו של אחד המוצרים | תוקפם של {string0} מוצרים פג | תוקפם של {string0} מוצרים פג | תוקפם של {string0} מוצרים פג", + "{count} product is expired | {count} products are expired": "פג תוקפו של אחד המוצרים | תוקפם של {count} מוצרים פג | תוקפם של {count} מוצרים פג | תוקפם של {count} מוצרים פג", "Expired": "התוקף פג", "Due soon days": "ימים לתפוגת תוקף קרבה", "Add overdue/expired products": "מוצרים שחרגו מהתוקף/פג תוקפם", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "עברית", "Tamil": "טמילית", "Finnish": "פינית", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/hu.json b/locale/hu.json index af196f56..8541a756 100644 --- a/locale/hu.json +++ b/locale/hu.json @@ -1,12 +1,12 @@ { "Stock overview": "Készlet áttekintése", - "{string0} product expires | {string0} products expiring": "{string0} termék szavatossága le fog járni | {string0} termék szavatossága le fog járni", - "within the next day | within the next {string0} days": "a következő napon | a következő {string0} napon belül", - "{string0} product is already expired | {string0} products are already expired": " {string0} termék szavatossága lejárt | {string0} termék szavatossága lejárt", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} termék a meghatározott minimum mennyiség alá esett | {string0} termék a meghatározott minimum mennyiség alá esett", + "{count} product expires | {count} products expiring": "{count} termék szavatossága le fog járni | {count} termék szavatossága le fog járni", + "within the next day | within the next {count} days": "a következő napon | a következő {count} napon belül", + "{count} product is already expired | {count} products are already expired": " {count} termék szavatossága lejárt | {count} termék szavatossága lejárt", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} termék a meghatározott minimum mennyiség alá esett | {count} termék a meghatározott minimum mennyiség alá esett", "Product": "Termék", - "{string0} Product | {string0} Products": "{string0} Termék | {string0} Termék", + "{count} Product | {count} Products": "{count} Termék | {count} Termék", "Amount": "Mennyiség", "Logout": "Kijelentkezés", "Chores overview": "Házimunkák áttekintése", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Teljesült követelmények", "Put missing products on shopping list": "Hiányzó termékek hozzáadása a bevásárlólistához", "Enough in stock": "Elegendő mennyiség készleten", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Nincs elegendő készleten, {string0} hozzávaló hiányzik, de már szerepel a bevásárlólistán | Nincs elegendő készleten, {string0} hozzávaló hiányzik, de már szerepel a bevásárlólistán", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Nincs elegendő készleten, {count} hozzávaló hiányzik, de már szerepel a bevásárlólistán | Nincs elegendő készleten, {count} hozzávaló hiányzik, de már szerepel a bevásárlólistán", "Expand to fullscreen": "Teljes képernyős megjelenítés", "Ingredients": "Hozzávalók", "Preparation": "Elkészítés", @@ -177,11 +177,11 @@ "No price history available": "Nincs elérhető ár előzmény", "Price": "Ár", "Unit": "Egység", - "{string0} Unit | {string0} Units": "{string0} Egység | {string0} Egység", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} házimunkát kell elvégezni | {string0} házimunkát kell elvégezni", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} házimunkára lejárt a határidő | {string0} házimunkára lejárt a határidő", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} elem feltöltése esedékes | {string0} elem feltöltése esedékes", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": " | ", + "{count} Unit | {count} Units": "{count} Egység | {count} Egység", + "{count} chore is due to be done | {count} chores are due to be done": "{count} házimunkát kell elvégezni | {count} házimunkát kell elvégezni", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} házimunkára lejárt a határidő | {count} házimunkára lejárt a határidő", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} elem feltöltése esedékes | {count} elem feltöltése esedékes", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": " | ", "in singular form": "egyes számban", "Quantity unit": "Mennyiségi egység", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Kategória", "Edit task": "Feladat szerkesztése", "Are you sure to delete task \"{string0}\"?": "Biztosan törölni szeretnéd a következő feladatot: \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} feladatot kell elvégezni | {string0} feladatot kell elvégezni", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} feladat elvégzése késésben van | {string0} feladat elvégzése késésben van", + "{count} task is due to be done | {count} tasks are due to be done": "{count} feladatot kell elvégezni | {count} feladatot kell elvégezni", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} feladat elvégzése késésben van | {count} feladat elvégzése késésben van", "Edit task category": "Feladat kategória szerkesztése", "Create task category": "Feladat kategória létrehozása", "Product groups": "Termékcsoportok", @@ -351,7 +351,7 @@ "Plural count": "", "Plural rule": "", "in plural form": "többesszámban", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Nincs elegendő készleten, {string0} hozzávaló hiányzik | Nincs elegendő készleten, {string0} hozzávaló hiányzik", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Nincs elegendő készleten, {count} hozzávaló hiányzik | Nincs elegendő készleten, {count} hozzávaló hiányzik", "Not enough in stock, but already on the shopping list": "Nincs elegendő készleten, de már szerepel a bevásárlólistán", "Not enough in stock": "Nincs elegendő készleten", "Expiring soon days": "Hamarosan lejár - napok száma", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "{string1} {string2} felhasználása", "Meal plan": "Menü terv", "Add recipe on {string0}": "", - "{string0} serving | {string0} servings": "{string0} tálalás | {string0} adag", + "{count} serving | {count} servings": "{count} tálalás | {count} adag", "Week costs": "Heti költség", "Configuration": "Konfiguráció", "A predefined list of values, one per line": "", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "", "Assign to": "Hozzárendelve", "This assignment type requires that at least one is assigned": "", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} házimunka hozzám rendelve | {string0} házimunka hozzám rendelve", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} házimunka hozzám rendelve | {count} házimunka hozzám rendelve", "Assigned to me": "Hozzám rendelve", "assigned to {string0}": "hozzárendelve ehhez: {string0}", "Assignment": "Hozzárendelés", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "Határidő", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": "{string0} termék lejárt | {string0} termék lejárt", + "{count} product is expired | {count} products are expired": "{count} termék lejárt | {count} termék lejárt", "Expired": "Lejárt", "Due soon days": "", "Add overdue/expired products": "", @@ -682,7 +682,7 @@ "timeago_locale": "hu", "timeago_nan": "NaN éve", "moment_locale": "hu", - "datatables_localization": "{\"sEmptyTable\":\"Nincs rendelkezésre álló adat\",\"sInfo\":\"Találatok: _START_ - _END_ Összesen: _TOTAL_\",\"sInfoEmpty\":\"Nulla találat\",\"sInfoFiltered\":\"(_MAX_ összes rekord közül szűrve)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\" \",\"sLengthMenu\":\"_MENU_ találat oldalanként\",\"sLoadingRecords\":\"Betöltés...\",\"sProcessing\":\"Feldolgozás...\",\"sSearch\":\"Keresés:\",\"sZeroRecords\":\"Nincs a keresésnek megfelelő találat\",\"oPaginate\":{\"sFirst\":\"Első\",\"sPrevious\":\"Előző\",\"sNext\":\"Következő\",\"sLast\":\"Utolsó\"},\"oAria\":{\"sSortAscending\":\": aktiválja a növekvő rendezéshez\",\"sSortDescending\":\": aktiválja a csökkenő rendezéshez\"},\"select\":{\"rows\":{\"0\":\"\",\"1\":\"1 sor kiválasztva\",\"_\":\"{num0} sor kiválasztva\"}},\"buttons\":{\"print\":\"Nyomtatás\",\"colvis\":\"Oszlopok\",\"copy\":\"Másolás\",\"copyTitle\":\"Vágólapra másolás\",\"copySuccess\":{\"1\":\"1 sor másolva\",\"_\":\"{num0} sor másolva\"}}}", + "datatables_localization": "{\"sEmptyTable\":\"Nincs rendelkezésre álló adat\",\"sInfo\":\"Találatok: _START_ - _END_ Összesen: _TOTAL_\",\"sInfoEmpty\":\"Nulla találat\",\"sInfoFiltered\":\"(_MAX_ összes rekord közül szűrve)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\" \",\"sLengthMenu\":\"_MENU_ találat oldalanként\",\"sLoadingRecords\":\"Betöltés...\",\"sProcessing\":\"Feldolgozás...\",\"sSearch\":\"Keresés:\",\"sZeroRecords\":\"Nincs a keresésnek megfelelő találat\",\"oPaginate\":{\"sFirst\":\"Első\",\"sPrevious\":\"Előző\",\"sNext\":\"Következő\",\"sLast\":\"Utolsó\"},\"oAria\":{\"sSortAscending\":\": aktiválja a növekvő rendezéshez\",\"sSortDescending\":\": aktiválja a csökkenő rendezéshez\"},\"select\":{\"rows\":{\"0\":\"\",\"1\":\"1 sor kiválasztva\",\"_\":\"{string0} sor kiválasztva\"}},\"buttons\":{\"print\":\"Nyomtatás\",\"colvis\":\"Oszlopok\",\"copy\":\"Másolás\",\"copyTitle\":\"Vágólapra másolás\",\"copySuccess\":{\"1\":\"1 sor másolva\",\"_\":\"{string0} sor másolva\"}}}", "summernote_locale": "hu-HU", "fullcalendar_locale": "hu", "bootstrap-select_locale": "hu_HU", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Héber (Izrael)", "Tamil": "Tamil", "Finnish": "Finn", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/it.json b/locale/it.json index 5cbf2e38..eeaa7c84 100644 --- a/locale/it.json +++ b/locale/it.json @@ -1,12 +1,12 @@ { "Stock overview": "Riepilogo della dispensa", - "{string0} product expires | {string0} products expiring": "{string0} prodotto in scadenza | {string0} prodotti in scadenza", - "within the next day | within the next {string0} days": "entro domani | entro i prossimi {string0} giorni", - "{string0} product is already expired | {string0} products are already expired": "{string0} prodotto è già scaduto | {string0} prodotti sono già scaduti", - "{string0} product is overdue | {string0} products are overdue": "{string0} prodotto è scaduto | {string0} prodotti sono scaduti", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} prodotto sotto la quantità minima di dispensa definita | {string0} prodotti sono al di sotto della quantità minima di dispensa definita", + "{count} product expires | {count} products expiring": "{count} prodotto in scadenza | {count} prodotti in scadenza", + "within the next day | within the next {count} days": "entro domani | entro i prossimi {count} giorni", + "{count} product is already expired | {count} products are already expired": "{count} prodotto è già scaduto | {count} prodotti sono già scaduti", + "{count} product is overdue | {count} products are overdue": "{count} prodotto è scaduto | {count} prodotti sono scaduti", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} prodotto sotto la quantità minima di dispensa definita | {count} prodotti sono al di sotto della quantità minima di dispensa definita", "Product": "Prodotto", - "{string0} Product | {string0} Products": "{string0} Prodotto | {string0} Prodotti", + "{count} Product | {count} Products": "{count} Prodotto | {count} Prodotti", "Amount": "Quantità", "Logout": "Disconnetti", "Chores overview": "Riepilogo delle faccende", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Requisiti soddisfatti", "Put missing products on shopping list": "Metti i prodotti mancanti nella lista della spesa", "Enough in stock": "Abbastanza in magazzino", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Quantità insufficiente in dispensa, {string0}ingrediente mancante ma già nella lista della spesa | Quantità insufficienti in dispensa, {string0} ingredienti mancanti ma già nella lista della spesa", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Quantità insufficiente in dispensa, {count}ingrediente mancante ma già nella lista della spesa | Quantità insufficienti in dispensa, {count} ingredienti mancanti ma già nella lista della spesa", "Expand to fullscreen": "Espandi a schermo intero", "Ingredients": "Ingredienti", "Preparation": "Preparazione", @@ -177,11 +177,11 @@ "No price history available": "Cronologia prezzi non disponibile", "Price": "Prezzo", "Unit": "Unità", - "{string0} Unit | {string0} Units": "{string0} Unità | {string0} Unità", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} faccenda in scadenza da fare | {string0} faccende in scadenza da fare", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} faccenda in ritardo da fare | {string0} faccende in ritardo da fare", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batteria deve essere caricata | {string0} batterie devono essere caricate", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batteria è in ritardo di essere ricaricata | {string0} batterie sono in ritardo di essere ricaricate", + "{count} Unit | {count} Units": "{count} Unità | {count} Unità", + "{count} chore is due to be done | {count} chores are due to be done": "{count} faccenda in scadenza da fare | {count} faccende in scadenza da fare", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} faccenda in ritardo da fare | {count} faccende in ritardo da fare", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batteria deve essere caricata | {count} batterie devono essere caricate", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batteria è in ritardo di essere ricaricata | {count} batterie sono in ritardo di essere ricaricate", "in singular form": "in forma singolare", "Quantity unit": "Unità di quantità", "Only check if any amount is in stock": "Controlla soltanto se è disponibile in dispensa una qualsiasi quantità ", @@ -204,8 +204,8 @@ "Category": "Categoria", "Edit task": "Modifica compito", "Are you sure to delete task \"{string0}\"?": "Sei sicuro di eliminare il compito \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} compito da fare | {string0} compiti da fare", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} compito in ritardo da fare | {string0} compiti in ritardo da fare", + "{count} task is due to be done | {count} tasks are due to be done": "{count} compito da fare | {count} compiti da fare", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} compito in ritardo da fare | {count} compiti in ritardo da fare", "Edit task category": "Modifica categoria di compito", "Create task category": "Crea categoria di compito", "Product groups": "Gruppi di prodotti", @@ -351,7 +351,7 @@ "Plural count": "Conteggio plurale", "Plural rule": "Regola plurale", "in plural form": "in forma plurale", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Non abbastanza in dispensa, {string0} ingrediente mancante | Non abbastanza in dispensa, {string0} ingredienti mancanti", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Non abbastanza in dispensa, {count} ingrediente mancante | Non abbastanza in dispensa, {count} ingredienti mancanti", "Not enough in stock, but already on the shopping list": "Non abbastanza disponibile in dispensa, ma già nella lista della spesa", "Not enough in stock": "Non abbastanza in dispensa", "Expiring soon days": "In scadenza tra pochi giorni", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consumare {string1} di {string2}", "Meal plan": "Piano dei pasti", "Add recipe on {string0}": "Aggiungi ricetta a {string0}", - "{string0} serving | {string0} servings": "{string0} porzione | {string0} porzioni", + "{count} serving | {count} servings": "{count} porzione | {count} porzioni", "Week costs": "Costi settimanali", "Configuration": "Configurazione", "A predefined list of values, one per line": "Un elenco predefinito di valori, uno per riga", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Questo significa che la prossima esecuzione di questa faccenda verrà assegnata al prossimo in ordine alfabetico", "Assign to": "Assegnato a", "This assignment type requires that at least one is assigned": "Questo tipo di assegnazione richiede che almeno uno sia incaricato", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} faccenda è assegnata a me | {string0} faccende sono assegnate a me", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} faccenda è assegnata a me | {count} faccende sono assegnate a me", "Assigned to me": "Assegnato a me", "assigned to {string0}": "assegnato a {string0}", "Assignment": "Incarico", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Spostando questo prodotto da un luogo di congelamento (quindi quando lo scongeli), la data di scadenza verrà sostituita con oggi + questo numero di giorni", "Default due days after thawing": "Giorni di scadenza predefiniti dopo lo scongelamento", "Next due date": "Prossima data di scadenza", - "{string0} product is due | {string0} products are due": "{string0} prodotto è scaduto | {string0} prodotti sono scaduti", + "{count} product is due | {count} products are due": "{count} prodotto è scaduto | {count} prodotti sono scaduti", "Due date": "Data di scadenza", "Never overdue": "Non scade mai", - "{string0} product is expired | {string0} products are expired": "{string0} prodotto è scaduto | {string0} prodotti sono scaduti", + "{count} product is expired | {count} products are expired": "{count} prodotto è scaduto | {count} prodotti sono scaduti", "Expired": "Scaduto", "Due soon days": "Scade tra pochi giorni", "Add overdue/expired products": "Aggiungi prodotti scaduti", @@ -682,7 +682,7 @@ "timeago_locale": "it", "timeago_nan": "NaN anni", "moment_locale": "it", - "datatables_localization": "{\"sProcessing\": \"Traitement en cours...\",\"sSearch\": \"Rechercher :\",\"sLengthMenu\": \"Afficher _MENU_ éléments\",\"sInfo\": \"Affichage de l'élément _START_ à _END_ sur _TOTAL_ éléments\",\"sInfoEmpty\": \"Affichage de l'élément 0 à 0 sur 0 élément\",\"sInfoFiltered\": \"(filtré de _MAX_ éléments au total)\",\"sInfoPostFix\": \"\",\"sLoadingRecords\": \"Chargement en cours...\",\"sZeroRecords\": \"Aucun élément à afficher\",\"sEmptyTable\": \"Aucune donnée disponible dans le tableau\",\"oPaginate\": {\"sFirst\": \"Premier\",\"sPrevious\": \"Précédent\",\"sNext\": \"Suivant\",\"sLast\": \"Dernier\"},\"oAria\": {\"sSortAscending\": \": activer pour trier la colonne par ordre croissant\",\"sSortDescending\": \": activer pour trier la colonne par ordre décroissant\"},\"select\": {\"rows\": {_: \"{num0} lignes séléctionnées\",0: \"Aucune ligne séléctionnée\",1: \"1 ligne séléctionnée\"} }}", + "datatables_localization": "{\"sProcessing\": \"Traitement en cours...\",\"sSearch\": \"Rechercher :\",\"sLengthMenu\": \"Afficher _MENU_ éléments\",\"sInfo\": \"Affichage de l'élément _START_ à _END_ sur _TOTAL_ éléments\",\"sInfoEmpty\": \"Affichage de l'élément 0 à 0 sur 0 élément\",\"sInfoFiltered\": \"(filtré de _MAX_ éléments au total)\",\"sInfoPostFix\": \"\",\"sLoadingRecords\": \"Chargement en cours...\",\"sZeroRecords\": \"Aucun élément à afficher\",\"sEmptyTable\": \"Aucune donnée disponible dans le tableau\",\"oPaginate\": {\"sFirst\": \"Premier\",\"sPrevious\": \"Précédent\",\"sNext\": \"Suivant\",\"sLast\": \"Dernier\"},\"oAria\": {\"sSortAscending\": \": activer pour trier la colonne par ordre croissant\",\"sSortDescending\": \": activer pour trier la colonne par ordre décroissant\"},\"select\": {\"rows\": {_: \"{string0} lignes séléctionnées\",0: \"Aucune ligne séléctionnée\",1: \"1 ligne séléctionnée\"} }}", "summernote_locale": "it-IT", "fullcalendar_locale": "it", "bootstrap-select_locale": "it_IT", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Ebraico (Israele)", "Tamil": "Tamil", "Finnish": "Finlandese", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/ja.json b/locale/ja.json index 76afc7b3..420cca5f 100644 --- a/locale/ja.json +++ b/locale/ja.json @@ -1,12 +1,12 @@ { "Stock overview": "在庫状況", - "{string0} product expires | {string0} products expiring": "{string0} 個が期限切れ", - "within the next day | within the next {string0} days": "({string0} 日以内に)", - "{string0} product is already expired | {string0} products are already expired": "{string0} 個がすでに消費期限切れ", - "{string0} product is overdue | {string0} products are overdue": "{string0} 個がすでに期限切れ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} 個が設定した最小の在庫未満", + "{count} product expires | {count} products expiring": "{count} 個が期限切れ", + "within the next day | within the next {count} days": "({count} 日以内に)", + "{count} product is already expired | {count} products are already expired": "{count} 個がすでに消費期限切れ", + "{count} product is overdue | {count} products are overdue": "{count} 個がすでに期限切れ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} 個が設定した最小の在庫未満", "Product": "製品", - "{string0} Product | {string0} Products": "{string0} 個", + "{count} Product | {count} Products": "{count} 個", "Amount": "量", "Logout": "ログアウト", "Chores overview": "家事の状況", @@ -144,7 +144,7 @@ "Requirements fulfilled": "要件", "Put missing products on shopping list": "不足している製品を買い物リストに入れる", "Enough in stock": "在庫あり", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "在庫不足。{string0} 個の材料がありませんが買い物リストに存在します", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "在庫不足。{count} 個の材料がありませんが買い物リストに存在します", "Expand to fullscreen": "フルスクリーンに拡大", "Ingredients": "材料", "Preparation": "準備", @@ -177,11 +177,11 @@ "No price history available": "価格履歴を利用できません", "Price": "価格", "Unit": "単位", - "{string0} Unit | {string0} Units": "{string0} 単位", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} 個の家事が予定されています", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} 個の家事が期限切れ", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} 個の電池の充電が予定されています", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} 個の交換が期限切れ", + "{count} Unit | {count} Units": "{count} 単位", + "{count} chore is due to be done | {count} chores are due to be done": "{count} 個の家事が予定されています", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} 個の家事が期限切れ", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} 個の電池の充電が予定されています", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} 個の交換が期限切れ", "in singular form": "単数形", "Quantity unit": "数量単位", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "カテゴリ", "Edit task": "タスクを編集", "Are you sure to delete task \"{string0}\"?": "タスク \"{string0}\" を本当に削除しますか?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} 個のタスクが予定されています", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} 個のタスクが期限切れ", + "{count} task is due to be done | {count} tasks are due to be done": "{count} 個のタスクが予定されています", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} 個のタスクが期限切れ", "Edit task category": "タスク カテゴリを編集", "Create task category": "タスク カテゴリを作成", "Product groups": "製品グループ", @@ -351,7 +351,7 @@ "Plural count": "", "Plural rule": "", "in plural form": "", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "在庫不足。{string0} 個の材料がありません", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "在庫不足。{count} 個の材料がありません", "Not enough in stock, but already on the shopping list": "在庫不足ですが、すでに買い物リストに存在します", "Not enough in stock": "在庫不足", "Expiring soon days": "まもなく期限切れ", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "{string2} の {string1} を消費", "Meal plan": "食事プラン", "Add recipe on {string0}": "{string0} のレシピを追加", - "{string0} serving | {string0} servings": "{string0} 人分", + "{count} serving | {count} servings": "{count} 人分", "Week costs": "週コスト", "Configuration": "構成", "A predefined list of values, one per line": "", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "この家事の次の実行がアルファベット順に割り当てられることを意味します", "Assign to": "次の人に割り当てる", "This assignment type requires that at least one is assigned": "", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0}個の家事が自分に割り当てられています", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count}個の家事が自分に割り当てられています", "Assigned to me": "自分に割り当てる", "assigned to {string0}": "{string0}に割り当てる", "Assignment": "割り当て", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "次の期日", - "{string0} product is due | {string0} products are due": "", + "{count} product is due | {count} products are due": "", "Due date": "", "Never overdue": "期限なし", - "{string0} product is expired | {string0} products are expired": "", + "{count} product is expired | {count} products are expired": "", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -821,22 +821,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/ko_KR.json b/locale/ko_KR.json index fbded23f..b5f49198 100644 --- a/locale/ko_KR.json +++ b/locale/ko_KR.json @@ -1,12 +1,12 @@ { "Stock overview": "재고 개요", - "{string0} product expires | {string0} products expiring": "{string0}개 제품 유통기한 만료", - "within the next day | within the next {string0} days": "(향후 {string0} 일 이내)", - "{string0} product is already expired | {string0} products are already expired": "{string0} 개 제품 유통기한이 이미 만료됨", - "{string0} product is overdue | {string0} products are overdue": "", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} 개 제품은 재고 미달", + "{count} product expires | {count} products expiring": "{count}개 제품 유통기한 만료", + "within the next day | within the next {count} days": "(향후 {count} 일 이내)", + "{count} product is already expired | {count} products are already expired": "{count} 개 제품 유통기한이 이미 만료됨", + "{count} product is overdue | {count} products are overdue": "", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} 개 제품은 재고 미달", "Product": "제품", - "{string0} Product | {string0} Products": "{string0} 개 제품", + "{count} Product | {count} Products": "{count} 개 제품", "Amount": "합계", "Logout": "로그아웃", "Chores overview": "집안일 개요", @@ -144,7 +144,7 @@ "Requirements fulfilled": "요구 사항 충족", "Put missing products on shopping list": "누락제품 구매목록 등록", "Enough in stock": "재고가 충분함", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "재고 부족. 재료 {string0} 개가 누락되었지만 이미 쇼핑 목록에 있음", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "재고 부족. 재료 {count} 개가 누락되었지만 이미 쇼핑 목록에 있음", "Expand to fullscreen": "전체 화면으로 확장", "Ingredients": "재료", "Preparation": "준비", @@ -177,11 +177,11 @@ "No price history available": "사용 가능한 금액 정보 없음", "Price": "금액", "Unit": "단위", - "{string0} Unit | {string0} Units": "{string0} 개", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} 개의 집안일이 예정되어 있습니다.", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} 개의 집안일이 밀려있습니다.", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} 배터리를 충전해야 합니다.", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} 배터리 충전 기한이 지났습니다.", + "{count} Unit | {count} Units": "{count} 개", + "{count} chore is due to be done | {count} chores are due to be done": "{count} 개의 집안일이 예정되어 있습니다.", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} 개의 집안일이 밀려있습니다.", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} 배터리를 충전해야 합니다.", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} 배터리 충전 기한이 지났습니다.", "in singular form": "단수형", "Quantity unit": "단위", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "구분", "Edit task": "작업 수정", "Are you sure to delete task \"{string0}\"?": "작업을 삭제하시겠습니까? \"{string0}\"", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} 작업이 완료될 예정입니다", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} 작업이 완료되지 않았습니다", + "{count} task is due to be done | {count} tasks are due to be done": "{count} 작업이 완료될 예정입니다", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} 작업이 완료되지 않았습니다", "Edit task category": "작업 구분 수정", "Create task category": "작업 구분 생성", "Product groups": "제품 그룹", @@ -351,7 +351,7 @@ "Plural count": "Plural count", "Plural rule": "Plural rule", "in plural form": "복수형", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "재고 부족, 재료{string0} 개 누락", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "재고 부족, 재료{count} 개 누락", "Not enough in stock, but already on the shopping list": "재고 부족, 그러나 구매 목록에 이미 있음", "Not enough in stock": "재고 부족", "Expiring soon days": "곧 만료", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consume {string1} of {string2}", "Meal plan": "식단 계획", "Add recipe on {string0}": "Add recipe on {string0}", - "{string0} serving | {string0} servings": "{string0} 인분", + "{count} serving | {count} servings": "{count} 인분", "Week costs": "주당 비용", "Configuration": "설정", "A predefined list of values, one per line": "A predefined list of values, one per line", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "이것은 이 일의 다음 실행이 알파벳순으로 할당된다는 것을 의미한다.", "Assign to": "역할 분담", "This assignment type requires that at least one is assigned": "This assignment type requires that at least one is assigned", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} 개의 집안일이 나에게 할당되었습니다.", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} 개의 집안일이 나에게 할당되었습니다.", "Assigned to me": "나에게 할당됨", "assigned to {string0}": "{string0}에게 할당됨", "Assignment": "작업 배분", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": "", + "{count} product is due | {count} products are due": "", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": "", + "{count} product is expired | {count} products are expired": "", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -860,22 +860,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/nl.json b/locale/nl.json index 182185e2..9507feea 100644 --- a/locale/nl.json +++ b/locale/nl.json @@ -1,12 +1,12 @@ { "Stock overview": "Voorraadoverzicht", - "{string0} product expires | {string0} products expiring": "{string0}product verloopt | {string0} producten verlopen", - "within the next day | within the next {string0} days": "tussen nu en morgen | binnen {string0} dagen", - "{string0} product is already expired | {string0} products are already expired": "{string0} product is al verlopen | {string0} producten zijn al verlopen", - "{string0} product is overdue | {string0} products are overdue": "product is verlopen | {string0} producten zijn verlopen", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} product onder min. voorraadhoeveelheid | {string0} producten onder min. voorraadhoeveelheid", + "{count} product expires | {count} products expiring": "{count}product verloopt | {count} producten verlopen", + "within the next day | within the next {count} days": "tussen nu en morgen | binnen {count} dagen", + "{count} product is already expired | {count} products are already expired": "{count} product is al verlopen | {count} producten zijn al verlopen", + "{count} product is overdue | {count} products are overdue": "product is verlopen | {count} producten zijn verlopen", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} product onder min. voorraadhoeveelheid | {count} producten onder min. voorraadhoeveelheid", "Product": "Product", - "{string0} Product | {string0} Products": "{string0} Product | {string0} producten", + "{count} Product | {count} Products": "{count} Product | {count} producten", "Amount": "Hoeveelheid", "Logout": "Uitloggen", "Chores overview": "Overzicht klussen", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Vereisten volbracht", "Put missing products on shopping list": "Voeg ontbrekende producten toe aan boodschappenlijst", "Enough in stock": "Genoeg in voorraad", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Niet genoeg op voorraad, {string0} ontbreekt en staat al op de boodschappenlijst | Niet genoeg in voorraad, {string0} ingrediënten ontbreken en staan al op de boodschappenlijst", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Niet genoeg op voorraad, {count} ontbreekt en staat al op de boodschappenlijst | Niet genoeg in voorraad, {count} ingrediënten ontbreken en staan al op de boodschappenlijst", "Expand to fullscreen": "Fullscreen", "Ingredients": "Ingrediënten", "Preparation": "Bereiding", @@ -177,11 +177,11 @@ "No price history available": "Geen prijsgeschiedenis beschikbaar", "Price": "Prijs", "Unit": "Eenheid", - "{string0} Unit | {string0} Units": "{string0}eenheid | {string0}eenheden", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0}klusje is om uitgevoerd te worden | {string0} klussen om te doen", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0}klus moet nog gedaan worden | {string0} achterstallige klussen te doen", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "Er moet {string0} batterij opgeladen worden | Er moeten {string0} batterijen opgeladen worden", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batterij moet nog opgeladen worden | {string0}batterijen moeten nog opgeladen worden", + "{count} Unit | {count} Units": "{count}eenheid | {count}eenheden", + "{count} chore is due to be done | {count} chores are due to be done": "{count}klusje is om uitgevoerd te worden | {count} klussen om te doen", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count}klus moet nog gedaan worden | {count} achterstallige klussen te doen", + "{count} battery is due to be charged | {count} batteries are due to be charged": "Er moet {count} batterij opgeladen worden | Er moeten {count} batterijen opgeladen worden", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batterij moet nog opgeladen worden | {count}batterijen moeten nog opgeladen worden", "in singular form": "In enkelvoud", "Quantity unit": "Hoeveelheidseenheid", "Only check if any amount is in stock": "Controleer alleen of er een aantal op voorraad is", @@ -204,8 +204,8 @@ "Category": "Categorie", "Edit task": "Bewerk taak", "Are you sure to delete task \"{string0}\"?": "Weet u zeker dat u taak \"{string0}\" wilt verwijderen?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0}taak moet uitgevoerd worden | {string0} taken te doen", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0}taak moet nog uitgevoerd worden | {string0} achterstallige taken te doen", + "{count} task is due to be done | {count} tasks are due to be done": "{count}taak moet uitgevoerd worden | {count} taken te doen", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count}taak moet nog uitgevoerd worden | {count} achterstallige taken te doen", "Edit task category": "Bewerk taakcategorie", "Create task category": "Maak kluscategorie", "Product groups": "Productcategorieën", @@ -351,7 +351,7 @@ "Plural count": "Meervoud aantal", "Plural rule": "Meervoudsregel", "in plural form": "In meervoud", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Niet genoeg op voorraad, {string0} ingrediënt ontbreekt. | Niet genoeg op voorraad, {string0} ingrediënten ontbreken.", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Niet genoeg op voorraad, {count} ingrediënt ontbreekt. | Niet genoeg op voorraad, {count} ingrediënten ontbreken.", "Not enough in stock, but already on the shopping list": "Niet genoeg op voorraad, maar al op het boodschappenlijstje", "Not enough in stock": "Niet genoeg op voorraad", "Expiring soon days": "Vervalt binnenkort dagen", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consumeer {string1} {string2}", "Meal plan": "Maaltijdplan", "Add recipe on {string0}": "Voeg recept toe op {string0}", - "{string0} serving | {string0} servings": "{string0} portie | {string0} porties", + "{count} serving | {count} servings": "{count} portie | {count} porties", "Week costs": "Weekkosten", "Configuration": "Configuratie", "A predefined list of values, one per line": "Een vooraf gedefinieerde lijst met waarden, één per regel", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Dit betekent dat de volgende uitvoering van deze klus in alfabetische volgorde aan iemand toegewezen wordt.", "Assign to": "Wijs toe aan", "This assignment type requires that at least one is assigned": "Voor dit toewijzingstype moet er minstens één worden toegewezen", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} klusje is toegewezen aan mij | {string0} klussen toegewezen aan mij", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} klusje is toegewezen aan mij | {count} klussen toegewezen aan mij", "Assigned to me": "Toegewezen aan mij", "assigned to {string0}": "Toegewezen aan {string0}", "Assignment": "Toewijzing", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "Wanneer dit product uit een diepvriezer locatie wordt gehaald (dus bij ontdooien), zal de THT datum vervangen worden door vandaag + dit aantal dagen", "Default due days after thawing": "Standaard houdbaarheidsdatum na ontdooien", "Next due date": "Volgend houdbaarheidsdatum", - "{string0} product is due | {string0} products are due": "{string0} product verloopt | {string0} producten verlopen", + "{count} product is due | {count} products are due": "{count} product verloopt | {count} producten verlopen", "Due date": "Vervaldatum", "Never overdue": "Verloopt nooit", - "{string0} product is expired | {string0} products are expired": "{string0} product is vervallen | {string0} producten zijn al vervallen", + "{count} product is expired | {count} products are expired": "{count} product is vervallen | {count} producten zijn al vervallen", "Expired": "Overdatum", "Due soon days": "Bij verlopen dagen", "Add overdue/expired products": "Voeg over datum of verlopen producten toe", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Hebreeuws (Israël)", "Tamil": "Tamil", "Finnish": "Fins", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/no.json b/locale/no.json index 8818bfd9..6070fdbf 100644 --- a/locale/no.json +++ b/locale/no.json @@ -1,12 +1,12 @@ { "Stock overview": "Beholdningsoversikt", - "{string0} product expires | {string0} products expiring": "{string0} produkt går ut på dato | {string0} produkter går ut på dato", - "within the next day | within the next {string0} days": "i løpet av neste dag | i løpet av de {string0} neste dagene", - "{string0} product is already expired | {string0} products are already expired": "{string0} produkt har gått ut på dato | {string0} produkter har gått ut på dato", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produkt er under satt minimum for beholdningen | {string0} produkter er under satt minimum for beholdningen", + "{count} product expires | {count} products expiring": "{count} produkt går ut på dato | {count} produkter går ut på dato", + "within the next day | within the next {count} days": "i løpet av neste dag | i løpet av de {count} neste dagene", + "{count} product is already expired | {count} products are already expired": "{count} produkt har gått ut på dato | {count} produkter har gått ut på dato", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produkt er under satt minimum for beholdningen | {count} produkter er under satt minimum for beholdningen", "Product": "Produkt", - "{string0} Product | {string0} Products": "{string0} Produkt | {string0} Produkter", + "{count} Product | {count} Products": "{count} Produkt | {count} Produkter", "Amount": "Mengde", "Logout": "Logg ut", "Chores overview": "Husarbeidsoversikt", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Har jeg alt jeg trenger for denne oppskriften?", "Put missing products on shopping list": "Legg manglende produkter til handlelisten", "Enough in stock": "Nok i beholdningen", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Ikke nok i beholdningen, {string0} ingrediens mangler, men den er på handlelisten. | Ikke nok i beholdningen, {string0} ingredienser mangler, men de er på handlelisten.", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Ikke nok i beholdningen, {count} ingrediens mangler, men den er på handlelisten. | Ikke nok i beholdningen, {count} ingredienser mangler, men de er på handlelisten.", "Expand to fullscreen": "Full skjerm", "Ingredients": "Ingredienser", "Preparation": "Forberedelse / Slik gjør du", @@ -177,11 +177,11 @@ "No price history available": "Ingen prishistorikk tilgjengelig", "Price": "Pris", "Unit": "Enhet", - "{string0} Unit | {string0} Units": "{string0} Enhet | {string0} Enheter", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} husarbeidsoppgave som må gjøres nå | {string0} husarbeidsoppgaver som må gjøres nå", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} husarbeidsoppgave har gått over fristen for utførelse | {string0} husarbeidsoppgaver har gått over fristen for utførelse", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batteri må lades | {string0} batterier må lades", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batteri har forfalt frist for å bli ladet | {string0} batterier har forfalt frist for å bli ladet", + "{count} Unit | {count} Units": "{count} Enhet | {count} Enheter", + "{count} chore is due to be done | {count} chores are due to be done": "{count} husarbeidsoppgave som må gjøres nå | {count} husarbeidsoppgaver som må gjøres nå", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} husarbeidsoppgave har gått over fristen for utførelse | {count} husarbeidsoppgaver har gått over fristen for utførelse", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batteri må lades | {count} batterier må lades", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batteri har forfalt frist for å bli ladet | {count} batterier har forfalt frist for å bli ladet", "in singular form": "I entall", "Quantity unit": "Forpakning", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Kategori", "Edit task": "Endre oppgave", "Are you sure to delete task \"{string0}\"?": "Er du sikker du ønsker slette oppgave \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} oppgave må gjøres | {string0} oppgaver må gjøres", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} oppgave har forfalt frist for å bli utført | {string0} oppgaver har forfalt frist for å bli utført", + "{count} task is due to be done | {count} tasks are due to be done": "{count} oppgave må gjøres | {count} oppgaver må gjøres", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} oppgave har forfalt frist for å bli utført | {count} oppgaver har forfalt frist for å bli utført", "Edit task category": "Endre oppgavekategori", "Create task category": "Opprett oppgavekategori", "Product groups": "Produktgrupper", @@ -351,7 +351,7 @@ "Plural count": "Flertallstelling", "Plural rule": "Flertallsregel", "in plural form": "I flertall", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Ikke nok i beholdningen, {string0} ingrediens mangler | Ikke nok i beholdningen, {string0} ingredienser mangler", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Ikke nok i beholdningen, {count} ingrediens mangler | Ikke nok i beholdningen, {count} ingredienser mangler", "Not enough in stock, but already on the shopping list": "Ikke nok i beholdningen, men er på handelisten", "Not enough in stock": "Ikke nok i beholdningen", "Expiring soon days": "Går ut på dato snart dager", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Forbruk {string1} av {string2}", "Meal plan": "Middagsplanlegger", "Add recipe on {string0}": "Legg oppskrift til {string0}", - "{string0} serving | {string0} servings": "{string0} porsjon | {string0} porsjoner", + "{count} serving | {count} servings": "{count} porsjon | {count} porsjoner", "Week costs": "Utgift for uke", "Configuration": "Oppsett", "A predefined list of values, one per line": "En forhåndsdefinert liste av verdier, en per linje", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Dette betyr at neste utførelse av denne husarbeidsoppgaven vil bli tildelt neste person i alfabetisk rekkefølge", "Assign to": "Tildelt til", "This assignment type requires that at least one is assigned": "Denne tildelningstypen krever at minst en er tildelt", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0}husarbeidsoppgave tildelt meg | {string0} husarbeidsoppgaver tildelt meg", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count}husarbeidsoppgave tildelt meg | {count} husarbeidsoppgaver tildelt meg", "Assigned to me": "Tildelt meg", "assigned to {string0}": "tildelt {string0}", "Assignment": "Oppgave", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -830,22 +830,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/pl.json b/locale/pl.json index 57cd2985..f7f7efe6 100644 --- a/locale/pl.json +++ b/locale/pl.json @@ -1,12 +1,12 @@ { "Stock overview": "Przegląd zapasów", - "{string0} product expires | {string0} products expiring": "{string0} produkt traci ważność | {string0} produkty tracą ważność | {string0} produktów traci ważność | {string0} produktów traci ważność", - "within the next day | within the next {string0} days": "jutro | w ciągu {string0} dni | w ciągu {string0} dni | w ciągu {string0} dni", - "{string0} product is already expired | {string0} products are already expired": "Przeterminowane produkty: {string0} | Przeterminowane produkty: {string0} | Przeterminowane produkty: {string0} | Przeterminowane produkty: {string0}", - "{string0} product is overdue | {string0} products are overdue": "{string0} produkt przeterminowany | {string0} produkty przeterminowane | {string0} produkty przeterminowane | {string0} produkty przeterminowane", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "Produkty poniżej zdefiniowanej minimalnej ilości: {string0} | Produkty poniżej zdefiniowanej minimalnej ilości: {string0} | Produkty poniżej zdefiniowanej minimalnej ilości: {string0} | Produkty poniżej zdefiniowanej minimalnej ilości: {string0}", + "{count} product expires | {count} products expiring": "{count} produkt traci ważność | {count} produkty tracą ważność | {count} produktów traci ważność | {count} produktów traci ważność", + "within the next day | within the next {count} days": "jutro | w ciągu {count} dni | w ciągu {count} dni | w ciągu {count} dni", + "{count} product is already expired | {count} products are already expired": "Przeterminowane produkty: {count} | Przeterminowane produkty: {count} | Przeterminowane produkty: {count} | Przeterminowane produkty: {count}", + "{count} product is overdue | {count} products are overdue": "{count} produkt przeterminowany | {count} produkty przeterminowane | {count} produkty przeterminowane | {count} produkty przeterminowane", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "Produkty poniżej zdefiniowanej minimalnej ilości: {count} | Produkty poniżej zdefiniowanej minimalnej ilości: {count} | Produkty poniżej zdefiniowanej minimalnej ilości: {count} | Produkty poniżej zdefiniowanej minimalnej ilości: {count}", "Product": "Produkt", - "{string0} Product | {string0} Products": "{string0} Produkt | {string0} Produkty | {string0} Produktów | {string0} Produktów", + "{count} Product | {count} Products": "{count} Produkt | {count} Produkty | {count} Produktów | {count} Produktów", "Amount": "Ilość", "Logout": "Wyloguj", "Chores overview": "Przegląd obowiązków", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Wymagania spełnione", "Put missing products on shopping list": "Dodaj brakujące produkty do listy zakupów", "Enough in stock": "Dość na stanie", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Za mało zapasów. Brakuje {string0} składnika, ale jest już na liście zakupów | Za mało zapasów. Brakuje {string0} składników, ale są już na liście zakupów | Za mało zapasów. Brakuje {string0} składników, ale są już na liście zakupów | Za mało zapasów. Brakuje {string0} składników, ale są już na liście zakupów", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Za mało zapasów. Brakuje {count} składnika, ale jest już na liście zakupów | Za mało zapasów. Brakuje {count} składników, ale są już na liście zakupów | Za mało zapasów. Brakuje {count} składników, ale są już na liście zakupów | Za mało zapasów. Brakuje {count} składników, ale są już na liście zakupów", "Expand to fullscreen": "Pokaż w pełnym ekranie", "Ingredients": "Składniki", "Preparation": "Przygotowanie", @@ -177,11 +177,11 @@ "No price history available": "Historia cen jest niedostępna", "Price": "Cena", "Unit": "Jednostka", - "{string0} Unit | {string0} Units": "{string0} Jednostka | {string0} Jednostki | {string0} Jednostek | {string0} Jednostek", - "{string0} chore is due to be done | {string0} chores are due to be done": "Obowiązki bliskie terminu realizacji: {string0} | Obowiązki bliskie terminu realizacji: {string0} | Obowiązki bliskie terminu realizacji: {string0} | Obowiązki bliskie terminu realizacji: {string0}", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "Obowiązki niewykonane w terminie: {string0} | Obowiązki niewykonane w terminie: {string0} | Obowiązki niewykonane w terminie: {string0} | Obowiązki niewykonane w terminie: {string0}", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "Baterie bliskie terminu ładowania: {string0} | Baterie bliskie terminu ładowania: {string0} | Baterie bliskie terminu ładowania: {string0} | Baterie bliskie terminu ładowania: {string0}", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "Baterie po terminie ładowania: {string0} | Baterie po terminie ładowania: {string0} | Baterie po terminie ładowania: {string0} | Baterie po terminie ładowania: {string0}", + "{count} Unit | {count} Units": "{count} Jednostka | {count} Jednostki | {count} Jednostek | {count} Jednostek", + "{count} chore is due to be done | {count} chores are due to be done": "Obowiązki bliskie terminu realizacji: {count} | Obowiązki bliskie terminu realizacji: {count} | Obowiązki bliskie terminu realizacji: {count} | Obowiązki bliskie terminu realizacji: {count}", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "Obowiązki niewykonane w terminie: {count} | Obowiązki niewykonane w terminie: {count} | Obowiązki niewykonane w terminie: {count} | Obowiązki niewykonane w terminie: {count}", + "{count} battery is due to be charged | {count} batteries are due to be charged": "Baterie bliskie terminu ładowania: {count} | Baterie bliskie terminu ładowania: {count} | Baterie bliskie terminu ładowania: {count} | Baterie bliskie terminu ładowania: {count}", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "Baterie po terminie ładowania: {count} | Baterie po terminie ładowania: {count} | Baterie po terminie ładowania: {count} | Baterie po terminie ładowania: {count}", "in singular form": "w liczbie pojedynczej", "Quantity unit": "Jednostka ilości", "Only check if any amount is in stock": "Zaznacz tylko, jeśli cokolwiek jest w zapasach", @@ -204,8 +204,8 @@ "Category": "Kategoria", "Edit task": "Edytuj zadanie", "Are you sure to delete task \"{string0}\"?": "Czy jesteś pewien, że chcesz usunąć zadanie \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "Zadania po terminie realizacji: {string0} | Zadania po terminie realizacji: {string0} | Zadania po terminie realizacji: {string0} | Zadania po terminie realizacji: {string0}", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "Zadania bliskie terminu realizacji: {string0} | Zadania bliskie terminu realizacji: {string0} | Zadania bliskie terminu realizacji: {string0} | Zadania bliskie terminu realizacji: {string0}", + "{count} task is due to be done | {count} tasks are due to be done": "Zadania po terminie realizacji: {count} | Zadania po terminie realizacji: {count} | Zadania po terminie realizacji: {count} | Zadania po terminie realizacji: {count}", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "Zadania bliskie terminu realizacji: {count} | Zadania bliskie terminu realizacji: {count} | Zadania bliskie terminu realizacji: {count} | Zadania bliskie terminu realizacji: {count}", "Edit task category": "Edytuj kategorię zadania", "Create task category": "Dodaj kategorię zadania", "Product groups": "Grupy produktów", @@ -351,7 +351,7 @@ "Plural count": "Ilość odmian liczby mnogiej", "Plural rule": "Reguła odmiany", "in plural form": "w liczbie mnogiej", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Za mało zapasów, brakuje {string0} składnika | Za mało zapasów, brakuje {string0} składników | Za mało zapasów, brakuje {string0} składników | Za mało zapasów, brakuje {string0} składników", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Za mało zapasów, brakuje {count} składnika | Za mało zapasów, brakuje {count} składników | Za mało zapasów, brakuje {count} składników | Za mało zapasów, brakuje {count} składników", "Not enough in stock, but already on the shopping list": "Za mało zapasów, ale już na liście zakupów", "Not enough in stock": "Za mało zapasów", "Expiring soon days": "Dni \"blisko terminu przydatności\"", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "{string2} - {string1} - spożyj", "Meal plan": "Plan posiłków", "Add recipe on {string0}": "Dodaj przepis {string0}", - "{string0} serving | {string0} servings": "{string0} porcja | {string0} porcje | {string0} porcji | {string0} porcji", + "{count} serving | {count} servings": "{count} porcja | {count} porcje | {count} porcji | {count} porcji", "Week costs": "Koszt tygodniowy", "Configuration": "Konfiguracja", "A predefined list of values, one per line": "Lista predefiniowanych wartości, jedna na linię", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Oznacza to, że następne wykonanie tego obowiązku zostanie przypisane do następnej osoby w porządku alfabetycznym", "Assign to": "Przypisz do", "This assignment type requires that at least one is assigned": "Ten typ przypisania wymaga przynajmniej jednej przypisanej osoby", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} obowiązki przypisane do mnie | {string0} obowiązków przypisanych do mnie | {string0} obowiązek przypisany do mnie | {string0} obowiązków przypisanych do mnie", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} obowiązki przypisane do mnie | {count} obowiązków przypisanych do mnie | {count} obowiązek przypisany do mnie | {count} obowiązków przypisanych do mnie", "Assigned to me": "Przypisane do mnie", "assigned to {string0}": "przypisane do {string0}", "Assignment": "Przypisanie", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | | | ", + "{count} product is due | {count} products are due": " | | | ", "Due date": "", "Never overdue": "Nigdy się nie przeterminuje", - "{string0} product is expired | {string0} products are expired": " | | | ", + "{count} product is expired | {count} products are expired": " | | | ", "Expired": "Przeterminowany", "Due soon days": "", "Add overdue/expired products": "", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "Hebrajski (Izrael)", "Tamil": "Tamilski", "Finnish": "Fiński", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/pt_BR.json b/locale/pt_BR.json index f43b4be7..a22ead91 100644 --- a/locale/pt_BR.json +++ b/locale/pt_BR.json @@ -1,12 +1,12 @@ { "Stock overview": "Resumo do estoque", - "{string0} product expires | {string0} products expiring": "{string0} produtos vencendo | {string0} produtos vencendo", - "within the next day | within the next {string0} days": "produtos vencendo nos próximos dias | nos próximos {string0} dias", - "{string0} product is already expired | {string0} products are already expired": "{string0} product is already expired | {string0} produtos já vencidos", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produtos estão abaixo da quantidade minima de estoque definida | {string0} produtos estão abaixo da quantidade minima de estoque", + "{count} product expires | {count} products expiring": "{count} produtos vencendo | {count} produtos vencendo", + "within the next day | within the next {count} days": "produtos vencendo nos próximos dias | nos próximos {count} dias", + "{count} product is already expired | {count} products are already expired": "{count} product is already expired | {count} produtos já vencidos", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produtos estão abaixo da quantidade minima de estoque definida | {count} produtos estão abaixo da quantidade minima de estoque", "Product": "Produto", - "{string0} Product | {string0} Products": "{string0} Produtos | {string0} Products", + "{count} Product | {count} Products": "{count} Produtos | {count} Products", "Amount": "Quantidade", "Logout": "Sair", "Chores overview": "Visão Geral das Tarefas Domésticas", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Requerimentos completos", "Put missing products on shopping list": "Colocar a quantidade faltante na lista de compras", "Enough in stock": "Suficiente em estoque", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Estoque insuficiente, {string0} ingrediente faltando mas já está na lista de compras | Estoque insuficiente, {string0} ingredientes faltando mas já estão na lista de compras", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Estoque insuficiente, {count} ingrediente faltando mas já está na lista de compras | Estoque insuficiente, {count} ingredientes faltando mas já estão na lista de compras", "Expand to fullscreen": "Expandir para tela inteira", "Ingredients": "Ingredientes", "Preparation": "Preparação", @@ -177,11 +177,11 @@ "No price history available": "Não há histórico de preço disponível", "Price": "Preço", "Unit": "Unidade", - "{string0} Unit | {string0} Units": "{string0} Unidade | {string0} Unidades", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} tarefa doméstica está para vencer | {string0} tarefas domésticas estão para vencer", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} tarefa doméstica está vencida | {string0} tarefas domésticas estão vencidas", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} bateria deve ser carregada | {string0} baterias devem ser carregadas", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} bateria está pendente de ser carregada | {string0} baterias estão pendentes de serem carregadas", + "{count} Unit | {count} Units": "{count} Unidade | {count} Unidades", + "{count} chore is due to be done | {count} chores are due to be done": "{count} tarefa doméstica está para vencer | {count} tarefas domésticas estão para vencer", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} tarefa doméstica está vencida | {count} tarefas domésticas estão vencidas", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} bateria deve ser carregada | {count} baterias devem ser carregadas", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} bateria está pendente de ser carregada | {count} baterias estão pendentes de serem carregadas", "in singular form": "no singular", "Quantity unit": "Unidade de medida", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Categoria", "Edit task": "Editar tarefa", "Are you sure to delete task \"{string0}\"?": "Tem certeza que quer apagar a tarefa \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} tarefa a ser feita | {string0} tarefas a serem feitas", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} tarefa pendente de ser feita | {string0} tarefas pendentes de serem feitas", + "{count} task is due to be done | {count} tasks are due to be done": "{count} tarefa a ser feita | {count} tarefas a serem feitas", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} tarefa pendente de ser feita | {count} tarefas pendentes de serem feitas", "Edit task category": "Editar categoria de tarefas", "Create task category": "Criar categoria de tarefas", "Product groups": "Grupos de produtos", @@ -351,7 +351,7 @@ "Plural count": "Contagem de plural", "Plural rule": "Regra de plural", "in plural form": "no plural", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Não há estoque suficiente, {string0} falta ingrediente | Não há estoque suficiente, {string0} faltam ingredientes", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Não há estoque suficiente, {count} falta ingrediente | Não há estoque suficiente, {count} faltam ingredientes", "Not enough in stock, but already on the shopping list": "Estoque insuficiente, mas já na lista de compras", "Not enough in stock": "Não há estoque suficiente", "Expiring soon days": "Expirando em breve", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Consumir {string1} de {string2}", "Meal plan": "Plano de refeições", "Add recipe on {string0}": "Adicionar receita a {string0}", - "{string0} serving | {string0} servings": "{string0}porção | {string0}porções", + "{count} serving | {count} servings": "{count}porção | {count}porções", "Week costs": "Custos na semana", "Configuration": "Configuração", "A predefined list of values, one per line": "Uma lista predefinida de valores, uma por linha", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Isso significa que a próxima execução desta tarefa será atribuída para o próximo de acordo com a ordem alfabética", "Assign to": "Atribuir para", "This assignment type requires that at least one is assigned": "Esse tipo de atribuição requer que ao menos um esteja atribuído", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} tarefa doméstica está atribuída a mim | {string0} tarefas domésticas estão atribuídas a mim", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} tarefa doméstica está atribuída a mim | {count} tarefas domésticas estão atribuídas a mim", "Assigned to me": "Atribuir para mim", "assigned to {string0}": "atribuído para {string0}", "Assignment": "Tarefa", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -682,7 +682,7 @@ "timeago_locale": "pt-br", "timeago_nan": "NaN anos atrás", "moment_locale": "pt-br", - "datatables_localization": "{    \"sEmptyTable\": \"Nenhum registro encontrado\",    \"sInfo\": \"Mostrando de _START_ até _END_ de _TOTAL_ registros\",    \"sInfoEmpty\": \"Mostrando 0 até 0 de 0 registros\",    \"sInfoFiltered\": \"(Filtrados de _MAX_ registros)\",    \"sInfoPostFix\": \"\",    \"sInfoThousands\": \".\",    \"sLengthMenu\": \"_MENU_ resultados por página\",    \"sLoadingRecords\": \"Carregando...\",    \"sProcessing\": \"Processando...\",    \"sZeroRecords\": \"Nenhum registro encontrado\",    \"sSearch\": \"Pesquisar\",    \"oPaginate\": {        \"sNext\": \"Próximo\",        \"sPrevious\": \"Anterior\",        \"sFirst\": \"Primeiro\",        \"sLast\": \"Último\"    },    \"oAria\": {        \"sSortAscending\": \": Ordenar colunas de forma ascendente\",        \"sSortDescending\": \": Ordenar colunas de forma descendente\"    },    \"select\": {        \"rows\": {            \"_\": \"Selecionado {num0} linhas\",            \"0\": \"Nenhuma linha selecionada\",            \"1\": \"Selecionado 1 linha\"        }    }}", + "datatables_localization": "{    \"sEmptyTable\": \"Nenhum registro encontrado\",    \"sInfo\": \"Mostrando de _START_ até _END_ de _TOTAL_ registros\",    \"sInfoEmpty\": \"Mostrando 0 até 0 de 0 registros\",    \"sInfoFiltered\": \"(Filtrados de _MAX_ registros)\",    \"sInfoPostFix\": \"\",    \"sInfoThousands\": \".\",    \"sLengthMenu\": \"_MENU_ resultados por página\",    \"sLoadingRecords\": \"Carregando...\",    \"sProcessing\": \"Processando...\",    \"sZeroRecords\": \"Nenhum registro encontrado\",    \"sSearch\": \"Pesquisar\",    \"oPaginate\": {        \"sNext\": \"Próximo\",        \"sPrevious\": \"Anterior\",        \"sFirst\": \"Primeiro\",        \"sLast\": \"Último\"    },    \"oAria\": {        \"sSortAscending\": \": Ordenar colunas de forma ascendente\",        \"sSortDescending\": \": Ordenar colunas de forma descendente\"    },    \"select\": {        \"rows\": {            \"_\": \"Selecionado {string0} linhas\",            \"0\": \"Nenhuma linha selecionada\",            \"1\": \"Selecionado 1 linha\"        }    }}", "summernote_locale": "pt-BR", "fullcalendar_locale": "pt-br", "bootstrap-select_locale": "pt_BR", @@ -830,22 +830,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/pt_PT.json b/locale/pt_PT.json index a74bb7e3..a7bf9086 100644 --- a/locale/pt_PT.json +++ b/locale/pt_PT.json @@ -1,11 +1,11 @@ { "Stock overview": "Visão geral do Stock", - "{string0} product expires | {string0} products expiring": "{string0} produto expira | {string0} produtos a expirar", - "within the next day | within the next {string0} days": "no próximo dia | nos próximos {string0} dias", - "{string0} product is already expired | {string0} products are already expired": "{string0} produto já expirou | {string0} produtos já expiraram", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produto está abaixo da quantidade min. de stock | {string0} produtos estão abaixo da quantidade min. de stock", + "{count} product expires | {count} products expiring": "{count} produto expira | {count} produtos a expirar", + "within the next day | within the next {count} days": "no próximo dia | nos próximos {count} dias", + "{count} product is already expired | {count} products are already expired": "{count} produto já expirou | {count} produtos já expiraram", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produto está abaixo da quantidade min. de stock | {count} produtos estão abaixo da quantidade min. de stock", "Product": "Produto", - "{string0} Product | {string0} Products": "{string0} Produto | {string0} Produtos", + "{count} Product | {count} Products": "{count} Produto | {count} Produtos", "Amount": "Quantidade", "Next best before date": "Próxima data de validade a expirar", "Logout": "Logout", @@ -153,7 +153,7 @@ "Requirements fulfilled": "Requisitos cumpridos", "Put missing products on shopping list": "Colocar produtos em falta na lista de compras", "Enough in stock": "Suficiente em stock", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Não existe suficiente em stock, {string0} ingrediente em falta mas já na lista de compras | Não existe suficiente em stock, {string0} ingredientes em falta mas já na lista de compras", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Não existe suficiente em stock, {count} ingrediente em falta mas já na lista de compras | Não existe suficiente em stock, {count} ingredientes em falta mas já na lista de compras", "Expand to fullscreen": "Expandir para ecrã inteiro", "Ingredients": "Ingredientes", "Preparation": "Preparação", @@ -189,11 +189,11 @@ "in {string0} per purchase quantity unit": "em {string0} por unidade de medida de compra", "The price cannot be lower than {string0}": "O preço não pode ser inferior a {string0}", "Unit": "Unidade", - "{string0} Unit | {string0} Units": "{string0} Unidade | {string0} Unidades", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} tarefa está agendada | {string0} tarefas estão agendadas ", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} tarefa já ultrapassou o prazo | {string0} tarefas já ultrapassaram o prazo", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} pilha precisa ser carregada | {string0} pilhas precisam ser carregadas", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} pilha ultrapassou o prazo para ser carregada | {string0} pilhas ultrapassaram o prazo para serem carregadas", + "{count} Unit | {count} Units": "{count} Unidade | {count} Unidades", + "{count} chore is due to be done | {count} chores are due to be done": "{count} tarefa está agendada | {count} tarefas estão agendadas ", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} tarefa já ultrapassou o prazo | {count} tarefas já ultrapassaram o prazo", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} pilha precisa ser carregada | {count} pilhas precisam ser carregadas", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} pilha ultrapassou o prazo para ser carregada | {count} pilhas ultrapassaram o prazo para serem carregadas", "in singular form": "no singular", "Never expires": "Nunca expira", "This cannot be lower than {string0}": "Não pode ser mais baixo do que {string0}", @@ -219,8 +219,8 @@ "Category": "Categoria", "Edit task": "Editar tarefa", "Are you sure to delete task \"{string0}\"?": "Pretende eliminar a tarefa \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} tarefa agendada | {string0} tarefas agendadas", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} tarefas pendentes de execução | {string0}taferas pendentes de realização", + "{count} task is due to be done | {count} tasks are due to be done": "{count} tarefa agendada | {count} tarefas agendadas", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} tarefas pendentes de execução | {count}taferas pendentes de realização", "Edit task category": "Editar categoria da tarefa", "Create task category": "Criar categoria da tarefa", "Product groups": "Grupos de produto", @@ -387,7 +387,7 @@ "Plural count": "Contagem plural", "Plural rule": "Regra plural", "in plural form": "no plural", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Sem stock suficiente, {string0} ingredientes faltando | Sem stock suficiente, {string0} ingredientes faltando", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Sem stock suficiente, {count} ingredientes faltando | Sem stock suficiente, {count} ingredientes faltando", "The amount cannot be lower than {string1} or equal {string2}": "A quantidade não pode ser menor que {string1} ou igual a {string2}", "Not enough in stock, but already on the shopping list": "Stock insuficiente, mas já na lista de compras", "Not enough in stock": "Não há stock suficiente", @@ -402,7 +402,7 @@ "Consume {string1} of {string2}": "Consumir {string1} de {string2}", "Meal plan": "Plano de refeições", "Add recipe on {string0}": "Adicionar receita em {string0}", - "{string0} serving | {string0} servings": "{string0} porções | {string0} porções", + "{count} serving | {count} servings": "{count} porções | {count} porções", "Week costs": "Custo por semana", "Configuration": "Configuração", "A predefined list of values, one per line": "Uma lista pré-definida de valores, um por linha", @@ -457,7 +457,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Isto significa que a próxima execução desta tarefa será atribuída ao seguinte por ordem alfabética", "Assign to": "Atribuí a", "This assignment type requires that at least one is assigned": "", - "{string0} chore is assigned to me | {string0} chores are assigned to me": " | ", + "{count} chore is assigned to me | {count} chores are assigned to me": " | ", "Assigned to me": "Atribuído a mim", "assigned to {string0}": "atribuído a {string0}", "Assignment": "", @@ -743,26 +743,21 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, "This activates night mode between {start} and {end} the same day": { "": "" - } + }, + "{string0} total value": "" } \ No newline at end of file diff --git a/locale/ru.json b/locale/ru.json index b409dad1..5a273309 100644 --- a/locale/ru.json +++ b/locale/ru.json @@ -1,12 +1,12 @@ { "Stock overview": "Обзор запасов", - "{string0} product expires | {string0} products expiring": "{string0} продукт испортится | {string0} продукта испортятся | {string0} продуктов испортятся | {string0} продуктов испортятся", - "within the next day | within the next {string0} days": "в течение следующего дня | в течение {string0} дней | в течение {string0} дней | в течение {string0} дней", - "{string0} product is already expired | {string0} products are already expired": "{string0} продукт испортился | {string0} продукта испортилось | {string0} продуктов испортилось | {string0} продуктов испортилось", - "{string0} product is overdue | {string0} products are overdue": " | | | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} продукт меньше мин. заданного кол-ва | {string0} продукта меньше мин. заданного кол-ва | {string0} продуктов меньше мин. заданного кол-ва | {string0} продуктов меньше мин. заданного кол-ва", + "{count} product expires | {count} products expiring": "{count} продукт испортится | {count} продукта испортятся | {count} продуктов испортятся | {count} продуктов испортятся", + "within the next day | within the next {count} days": "в течение следующего дня | в течение {count} дней | в течение {count} дней | в течение {count} дней", + "{count} product is already expired | {count} products are already expired": "{count} продукт испортился | {count} продукта испортилось | {count} продуктов испортилось | {count} продуктов испортилось", + "{count} product is overdue | {count} products are overdue": " | | | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} продукт меньше мин. заданного кол-ва | {count} продукта меньше мин. заданного кол-ва | {count} продуктов меньше мин. заданного кол-ва | {count} продуктов меньше мин. заданного кол-ва", "Product": "Продукт", - "{string0} Product | {string0} Products": "{string0} продукт | {string0} продукта | {string0} продуктов | {string0} продуктов", + "{count} Product | {count} Products": "{count} продукт | {count} продукта | {count} продуктов | {count} продуктов", "Amount": "Количество", "Logout": "Выйти", "Chores overview": "Обзор работ", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Требования выполнены", "Put missing products on shopping list": "Добавить отсутствующие продукты в список покупок", "Enough in stock": "Достаточно в запасе", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Запасов не достаточно, {string0} ингредиент отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {string0} ингредиента отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {string0} ингредиентов отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {string0} ингредиентов отсутствуют, но уже добавлены в список покупок", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Запасов не достаточно, {count} ингредиент отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {count} ингредиента отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {count} ингредиентов отсутствуют, но уже добавлены в список покупок | Запасов не достаточно, {count} ингредиентов отсутствуют, но уже добавлены в список покупок", "Expand to fullscreen": "Раскрыть на весь экран", "Ingredients": "Ингредиенты", "Preparation": "Приготовление", @@ -177,11 +177,11 @@ "No price history available": "Нет истории цен для показа", "Price": "Цена", "Unit": "Ед.изм.", - "{string0} Unit | {string0} Units": "{string0} Единица Измерения | {string0} Единицы Измерения | {string0} Единиц Измерения | {string0} Единиц Измерения", - "{string0} chore is due to be done | {string0} chores are due to be done": "Подходит срок {string0} работы по дому | Подходит срок {string0} работ по дому | Подходит срок {string0} работ по дому | Подходит срок {string0} работ по дому", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "Прошёл срок {string0} работы по дому | Прошёл срок {string0} работ по дому | Прошёл срок {string0} работ по дому | Прошёл срок {string0} работ по дому", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "Подходит срок заряда {string0} батареи | Подходит срок заряда {string0} батарей | Подходит срок заряда {string0} батарей | Подходит срок заряда {string0} батарей ", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "Вышел срок заряда у {string0} батареи | Вышел срок заряда у {string0} батарей | Вышел срок заряда у {string0} батарей | Вышел срок заряда у {string0} батарей", + "{count} Unit | {count} Units": "{count} Единица Измерения | {count} Единицы Измерения | {count} Единиц Измерения | {count} Единиц Измерения", + "{count} chore is due to be done | {count} chores are due to be done": "Подходит срок {count} работы по дому | Подходит срок {count} работ по дому | Подходит срок {count} работ по дому | Подходит срок {count} работ по дому", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "Прошёл срок {count} работы по дому | Прошёл срок {count} работ по дому | Прошёл срок {count} работ по дому | Прошёл срок {count} работ по дому", + "{count} battery is due to be charged | {count} batteries are due to be charged": "Подходит срок заряда {count} батареи | Подходит срок заряда {count} батарей | Подходит срок заряда {count} батарей | Подходит срок заряда {count} батарей ", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "Вышел срок заряда у {count} батареи | Вышел срок заряда у {count} батарей | Вышел срок заряда у {count} батарей | Вышел срок заряда у {count} батарей", "in singular form": "в единственном числе", "Quantity unit": "Единица измерения", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Категория", "Edit task": "Изменить задачу", "Are you sure to delete task \"{string0}\"?": "Вы уверены, что нужно удалить задачу \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "Подходит срок {string0} задачи | Подходит срок {string0} задач | Подходит срок {string0} задач | Подходит срок {string0} задач", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "Вышел срок {string0} задачи | Вышел срок {string0} задач | Вышел срок {string0} задач | Вышел срок {string0} задач", + "{count} task is due to be done | {count} tasks are due to be done": "Подходит срок {count} задачи | Подходит срок {count} задач | Подходит срок {count} задач | Подходит срок {count} задач", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "Вышел срок {count} задачи | Вышел срок {count} задач | Вышел срок {count} задач | Вышел срок {count} задач", "Edit task category": "Изменить категорию задач", "Create task category": "Создать категорию задач", "Product groups": "Группы продуктов", @@ -351,7 +351,7 @@ "Plural count": "Число форм множественного числа", "Plural rule": "Правило формирование множественного числа", "in plural form": "в множественном числе", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Недостаточно в запасе, {string0} ингредиент отсутствует | Недостаточно в запасе, {string0} ингредиента отсутствуют | Недостаточно в запасе, {string0} ингредиентов отсутствуют | Недостаточно в запасе, {string0} ингредиентов отсутствуют", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Недостаточно в запасе, {count} ингредиент отсутствует | Недостаточно в запасе, {count} ингредиента отсутствуют | Недостаточно в запасе, {count} ингредиентов отсутствуют | Недостаточно в запасе, {count} ингредиентов отсутствуют", "Not enough in stock, but already on the shopping list": "Не достаточно в запасе, но уже добавлено в список покупок", "Not enough in stock": "Не достаточно в запасе", "Expiring soon days": "Дней до окончания срока годности", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Употребить {string1} {string2}", "Meal plan": "План питания", "Add recipe on {string0}": "Добавить рецепт в {string0}", - "{string0} serving | {string0} servings": "{string0} порция | {string0} порции | {string0} порций | {string0} порций", + "{count} serving | {count} servings": "{count} порция | {count} порции | {count} порций | {count} порций", "Week costs": "Затраты на неделю", "Configuration": "Конфигурация", "A predefined list of values, one per line": "Предопределённый список значений, по одному на строку", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Это означает, что следующее исполнение этой работы будет назначено в алфавитном порядке", "Assign to": "Назначить", "This assignment type requires that at least one is assigned": "Для этого типа назначения необходим как минимум один назначенный", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} работа назначена мне | {string0} работы назначены мне | {string0}работ назначены мне | {string0} работ назначены мне", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} работа назначена мне | {count} работы назначены мне | {count}работ назначены мне | {count} работ назначены мне", "Assigned to me": "Назначено мне", "assigned to {string0}": "назначено {string0}", "Assignment": "", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | | | ", + "{count} product is due | {count} products are due": " | | | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | | | ", + "{count} product is expired | {count} products are expired": " | | | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -825,22 +825,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/sk_SK.json b/locale/sk_SK.json index 2ee0e6fd..3ac7dca0 100644 --- a/locale/sk_SK.json +++ b/locale/sk_SK.json @@ -1,12 +1,12 @@ { "Stock overview": "Prehľad zásob", - "{string0} product expires | {string0} products expiring": "{string0} výrobok expiruje | {string0} výrobky expirujú | {string0} výrobkov expiruje | {string0} výrobkov expiruje", - "within the next day | within the next {string0} days": "počas nasledujúceho dňa | počas nasledujúcich {num0} dní | počas nasledujúcich {string0} dní | počas nasledujúcich {string0} dní", - "{string0} product is already expired | {string0} products are already expired": "{string0} výrobok je po záruke | {string0} výrobky sú po záruke | {string0} výrobkov je po záruke | {string0} výrobkov je po záruke", - "{string0} product is overdue | {string0} products are overdue": " | | | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} výrobok má stav pod definovanou minimálnou zásobou | {string0} výrobky majú stav pod definovanou minimálnou zásobou | {string0} výrobkov má stav pod definovanou minimálnou zásobou | {string0} výrobkov má stav pod definovanou minimálnou zásobou", + "{count} product expires | {count} products expiring": "{count} výrobok expiruje | {count} výrobky expirujú | {count} výrobkov expiruje | {count} výrobkov expiruje", + "within the next day | within the next {count} days": "počas nasledujúceho dňa | počas nasledujúcich {count} dní | počas nasledujúcich {count} dní | počas nasledujúcich {count} dní", + "{count} product is already expired | {count} products are already expired": "{count} výrobok je po záruke | {count} výrobky sú po záruke | {count} výrobkov je po záruke | {count} výrobkov je po záruke", + "{count} product is overdue | {count} products are overdue": " | | | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} výrobok má stav pod definovanou minimálnou zásobou | {count} výrobky majú stav pod definovanou minimálnou zásobou | {count} výrobkov má stav pod definovanou minimálnou zásobou | {count} výrobkov má stav pod definovanou minimálnou zásobou", "Product": "Výrobok", - "{string0} Product | {string0} Products": "{string0} výrobok | {string0} výrobky | {string0} výrobkov | {string0} výrobkov", + "{count} Product | {count} Products": "{count} výrobok | {count} výrobky | {count} výrobkov | {count} výrobkov", "Amount": "Množstvo", "Logout": "Odhlásiť", "Chores overview": "Prehľad povinností", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Požiadavky splnené", "Put missing products on shopping list": "Pridať chýbajúce výrobky do nákupného zoznamu", "Enough in stock": "Dostatočná zásoba", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Nedostatočná zásoba, chýba {string0} ingrediencia, ktorá už ale je v nákupnom zozname | Nedostatočná zásoba, chýbajú {string0} ingrediencie, ktoré už ale sú v nákupnom zozname | Nedostatočná zásoba, chýba {string0} ingrediencií, ktoré už ale sú v nákupnom zozname | Nedostatočná zásoba, chýba {string0} ingrediencií, ktoré už ale sú v nákupnom zozname", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Nedostatočná zásoba, chýba {count} ingrediencia, ktorá už ale je v nákupnom zozname | Nedostatočná zásoba, chýbajú {count} ingrediencie, ktoré už ale sú v nákupnom zozname | Nedostatočná zásoba, chýba {count} ingrediencií, ktoré už ale sú v nákupnom zozname | Nedostatočná zásoba, chýba {count} ingrediencií, ktoré už ale sú v nákupnom zozname", "Expand to fullscreen": "Rozšíriť na celú obrazovku", "Ingredients": "Ingrediencie", "Preparation": "Príprava", @@ -177,11 +177,11 @@ "No price history available": "História cien nie je dostupná", "Price": "Cena", "Unit": "Jednotka", - "{string0} Unit | {string0} Units": "{string0} jednotka | {string0} jednotky | {string0} jednotiek | {string0} jednotiek", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} povinnosť má byť splnená | {string0} povinnosti majú byť splnené | {string0} povinností má byť splnených | {string0} povinností má byť splnených", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} povinnosť už mala byť splnená | {string0} povinnosti už mali byť splnené | {string0} povinností už malo byť splnených | {string0} povinností už malo byť splnených", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batéria by mala byť nabitá | {string0} batérie by mali byť nabité | {string0} batérií by malo byť nabitých | {string0} batérií by malo byť nabitých", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batéria už mala byť nabitá | {string0} batérie už mali byť nabité | {string0} batérií už malo byť nabitých | {string0} batérií už malo byť nabitých", + "{count} Unit | {count} Units": "{count} jednotka | {count} jednotky | {count} jednotiek | {count} jednotiek", + "{count} chore is due to be done | {count} chores are due to be done": "{count} povinnosť má byť splnená | {count} povinnosti majú byť splnené | {count} povinností má byť splnených | {count} povinností má byť splnených", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} povinnosť už mala byť splnená | {count} povinnosti už mali byť splnené | {count} povinností už malo byť splnených | {count} povinností už malo byť splnených", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batéria by mala byť nabitá | {count} batérie by mali byť nabité | {count} batérií by malo byť nabitých | {count} batérií by malo byť nabitých", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batéria už mala byť nabitá | {count} batérie už mali byť nabité | {count} batérií už malo byť nabitých | {count} batérií už malo byť nabitých", "in singular form": "v jednotnom čísle", "Quantity unit": "Merná jednotka", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Kategória", "Edit task": "Upraviť úlohu", "Are you sure to delete task \"{string0}\"?": "Naozaj si prajete odstrániť úlohu \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} úloha má byť splnená | {string0} úlohy majú byť splnené | {string0} úloh má byť splnených | {string0} úloh má byť splnených", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} úloha už mala byť splnená | {string0} úlohy už mali byť splnené | {string0} úloh už malo byť splnených | {string0} úloh už malo byť splnených", + "{count} task is due to be done | {count} tasks are due to be done": "{count} úloha má byť splnená | {count} úlohy majú byť splnené | {count} úloh má byť splnených | {count} úloh má byť splnených", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} úloha už mala byť splnená | {count} úlohy už mali byť splnené | {count} úloh už malo byť splnených | {count} úloh už malo byť splnených", "Edit task category": "Upraviť kategóriu úlohy", "Create task category": "Vytvoriť kategóriu úloh", "Product groups": "Skupiny výrobkov", @@ -351,7 +351,7 @@ "Plural count": "Počet plurálov", "Plural rule": "Pravidlá pre plurál", "in plural form": "v množnom čísle", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Nedostatočná zásoba, chýba {string0} ingrediencia | Nedostatočná zásoba, chýbajú {string0} ingrediencie | Nedostatočná zásoba, chýba {string0} ingrediencií | Nedostatočná zásoba, chýba {string0} ingrediencií", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Nedostatočná zásoba, chýba {count} ingrediencia | Nedostatočná zásoba, chýbajú {count} ingrediencie | Nedostatočná zásoba, chýba {count} ingrediencií | Nedostatočná zásoba, chýba {count} ingrediencií", "Not enough in stock, but already on the shopping list": "Nedostatočná zásoba, ale už je v nákupnom zozname", "Not enough in stock": "Nedostatočná zásoba", "Expiring soon days": "Dni blížiaceho sa uplynutia dátumu spotreby", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Spotrebovať {string1} {string2}", "Meal plan": "Jedálniček", "Add recipe on {string0}": "Pridať recept na {string0}", - "{string0} serving | {string0} servings": "{string0} porcia | {string0} porcie | {string0} porcií | {string0} porcií", + "{count} serving | {count} servings": "{count} porcia | {count} porcie | {count} porcií | {count} porcií", "Week costs": "Týždenné náklady", "Configuration": "Nastavenia", "A predefined list of values, one per line": "Preddefinovaný zoznam hodnôt, jedna na riadok", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Toto znamená, že ďalšie vykonávanie tejto povinnosti bude priradené osobám podľa abecedného poradia", "Assign to": "Priradiť používateľovi", "This assignment type requires that at least one is assigned": "Tejto typ priradenia vyžaduje priradenie aspoň jednej osoby", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} povinnosť je priradená mne | {string0} povinnosti sú priradené mne | {string0} povinností je priradených mne | {string0} povinností je priradených mne", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} povinnosť je priradená mne | {count} povinnosti sú priradené mne | {count} povinností je priradených mne | {count} povinností je priradených mne", "Assigned to me": "Priradené mne", "assigned to {string0}": "priradené používateľovi {string0}", "Assignment": "Priradenie", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | | | ", + "{count} product is due | {count} products are due": " | | | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | | | ", + "{count} product is expired | {count} products are expired": " | | | ", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -860,22 +860,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/sv_SE.json b/locale/sv_SE.json index 12401445..8788fe53 100644 --- a/locale/sv_SE.json +++ b/locale/sv_SE.json @@ -1,12 +1,12 @@ { "Stock overview": "Lageröversikt", - "{string0} product expires | {string0} products expiring": "{string0} produkt går ut | {string0} produkter går ut", - "within the next day | within the next {string0} days": "inom en dag | inom {string0} dagar", - "{string0} product is already expired | {string0} products are already expired": "{string0} produkt har redan gått ut | {string0} produkter har redan gått ut", - "{string0} product is overdue | {string0} products are overdue": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} produkt är under beställningspunkten | {string0} produkter är under den valda gränsen för minsta lagerantal", + "{count} product expires | {count} products expiring": "{count} produkt går ut | {count} produkter går ut", + "within the next day | within the next {count} days": "inom en dag | inom {count} dagar", + "{count} product is already expired | {count} products are already expired": "{count} produkt har redan gått ut | {count} produkter har redan gått ut", + "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} produkt är under beställningspunkten | {count} produkter är under den valda gränsen för minsta lagerantal", "Product": "Produkt", - "{string0} Product | {string0} Products": "{string0} Produkt | {string0} produkter", + "{count} Product | {count} Products": "{count} Produkt | {count} produkter", "Amount": "Mängd", "Logout": "Logga ut", "Chores overview": "Sysslor, översikt", @@ -144,7 +144,7 @@ "Requirements fulfilled": "Krav uppfyllda", "Put missing products on shopping list": "Lägg saknade produkter i inköpslista", "Enough in stock": "Tillräckligt i lager", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "Inte tillräckligt mycket i lager, {string0} ingrediens saknas men finns redan på inköpslistan | Inte tillräckligt mycket i lager, {string0} ingredienser saknas men finns redan i inköpslistan", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "Inte tillräckligt mycket i lager, {count} ingrediens saknas men finns redan på inköpslistan | Inte tillräckligt mycket i lager, {count} ingredienser saknas men finns redan i inköpslistan", "Expand to fullscreen": "Expandera till fullskärm", "Ingredients": "Ingredienser", "Preparation": "Förberedelse", @@ -177,11 +177,11 @@ "No price history available": "Ingen prishistorik tillgänglig", "Price": "Pris", "Unit": "Enhet", - "{string0} Unit | {string0} Units": "{string0} Enhet | {string0} Enheter", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} syssla att utföra | {string0} sysslor att utföra", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} syssla är försenad | {string0} sysslor är försenade", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} batteri behöver laddas | {string0}batterier behöver laddas", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} batteri borde redan ha laddats | {string0} batterier borde redan ha laddats", + "{count} Unit | {count} Units": "{count} Enhet | {count} Enheter", + "{count} chore is due to be done | {count} chores are due to be done": "{count} syssla att utföra | {count} sysslor att utföra", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} syssla är försenad | {count} sysslor är försenade", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} batteri behöver laddas | {count}batterier behöver laddas", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} batteri borde redan ha laddats | {count} batterier borde redan ha laddats", "in singular form": "i singular form", "Quantity unit": "Mängdenhet", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "Kategori", "Edit task": "Redigera uppgiften", "Are you sure to delete task \"{string0}\"?": "Vill du radera uppgift \"{string0}\"?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} uppgift skall utföras | {string0} uppgifter att utföra", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} uppgift är försenad | {string0} uppgifter är försenade", + "{count} task is due to be done | {count} tasks are due to be done": "{count} uppgift skall utföras | {count} uppgifter att utföra", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} uppgift är försenad | {count} uppgifter är försenade", "Edit task category": "Redigera uppgiftskategori", "Create task category": "Skapa uppgiftskategori", "Product groups": "Produktgrupper", @@ -351,7 +351,7 @@ "Plural count": "Flertal", "Plural rule": "Pluralregel", "in plural form": "i plural form", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "Inte tillräckligt i lager, {string0} ingrediens saknas | Inte tillräckligt i lager, {string0} ingredienser saknas", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "Inte tillräckligt i lager, {count} ingrediens saknas | Inte tillräckligt i lager, {count} ingredienser saknas", "Not enough in stock, but already on the shopping list": "Inte tillräckligt i lager, men finns redan på inköpslistan", "Not enough in stock": "Inte tillräckligt i lager", "Expiring soon days": "Utgår snart dagar", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "Konsumera {string1} av {string2}", "Meal plan": "Måltidsplan", "Add recipe on {string0}": "Lägg till recept i {string0}", - "{string0} serving | {string0} servings": "{string0} portion | {string0} portioner", + "{count} serving | {count} servings": "{count} portion | {count} portioner", "Week costs": "Veckokostnader", "Configuration": "Konfiguration", "A predefined list of values, one per line": "En fördefinierad lista med värden, en per rad", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "Det innebär att nästa utförande av denna syssla tilldelas nästa person i bokstavsordning", "Assign to": "Tilldela", "This assignment type requires that at least one is assigned": "Denna uppgiftstyp kräver åtminstone en tilldelning", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "Jag har {string0} tilldelad syssla | Jag har {string0} tilldelade sysslor", + "{count} chore is assigned to me | {count} chores are assigned to me": "Jag har {count} tilldelad syssla | Jag har {count} tilldelade sysslor", "Assigned to me": "Tilldelad till mig", "assigned to {string0}": "Tilldelad till {string0}", "Assignment": "Uppgift", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": " | ", + "{count} product is expired | {count} products are expired": " | ", "Expired": "Utgått", "Due soon days": "", "Add overdue/expired products": "", @@ -860,22 +860,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/ta.json b/locale/ta.json index 939e5f67..5ce8cd43 100644 --- a/locale/ta.json +++ b/locale/ta.json @@ -1,12 +1,12 @@ { "Stock overview": "இருப்பு கண்ணோட்டம்", - "{string0} product expires | {string0} products expiring": "{string0} பொருள் காலாவதியாகின்றது | {string0} பொருட்கள் காலாவதியாகின்றன", - "within the next day | within the next {string0} days": "நாளையிற்குள் | அடுத்த {string0} நாட்களிற்குள்", - "{string0} product is already expired | {string0} products are already expired": "{string0} பொருள் காலாவதியாகிவிட்டது | {string0} பொருட்கள் காலாவதியாகிவிட்டன", - "{string0} product is overdue | {string0} products are overdue": "{string0} பொருளை வாங்க வேண்டும் | {string0} பொருட்களை வாங்க வேண்டும்", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "குறிப்பிட்ட கையிருப்பு அளவினை விட குறைவான {string0} பொருள் | குறிப்பிட்ட கையிருப்பு அளவினை விட குறைவான {string0} பொருட்கள்", + "{count} product expires | {count} products expiring": "{count} பொருள் காலாவதியாகின்றது | {count} பொருட்கள் காலாவதியாகின்றன", + "within the next day | within the next {count} days": "நாளையிற்குள் | அடுத்த {count} நாட்களிற்குள்", + "{count} product is already expired | {count} products are already expired": "{count} பொருள் காலாவதியாகிவிட்டது | {count} பொருட்கள் காலாவதியாகிவிட்டன", + "{count} product is overdue | {count} products are overdue": "{count} பொருளை வாங்க வேண்டும் | {count} பொருட்களை வாங்க வேண்டும்", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "குறிப்பிட்ட கையிருப்பு அளவினை விட குறைவான {count} பொருள் | குறிப்பிட்ட கையிருப்பு அளவினை விட குறைவான {count} பொருட்கள்", "Product": "பொருள்", - "{string0} Product | {string0} Products": "{string0} பொருள் | {string0} பொருட்கள்", + "{count} Product | {count} Products": "{count} பொருள் | {count} பொருட்கள்", "Amount": "மொத்தம்", "Logout": "வெளியேறு", "Chores overview": "பணிகளின் கண்ணோட்டம்", @@ -144,7 +144,7 @@ "Requirements fulfilled": "தேவைகள் பூர்திசெய்யப்பட்டுள்ளன", "Put missing products on shopping list": "காலியாகிய பொருட்களினை வாங்கும் பட்டியலில் இணை", "Enough in stock": "போதுமானதாக கையிருப்பில் உள்ளது", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": " | ", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": " | ", "Expand to fullscreen": "முழுத்திரையிற்கு விரிவாக்கு", "Ingredients": "தேவையான பொருட்கள்", "Preparation": "தயாரிப்பு", @@ -177,11 +177,11 @@ "No price history available": "விலை வரலாறு இருக்கவில்லை", "Price": "விலை", "Unit": "அலகு", - "{string0} Unit | {string0} Units": "{string0} அலகு | {string0} அலகுகள்", - "{string0} chore is due to be done | {string0} chores are due to be done": " | ", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": " | ", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} மின்கலம் சக்தியேற்றப்பட வேண்டும் | {string0} மின்கலங்கள் சக்தியேற்றப்பட வேண்டும்", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} மின்கலம் சக்தியேற்றப்பட வேண்டும் | {string0} மின்கலங்கள் சக்தியேற்றப்பட வேண்டும்", + "{count} Unit | {count} Units": "{count} அலகு | {count} அலகுகள்", + "{count} chore is due to be done | {count} chores are due to be done": " | ", + "{count} chore is overdue to be done | {count} chores are overdue to be done": " | ", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} மின்கலம் சக்தியேற்றப்பட வேண்டும் | {count} மின்கலங்கள் சக்தியேற்றப்பட வேண்டும்", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} மின்கலம் சக்தியேற்றப்பட வேண்டும் | {count} மின்கலங்கள் சக்தியேற்றப்பட வேண்டும்", "in singular form": "ஒருமை வடிவத்தில்", "Quantity unit": "அளவு அலகு", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "பகுப்பு", "Edit task": "பணியினை தொகு", "Are you sure to delete task \"{string0}\"?": "\"{string0}\" பணியினை நீக்க உறுதியா?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} வேலையினை முடிக்க வேண்டும் | {string0} வேலைகளை முடிக்க வேண்டும்", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": " | ", + "{count} task is due to be done | {count} tasks are due to be done": "{count} வேலையினை முடிக்க வேண்டும் | {count} வேலைகளை முடிக்க வேண்டும்", + "{count} task is overdue to be done | {count} tasks are overdue to be done": " | ", "Edit task category": "பணியின் பகுப்பினைத் தொகு", "Create task category": "பணியின் பகுப்பினை உருவாக்கு", "Product groups": "பொருட்களின் குழுக்கள்", @@ -351,7 +351,7 @@ "Plural count": "பன்மை எண்ணிக்கை", "Plural rule": "பன்மை விதி", "in plural form": "பன்மை வடிவத்தில்", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": " | ", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": " | ", "Not enough in stock, but already on the shopping list": "கையிருப்பில் போதுமானதாக இல்லை, ஆனால் ஏற்கனவே வாங்க வேண்டியவை பட்டியலில் உள்ளது", "Not enough in stock": "கையிருப்பில் போதுமானதாக இல்லை", "Expiring soon days": "விரைவில் காலாவதியாகப்போகும் நாட்கள்", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "{string2} இலிருந்து {string1} இனை உண்", "Meal plan": "உணவுத்திட்டம்", "Add recipe on {string0}": "{string0} இல் சமையல் குறிப்பினை இணை", - "{string0} serving | {string0} servings": "{string0} பரிமாறல் | {string0} பரிமாறல்கள்", + "{count} serving | {count} servings": "{count} பரிமாறல் | {count} பரிமாறல்கள்", "Week costs": "வார செலவுகள்", "Configuration": "உள்ளமைவு", "A predefined list of values, one per line": "மதிப்புகளின் முன் வரையறுக்கப்பட்ட பட்டியல், ஒரு வரிக்கு ஒன்று", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "", "Assign to": "இவருக்கு நிச்சியி", "This assignment type requires that at least one is assigned": "", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} பணி எனக்கு நிச்சியக்கப்பட்டுள்ளது | {string0} பணிகள் எனக்கு நிச்சியக்கப்பட்டுள்ளன", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} பணி எனக்கு நிச்சியக்கப்பட்டுள்ளது | {count} பணிகள் எனக்கு நிச்சியக்கப்பட்டுள்ளன", "Assigned to me": "எனக்கு நிச்சியக்கப்பட்டுள்ளவை", "assigned to {string0}": "{string0}இற்கு நிச்சியக்கப்பட்டது", "Assignment": "பணி", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "", "Default due days after thawing": "", "Next due date": "அடுத்த உரிய தேதி", - "{string0} product is due | {string0} products are due": " | ", + "{count} product is due | {count} products are due": " | ", "Due date": "உரிய தேதி", "Never overdue": "", - "{string0} product is expired | {string0} products are expired": "{string0} பொருள் காலாவதியாகிவிட்டது | {string0} பொருட்கள் காலாவதியாகிவிட்டன", + "{count} product is expired | {count} products are expired": "{count} பொருள் காலாவதியாகிவிட்டது | {count} பொருட்கள் காலாவதியாகிவிட்டன", "Expired": "காலாவதியானவை", "Due soon days": "", "Add overdue/expired products": "", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "ஹீபுரு (இசுரேல்)", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/tr.json b/locale/tr.json index bf4d3649..a8e9a9c6 100644 --- a/locale/tr.json +++ b/locale/tr.json @@ -1,11 +1,11 @@ { "Stock overview": "Stoklara genel bakış", - "{string0} product expires | {string0} products expiring": " | ", - "within the next day | within the next {string0} days": " | ", - "{string0} product is already expired | {string0} products are already expired": " | ", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": " | ", + "{count} product expires | {count} products expiring": " | ", + "within the next day | within the next {count} days": " | ", + "{count} product is already expired | {count} products are already expired": " | ", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": " | ", "Product": "Ürün", - "{string0} Product | {string0} Products": " | ", + "{count} Product | {count} Products": " | ", "Amount": "Miktar", "Next best before date": "Son kullanma tarihi", "Logout": "Çıkış yap", @@ -151,7 +151,7 @@ "Requirements fulfilled": "Gereklilikler sağlandı", "Put missing products on shopping list": "Eksik malzemeleri alışveriş listesine ekle", "Enough in stock": "Stokta yeteri kadar var", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": " | ", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": " | ", "Expand to fullscreen": "Tam ekran yap", "Ingredients": "Malzemeler", "Preparation": "Hazırlanışı", @@ -187,11 +187,11 @@ "in {string0} per purchase quantity unit": "{string0} için her miktar birimini satın al", "The price cannot be lower than {string0}": "Fiyat {string0}'den az olamaz", "Unit": "Birim", - "{string0} Unit | {string0} Units": " | ", - "{string0} chore is due to be done | {string0} chores are due to be done": " | ", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": " | ", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": " | ", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": " | ", + "{count} Unit | {count} Units": " | ", + "{count} chore is due to be done | {count} chores are due to be done": " | ", + "{count} chore is overdue to be done | {count} chores are overdue to be done": " | ", + "{count} battery is due to be charged | {count} batteries are due to be charged": " | ", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": " | ", "in singular form": "tekil formda", "Never expires": "Son kullanma tarihi asla dolmuyor", "This cannot be lower than {string0}": "Bu {string0}'den az olamaz", @@ -217,8 +217,8 @@ "Category": "Kategori", "Edit task": "Görevi düzenle", "Are you sure to delete task \"{string0}\"?": "Görev \"{string0}\"'i silmek istediğine emin misin?", - "{string0} task is due to be done | {string0} tasks are due to be done": " | ", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": " | ", + "{count} task is due to be done | {count} tasks are due to be done": " | ", + "{count} task is overdue to be done | {count} tasks are overdue to be done": " | ", "Edit task category": "Görev kategorisini düzenle", "Create task category": "Görev kategorisi oluştur", "Product groups": "Ürün grupları", @@ -385,7 +385,7 @@ "Plural count": "", "Plural rule": "", "in plural form": "çoğul formda", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": " | ", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": " | ", "Consume {string0} of {string0}": "", "no-assignment": "", "who-least-did-first": "", @@ -498,27 +498,22 @@ "The thing which happens on Mondays and Wednesdays": "", "Swedish": "", "Polish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on": "", "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, "This activates night mode between {start} and {end} the same day": { "": "" - } + }, + "{string0} total value": "" } \ No newline at end of file diff --git a/locale/zh_CN.json b/locale/zh_CN.json index 241d9fbe..72711367 100644 --- a/locale/zh_CN.json +++ b/locale/zh_CN.json @@ -1,12 +1,12 @@ { "Stock overview": "库存总览", - "{string0} product expires | {string0} products expiring": "{string0}产品即将过期", - "within the next day | within the next {string0} days": "在接下来{string0}天", - "{string0} product is already expired | {string0} products are already expired": "{string0}产品已经过期", - "{string0} product is overdue | {string0} products are overdue": "{string0}产品过期", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0}产品低于定义的最小库存", + "{count} product expires | {count} products expiring": "{count}产品即将过期", + "within the next day | within the next {count} days": "在接下来{count}天", + "{count} product is already expired | {count} products are already expired": "{count}产品已经过期", + "{count} product is overdue | {count} products are overdue": "{count}产品过期", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count}产品低于定义的最小库存", "Product": "产品", - "{string0} Product | {string0} Products": "{string0}产品", + "{count} Product | {count} Products": "{count}产品", "Amount": "总量", "Logout": "注销", "Chores overview": "家务总览", @@ -144,7 +144,7 @@ "Requirements fulfilled": "满足要求", "Put missing products on shopping list": "将缺失的产品列在购物清单上", "Enough in stock": "库存充足", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "库存不足,缺少原料{string0},但已经在购物清单上", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "库存不足,缺少原料{count},但已经在购物清单上", "Expand to fullscreen": "全屏", "Ingredients": "配料", "Preparation": "预备", @@ -177,11 +177,11 @@ "No price history available": "没有价格历史", "Price": "价格", "Unit": "单位", - "{string0} Unit | {string0} Units": "{string0}单位", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0}家务要去完成", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0}家务逾期未做", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0}电池要充电了", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0}电池逾期未充电", + "{count} Unit | {count} Units": "{count}单位", + "{count} chore is due to be done | {count} chores are due to be done": "{count}家务要去完成", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count}家务逾期未做", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count}电池要充电了", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count}电池逾期未充电", "in singular form": "单数形式", "Quantity unit": "数量单位", "Only check if any amount is in stock": "只检查是否有任何数量的库存", @@ -204,8 +204,8 @@ "Category": "类别", "Edit task": "编辑任务", "Are you sure to delete task \"{string0}\"?": "确定要删除任务 “{string0}” 吗?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0}任务要去完成", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0}任务逾期未做", + "{count} task is due to be done | {count} tasks are due to be done": "{count}任务要去完成", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count}任务逾期未做", "Edit task category": "编辑任务分类", "Create task category": "创建任务分类", "Product groups": "产品组", @@ -351,7 +351,7 @@ "Plural count": "复数计数", "Plural rule": "复数规则", "in plural form": "复数形式", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "库存不足,缺少原料{string0}", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "库存不足,缺少原料{count}", "Not enough in stock, but already on the shopping list": "库存不足,但已在购物清单中", "Not enough in stock": "库存不足", "Expiring soon days": "即将过期", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "消费{string2}中的{string1}", "Meal plan": "膳食计划", "Add recipe on {string0}": "在{string0}上添加食谱", - "{string0} serving | {string0} servings": "{string0}份", + "{count} serving | {count} servings": "{count}份", "Week costs": "周成本", "Configuration": "配置", "A predefined list of values, one per line": "一个预定义的值列表,每行一个", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "这意味着该任务的下一个执行将按字母顺序分配给下一个执行", "Assign to": "分配给", "This assignment type requires that at least one is assigned": "此赋值类型要求至少分配一个", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "分配给我做家务{string0}", + "{count} chore is assigned to me | {count} chores are assigned to me": "分配给我做家务{count}", "Assigned to me": "分配给我", "assigned to {string0}": "分配给 {string0}", "Assignment": "分配", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "当将产品从冷冻室移出时(解冻时),到期日将改为今天+这个天数", "Default due days after thawing": "解冻后默认到期日", "Next due date": "下一个到期日", - "{string0} product is due | {string0} products are due": "{string0}产品到期", + "{count} product is due | {count} products are due": "{count}产品到期", "Due date": "截止日期", "Never overdue": "永不逾期", - "{string0} product is expired | {string0} products are expired": "{string0}产品已过期", + "{count} product is expired | {count} products are expired": "{count}产品已过期", "Expired": "已过期", "Due soon days": "即将到期", "Add overdue/expired products": "添加过期/过期的产品", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "希伯来语(以色列)", "Tamil": "泰米尔语", "Finnish": "芬兰语", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/locale/zh_TW.json b/locale/zh_TW.json index 61710527..098d1052 100644 --- a/locale/zh_TW.json +++ b/locale/zh_TW.json @@ -1,12 +1,12 @@ { "Stock overview": "庫存總覽", - "{string0} product expires | {string0} products expiring": "{string0} 項物品將於", - "within the next day | within the next {string0} days": "{string0} 天後過期", - "{string0} product is already expired | {string0} products are already expired": "{string0} 項物品已經過期", - "{string0} product is overdue | {string0} products are overdue": "{string0} 項物品逾期", - "{string0} product is below defined min. stock amount | {string0} products are below defined min. stock amount": "{string0} 項物品數量小於最低庫存數量", + "{count} product expires | {count} products expiring": "{count} 項物品將於", + "within the next day | within the next {count} days": "{count} 天後過期", + "{count} product is already expired | {count} products are already expired": "{count} 項物品已經過期", + "{count} product is overdue | {count} products are overdue": "{count} 項物品逾期", + "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} 項物品數量小於最低庫存數量", "Product": "物品", - "{string0} Product | {string0} Products": "{string0} 項物品", + "{count} Product | {count} Products": "{count} 項物品", "Amount": "數量", "Logout": "登出", "Chores overview": "家務總覽", @@ -144,7 +144,7 @@ "Requirements fulfilled": "符合需求", "Put missing products on shopping list": "將不足的物品加入購物清單", "Enough in stock": "庫存充足", - "Not enough in stock, {string0} ingredient missing but already on the shopping list | Not enough in stock, {string0} ingredients missing but already on the shopping list": "食材 {string0} 庫存不足,但已加入購物清單。", + "Not enough in stock, {count} ingredient missing but already on the shopping list | Not enough in stock, {count} ingredients missing but already on the shopping list": "食材 {count} 庫存不足,但已加入購物清單。", "Expand to fullscreen": "全螢幕模式", "Ingredients": "食材", "Preparation": "前置作業", @@ -177,11 +177,11 @@ "No price history available": "沒有可用價格歷史", "Price": "價格", "Unit": "單位", - "{string0} Unit | {string0} Units": "{string0} 個單位", - "{string0} chore is due to be done | {string0} chores are due to be done": "{string0} 件家務需要完成", - "{string0} chore is overdue to be done | {string0} chores are overdue to be done": "{string0} 件家務早該完成", - "{string0} battery is due to be charged | {string0} batteries are due to be charged": "{string0} 組電池需要充電", - "{string0} battery is overdue to be charged | {string0} batteries are overdue to be charged": "{string0} 組電池早該充電", + "{count} Unit | {count} Units": "{count} 個單位", + "{count} chore is due to be done | {count} chores are due to be done": "{count} 件家務需要完成", + "{count} chore is overdue to be done | {count} chores are overdue to be done": "{count} 件家務早該完成", + "{count} battery is due to be charged | {count} batteries are due to be charged": "{count} 組電池需要充電", + "{count} battery is overdue to be charged | {count} batteries are overdue to be charged": "{count} 組電池早該充電", "in singular form": "單數形式", "Quantity unit": "數量單位", "Only check if any amount is in stock": "", @@ -204,8 +204,8 @@ "Category": "分類", "Edit task": "編輯任務", "Are you sure to delete task \"{string0}\"?": "確定要刪除任務「{string0}」嗎?", - "{string0} task is due to be done | {string0} tasks are due to be done": "{string0} 項任務需要完成", - "{string0} task is overdue to be done | {string0} tasks are overdue to be done": "{string0} 項任務早該完成", + "{count} task is due to be done | {count} tasks are due to be done": "{count} 項任務需要完成", + "{count} task is overdue to be done | {count} tasks are overdue to be done": "{count} 項任務早該完成", "Edit task category": "編輯任務分類", "Create task category": "新增任務分類", "Product groups": "物品分類", @@ -351,7 +351,7 @@ "Plural count": "複數", "Plural rule": "複數規則", "in plural form": "複數形式", - "Not enough in stock, {string0} ingredient missing | Not enough in stock, {string0} ingredients missing": "食材 {string0} 庫存不足", + "Not enough in stock, {count} ingredient missing | Not enough in stock, {count} ingredients missing": "食材 {count} 庫存不足", "Not enough in stock, but already on the shopping list": "庫存不足,但已在購物清單中", "Not enough in stock": "庫存不足", "Expiring soon days": "即將過期天數", @@ -365,7 +365,7 @@ "Consume {string1} of {string2}": "消耗 {string2} 的 {string1}", "Meal plan": "菜單", "Add recipe on {string0}": "在 {string0} 上增加食譜", - "{string0} serving | {string0} servings": "{string0} 份", + "{count} serving | {count} servings": "{count} 份", "Week costs": "週花費", "Configuration": "設定", "A predefined list of values, one per line": "預定義的值列表,每行一個", @@ -416,7 +416,7 @@ "This means the next execution of this chore will be assigned to the next one in alphabetical order": "這代表此家務下一次執行將分配給按字母順序的下一位", "Assign to": "分配給", "This assignment type requires that at least one is assigned": "此分配類型要求至少分配一個", - "{string0} chore is assigned to me | {string0} chores are assigned to me": "{string0} 件家務分配給我", + "{count} chore is assigned to me | {count} chores are assigned to me": "{count} 件家務分配給我", "Assigned to me": "分配給我", "assigned to {string0}": "分配給 {string0}", "Assignment": "分配", @@ -625,10 +625,10 @@ "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days": "將此物品移出冷凍庫時(將其解凍時),到期日期將由今天加上此天數代替", "Default due days after thawing": "預設解凍後到期天數", "Next due date": "新到期日期", - "{string0} product is due | {string0} products are due": "{string0} 項物品到期", + "{count} product is due | {count} products are due": "{count} 項物品到期", "Due date": "到期日期", "Never overdue": "永不逾期", - "{string0} product is expired | {string0} products are expired": "{string0} 物品已過期", + "{count} product is expired | {count} products are expired": "{count} 物品已過期", "Expired": "已過期", "Due soon days": "即將到期天數", "Add overdue/expired products": "新增逾期/過期物品", @@ -886,22 +886,16 @@ "Hebrew (Israel)": "", "Tamil": "", "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", "Auto": "", "Automatic night mode range": "", "Invert": "", - "This activates night mode between {nightModeStart} and {nightModeEnd} the next day": { - "": "" - }, - "This activates night mode between {nightModeStart} and {nightModeEnd} the same day": { - "": "" - }, "Keep screen on while displaying fullscreen content": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", "This activates night mode between {start} and {end} the next day": { "": "" }, diff --git a/php/Controllers/StockApiController.php b/php/Controllers/StockApiController.php index c76fd24a..81098c86 100644 --- a/php/Controllers/StockApiController.php +++ b/php/Controllers/StockApiController.php @@ -329,6 +329,16 @@ public function CurrentStock(\Psr\Http\Message\ServerRequestInterface $request, return $this->ApiResponse($response, $this->getStockService()->GetCurrentStock()); } + public function StockOverview(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + { + $usersService = $this->getUsersService(); + + return $this->ApiResponse($response, [ + 'currentStock' => $this->getStockService()->GetCurrentStockOverview(), + 'currentStockLocations' => $this->getStockService()->GetCurrentStockLocations(), + ]); + } + public function CurrentVolatileStock(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { $nextXDays = 5; diff --git a/php/routes.php b/php/routes.php index b7bb8f7c..83bcc76b 100644 --- a/php/routes.php +++ b/php/routes.php @@ -186,6 +186,7 @@ // Stock $group->get('/stock', '\Grocy\Controllers\StockApiController:CurrentStock'); + $group->get('/stock/overview', '\Grocy\Controllers\StockApiController:StockOverview'); $group->get('/stock/entry/{entryId}', '\Grocy\Controllers\StockApiController:StockEntry'); $group->put('/stock/entry/{entryId}', '\Grocy\Controllers\StockApiController:EditStockEntry'); $group->get('/stock/volatile', '\Grocy\Controllers\StockApiController:CurrentVolatileStock'); diff --git a/scss/sigma/_overrides.scss b/scss/sigma/_overrides.scss index de2f4c5f..79cd0589 100644 --- a/scss/sigma/_overrides.scss +++ b/scss/sigma/_overrides.scss @@ -5,6 +5,28 @@ body { color: rgba(255, 255, 255, 0.87); // text-color background-color: #121212; // surface-b + .stock-table tr { + &.row-due-soon { + background-color: #FFE082 !important; + color: #121212; + } + + &.row-expired { + background-color: #F48FB1 !important; + color: #121212; + } + + &.row-overdue { + background-color: #78909C !important; + color: #fff; + } + + &.row-on-shoppinglist { + background-color: #81D4FA !important; + color: #121212; + } + } + .layout-footer { background-color: #1e1e1e; // surface-a } @@ -50,6 +72,24 @@ body { } &.theme-day { + .stock-table tr { + &.row-due-soon { + background-color: adjust-color(#fbc02d, $lightness: 30%) !important; // yellow 500 + } + + &.row-expired { + background-color: adjust-color(#D32F2F, $lightness: 30%) !important; + } + + &.row-overdue { + background-color: adjust-color(#607D8B, $lightness: 30%) !important; + } + + &.row-on-shoppinglist { + background-color: adjust-color(#0288D1, $lightness: 50%) !important; + } + } + .user-settings-menu { > li { > a { @@ -142,4 +182,22 @@ body { .layout-headerclock { float: left; font-size: 20px; +} + +.p-card.with-title-addon .p-card-title span { + font-size: 80%; + color: #777; +} + +.filter-buttons button { + margin-right: .5rem; +} + +.product-context-menu { + width: 22rem !important; +} + +.row-due-soon { + background-color: #fbc02d; // yellow 500 + color: #212529; } \ No newline at end of file From 560cc231368343d2c80c7987e93641943239fe68 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Mon, 5 Jul 2021 10:53:44 +0200 Subject: [PATCH 12/16] Locale fallbacking now works --- js/locale.ts | 12 ++++++++++-- js/pages/Stock/Overview.vue | 16 +++++++++++----- locale/da.json | 20 ++------------------ locale/en.json | 12 +++--------- 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/js/locale.ts b/js/locale.ts index 2f9804a4..bfd0e36e 100644 --- a/js/locale.ts +++ b/js/locale.ts @@ -8,12 +8,20 @@ export const ALL_LANGS = ['cs', 'da', 'de', 'el_GR', 'en', 'en_GB', 'es', 'fi', export function setupI18n(options: any = { locale: 'en' }): I18n { // force "format fallback messages" - options.formatFallbackMessages = true; + options.fallbackFormat = true; options.legacy = false; options.globalInjection = true; + options.fallbackLocale = "root"; + options.fallbackWarn = false; + options.missing = (locale :string, key :string) => + { + // en is integrated into code. + if (locale == "en" || locale == "root") return; + + console.warn(`i18n: (${locale}) Missing key '${key}'.`); + }; const i18n = createI18n(options); - setI18nLanguage(i18n, options.locale); return i18n; } diff --git a/js/pages/Stock/Overview.vue b/js/pages/Stock/Overview.vue index aedac4fe..379eab5e 100644 --- a/js/pages/Stock/Overview.vue +++ b/js/pages/Stock/Overview.vue @@ -25,6 +25,7 @@ :value="stock" :scrollable="true" scrollDirection="horizontal" + responsiveLayout="scroll" class="stock-table p-mt-2 p-shadow-1 p-datatable-sm" contextMenu v-model:contextMenuSelection="selectedProduct" @@ -104,8 +105,13 @@ @@ -145,7 +151,7 @@ export default defineComponent({ {label: 'Stock entries', command: () => {}}, {label: 'Stock journal', command: () => {}}, {label: 'Stock journal summary', command: () => {}}, - {label: 'Edit Product', command: () => {}}, + {label: 'Edit product', command: () => {}}, { separator: true }, {label: 'Download product grocycode', command: () => {}}, ] @@ -182,8 +188,8 @@ export default defineComponent({ { if(item.disabledKey !== undefined && this.selectedProduct !== null) { - if(item.disabledKey == "consume") return parseFloat(item.amount_aggregated) > 0; - if(item.disabledKey == "transfer") return parseFloat(item.amount) > 0; + if(item.disabledKey == "consume") return parseFloat(item.amount_aggregated) == 0; + if(item.disabledKey == "transfer") return parseFloat(item.amount) == 0; } return false; }, diff --git a/locale/da.json b/locale/da.json index 08b48532..1c7bfc53 100644 --- a/locale/da.json +++ b/locale/da.json @@ -3,7 +3,7 @@ "{count} product expires | {count} products expiring": "{count} produkt udløber | {count} varer udløber", "within the next day | within the next {count} days": "indenfor den næstkommende dag | indenfor de næste {count} dage", "{count} product is already expired | {count} products are already expired": "{count} vare er allerede udløbet | {count} varer er allerede udløbet", - "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is overdue | {count} products are overdue": "{count} vare er allerede udløbet | {count} varer er allerede udløbet", "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} vare er under den definerede minimumsbeholdning | {count} varer er under den definerede minimumsbeholdning", "Product": "Vare", "{count} Product | {count} Products": "{count} Vare | {count} Varer", @@ -829,21 +829,5 @@ "Chinese (China)": "", "Hebrew (Israel)": "", "Tamil": "", - "Finnish": "", - "many awesome people": { - "": "" - }, - "{project_link} made with ❤️ and ☕ by {contributors}": "", - "Automatically reload data": "", - "Night mode": "", - "Auto": "", - "Automatic night mode range": "", - "Invert": "", - "Keep screen on while displaying fullscreen content": "", - "This activates night mode between {start} and {end} the next day": { - "": "" - }, - "This activates night mode between {start} and {end} the same day": { - "": "" - } + "Finnish": "" } \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index abcf0869..225395bc 100644 --- a/locale/en.json +++ b/locale/en.json @@ -585,9 +585,7 @@ "Polish": "Polish", "DemoSupermarket1": "Walmart", "DemoSupermarket2": "Kroger", - "many awesome people": { - "": "" - }, + "many awesome people.": "", "{project_link} made with ❤️ and ☕ by {contributors}": "", "Automatically reload data": "", "Night mode": "", @@ -596,12 +594,8 @@ "Invert": "", "Keep screen on": "", "Keep screen on while displaying fullscreen content": "", - "This activates night mode between {start} and {end} the next day": { - "": "" - }, - "This activates night mode between {start} and {end} the same day": { - "": "" - }, + "This activates night mode between {start} and {end} the next day.": "", + "This activates night mode between {start} and {end} the same day.": "", "{count} Product | {count} Products": "", "{string0} total value": "" } \ No newline at end of file From f5ceed59e52590c57029d4e7ab3c6aa5e2d29d57 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Tue, 6 Jul 2021 15:53:13 +0200 Subject: [PATCH 13/16] Stockoverview: Stock table --- Makefile | 3 +- buildfiles/make-locales.php | 13 ++ js/App.vue | 17 +- js/lib/filters.ts | 76 +++++++ js/lib/localstorage.ts | 2 + js/locale.ts | 26 ++- js/main.ts | 26 ++- js/pages/Stock/Overview.vue | 390 +++++++++++++++++++++++++++++++++--- locale/cs.json | 20 ++ locale/da.json | 38 +++- locale/de.json | 135 ++++++++++++- locale/el_GR.json | 20 ++ locale/en.json | 126 ++++++++++-- locale/en_GB.json | 41 +++- locale/es.json | 20 ++ locale/fi.json | 20 ++ locale/fr.json | 20 ++ locale/he_IL.json | 20 ++ locale/hu.json | 20 ++ locale/it.json | 20 ++ locale/ja.json | 20 ++ locale/ko_KR.json | 20 ++ locale/nl.json | 20 ++ locale/no.json | 20 ++ locale/pl.json | 20 ++ locale/pt_BR.json | 20 ++ locale/pt_PT.json | 26 ++- locale/ru.json | 20 ++ locale/sk_SK.json | 20 ++ locale/sv_SE.json | 20 ++ locale/ta.json | 20 ++ locale/tr.json | 30 ++- locale/zh_CN.json | 20 ++ locale/zh_TW.json | 20 ++ 34 files changed, 1291 insertions(+), 58 deletions(-) create mode 100644 buildfiles/make-locales.php create mode 100644 js/lib/filters.ts create mode 100644 js/lib/localstorage.ts diff --git a/Makefile b/Makefile index b1f22ec3..226f4e5d 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ PHP=php COMPOSER=composer POSTCSS=npx postcss I18NEXTRACT=yarn run vue-i18n-extract +LOCALEMAKE=php buildfiles/make-locales.php # Configure some default flags for the tooling. Set include paths. SASSFLAGS=-I node_modules/ --color --quiet-deps @@ -216,7 +217,7 @@ public/img/%.svg: artwork/%.svg cp $< $@ public/locale/%.json: locale/%.json | $(OBJDIRS) - cp $< $@ + $(LOCALEMAKE) $< $@ node_modules/swagger-ui-dist/swagger-ui.js: yarn.lock diff --git a/buildfiles/make-locales.php b/buildfiles/make-locales.php new file mode 100644 index 00000000..74b3b6d7 --- /dev/null +++ b/buildfiles/make-locales.php @@ -0,0 +1,13 @@ + boolean): boolean +{ + if (value === undefined || value === null) return false; + if (filter === undefined || filter === null) return true; + + if (typeof filter === "string" && filter.trim() == "") return false; + + const { realFilter, realValue } = getValues(value, filter); + + return func(realFilter, realValue); +} + +export const luxonDateBeforeFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a > b); +export const luxonDateBeforeOrEqualFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a >= b); + +export const luxonDateAfterFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a < b); +export const luxonDateAfterOrEqualFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a <= b); + +export const luxonDateEqualFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a == b); +export const luxonDateNotEqualFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => a != b); + +export const luxonDateEqualDayFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => LuxonDateTime.fromMillis(a).startOf('day').toMillis() == LuxonDateTime.fromMillis(b).startOf('day').toMillis()); +export const luxonDateNotEqualDayFilter = + (value: LuxonDateTime | Date | string | undefined | null, filter: LuxonDateTime | Date | number | string | undefined | null): boolean => + doFilter(value, filter, (a, b) => LuxonDateTime.fromMillis(a).startOf('day').toMillis() != LuxonDateTime.fromMillis(b).startOf('day').toMillis()); \ No newline at end of file diff --git a/js/lib/localstorage.ts b/js/lib/localstorage.ts new file mode 100644 index 00000000..ff388f41 --- /dev/null +++ b/js/lib/localstorage.ts @@ -0,0 +1,2 @@ +export const STOCKOVERVIEW_DATATABLE_STATE = 'dt-state-stockoverview'; +export const STOCKOVERVIEW_COLUMN_STATE = 'column-state-stockoverview'; \ No newline at end of file diff --git a/js/locale.ts b/js/locale.ts index bfd0e36e..3c474fec 100644 --- a/js/locale.ts +++ b/js/locale.ts @@ -1,5 +1,7 @@ import { nextTick } from 'vue'; import { createI18n, I18n } from 'vue-i18n'; +import { Store } from 'vuex'; +import { RootState } from './store/interfaces'; export const ALL_LANGS = ['cs', 'da', 'de', 'el_GR', 'en', 'en_GB', 'es', 'fi', 'fr', 'he_IL', 'hu', 'it', 'ja', 'ko_KR', 'nl', 'no', 'pl', 'pt_BR', 'pt_PT', 'ru', 'sk_SK', 'sv_SE', 'ta', 'tr', 'zh_CN', 'zh_TW']; @@ -11,7 +13,7 @@ export function setupI18n(options: any = { locale: 'en' }): I18n { @@ -38,14 +40,34 @@ export function setI18nLanguage(i18n: I18n, lo document.querySelector('html')?.setAttribute('lang', locale); } -export async function loadLocaleMessages(i18n : I18n, locale : string) : Promise +export async function loadLocaleMessages(i18n : I18n, locale: string, store: Store) : Promise { // load locale messages with dynamic import // this gets 1:1 translated into a network call, so.... const messages = await (await fetch(`/locale/${locale}.json`)).json(); + if (messages.numberFormats !== undefined) + { + if (messages.numberFormats.currency !== undefined) + { + messages.numberFormats.currency.currency = store.state.Settings.Currency; + messages.numberFormats.currency.maximumFractionDigits = store.state.Settings.User?.DecimalPlacesPrices || 2; + messages.numberFormats.currency.manimumFractionDigits = store.state.Settings.User?.DecimalPlacesPrices || 2; + } + if (messages.numberFormats["avoid-decimal"] !== undefined) + { + messages.numberFormats["avoid-decimal"].maximumFractionDigits = store.state.Settings.User?.DecimalPlacesAmount || 4; + } + if (messages.numberFormats["decimal"] !== undefined) + { + messages.numberFormats["decimal"].maximumFractionDigits = store.state.Settings.User?.DecimalPlacesAmount || 4; + messages.numberFormats["decimal"].minimumFractionDigits = store.state.Settings.User?.DecimalPlacesAmount || 4; + } + } + // set locale and locale message i18n.global.setLocaleMessage(locale, messages); + i18n.global.setNumberFormat(locale, messages.numberFormats); return nextTick(); } \ No newline at end of file diff --git a/js/main.ts b/js/main.ts index 8f0344db..7c7ce984 100644 --- a/js/main.ts +++ b/js/main.ts @@ -5,9 +5,11 @@ import { store, key } from './store'; import { LOAD_CONFIG } from './store/mutations'; import App from './App.vue'; import api from './api'; +import * as Filters from './lib/filters'; // PrimeVue components -import PrimeVue from 'primevue/config'; +import PrimeVue from 'primevue/config'; +import { FilterService } from 'primevue/api'; import ToastService from 'primevue/toastservice'; @@ -22,6 +24,9 @@ import Button from 'primevue/button'; import DataTable from 'primevue/datatable'; import Column from 'primevue/column'; import ContextMenu from 'primevue/contextmenu'; +import InputNumber from 'primevue/inputnumber'; +import Calendar from 'primevue/calendar'; +import MultiSelect from 'primevue/multiselect'; const app = createApp(App); const i18n = setupI18n(); @@ -42,15 +47,30 @@ app.component('Button', Button); app.component('DataTable', DataTable); app.component('Column', Column); app.component('ContextMenu', ContextMenu); +app.component('InputNumber', InputNumber); +app.component('Calendar', Calendar); +app.component('MultiSelect', MultiSelect); + +if (FilterService.register !== undefined) +{ + FilterService.register(Filters.LUXON_DATE_BEFORE, Filters.luxonDateBeforeFilter); + FilterService.register(Filters.LUXON_DATE_BEFORE_OR_EQUAL, Filters.luxonDateBeforeOrEqualFilter); + FilterService.register(Filters.LUXON_DATE_AFTER, Filters.luxonDateAfterFilter); + FilterService.register(Filters.LUXON_DATE_AFTER_OR_EQUAL, Filters.luxonDateAfterOrEqualFilter); + FilterService.register(Filters.LUXON_DATE_EQUAL, Filters.luxonDateEqualFilter); + FilterService.register(Filters.LUXON_DATE_NOT_EQUAL, Filters.luxonDateNotEqualFilter); + FilterService.register(Filters.LUXON_DATE_EQUAL_DAY, Filters.luxonDateEqualDayFilter); + FilterService.register(Filters.LUXON_DATE_NOT_EQUAL_DAY, Filters.luxonDateNotEqualDayFilter); +} // load configs api.System.GetConfig().then((config) => { store.commit(LOAD_CONFIG, config); - const promises = [loadLocaleMessages(i18n, "en")]; + const promises = [loadLocaleMessages(i18n, "en", store)]; if (store.state.Settings.Locale != "en") { - promises.push(loadLocaleMessages(i18n, store.state.Settings.Locale)); + promises.push(loadLocaleMessages(i18n, store.state.Settings.Locale, store)); } Promise.all(promises).then(() => { diff --git a/js/pages/Stock/Overview.vue b/js/pages/Stock/Overview.vue index 379eab5e..40b7636e 100644 --- a/js/pages/Stock/Overview.vue +++ b/js/pages/Stock/Overview.vue @@ -2,20 +2,29 @@
@@ -23,8 +32,6 @@ - + + + - + @@ -61,26 +86,81 @@
+ + + + + + + + + + - + + + + + + + + + + - + @@ -90,17 +170,109 @@
+ + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + @@ -108,7 +280,7 @@ - + @@ -126,6 +298,9 @@ import ContextMenu from 'primevue/contextmenu'; import { DateTime, Duration } from 'luxon'; import Skeleton from 'primevue/skeleton'; +import { FilterMatchMode, FilterOperator } from 'primevue/api'; +import * as Filters from '../../lib/filters'; +import * as localStorageKeys from '../../lib/localstorage'; export default defineComponent({ @@ -154,20 +329,93 @@ export default defineComponent({ {label: 'Edit product', command: () => {}}, { separator: true }, {label: 'Download product grocycode', command: () => {}}, - ] + ], + stockFilters: null, + stringMatchModeOptions: [ + { label: this.$t('Starts With'), value: FilterMatchMode.STARTS_WITH }, + { label: this.$t('Ends With'), value: FilterMatchMode.ENDS_WITH }, + { label: this.$t('Equals'), value: FilterMatchMode.EQUALS }, + { label: this.$t('Not Equals'), value: FilterMatchMode.NOT_EQUALS }, + { label: this.$t('Contains'), value: FilterMatchMode.CONTAINS }, + { label: this.$t('Not Contains'), value: FilterMatchMode.NOT_CONTAINS }, + ], + numberMatchModeOptions: [ + { label: this.$t("Less Than"), value: FilterMatchMode.LESS_THAN }, + { label: this.$t("Less Than Or Equal"), value: FilterMatchMode.LESS_THAN_OR_EQUAL_TO }, + { label: this.$t("Equals"), value: FilterMatchMode.EQUALS }, + { label: this.$t('Not Equals'), value: FilterMatchMode.NOT_EQUALS }, + { label: this.$t("Greater Than Or Equal"), value: FilterMatchMode.GREATER_THAN_OR_EQUAL_TO }, + { label: this.$t("Greater Than"), value: FilterMatchMode.GREATER_THAN }, + ], + dateMatchModeOptions: [ + { label: this.$t("Before"), value: Filters.LUXON_DATE_BEFORE }, + { label: this.$t("Before Or On"), value: Filters.LUXON_DATE_BEFORE_OR_EQUAL}, + { label: this.$t("On Day"), value: Filters.LUXON_DATE_EQUAL_DAY }, + { label: this.$t("Not On Day"), value: Filters.LUXON_DATE_NOT_EQUAL_DAY }, + { label: this.$t("After Or On"), value: Filters.LUXON_DATE_AFTER_OR_EQUAL }, + { label: this.$t("After"), value: Filters.LUXON_DATE_AFTER }, + ], + initialFilters: { + 'global' : { value: null, matchMode: FilterMatchMode.CONTAINS }, + 'product_name' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.STARTS_WITH}] }, + 'product_group_name' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.CONTAINS}] }, + 'amount' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'value' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'best_before_date' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.CONTAINS}] }, + 'product_calories' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'calories' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'last_pruchased' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.CONTAINS}] }, + 'last_price' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'min_stock_amount' : { operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.EQUALS}] }, + 'isBelowMin' : { value: null, matchMode: FilterMatchMode.EQUALS }, + 'due_type' : { value: null, matchMode: FilterMatchMode.EQUALS } + }, + selectedColumns: > Array(0), + allColumns: > Array(0), + columnVisibility: { + product_name : true, + product_group_name : true, + amount : true, + value : true, + best_before_date : true, + product_calories : true, + calories : true, + last_pruchased : true, + last_price : true, + min_stock_amount : true, + }, + changing: false, }; }, mounted() { + this.clearAllFilters(); api.Stock.Overview().then((data) => { let stock = data.currentStock; + + // I have absolutely no words for this; and this is the absolute wrong place + // to fix it. But for the time being, don't break the backend. for(let elem of stock) { elem.ExpireMillis = elem.best_before_date != null ? DateTime.fromISO(elem.best_before_date).toMillis() : DateTime.fromISO("2999-12-31").toMillis(); + elem.amount_aggregated = parseFloat(elem.amount_aggregated); + elem.amount = parseFloat(elem.amount); + elem.amount_opened = parseFloat(elem.amount_opened); + elem.quick_consume_amount = parseFloat(elem.quick_consume_amount); + elem.value = parseFloat(elem.value); + elem.calories = parseFloat(elem.calories); + elem.calories_aggregated = parseFloat(elem.calories_aggregated); + elem.product_calories = parseFloat(elem.product_calories); + elem.min_stock_amount = parseFloat(elem.min_stock_amount); + const amount = parseInt(elem.is_aggregated_amount) > 0 ? elem.amount_aggregated : elem.amount; + elem.isBelowMin = amount < elem.min_stock_amount; + elem.due_type = parseInt(elem.due_type); } this.stock = stock; + this.clearAllFilters(); + this.resetColumnVisiblity(); this.loaded = true; }); }, @@ -184,6 +432,84 @@ export default defineComponent({ return { store, cm, onRowContextMenu }; }, methods: { + setFilterDueSoon() : void + { + this.stockFilters.best_before_date.constraints.splice(0, this.stockFilters.best_before_date.constraints); + this.stockFilters.best_before_date.constraints.push({ value: DateTime.now().startOf('day').toJSDate(), matchMode: Filters.LUXON_DATE_AFTER_OR_EQUAL }); + this.stockFilters.best_before_date.constraints.push({ value: DateTime.now().startOf('day').plus(Duration.fromObject({ days: this.dueSoonDays })).toJSDate(), matchMode: Filters.LUXON_DATE_BEFORE_OR_EQUAL }); + }, + setFilterExpired() : void + { + this.stockFilters.best_before_date.constraints.splice(0, this.stockFilters.best_before_date.constraints); + this.stockFilters.best_before_date.constraints.push({ value: DateTime.now().startOf('day').toJSDate(), matchMode: Filters.LUXON_DATE_BEFORE }); + this.stockFilters.due_type.value = 2; + }, + setFilterOverdue() : void + { + this.stockFilters.best_before_date.constraints.splice(0, this.stockFilters.best_before_date.constraints); + this.stockFilters.best_before_date.constraints.push({ value: DateTime.now().startOf('day').toJSDate(), matchMode: Filters.LUXON_DATE_BEFORE }); + this.stockFilters.due_type.value = 1; + }, + setFilterBelowMin() : void + { + this.stockFilters.isBelowMin.value = true; + }, + resetColumnVisiblity() : void + { + this.allColumns = [ + {field: 'product_name', header: this.$t('Product')}, + {field: 'product_group_name', header: this.$t('Product group') }, + {field: 'amount', header: this.$t('Amount')}, + {field: 'value', header: this.$t('Value')}, + {field: 'best_before_date', header: this.$t('Next due date')}, + {field: 'product_calories', header: this.$t('Calories') + " " + this.$t('Per stock quantity unit')}, + {field: 'calories', header: this.$t('Calories')}, + {field: 'last_purchased', header: this.$t('Last purchased')}, + {field: 'last_price', header: this.$t('Last price')}, + {field: 'min_stock_amount', header: this.$t('Min. stock amount')}, + ]; + + // TODO: make option to sync this across browsers + // TODO2: restore from old format + const storage = window.localStorage; + const savedColumns = storage.getItem(localStorageKeys.STOCKOVERVIEW_COLUMN_STATE); + if(savedColumns != null) + { + const selected = JSON.parse(savedColumns); + for(const column of this.allColumns) + { + if (selected.find((x: { field?: string; }) => x.field === column.field) !== undefined) + this.selectedColumns.push(column); + } + this.updateColumnVisibility(this.selectedColumns); + } + else + { + this.allColumns.map(x => this.selectedColumns.push(x)); + } + }, + updateColumnVisibility(val: Array) : void + { + this.allColumns.map(x => { const newval = val.includes(x); if(this.columnVisibility[x.field] != newval) this.columnVisibility[x.field] = newval; } ); + }, + onToggleColumns(val: Array) : void + { + this.updateColumnVisibility(val); + this.selectedColumns = this.allColumns.filter(x => val.includes(x)); + + const storage = window.localStorage; + + storage.setItem(localStorageKeys.STOCKOVERVIEW_COLUMN_STATE, JSON.stringify(this.selectedColumns)); + }, + clearAllFilters() : void + { + this.stockFilters = {}; + Object.assign(this.stockFilters, JSON.parse(JSON.stringify(this.initialFilters))); + }, + clearFilter(key: string) : void + { + this.stockFilters[key] = JSON.parse(JSON.stringify(this.initialFilters[key])); + }, checkDisabled(item: any) : boolean { if(item.disabledKey !== undefined && this.selectedProduct !== null) @@ -200,8 +526,8 @@ export default defineComponent({ const now = DateTime.now().toMillis(); const dueMillis = Duration.fromObject({ days: this.dueSoonDays }).toMillis(); const due = now + dueMillis; - if(item.ExpireMillis < now && parseInt(item.due_type) == 1) return "row-overdue"; - if(item.ExpireMillis < now && parseInt(item.due_type) == 2) return "row-expired"; + if(item.ExpireMillis < now && (item.due_type) == 1) return "row-overdue"; + if(item.ExpireMillis < now && (item.due_type) == 2) return "row-expired"; if(item.ExpireMillis > now && item.ExpireMillis <= due) return "row-due-soon"; if(parseInt(item.on_shopping_list)) return "row-on-shoppinglist"; @@ -209,10 +535,22 @@ export default defineComponent({ } }, computed: { + datatableColumnKey() : string + { + return localStorageKeys.STOCKOVERVIEW_DATATABLE_STATE; + }, + locale() : string + { + return this.store.state.Settings.Locale; + }, currency() : string { return this.store.state.Settings.Currency; }, + showProductOnShoppingList() : boolean | undefined + { + return this.store.state.Settings.User?.ShowIconWhenProductOnShoppinglist; + }, aggregateValue() : number { let sum = 0; @@ -243,7 +581,7 @@ export default defineComponent({ aggregateBelowMinAmount() : number { let sum = 0; - this.stock.forEach(x => sum += (parseFloat(x.amount_aggregated) < parseFloat(x.min_stock_amount) ? 1 : 0)); + this.stock.forEach(x => sum += ((parseInt(x.is_aggregated_amount) ? x.amount_aggregated : x.amount) < parseFloat(x.min_stock_amount) ? 1 : 0)); return sum; }, dueSoonDays() : number diff --git a/locale/cs.json b/locale/cs.json index eeb90714..5d28037c 100644 --- a/locale/cs.json +++ b/locale/cs.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/da.json b/locale/da.json index 1c7bfc53..c033fe90 100644 --- a/locale/da.json +++ b/locale/da.json @@ -829,5 +829,41 @@ "Chinese (China)": "", "Hebrew (Israel)": "", "Tamil": "", - "Finnish": "" + "Finnish": "", + "many awesome people": { + "": "" + }, + "{project_link} made with ❤️ and ☕ by {contributors}": "", + "Automatically reload data": "", + "Night mode": "", + "Auto": "", + "Automatic night mode range": "", + "Invert": "", + "Keep screen on while displaying fullscreen content": "", + "This activates night mode between {start} and {end} the next day": { + "": "" + }, + "This activates night mode between {start} and {end} the same day": { + "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" + } } \ No newline at end of file diff --git a/locale/de.json b/locale/de.json index 223a538d..87a81090 100644 --- a/locale/de.json +++ b/locale/de.json @@ -895,5 +895,138 @@ "This activates night mode between {start} and {end} the same day.": "Der Nachtmodus ist zwischen {start} und {end} am selben Tag aktiv.", "Keep screen on while displaying fullscreen content": "Bildschirm nicht abschalten, solange Vollbild-Inhalte angezeigt werden", "many awesome people.": "vielen großartigen Menschen gebaut.", - "{project_link} made with ❤️ and ☕ by {contributors}": "{project_link} wird mit ❤️ and ☕ von {contributors}" + "{project_link} made with ❤️ and ☕ by {contributors}": "{project_link} wird mit ❤️ and ☕ von {contributors}", + "numberFormats": { + "currency": { + "style": "currency", + "notation": "standard", + "decimalSepSymbol": "," + }, + "decimal": { + "style": "decimal", + "minimumFractionDigits": 2, + "maximumFractionDigits": 2, + "decimalSepSymbol": "," + }, + "avoid-decimal": { + "style": "decimal", + "minimumFractionDigits": 0, + "maximumFractionDigits": 2, + "decimalSepSymbol": "," + }, + "percent": { + "style": "percent", + "useGrouping": false, + "decimalSepSymbol": "," + } + }, + "Starts With": "Beginnt mit", + "Ends With": "Endet mit", + "Equals": "Gleich", + "Not Equals": "Ungleich", + "Contains": "Enthält", + "Not Contains": "Enthält nicht", + "Less Than": "Weniger als", + "Less Than Or Equal": "Weniger als oder gleich", + "Greater Than Or Equal": "Größer als oder gleich", + "Greater Than": "Größer als", + "Before": "Vor", + "Before Or On": "Vor oder an", + "On Day": "An Tag", + "Not On Day": "Nicht an Tag", + "After Or On": "Nach oder an", + "After": "Nach", + "primevue": { + "startsWith": "Beginnt mit", + "contains": "Enthält", + "notContains": "Enthält nicht", + "endsWith": "Endet mit", + "equals": "Gleich", + "notEquals": "Ungleich", + "noFilter": "Kein Filter", + "lt": "Weniger als", + "lte": "Weniger als oder gleich", + "gt": "Größer als", + "gte": "Größer als oder gleich", + "dateIs": "Datum ist", + "dateIsNot": "Datum ist nicht", + "dateBefore": "Datum ist vor", + "dateAfter": "Datum ist nach", + "clear": "Leeren", + "apply": "Anwenden", + "matchAll": "Bedinge alle", + "matchAny": "Bedinge eines", + "addRule": "Regel hinzufügen", + "removeRule": "Regel löschen", + "accept": "Ja", + "reject": "Nein", + "choose": "Wähle", + "upload": "Hochladen", + "cancel": "Abbrechen", + "dayNames": [ + "Sonntag", + "Montag", + "Dienstag", + "Mittwoch", + "Donnerstag", + "Freitag", + "Samstag" + ], + "dayNamesShort": [ + "So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa" + ], + "dayNamesMin": [ + "So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa" + ], + "monthNames": [ + "Jänner", + "Februar", + "März", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember" + ], + "monthNamesShort": [ + "Jän", + "Feb", + "Mär", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez" + ], + "today": "Heute", + "weekHeader": "KW", + "firstDayOfWeek": 1, + "dateFormat": "yy-mm-dd", + "weak": "Schwach", + "medium": "Mittel", + "strong": "Stark", + "passwordPrompt": "Passwort eingeben", + "emptyFilterMessage": "Nichts gefunden", + "emptyMessage": "Keine Optionen verfügbar" + } } \ No newline at end of file diff --git a/locale/el_GR.json b/locale/el_GR.json index 8ad13aee..5464b664 100644 --- a/locale/el_GR.json +++ b/locale/el_GR.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index 225395bc..ce80174e 100644 --- a/locale/en.json +++ b/locale/en.json @@ -585,17 +585,117 @@ "Polish": "Polish", "DemoSupermarket1": "Walmart", "DemoSupermarket2": "Kroger", - "many awesome people.": "", - "{project_link} made with ❤️ and ☕ by {contributors}": "", - "Automatically reload data": "", - "Night mode": "", - "Auto": "", - "Automatic night mode range": "", - "Invert": "", - "Keep screen on": "", - "Keep screen on while displaying fullscreen content": "", - "This activates night mode between {start} and {end} the next day.": "", - "This activates night mode between {start} and {end} the same day.": "", - "{count} Product | {count} Products": "", - "{string0} total value": "" + "numberFormats": { + "currency": { + "style": "currency", + "notation": "standard" + }, + "decimal": { + "style": "decimal", + "minimumFractionDigits": 2, + "maximumFractionDigits": 2 + }, + "avoid-decimal": { + "style": "decimal", + "minimumFractionDigits": 0, + "maximumFractionDigits": 2 + }, + "percent": { + "style": "percent", + "useGrouping": false + } + }, + "primevue": { + "startsWith": "Starts with", + "contains": "Contains", + "notContains": "Not contains", + "endsWith": "Ends with", + "equals": "Equals", + "notEquals": "Not equals", + "noFilter": "No Filter", + "lt": "Less than", + "lte": "Less than or equal to", + "gt": "Greater than", + "gte": "Greater than or equal to", + "dateIs": "Date is", + "dateIsNot": "Date is not", + "dateBefore": "Date is before", + "dateAfter": "Date is after", + "clear": "Clear", + "apply": "Apply", + "matchAll": "Match All", + "matchAny": "Match Any", + "addRule": "Add Rule", + "removeRule": "Remove Rule", + "accept": "Yes", + "reject": "No", + "choose": "Choose", + "upload": "Upload", + "cancel": "Cancel", + "dayNames": [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + ], + "dayNamesShort": [ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + ], + "dayNamesMin": [ + "Su", + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa" + ], + "monthNames": [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], + "monthNamesShort": [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ], + "today": "Today", + "weekHeader": "Wk", + "firstDayOfWeek": 0, + "dateFormat": "yy-mm-dd", + "weak": "Weak", + "medium": "Medium", + "strong": "Strong", + "passwordPrompt": "Enter a password", + "emptyFilterMessage": "No results found", + "emptyMessage": "No available options" + } } \ No newline at end of file diff --git a/locale/en_GB.json b/locale/en_GB.json index 169a6ae8..02dcc3f1 100644 --- a/locale/en_GB.json +++ b/locale/en_GB.json @@ -1,9 +1,9 @@ { "Stock overview": "Stock overview", - "{count} product expires | {count} products expiring": "{count} product expires | {count} products expiring", - "within the next day | within the next {count} days": "within the next day | within the next {count} days", + "{count} product expires | {count} products expiring": "", + "within the next day | within the next {count} days": "", "{count} product is already expired | {count} products are already expired": "{count} product is already expired | {count} products are already expired", - "{count} product is overdue | {count} products are overdue": " | ", + "{count} product is overdue | {count} products are overdue": "", "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount": "{count} product is below defined min. stock amount | {count} products are below defined min. stock amount", "Product": "Product", "{count} Product | {count} Products": "", @@ -380,7 +380,7 @@ "Undo task": "", "Due date rollover": "Due date rollover", "When enabled the chore can never be overdue, the due date will shift forward each day when due": "When enabled the chore can never be overdue, the due date will shift forward each day when due", - "Location Content Sheet": "Location Content Sheet", + "Location Content Sheet": "", "Print": "Print", "all locations": "all locations", "Here you can print a page per location with the current stock, maybe to hang it there and note the consumed things on it": "", @@ -446,7 +446,7 @@ "Added {string1} of {string2} to the shopping list \"{string3}\"": "Added {string1} of {string2} to the shopping list \"{string3}\"", "Output": "Output", "Energy (kcal)": "Energy (kcal)", - "Per stock quantity unit": "Per stock quantity unit", + "Per stock quantity unit": "", "Barcode scanner testing": "Barcode scanner testing", "Expected barcode": "Expected barcode", "Scan field": "Scan field", @@ -475,7 +475,7 @@ "There are no units available at this location": "There are no units available at this location", "Amount: {string1}; Due on {string2}; Bought on {string3}": "", "Transfered {string1} of {string2} from {string3} to {string4}": "Transfered {string1} of {string2} from {string3} to {string4}", - "Stock entries": "Stock entries", + "Stock entries": "", "Best before date": "Best before date", "Purchased date": "Purchased date", "Consume all {string0} for this stock entry": "Consume all {string0} for this stock entry", @@ -531,7 +531,7 @@ "Edit this item": "Edit this item", "Delete this item": "Delete this item", "Show an icon if the product is already on the shopping list": "Show an icon if the product is already on the shopping list", - "Calories": "Calories", + "Calories": "", "means {string1} per {string2}": "means {string1} per {string2}", "Create inverse QU conversion": "Create inverse QU conversion", "Create recipe": "Create recipe", @@ -628,7 +628,7 @@ "{count} product is due | {count} products are due": " | ", "Due date": "", "Never overdue": "", - "{count} product is expired | {count} products are expired": " | ", + "{count} product is expired | {count} products are expired": "", "Expired": "", "Due soon days": "", "Add overdue/expired products": "", @@ -901,5 +901,30 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" + }, + "{count} product is below defined min": { + " stock amount | {count} products are below defined min": { + " stock amount": "" + } } } \ No newline at end of file diff --git a/locale/es.json b/locale/es.json index 50efa5d6..ae4fc26a 100644 --- a/locale/es.json +++ b/locale/es.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/fi.json b/locale/fi.json index 74e5711c..a5f41341 100644 --- a/locale/fi.json +++ b/locale/fi.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/fr.json b/locale/fr.json index bbf2ff49..48b8e336 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/he_IL.json b/locale/he_IL.json index 8056c151..a025486c 100644 --- a/locale/he_IL.json +++ b/locale/he_IL.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/hu.json b/locale/hu.json index 8541a756..cd25438f 100644 --- a/locale/hu.json +++ b/locale/hu.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/it.json b/locale/it.json index eeaa7c84..1218a6a8 100644 --- a/locale/it.json +++ b/locale/it.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/ja.json b/locale/ja.json index 420cca5f..b3e1677f 100644 --- a/locale/ja.json +++ b/locale/ja.json @@ -836,5 +836,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/ko_KR.json b/locale/ko_KR.json index b5f49198..0355d747 100644 --- a/locale/ko_KR.json +++ b/locale/ko_KR.json @@ -875,5 +875,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/nl.json b/locale/nl.json index 9507feea..5644f921 100644 --- a/locale/nl.json +++ b/locale/nl.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/no.json b/locale/no.json index 6070fdbf..c287ec7b 100644 --- a/locale/no.json +++ b/locale/no.json @@ -845,5 +845,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/pl.json b/locale/pl.json index f7f7efe6..43618c94 100644 --- a/locale/pl.json +++ b/locale/pl.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/pt_BR.json b/locale/pt_BR.json index a22ead91..4c2851ee 100644 --- a/locale/pt_BR.json +++ b/locale/pt_BR.json @@ -845,5 +845,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/pt_PT.json b/locale/pt_PT.json index a7bf9086..ab9ec6e2 100644 --- a/locale/pt_PT.json +++ b/locale/pt_PT.json @@ -759,5 +759,29 @@ "This activates night mode between {start} and {end} the same day": { "": "" }, - "{string0} total value": "" + "{string0} total value": "", + "{count} product is overdue | {count} products are overdue": "", + "{count} product is expired | {count} products are expired": "", + "Value": "", + "Next due date": "", + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" + } } \ No newline at end of file diff --git a/locale/ru.json b/locale/ru.json index 5a273309..2ca3b760 100644 --- a/locale/ru.json +++ b/locale/ru.json @@ -840,5 +840,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/sk_SK.json b/locale/sk_SK.json index 3ac7dca0..defe9f5f 100644 --- a/locale/sk_SK.json +++ b/locale/sk_SK.json @@ -875,5 +875,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/sv_SE.json b/locale/sv_SE.json index 8788fe53..b6dfb339 100644 --- a/locale/sv_SE.json +++ b/locale/sv_SE.json @@ -875,5 +875,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/ta.json b/locale/ta.json index 5ce8cd43..25e8cc18 100644 --- a/locale/ta.json +++ b/locale/ta.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/tr.json b/locale/tr.json index a8e9a9c6..1e53b19d 100644 --- a/locale/tr.json +++ b/locale/tr.json @@ -515,5 +515,33 @@ "This activates night mode between {start} and {end} the same day": { "": "" }, - "{string0} total value": "" + "{string0} total value": "", + "Stock entries": "", + "Location Content Sheet": "", + "{count} product is overdue | {count} products are overdue": "", + "{count} product is expired | {count} products are expired": "", + "Value": "", + "Next due date": "", + "Calories": "", + "Per stock quantity unit": "", + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" + } } \ No newline at end of file diff --git a/locale/zh_CN.json b/locale/zh_CN.json index 72711367..011806f6 100644 --- a/locale/zh_CN.json +++ b/locale/zh_CN.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file diff --git a/locale/zh_TW.json b/locale/zh_TW.json index 098d1052..02c6274c 100644 --- a/locale/zh_TW.json +++ b/locale/zh_TW.json @@ -901,5 +901,25 @@ }, "This activates night mode between {start} and {end} the same day": { "": "" + }, + "Starts With": "", + "Ends With": "", + "Equals": "", + "Not Equals": "", + "Contains": "", + "Not Contains": "", + "Less Than": "", + "Less Than Or Equal": "", + "Equal": "", + "Greater Than Or Equal": "", + "Greater Than": "", + "Before": "", + "Before Or On": "", + "On Day": "", + "Not On Day": "", + "After Or On": "", + "After": "", + "item": { + "label": "" } } \ No newline at end of file From ddc2dad07ec26f854cca78bbdbec92b2213ad235 Mon Sep 17 00:00:00 2001 From: Katharina Bogad Date: Thu, 8 Jul 2021 08:27:55 +0200 Subject: [PATCH 14/16] Recipes View --- changelog/62_UNRELEASED_xxxx-xx-xx.md | 4 + js/App.vue | 7 +- js/api/RecipesApi.ts | 65 +++ js/api/StockApi.ts | 13 + js/api/index.ts | 4 + js/components/App/Profile.vue | 4 +- js/components/App/QuickUserSettings.vue | 26 +- js/components/App/TopBar.vue | 2 +- js/components/Recipes/RecipeDisplayBody.vue | 100 +++++ .../Recipes/StockFulfillmentStatus.vue | 58 +++ js/components/Stock/Productpicker.vue | 17 + js/locale.ts | 12 +- js/main.ts | 24 +- js/pages/Recipes/Edit.vue | 89 ++++ js/pages/Recipes/Recipes.vue | 397 ++++++++++++++++++ js/pages/Stock/Overview.vue | 26 +- js/router.ts | 16 + js/store/index.ts | 23 +- js/store/interfaces.ts | 16 +- js/store/mutations.ts | 1 + js/types/Recipe.ts | 47 +++ js/types/Stock.ts | 44 ++ package.json | 2 + php/Controllers/RecipesApiController.php | 119 ++++++ php/Controllers/StockApiController.php | 48 +++ php/Services/RecipesService.php | 36 ++ php/Services/StockService.php | 42 ++ php/routes.php | 2 + rollup.vue.js | 3 + scss/sigma/_overrides.scss | 58 +++ scss/sigma/sass/_typography.scss | 51 ++- yarn.lock | 94 ++++- 32 files changed, 1396 insertions(+), 54 deletions(-) create mode 100644 js/api/RecipesApi.ts create mode 100644 js/components/Recipes/RecipeDisplayBody.vue create mode 100644 js/components/Recipes/StockFulfillmentStatus.vue create mode 100644 js/components/Stock/Productpicker.vue create mode 100644 js/pages/Recipes/Edit.vue create mode 100644 js/pages/Recipes/Recipes.vue create mode 100644 js/types/Recipe.ts create mode 100644 js/types/Stock.ts diff --git a/changelog/62_UNRELEASED_xxxx-xx-xx.md b/changelog/62_UNRELEASED_xxxx-xx-xx.md index 5fac861b..a747bd94 100644 --- a/changelog/62_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/62_UNRELEASED_xxxx-xx-xx.md @@ -5,6 +5,10 @@ In between grocy v3.0.1 and this version, we forked the project to not-grocy. This moved a lot of code around. - Made not-grocy much faster. +- New frontend URLs: Everything is handled through vue-router. Old URLs are not neccessarily the valid anymore. + While this mostly applies to object editing, we can't promise that all your bookmarks work. +- New and modern look and feel. +- Some new API endpoints that are easier to use than the raw object API. ### New feature: (Own) Product and stock entry labels/barcodes ("grocycode") - Print own labels/barcodes for products and/or every stock entry and then scan that code on every place a product or stock entry can be selected diff --git a/js/App.vue b/js/App.vue index 4d85739b..906be710 100644 --- a/js/App.vue +++ b/js/App.vue @@ -1,5 +1,6 @@ diff --git a/js/components/Stock/WebcamBarcodeScanner.vue b/js/components/Stock/WebcamBarcodeScanner.vue new file mode 100644 index 00000000..b5107f12 --- /dev/null +++ b/js/components/Stock/WebcamBarcodeScanner.vue @@ -0,0 +1,356 @@ + + + \ No newline at end of file diff --git a/js/lib/grocycode.ts b/js/lib/grocycode.ts new file mode 100644 index 00000000..6aff0bf7 --- /dev/null +++ b/js/lib/grocycode.ts @@ -0,0 +1,16 @@ +export const GROCYCODE_PRODUCT = 'p'; +export const GROCYCODE_CHORE = 'c'; +export const GROCYCODE_BATTERY = 'b'; + +export const allGrocycodes = [GROCYCODE_PRODUCT, GROCYCODE_CHORE, GROCYCODE_BATTERY]; + +export function decodeGrocycode(code: string) : { valid: boolean, type?: string, id?: number, extraData?: Array } +{ + const parts = code.split(':'); + + if (parts[0] != "grcy") return { valid: false }; + if (!allGrocycodes.includes(parts[1])) return { valid: false }; + if (!parts[2].match(/\d+/i)) return { valid: false }; + + return { valid: true, type: parts[1], id: parseInt(parts[2]), extraData: parts.slice(3) }; +} \ No newline at end of file diff --git a/js/main.ts b/js/main.ts index d6a00cff..e1e3698f 100644 --- a/js/main.ts +++ b/js/main.ts @@ -32,6 +32,7 @@ import Toast from 'primevue/toast'; import ProgressSpinner from 'primevue/progressspinner'; import Dropdown from 'primevue/dropdown'; import Dialog from 'primevue/dialog'; +import { ENSURE_PRODUCTS_LOADED } from './store/actions'; const app = createApp(App); const i18n = setupI18n(); @@ -78,6 +79,7 @@ if (FilterService.register !== undefined) api.System.GetConfig().then((config) => { store.commit(LOAD_CONFIG, config); + store.dispatch(ENSURE_PRODUCTS_LOADED); const promises = [loadLocaleMessages(i18n, "en", store)]; let setLanguage = "en"; if (store.state.Settings !== undefined && store.state.Settings.Locale != "en") diff --git a/js/pages/Recipes/Edit.vue b/js/pages/Recipes/Edit.vue index 80047db4..3c3c549e 100644 --- a/js/pages/Recipes/Edit.vue +++ b/js/pages/Recipes/Edit.vue @@ -1,4 +1,5 @@