From ab9edfa064918d29080016724b0428d88b138a47 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 12 Nov 2024 13:59:34 -0800 Subject: [PATCH] feat: Add details on translation status (#2139) languages specified in `translatedLocales` - Updates user language preference beta badge text - Adds link to translation contribution docs - Refactors component name --------- Co-authored-by: SuaYoo Co-authored-by: Henry Wilkinson Co-authored-by: emma --- .../ISSUE_TEMPLATE/localization-request.yml | 2 +- frontend/docs/docs/develop/localization.md | 2 + frontend/src/assets/icons/flask-fill.svg | 3 + frontend/src/components/beta-badges.ts | 23 +++-- frontend/src/components/ui/index.ts | 2 +- frontend/src/components/ui/locale-picker.ts | 77 ---------------- .../src/components/ui/user-language-select.ts | 91 +++++++++++++++++++ .../features/crawl-workflows/workflow-list.ts | 11 +-- frontend/src/index.ts | 12 ++- frontend/src/pages/account-settings.ts | 43 +++++++-- .../pages/org/settings/components/billing.ts | 2 +- frontend/src/types/localization.ts | 3 + frontend/src/utils/cron.ts | 3 +- frontend/xliff/es.xlf | 20 +++- 14 files changed, 180 insertions(+), 114 deletions(-) create mode 100644 frontend/src/assets/icons/flask-fill.svg delete mode 100644 frontend/src/components/ui/locale-picker.ts create mode 100644 frontend/src/components/ui/user-language-select.ts diff --git a/.github/ISSUE_TEMPLATE/localization-request.yml b/.github/ISSUE_TEMPLATE/localization-request.yml index e2f2c8c050..b1f7a6c403 100644 --- a/.github/ISSUE_TEMPLATE/localization-request.yml +++ b/.github/ISSUE_TEMPLATE/localization-request.yml @@ -1,7 +1,7 @@ name: Localization Request description: Request a new language or translation. title: "[L10N]: " -labels: ["enhancement"] +labels: ["localization"] body: - type: textarea attributes: diff --git a/frontend/docs/docs/develop/localization.md b/frontend/docs/docs/develop/localization.md index bfd9963569..f704cb7497 100644 --- a/frontend/docs/docs/develop/localization.md +++ b/frontend/docs/docs/develop/localization.md @@ -35,6 +35,8 @@ To add a new language directly through code change: 3. Open a pull request with the changes. 4. Once the pull request is merged, manually refresh the language list in the [Weblate Browsertrix project](https://hosted.weblate.org/projects/browsertrix). Translations are managed entirely through the Weblate interface. +New languages will be available in user preferences only after the app is redeployed. + ## Making Strings Localizable All text should be wrapped in the `msg` helper to make them localizable: diff --git a/frontend/src/assets/icons/flask-fill.svg b/frontend/src/assets/icons/flask-fill.svg new file mode 100644 index 0000000000..c0a2265a81 --- /dev/null +++ b/frontend/src/assets/icons/flask-fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/beta-badges.ts b/frontend/src/components/beta-badges.ts index 40c8b17bdc..cbe9d521c5 100644 --- a/frontend/src/components/beta-badges.ts +++ b/frontend/src/components/beta-badges.ts @@ -14,12 +14,14 @@ const styles = unsafeCSS(stylesheet); export class BetaIcon extends TailwindElement { static styles = styles; render() { - return html` + return html` + ${msg("Beta feature")} `; } } @@ -34,15 +36,20 @@ export class BetaBadge extends TailwindElement { render() { return html` -
- ${msg("This part of Browsertrix is in beta!")} - ${msg( - "Parts might change or be broken. Please share your thoughts with us!", - )} +
+ + ${msg("This part of Browsertrix is in beta!")} +

+ ${msg( + "Parts might change or be broken. Please share your thoughts with us!", + )} +

+
{ - this.localeNames![locale] = new Intl.DisplayNames([locale], { - type: "language", - }).of(locale.toUpperCase())!; - }; - - firstUpdated() { - this.localeNames = {} as LocaleNames; - allLocales.forEach(this.setLocaleName); - } - - render() { - if (!this.localeNames) { - return; - } - - const selectedLocale = - this.appState.userPreferences?.locale || sourceLocale; - - return html` - - - ${this.localeNames[selectedLocale as LocaleCodeEnum]} - - - ${allLocales.map( - (locale) => - html` - ${this.localeNames![locale]} - `, - )} - - - `; - } - - async localeChanged(event: SlSelectEvent) { - const newLocale = event.detail.item.value as LocaleCodeEnum; - - AppStateService.partialUpdateUserPreferences({ locale: newLocale }); - - if (newLocale !== getLocale()) { - void setLocale(newLocale); - } - } -} diff --git a/frontend/src/components/ui/user-language-select.ts b/frontend/src/components/ui/user-language-select.ts new file mode 100644 index 0000000000..eba4547ad3 --- /dev/null +++ b/frontend/src/components/ui/user-language-select.ts @@ -0,0 +1,91 @@ +import type { SlSelectEvent } from "@shoelace-style/shoelace"; +import { html } from "lit"; +import { customElement, state } from "lit/decorators.js"; + +import { sourceLocale } from "@/__generated__/locale-codes"; +import { BtrixElement } from "@/classes/BtrixElement"; +import { allLocales, type LocaleCodeEnum } from "@/types/localization"; +import { getLocale, setLocale } from "@/utils/localization"; +import { AppStateService } from "@/utils/state"; + +/** + * Select language that Browsertrix app will be shown in + */ +@customElement("btrix-user-language-select") +export class LocalePicker extends BtrixElement { + @state() + private localeNames: { [locale: string]: string } = {}; + + firstUpdated() { + this.setLocaleNames(); + } + + private setLocaleNames() { + const localeNames: LocalePicker["localeNames"] = {}; + + // TODO Add browser-preferred languages + // https://github.com/webrecorder/browsertrix/issues/2143 + allLocales.forEach((locale) => { + const name = new Intl.DisplayNames([locale], { + type: "language", + }).of(locale); + + if (!name) return; + + localeNames[locale] = name; + }); + + this.localeNames = localeNames; + } + + render() { + const selectedLocale = + this.appState.userPreferences?.locale || sourceLocale; + + return html` + + + + ${this.localeNames[selectedLocale as LocaleCodeEnum]} + + + ${Object.keys(this.localeNames) + .sort() + .map( + (locale) => + html` + ${this.localeNames[locale]} + `, + )} + + + `; + } + + async localeChanged(event: SlSelectEvent) { + const newLocale = event.detail.item.value as LocaleCodeEnum; + + AppStateService.partialUpdateUserPreferences({ locale: newLocale }); + + if (newLocale !== getLocale()) { + void setLocale(newLocale); + } + } +} diff --git a/frontend/src/features/crawl-workflows/workflow-list.ts b/frontend/src/features/crawl-workflows/workflow-list.ts index 74f8a1172f..7ecc34f50e 100644 --- a/frontend/src/features/crawl-workflows/workflow-list.ts +++ b/frontend/src/features/crawl-workflows/workflow-list.ts @@ -27,7 +27,6 @@ import type { ListWorkflow } from "@/types/crawler"; import { humanizeSchedule } from "@/utils/cron"; import { srOnly, truncate } from "@/utils/css"; import { formatNumber, getLocale } from "@/utils/localization"; -import { numberFormatter } from "@/utils/number"; import { pluralOf } from "@/utils/pluralize"; const formatNumberCompact = (v: number) => @@ -245,13 +244,9 @@ export class WorkflowListItem extends LitElement { ${this.safeRender((workflow) => { if (workflow.schedule) { return msg( - str`${humanizeSchedule( - workflow.schedule, - { - length: "short", - }, - numberFormatter, - )}`, + str`${humanizeSchedule(workflow.schedule, { + length: "short", + })}`, ); } if (workflow.lastStartedByName) { diff --git a/frontend/src/index.ts b/frontend/src/index.ts index 2dbb983c2c..495303353c 100644 --- a/frontend/src/index.ts +++ b/frontend/src/index.ts @@ -30,7 +30,7 @@ import type { NavigateEventDetail } from "@/controllers/navigate"; import type { NotifyEventDetail } from "@/controllers/notify"; import { theme } from "@/theme"; import { type Auth } from "@/types/auth"; -import { type LocaleCodeEnum } from "@/types/localization"; +import { translatedLocales, type LocaleCodeEnum } from "@/types/localization"; import { type AppSettings } from "@/utils/app"; import { getLocale, @@ -454,9 +454,13 @@ export class App extends BtrixElement { ` : html` ${this.renderSignUpLink()} - + ${(translatedLocales as unknown as string[]).length > 1 + ? html` + + ` + : nothing} `}
${isSuperAdmin diff --git a/frontend/src/pages/account-settings.ts b/frontend/src/pages/account-settings.ts index 51f7338d19..14ce5fbf93 100644 --- a/frontend/src/pages/account-settings.ts +++ b/frontend/src/pages/account-settings.ts @@ -243,7 +243,7 @@ export class AccountSettings extends LiteElement { ${(allLocales as unknown as string[]).length > 1 - ? this.renderPreferences() + ? this.renderLanguage() : nothing} `; } @@ -322,18 +322,45 @@ export class AccountSettings extends LiteElement { `; } - private renderPreferences() { + private renderLanguage() { return html` -

${msg("Preferences")}

+

+ ${msg("Language")} + +
+ ${msg("Translations are in beta")} +

+ ${msg( + "Parts of the app may not be translated yet in some languages.", + )} +

+
+
+

-
-

- ${msg("Language")} +
+

+ ${msg( + "Choose your preferred language for displaying Browsertrix in your browser.", + )}

- + >
+

`; } diff --git a/frontend/src/pages/org/settings/components/billing.ts b/frontend/src/pages/org/settings/components/billing.ts index b7fe0ddfbf..5ba2459e9c 100644 --- a/frontend/src/pages/org/settings/components/billing.ts +++ b/frontend/src/pages/org/settings/components/billing.ts @@ -280,7 +280,7 @@ export class OrgSettingsBilling extends BtrixElement { quotas.maxExecMinutesPerMonth && humanizeSeconds( quotas.maxExecMinutesPerMonth * 60, - undefined, + getLocale(), undefined, "long", ); diff --git a/frontend/src/types/localization.ts b/frontend/src/types/localization.ts index ec7c1867dd..58c0e9c2c3 100644 --- a/frontend/src/types/localization.ts +++ b/frontend/src/types/localization.ts @@ -3,5 +3,8 @@ import { z } from "zod"; import { allLocales } from "@/__generated__/locale-codes"; export { allLocales }; +// Translated languages to show in app: +export const translatedLocales = ["en"] as const; + export const localeCodeEnum = z.enum(allLocales); export type LocaleCodeEnum = z.infer; diff --git a/frontend/src/utils/cron.ts b/frontend/src/utils/cron.ts index 75bad2ac97..ad0da87a5a 100644 --- a/frontend/src/utils/cron.ts +++ b/frontend/src/utils/cron.ts @@ -62,7 +62,6 @@ export function humanizeNextDate( export function humanizeSchedule( schedule: string, options: { length?: "short" } = {}, - numberFormatter = numberUtils.numberFormatter, ): string { const interval = getScheduleInterval(schedule); const parsed = parseCron(schedule); @@ -92,7 +91,7 @@ export function humanizeSchedule( intervalMsg = msg(str`Every ${formattedWeekDay}`); break; case "monthly": { - const { format } = numberFormatter(getLocale()); + const { format } = numberUtils.numberFormatter(getLocale()); intervalMsg = msg( str`Monthly on the ${format(days[0], { ordinal: true })}`, ); diff --git a/frontend/xliff/es.xlf b/frontend/xliff/es.xlf index 6139e34d04..9cecfcd50b 100644 --- a/frontend/xliff/es.xlf +++ b/frontend/xliff/es.xlf @@ -2929,7 +2929,7 @@ - + @@ -3773,9 +3773,6 @@ Security - - Preferences - Your language preference has been updated. @@ -3786,6 +3783,21 @@ . + + Translations are in beta + + + Parts of the app may not be translated yet in some languages. + + + Help us translate Browsertrix. + + + Contribute to translations + + + Choose your preferred language for displaying Browsertrix in your browser. +