From 1b7fabe5071f0ca55984242e02ef6a2323e81e00 Mon Sep 17 00:00:00 2001 From: Al Marks Date: Wed, 3 Jun 2026 16:34:19 -0700 Subject: [PATCH 1/2] Initial Lit Localize integration --- package-lock.json | 8 +++-- packages/visual-editor/localization/de.xlb | 10 +++++++ packages/visual-editor/localization/fr.xlb | 10 +++++++ .../localization/lit-localize.json | 17 +++++++++++ .../visual-editor/localization/messages.xmb | 10 +++++++ packages/visual-editor/package.json | 29 ++++++++++++++++++- packages/visual-editor/src/bootstrap.ts | 4 +++ .../src/generated/localization/de.ts | 18 ++++++++++++ .../src/generated/localization/fr.ts | 18 ++++++++++++ .../generated/localization/locale-codes.ts | 25 ++++++++++++++++ packages/visual-editor/src/index-lite.ts | 6 ++-- .../visual-editor/src/localization/config.ts | 28 ++++++++++++++++++ packages/visual-editor/src/opal-lang.ts | 4 +-- packages/visual-editor/src/sca/types.ts | 1 + .../src/ui/elements/shell/global-settings.ts | 5 ++-- .../src/ui/elements/shell/warm-welcome.ts | 5 ++-- .../visual-editor/src/ui/navigation/urls.ts | 14 +++++++++ .../src/ui/strings/en_US/global.ts | 7 +---- 18 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 packages/visual-editor/localization/de.xlb create mode 100644 packages/visual-editor/localization/fr.xlb create mode 100644 packages/visual-editor/localization/lit-localize.json create mode 100644 packages/visual-editor/localization/messages.xmb create mode 100644 packages/visual-editor/src/generated/localization/de.ts create mode 100644 packages/visual-editor/src/generated/localization/fr.ts create mode 100644 packages/visual-editor/src/generated/localization/locale-codes.ts create mode 100644 packages/visual-editor/src/localization/config.ts diff --git a/package-lock.json b/package-lock.json index afc642c49ff..c6da2aa4660 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1277,9 +1277,9 @@ } }, "node_modules/@lit/localize-tools": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@lit/localize-tools/-/localize-tools-0.8.1.tgz", - "integrity": "sha512-bWjKXdcTDW0v3w/errna+bzJ2eDfpuz+V10sswF5oXd6C0mHw20sRU+wrXOoj1avVcI7g8cWVQHUJHkQs9UxAQ==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@lit/localize-tools/-/localize-tools-0.8.2.tgz", + "integrity": "sha512-Hf7jMfamdX26FN4SBRtM7zoToCUeShzI8NQcLdHLdpPdNPlqbEYMjqRaTQPm/PsbzTl6uZwfE3/kV+NvvrJS2A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10824,6 +10824,8 @@ "devDependencies": { "@cfworker/json-schema": "^4.1.1", "@google-labs/tsconfig": "^0.0.2", + "@lit/localize": "^0.12.2", + "@lit/localize-tools": "^0.8.2", "@tweakpane/core": "^2.0.5", "@types/flubber": "^0.4.0", "@types/gapi": "^0.0.47", diff --git a/packages/visual-editor/localization/de.xlb b/packages/visual-editor/localization/de.xlb new file mode 100644 index 00000000000..6833112093d --- /dev/null +++ b/packages/visual-editor/localization/de.xlb @@ -0,0 +1,10 @@ + + + + Dieses Gem wird von Google Labs bereitgestellt, nicht von Gemini Apps. Es kann + Fehler machen. Bitte überprüfen Sie die Ergebnisse. + <a href="https://support.google.com/gemini?p=ai_apps">Weitere Informationen</a> + Ich möchte E-Mails zu Modellaktualisierungen, Angeboten, nützlichen Tipps und Neuigkeiten zu Opal erhalten + Ich möchte zu künftigen Nutzerstudien von Opal eingeladen werden + + diff --git a/packages/visual-editor/localization/fr.xlb b/packages/visual-editor/localization/fr.xlb new file mode 100644 index 00000000000..57cbac3df3f --- /dev/null +++ b/packages/visual-editor/localization/fr.xlb @@ -0,0 +1,10 @@ + + + + Ce Gem est fourni par Google Labs, et non par Gemini Apps. Il peut + faire des erreurs, veuillez donc vérifier les résultats. + <a href="https://support.google.com/gemini?p=ai_apps">En savoir plus</a> + Je souhaite recevoir des e-mails concernant les mises à jour des modèles, les offres, les conseils utiles et les actualités d'Opal + Je souhaite être invité(e) à participer à de futures études de recherche d'Opal + + diff --git a/packages/visual-editor/localization/lit-localize.json b/packages/visual-editor/localization/lit-localize.json new file mode 100644 index 00000000000..62a4958d91c --- /dev/null +++ b/packages/visual-editor/localization/lit-localize.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json", + "sourceLocale": "en", + "targetLocales": ["fr", "de"], + "tsConfig": "../tsconfig.json", + "output": { + "mode": "runtime", + "outputDir": "../src/generated/localization", + "language": "ts", + "localeCodesModule": "src/generated/localization/locale-codes.ts" + }, + "interchange": { + "format": "xlb", + "outputFile": "messages.xmb", + "translationsGlob": "*.xlb" + } +} diff --git a/packages/visual-editor/localization/messages.xmb b/packages/visual-editor/localization/messages.xmb new file mode 100644 index 00000000000..206edfea9c7 --- /dev/null +++ b/packages/visual-editor/localization/messages.xmb @@ -0,0 +1,10 @@ + + + + This Gem is powered by Google Labs, not Gemini Apps. It can + make mistakes, so double-check it. + <a href="https://support.google.com/gemini?p=ai_apps">Learn more</a> + I’d like to receive emails for model updates, offers, useful tips and news about Opal + I’m interested in being invited to future research studies from Opal + + diff --git a/packages/visual-editor/package.json b/packages/visual-editor/package.json index a7143749249..11adfc1a525 100644 --- a/packages/visual-editor/package.json +++ b/packages/visual-editor/package.json @@ -31,7 +31,9 @@ "lint": "wireit", "export-functions": "wireit", "import-declarations": "wireit", - "build:opie-instructions": "wireit" + "build:opie-instructions": "wireit", + "localize:extract": "wireit", + "localize:build": "wireit" }, "wireit": { "build": { @@ -269,6 +271,29 @@ "output": [ "src/a2/agent/graph-editing/instructions/generated.ts" ] + }, + "localize:extract": { + "command": "lit-localize extract --config localization/lit-localize.json", + "files": [ + "src/**/*.ts", + "!src/generated/localization/", + "localization/lit-localize.json" + ], + "output": [ + "localization/messages.xmb" + ] + }, + "localize:build": { + "command": "lit-localize build --config localization/lit-localize.json", + "files": [ + "src/**/*.ts", + "!src/generated/localization/", + "localization/*.xlb", + "localization/lit-localize.json" + ], + "output": [ + "src/generated/localization/" + ] } }, "repository": { @@ -291,6 +316,8 @@ "devDependencies": { "@cfworker/json-schema": "^4.1.1", "@google-labs/tsconfig": "^0.0.2", + "@lit/localize": "^0.12.2", + "@lit/localize-tools": "^0.8.2", "@tweakpane/core": "^2.0.5", "@types/flubber": "^0.4.0", "@types/gapi": "^0.0.47", diff --git a/packages/visual-editor/src/bootstrap.ts b/packages/visual-editor/src/bootstrap.ts index aa99b261ae1..d37026d4e7d 100644 --- a/packages/visual-editor/src/bootstrap.ts +++ b/packages/visual-editor/src/bootstrap.ts @@ -15,6 +15,7 @@ import { makeUrl, OAUTH_REDIRECT, parseUrl } from "./ui/navigation/urls.js"; import { CLIENT_DEPLOYMENT_CONFIG } from "./ui/config/client-deployment-configuration.js"; import { connectToOpalShellHost } from "./ui/utils/opal-shell-guest.js"; import { Utils } from "./sca/utils.js"; +import { setLocale } from "./localization/config.js"; export { bootstrap }; @@ -68,6 +69,9 @@ async function bootstrap(bootstrapArgs: BootstrapArguments) { const guestConfiguration = await shellHost.getConfiguration(); const parsedUrl = parseUrl(window.location.href); + if (parsedUrl.hl) { + await setLocale(parsedUrl.hl); + } const { lite, page, colorScheme } = parsedUrl; if ( // Signed-out users can access public graphs. diff --git a/packages/visual-editor/src/generated/localization/de.ts b/packages/visual-editor/src/generated/localization/de.ts new file mode 100644 index 00000000000..92465e17016 --- /dev/null +++ b/packages/visual-editor/src/generated/localization/de.ts @@ -0,0 +1,18 @@ + + // Do not modify this file by hand! + // Re-generate this file by running lit-localize + + import {html} from 'lit'; + + + /* eslint-disable no-irregular-whitespace */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export const templates = { + 'hbeb436a6f6a2a826': html`Dieses Gem wird von Google Labs bereitgestellt, nicht von Gemini Apps. Es kann + Fehler machen. Bitte überprüfen Sie die Ergebnisse. + Weitere Informationen`, +'s018656f690df2a0e': `Ich möchte zu künftigen Nutzerstudien von Opal eingeladen werden`, +'s718207f6b5b1ed0b': `Ich möchte E-Mails zu Modellaktualisierungen, Angeboten, nützlichen Tipps und Neuigkeiten zu Opal erhalten`, + }; + \ No newline at end of file diff --git a/packages/visual-editor/src/generated/localization/fr.ts b/packages/visual-editor/src/generated/localization/fr.ts new file mode 100644 index 00000000000..61d715383e0 --- /dev/null +++ b/packages/visual-editor/src/generated/localization/fr.ts @@ -0,0 +1,18 @@ + + // Do not modify this file by hand! + // Re-generate this file by running lit-localize + + import {html} from 'lit'; + + + /* eslint-disable no-irregular-whitespace */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export const templates = { + 'hbeb436a6f6a2a826': html`Ce Gem est fourni par Google Labs, et non par Gemini Apps. Il peut + faire des erreurs, veuillez donc vérifier les résultats. + En savoir plus`, +'s018656f690df2a0e': `Je souhaite être invité(e) à participer à de futures études de recherche d'Opal`, +'s718207f6b5b1ed0b': `Je souhaite recevoir des e-mails concernant les mises à jour des modèles, les offres, les conseils utiles et les actualités d'Opal`, + }; + \ No newline at end of file diff --git a/packages/visual-editor/src/generated/localization/locale-codes.ts b/packages/visual-editor/src/generated/localization/locale-codes.ts new file mode 100644 index 00000000000..f6a094041d8 --- /dev/null +++ b/packages/visual-editor/src/generated/localization/locale-codes.ts @@ -0,0 +1,25 @@ +// Do not modify this file by hand! +// Re-generate this file by running lit-localize. + +/** + * The locale code that templates in this source code are written in. + */ +export const sourceLocale = `en`; + +/** + * The other locale codes that this application is localized into. Sorted + * lexicographically. + */ +export const targetLocales = [ + `de`, + `fr`, +] as const; + +/** + * All valid project locale codes. Sorted lexicographically. + */ +export const allLocales = [ + `de`, + `en`, + `fr`, +] as const; diff --git a/packages/visual-editor/src/index-lite.ts b/packages/visual-editor/src/index-lite.ts index b11e031c2ce..b34092c3a96 100644 --- a/packages/visual-editor/src/index-lite.ts +++ b/packages/visual-editor/src/index-lite.ts @@ -27,7 +27,7 @@ import { blankBoard } from "./ui/utils/blank-board.js"; import { repeat } from "lit/directives/repeat.js"; import { createRef, ref, Ref } from "lit/directives/ref.js"; -import { markdown } from "./ui/directives/markdown.js"; +import { msg } from "@lit/localize"; import { type SharePanel } from "./ui/elements/elements.js"; import { deriveLiteViewType } from "./sca/utils/lite-view-type.js"; import { @@ -665,7 +665,9 @@ export class LiteMain extends MainBase implements LiteEditInputController { #renderMessage() { return html`
- ${markdown(Strings.from("LABEL_DISCLAIMER_LITE"))} + ${msg(html`This Gem is powered by Google Labs, not Gemini Apps. It can + make mistakes, so double-check it. + Learn more`)}
`; } diff --git a/packages/visual-editor/src/localization/config.ts b/packages/visual-editor/src/localization/config.ts new file mode 100644 index 00000000000..45cea521527 --- /dev/null +++ b/packages/visual-editor/src/localization/config.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configureLocalization, type LocaleModule } from "@lit/localize"; +import { + sourceLocale, + targetLocales, +} from "../generated/localization/locale-codes.js"; + +// Non-standard Vite function. Detects all available locales at build time and +// substitutes static module names for the dynamic imports. +const localeModules = import.meta.glob("../generated/localization/*.ts"); + +const { setLocale } = configureLocalization({ + sourceLocale, + targetLocales, + loadLocale: (locale) => { + const loader = localeModules[`../generated/localization/${locale}.ts`]; + if (!loader) { + return Promise.reject(new Error(`Unknown locale: ${locale}`)); + } + return loader() as Promise; + }, +}); +export { setLocale }; diff --git a/packages/visual-editor/src/opal-lang.ts b/packages/visual-editor/src/opal-lang.ts index a3ead1be76b..694c747d592 100644 --- a/packages/visual-editor/src/opal-lang.ts +++ b/packages/visual-editor/src/opal-lang.ts @@ -76,9 +76,7 @@ export default { LABEL_DISCLAIMER: { str: "Opal can make mistakes, so double-check it", }, - LABEL_DISCLAIMER_LITE: { - str: "This Gem is powered by Google Labs, not Gemini Apps. It can make mistakes, so double-check it. [Learn more](https://support.google.com/gemini?p=ai_apps)", - }, + LABEL_FIRST_RUN: { str: "Click start to try this Opal", }, diff --git a/packages/visual-editor/src/sca/types.ts b/packages/visual-editor/src/sca/types.ts index e4ccf398e60..318cc82be73 100644 --- a/packages/visual-editor/src/sca/types.ts +++ b/packages/visual-editor/src/sca/types.ts @@ -145,6 +145,7 @@ export interface BaseUrlInit { forceSurveySelection?: "true"; }; oauthRedirect?: string; + hl?: string; lite?: boolean; colorScheme?: "light" | "dark"; guestPrefixed: boolean; diff --git a/packages/visual-editor/src/ui/elements/shell/global-settings.ts b/packages/visual-editor/src/ui/elements/shell/global-settings.ts index f68de54f2af..3aed5236d99 100644 --- a/packages/visual-editor/src/ui/elements/shell/global-settings.ts +++ b/packages/visual-editor/src/ui/elements/shell/global-settings.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import { LitElement, html, css, nothing } from "lit"; +import { msg } from "@lit/localize"; import { customElement, property, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { baseColors } from "../../styles/host/base-colors.js"; @@ -189,7 +190,7 @@ export class VEGlobalSettingsModal extends SignalWatcher(LitElement) { ["OPAL_MARKETING_UPDATES", target.checked], ])} > - ${Strings.from("LABEL_EMAIL_UPDATES")} + ${msg("I’d like to receive emails for model updates, offers, useful tips and news about Opal")} `, }, [TabId.INTEGRATIONS]: { diff --git a/packages/visual-editor/src/ui/elements/shell/warm-welcome.ts b/packages/visual-editor/src/ui/elements/shell/warm-welcome.ts index 46fe12a3e22..13158d0ba52 100644 --- a/packages/visual-editor/src/ui/elements/shell/warm-welcome.ts +++ b/packages/visual-editor/src/ui/elements/shell/warm-welcome.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import { LitElement, html, css } from "lit"; +import { msg } from "@lit/localize"; import { customElement, state, property } from "lit/decorators.js"; import { baseColors } from "../../styles/host/base-colors.js"; import { type } from "../../styles/host/type.js"; @@ -96,7 +97,7 @@ export class VEWarmWelcomeModal extends LitElement { @change=${({ target }: { target: MdCheckbox }) => (this.emailUpdates = target.checked)} > - ${Strings.from("LABEL_EMAIL_UPDATES")} + ${msg("I’d like to receive emails for model updates, offers, useful tips and news about Opal")} `; } diff --git a/packages/visual-editor/src/ui/navigation/urls.ts b/packages/visual-editor/src/ui/navigation/urls.ts index 969678cab8f..e1daf809f9f 100644 --- a/packages/visual-editor/src/ui/navigation/urls.ts +++ b/packages/visual-editor/src/ui/navigation/urls.ts @@ -35,6 +35,7 @@ const MISSING_SCOPES = "missing-scopes"; const AUTO_SIGN_IN = "auto-sign-in"; const RESOURCE_KEY = "resourcekey"; export const OAUTH_REDIRECT = "oauth_redirect"; +const HL = "hl"; const DEV_PREFIX = "dev-"; /** @@ -173,6 +174,9 @@ export function makeUrl( `unhandled page ${JSON.stringify(page)} from ${JSON.stringify(init)}` ); } + if (init.hl) { + url.searchParams.set(HL, init.hl); + } if (init.dev) { for (const [key, val] of Object.entries( init.dev as Record @@ -200,6 +204,7 @@ export function parseUrl(url: string | URL): MakeUrlInit { } let dev: BaseUrlInit["dev"]; const oauthRedirect = url.searchParams.get(OAUTH_REDIRECT); + const hl = url.searchParams.get(HL); for (const [key, val] of url.searchParams) { if (key.startsWith(DEV_PREFIX)) { dev ??= {}; @@ -250,6 +255,9 @@ export function parseUrl(url: string | URL): MakeUrlInit { if (dev) { landing.dev = dev; } + if (hl) { + landing.hl = hl; + } return landing; } else if (pathname.startsWith("/open/")) { const open: OpenUrlInit = { @@ -291,6 +299,9 @@ export function parseUrl(url: string | URL): MakeUrlInit { if (oauthRedirect) { home.oauthRedirect = oauthRedirect; } + if (hl) { + home.hl = hl; + } return home; } const mode = @@ -325,6 +336,9 @@ export function parseUrl(url: string | URL): MakeUrlInit { if (oauthRedirect) { graph.oauthRedirect = oauthRedirect; } + if (hl) { + graph.hl = hl; + } return graph; } } diff --git a/packages/visual-editor/src/ui/strings/en_US/global.ts b/packages/visual-editor/src/ui/strings/en_US/global.ts index fe38e5b5ec4..9678f3ef1ad 100644 --- a/packages/visual-editor/src/ui/strings/en_US/global.ts +++ b/packages/visual-editor/src/ui/strings/en_US/global.ts @@ -146,12 +146,7 @@ export default { LABEL_SHARE: { str: "A Breadboard mini-app has been shared with you", }, - LABEL_EMAIL_UPDATES: { - str: "I’d like to receive emails for model updates, offers, useful tips and news about Opal", - }, - LABEL_EMAIL_RESEARCH: { - str: "I’m interested in being invited to future research studies from Opal", - }, + LABEL_SETTINGS_GENERAL: { str: "General", }, From c67869a1b843678f4ca5b34ce5eb7d4937fea1f6 Mon Sep 17 00:00:00 2001 From: Al Marks Date: Wed, 3 Jun 2026 16:43:50 -0700 Subject: [PATCH 2/2] Bump version --- packages/folio/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/folio/package.json b/packages/folio/package.json index cac3bb683c7..21ed5ff46c1 100644 --- a/packages/folio/package.json +++ b/packages/folio/package.json @@ -27,7 +27,7 @@ }, "devDependencies": { "@google-labs/tsconfig": "^0.0.2", - "@lit/localize-tools": "^0.8.1", + "@lit/localize-tools": "^0.8.2", "vite": "^7.3.2", "wireit": "^0.15.0-pre.2" },