diff --git a/.github/workflows/ui-tests-playwright.yml b/.github/workflows/ui-tests-playwright.yml
index c8b02e8e82..8cc5ba32a5 100644
--- a/.github/workflows/ui-tests-playwright.yml
+++ b/.github/workflows/ui-tests-playwright.yml
@@ -7,6 +7,7 @@ on:
- 'frontend/*.json'
- 'frontend/*.js'
- 'frontend/*.ts'
+ - '.github/workflows/ui-tests-playwright.yml'
jobs:
test:
@@ -69,11 +70,13 @@ jobs:
cat .env
- name: Build frontend
- run: cd frontend && yarn build
+ working-directory: frontend
+ run: yarn build
id: build-frontend
- name: Run Playwright tests
- run: cd frontend && yarn playwright test
+ working-directory: frontend
+ run: yarn playwright test
- uses: actions/upload-artifact@v4
if: always()
diff --git a/backend/btrixcloud/main.py b/backend/btrixcloud/main.py
index 71e0150b15..f6b678cb82 100644
--- a/backend/btrixcloud/main.py
+++ b/backend/btrixcloud/main.py
@@ -6,6 +6,7 @@
import os
import asyncio
import sys
+from typing import List, Optional
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
@@ -121,6 +122,8 @@ class SettingsResponse(BaseModel):
salesEmail: str = ""
supportEmail: str = ""
+ localesEnabled: Optional[List[str]]
+
# ============================================================================
# pylint: disable=too-many-locals, duplicate-code
@@ -150,6 +153,11 @@ def main() -> None:
signUpUrl=os.environ.get("SIGN_UP_URL", ""),
salesEmail=os.environ.get("SALES_EMAIL", ""),
supportEmail=os.environ.get("EMAIL_SUPPORT", ""),
+ localesEnabled=(
+ [lang.strip() for lang in os.environ.get("LOCALES_ENABLED", "").split(",")]
+ if os.environ.get("LOCALES_ENABLED")
+ else None
+ ),
)
invites = init_invites(mdb, email)
diff --git a/backend/test/test_api.py b/backend/test/test_api.py
index 5c0a1d68b1..fbe265c90e 100644
--- a/backend/test/test_api.py
+++ b/backend/test/test_api.py
@@ -50,4 +50,5 @@ def test_api_settings():
"signUpUrl": "",
"salesEmail": "",
"supportEmail": "",
+ "localesEnabled": ["en", "es"],
}
diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml
index 4da46697fe..2a57a6d66a 100644
--- a/chart/templates/configmap.yaml
+++ b/chart/templates/configmap.yaml
@@ -83,6 +83,8 @@ data:
BACKEND_IMAGE_PULL_POLICY: "{{ .Values.backend_pull_policy }}"
+ LOCALES_ENABLED: "{{ .Values.locales_enabled }}"
+
---
apiVersion: v1
diff --git a/chart/values.yaml b/chart/values.yaml
index 2caeb1c5ad..163ce63e63 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -1,3 +1,9 @@
+# Global Settings
+# =========================================
+
+# locales available to choose from in the front end
+locales_enabled: "en,es"
+
# Crawler Settings
# =========================================
diff --git a/frontend/package.json b/frontend/package.json
index 312a06927a..c972a2766e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,6 +6,7 @@
"dependencies": {
"@cheap-glitch/mi-cron": "^1.0.1",
"@formatjs/intl-durationformat": "^0.6.4",
+ "@formatjs/intl-localematcher": "^0.5.9",
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"@lit/localize": "^0.12.1",
"@lit/task": "^1.0.0",
diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts
index dbf85a6499..58a5212e5f 100644
--- a/frontend/playwright.config.ts
+++ b/frontend/playwright.config.ts
@@ -90,5 +90,6 @@ export default defineConfig({
command: "yarn serve",
port: 9871,
reuseExistingServer: !process.env.CI,
+ stdout: "pipe",
},
});
diff --git a/frontend/scripts/serve.js b/frontend/scripts/serve.js
index 7163efa331..b790435d36 100644
--- a/frontend/scripts/serve.js
+++ b/frontend/scripts/serve.js
@@ -1,10 +1,15 @@
// Serve app locally without building with webpack, e.g. for e2e
+const fs = require("fs");
const path = require("path");
-const connectHistoryApiFallback = require("connect-history-api-fallback");
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
+const distPath = path.join(process.cwd(), "dist");
+if (!fs.existsSync(path.join(distPath, "index.html"))) {
+ throw new Error("dist folder is missing");
+}
+
const dotEnvPath = path.resolve(process.cwd(), ".env.local");
require("dotenv").config({
path: dotEnvPath,
@@ -18,10 +23,20 @@ const { devServer } = devConfig;
devServer.setupMiddlewares([], { app });
-app.use("/", express.static("dist"));
Object.keys(devServer.proxy).forEach((path) => {
- app.use(path, createProxyMiddleware(devServer.proxy[path]));
+ app.use(
+ path,
+ createProxyMiddleware({
+ target: devServer.proxy[path],
+ changeOrigin: true,
+ }),
+ );
+});
+app.use("/", express.static(distPath));
+app.get("/*", (req, res) => {
+ res.sendFile(path.join(distPath, "index.html"));
});
-app.use(connectHistoryApiFallback());
-app.listen(9871);
+app.listen(9871, () => {
+ console.log("Server listening on http://localhost:9871");
+});
diff --git a/frontend/src/components/orgs-list.ts b/frontend/src/components/orgs-list.ts
index 4770a2da19..50afa3afb6 100644
--- a/frontend/src/components/orgs-list.ts
+++ b/frontend/src/components/orgs-list.ts
@@ -319,7 +319,7 @@ export class OrgsList extends BtrixElement {
${msg(
html`Deleting an org will delete all
-