Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow configuring available languages from helm chart #2230

Merged
merged 55 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
040f186
first pass at optional hardcoded languages
emma-sg Dec 10, 2024
051b4dd
format
emma-sg Dec 10, 2024
d5671ef
fix test
emma-sg Dec 10, 2024
2227267
remove browser locales from language dropdown
ikreymer Dec 11, 2024
7641fd5
create new duration formatter when changing language
emma-sg Dec 11, 2024
c939925
ensure user locales are included when formatting numbers, dates, etc
emma-sg Dec 11, 2024
322456b
use messages for ordinal suffixes when formatting numbers
emma-sg Dec 11, 2024
9ef7bd5
ensure everything relying on language lists pulls from localize
emma-sg Dec 11, 2024
9b88a18
use user locale (matching set language) on html element
emma-sg Dec 11, 2024
a2b3871
fix tests
emma-sg Dec 11, 2024
bae0594
fix & add more tests
emma-sg Dec 11, 2024
adab438
use Intl.Locale for getting normalized language from locale
emma-sg Dec 11, 2024
ecd75d7
use non-fp version of lodash fn
emma-sg Dec 11, 2024
69fa3d6
improve initial locale detection & matching
emma-sg Dec 11, 2024
6a3eb9b
better name default lang fn
emma-sg Dec 11, 2024
4052bfa
add browser language setting toggle
emma-sg Dec 11, 2024
66991f1
fix rerender & bug in language selector
emma-sg Dec 11, 2024
b0f1be7
use `useBrowserLanguageForFormatting` preference
emma-sg Dec 11, 2024
339244e
replace `<sl-format-bytes>` with `this.localize.bytes` so that correc…
emma-sg Dec 11, 2024
6c5cf88
swap out `sl-format-date` for `btrix-format-date`
emma-sg Dec 12, 2024
f723ce2
remove unused shoelace format imports
emma-sg Dec 12, 2024
e912f41
fix test i think??
emma-sg Dec 12, 2024
fe0a5ae
fix localize change being async now
emma-sg Dec 12, 2024
82b734d
fix tests
emma-sg Dec 12, 2024
13f2da5
cache formatters by combination key of app lang, user prefs, & naviga…
emma-sg Dec 13, 2024
ee8850a
add demo of value formatting
emma-sg Dec 13, 2024
ad0eb23
revert accidentally-committed playwright test change
emma-sg Dec 13, 2024
1ad8eed
try older node version??
emma-sg Dec 13, 2024
16a1503
Revert "try older node version??"
emma-sg Dec 13, 2024
438c952
try adding more timeout to web server?
emma-sg Dec 13, 2024
ed257a8
ci: attempt to fix ci test
ikreymer Dec 13, 2024
f7204a9
force run
ikreymer Dec 13, 2024
79f686a
Merge branch 'main' into i18n-hardcode-locales
ikreymer Dec 13, 2024
3a23146
build & test in same step
emma-sg Dec 13, 2024
eb14aa2
undo last & try skipping page goto?
emma-sg Dec 13, 2024
befc399
un-skip playwright cache
emma-sg Dec 13, 2024
3494f51
upload built app for debugging
emma-sg Dec 13, 2024
eabfd33
serve correct dist folder
emma-sg Dec 13, 2024
60bb96b
add checks & logs to script
emma-sg Dec 13, 2024
b04c639
re-add base url goto
emma-sg Dec 13, 2024
465ea58
run with webserver debug logs
emma-sg Dec 13, 2024
aab3b05
log base url
emma-sg Dec 13, 2024
1ad1a99
try super simple fetch??
emma-sg Dec 13, 2024
5ab7328
Revert "try super simple fetch??"
emma-sg Dec 13, 2024
3fb16dd
try yarn start
emma-sg Dec 13, 2024
3ec6930
Revert "try yarn start"
emma-sg Dec 13, 2024
e3e7968
show all debug logs
emma-sg Dec 13, 2024
0677e05
catch getAppSettings failures
emma-sg Dec 13, 2024
147dd13
try enabling `changeOrigin` in createProxyMiddleware
emma-sg Dec 13, 2024
bf196e7
remove extra debugging stuff & clean up
emma-sg Dec 13, 2024
766e286
remove extra-long webserver timeout
emma-sg Dec 13, 2024
c3c12c4
add missing ids to ordinal suffix messages
emma-sg Dec 14, 2024
9488357
split out ordinal helper from number formatter
emma-sg Dec 14, 2024
c8a66ff
tests
emma-sg Dec 14, 2024
4f53d76
remove extra console logs
emma-sg Dec 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ui-tests-playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- 'frontend/*.json'
- 'frontend/*.js'
- 'frontend/*.ts'
- '.github/workflows/ui-tests-playwright.yml'

jobs:
test:
Expand Down Expand Up @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions backend/btrixcloud/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -121,6 +122,8 @@ class SettingsResponse(BaseModel):
salesEmail: str = ""
supportEmail: str = ""

localesEnabled: Optional[List[str]]


# ============================================================================
# pylint: disable=too-many-locals, duplicate-code
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions backend/test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ def test_api_settings():
"signUpUrl": "",
"salesEmail": "",
"supportEmail": "",
"localesEnabled": ["en", "es"],
}
2 changes: 2 additions & 0 deletions chart/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ data:

BACKEND_IMAGE_PULL_POLICY: "{{ .Values.backend_pull_policy }}"

LOCALES_ENABLED: "{{ .Values.locales_enabled }}"


---
apiVersion: v1
Expand Down
6 changes: 6 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Global Settings
# =========================================

# locales available to choose from in the front end
locales_enabled: "en,es"


# Crawler Settings
# =========================================
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,6 @@ export default defineConfig({
command: "yarn serve",
port: 9871,
reuseExistingServer: !process.env.CI,
stdout: "pipe",
},
});
25 changes: 20 additions & 5 deletions frontend/scripts/serve.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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");
});
33 changes: 9 additions & 24 deletions frontend/src/components/orgs-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,34 +319,28 @@ export class OrgsList extends BtrixElement {
${msg(
html`Deleting an org will delete all
<strong class="font-semibold">
<sl-format-bytes value=${org.bytesStored}></sl-format-bytes>
${this.localize.bytes(org.bytesStored)}
</strong>
of data associated with the org.`,
)}
</p>
<ul class="mb-3 text-neutral-600">
<li>
${msg(
html`Crawls:
<sl-format-bytes
value=${org.bytesStoredCrawls}
></sl-format-bytes>`,
html`${msg("Crawls")}:
${this.localize.bytes(org.bytesStoredCrawls)}`,
)}
</li>
<li>
${msg(
html`Uploads:
<sl-format-bytes
value=${org.bytesStoredUploads}
></sl-format-bytes>`,
html`${msg("Uploads")}:
${this.localize.bytes(org.bytesStoredUploads)}`,
)}
</li>
<li>
${msg(
html`Profiles:
<sl-format-bytes
value=${org.bytesStoredProfiles}
></sl-format-bytes>`,
html`${msg("Profiles")}:
${this.localize.bytes(org.bytesStoredProfiles)}`,
)}
</li>
</ul>
Expand Down Expand Up @@ -627,23 +621,14 @@ export class OrgsList extends BtrixElement {
</btrix-table-cell>

<btrix-table-cell class="p-2">
<sl-format-date
class="truncate"
date=${org.created}
month="2-digit"
day="2-digit"
year="2-digit"
></sl-format-date>
${this.localize.date(org.created, { dateStyle: "short" })}
</btrix-table-cell>
<btrix-table-cell class="p-2">
${memberCount ? this.localize.number(memberCount) : none}
</btrix-table-cell>
<btrix-table-cell class="p-2">
${org.bytesStored
? html`<sl-format-bytes
value=${org.bytesStored}
display="narrow"
></sl-format-bytes>`
? this.localize.bytes(org.bytesStored, { unitDisplay: "narrow" })
: none}
</btrix-table-cell>
<btrix-table-cell class="p-1">
Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/ui/config-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,7 @@ export class ConfigDetails extends BtrixElement {
const renderSize = (valueBytes?: number | null) => {
// Eventually we will want to set this to the selected locale
if (valueBytes) {
return html`<sl-format-bytes
value=${valueBytes}
display="narrow"
></sl-format-bytes>`;
return this.localize.bytes(valueBytes, { unitDisplay: "narrow" });
}

return html`<span class="text-neutral-400"
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/components/ui/file-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
queryAssignedElements,
} from "lit/decorators.js";

import { BtrixElement } from "@/classes/BtrixElement";
import { TailwindElement } from "@/classes/TailwindElement";
import { truncate } from "@/utils/css";

Expand All @@ -19,7 +20,7 @@ export type FileRemoveEvent = CustomEvent<FileRemoveDetail>;
*/
@localized()
@customElement("btrix-file-list-item")
export class FileListItem extends TailwindElement {
export class FileListItem extends BtrixElement {
static styles = [
truncate,
css`
Expand Down Expand Up @@ -82,11 +83,11 @@ export class FileListItem extends TailwindElement {
<div class="name">${this.file.name}</div>
<div class="size">
${this.progressValue !== undefined
? html`<sl-format-bytes
value=${(this.progressValue / 100) * this.file.size}
></sl-format-bytes>
/ `
: ""}<sl-format-bytes value=${this.file.size}></sl-format-bytes>
? html`${this.localize.bytes(
(this.progressValue / 100) * this.file.size,
)}
/ `
: ""}${this.localize.bytes(this.file.size)}
</div>
</div>
<div class="actions">
Expand Down
90 changes: 90 additions & 0 deletions frontend/src/components/ui/format-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { localized } from "@lit/localize";
import { html, LitElement } from "lit";
import { customElement } from "lit/decorators/custom-element.js";
import { property } from "lit/decorators/property.js";

import { LocalizeController } from "@/controllers/localize";

/**
* Re-implementation of Shoelace's `<sl-format-date>` element using
* Browsertrix's localization implementation.
*
* This allows for multiple locales to be passed into the date formatter, in
* order of the user's preferences.
*/
@customElement("btrix-format-date")
@localized()
export class FormatDate extends LitElement {
private readonly localize = new LocalizeController(this);

/**
* The date/time to format. If not set, the current date and time will be used. When passing a string, it's strongly
* recommended to use the ISO 8601 format to ensure timezones are handled correctly. To convert a date to this format
* in JavaScript, use [`date.toISOString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString).
*/
@property() date?: Date | string | null = new Date();

/** The format for displaying the weekday. */
@property() weekday?: "narrow" | "short" | "long";

/** The format for displaying the era. */
@property() era?: "narrow" | "short" | "long";

/** The format for displaying the year. */
@property() year?: "numeric" | "2-digit";

/** The format for displaying the month. */
@property() month?: "numeric" | "2-digit" | "narrow" | "short" | "long";

/** The format for displaying the day. */
@property() day?: "numeric" | "2-digit";

/** The format for displaying the hour. */
@property() hour?: "numeric" | "2-digit";

/** The format for displaying the minute. */
@property() minute?: "numeric" | "2-digit";

/** The format for displaying the second. */
@property() second?: "numeric" | "2-digit";

/** The format for displaying the time. */
@property({ attribute: "time-zone-name" }) timeZoneName?: "short" | "long";

/** The time zone to express the time in. */
@property({ attribute: "time-zone" }) timeZone?: string;

/** The format for displaying the hour. */
@property({ attribute: "hour-format" }) hourFormat: "auto" | "12" | "24" =
"auto";

render() {
if (!this.date) return undefined;
const date = new Date(this.date);
const hour12 =
this.hourFormat === "auto" ? undefined : this.hourFormat === "12";

// Check for an invalid date
if (isNaN(date.getMilliseconds())) {
return undefined;
}

return html`
<time datetime=${date.toISOString()}>
${this.localize.date(date, {
weekday: this.weekday,
era: this.era,
year: this.year,
month: this.month,
day: this.day,
hour: this.hour,
minute: this.minute,
second: this.second,
timeZoneName: this.timeZoneName,
timeZone: this.timeZone,
hour12: hour12,
})}
</time>
`;
}
}
5 changes: 3 additions & 2 deletions frontend/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import("./copy-button");
import("./copy-field");
import("./details");
import("./file-list");
import("./format-date");
import("./inline-input");
import("./language-select");
import("./user-language-select");
import("./markdown-editor");
import("./markdown-viewer");
import("./menu-item-link");
Expand All @@ -30,9 +30,10 @@ import("./pw-strength-alert");
import("./relative-duration");
import("./search-combobox");
import("./section-heading");
import("./select-crawler");
import("./select-crawler-proxy");
import("./select-crawler");
import("./table");
import("./tag-input");
import("./tag");
import("./time-input");
import("./user-language-select");
4 changes: 2 additions & 2 deletions frontend/src/components/ui/language-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import sortBy from "lodash/fp/sortBy";

import { allLanguageCodes, type LanguageCode } from "@/types/localization";
import { getBrowserLang } from "@/utils/localize";
import { getDefaultLang } from "@/utils/localize";

const languages = sortBy("name")(
ISO6391.getLanguages(allLanguageCodes),
Expand Down Expand Up @@ -53,7 +53,7 @@ export class LanguageSelect extends LitElement {
return html`
<sl-select
placeholder=${msg("Browser Default")}
value=${ifDefined(this.value || getBrowserLang() || undefined)}
value=${ifDefined(this.value || getDefaultLang())}
size=${ifDefined(this.size)}
?hoist=${this.hoist}
@sl-change=${async (e: Event) => {
Expand Down
Loading
Loading