From 12e5377c40094ad0d70c964c83b5ce9934f3c986 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sun, 6 Feb 2022 21:09:34 +0100 Subject: [PATCH] Split application translation strings and QU strings (fixes #1705) --- changelog/66_UNRELEASED_xxxx-xx-xx.md | 1 + controllers/BaseController.php | 5 ++- public/js/grocy.js | 12 +++++- .../viewjs/components/productamountpicker.js | 2 +- public/viewjs/components/productcard.js | 4 +- public/viewjs/consume.js | 6 +-- public/viewjs/inventory.js | 6 +-- public/viewjs/mealplan.js | 6 +-- public/viewjs/productform.js | 2 +- public/viewjs/purchase.js | 2 +- public/viewjs/quantityunitconversionform.js | 4 +- public/viewjs/quantityunitpluraltesting.js | 2 +- public/viewjs/shoppinglistitemform.js | 6 +-- public/viewjs/stockentries.js | 10 ++--- public/viewjs/stockoverview.js | 6 +-- public/viewjs/transfer.js | 4 +- services/LocalizationService.php | 40 ++++++++++++------- views/layout/default.blade.php | 1 + views/locationcontentsheet.blade.php | 2 +- views/productform.blade.php | 2 +- views/recipeform.blade.php | 2 +- views/shoppinglist.blade.php | 6 +-- views/stockentries.blade.php | 2 +- views/stockjournal.blade.php | 2 +- views/stockjournalsummary.blade.php | 2 +- views/stockoverview.blade.php | 2 +- 26 files changed, 81 insertions(+), 58 deletions(-) diff --git a/changelog/66_UNRELEASED_xxxx-xx-xx.md b/changelog/66_UNRELEASED_xxxx-xx-xx.md index f5ca33d57..aa8a5ddac 100644 --- a/changelog/66_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/66_UNRELEASED_xxxx-xx-xx.md @@ -11,6 +11,7 @@ - Fixed that when adding missing recipe ingredients, with the option "Only check if any amount is in stock" enabled, to the shopping list, unit conversions (if any) weren't considered - Fixed that the recipe stock fulfillment information about shopping list amounts was not correct when the ingredient had a decimal amount - Fixed that the meal plan showed the total calories per recipe (instead of per serving as stated by the suffix) +- Fixed that when having a quantity unit matching any application string, the translation of that string was used to display that unit - Fixed that formatted (HTML) text for the (hidden by default) product description column on the stock overview page was not correctly displayed - Fixed that numeric and date-time sorting of table columns on the stock entries page did not work correctly (thanks @MasterofJOKers) - Fixed that the barcode lookup for the "Stock by-barcode" API endpoints was case sensitive diff --git a/controllers/BaseController.php b/controllers/BaseController.php index 9d14e15d1..59a65a378 100644 --- a/controllers/BaseController.php +++ b/controllers/BaseController.php @@ -126,10 +126,11 @@ protected function render($response, $page, $data = []) $this->View->set('__t', function (string $text, ...$placeholderValues) use ($localizationService) { return $localizationService->__t($text, $placeholderValues); }); - $this->View->set('__n', function ($number, $singularForm, $pluralForm) use ($localizationService) { - return $localizationService->__n($number, $singularForm, $pluralForm); + $this->View->set('__n', function ($number, $singularForm, $pluralForm, $isQu = false) use ($localizationService) { + return $localizationService->__n($number, $singularForm, $pluralForm, $isQu); }); $this->View->set('LocalizationStrings', $localizationService->GetPoAsJsonString()); + $this->View->set('LocalizationStringsQu', $localizationService->GetPoAsJsonStringQu()); // TODO: Better handle this generically based on the current language (header in .po file?) $dir = 'ltr'; diff --git a/public/js/grocy.js b/public/js/grocy.js index a896fba98..4557f7467 100644 --- a/public/js/grocy.js +++ b/public/js/grocy.js @@ -232,6 +232,7 @@ U = function(relativePath) } Grocy.Translator = new Translator(Grocy.LocalizationStrings); +Grocy.TranslatorQu = new Translator(Grocy.LocalizationStringsQu); __t = function(text, ...placeholderValues) { if (Grocy.Mode === "dev") @@ -245,7 +246,7 @@ __t = function(text, ...placeholderValues) return Grocy.Translator.__(text, ...placeholderValues) } -__n = function(number, singularForm, pluralForm) +__n = function(number, singularForm, pluralForm, isQu = false) { if (Grocy.Mode === "dev") { @@ -256,7 +257,14 @@ __n = function(number, singularForm, pluralForm) } } - return Grocy.Translator.n__(singularForm, pluralForm, Math.abs(number), Math.abs(number)) + if (isQu) + { + return Grocy.TranslatorQu.n__(singularForm, pluralForm, Math.abs(number), Math.abs(number)) + } + else + { + return Grocy.Translator.n__(singularForm, pluralForm, Math.abs(number), Math.abs(number)) + } } if (!Grocy.ActiveNav.isEmpty()) diff --git a/public/viewjs/components/productamountpicker.js b/public/viewjs/components/productamountpicker.js index 1f75221fa..bb085286c 100644 --- a/public/viewjs/components/productamountpicker.js +++ b/public/viewjs/components/productamountpicker.js @@ -94,7 +94,7 @@ $(".input-group-productamountpicker").on("change", function() var quFactor = $("#qu_id option:selected").attr("data-qu-factor"); var amount = $("#display_amount").val(); var destinationAmount = amount / quFactor; - var destinationQuName = __n(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural")) + var destinationQuName = __n(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural"), true); if ($("#qu_id").attr("data-destination-qu-name") == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled || amount.toString().isEmpty() || selectedQuName.toString().isEmpty()) { diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js index 97436a250..20812f4eb 100644 --- a/public/viewjs/components/productcard.js +++ b/public/viewjs/components/productcard.js @@ -11,7 +11,7 @@ Grocy.Components.ProductCard.Refresh = function(productId) $('#productcard-product-name').text(productDetails.product.name); $('#productcard-product-description').html(productDetails.product.description); $('#productcard-product-stock-amount').text(stockAmount); - $('#productcard-product-stock-qu-name').text(__n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); + $('#productcard-product-stock-qu-name').text(__n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true)); $('#productcard-product-stock-value').text(stockValue + ' ' + Grocy.Currency); $('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10)); $('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31'); @@ -26,7 +26,7 @@ Grocy.Components.ProductCard.Refresh = function(productId) if (productDetails.is_aggregated_amount == 1) { $('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated); - $('#productcard-product-stock-qu-name-aggregated').text(__n(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); + $('#productcard-product-stock-qu-name-aggregated').text(__n(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true)); if (productDetails.stock_amount_opened_aggregated > 0) { diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index 49f7f5b39..f8d9c4318 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -77,11 +77,11 @@ if (productDetails.product.enable_tare_weight_handling == 1 && !jsonData.exact_amount) { - var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount - (parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount))) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + ''; + var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount - (parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount))) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '
' + __t("Undo") + ''; } else { - var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + ''; + var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '
' + __t("Undo") + ''; } if (GetUriParam("embedded") !== undefined) @@ -177,7 +177,7 @@ $('#save-mark-as-open-button').on('click', function(e) } Grocy.FrontendHelpers.EndUiBusy("consume-form"); - toastr.success(__t('Marked %1$s of %2$s as opened', parseFloat(jsonForm.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + ''); + toastr.success(__t('Marked %1$s of %2$s as opened', parseFloat(jsonForm.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '
' + __t("Undo") + ''); if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount)) { diff --git a/public/viewjs/inventory.js b/public/viewjs/inventory.js index 38e7a6daf..0500ab314 100644 --- a/public/viewjs/inventory.js +++ b/public/viewjs/inventory.js @@ -114,7 +114,7 @@ Grocy.Api.Get('stock/products/' + jsonForm.product_id, function(result) { - var successMessage = __t('Stock amount of %1$s is now %2$s', result.product.name, result.stock_amount + " " + __n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural)) + '
' + __t("Undo") + ''; + var successMessage = __t('Stock amount of %1$s is now %2$s', result.product.name, result.stock_amount + " " + __n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true)) + '
' + __t("Undo") + ''; if (GetUriParam("embedded") !== undefined) { @@ -384,7 +384,7 @@ $('#display_amount').on('keyup', function(e) } else if (newAmount > productStockAmount + containerWeight) { - $('#inventory-change-info').text(__t('This means %s will be added to stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural))); + $('#inventory-change-info').text(__t('This means %s will be added to stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true))); Grocy.Components.DateTimePicker.GetInputElement().attr('required', ''); if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) { @@ -393,7 +393,7 @@ $('#display_amount').on('keyup', function(e) } else if (newAmount < productStockAmount + containerWeight) { - $('#inventory-change-info').text(__t('This means %s will be removed from stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural))); + $('#inventory-change-info').text(__t('This means %s will be removed from stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true))); Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required'); if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) { diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 71b3b67e6..91fcbadd8 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -252,13 +252,13 @@ $(".calendar").each(function() element.html('\
\ \ -
' + mealPlanEntry.product_amount + " " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural) + '
\ +
' + mealPlanEntry.product_amount + " " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true) + '
\
' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '
\ ' + costsAndCaloriesPerServing + ' \
\ \ \ - \ + \ ' + shoppingListButtonHtml + ' \ ' + doneButtonHtml + ' \
\ @@ -800,7 +800,7 @@ $(document).on('click', '.product-consume-button', function(e) Grocy.Api.Get('stock/products/' + productId, function(result) { - var toastMessage = __t('Removed %1$s of %2$s from stock', consumeAmount.toString() + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '
' + __t("Undo") + ''; + var toastMessage = __t('Removed %1$s of %2$s from stock', consumeAmount.toString() + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '
' + __t("Undo") + ''; Grocy.Api.Put('objects/meal_plan/' + mealPlanEntryId, { "done": 1 }, function(result) diff --git a/public/viewjs/productform.js b/public/viewjs/productform.js index 882c2a575..ab5df14f0 100644 --- a/public/viewjs/productform.js +++ b/public/viewjs/productform.js @@ -177,7 +177,7 @@ $('.input-group-qu').on('change', function(e) if (factor > 1 || quIdPurchase != quIdStock) { - $('#qu-conversion-info').text(__t('This means 1 %1$s purchased will be converted into %2$s %3$s in stock', $("#qu_id_purchase option:selected").text(), (1 * factor).toString(), __n((1 * factor).toString(), $("#qu_id_stock option:selected").text(), $("#qu_id_stock option:selected").data("plural-form")))); + $('#qu-conversion-info').text(__t('This means 1 %1$s purchased will be converted into %2$s %3$s in stock', $("#qu_id_purchase option:selected").text(), (1 * factor).toString(), __n((1 * factor).toString(), $("#qu_id_stock option:selected").text(), $("#qu_id_stock option:selected").data("plural-form"), true))); $('#qu-conversion-info').removeClass('d-none'); } else diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js index bd51110c4..c340fba39 100644 --- a/public/viewjs/purchase.js +++ b/public/viewjs/purchase.js @@ -115,7 +115,7 @@ $('#save-purchase-button').on('click', function(e) { amountMessage = parseFloat(jsonForm.amount) - parseFloat(productDetails.stock_amount) - parseFloat(productDetails.product.tare_weight); } - var successMessage = __t('Added %1$s of %2$s to stock', amountMessage + " " + __n(amountMessage, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + __t("Undo") + ''; + var successMessage = __t('Added %1$s of %2$s to stock', amountMessage + " " + __n(amountMessage, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '
' + __t("Undo") + ''; if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) { diff --git a/public/viewjs/quantityunitconversionform.js b/public/viewjs/quantityunitconversionform.js index 61bec4458..7f206d4b1 100644 --- a/public/viewjs/quantityunitconversionform.js +++ b/public/viewjs/quantityunitconversionform.js @@ -179,12 +179,12 @@ $('.input-group-qu').on('change', function(e) if (fromQuId && toQuId) { - $('#qu-conversion-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#from_qu_id option:selected").text(), parseFloat((1 * factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 * factor).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), $("#to_qu_id option:selected").text(), $("#to_qu_id option:selected").data("plural-form")))); + $('#qu-conversion-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#from_qu_id option:selected").text(), parseFloat((1 * factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 * factor).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), $("#to_qu_id option:selected").text(), $("#to_qu_id option:selected").data("plural-form"), true))); $('#qu-conversion-info').removeClass('d-none'); if (Grocy.EditMode === 'create') { - $('#qu-conversion-inverse-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#to_qu_id option:selected").text(), parseFloat((1 / factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 / factor).toString(), $("#from_qu_id option:selected").text(), $("#from_qu_id option:selected").data("plural-form")))); + $('#qu-conversion-inverse-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#to_qu_id option:selected").text(), parseFloat((1 / factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 / factor).toString(), $("#from_qu_id option:selected").text(), $("#from_qu_id option:selected").data("plural-form"), true))); $('#qu-conversion-inverse-info').removeClass('d-none'); } } diff --git a/public/viewjs/quantityunitpluraltesting.js b/public/viewjs/quantityunitpluraltesting.js index 1e4169afc..0ee08c168 100644 --- a/public/viewjs/quantityunitpluraltesting.js +++ b/public/viewjs/quantityunitpluraltesting.js @@ -25,7 +25,7 @@ function RefreshQuPluralTestingResult() } animateCSS("h2", "shake"); - $("#result").text(__n(amount, singularForm, pluralForm)); + $("#result").text(__n(amount, singularForm, pluralForm, true)); } if (GetUriParam("qu") !== undefined) diff --git a/public/viewjs/shoppinglistitemform.js b/public/viewjs/shoppinglistitemform.js index ab201a313..3e5fcf64d 100644 --- a/public/viewjs/shoppinglistitemform.js +++ b/public/viewjs/shoppinglistitemform.js @@ -59,7 +59,7 @@ $('#save-shoppinglist-button').on('click', function(e) Grocy.Api.Get('stock/products/' + jsonData.product_id, function(productDetails) { - window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); + window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl); }, @@ -96,7 +96,7 @@ $('#save-shoppinglist-button').on('click', function(e) Grocy.Api.Get('stock/products/' + jsonData.product_id, function(productDetails) { - window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); + window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl); }, @@ -138,7 +138,7 @@ $('#save-shoppinglist-button').on('click', function(e) Grocy.Api.Get('stock/products/' + jsonData.product_id, function(productDetails) { - window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural")), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); + window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl); window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl); }, diff --git a/public/viewjs/stockentries.js b/public/viewjs/stockentries.js index 4b3fdd6f9..4a89fdd44 100644 --- a/public/viewjs/stockentries.js +++ b/public/viewjs/stockentries.js @@ -4,12 +4,12 @@ { 'orderable': false, 'targets': 0 }, { 'searchable': false, "targets": 0 }, { 'visible': false, 'targets': 10 }, - { "type": "num", "targets": 1}, + { "type": "num", "targets": 1 }, { "type": "num", "targets": 3 }, { "type": "html", "targets": 4 }, - { "type": "html-num-fmt", "targets": 7}, - { "type": "html", "targets": 8}, - { "type": "html", "targets": 9} + { "type": "html-num-fmt", "targets": 7 }, + { "type": "html", "targets": 8 }, + { "type": "html", "targets": 9 } ].concat($.fn.dataTable.defaults.columnDefs) }); $('#stockentries-table tbody').removeClass("d-none"); @@ -67,7 +67,7 @@ $(document).on('click', '.stock-consume-button', function(e) Grocy.Api.Get('stock/products/' + productId, function(result) { - var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '
' + __t("Undo") + ''; + var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '
' + __t("Undo") + ''; if (wasSpoiled) { toastMessage += " (" + __t("Spoiled") + ")"; diff --git a/public/viewjs/stockoverview.js b/public/viewjs/stockoverview.js index 4dc03eacc..54c56f78e 100755 --- a/public/viewjs/stockoverview.js +++ b/public/viewjs/stockoverview.js @@ -145,11 +145,11 @@ $(document).on('click', '.product-consume-button', function(e) { if (result.product.enable_tare_weight_handling == 1) { - var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(originalTotalStockAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '
' + __t("Undo") + ''; + var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(originalTotalStockAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '
' + __t("Undo") + ''; } else { - var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '
' + __t("Undo") + ''; + var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '
' + __t("Undo") + ''; } if (wasSpoiled) @@ -328,7 +328,7 @@ function RefreshProductRow(productId) { animateCSS("#product-" + productId + "-row td:not(:first)", "shake"); - $('#product-' + productId + '-qu-name').text(__n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural)); + $('#product-' + productId + '-qu-name').text(__n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true)); $('#product-' + productId + '-amount').text(result.stock_amount); $('#product-' + productId + '-consume-all-button').attr('data-consume-amount', result.stock_amount); $('#product-' + productId + '-value').text(result.stock_value); diff --git a/public/viewjs/transfer.js b/public/viewjs/transfer.js index 74126afc4..4ad149ee7 100644 --- a/public/viewjs/transfer.js +++ b/public/viewjs/transfer.js @@ -56,11 +56,11 @@ if (productDetails.product.enable_tare_weight_handling == 1) { - var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount - parseFloat(productDetails.product.tare_weight)) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + ''; + var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount - parseFloat(productDetails.product.tare_weight)) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + ''; } else { - var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + ''; + var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + ''; } if (GetUriParam("embedded") !== undefined) diff --git a/services/LocalizationService.php b/services/LocalizationService.php index dbd5b4171..16b27ae6c 100644 --- a/services/LocalizationService.php +++ b/services/LocalizationService.php @@ -17,21 +17,21 @@ public function __construct(string $culture) protected $Po; - protected $PoUserStrings; - protected $Pot; protected $PotMain; protected $Translator; + protected $TranslatorQU; + private static $instanceMap = []; public function CheckAndAddMissingTranslationToPot($text) { if (GROCY_MODE === 'dev') { - if ($this->Pot->find('', $text) === false && $this->PoUserStrings->find('', $text) === false && empty($text) === false) + if ($this->Pot->find('', $text) === false && empty($text) === false) { $translation = new Translation('', $text); $this->PotMain[] = $translation; @@ -69,11 +69,23 @@ public function GetPoAsJsonString() return $this->Po->toJsonString(); } - public function __n($number, $singularForm, $pluralForm) + public function GetPoAsJsonStringQu() + { + return $this->PoQu->toJsonString(); + } + + public function __n($number, $singularForm, $pluralForm, $isQu = false) { $this->CheckAndAddMissingTranslationToPot($singularForm); - return sprintf($this->Translator->ngettext($singularForm, $pluralForm, abs(floatval($number))), $number); + if ($isQu) + { + return sprintf($this->TranslatorQU->ngettext($singularForm, $pluralForm, abs(floatval($number))), $number); + } + else + { + return sprintf($this->Translator->ngettext($singularForm, $pluralForm, abs(floatval($number))), $number); + } } public function __t($text, ...$placeholderValues) @@ -140,9 +152,6 @@ private function LoadLocalizations() } } - $this->PoUserStrings = new Translations(); - $this->PoUserStrings->setDomain('grocy/userstrings'); - $this->Po = Translations::fromPoFile(__DIR__ . "/../localization/$culture/strings.po"); if (file_exists(__DIR__ . "/../localization/$culture/chore_assignment_types.po")) @@ -185,6 +194,9 @@ private function LoadLocalizations() $this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/demo_data.po")); } + $this->Translator = new Translator(); + $this->Translator->loadTranslations($this->Po); + $quantityUnits = null; try { @@ -197,6 +209,8 @@ private function LoadLocalizations() if ($quantityUnits !== null) { + $this->PoQu = new Translations(); + foreach ($quantityUnits as $quantityUnit) { $translation = new Translation('', $quantityUnit['name']); @@ -204,13 +218,11 @@ private function LoadLocalizations() $translation->setPlural($quantityUnit['name_plural']); $translation->setPluralTranslations(preg_split('/\r\n|\r|\n/', $quantityUnit['plural_forms'])); - $this->PoUserStrings[] = $translation; + $this->PoQu[] = $translation; } - $this->Po = $this->Po->mergeWith($this->PoUserStrings); - } - - $this->Translator = new Translator(); - $this->Translator->loadTranslations($this->Po); + $this->TranslatorQU = new Translator(); + $this->TranslatorQU->loadTranslations($this->PoQu); + }; } } diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php index c0057c252..e1b1377a4 100644 --- a/views/layout/default.blade.php +++ b/views/layout/default.blade.php @@ -95,6 +95,7 @@ Grocy.CalendarFirstDayOfWeek = '{{ GROCY_CALENDAR_FIRST_DAY_OF_WEEK }}'; Grocy.CalendarShowWeekNumbers = {{ BoolToString(GROCY_CALENDAR_SHOW_WEEK_OF_YEAR) }}; Grocy.LocalizationStrings = {!! $LocalizationStrings !!}; + Grocy.LocalizationStringsQu = {!! $LocalizationStringsQu !!}; Grocy.FeatureFlags = {!! json_encode($featureFlags) !!}; Grocy.Webhooks = { @if(GROCY_FEATURE_FLAG_LABEL_PRINTER && !GROCY_LABEL_PRINTER_RUN_SERVER) diff --git a/views/locationcontentsheet.blade.php b/views/locationcontentsheet.blade.php index 5f27ca1e2..73e991251 100644 --- a/views/locationcontentsheet.blade.php +++ b/views/locationcontentsheet.blade.php @@ -89,7 +89,7 @@ class="d-none d-print-flex mx-auto"> {{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }} - {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }} + {{ $currentStockEntry->amount }} {{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural, true) }} @if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif diff --git a/views/productform.blade.php b/views/productform.blade.php index 749617ca0..9f48bffa3 100644 --- a/views/productform.blade.php +++ b/views/productform.blade.php @@ -717,7 +717,7 @@ class="table table-sm table-striped nowrap w-100"> @endif - {!! $__t('This means 1 %1$s is the same as %2$s %3$s', FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->from_qu_id)->name, '' . $quConversion->factor . '', $__n($quConversion->factor, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name_plural)) !!} + {!! $__t('This means 1 %1$s is the same as %2$s %3$s', FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->from_qu_id)->name, '' . $quConversion->factor . '', $__n($quConversion->factor, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name_plural, true)) !!} @endif diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index 27275fd6d..bd2d3a1ad 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -197,7 +197,7 @@ class="table table-sm table-striped nowrap w-100"> @else @if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif @endif - {{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural) }} + {{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural, true) }} @if(!empty($recipePosition->variable_amount))
{{ $__t('Variable amount') }}
diff --git a/views/shoppinglist.blade.php b/views/shoppinglist.blade.php index d7e4d6f4e..122067380 100644 --- a/views/shoppinglist.blade.php +++ b/views/shoppinglist.blade.php @@ -242,7 +242,7 @@ class="@if(FindObjectInArrayByPropertyValue($missingProducts, 'id', $listItem->p @endif amount }}> - {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural) }}@endif + {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}@endif @if(!empty($listItem->product_group_name)) {{ $listItem->product_group_name }} @else {{ $__t('Ungrouped') }} @endif @@ -376,7 +376,7 @@ class="table table-sm table-striped nowrap"> @if(!empty($listItem->product_id)) {{ $listItem->product_name }}
@endif{!! nl2br($listItem->note) !!} - {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural) }}@endif + {{ $listItem->amount }} @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}@endif @if(!empty($listItem->product_group_name)) {{ $listItem->product_group_name }} @else {{ $__t('Ungrouped') }} @endif @@ -400,7 +400,7 @@ class="table table-sm table-striped nowrap">