diff --git a/.tool_version b/.tool_version index 709c55f..73d7467 100644 --- a/.tool_version +++ b/.tool_version @@ -1 +1 @@ -1.16.4 \ No newline at end of file +1.17.0 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6335642..3e2dbfa 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG OPENGREP_VERSION=v1.16.4 +ARG OPENGREP_VERSION=v1.17.0 # Build codacy-opengrep wrapper FROM golang:1.23-alpine3.21 AS builder diff --git a/docs/codacy-rules-i18n.yaml b/docs/codacy-rules-i18n.yaml index 7d36c36..8699455 100644 --- a/docs/codacy-rules-i18n.yaml +++ b/docs/codacy-rules-i18n.yaml @@ -432,3 +432,109 @@ rules: impact: MEDIUM confidence: LOW likelihood: HIGH + + - id: codacy.python.i18n.no-hardcoded-print-concat + severity: WARNING + languages: + - python + pattern-either: + - pattern: print("..." + ...) + - pattern: print(... + "...") + message: >- + Avoid hardcoded or concatenated strings in print. Use an i18n translation function (e.g., _("key")) with .format() or f-strings. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded string literals concatenated in print calls to enforce localization + technology: + - python + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.python.i18n.no-hardcoded-strftime + severity: WARNING + languages: + - python + pattern: $X.strftime("...") + message: >- + Avoid hardcoded date format strings in strftime. Use locale.nl_langinfo(locale.D_FMT) or similar locale-aware formatting. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded date format strings passed to strftime to enforce locale-aware date formatting + technology: + - python + impact: MEDIUM + confidence: HIGH + likelihood: HIGH + + - id: codacy.python.i18n.no-hardcoded-number-format + severity: WARNING + languages: + - python + pattern-regex: ":\\.[0-9]+f" + message: >- + Avoid using :.Nf format specifiers for user-visible number formatting. Use locale.currency() or locale.format_string() for locale-aware formatting. + metadata: + category: codestyle + subcategory: i18n + description: Flags :.Nf format specifiers in f-strings used for user-visible numbers instead of locale-aware formatting + technology: + - python + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.cpp.i18n.no-hardcoded-cout + severity: WARNING + languages: + - cpp + pattern: std::cout << "$MSG" + message: >- + Avoid hardcoded strings in std::cout. Use a localization function or resource bundle for user-facing output. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded string literals streamed directly to std::cout to enforce localization + technology: + - cpp + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.cpp.i18n.no-hardcoded-strftime + severity: WARNING + languages: + - cpp + pattern-either: + - pattern: std::strftime($BUF, $SIZE, "$FMT", $TIME); + - pattern: strftime($BUF, $SIZE, "$FMT", $TIME); + message: >- + Avoid hardcoded date format strings in strftime. Use locale-aware date formatting (e.g., std::put_time with a locale-imbued stream). + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded date format strings passed to strftime to enforce locale-aware date formatting + technology: + - cpp + impact: MEDIUM + confidence: HIGH + likelihood: HIGH + + - id: codacy.cpp.i18n.no-hardcoded-number-format + severity: WARNING + languages: + - cpp + pattern: std::setprecision($N) + message: >- + Avoid using std::setprecision for user-visible number formatting. Imbue the stream with a locale and use std::use_facet for locale-aware output. + metadata: + category: codestyle + subcategory: i18n + description: Flags std::setprecision used for user-visible number formatting instead of locale-aware alternatives + technology: + - cpp + impact: MEDIUM + confidence: LOW + likelihood: HIGH diff --git a/docs/multiple-tests/i18n/patterns.xml b/docs/multiple-tests/i18n/patterns.xml index 3f7d685..d57e38c 100644 --- a/docs/multiple-tests/i18n/patterns.xml +++ b/docs/multiple-tests/i18n/patterns.xml @@ -16,4 +16,10 @@ + + + + + + diff --git a/docs/multiple-tests/i18n/results.xml b/docs/multiple-tests/i18n/results.xml index 194728d..5d6ef61 100644 --- a/docs/multiple-tests/i18n/results.xml +++ b/docs/multiple-tests/i18n/results.xml @@ -315,4 +315,56 @@ message="Avoid hardcoded strings in Map.put(). Use ResourceBundle.getString() or a localization key for user-facing messages." severity="warning" /> + + + + + + + + + + + + + + + + + + + + diff --git a/docs/multiple-tests/i18n/src/Sample.py b/docs/multiple-tests/i18n/src/Sample.py new file mode 100644 index 0000000..eafd9e5 --- /dev/null +++ b/docs/multiple-tests/i18n/src/Sample.py @@ -0,0 +1,83 @@ +import datetime +import locale +import gettext + +# Set up gettext (only English & French, but French translations missing some keys) +locales = { + "en": gettext.translation("messages", localedir="locales", languages=["en"], fallback=True), + "fr": gettext.translation("messages", localedir="locales", languages=["fr"], fallback=True), +} + +current_locale = "en" +_ = locales[current_locale].gettext + +orders = [ + {"id": 1, "customer": "Alice", "amount": 1234.56, "date": datetime.date.today()}, + {"id": 2, "customer": "Bob", "amount": 98765.43, "date": datetime.date.today()}, +] + + +def switch_language(lang): + global _, current_locale + if lang in locales: + current_locale = lang + _ = locales[lang].gettext + else: + print(f"Language {lang} not supported, falling back to English") + current_locale = "en" + _ = locales["en"].gettext + + +def add_order(customer, amount): + today = datetime.date.today() + + # ❌ BAD: Hardcoded English + concatenation + print("Order for " + customer + " created on " + str(today)) + + # ✅ GOOD: Proper i18n message + print(_("Order for {customer} created on {date}").format(customer=customer, date=today)) + + orders.append({"id": len(orders) + 1, "customer": customer, "amount": amount, "date": today}) + + +def list_orders(): + print(_("Order List")) + print("------------") + for o in orders: + # ❌ BAD: Hardcoded date format + print(f"{o['customer']} | {o['date'].strftime('%m/%d/%Y')} | ${o['amount']:.2f}") + + # ✅ GOOD: Locale-aware formatting + locale.setlocale(locale.LC_ALL, current_locale) + formatted_date = o['date'].strftime(locale.nl_langinfo(locale.D_FMT)) + formatted_amount = locale.currency(o['amount'], grouping=True) + print(f"{o['customer']} | {formatted_date} | {formatted_amount}") + + +def summary(): + total = sum(o["amount"] for o in orders) + + # ❌ BAD: Hardcoded string + print("Total Orders: " + str(len(orders))) + print("Total Revenue: $" + str(total)) + + # ✅ GOOD: Localized message + print(_("Total Orders: {count}").format(count=len(orders))) + print(_("Total Revenue: {revenue}").format(revenue=locale.currency(total, grouping=True))) + + +if __name__ == "__main__": + print(_("Welcome to Order Management System")) + + list_orders() + + add_order("Charlie", 555.75) + + print("\nAfter Adding Order:") + list_orders() + + summary() + + print("\nSwitching to French (missing translations -> fallback):") + switch_language("fr") + list_orders()