diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000000..70e46f1dc2768 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,66 @@ +name: "Docker Images" + +on: + push: + branches: + - master + workflow_call: + outputs: + tag_hash: + description: "The commit hash used to tag the Docker image" + value: ${{ jobs.build-image.outputs.tag_hash }} + +env: + AWS_REGION: eu-west-1 + +jobs: + build-image: + name: Build, Tag and Upload + runs-on: ubuntu-latest + env: + SUPERSET_REPOSITORY_URI: "289222877357.dkr.ecr.eu-west-1.amazonaws.com/webgains/superset" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create dist directory + run: mkdir dist + + - name: Compute labels + id: compute_labels + run: | + TAG_HASH="$(echo $GITHUB_SHA | head -c 8)" + TAG_NAME="$(echo $GITHUB_REF | sed -E -e 's:refs/pull/([0-9]+)/merge:pr.\1:' -e 's:refs/heads/::' -e 's:tag/v?([.0-9]+):v\1:' -e 's:/:.:')" + echo "TAG_HASH=$TAG_HASH" >> $GITHUB_ENV + echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV + if [ $TAG_NAME = "main" ]; then echo "LATEST_TAG=$SUPERSET_REPOSITORY_URI:latest" >> $GITHUB_ENV; fi + echo "tag_hash=$TAG_HASH" >> $GITHUB_OUTPUT + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4.0.2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2.0.1 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + tags: | + ${{ env.SUPERSET_REPOSITORY_URI }}:${{ env.TAG_HASH }} + ${{ env.SUPERSET_REPOSITORY_URI }}:${{ env.TAG_NAME }} + ${{ env.LATEST_TAG }} + outputs: + tag_hash: ${{ steps.compute_labels.outputs.tag_hash }} diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 6001eede70da3..fe748d57c360c 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -1,8 +1,9 @@ name: License Template Check on: - pull_request: - types: [synchronize, opened, reopened, ready_for_review] + workflow_dispatch: +# pull_request: +# types: [synchronize, opened, reopened, ready_for_review] # cancel previous workflow jobs for PRs concurrency: diff --git a/.github/workflows/pr-closed.yml b/.github/workflows/pr-closed.yml new file mode 100644 index 0000000000000..84105fc6a5a08 --- /dev/null +++ b/.github/workflows/pr-closed.yml @@ -0,0 +1,18 @@ +name: PR Cleanup + +on: + pull_request: + types: [closed] + +jobs: + destroy: + name: Destroy Stack + uses: webgains/github-workflows/.github/workflows/destroy-stack.yml@main + secrets: inherit + with: + environment: feature + stack: superset-pr-${{ github.event.pull_request.number }}SupersetService + permissions: + contents: read + deployments: write + id-token: write diff --git a/.github/workflows/pr-deployment.yml b/.github/workflows/pr-deployment.yml new file mode 100644 index 0000000000000..16d98955bdc5b --- /dev/null +++ b/.github/workflows/pr-deployment.yml @@ -0,0 +1,50 @@ +name: PR Deployment + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + uses: ./.github/workflows/build.yml + name: Build + secrets: inherit + permissions: + checks: write + actions: write + contents: write + packages: read + issues: read + pull-requests: write + + labels: + runs-on: ubuntu-latest + name: Add Labels + steps: + - name: Add labels + id: add_labels + run: | + echo "feature_name=superset-pr-${{ github.event.pull_request.number }}" | sed 's:\.:-:g' >> $GITHUB_OUTPUT + outputs: + feature_name: ${{ steps.add_labels.outputs.feature_name }} + + deploy: + name: Deploy to Feature Environment + needs: [labels, build] + uses: webgains/github-workflows/.github/workflows/deploy-service-to-environment.yml@WI-5868-allow-service-deploy-workflow-to-use-any-repositor + secrets: inherit + with: + environment: feature + name: ${{ needs.labels.outputs.feature_name }} + stack: ${{ needs.labels.outputs.feature_name }} + service: superset + component: service + override: | + { + "namespace": "${{ needs.labels.outputs.feature_name }}", + "supersetImageTag": "${{ needs.build.outputs.tag_hash }}" + } + permissions: + contents: read + deployments: write + id-token: write diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index 230af3d19c097..bfbd2647be49c 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -1,12 +1,13 @@ name: PR Lint on: - pull_request: + workflow_dispatch: +# pull_request: # By default, a workflow only runs when a pull_request's activity type is opened, synchronize, or reopened. We # explicity override here so that PR titles are re-linted when the PR text content is edited. # # Possible values: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request - types: [opened, edited, reopened, synchronize] +# types: [opened, edited, reopened, synchronize] jobs: lint-check: diff --git a/.github/workflows/superset-e2e.yml b/.github/workflows/superset-e2e.yml index b5646e8f41704..0228c9f11c157 100644 --- a/.github/workflows/superset-e2e.yml +++ b/.github/workflows/superset-e2e.yml @@ -1,12 +1,12 @@ name: E2E on: - push: - branches: - - "master" - - "[0-9].[0-9]*" - pull_request: - types: [synchronize, opened, reopened, ready_for_review] +# push: +# branches: +# - "master" +# - "[0-9].[0-9]*" +# pull_request: +# types: [synchronize, opened, reopened, ready_for_review] workflow_dispatch: inputs: use_dashboard: diff --git a/.github/workflows/superset-frontend.yml b/.github/workflows/superset-frontend.yml index 9451692f5f7a5..37cb212f9b55c 100644 --- a/.github/workflows/superset-frontend.yml +++ b/.github/workflows/superset-frontend.yml @@ -1,12 +1,13 @@ name: Frontend on: - push: - branches: - - "master" - - "[0-9].[0-9]*" - pull_request: - types: [synchronize, opened, reopened, ready_for_review] + workflow_dispatch: +# push: +# branches: +# - "master" +# - "[0-9].[0-9]*" +# pull_request: +# types: [synchronize, opened, reopened, ready_for_review] # cancel previous workflow jobs for PRs concurrency: diff --git a/Dockerfile b/Dockerfile index 4f24360988101..6fbb7f358d915 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64} # superset-node used for building frontend assets ###################################################################### FROM --platform=${BUILDPLATFORM} node:20-bullseye-slim AS superset-node -ARG BUILD_TRANSLATIONS="false" # Include translations in the final build +ARG BUILD_TRANSLATIONS="true" ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS} ARG DEV_MODE="false" # Skip frontend build in dev mode ENV DEV_MODE=${DEV_MODE} @@ -37,7 +37,7 @@ COPY docker/ /app/docker/ ARG NPM_BUILD_CMD="build" # Install system dependencies required for node-gyp -RUN /app/docker/apt-install.sh build-essential python3 zstd +RUN /app/docker/apt-install.sh build-essential python3 zstd jq # Define environment variables for frontend build ENV BUILD_CMD=${NPM_BUILD_CMD} \ @@ -66,26 +66,19 @@ RUN --mount=type=bind,source=./superset-frontend/package.json,target=./package.j # Runs the webpack build process COPY superset-frontend /app/superset-frontend -# Build the frontend if not in dev mode -RUN --mount=type=cache,target=/app/superset-frontend/.temp_cache \ - --mount=type=cache,target=/root/.npm \ - if [ "$DEV_MODE" = "false" ]; then \ - echo "Running 'npm run ${BUILD_CMD}'"; \ - npm run ${BUILD_CMD}; \ - else \ - echo "Skipping 'npm run ${BUILD_CMD}' in dev mode"; \ - fi; - -# Copy translation files +# This copies the .po files needed for translation +RUN mkdir -p /app/superset/translations COPY superset/translations /app/superset/translations -# Build the frontend if not in dev mode -RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \ - npm run build-translation; \ - fi; \ - rm -rf /app/superset/translations/*/*/*.po; \ - rm -rf /app/superset/translations/*/*/*.mo; +RUN mkdir -p /app/locales +COPY locales /app/locales + +# Compiles .json files from the .po files, then deletes the .po files +RUN npm run build-translation +RUN rm /app/superset/translations/*/LC_MESSAGES/*.po +RUN rm /app/superset/translations/messages.pot +RUN npm run ${BUILD_CMD} ###################################################################### # Base python layer @@ -96,6 +89,11 @@ ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS} ARG DEV_MODE="false" # Skip frontend build in dev mode ENV DEV_MODE=${DEV_MODE} +# Include translations in the final build. The default supports en only to +# reduce complexity and weight for those only using en +ARG BUILD_TRANSLATIONS="true" + +WORKDIR /app ENV LANG=C.UTF-8 \ LC_ALL=C.UTF-8 \ SUPERSET_ENV=production \ diff --git a/locales/.gitkeep b/locales/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/locales/de/translation.json b/locales/de/translation.json new file mode 100644 index 0000000000000..b24922f9fae7b --- /dev/null +++ b/locales/de/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Kanäle", + "Dates": "Daten", + "Granularity": "Granularität", + "Status": "Status", + "Groups": "Gruppen", + "Performance Report": "Performance-Report", + "Details": "Details", + "Performance Comparison": "Performance-Vergleich", + "Force refresh": "Aktualisierung erzwingen", + "Fetched": "Abgerufen", + "Enter fullscreen": "Vollbildmodus", + "Share": "Teilen", + "Download": "Herunterladen", + "Export to .CSV": "Als CSV exportieren", + "Export to Excel": "Als XLSX exportieren", + "Download as image": "Als Bild herunterladen", + "Sales & Traffic": "Umsatz und Traffic", + "Performance": "Performance", + "Total no. of sales": "Gesamtanzahl der Verkäufe", + "Total no. of products": "Gesamtanzahl der Produkte", + "Total Sales": "Gesamtumsatz", + "Clicks": "Klicks", + "Views": "Aufrufe", + "Conversion rate": "Conversion Rate", + "AOV": "AOV", + "Total Commission": "Gesamtprovision", + "EPHC": "EPHC", + "ROI %": "ROI (%)", + "Publishers": "Publisher", + "Devices": "Geräte", + "Countries": "Länder", + "Map": "Karte", + "Commission": "Provision", + "Ads": "Werbemittel", + "Products": "Produkte", + "Vouchers": "Gutscheine", + "More filters": "Weitere Filter", + "APPLY": "ANWENDEN", + "Clear all": "Filter aufheben", + "Month to date vs Last Month": "Bisheriger Monat im Vergleich zum Vormonat", + "Week to date vs Last week": "Bisherige Woche im Vergleich zur Vorwoche", + "Year to date vs Last Year": "Bisheriges Jahr im Vergleich zum Vorjahr", + "Month to date vs Prev Year": "Bisheriger Monat im Vergleich zum Vorjahr", + "Total Sales & Traffic": "Gesamtumsatz und Traffic", + "Commission & Traffic": "Provision und Traffic", + "Top Publishers by Sales": "Top-Publisher nach Umsatz", + "# Previous value": "Vorheriger Wert", + "Difference": "Differenz", + "% Difference": "Differenz (%)", + "Top Channels by Sales": "Top-Kanäle nach Umsatz", + "# Transactions": "Anz. Transaktionen", + "# Products": "Anz. Produkte", + "Total Sales Value": "Gesamtumsatz", + "# Clicks": "Anz. Klicks", + "Conversion Rate": "Conversion Rate", + "Country": "Land", + "Scheme": "Modell", + "Comm": "Provision", + "Ads Type": "Art des Werbemittels", + "Ads Name": "Name des Werbemittels", + "Product": "Produkt", + "Voucher": "Gutschein", + "Select All": "Alle auswählen", + "Last": "Letzter", + "Previous": "Vorheriger", + "Current": "Aktueller", + "Advanced": "Erweitert", + "No filter": "Kein Filter", + "Last day": "Letzter Tag", + "Last week": "Letzte Woche", + "Last month": "Letzter Monat", + "Last quarter": "Letztes Quartal", + "Last year": "Letztes Jahr", + "previous calendar week": "vorherige Kalenderwoche", + "previous calendar month": "vorheriger Kalendermonat", + "previous calendar year": "vorheriges Kalenderjahr", + "Current day": "Aktueller Tag", + "Current week": "Aktuelle Woche", + "Current month": "Aktueller Monat", + "Current quarter": "Aktuelles Quartal", + "Current year": "Aktuelles Jahr", + "Seconds %s": "Sekunden %s", + "Minutes %s": "Minuten %s", + "Hours %s": "Stunden %s", + "Days %s": "Tage %s", + "Weeks %s": "Wochen %s", + "Months %s": "Monate %s", + "Quarters %s": "Quartale %s", + "Years %s": "Jahre %s", + "Specific Date\/Time": "Datum\/Uhrzeit (spezifisch)", + "Relative Date\/Time": "Datum\/Uhrzeit (relativ)", + "Now": "Jetzt", + "Midnight": "Mitternacht", + "Transactions": "Transaktionen", + "Items": "Artikel", + "Override": "Override", + "Transaction Id": "Transaktions-ID", + "Program Id": "Programm-ID", + "Order Reference": "Bestellreferenz", + "Transaction value": "Transaktionswert", + "Browser": "Browser", + "Platform": "Plattform", + "Device": "Gerät", + "Campaign name": "Kampagnenname", + "Campaign id": "Kampagnen-ID", + "Publisher Id": "Publisher-ID", + "Publisher name": "Name des Publishers", + "Channel Type": "Kanaltyp", + "Category": "Kategorie", + "Currency": "Währung", + "Program": "Programm", + "Date": "Datum", + "Channel": "Kanal", + "Add\/Edit Filters": "Filter hinzufügen\/bearbeiten", + "Loading...": "Wird geladen...", + "Ad Type": "Art des Werbemittels", + "Ad name": "Name des Werbemittels", + "Publisher": "Publisher", + "Filters out of scope (%d)": "Filter außerhalb des Geltungsbereichs (%d)", + "Item id": "Artikel-ID", + "Item price": "Artikelpreis", + "Item Commission": "Provision für den Artikel", + "Product name": "Produktname", + "Product code": "Produktcode", + "Voucher code": "Gutscheincode", + "Commission type": "Provisionsart", + "Commission Scheme": "Provisionsmodell", + "Commission applied": "Angewendete Provision", + "Previous value": "Vorheriger Wert", + "Current value": "Aktueller Wert", + "Quantity": "", + "Confirmed": "", + "Delayed": "", + "Cancelled": "", + "Program ID": "", + "Actual time range": "Tatsächlicher Zeitbereich", + "Applied filters (%d)": "Angewendete Filter (%d)", + "Applied filters (%s)": "Angewendete Filter (%d)", + "CANCEL": "ABBRECHEN", + "Cached %s": "%s zwischengespeichert", + "Cancel": "Abbrechen", + "Click to edit %s.": "Klicken Sie hier, um %s zu bearbeiten.", + "Configure Time Range: Current...": "Zeitraum konfigurieren: Letzte...", + "Configure Time Range: Last...": "Zeitraum konfigurieren: Letzte...", + "Configure Time Range: Previous...": "Zeitraum konfigurieren: Vorhergehende…", + "Copied to clipboard!": "In Zwischenablage kopiert!", + "Copy permalink to clipboard": "Permalink in Zwischenablage kopieren", + "Custom": "Angepasst", + "Data refreshed": "Daten aktualisiert", + "Day": "Tag", + "Display all": "Anzeigename", + "Edit chart": "Diagramm bearbeiten", + "Edit time range": "Zeitraum bearbeiten", + "Fetched %s": "%s abgerufen", + "Filter type": "Filter Typ", + "Month": "Monat", + "No applied filters": "Keine angewendete Filter", + "Quarter": "Quartal", + "RANGE TYPE": "BEREICHSTYP", + "Scope": "Geltungsbereich", + "Select columns that will be displayed in the table. You can multiselect columns.": "", + "Select filter": "Filter auswählen", + "Share chart by email": "Diagramm per Email teilen", + "Share permalink by email": "Permalink per E-Mail teilen", + "Shift + Click to sort by multiple columns": "UMSCHALT+Klicken um nach mehreren Spalten zu sortieren", + "Show %s entries": "Metrik anzeigen", + "Tabs": "Reiter", + "Time filter": "Zeitfilter", + "Time grain": "Zeitgranularität", + "Use %s to open in a new tab.": "Verwenden Sie %s, um eine neue Registerkarte zu öffnen.", + "View as table": "Als Tabelle anzeigen", + "View query": "Abfrage anzeigen", + "Week": "Woche", + "Year": "Jahr", + "date": "Datum", + "sv_current": "", + "sv_previous": "", + "sv_change": "", + "sv_change_percentage": "" +} \ No newline at end of file diff --git a/locales/en/translation.json b/locales/en/translation.json new file mode 100644 index 0000000000000..0cc301ccb997c --- /dev/null +++ b/locales/en/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Channels", + "Dates": "Dates", + "Granularity": "Granularity", + "Status": "Status", + "Groups": "Groups", + "Performance Report": "Performance Report", + "Details": "Details", + "Performance Comparison": "Performance Comparison", + "Force refresh": "Force refresh", + "Fetched": "Fetched", + "Enter fullscreen": "Enter fullscreen", + "Share": "Share", + "Download": "Download", + "Export to .CSV": "Export to CSV", + "Export to Excel": "Export to Excel", + "Download as image": "Download as image", + "Sales & Traffic": "Sales & Traffic", + "Performance": "Performance", + "Total no. of sales": "Total no. of sales", + "Total no. of products": "Total no. of products", + "Total Sales": "Total sales", + "Clicks": "Clicks", + "Views": "Views", + "Conversion rate": "Conversion rate", + "AOV": "AOV", + "Total Commission": "Total Commission", + "EPHC": "EPHC", + "ROI %": "ROI %", + "Publishers": "Publishers", + "Devices": "Devices", + "Countries": "Countries", + "Map": "Map", + "Commission": "Commission", + "Ads": "Ads", + "Products": "Products", + "Vouchers": "Vouchers", + "More filters": "More filters", + "APPLY": "APPLY", + "Clear all": "Clear all", + "Month to date vs Last Month": "Month to date vs last month", + "Week to date vs Last week": "Week to date vs last week", + "Year to date vs Last Year": "Year to date vs last year", + "Month to date vs Prev Year": "Month to date vs prev year", + "Total Sales & Traffic": "Total Sales & Traffic", + "Commission & Traffic": "Commission & Traffic", + "Top Publishers by Sales": "Top Publishers by Sales", + "# Previous value": "Previous", + "Difference": "Difference", + "% Difference": "% Difference", + "Top Channels by Sales": "Top Channels by Sales", + "# Transactions": "# Transactions", + "# Products": "# Products", + "Total Sales Value": "Total Sales Value", + "# Clicks": "# Clicks", + "Conversion Rate": "Conversion Rate", + "Country": "Country", + "Scheme": "Scheme", + "Comm": "Commission", + "Ads Type": "Ads type", + "Ads Name": "Ads name", + "Product": "Product", + "Voucher": "Voucher", + "Select All": "Select All", + "Last": "Last", + "Previous": "Previous", + "Current": "Current", + "Advanced": "Advanced", + "No filter": "No filter", + "Last day": "Last day", + "Last week": "Last week", + "Last month": "Last month", + "Last quarter": "Last quarter", + "Last year": "Last year", + "previous calendar week": "previous calendar week", + "previous calendar month": "previous calendar month", + "previous calendar year": "previous calendar year", + "Current day": "Current day", + "Current week": "Current week", + "Current month": "Current month", + "Current quarter": "Current quarter", + "Current year": "Current year", + "Seconds %s": "Seconds %s", + "Minutes %s": "Minutes %s", + "Hours %s": "Hours %s", + "Days %s": "Days %s", + "Weeks %s": "Weeks %s", + "Months %s": "Months %s", + "Quarters %s": "Quarters %s", + "Years %s": "Years %s", + "Specific Date\/Time": "Specific Date\/Time", + "Relative Date\/Time": "Relative Date\/Time", + "Now": "Now", + "Midnight": "Midnight", + "Transactions": "Transactions", + "Items": "Items", + "Override": "Override", + "Transaction Id": "Transaction Id", + "Program Id": "Program Id", + "Order Reference": "Order Reference", + "Transaction value": "Transaction value", + "Browser": "Browser", + "Platform": "Platform", + "Device": "Device", + "Campaign name": "Campaign name", + "Campaign id": "Campaign id", + "Publisher Id": "Publisher Id", + "Publisher name": "Publisher name", + "Channel Type": "Channel Type", + "Category": "Category", + "Currency": "Currency", + "Program": "Program", + "Date": "Date", + "Channel": "Channel", + "Add\/Edit Filters": "Add\/Edit Filters", + "Loading...": "Loading...", + "Ad Type": "Ad Type", + "Ad name": "Ad name", + "Publisher": "Publisher", + "Filters out of scope (%d)": "Filters out of scope (%d)", + "Item id": "Item id", + "Item price": "Item price", + "Item Commission": "Item Commission", + "Product name": "Product name", + "Product code": "Product code", + "Voucher code": "Voucher code", + "Commission type": "Commission type", + "Commission Scheme": "Commission Scheme", + "Commission applied": "Commission applied", + "Previous value": "Previous value", + "Current value": "Current value", + "Quantity": "Quantity", + "Confirmed": "Confirmed", + "Delayed": "Delayed", + "Cancelled": "Cancelled", + "Program ID": "Program ID", + "Actual time range": "Actual time range", + "Applied filters (%d)": "Applied filters (%d)", + "Applied filters (%s)": "Applied filters (%s)", + "CANCEL": "CANCEL", + "Cached %s": "Cached %s", + "Cancel": "Cancel", + "Click to edit %s.": "Click to edit %s.", + "Configure Time Range: Current...": "Configure Time Range: Current...", + "Configure Time Range: Last...": "Configure Time Range: Last...", + "Configure Time Range: Previous...": "Configure Time Range: Previous...", + "Copied to clipboard!": "Copied to clipboard!", + "Copy permalink to clipboard": "Copy permalink to clipboard", + "Custom": "Custom", + "Data refreshed": "Data refreshed", + "Day": "Day", + "Display all": "Display all", + "Edit chart": "Edit chart", + "Edit time range": "Edit time range", + "Fetched %s": "Fetched %s", + "Filter type": "Filter type", + "Month": "Month", + "No applied filters": "No applied filters", + "Quarter": "Quarter", + "RANGE TYPE": "RANGE TYPE", + "Scope": "Scope", + "Select columns that will be displayed in the table. You can multiselect columns.": "Select columns that will be displayed in the table. You can multiselect columns.", + "Select filter": "Select filter", + "Share chart by email": "Share chart by email", + "Share permalink by email": "Share permalink by email", + "Shift + Click to sort by multiple columns": "Shift + Click to sort by multiple columns", + "Show %s entries": "Show %s entries", + "Tabs": "Tabs", + "Time filter": "Time filter", + "Time grain": "Time grain", + "Use %s to open in a new tab.": "Use %s to open in a new tab.", + "View as table": "View as table", + "View query": "View query", + "Week": "Week", + "Year": "Year", + "date": "date", + "sv_current": "Current", + "sv_previous": "Previous", + "sv_change": "Change", + "sv_change_percentage": "Change (%)" +} \ No newline at end of file diff --git a/locales/es/translation.json b/locales/es/translation.json new file mode 100644 index 0000000000000..7d1d1cc4388c9 --- /dev/null +++ b/locales/es/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Canales", + "Dates": "Fechas", + "Granularity": "Granulado", + "Status": "Estado", + "Groups": "Grupos", + "Performance Report": "Informe de rendimiento", + "Details": "Detalles", + "Performance Comparison": "Comparación de rendimiento", + "Force refresh": "Forzar actualización", + "Fetched": "Obtenido", + "Enter fullscreen": "Modo pantalla completa", + "Share": "Compartir", + "Download": "Descargar", + "Export to .CSV": "Exportar a CSV", + "Export to Excel": "Exportar a Excel", + "Download as image": "Descargar como imagen", + "Sales & Traffic": "Ventas y tráfico", + "Performance": "Rendimiento", + "Total no. of sales": "N.º total de ventas", + "Total no. of products": "N.º total de productos", + "Total Sales": "Total de ventas", + "Clicks": "Clics", + "Views": "Visualizaciones", + "Conversion rate": "Tasa de conversión", + "AOV": "Cesta media", + "Total Commission": "Comisión total", + "EPHC": "EPHC", + "ROI %": "ROI (%)", + "Publishers": "Afiliados", + "Devices": "Dispositivos", + "Countries": "Países", + "Map": "Mapa", + "Commission": "Comisión", + "Ads": "Anuncios", + "Products": "Productos", + "Vouchers": "Descuentos", + "More filters": "Más filtros", + "APPLY": "APLICAR", + "Clear all": "Borrar todo", + "Month to date vs Last Month": "Mes hasta la fecha vs. mes pasado", + "Week to date vs Last week": "Semana hasta la fecha vs. semana pasada", + "Year to date vs Last Year": "Año hasta la fecha vs. año pasado", + "Month to date vs Prev Year": "Mes hasta la fecha vs. año anterior", + "Total Sales & Traffic": "Ventas y tráfico totales", + "Commission & Traffic": "Comisión y tráfico", + "Top Publishers by Sales": "Principales afiliados por ventas", + "# Previous value": "Anterior", + "Difference": "Diferencia", + "% Difference": "Diferencia (%)", + "Top Channels by Sales": "Principales canales por ventas", + "# Transactions": "N.º de transacciones", + "# Products": "N.º de productos", + "Total Sales Value": "Valor total de las ventas", + "# Clicks": "N.º de clics", + "Conversion Rate": "Tasa de conversión", + "Country": "País", + "Scheme": "Plan", + "Comm": "Comisión", + "Ads Type": "Tipo de anuncios", + "Ads Name": "Nombre de los anuncios", + "Product": "Producto", + "Voucher": "Descuento", + "Select All": "Seleccionar todo", + "Last": "Último", + "Previous": "Anterior", + "Current": "Actual", + "Advanced": "Búsqueda avanzada", + "No filter": "Sin filtros", + "Last day": "Último día", + "Last week": "La semana pasada", + "Last month": "El mes pasado", + "Last quarter": "El trimestre pasado", + "Last year": "El año pasado", + "previous calendar week": "semana natural anterior", + "previous calendar month": "mes natural anterior", + "previous calendar year": "año natural anterior", + "Current day": "Día actual", + "Current week": "Semana actual", + "Current month": "Mes actual", + "Current quarter": "Trimestre actual", + "Current year": "Año actual", + "Seconds %s": "%s segundos", + "Minutes %s": "%s minutos", + "Hours %s": "%s horas", + "Days %s": "%s días", + "Weeks %s": "%s semanas", + "Months %s": "%s meses", + "Quarters %s": "%s trimestres", + "Years %s": "%s años", + "Specific Date\/Time": "Fecha\/hora específica", + "Relative Date\/Time": "Fecha\/hora relativa", + "Now": "Ahora", + "Midnight": "Medianoche", + "Transactions": "Transacciones", + "Items": "Artículos", + "Override": "Sobrecomisión", + "Transaction Id": "ID de transacción", + "Program Id": "ID de programa", + "Order Reference": "Referencia del pedido", + "Transaction value": "Valor de la transacción", + "Browser": "Navegador", + "Platform": "Plataforma", + "Device": "Dispositivo", + "Campaign name": "Nombre de la campaña", + "Campaign id": "ID de campaña", + "Publisher Id": "ID de afiliado", + "Publisher name": "Nombre del afiliado", + "Channel Type": "Tipo de canal", + "Category": "Categoría", + "Currency": "Divisa", + "Program": "Programa", + "Date": "Fecha", + "Channel": "Canal", + "Add\/Edit Filters": "Agregar\/Modificar filtros", + "Loading...": "Cargando...", + "Ad Type": "Tipo de anuncio", + "Ad name": "Nombre del anuncio", + "Publisher": "Afiliado", + "Filters out of scope (%d)": "Los filtros no se corresponden con los datos (%d)", + "Item id": "ID del artículo", + "Item price": "Precio del artículo", + "Item Commission": "Comisión por el artículo", + "Product name": "Nombre del producto", + "Product code": "Código del producto", + "Voucher code": "Código de descuento", + "Commission type": "Tipo de comisión", + "Commission Scheme": "Plan de comisión", + "Commission applied": "Comisión aplicada", + "Previous value": "Valor anterior", + "Current value": "Valor actual", + "Quantity": "", + "Confirmed": "", + "Delayed": "", + "Cancelled": "", + "Program ID": "", + "Actual time range": "Rango de tiempo actual", + "Applied filters (%d)": "Filtros aplicados (%d)", + "Applied filters (%s)": "Filtros aplicados (%d)", + "CANCEL": "CANCELAR", + "Cached %s": "En cache %s", + "Cancel": "Cancelar", + "Click to edit %s.": "Click para editar", + "Configure Time Range: Current...": "Configurar Rango de Tiempo: Últimos..", + "Configure Time Range: Last...": "Configurar Rango de Tiempo: Últimos..", + "Configure Time Range: Previous...": "Configurar Ranfo de Tiempo: Anteriores...", + "Copied to clipboard!": "Copiar al portapapeles", + "Copy permalink to clipboard": "Copiar consulta de partición al portapapeles", + "Custom": "Personalizar", + "Data refreshed": "Última actualización de metadatos", + "Day": "día", + "Display all": "Valor del Filtro", + "Edit chart": "Editar Gráfico", + "Edit time range": "Editar rango de tiempo", + "Fetched %s": "Obtenido %s", + "Filter type": "Filtrar por usuario", + "Month": "mes", + "No applied filters": "Filtros aplicados (%d)", + "Quarter": "consulta", + "RANGE TYPE": "TIPO DE RANGO", + "Scope": "", + "Select columns that will be displayed in the table. You can multiselect columns.": "", + "Select filter": "Buscar \/ Filtrar", + "Share chart by email": "Compartir gráfico", + "Share permalink by email": "Compartir gráfico", + "Shift + Click to sort by multiple columns": "", + "Show %s entries": "Mostrar Métrica", + "Tabs": "Pestañas", + "Time filter": "Filtro de Fecha", + "Time grain": "Granularidad Temporal", + "Use %s to open in a new tab.": "Consulta en nueva pestaña", + "View as table": "Ver ejemplos", + "View query": "Ver consulta", + "Week": "semana", + "Year": "año", + "date": "fecha", + "sv_current": "", + "sv_previous": "", + "sv_change": "", + "sv_change_percentage": "" +} \ No newline at end of file diff --git a/locales/fr/translation.json b/locales/fr/translation.json new file mode 100644 index 0000000000000..c284dc0a24baf --- /dev/null +++ b/locales/fr/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Canaux", + "Dates": "Dates", + "Granularity": "Niveau de détail", + "Status": "Statut", + "Groups": "Groupes", + "Performance Report": "Rapport de performance", + "Details": "Données détaillées", + "Performance Comparison": "Comparaison des performances", + "Force refresh": "Forcer l'actualisation", + "Fetched": "Récupéré", + "Enter fullscreen": "Mode plein écran", + "Share": "Partager", + "Download": "Télécharger", + "Export to .CSV": "Exporter au format CSV", + "Export to Excel": "Exporter vers Excel", + "Download as image": "Télécharger l'image", + "Sales & Traffic": "Montant total des ventes et trafic", + "Performance": "Performance", + "Total no. of sales": "Nombre total de ventes", + "Total no. of products": "Nombre total de produits", + "Total Sales": "Montant total des ventes", + "Clicks": "Clics", + "Views": "Affichages", + "Conversion rate": "Taux de conversion", + "AOV": "Panier moyen", + "Total Commission": "Commission totale", + "EPHC": "EPHC", + "ROI %": "ROI (%)", + "Publishers": "Éditeurs", + "Devices": "Appareils", + "Countries": "Pays", + "Map": "Carte", + "Commission": "Commission", + "Ads": "Annonces", + "Products": "Produits", + "Vouchers": "Codes promotionnels", + "More filters": "Plus de filtres", + "APPLY": "APPLIQUER", + "Clear all": "Tout effacer", + "Month to date vs Last Month": "Mois en cours par rapport au mois précédent", + "Week to date vs Last week": "Semaine en cours par rapport à la semaine précédente", + "Year to date vs Last Year": "Année en cours par rapport à l'année précédente", + "Month to date vs Prev Year": "Mois en cours par rapport à l'année précédente", + "Total Sales & Traffic": "Montant total des ventes et trafic", + "Commission & Traffic": "Commission et trafic", + "Top Publishers by Sales": "Meilleures ventes, par éditeur", + "# Previous value": "Valeur antérieure", + "Difference": "Différence", + "% Difference": "Différence (%)", + "Top Channels by Sales": "Meilleurs canaux de ventes", + "# Transactions": "Nombre de transactions", + "# Products": "Nombre de produits", + "Total Sales Value": "Valeur totale des ventes", + "# Clicks": "Nombre de clics", + "Conversion Rate": "Taux de conversion", + "Country": "Pays", + "Scheme": "Plan", + "Comm": "Commission", + "Ads Type": "Type d'annonces", + "Ads Name": "Nom des annonces", + "Product": "Produit", + "Voucher": "Code promotionnel", + "Select All": "Tout sélectionner", + "Last": "Dernier(e)", + "Previous": "Précédent(e)", + "Current": "En cours", + "Advanced": "Recherche détaillée", + "No filter": "Aucun filtre", + "Last day": "Dernier jour", + "Last week": "Semaine dernière", + "Last month": "Mois dernier", + "Last quarter": "Trimestre dernier", + "Last year": "Année dernière", + "previous calendar week": "Semaine précédente", + "previous calendar month": "Mois précédent", + "previous calendar year": "Année civile précédente", + "Current day": "Aujourd'hui", + "Current week": "Semaine en cours", + "Current month": "Mois en cours", + "Current quarter": "Trimestre en cours", + "Current year": "Année en cours", + "Seconds %s": "%s secondes", + "Minutes %s": "%s minutes", + "Hours %s": "%s heures", + "Days %s": "%s jours", + "Weeks %s": "%s semaines", + "Months %s": "%s mois", + "Quarters %s": "%s trimestres", + "Years %s": "%s années", + "Specific Date\/Time": "Date\/heure exacte", + "Relative Date\/Time": "Date\/heure relative", + "Now": "Maintenant", + "Midnight": "Minuit", + "Transactions": "Transactions", + "Items": "Articles", + "Override": "Frais sur commission", + "Transaction Id": "Identifiant (ID) de transaction", + "Program Id": "Identifiant (ID) du programme", + "Order Reference": "Référence de commande", + "Transaction value": "Valeur de la transaction", + "Browser": "Navigateur", + "Platform": "Plateforme", + "Device": "Appareil", + "Campaign name": "Nom de la campagne", + "Campaign id": "Identifiant (ID) de campagne", + "Publisher Id": "Identifiant (ID) de l'éditeur", + "Publisher name": "Nom de l'éditeur", + "Channel Type": "Type de canal", + "Category": "Catégorie", + "Currency": "Devise", + "Program": "Programme", + "Date": "Date", + "Channel": "Canal", + "Add\/Edit Filters": "Ajouter\/modifier des filtres", + "Loading...": "Chargement en cours...", + "Ad Type": "Type d'annonce", + "Ad name": "Nom de l'annonce", + "Publisher": "Éditeur", + "Filters out of scope (%d)": "Les filtres ne correspondent pas aux données (%d)", + "Item id": "Identifiant (ID) de l'article", + "Item price": "Prix de l'article", + "Item Commission": "Commission sur l'article", + "Product name": "Nom du produit", + "Product code": "Code produit", + "Voucher code": "Code promotionnel", + "Commission type": "Type de commission", + "Commission Scheme": "Plan de commission", + "Commission applied": "Commission appliquée", + "Previous value": "Valeur antérieure", + "Current value": "Valeur actuelle", + "Quantity": "", + "Confirmed": "", + "Delayed": "", + "Cancelled": "", + "Program ID": "", + "Actual time range": "Intervalle de temps courant", + "Applied filters (%d)": "Filtres appliqués (%d)", + "Applied filters (%s)": "Filtres appliqués (%d)", + "CANCEL": "CANCEL", + "Cached %s": "%s mis en cache", + "Cancel": "Annuler", + "Click to edit %s.": "Cliquer pour modifier %s.", + "Configure Time Range: Current...": "Configurer l'intervalle de temps : Dernier…", + "Configure Time Range: Last...": "Configurer l'intervalle de temps : Dernier…", + "Configure Time Range: Previous...": "Configurer intervalle de temps : Précédent…", + "Copied to clipboard!": "Copié vers le presse-papier!", + "Copy permalink to clipboard": "Copier le lien dans le presse-papiers", + "Custom": "Personnalisé", + "Data refreshed": "Données rafraîchies", + "Day": "Jour", + "Display all": "Nom d'affichage", + "Edit chart": "Modifier le graphique", + "Edit time range": "Modifier l’intervalle de temps", + "Fetched %s": "Récupération de %s", + "Filter type": "Type de filtre", + "Month": "Mois", + "No applied filters": "Aucun filtre appliqué", + "Quarter": "Trimestre", + "RANGE TYPE": "TYPE DE PLAGE", + "Scope": "Portée", + "Select columns that will be displayed in the table. You can multiselect columns.": "", + "Select filter": "Selectionner un filtre", + "Share chart by email": "Partager le graphique par courriel", + "Share permalink by email": "Partager le lien par courriel", + "Shift + Click to sort by multiple columns": "Shift + clic pour classer par plusieurs colonnes", + "Show %s entries": "Afficher la mesure", + "Tabs": "Onglets", + "Time filter": "Filtre de temps", + "Time grain": "Fragment de temps", + "Use %s to open in a new tab.": "Utilisez %s pour ouvrir un nouvel onglet.", + "View as table": "Voir sous forme de tableau", + "View query": "Voir la requête", + "Week": "Semaine", + "Year": "Année", + "date": "Date", + "sv_current": "", + "sv_previous": "", + "sv_change": "", + "sv_change_percentage": "" +} \ No newline at end of file diff --git a/locales/it/translation.json b/locales/it/translation.json new file mode 100644 index 0000000000000..79f55448153ef --- /dev/null +++ b/locales/it/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Canali", + "Dates": "Date", + "Granularity": "Granularità", + "Status": "Stato", + "Groups": "Gruppi", + "Performance Report": "Report delle prestazioni", + "Details": "Dettagli", + "Performance Comparison": "Confronto tra prestazioni", + "Force refresh": "", + "Fetched": "Recuperato", + "Enter fullscreen": "Modalità schermo intero", + "Share": "Condividi", + "Download": "Scarica", + "Export to .CSV": "Esporta come CSV", + "Export to Excel": "Esporta in Excel", + "Download as image": "Scarica come immagine", + "Sales & Traffic": "Vendite e traffico", + "Performance": "Prestazioni", + "Total no. of sales": "N. totale di vendite", + "Total no. of products": "N. totale di prodotti", + "Total Sales": "Vendite totali", + "Clicks": "Click", + "Views": "Visualizzazioni", + "Conversion rate": "Tasso di conversione", + "AOV": "Carrello medio", + "Total Commission": "Commissione totale", + "EPHC": "EPHC", + "ROI %": "% ROI", + "Publishers": "Publisher", + "Devices": "Dispositivi", + "Countries": "Paesi", + "Map": "Mappa", + "Commission": "Commissione", + "Ads": "Annunci", + "Products": "Prodotti", + "Vouchers": "Coupon", + "More filters": "Più filtri", + "APPLY": "APPLICA", + "Clear all": "Cancella tutto", + "Month to date vs Last Month": "Mese in corso rispetto al mese scorso", + "Week to date vs Last week": "Settimana in corso rispetto alla settimana scorsa", + "Year to date vs Last Year": "Anno in corso rispetto all'anno scorso", + "Month to date vs Prev Year": "Mese in corso rispetto all'anno scorso", + "Total Sales & Traffic": "Vendite e traffico totali", + "Commission & Traffic": "Commissione e traffico", + "Top Publishers by Sales": "Migliori publisher per vendite", + "# Previous value": "# Valore precedente", + "Difference": "Differenza", + "% Difference": "Differenza %", + "Top Channels by Sales": "Migliori canali per vendite", + "# Transactions": "# Transazioni", + "# Products": "# Prodotti", + "Total Sales Value": "Valore totale vendite", + "# Clicks": "# Click", + "Conversion Rate": "Tasso di conversione", + "Country": "Paese", + "Scheme": "Schema", + "Comm": "Commissione", + "Ads Type": "Tipo annunci", + "Ads Name": "Nome annunci", + "Product": "Prodotto", + "Voucher": "Coupon", + "Select All": "Seleziona tutto", + "Last": "Ultimo", + "Previous": "Precedente", + "Current": "Attuale", + "Advanced": "Avanzato", + "No filter": "Nessun filtro", + "Last day": "L'ultimo giorno", + "Last week": "La settimana scorsa", + "Last month": "Il mese scorso", + "Last quarter": "Il trimestre scorso", + "Last year": "L'anno scorso", + "previous calendar week": "settimana solare precedente", + "previous calendar month": "mese solare precedente", + "previous calendar year": "anno solare precedente", + "Current day": "Giorno attuale", + "Current week": "Settimana attuale", + "Current month": "Mese attuale", + "Current quarter": "Trimestre attuale", + "Current year": "Anno attuale", + "Seconds %s": "%s secondi", + "Minutes %s": "%s minuti", + "Hours %s": "%s ore", + "Days %s": "%s giorni", + "Weeks %s": "%s settimane", + "Months %s": "%s mesi", + "Quarters %s": "%s trimestri", + "Years %s": "%s anni", + "Specific Date\/Time": "Data\/ora specifica", + "Relative Date\/Time": "Data\/ora relativa", + "Now": "Adesso", + "Midnight": "Mezzanotte", + "Transactions": "Transazioni", + "Items": "Articoli", + "Override": "Provvigione", + "Transaction Id": "ID transazione", + "Program Id": "ID programma", + "Order Reference": "ID ordine", + "Transaction value": "Valore transazione", + "Browser": "Browser", + "Platform": "Piattaforma", + "Device": "Dispositivo", + "Campaign name": "Nome campagna", + "Campaign id": "ID campagna", + "Publisher Id": "ID publisher", + "Publisher name": "Nome publisher", + "Channel Type": "Tipo di canale", + "Category": "Categoria", + "Currency": "Valuta", + "Program": "Programma", + "Date": "Data", + "Channel": "Canale", + "Add\/Edit Filters": "Aggiungi\/Modifica filtri", + "Loading...": "Caricamento in corso...", + "Ad Type": "Tipo annuncio", + "Ad name": "Nome annuncio", + "Publisher": "Publisher", + "Filters out of scope (%d)": "Filtri esclusi (%d)", + "Item id": "ID articolo", + "Item price": "Prezzo articolo", + "Item Commission": "Commissione articolo", + "Product name": "Nome prodotto", + "Product code": "Codice prodotto", + "Voucher code": "Codice sconto", + "Commission type": "Tipologia di commissione", + "Commission Scheme": "Struttura commissionale", + "Commission applied": "Commissione applicata", + "Previous value": "Valore precedente", + "Current value": "Valore attuale", + "Quantity": "", + "Confirmed": "", + "Delayed": "", + "Cancelled": "", + "Program ID": "", + "Actual time range": "", + "Applied filters (%d)": "Aggiungi filtro", + "Applied filters (%s)": "Aggiungi filtro", + "CANCEL": "", + "Cached %s": "", + "Cancel": "Annulla", + "Click to edit %s.": "", + "Configure Time Range: Current...": "", + "Configure Time Range: Last...": "", + "Configure Time Range: Previous...": "", + "Copied to clipboard!": "copia URL in appunti", + "Copy permalink to clipboard": "copia URL in appunti", + "Custom": "", + "Data refreshed": "", + "Day": "giorno", + "Display all": "Valore del filtro", + "Edit chart": "Modifica grafico", + "Edit time range": "", + "Fetched %s": "", + "Filter type": "Valore del filtro", + "Month": "mese", + "No applied filters": "Aggiungi filtro", + "Quarter": "condividi query", + "RANGE TYPE": "", + "Scope": "", + "Select columns that will be displayed in the table. You can multiselect columns.": "", + "Select filter": "Seleziona data finale", + "Share chart by email": "Esplora grafico", + "Share permalink by email": "Esplora grafico", + "Shift + Click to sort by multiple columns": "", + "Show %s entries": "Mostra metrica", + "Tabs": "", + "Time filter": "Aggiungi filtro", + "Time grain": "", + "Use %s to open in a new tab.": "Query in un nuovo tab", + "View as table": "", + "View query": "condividi query", + "Week": "settimana", + "Year": "anno", + "date": "", + "sv_current": "", + "sv_previous": "", + "sv_change": "", + "sv_change_percentage": "" +} \ No newline at end of file diff --git a/locales/nl/translation.json b/locales/nl/translation.json new file mode 100644 index 0000000000000..b4c95c2450d96 --- /dev/null +++ b/locales/nl/translation.json @@ -0,0 +1,181 @@ +{ + "Channels": "Kanalen", + "Dates": "Data", + "Granularity": "Korreligheid", + "Status": "Status", + "Groups": "Groepen", + "Performance Report": "Performance rapport", + "Details": "Details", + "Performance Comparison": "Prestatie vergelijking", + "Force refresh": "Vernieuwen forceren", + "Fetched": "Opgehaald", + "Enter fullscreen": "Volledig scherm openen", + "Share": "Delen", + "Download": "Downloaden", + "Export to .CSV": "Naar CSV exporteren", + "Export to Excel": "Naar excel exporteren", + "Download as image": "Als afbeelding downloaden", + "Sales & Traffic": "Verkoop & verkeer", + "Performance": "Performance", + "Total no. of sales": "Totale aantal sales", + "Total no. of products": "Totale aantal producten", + "Total Sales": "Totale verkoop", + "Clicks": "Kliks", + "Views": "Weergaven", + "Conversion rate": "Conversieratio", + "AOV": "AOV", + "Total Commission": "Totale commissie", + "EPHC": "EPHC", + "ROI %": "ROI %", + "Publishers": "Publishers", + "Devices": "Apparaten", + "Countries": "Landen", + "Map": "Kaart", + "Commission": "Commissie", + "Ads": "Advertenties", + "Products": "Producten", + "Vouchers": "Kortingscodes", + "More filters": "Meer filters", + "APPLY": "TOEPASSEN", + "Clear all": "Alles legen", + "Month to date vs Last Month": "Maand tot vandaag vs. vorige maand", + "Week to date vs Last week": "Week tot vandaag vs. vorige week", + "Year to date vs Last Year": "Jaar tot vandaag vs. vorig jaar", + "Month to date vs Prev Year": "Maand tot vandaag vs. vorige jaar", + "Total Sales & Traffic": "Totale verkoop & verkeer", + "Commission & Traffic": "Commissie & verkeer", + "Top Publishers by Sales": "Beste publishers op verkoop", + "# Previous value": "# vorige waarde", + "Difference": "Verschil", + "% Difference": "% verschil", + "Top Channels by Sales": "Beste kanalen op verkoop", + "# Transactions": "# transacties", + "# Products": "# producten", + "Total Sales Value": "Totale waarde verkoop", + "# Clicks": "# kliks", + "Conversion Rate": "Conversieratio", + "Country": "Land", + "Scheme": "Schema", + "Comm": "Commissie", + "Ads Type": "Advertenties type", + "Ads Name": "Advertenties naam", + "Product": "Product", + "Voucher": "Kortingscode", + "Select All": "Alle selecteren", + "Last": "Laatste", + "Previous": "Vorige", + "Current": "Huidig", + "Advanced": "Geavanceerd", + "No filter": "Geen filter", + "Last day": "Vorige dag", + "Last week": "Vorige week", + "Last month": "Vorige maand", + "Last quarter": "Vorige kwartaal", + "Last year": "Vorig jaar", + "previous calendar week": "vorige kalenderweek", + "previous calendar month": "vorige kalendermaand", + "previous calendar year": "vorige kalenderjaar", + "Current day": "Huidige dag", + "Current week": "Huidige week", + "Current month": "Huidige maand", + "Current quarter": "Huidige kwartaal", + "Current year": "Huidige jaar", + "Seconds %s": "Seconden %s", + "Minutes %s": "Minuten (%s)", + "Hours %s": "Uren %s", + "Days %s": "Dagen %s", + "Weeks %s": "Weken %s", + "Months %s": "Maanden %s", + "Quarters %s": "Kwartalen %s", + "Years %s": "Jaren %s", + "Specific Date\/Time": "Specifieke datum\/tijd", + "Relative Date\/Time": "Relatieve datum\/tijd", + "Now": "Nu", + "Midnight": "Middernacht", + "Transactions": "Transacties", + "Items": "Items", + "Override": "Override", + "Transaction Id": "Transactie ID", + "Program Id": "Programma ID", + "Order Reference": "Orderreferentie", + "Transaction value": "Transactiewaarde", + "Browser": "Browser", + "Platform": "Platform", + "Device": "Apparaat", + "Campaign name": "Campagne naam", + "Campaign id": "Campagne id", + "Publisher Id": "Publisher id", + "Publisher name": "Publisher naam", + "Channel Type": "Kanaaltype", + "Category": "Categorie", + "Currency": "Valuta", + "Program": "Programma", + "Date": "Datum", + "Channel": "Kanaal", + "Add\/Edit Filters": "Filters toevoegen\/bewerken", + "Loading...": "Laden...", + "Ad Type": "Ad type", + "Ad name": "Ad naam", + "Publisher": "Publisher", + "Filters out of scope (%d)": "Filters buiten bereik (%d)", + "Item id": "Item id", + "Item price": "Item prijs", + "Item Commission": "Item commissie", + "Product name": "Productnaam", + "Product code": "Productcode", + "Voucher code": "Kortingscode", + "Commission type": "Commissie type", + "Commission Scheme": "Commissie schema", + "Commission applied": "Commissie toegepast", + "Previous value": "Vorige waarde", + "Current value": "Huidige waarde", + "Quantity": "", + "Confirmed": "", + "Delayed": "", + "Cancelled": "", + "Program ID": "", + "Actual time range": "Werkelijk tijdsbereik", + "Applied filters (%d)": "Toegepaste filters (%d)", + "Applied filters (%s)": "Toegepaste filters (%d)", + "CANCEL": "ANNULEER", + "Cached %s": "Gebufferd %s", + "Cancel": "Annuleer", + "Click to edit %s.": "Klik om te bewerken %s.", + "Configure Time Range: Current...": "Configureer Tijdbereik: Laatste...", + "Configure Time Range: Last...": "Configureer Tijdbereik: Laatste...", + "Configure Time Range: Previous...": "Configureer Tijdbereik: Vorige...", + "Copied to clipboard!": "Gekopieerd naar het klembord!", + "Copy permalink to clipboard": "Kopieer permalink naar klembord", + "Custom": "Aangepast", + "Data refreshed": "Gegevens verversen", + "Day": "Dag", + "Display all": "Toon naam", + "Edit chart": "Bewerk grafiek", + "Edit time range": "Tijdsbereik bewerken", + "Fetched %s": "Opgehaald %s", + "Filter type": "Filter type", + "Month": "Maand", + "No applied filters": "Geen toegepaste filters", + "Quarter": "Kwartaal", + "RANGE TYPE": "BEREIK TYPE", + "Scope": "Scope", + "Select columns that will be displayed in the table. You can multiselect columns.": "", + "Select filter": "Selecteer filter", + "Share chart by email": "Deel grafiek per e-mail", + "Share permalink by email": "Deel permalink via e-mail", + "Shift + Click to sort by multiple columns": "Shift + Klik om te sorteren op meerdere kolommen", + "Show %s entries": "Toon metriek", + "Tabs": "Tabs", + "Time filter": "Tijdfilter", + "Time grain": "Tijdsinterval", + "Use %s to open in a new tab.": "Gebruik %s om een nieuw tabblad te openen.", + "View as table": "Bekijk als tabel", + "View query": "Bekijk zoekopdracht", + "Week": "Week", + "Year": "Jaar", + "date": "datum", + "sv_current": "", + "sv_previous": "", + "sv_change": "", + "sv_change_percentage": "" +} \ No newline at end of file diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 092f0c6833dd7..a03510cbd7be7 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -42,7 +42,7 @@ "build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color", "build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color", "build-storybook": "storybook build", - "build-translation": "scripts/po2json.sh", + "build-translation": "scripts/po2json.sh && scripts/lokalise_merger.sh", "bundle-stats": "cross-env BUNDLE_ANALYZER=true npm run build && npx open-cli ../superset/static/stats/statistics.html", "core:cover": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --coverage --coverageThreshold='{\"global\":{\"statements\":100,\"branches\":100,\"functions\":100,\"lines\":100}}' --collectCoverageFrom='[\"packages/**/src/**/*.{js,ts}\", \"!packages/superset-ui-demo/**/*\"]' packages", "cover": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --coverage", diff --git a/superset-frontend/packages/superset-ui-core/src/translation/TranslatorSingleton.ts b/superset-frontend/packages/superset-ui-core/src/translation/TranslatorSingleton.ts index cc38e3012ab2e..a974d554315c0 100644 --- a/superset-frontend/packages/superset-ui-core/src/translation/TranslatorSingleton.ts +++ b/superset-frontend/packages/superset-ui-core/src/translation/TranslatorSingleton.ts @@ -64,7 +64,11 @@ function addLocaleData(data: LocaleData) { } function t(input: string, ...args: unknown[]) { - return getInstance().translate(input, ...args); + try { + return getInstance().translate(input, ...args); + } catch (e) { + return `${input}` + } } function tn(key: string, ...args: unknown[]) { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx index d7882ccdb6c15..4a9499a565e02 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx @@ -221,7 +221,7 @@ class BigNumberVis extends PureComponent { height: maxHeight, }} > - {text} + {t(text)} ); } diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts index d4d19f9c2f6af..3c774f582f2a3 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts @@ -96,6 +96,7 @@ import { getXAxisFormatter, getYAxisFormatter, } from '../utils/formatters'; +import moment from 'moment'; const getFormatter = ( customFormatters: Record, @@ -580,7 +581,7 @@ export default function transformProps( show: !inContextMenu, trigger: richTooltip ? 'axis' : 'item', formatter: (params: any) => { - const xValue: number = richTooltip + let xValue: number = richTooltip ? params[0].value[0] : params.value[0]; const forecastValue: any[] = richTooltip ? params : [params]; @@ -639,6 +640,15 @@ export default function transformProps( focusedRow = rows.length - 1; } }); + const { seriesName } = params + if (seriesName) { + const regex = new RegExp(".*(\\d+) (\\w+) ago") + const result = seriesName.match(regex) + if (result && result.length == 3) { + const [_, diff, diffType] = result + xValue = moment.utc(xValue).subtract(Number(diff), diffType).valueOf() + } + } return tooltipHtml(rows, tooltipFormatter(xValue), focusedRow); }, }, diff --git a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx index 21ce6541cf2fd..75c41ec229d5c 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx @@ -363,9 +363,7 @@ export default typedMemo(function DataTable({ {hasGlobalControl ? (
-
+
{hasPagination ? ( ({ ) : null}
{searchInput ? ( -
+
searchInput={ typeof searchInput === 'boolean' ? undefined : searchInput diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx index b9cc335a34f0b..67cd63fb65d2d 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx @@ -270,9 +270,9 @@ export default function TableChart( } = props; const comparisonColumns = [ { key: 'all', label: t('Display all') }, - { key: '#', label: '#' }, - { key: '△', label: '△' }, - { key: '%', label: '%' }, + { key: t('sv_previous'), label: t('sv_previous') }, + { key: t('sv_change') , label: t('sv_change') }, + { key: t('sv_change_percentage'), label: t('sv_change_percentage') }, ]; const timestampFormatter = useCallback( value => getTimeFormatterForGranularity(timeGrain)(value), @@ -407,7 +407,7 @@ export default function TableChart( }; }; - const comparisonLabels = [t('Main'), '#', '△', '%']; + const comparisonLabels = [t('sv_current'), t('sv_previous'), t('sv_change'), t('sv_change_percentage')]; const filteredColumnsMeta = useMemo(() => { if (!isUsingTimeComparison) { return columnsMeta; @@ -571,7 +571,7 @@ export default function TableChart( color: ${theme.colors.grayscale.dark2}; `} > - {column.label} + {t(column.label)} ( />, ); } - // Add the current header headers.push( - {key} + {t(key.trim())} ( alignItems: 'flex-end', }} > - {label} + {t(label)}
diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts index 48871e4ea4185..c8ba4e690cf1f 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts @@ -118,21 +118,21 @@ const processComparisonTotals = ( totals.map((totalRecord: DataRecord) => Object.keys(totalRecord).forEach(key => { if (totalRecord[key] !== undefined && !key.includes(comparisonSuffix)) { - transformedTotals[`Main ${key}`] = - parseInt(transformedTotals[`Main ${key}`]?.toString() || '0', 10) + + transformedTotals[`${t('sv_current')} ${key}`] = + parseInt(transformedTotals[`${t('sv_current')} ${key}`]?.toString() || '0', 10) + parseInt(totalRecord[key]?.toString() || '0', 10); - transformedTotals[`# ${key}`] = - parseInt(transformedTotals[`# ${key}`]?.toString() || '0', 10) + + transformedTotals[`${t('sv_previous')} ${key}`] = + parseInt(transformedTotals[`${t('sv_previous')} ${key}`]?.toString() || '0', 10) + parseInt( totalRecord[`${key}__${comparisonSuffix}`]?.toString() || '0', 10, ); const { valueDifference, percentDifferenceNum } = calculateDifferences( - transformedTotals[`Main ${key}`] as number, - transformedTotals[`# ${key}`] as number, + transformedTotals[`${t('sv_current')} ${key}`] as number, + transformedTotals[`${t('sv_previous')} ${key}`] as number, ); - transformedTotals[`△ ${key}`] = valueDifference; - transformedTotals[`% ${key}`] = percentDifferenceNum; + transformedTotals[`${t('sv_change')} ${key}`] = valueDifference; + transformedTotals[`${t('sv_change_percentage')} ${key}`] = percentDifferenceNum; } }), ); @@ -166,10 +166,10 @@ const processComparisonDataRecords = memoizeOne( comparisonValue as number, ); - transformedItem[`Main ${origCol.key}`] = originalValue; - transformedItem[`# ${origCol.key}`] = comparisonValue; - transformedItem[`△ ${origCol.key}`] = valueDifference; - transformedItem[`% ${origCol.key}`] = percentDifferenceNum; + transformedItem[`${t('sv_current')} ${origCol.key}`] = originalValue; + transformedItem[`${t('sv_previous')} ${origCol.key}`] = comparisonValue; + transformedItem[`${t('sv_change')} ${origCol.key}`] = valueDifference; + transformedItem[`${t('sv_change_percentage')} ${origCol.key}`] = percentDifferenceNum; } }); @@ -355,11 +355,11 @@ const processComparisonColumns = ( return [ { ...col, - label: t('Main'), - key: `${t('Main')} ${col.key}`, - config: getComparisonColConfig(t('Main'), col.key, columnConfig), + label: t('sv_current'), + key: `${t('sv_current')} ${col.key}`, + config: getComparisonColConfig(t('sv_current'), col.key, columnConfig), formatter: getComparisonColFormatter( - t('Main'), + t('sv_current'), col, columnConfig, savedFormat, @@ -368,11 +368,11 @@ const processComparisonColumns = ( }, { ...col, - label: `#`, - key: `# ${col.key}`, - config: getComparisonColConfig(`#`, col.key, columnConfig), + label: t('sv_previous'), + key: `${t('sv_previous')} ${col.key}`, + config: getComparisonColConfig(t('sv_previous'), col.key, columnConfig), formatter: getComparisonColFormatter( - `#`, + t('sv_previous'), col, columnConfig, savedFormat, @@ -381,11 +381,11 @@ const processComparisonColumns = ( }, { ...col, - label: `△`, - key: `△ ${col.key}`, - config: getComparisonColConfig(`△`, col.key, columnConfig), + label: t('sv_change'), + key: `${t('sv_change')} ${col.key}`, + config: getComparisonColConfig(t('sv_change'), col.key, columnConfig), formatter: getComparisonColFormatter( - `△`, + t('sv_change'), col, columnConfig, savedFormat, @@ -394,11 +394,11 @@ const processComparisonColumns = ( }, { ...col, - label: `%`, - key: `% ${col.key}`, - config: getComparisonColConfig(`%`, col.key, columnConfig), + label: t('sv_change_percentage'), + key: `${t('sv_change_percentage')} ${col.key}`, + config: getComparisonColConfig(t('sv_change_percentage'), col.key, columnConfig), formatter: getComparisonColFormatter( - `%`, + t('sv_change_percentage'), col, columnConfig, savedFormat, diff --git a/superset-frontend/scripts/lokalise_merger.sh b/superset-frontend/scripts/lokalise_merger.sh new file mode 100755 index 0000000000000..d7953ca3b4274 --- /dev/null +++ b/superset-frontend/scripts/lokalise_merger.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +for file in $( find ../superset/translations/** -name '*.json' ); +do + extension=${file##*.} + filename="${file%.*}" + if [ $extension == "json" ] + then + locale=${file/#..\/superset\/translations\/} + locale=${locale%\/LC_MESSAGES\/messages.json} + if [ -f "../locales/$locale/translation.json" ] + then + output=$(jq -s '.[0].locale_data.superset *= (.[1] | with_entries({key:.key,value:[.value]})) | first' $file "../locales/$locale/translation.json") + echo $output > $file + fi + fi +done diff --git a/superset-frontend/src/components/Chart/Chart.tsx b/superset-frontend/src/components/Chart/Chart.tsx index 3526a26b3f9ce..ad2084a296f83 100644 --- a/superset-frontend/src/components/Chart/Chart.tsx +++ b/superset-frontend/src/components/Chart/Chart.tsx @@ -283,9 +283,7 @@ class Chart extends PureComponent { } renderSpinner(databaseName: string | undefined) { - const message = databaseName - ? t('Waiting on %s', databaseName) - : t('Waiting on database...'); + const message = t('Loading...'); return ( diff --git a/superset-frontend/src/components/DynamicEditableTitle/index.tsx b/superset-frontend/src/components/DynamicEditableTitle/index.tsx index dd439194383ad..b8e1dac79b25b 100644 --- a/superset-frontend/src/components/DynamicEditableTitle/index.tsx +++ b/superset-frontend/src/components/DynamicEditableTitle/index.tsx @@ -192,7 +192,7 @@ export const DynamicEditableTitle = memo( onClick={handleClick} onKeyPress={handleKeyPress} placeholder={placeholder} - value={currentTitle} + value={isEditing ? currentTitle : t(currentTitle)} css={css` cursor: ${isEditing ? 'text' : 'pointer'}; diff --git a/superset-frontend/src/components/EditableTitle/index.tsx b/superset-frontend/src/components/EditableTitle/index.tsx index fc01fb9f63b99..de84cb04f8fa0 100644 --- a/superset-frontend/src/components/EditableTitle/index.tsx +++ b/superset-frontend/src/components/EditableTitle/index.tsx @@ -232,10 +232,10 @@ export default function EditableTitle({ display: inline-block; `} > - {value} + {t(value)} ) : ( - {value} + {t(value)} ); } return ( diff --git a/superset-frontend/src/components/Select/Select.tsx b/superset-frontend/src/components/Select/Select.tsx index 91f9515448dc2..f760dbff6820d 100644 --- a/superset-frontend/src/components/Select/Select.tsx +++ b/superset-frontend/src/components/Select/Select.tsx @@ -447,7 +447,7 @@ const Select = forwardRef( const selectAllLabel = useMemo( () => () => // TODO: localize - `${SELECT_ALL_VALUE} (${formatNumber( + `${t(SELECT_ALL_VALUE as string)} (${formatNumber( NumberFormats.INTEGER, selectAllEligible.length, )})`, diff --git a/superset-frontend/src/constants.ts b/superset-frontend/src/constants.ts index c4fd0e94f9c3f..3869fe8a0e2ed 100644 --- a/superset-frontend/src/constants.ts +++ b/superset-frontend/src/constants.ts @@ -111,6 +111,14 @@ export const URL_PARAMS = { name: 'focused_chart', type: 'number', }, + language: { + name: 'lang', + type: 'string', + }, + currency: { + name: 'currency', + type: 'string', + }, } as const; export const RESERVED_CHART_URL_PARAMS: string[] = [ diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index e5caed6968642..7670618ee2a53 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -444,6 +444,22 @@ const DashboardBuilder = () => { const [barTopOffset, setBarTopOffset] = useState(0); + const fireResizeEventToParent = () => { + const data = { + event: 'supersetContentRefreshed', + width: window.document.body.scrollWidth, + height: window.document.body.scrollHeight, + }; + window.parent.postMessage(data, '*'); + }; + + const fireWindowClickToParent = () => { + const data = { + event: 'supersetWindowClick', + }; + window.parent.postMessage(data, '*'); + }; + useEffect(() => { setBarTopOffset(headerRef.current?.getBoundingClientRect()?.height || 0); @@ -453,13 +469,17 @@ const DashboardBuilder = () => { setBarTopOffset( current => entries?.[0]?.contentRect?.height || current, ); + fireResizeEventToParent(); }); observer.observe(headerRef.current); } + document.addEventListener('click', fireWindowClickToParent); + return () => { observer?.disconnect(); + document.removeEventListener('click', fireWindowClickToParent); }; }, []); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx index 089b6857cb3f4..a34637bc4bfd8 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx @@ -22,7 +22,7 @@ import { InPortal, OutPortal, } from 'react-reverse-portal'; -import { styled, SupersetTheme, truncationCSS } from '@superset-ui/core'; +import { t, styled, SupersetTheme, truncationCSS } from '@superset-ui/core'; import { FormItem as StyledFormItem, Form } from 'src/components/Form'; import { Tooltip } from 'src/components/Tooltip'; import { FilterBarOrientation } from 'src/dashboard/types'; @@ -252,15 +252,12 @@ const FilterControl = ({ const label = useMemo( () => ( - - {name} + + {t(name)} {isRequired && } {filter.description?.trim() && ( - + )} {icon} diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js index dcc79a900f79d..6a7af30945759 100644 --- a/superset-frontend/webpack.config.js +++ b/superset-frontend/webpack.config.js @@ -240,7 +240,7 @@ const config = { }, }, output, - stats: 'minimal', + stats: 'none', /* Silence warning for missing export in @data-ui's internal structure. This issue arises from an internal implementation detail of @data-ui. As it's diff --git a/superset/common/query_actions.py b/superset/common/query_actions.py index 9e61de6e1aaa2..72066b8cc5bf5 100644 --- a/superset/common/query_actions.py +++ b/superset/common/query_actions.py @@ -21,7 +21,7 @@ from flask_babel import _ -from superset import app +from superset import app, security_manager from superset.common.chart_data import ChartDataResultType from superset.common.db_query_status import QueryStatus from superset.connectors.sqla.models import BaseDatasource @@ -87,7 +87,10 @@ def _get_query( datasource = _get_datasource(query_context, query_obj) result = {"language": datasource.query_language} try: - result["query"] = datasource.get_query_str(query_obj.to_dict()) + if security_manager.can_access('can_view_query', 'Dashboard'): + result["query"] = datasource.get_query_str(query_obj.to_dict()) + else: + result["query"] = 'Forbidden' except QueryObjectValidationError as err: result["error"] = err.message return result diff --git a/superset/config.py b/superset/config.py index 0bce5a876ea43..605c45e13f2d2 100644 --- a/superset/config.py +++ b/superset/config.py @@ -393,7 +393,14 @@ def _try_json_readsha(filepath: str, length: int) -> str | None: } # Turning off i18n by default as translation in most languages are # incomplete and not well maintained. -LANGUAGES = {} +LANGUAGES = { + "en": {"flag": "us", "name": "English"}, + "es": {"flag": "es", "name": "Spanish"}, + "it": {"flag": "it", "name": "Italian"}, + "fr": {"flag": "fr", "name": "French"}, + "de": {"flag": "de", "name": "German"}, + "nl": {"flag": "nl", "name": "Dutch"}, +} # Override the default d3 locale format diff --git a/superset/jinja_context.py b/superset/jinja_context.py index b0e29505a0d0b..02fbfbd064b60 100644 --- a/superset/jinja_context.py +++ b/superset/jinja_context.py @@ -356,22 +356,25 @@ def get_filters(self, column: str, remove_filter: bool = False) -> list[Filter]: if ( flt.get("expressionType") == "SIMPLE" and flt.get("clause") == "WHERE" - and flt.get("subject") == column and val ): - if remove_filter: - if column not in self.removed_filters: - self.removed_filters.append(column) - if column not in self.applied_filters: - self.applied_filters.append(column) - - if op in ( - FilterOperator.IN.value, - FilterOperator.NOT_IN.value, - ) and not isinstance(val, list): - val = [val] - - filters.append({"op": op, "col": column, "val": val}) + if (flt.get("subject") == column) or ( + isinstance(flt.get("subject"), dict) + and flt.get("subject").get("label") == column + ): + if remove_filter: + if column not in self.removed_filters: + self.removed_filters.append(column) + if column not in self.applied_filters: + self.applied_filters.append(column) + + if op in ( + FilterOperator.IN.value, + FilterOperator.NOT_IN.value, + ) and not isinstance(val, list): + val = [val] + + filters.append({"op": op, "col": column, "val": val}) return filters diff --git a/superset/models/helpers.py b/superset/models/helpers.py index 784d86d7292d3..e031e8c6c8fbc 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -51,7 +51,7 @@ from sqlalchemy.sql.selectable import Alias, TableClause from sqlalchemy_utils import UUIDType -from superset import app, db, is_feature_enabled +from superset import app, db, is_feature_enabled, security_manager from superset.advanced_data_type.types import AdvancedDataTypeResponse from superset.common.db_query_status import QueryStatus from superset.common.utils.time_range_utils import get_since_until_from_time_range @@ -579,7 +579,7 @@ def __init__( # pylint: disable=too-many-arguments to_dttm: Optional[datetime] = None, ) -> None: self.df = df - self.query = query + self.query = query if security_manager.can_access('can_view_query', 'Dashboard') else '' self.duration = duration self.applied_template_filters = applied_template_filters or [] self.applied_filter_columns = applied_filter_columns or [] diff --git a/superset/security/manager.py b/superset/security/manager.py index 38fff7a3f44f8..11180e7c4409e 100644 --- a/superset/security/manager.py +++ b/superset/security/manager.py @@ -2662,7 +2662,9 @@ def has_guest_access(self, dashboard: "Dashboard") -> bool: return False dashboards = [ - r for r in user.resources if r["type"] == GuestTokenResourceType.DASHBOARD + r + for r in user.resources + if r["type"] == GuestTokenResourceType.DASHBOARD.value ] # TODO (embedded): remove this check once uuids are rolled out diff --git a/superset/translations/utils.py b/superset/translations/utils.py index 36be0331a4d77..e4f8ae80ffd10 100644 --- a/superset/translations/utils.py +++ b/superset/translations/utils.py @@ -38,7 +38,7 @@ def get_language_pack(locale: str) -> Optional[dict[str, Any]]: pack = ALL_LANGUAGE_PACKS.get(locale) if not pack: filename = DIR + f"/{locale}/LC_MESSAGES/messages.json" - if not locale or locale == "en": + if not locale: # Forcing a dummy, quasy-empty language pack for English since the file # in the en directory is contains data with empty mappings filename = DIR + "/empty_language_pack.json" @@ -50,5 +50,5 @@ def get_language_pack(locale: str) -> Optional[dict[str, Any]]: logger.error( "Error loading language pack for, falling back on en %s", locale ) - pack = get_language_pack("en") + pack = get_language_pack('') return pack diff --git a/superset/views/base.py b/superset/views/base.py index 33ba43b2f6870..9ddd5be422b32 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -31,6 +31,7 @@ g, get_flashed_messages, redirect, + request, Response, session, ) @@ -348,8 +349,11 @@ def cached_common_bootstrap_data( # pylint: disable=unused-argument def common_bootstrap_payload() -> dict[str, Any]: + locale = get_locale() + if request.args.get("lang"): + locale = Locale.parse(request.args.get("lang")) return { - **cached_common_bootstrap_data(utils.get_user_id(), get_locale()), + **cached_common_bootstrap_data(utils.get_user_id(), locale), "flash_messages": get_flashed_messages(with_categories=True), }