diff --git a/.changeset/26136-objectos-cloud-naming.md b/.changeset/26136-objectos-cloud-naming.md new file mode 100644 index 00000000000..42b74a602e4 --- /dev/null +++ b/.changeset/26136-objectos-cloud-naming.md @@ -0,0 +1,20 @@ +--- +'@objectstack/cli': patch +'@object-ui/i18n': patch +'@object-ui/app-shell': patch +'@object-ui/console': patch +--- + +fix(cli,i18n,console): the hosted product reads "ObjectOS Cloud" everywhere a user reads it; the Community Console carries a "Powered by ObjectStack" mark + +Clause-②: no +Metadata-protocol: none + +ObjectOS is one product in three editions — Community (this repository's runtime and Console, open source), Cloud (cloud.objectos.ai) and Enterprise — and ObjectStack is the name of the protocol, SDK, CLI and framework. The hosted product is "ObjectOS Cloud" wherever a user reads it; it was still "ObjectStack Cloud" in the CLI and in the Console's cloud-install strings. + +- **`@objectstack/cli`.** `os cloud login`, `os cloud logout`, `os cloud whoami`, `os package publish` and `os plugin publish` say "ObjectOS Cloud" in their `--help` descriptions, their `--url` / `--server` / `--token` / `--visibility` flag descriptions, their headers and their "Not logged in" prescriptions; so do the `os serve` welcome page's link to the cloud console and the shared "Authentication required" message. The CLI README and the deployment CLI reference follow. Internal identifiers are unchanged: `~/.objectstack/cloud.json`, `OS_CLOUD_API_KEY`, `OS_CLOUD_URL`, the `os cloud …` command names. +- **`@object-ui/i18n`.** The cloud-connection "Connected to …" title and the three cloud-install dialog sentences ("You need to be signed into …", "… sign into … first") name "ObjectOS Cloud" in all ten locales, the product name kept untranslated inside each locale's sentence. New key `auth.shell.poweredBy` ("Powered by ObjectStack") in all ten locales. +- **`@object-ui/app-shell`.** New `isDefaultProductName()`: true while the deployment carries the default `branding.productName` ("ObjectOS"), false once a runtime has pushed its own. +- **`@object-ui/console`.** The sign-in layout (login, register, forgot/reset password, verify email, setup, consent, device, invitation) shows a small "Powered by ObjectStack" link to objectstack.ai under the form when `isDefaultProductName()` is true. A deployment that pushes its own product name — ObjectOS Cloud, Enterprise, a cloud-connected workspace — shows its own name and no second brand line. The default product name, `logo.svg`, `manifest.json` and `admin@objectos.ai` are unchanged. + +What to do: nothing. An operator who rebrands the Console through `branding.productName` keeps a single brand line, as before. diff --git a/README.md b/README.md index 384a6354884..77c89c0fb4f 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ os explain # Explain protocol concepts on the command line ``` Cloud, package registry, secrets, and environment subcommands (`os package …`, -`os environments …`, `os login`, `os cloud …`) target an ObjectStack Cloud +`os environments …`, `os login`, `os cloud …`) target an ObjectOS Cloud control plane. ### Package directory diff --git a/content/docs/deployment/cli.mdx b/content/docs/deployment/cli.mdx index d1efceaf2f3..0b6cdaea6d9 100644 --- a/content/docs/deployment/cli.mdx +++ b/content/docs/deployment/cli.mdx @@ -2626,7 +2626,7 @@ os doctor -v # Show fix suggestions for warnings | `os login` | Sign in and store credentials in `~/.objectstack/credentials.json` | | `os whoami` | Show the current authenticated user | | `os logout` | Revoke the server session and clear local credentials | -| `os cloud login` | Sign in to ObjectStack Cloud (the hosted package registry) and store credentials in `~/.objectstack/cloud.json` | +| `os cloud login` | Sign in to ObjectOS Cloud (the hosted package registry) and store credentials in `~/.objectstack/cloud.json` | #### `os register` @@ -2727,7 +2727,7 @@ os logout #### `os cloud login` -Signs you in to **ObjectStack Cloud** — the hosted package registry — rather +Signs you in to **ObjectOS Cloud** — the hosted package registry — rather than to a runtime instance. It is the credential `os package publish` and the marketplace commands use, and it lands in its own file (`~/.objectstack/cloud.json`), separate from `os login`'s @@ -2823,12 +2823,12 @@ is available in your deployment. The two commands that move a compiled app onto a platform. They target **different systems and authenticate as different identities**: publish uploads -to ObjectStack Cloud (the catalog), install registers an app into a **running +to ObjectOS Cloud (the catalog), install registers an app into a **running runtime**. | Command | Talks to | Description | |---------|----------|-------------| -| `os package publish [artifact]` | ObjectStack Cloud | Upload a compiled artifact as a versioned package in your organization | +| `os package publish [artifact]` | ObjectOS Cloud | Upload a compiled artifact as a versioned package in your organization | | `os package install ` | A running runtime | Install a package into a live kernel, from that runtime's catalog or from a local artifact | For which one to reach for and the preview patterns around them, see @@ -2882,7 +2882,7 @@ when both files' `url`s agree. | `--display-name ` | — | Name shown in the Marketplace (default `artifact.manifest.name`) | | `--description ` | — | Short package description | | `--category ` | — | Marketplace category slug (`crm`, `hr`, `devtools`, …) | -| `--visibility ` | — | `org` (installable across your organization) · `private` (explicit grants only) · `marketplace` (public after review). Omitted: not sent, so the control plane decides — an existing package keeps its visibility, a new one gets the control plane's default (`org` on ObjectStack Cloud) | +| `--visibility ` | — | `org` (installable across your organization) · `private` (explicit grants only) · `marketplace` (public after review). Omitted: not sent, so the control plane decides — an existing package keeps its visibility, a new one gets the control plane's default (`org` on ObjectOS Cloud) | | `--org ` | `OS_ORG_ID` | `owner_org_id`. Required with a bearer key in service mode; ignored in user mode | | `--env ` | `OS_ENVIRONMENT_ID` | Environment to install the new version into. Defaults to the environment [`os environments switch`](#cloud-environments) — or `os environments create --activate` — recorded **for this control plane** in `~/.objectstack/cloud.json` | | `--install` | — | Auto-install into `--env` after publishing. With no `--env`, no `$OS_ENVIRONMENT_ID` and no active environment for this control plane, it reports that and publishes without installing | @@ -3018,7 +3018,7 @@ A `404` means the target runtime does not mount `MarketplaceInstallLocalPlugin` (from `@objectstack/cloud-connection`). The endpoint is opt-in, so a runtime composed without it will not accept installs. -In catalog mode, a runtime connected to an ObjectStack Cloud account presents its connection credential to the cloud catalog — see [Cloud Connection](/docs/deployment/cloud-connection). +In catalog mode, a runtime connected to an ObjectOS Cloud account presents its connection credential to the cloud catalog — see [Cloud Connection](/docs/deployment/cloud-connection). ## Configuration diff --git a/packages/cli/README.md b/packages/cli/README.md index 11d4423881c..32a44cf7e56 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -80,13 +80,13 @@ and keep loading through their barrel `index.ts`. ### Cloud — publish & install -Push a locally-built package to ObjectStack Cloud and (optionally) install it +Push a locally-built package to ObjectOS Cloud and (optionally) install it into one of your environments in a single command. The commands below do not share one session or one flag spelling — see [Credentials and server URL](#credentials-and-server-url). | Command | Description | |---------|-------------| -| `os cloud login` | Authenticate to ObjectStack Cloud (browser or `--email/--password`); writes `~/.objectstack/cloud.json` | +| `os cloud login` | Authenticate to ObjectOS Cloud (browser or `--email/--password`); writes `~/.objectstack/cloud.json` | | `os cloud whoami` / `os cloud logout` | Inspect / clear the stored cloud session | | `os environments create --org --name ` | Provision a new environment (was `os projects create` before the v5.0 project → environment rename; ADR-0006, no aliases) | | `os environments list` / `show ` | List / inspect your environments | @@ -146,7 +146,7 @@ A code-bearing plugin — a directory carrying an `objectstack.plugin.json` mani |---------|-------------| | `os plugin build [dir]` | Compile a plugin into a signed-ready `.osplugin` artifact (`--entry`, `--out`, `--minify`) | | `os plugin sign --key ` | Sign a built `.osplugin` with a publisher Ed25519 key, writing a detached `.sig` | -| `os plugin publish [artifact]` | Publish a signed `.osplugin` to ObjectStack Cloud | +| `os plugin publish [artifact]` | Publish a signed `.osplugin` to ObjectOS Cloud | The group has no `install`: ADR-0025 records the code-plugin install half (download, verify, materialize, load) as not yet implemented. `os plugin` (singular) is unrelated to `os plugins` (plural), oclif's plugin manager, which this package does not ship — see [`os plugins` and `os help`](#os-plugins-and-os-help-not-commands). diff --git a/packages/cli/src/commands/cloud/login.ts b/packages/cli/src/commands/cloud/login.ts index e915e7a6030..f626146ccf2 100644 --- a/packages/cli/src/commands/cloud/login.ts +++ b/packages/cli/src/commands/cloud/login.ts @@ -139,7 +139,7 @@ async function promptPassword(promptText: string): Promise { export default class CloudLogin extends Command { static override description = - 'Authenticate against ObjectStack Cloud (the hosted package registry). Stores credentials in ~/.objectstack/cloud.json.'; + 'Authenticate against ObjectOS Cloud (the hosted package registry). Stores credentials in ~/.objectstack/cloud.json.'; static override examples = [ '$ os cloud login', @@ -151,7 +151,7 @@ export default class CloudLogin extends Command { static override flags = { url: Flags.string({ char: 'u', - description: 'ObjectStack Cloud URL (override for self-hosted control planes)', + description: 'ObjectOS Cloud URL (override for self-hosted control planes)', default: DEFAULT_CLOUD_URL, env: 'OS_CLOUD_URL', }), @@ -198,7 +198,7 @@ export default class CloudLogin extends Command { const url = flags.url.replace(/\/+$/, ''); if (!flags.json) { - printHeader('ObjectStack Cloud Login'); + printHeader('ObjectOS Cloud Login'); printKV('Server', url); console.log(''); } diff --git a/packages/cli/src/commands/cloud/logout.ts b/packages/cli/src/commands/cloud/logout.ts index 9349f965c74..f94c62259a5 100644 --- a/packages/cli/src/commands/cloud/logout.ts +++ b/packages/cli/src/commands/cloud/logout.ts @@ -12,7 +12,7 @@ import { printHeader, printSuccess, printError, emitJson, errorCodeFields } from import { deleteCloudConfig, tryReadCloudConfig } from '../../utils/cloud-config.js'; export default class CloudLogout extends Command { - static override description = 'Clear stored ObjectStack Cloud credentials'; + static override description = 'Clear stored ObjectOS Cloud credentials'; static override examples = ['$ os cloud logout']; @@ -24,7 +24,7 @@ export default class CloudLogout extends Command { const { flags } = await this.parse(CloudLogout); try { - if (!flags.json) printHeader('ObjectStack Cloud Logout'); + if (!flags.json) printHeader('ObjectOS Cloud Logout'); const config = await tryReadCloudConfig(); if (config?.token && config?.url) { diff --git a/packages/cli/src/commands/cloud/whoami.ts b/packages/cli/src/commands/cloud/whoami.ts index 70dbe6167e3..eaddd2e7081 100644 --- a/packages/cli/src/commands/cloud/whoami.ts +++ b/packages/cli/src/commands/cloud/whoami.ts @@ -11,7 +11,7 @@ import { printHeader, printKV, printSuccess, printError, emitJson, isExitSignal, import { tryReadCloudConfig } from '../../utils/cloud-config.js'; export default class CloudWhoami extends Command { - static override description = 'Show the active ObjectStack Cloud identity'; + static override description = 'Show the active ObjectOS Cloud identity'; static override examples = ['$ os cloud whoami']; @@ -28,7 +28,7 @@ export default class CloudWhoami extends Command { if (flags.json) { await emitJson({ logged_in: false }); } else { - printError('Not logged in to ObjectStack Cloud. Run `os cloud login` first.'); + printError('Not logged in to ObjectOS Cloud. Run `os cloud login` first.'); } this.exit(1); return; @@ -56,7 +56,7 @@ export default class CloudWhoami extends Command { if (flags.json) { await emitJson({ logged_in: true, valid, url: config.url, email, userId }); } else { - printHeader('ObjectStack Cloud Identity'); + printHeader('ObjectOS Cloud Identity'); printKV('Server', config.url); if (email) printKV('Email', email); if (userId) printKV('User ID', userId); diff --git a/packages/cli/src/commands/package/publish.ts b/packages/cli/src/commands/package/publish.ts index 5612334e5c7..756e31dc55a 100644 --- a/packages/cli/src/commands/package/publish.ts +++ b/packages/cli/src/commands/package/publish.ts @@ -298,13 +298,13 @@ export default class PackagePublish extends Command { static override flags = { server: Flags.string({ char: 's', - description: 'ObjectStack Cloud control-plane URL (override for self-hosted registries)', + description: 'ObjectOS Cloud control-plane URL (override for self-hosted registries)', env: 'OS_CLOUD_URL', default: DEFAULT_CLOUD_URL, }), token: Flags.string({ char: 't', - description: 'API key for ObjectStack Cloud (bearer token; defaults to OS_CLOUD_API_KEY / OS_TOKEN)', + description: 'API key for ObjectOS Cloud (bearer token; defaults to OS_CLOUD_API_KEY / OS_TOKEN)', env: 'OS_CLOUD_API_KEY', }), 'manifest-id': Flags.string({ @@ -337,7 +337,7 @@ export default class PackagePublish extends Command { "'private': only explicitly-granted orgs/envs. " + "'marketplace': public after review. " + 'Omit it to leave an existing package\'s visibility unchanged; a new package then gets ' + - "the control plane's default ('org' on ObjectStack Cloud).", + "the control plane's default ('org' on ObjectOS Cloud).", options: ['private', 'org', 'marketplace'], }), org: Flags.string({ @@ -517,7 +517,7 @@ export default class PackagePublish extends Command { } if (!token) { printError( - 'Not logged in to ObjectStack Cloud. Run `os cloud login` first, or pass --token / set $OS_TOKEN.', + 'Not logged in to ObjectOS Cloud. Run `os cloud login` first, or pass --token / set $OS_TOKEN.', ); this.exit(1); return; diff --git a/packages/cli/src/commands/plugin/publish.ts b/packages/cli/src/commands/plugin/publish.ts index f5a598c7ad6..ced0bfd5444 100644 --- a/packages/cli/src/commands/plugin/publish.ts +++ b/packages/cli/src/commands/plugin/publish.ts @@ -44,7 +44,7 @@ interface PostResult { ok: boolean; status: number; body: any; error?: string } export default class PluginPublish extends Command { static override description = - 'Publish a signed .osplugin to ObjectStack Cloud (ADR-0025 §3.4)'; + 'Publish a signed .osplugin to ObjectOS Cloud (ADR-0025 §3.4)'; static override examples = [ '$ os plugin publish ./com.acme.stripe-1.0.0.osplugin --visibility marketplace --submit', @@ -72,7 +72,7 @@ export default class PluginPublish extends Command { description: "Who can see / install this plugin's package. " + "Omit it to leave an existing package's visibility unchanged; a new package then gets " + - "the control plane's default ('org' on ObjectStack Cloud).", + "the control plane's default ('org' on ObjectOS Cloud).", options: ['private', 'org', 'marketplace'], }), org: Flags.string({ description: 'owner_org_id (service mode)', env: 'OS_ORG_ID' }), diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index e437d84994d..c7c3fd972fe 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -6232,7 +6232,7 @@ export function createUnknownHostnameGuardPlugin(
${safeHost}

If you own this domain, bind it to an environment in the - ObjectStack Cloud console. + ObjectOS Cloud console.

diff --git a/packages/cli/src/utils/api-client.ts b/packages/cli/src/utils/api-client.ts index 16c45fb13d9..73e17b55016 100644 --- a/packages/cli/src/utils/api-client.ts +++ b/packages/cli/src/utils/api-client.ts @@ -222,7 +222,7 @@ export function requireAuth(token?: string): void { export function requireControlPlaneAuth(token?: string): void { if (!token) { throw new Error( - 'Authentication required. Run `os cloud login` for ObjectStack Cloud or `os login` for a ' + 'Authentication required. Run `os cloud login` for ObjectOS Cloud or `os login` for a ' + 'self-hosted control plane, or set OS_TOKEN.' ); } diff --git a/packages/cli/src/utils/cloud-config.ts b/packages/cli/src/utils/cloud-config.ts index 7e7d6a2236b..f9d2447d1af 100644 --- a/packages/cli/src/utils/cloud-config.ts +++ b/packages/cli/src/utils/cloud-config.ts @@ -74,7 +74,7 @@ export async function readCloudConfig(): Promise { return JSON.parse(content) as CloudConfig; } catch (error: any) { if (error.code === 'ENOENT') { - throw new Error('Not logged in to ObjectStack Cloud. Run `os cloud login` first.'); + throw new Error('Not logged in to ObjectOS Cloud. Run `os cloud login` first.'); } throw new Error(`Failed to read cloud credentials: ${error.message}`); } diff --git a/packages/cli/test/cloud-login-json-ndjson.e2e.test.ts b/packages/cli/test/cloud-login-json-ndjson.e2e.test.ts index bc0d596989a..1c8cf80d177 100644 --- a/packages/cli/test/cloud-login-json-ndjson.e2e.test.ts +++ b/packages/cli/test/cloud-login-json-ndjson.e2e.test.ts @@ -395,7 +395,7 @@ describe('os cloud login --json — the declared NDJSON stream (#6730)', () => { // this subsumes "nothing else was written" rather than listing banners a // future edit could add to. expect(lines(ok.stdout)).toHaveLength(2); - expect(ok.stdout).not.toContain('ObjectStack Cloud Login'); + expect(ok.stdout).not.toContain('ObjectOS Cloud Login'); expect(ok.stdout).not.toContain('To authorize this CLI'); expect(ok.stdout).not.toContain('Waiting for browser approval'); expect(ok.stdout).not.toContain('Credentials stored in'); diff --git a/packages/cli/test/cloud-product-name.test.ts b/packages/cli/test/cloud-product-name.test.ts new file mode 100644 index 00000000000..03a168ed9a7 --- /dev/null +++ b/packages/cli/test/cloud-product-name.test.ts @@ -0,0 +1,61 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * The hosted product reads "ObjectOS Cloud" wherever the CLI names it. + * + * ObjectOS is the product, in three editions (Community, Cloud, Enterprise); + * ObjectStack is the protocol, SDK, CLI and framework. The strings a user + * reads from the cloud commands — the command descriptions `--help` prints, + * the flag descriptions, the "not logged in" prescriptions — name the hosted + * product by its product name. Internal identifiers keep theirs: + * `~/.objectstack/cloud.json`, `OS_CLOUD_API_KEY`, `os cloud login`. + * + * Pinned on the command classes' own statics rather than on a spawned + * `--help`: it is the same text oclif prints, read without a process. + */ + +import { describe, it, expect } from 'vitest'; +import CloudLogin from '../src/commands/cloud/login.js'; +import CloudLogout from '../src/commands/cloud/logout.js'; +import CloudWhoami from '../src/commands/cloud/whoami.js'; +import PackagePublish from '../src/commands/package/publish.js'; +import PluginPublish from '../src/commands/plugin/publish.js'; + +const COMMANDS = [CloudLogin, CloudLogout, CloudWhoami, PackagePublish, PluginPublish] as const; + +/** Every user-readable string a command's help carries: its description and its flags'. */ +function helpStrings(command: (typeof COMMANDS)[number]): string[] { + const flags = Object.values(command.flags ?? {}) as Array<{ description?: string }>; + return [command.description ?? '', ...flags.map((flag) => flag.description ?? '')]; +} + +describe('cloud commands name the hosted product "ObjectOS Cloud"', () => { + it('names it in the login, logout and whoami descriptions', () => { + expect(CloudLogin.description).toContain('ObjectOS Cloud'); + expect(CloudLogout.description).toContain('ObjectOS Cloud'); + expect(CloudWhoami.description).toContain('ObjectOS Cloud'); + }); + + it('names it in the control-plane URL and API-key flag descriptions', () => { + expect(CloudLogin.flags.url.description).toContain('ObjectOS Cloud'); + expect(PackagePublish.flags.server.description).toContain('ObjectOS Cloud'); + expect(PackagePublish.flags.token.description).toContain('ObjectOS Cloud'); + expect(PackagePublish.flags.visibility.description).toContain('ObjectOS Cloud'); + expect(PluginPublish.description).toContain('ObjectOS Cloud'); + expect(PluginPublish.flags.visibility.description).toContain('ObjectOS Cloud'); + }); + + it('carries no "ObjectStack Cloud" in any help string of those commands', () => { + const stale = COMMANDS.flatMap((command) => + helpStrings(command) + .filter((text) => text.includes('ObjectStack Cloud')) + .map((text) => `${command.id ?? command.name}: ${text}`), + ); + expect(stale).toEqual([]); + }); + + it('keeps the internal identifiers as they are', () => { + expect(CloudLogin.description).toContain('~/.objectstack/cloud.json'); + expect(PackagePublish.flags.token.description).toContain('OS_CLOUD_API_KEY'); + }); +}); diff --git a/scripts/sdui-manifest.sources/i18n.json b/scripts/sdui-manifest.sources/i18n.json index 0c252a718c8..84836d6c007 100644 --- a/scripts/sdui-manifest.sources/i18n.json +++ b/scripts/sdui-manifest.sources/i18n.json @@ -1 +1 @@ -{"package":"ui/packages/i18n","sources":31,"sha256":"83894d7c91938618b320bda4f6664c1b39a0d5d6e7c3238e8e5eac5c51e1ec39","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/i18n","sources":31,"sha256":"4bcb36ff124e98b8405929277c2acd9afdeb73347b814df67cf26a80727996af","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/ui/apps/console/src/pages/auth/AuthLayout.tsx b/ui/apps/console/src/pages/auth/AuthLayout.tsx index 4859c72d6f7..adebb0cb3a2 100644 --- a/ui/apps/console/src/pages/auth/AuthLayout.tsx +++ b/ui/apps/console/src/pages/auth/AuthLayout.tsx @@ -33,7 +33,14 @@ import { useEffect, type ReactNode } from 'react'; import { useObjectTranslation } from '@object-ui/i18n'; import { cn } from '@object-ui/components'; -import { getProductName, getLogoUrl } from '@object-ui/app-shell'; +import { getProductName, getLogoUrl, isDefaultProductName } from '@object-ui/app-shell'; + +/** + * The developer site the Community Console's "Powered by ObjectStack" mark + * links to. ObjectStack is the protocol, SDK and framework the product is built + * on; the product itself is ObjectOS, in its three editions. + */ +const OBJECTSTACK_SITE_URL = 'https://objectstack.ai'; export interface AuthLayoutProps { children: ReactNode; @@ -119,6 +126,10 @@ export function AuthLayout({ children, formWidth = 'sm' }: AuthLayoutProps) { const host = currentHost(); const showHostPill = !!host && !isCanonicalCloudHost(host) && !isLocalHost(host); + // The Community edition carries the default wordmark and says what it is + // built on; a runtime that pushed its own `branding.productName` (Cloud, + // Enterprise, a cloud-connected workspace) keeps its single brand line. + const showPoweredBy = isDefaultProductName(); useEffect(() => { if (typeof document === 'undefined' || !host) return; @@ -162,6 +173,19 @@ export function AuthLayout({ children, formWidth = 'sm' }: AuthLayoutProps) { ) : null} {children} + {showPoweredBy ? ( +

+ + {t('auth.shell.poweredBy', { defaultValue: 'Powered by ObjectStack' })} + +

+ ) : null} ); diff --git a/ui/apps/console/src/pages/auth/__tests__/AuthLayout.powered-by.test.tsx b/ui/apps/console/src/pages/auth/__tests__/AuthLayout.powered-by.test.tsx new file mode 100644 index 00000000000..7e51161e9c5 --- /dev/null +++ b/ui/apps/console/src/pages/auth/__tests__/AuthLayout.powered-by.test.tsx @@ -0,0 +1,81 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * The sign-in layout's "Powered by ObjectStack" mark. + * + * ObjectOS is the product, in three editions; ObjectStack is the protocol, + * SDK and framework it is built on. The open-source Community Console keeps + * the ObjectOS wordmark and, below the form, says what it is built on with a + * small link to the developer site. A deployment that pushed its own + * `branding.productName` — Cloud, Enterprise, a cloud-connected workspace — + * shows its own name and no second brand line. + * + * The runtime-config singleton is the real one: branding arrives through a + * stubbed `GET /api/v1/runtime/config`, exactly as it does at boot, so the + * mark is judged on the same reading the wordmark uses. + */ + +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { render, screen, cleanup } from '@testing-library/react'; +import { initRuntimeConfig } from '@object-ui/app-shell'; +import { resetRuntimeConfigForTesting } from '../../../../../../packages/app-shell/src/runtime-config'; +import { AuthLayout } from '../AuthLayout'; + +afterEach(() => { + cleanup(); + resetRuntimeConfigForTesting(); + vi.unstubAllGlobals(); +}); + +async function bootWithBranding(branding: Record) { + vi.stubGlobal( + 'fetch', + vi.fn(async () => ({ ok: true, json: async () => ({ branding }) })) as unknown as typeof fetch, + ); + await initRuntimeConfig(); +} + +function renderLayout() { + return render( + +

form

+
, + ); +} + +describe('AuthLayout — "Powered by ObjectStack" on the Community Console', () => { + it('shows the mark, linking to the developer site, under the default wordmark', () => { + renderLayout(); + + expect(screen.getByTestId('auth-brand-name').textContent).toBe('ObjectOS'); + const mark = screen.getByTestId('auth-powered-by'); + expect(mark.textContent).toBe('Powered by ObjectStack'); + expect(mark.getAttribute('href')).toBe('https://objectstack.ai'); + expect(mark.getAttribute('rel')).toContain('noopener'); + }); + + it('shows the mark when the runtime pushes branding that keeps the default name', async () => { + await bootWithBranding({ productName: 'ObjectOS', brandColor: '#0EA5E9' }); + renderLayout(); + + expect(screen.getByTestId('auth-brand-name').textContent).toBe('ObjectOS'); + expect(screen.getByTestId('auth-powered-by')).toBeTruthy(); + }); + + it('shows no mark when the runtime pushes its own product name', async () => { + await bootWithBranding({ productName: 'ObjectOS Cloud' }); + renderLayout(); + + expect(screen.getByTestId('auth-brand-name').textContent).toBe('ObjectOS Cloud'); + expect(screen.queryByTestId('auth-powered-by')).toBeNull(); + expect(screen.queryByText(/Powered by/)).toBeNull(); + }); + + it('shows no mark for an operator rebrand either', async () => { + await bootWithBranding({ productName: 'Acme Studio', logoUrl: '/acme.svg' }); + renderLayout(); + + expect(screen.getByTestId('auth-brand-name').textContent).toBe('Acme Studio'); + expect(screen.queryByTestId('auth-powered-by')).toBeNull(); + }); +}); diff --git a/ui/packages/app-shell/src/index.ts b/ui/packages/app-shell/src/index.ts index 807d46d84c4..0ea5a7c538d 100644 --- a/ui/packages/app-shell/src/index.ts +++ b/ui/packages/app-shell/src/index.ts @@ -196,6 +196,9 @@ export { getCloudBase, getProductName, getProductShortName, + // The Community-default reading behind the sign-in page's "Powered by + // ObjectStack" mark: true until a runtime pushes its own `productName`. + isDefaultProductName, getPlatformStage, getBrandColor, getLogoUrl, diff --git a/ui/packages/app-shell/src/runtime-config.test.ts b/ui/packages/app-shell/src/runtime-config.test.ts index 42143a0829b..e6d09b2e606 100644 --- a/ui/packages/app-shell/src/runtime-config.test.ts +++ b/ui/packages/app-shell/src/runtime-config.test.ts @@ -9,7 +9,7 @@ */ import { describe, it, expect, afterEach, vi } from 'vitest'; -import { initRuntimeConfig, getRuntimeConfig, getPlatformStage, isAiStudioEnabled, isMarketplaceEnabled, isStorageUsageServed, isSingleAppSwitcherHidden, getClientErrorReporting, resetRuntimeConfigForTesting } from './runtime-config.js'; +import { initRuntimeConfig, getRuntimeConfig, getPlatformStage, getProductName, isDefaultProductName, isAiStudioEnabled, isMarketplaceEnabled, isStorageUsageServed, isSingleAppSwitcherHidden, getClientErrorReporting, resetRuntimeConfigForTesting } from './runtime-config.js'; function mockConfig(features: Record) { vi.stubGlobal('fetch', vi.fn(async () => ({ @@ -498,3 +498,41 @@ describe('runtime-config client error-reporting sink', () => { expect(getClientErrorReporting()).toBeNull(); }); }); + +/** + * `isDefaultProductName()` — the one reading behind the Community Console's + * "Powered by ObjectStack" mark on the sign-in page. True while the deployment + * carries the open-source default wordmark; false once a runtime has pushed + * its own `branding.productName` (Cloud, Enterprise, a cloud-connected + * workspace). The negative legs are the ones that matter: a mark that also + * shows on a branded deployment is a second brand line nobody asked for. + */ +describe('runtime-config default product name', () => { + it('is the default before init, and after a payload that carries no branding', async () => { + resetRuntimeConfigForTesting(); + expect(isDefaultProductName()).toBe(true); + mockBranding({}); + await initRuntimeConfig(); + expect(isDefaultProductName()).toBe(true); + }); + + it('is not the default once the runtime pushes its own product name', async () => { + mockBranding({ productName: 'ObjectOS Cloud' }); + await initRuntimeConfig(); + expect(isDefaultProductName()).toBe(false); + expect(getProductName()).toBe('ObjectOS Cloud'); + }); + + it('stays the default when the runtime pushes the same name the default already has', async () => { + mockBranding({ productName: 'ObjectOS', logoUrl: '/custom-logo.svg' }); + await initRuntimeConfig(); + expect(isDefaultProductName()).toBe(true); + }); + + it('ignores a blank product name the way the product-name accessor does', async () => { + mockBranding({ productName: ' ' }); + await initRuntimeConfig(); + expect(getProductName()).toBe('ObjectOS'); + expect(isDefaultProductName()).toBe(true); + }); +}); diff --git a/ui/packages/app-shell/src/runtime-config.ts b/ui/packages/app-shell/src/runtime-config.ts index f6d6eedb9aa..7744ff93f7a 100644 --- a/ui/packages/app-shell/src/runtime-config.ts +++ b/ui/packages/app-shell/src/runtime-config.ts @@ -527,6 +527,24 @@ export function getProductShortName(): string { return current.branding?.productShortName || getProductName(); } +/** + * Whether the deployment still carries the open-source Console's default + * product name — i.e. no server-pushed `branding.productName` has rebranded + * it. The Community edition's sign-in page shows its "Powered by ObjectStack" + * mark on this reading and on nothing else: a Cloud or Enterprise runtime + * (and a cloud-connected runtime, which pushes its workspace name) sends its + * own name and gets no second brand line. + * + * The product name is the one branding key every runtime that brands itself + * sends, so its equality with the default is the signal; the server pushes no + * explicit "edition" key today, and a logo or colour alone does not count as + * a rebrand here. Before `initRuntimeConfig()` has run the default is in + * force, so this answers `true` — the same answer the wordmark gives. + */ +export function isDefaultProductName(): boolean { + return getProductName() === defaults.branding.productName; +} + /** * Product lifecycle stage — drives the top-bar preview/beta badge. Defaults to * `'preview'` until the server (or an operator override) says otherwise, so the diff --git a/ui/packages/i18n/src/__tests__/untranslated-identity-4376.test.ts b/ui/packages/i18n/src/__tests__/untranslated-identity-4376.test.ts index c0b7bc82b34..14b296849a0 100644 --- a/ui/packages/i18n/src/__tests__/untranslated-identity-4376.test.ts +++ b/ui/packages/i18n/src/__tests__/untranslated-identity-4376.test.ts @@ -101,6 +101,7 @@ const LEGITIMATE_IDENTITIES: Record = { // failed on a leftover entry — dead (no identity left to excuse) and unknown // (no such key in `en`) — which is that guard doing its job. 'publicForm.poweredBy': 'Powered by ObjectStack — product attribution; ja keeps the English wordmark line.', + 'auth.shell.poweredBy': 'Powered by ObjectStack — the Community Console sign-in page attribution; ja keeps the English wordmark line, as publicForm.poweredBy does.', 'console.studio.title': 'Studio — the product name, which every pack writes in Latin script in its own Studio strings (objectui#11863).', 'connectAgent.apiKey.badge': 'headless — the literal mode name the CLI and API use.', // `marketplace.pricing.freemium` sat here until objectui#8754 retired the diff --git a/ui/packages/i18n/src/locales/ar.ts b/ui/packages/i18n/src/locales/ar.ts index e7fe620e843..a55382e4b64 100644 --- a/ui/packages/i18n/src/locales/ar.ts +++ b/ui/packages/i18n/src/locales/ar.ts @@ -2877,6 +2877,7 @@ const ar = { }, shell: { tenantHostHint: "تسجل الدخول إلى هذا النظام", + poweredBy: "مدعوم بـ ObjectStack", }, layout: { headline: "أنشئ تطبيقات أعمال قوية بسرعة أكبر.", @@ -4090,7 +4091,7 @@ const ar = { cancel: "إلغاء", }, bound: { - title: "متصل بـ ObjectStack Cloud", + title: "متصل بـ ObjectOS Cloud", runtime: "بيئة التشغيل", organization: "المؤسسة", approvedBy: "تمت الموافقة بواسطة", @@ -4217,13 +4218,13 @@ const ar = { install: { dialogTitle: "تثبيت {{name}}", dialogDescCurrent: "تثبيت في هذه البيئة ({{host}}).", - dialogDescPicker: "اختر بيئة لتثبيت هذا التطبيق. يجب أن تكون مسجلاً في ObjectStack Cloud.", + dialogDescPicker: "اختر بيئة لتثبيت هذا التطبيق. يجب أن تكون مسجلاً في ObjectOS Cloud.", environment: "البيئة", environmentPlaceholder: "تحديد بيئة", includeSampleData: "تضمين بيانات نموذجية", noEnvs: "لم يتم العثور على بيئات في مؤسستك النشطة.", noPermission: "ليس لديك إذن لتثبيت التطبيقات. فقط مالكو المؤسسة والمشرفون يمكنهم التثبيت — تواصل مع مشرف مؤسستك.", - signInFirst: "يجب عليك أولاً تسجيل الدخول إلى ObjectStack Cloud.", + signInFirst: "يجب عليك أولاً تسجيل الدخول إلى ObjectOS Cloud.", success: "تم التثبيت بنجاح. افتح البيئة لرؤية التطبيق الجديد.", localSuccess: "تم تثبيت v{{version}} في هذا الوقت التشغيل. يجب أن يظهر \"{{name}}\" الآن في مبدّل التطبيقات.", localLoadsAtRestart: "تم تثبيت v{{version}} في وقت التشغيل هذا. سيُحمَّل \"{{name}}\" عند إعادة التشغيل التالية لوقت التشغيل، ولن يظهر في مبدّل التطبيقات قبل ذلك.", @@ -4265,7 +4266,7 @@ const ar = { update: { dialogTitle: "تحديث {{name}}", dialogDescCurrent: "تحديث النسخة المثبتة في هذه البيئة ({{host}}).", - dialogDescPicker: "اختر البيئة التي تريد تحديث النسخة المثبتة فيها. يجب أن تكون مسجلاً في ObjectStack Cloud.", + dialogDescPicker: "اختر البيئة التي تريد تحديث النسخة المثبتة فيها. يجب أن تكون مسجلاً في ObjectOS Cloud.", majorNotice: "هذا تحديث إلى إصدار رئيسي جديد (v{{fromMajor}} → v{{toMajor}})، وقد يتضمن تغييرات غير متوافقة.", releaseNotes: "اقرأ ملاحظات الإصدار v{{version}}", success: "تم التحديث إلى v{{version}}.", diff --git a/ui/packages/i18n/src/locales/de.ts b/ui/packages/i18n/src/locales/de.ts index 4ce472a35ed..383f8ec23bd 100644 --- a/ui/packages/i18n/src/locales/de.ts +++ b/ui/packages/i18n/src/locales/de.ts @@ -2675,6 +2675,7 @@ const de = { }, shell: { tenantHostHint: "Sie melden sich bei diesem System an", + poweredBy: "Bereitgestellt von ObjectStack", }, layout: { headline: "Leistungsstarke Geschäftsanwendungen schneller entwickeln.", @@ -3797,7 +3798,7 @@ const de = { cancel: "Abbrechen", }, bound: { - title: "Mit ObjectStack Cloud verbunden", + title: "Mit ObjectOS Cloud verbunden", runtime: "Runtime", organization: "Organisation", approvedBy: "Genehmigt von", @@ -3916,13 +3917,13 @@ const de = { install: { dialogTitle: "{{name}} installieren", dialogDescCurrent: "In dieser Umgebung installieren ({{host}}).", - dialogDescPicker: "Wählen Sie eine Umgebung, in der Sie diese App installieren möchten. Sie müssen bei ObjectStack Cloud angemeldet sein.", + dialogDescPicker: "Wählen Sie eine Umgebung, in der Sie diese App installieren möchten. Sie müssen bei ObjectOS Cloud angemeldet sein.", environment: "Umgebung", environmentPlaceholder: "Umgebung auswählen", includeSampleData: "Beispieldaten einschließen", noEnvs: "Keine Umgebungen in Ihrer aktiven Organisation gefunden.", noPermission: "Sie haben keine Berechtigung, Apps in einer Umgebung zu installieren. Nur Organisationsinhaber und Administratoren können installieren — wenden Sie sich an den Administrator Ihrer Organisation.", - signInFirst: "Sie müssen sich zuerst bei ObjectStack Cloud anmelden.", + signInFirst: "Sie müssen sich zuerst bei ObjectOS Cloud anmelden.", success: "Erfolgreich installiert. Öffnen Sie die Umgebung, um die neue App zu sehen.", localSuccess: "v{{version}} in diesem Laufzeitsystem installiert. „{{name}}“ sollte jetzt im App-Wechsler erscheinen.", localLoadsAtRestart: "v{{version}} in diesem Laufzeitsystem installiert. „{{name}}“ wird beim nächsten Neustart des Laufzeitsystems geladen und erscheint erst dann im App-Wechsler.", @@ -3964,7 +3965,7 @@ const de = { update: { dialogTitle: "{{name}} aktualisieren", dialogDescCurrent: "Die in dieser Umgebung installierte Kopie aktualisieren ({{host}}).", - dialogDescPicker: "Wählen Sie die Umgebung, deren installierte Kopie Sie aktualisieren möchten. Sie müssen bei ObjectStack Cloud angemeldet sein.", + dialogDescPicker: "Wählen Sie die Umgebung, deren installierte Kopie Sie aktualisieren möchten. Sie müssen bei ObjectOS Cloud angemeldet sein.", majorNotice: "Dies ist ein Update auf eine neue Hauptversion (v{{fromMajor}} → v{{toMajor}}), das inkompatible Änderungen enthalten kann.", releaseNotes: "Versionshinweise zu v{{version}} lesen", success: "Auf v{{version}} aktualisiert.", diff --git a/ui/packages/i18n/src/locales/en.ts b/ui/packages/i18n/src/locales/en.ts index 16f55d84818..b439ef3d0af 100644 --- a/ui/packages/i18n/src/locales/en.ts +++ b/ui/packages/i18n/src/locales/en.ts @@ -3189,6 +3189,7 @@ const en = { }, shell: { tenantHostHint: 'You are signing in to this system', + poweredBy: 'Powered by ObjectStack', }, // These were previously supplied only as inline `defaultValue:` args at the // SetPasswordPage call sites, so `zh` was the only pack that could carry a @@ -4558,7 +4559,7 @@ const en = { cancel: 'Cancel', }, bound: { - title: 'Connected to ObjectStack Cloud', + title: 'Connected to ObjectOS Cloud', runtime: 'Runtime', organization: 'Organization', approvedBy: 'Approved by', @@ -4661,13 +4662,13 @@ const en = { install: { dialogTitle: 'Install {{name}}', dialogDescCurrent: 'Install into this environment ({{host}}).', - dialogDescPicker: 'Choose an environment to install this app into. You need to be signed into ObjectStack Cloud.', + dialogDescPicker: 'Choose an environment to install this app into. You need to be signed into ObjectOS Cloud.', environment: 'Environment', environmentPlaceholder: 'Pick an environment', includeSampleData: 'Include sample data', noEnvs: 'No environments found in your active organization.', noPermission: 'You do not have permission to install apps in any environment. Only organization owners and admins can install — ask your organization admin.', - signInFirst: 'You need to sign into ObjectStack Cloud first. Click "Open on cloud" below.', + signInFirst: 'You need to sign into ObjectOS Cloud first. Click "Open on cloud" below.', success: 'Installed successfully. Open the environment to see the new app.', localSuccess: 'Installed v{{version}} to this runtime. "{{name}}" should now appear in the app switcher.', // objectui#12098 — the install answered `hotLoaded: false`: installed, and @@ -4714,7 +4715,7 @@ const en = { update: { dialogTitle: 'Update {{name}}', dialogDescCurrent: 'Update the copy installed in this environment ({{host}}).', - dialogDescPicker: 'Choose the environment whose installed copy to update. You need to be signed into ObjectStack Cloud.', + dialogDescPicker: 'Choose the environment whose installed copy to update. You need to be signed into ObjectOS Cloud.', majorNotice: 'This is a major version update (v{{fromMajor}} → v{{toMajor}}), which may include breaking changes.', releaseNotes: 'Read the v{{version}} release notes', success: 'Updated to v{{version}}.', diff --git a/ui/packages/i18n/src/locales/es.ts b/ui/packages/i18n/src/locales/es.ts index f41b17f9574..64b07d1885f 100644 --- a/ui/packages/i18n/src/locales/es.ts +++ b/ui/packages/i18n/src/locales/es.ts @@ -2727,6 +2727,7 @@ const es = { }, shell: { tenantHostHint: "Está iniciando sesión en este sistema", + poweredBy: "Desarrollado por ObjectStack", }, layout: { headline: "Cree aplicaciones empresariales potentes, más rápido.", @@ -3871,7 +3872,7 @@ const es = { cancel: "Cancelar", }, bound: { - title: "Conectado a ObjectStack Cloud", + title: "Conectado a ObjectOS Cloud", runtime: "Runtime", organization: "Organización", approvedBy: "Aprobado por", @@ -3992,13 +3993,13 @@ const es = { install: { dialogTitle: "Instalar {{name}}", dialogDescCurrent: "Instalar en este entorno ({{host}}).", - dialogDescPicker: "Elija un entorno donde instalar esta aplicación. Debe haber iniciado sesión en ObjectStack Cloud.", + dialogDescPicker: "Elija un entorno donde instalar esta aplicación. Debe haber iniciado sesión en ObjectOS Cloud.", environment: "Entorno", environmentPlaceholder: "Seleccionar entorno", includeSampleData: "Incluir datos de muestra", noEnvs: "No se encontraron entornos en su organización activa.", noPermission: "No tiene permiso para instalar aplicaciones en un entorno. Solo los propietarios y administradores de la organización pueden instalar; pida ayuda al administrador de su organización.", - signInFirst: "Primero debe iniciar sesión en ObjectStack Cloud.", + signInFirst: "Primero debe iniciar sesión en ObjectOS Cloud.", success: "Instalado correctamente. Abra el entorno para ver la nueva aplicación.", localSuccess: "v{{version}} instalado en este runtime. \"{{name}}\" debería aparecer en el selector de aplicaciones ahora.", localLoadsAtRestart: "v{{version}} instalado en este runtime. \"{{name}}\" se cargará en el próximo reinicio del runtime y no aparecerá en el selector de aplicaciones hasta entonces.", @@ -4040,7 +4041,7 @@ const es = { update: { dialogTitle: "Actualizar {{name}}", dialogDescCurrent: "Actualizar la copia instalada en este entorno ({{host}}).", - dialogDescPicker: "Elija el entorno cuya copia instalada desea actualizar. Debe haber iniciado sesión en ObjectStack Cloud.", + dialogDescPicker: "Elija el entorno cuya copia instalada desea actualizar. Debe haber iniciado sesión en ObjectOS Cloud.", majorNotice: "Esta es una actualización de versión principal (v{{fromMajor}} → v{{toMajor}}), que puede incluir cambios incompatibles.", releaseNotes: "Leer las notas de la versión v{{version}}", success: "Actualizado a la v{{version}}.", diff --git a/ui/packages/i18n/src/locales/fr.ts b/ui/packages/i18n/src/locales/fr.ts index c33746f956c..95c9cf48b5b 100644 --- a/ui/packages/i18n/src/locales/fr.ts +++ b/ui/packages/i18n/src/locales/fr.ts @@ -2723,6 +2723,7 @@ const fr = { }, shell: { tenantHostHint: "Vous vous connectez à ce système", + poweredBy: "Propulsé par ObjectStack", }, layout: { headline: "Créez des applications métier puissantes, plus vite.", @@ -3867,7 +3868,7 @@ const fr = { cancel: "Annuler", }, bound: { - title: "Connecté à ObjectStack Cloud", + title: "Connecté à ObjectOS Cloud", runtime: "Runtime", organization: "Organisation", approvedBy: "Approuvé par", @@ -3988,13 +3989,13 @@ const fr = { install: { dialogTitle: "Installer {{name}}", dialogDescCurrent: "Installer dans cet environnement ({{host}}).", - dialogDescPicker: "Choisissez un environnement où installer cette application. Vous devez être connecté à ObjectStack Cloud.", + dialogDescPicker: "Choisissez un environnement où installer cette application. Vous devez être connecté à ObjectOS Cloud.", environment: "Environnement", environmentPlaceholder: "Sélectionner un environnement", includeSampleData: "Inclure des données d'exemple", noEnvs: "Aucun environnement trouvé dans votre organisation active.", noPermission: "Vous n'avez pas la permission d'installer des applications dans un environnement. Seuls les propriétaires et administrateurs de l'organisation peuvent installer — contactez l'administrateur de votre organisation.", - signInFirst: "Vous devez d'abord vous connecter à ObjectStack Cloud.", + signInFirst: "Vous devez d'abord vous connecter à ObjectOS Cloud.", success: "Installé avec succès. Ouvrez l'environnement pour voir la nouvelle application.", localSuccess: "v{{version}} installé dans ce runtime. \"{{name}}\" devrait maintenant apparaître dans le sélecteur d'applications.", localLoadsAtRestart: "v{{version}} installé dans ce runtime. \"{{name}}\" sera chargé au prochain redémarrage du runtime et n'apparaîtra pas dans le sélecteur d'applications d'ici là.", @@ -4036,7 +4037,7 @@ const fr = { update: { dialogTitle: "Mettre à jour {{name}}", dialogDescCurrent: "Mettre à jour la copie installée dans cet environnement ({{host}}).", - dialogDescPicker: "Choisissez l'environnement dont la copie installée doit être mise à jour. Vous devez être connecté à ObjectStack Cloud.", + dialogDescPicker: "Choisissez l'environnement dont la copie installée doit être mise à jour. Vous devez être connecté à ObjectOS Cloud.", majorNotice: "Il s'agit d'une mise à jour de version majeure (v{{fromMajor}} → v{{toMajor}}), qui peut comporter des changements incompatibles.", releaseNotes: "Lire les notes de version de la v{{version}}", success: "Mis à jour vers la v{{version}}.", diff --git a/ui/packages/i18n/src/locales/ja.ts b/ui/packages/i18n/src/locales/ja.ts index ab506d3d835..a8891a78470 100644 --- a/ui/packages/i18n/src/locales/ja.ts +++ b/ui/packages/i18n/src/locales/ja.ts @@ -2677,6 +2677,7 @@ const ja = { }, shell: { tenantHostHint: "このシステムにサインインしています", + poweredBy: "Powered by ObjectStack", }, layout: { headline: "強力なビジネスアプリを、より速く。", @@ -3799,7 +3800,7 @@ const ja = { cancel: "キャンセル", }, bound: { - title: "ObjectStack Cloud に接続済み", + title: "ObjectOS Cloud に接続済み", runtime: "ランタイム", organization: "組織", approvedBy: "承認者", @@ -3918,13 +3919,13 @@ const ja = { install: { dialogTitle: "{{name}} をインストール", dialogDescCurrent: "この環境({{host}})にインストールします。", - dialogDescPicker: "このアプリをインストールする環境を選択します。ObjectStack Cloudにサインインする必要があります。", + dialogDescPicker: "このアプリをインストールする環境を選択します。ObjectOS Cloudにサインインする必要があります。", environment: "環境", environmentPlaceholder: "環境を選択", includeSampleData: "サンプルデータを含む", noEnvs: "アクティブな組織に環境が見つかりません。", noPermission: "どの環境にもアプリをインストールする権限がありません。組織のオーナーと管理者のみインストールできます。組織の管理者にお問い合わせください。", - signInFirst: "最初にObjectStack Cloudにサインインしてください。", + signInFirst: "最初にObjectOS Cloudにサインインしてください。", success: "インストールが完了しました。環境を開いて新しいアプリを確認してください。", localSuccess: "このランタイムに v{{version}} をインストールしました。「{{name}}」がアプリスイッチャーに表示されます。", localLoadsAtRestart: "このランタイムに v{{version}} をインストールしました。「{{name}}」はランタイムの次回再起動時に読み込まれ、それまではアプリスイッチャーに表示されません。", @@ -3966,7 +3967,7 @@ const ja = { update: { dialogTitle: "{{name}} を更新", dialogDescCurrent: "この環境({{host}})にインストール済みのアプリを更新します。", - dialogDescPicker: "インストール済みのアプリを更新する環境を選択します。ObjectStack Cloudにサインインする必要があります。", + dialogDescPicker: "インストール済みのアプリを更新する環境を選択します。ObjectOS Cloudにサインインする必要があります。", majorNotice: "メジャーバージョンの更新です(v{{fromMajor}} → v{{toMajor}})。互換性のない変更が含まれる可能性があります。", releaseNotes: "v{{version}} のリリースノートを読む", success: "v{{version}} に更新しました。", diff --git a/ui/packages/i18n/src/locales/ko.ts b/ui/packages/i18n/src/locales/ko.ts index cc986a057fa..6b547e82c61 100644 --- a/ui/packages/i18n/src/locales/ko.ts +++ b/ui/packages/i18n/src/locales/ko.ts @@ -2675,6 +2675,7 @@ const ko = { }, shell: { tenantHostHint: "이 시스템에 로그인하는 중입니다", + poweredBy: "ObjectStack 제공", }, layout: { headline: "강력한 비즈니스 애플리케이션을 더 빠르게.", @@ -3796,7 +3797,7 @@ const ko = { cancel: "취소", }, bound: { - title: "ObjectStack Cloud에 연결됨", + title: "ObjectOS Cloud에 연결됨", runtime: "런타임", organization: "조직", approvedBy: "승인자", @@ -3915,13 +3916,13 @@ const ko = { install: { dialogTitle: "{{name}} 설치", dialogDescCurrent: "이 환경에 설치 ({{host}}).", - dialogDescPicker: "이 앱을 설치할 환경을 선택하세요. ObjectStack Cloud에 로그인되어 있어야 합니다.", + dialogDescPicker: "이 앱을 설치할 환경을 선택하세요. ObjectOS Cloud에 로그인되어 있어야 합니다.", environment: "환경", environmentPlaceholder: "환경 선택", includeSampleData: "샘플 데이터 포함", noEnvs: "활성 조직에서 환경을 찾을 수 없습니다.", noPermission: "환경에 앱을 설치할 권한이 없습니다. 조직 소유자 및 관리자만 설치할 수 있습니다 — 조직 관리자에게 문의하세요.", - signInFirst: "먼저 ObjectStack Cloud에 로그인해야 합니다.", + signInFirst: "먼저 ObjectOS Cloud에 로그인해야 합니다.", success: "설치 성공. 환경을 열어 새 앱을 확인하세요.", localSuccess: "이 런타임에 v{{version}} 설치됨. \"{{name}}\"이(가) 이제 앱 전환기에 표시됩니다.", localLoadsAtRestart: "이 런타임에 v{{version}} 설치됨. \"{{name}}\"은(는) 런타임이 다음에 다시 시작될 때 로드되며, 그때까지는 앱 전환기에 표시되지 않습니다.", @@ -3963,7 +3964,7 @@ const ko = { update: { dialogTitle: "{{name}} 업데이트", dialogDescCurrent: "이 환경({{host}})에 설치된 앱을 업데이트합니다.", - dialogDescPicker: "설치된 앱을 업데이트할 환경을 선택하세요. ObjectStack Cloud에 로그인되어 있어야 합니다.", + dialogDescPicker: "설치된 앱을 업데이트할 환경을 선택하세요. ObjectOS Cloud에 로그인되어 있어야 합니다.", majorNotice: "메이저 버전 업데이트입니다(v{{fromMajor}} → v{{toMajor}}). 호환되지 않는 변경이 포함될 수 있습니다.", releaseNotes: "v{{version}} 릴리스 노트 읽기", success: "v{{version}}(으)로 업데이트했습니다.", diff --git a/ui/packages/i18n/src/locales/pt.ts b/ui/packages/i18n/src/locales/pt.ts index 078ff6f1af5..d345eebebca 100644 --- a/ui/packages/i18n/src/locales/pt.ts +++ b/ui/packages/i18n/src/locales/pt.ts @@ -2722,6 +2722,7 @@ const pt = { }, shell: { tenantHostHint: "Você está fazendo login neste sistema", + poweredBy: "Desenvolvido por ObjectStack", }, layout: { headline: "Crie aplicativos de negócios poderosos, com mais rapidez.", @@ -3866,7 +3867,7 @@ const pt = { cancel: "Cancelar", }, bound: { - title: "Conectado ao ObjectStack Cloud", + title: "Conectado ao ObjectOS Cloud", runtime: "Runtime", organization: "Organização", approvedBy: "Aprovado por", @@ -3987,13 +3988,13 @@ const pt = { install: { dialogTitle: "Instalar {{name}}", dialogDescCurrent: "Instalar neste ambiente ({{host}}).", - dialogDescPicker: "Escolha um ambiente para instalar este aplicativo. Você deve estar conectado ao ObjectStack Cloud.", + dialogDescPicker: "Escolha um ambiente para instalar este aplicativo. Você deve estar conectado ao ObjectOS Cloud.", environment: "Ambiente", environmentPlaceholder: "Selecionar ambiente", includeSampleData: "Incluir dados de amostra", noEnvs: "Nenhum ambiente encontrado em sua organização ativa.", noPermission: "Você não tem permissão para instalar aplicativos em um ambiente. Somente proprietários e administradores da organização podem instalar — fale com o administrador da sua organização.", - signInFirst: "Você deve primeiro fazer login no ObjectStack Cloud.", + signInFirst: "Você deve primeiro fazer login no ObjectOS Cloud.", success: "Instalado com sucesso. Abra o ambiente para ver o novo aplicativo.", localSuccess: "v{{version}} instalado neste runtime. \"{{name}}\" deve aparecer no seletor de aplicativos agora.", localLoadsAtRestart: "v{{version}} instalado neste runtime. \"{{name}}\" será carregado na próxima reinicialização do runtime e não aparecerá no seletor de aplicativos até lá.", @@ -4035,7 +4036,7 @@ const pt = { update: { dialogTitle: "Atualizar {{name}}", dialogDescCurrent: "Atualizar a cópia instalada neste ambiente ({{host}}).", - dialogDescPicker: "Escolha o ambiente cuja cópia instalada deseja atualizar. Você deve estar conectado ao ObjectStack Cloud.", + dialogDescPicker: "Escolha o ambiente cuja cópia instalada deseja atualizar. Você deve estar conectado ao ObjectOS Cloud.", majorNotice: "Esta é uma atualização de versão principal (v{{fromMajor}} → v{{toMajor}}), que pode incluir alterações incompatíveis.", releaseNotes: "Ler as notas da versão v{{version}}", success: "Atualizado para a v{{version}}.", diff --git a/ui/packages/i18n/src/locales/ru.ts b/ui/packages/i18n/src/locales/ru.ts index e4f4ed7bed8..b4cea4cf882 100644 --- a/ui/packages/i18n/src/locales/ru.ts +++ b/ui/packages/i18n/src/locales/ru.ts @@ -2784,6 +2784,7 @@ const ru = { }, shell: { tenantHostHint: "Вы входите в эту систему", + poweredBy: "Работает на ObjectStack", }, layout: { headline: "Создавайте мощные бизнес-приложения быстрее.", @@ -3954,7 +3955,7 @@ const ru = { cancel: "Отмена", }, bound: { - title: "Подключено к ObjectStack Cloud", + title: "Подключено к ObjectOS Cloud", runtime: "Среда выполнения", organization: "Организация", approvedBy: "Подтвердил", @@ -4077,13 +4078,13 @@ const ru = { install: { dialogTitle: "Установить {{name}}", dialogDescCurrent: "Установить в эту среду ({{host}}).", - dialogDescPicker: "Выберите среду для установки. Требуется вход в ObjectStack Cloud.", + dialogDescPicker: "Выберите среду для установки. Требуется вход в ObjectOS Cloud.", environment: "Среда", environmentPlaceholder: "Выбрать среду", includeSampleData: "Включить образцы данных", noEnvs: "В активной организации нет сред.", noPermission: "Нет прав на установку. Только владельцы и администраторы организации могут устанавливать — обратитесь к администратору вашей организации.", - signInFirst: "Сначала войдите в ObjectStack Cloud.", + signInFirst: "Сначала войдите в ObjectOS Cloud.", success: "Установлено. Откройте среду, чтобы увидеть новое приложение.", localSuccess: "v{{version}} установлено. \"{{name}}\" должно появиться в переключателе.", localLoadsAtRestart: "v{{version}} установлено. \"{{name}}\" загрузится при следующем перезапуске среды выполнения и до этого не появится в переключателе.", @@ -4125,7 +4126,7 @@ const ru = { update: { dialogTitle: "Обновить {{name}}", dialogDescCurrent: "Обновить копию, установленную в этой среде ({{host}}).", - dialogDescPicker: "Выберите среду, в которой нужно обновить установленную копию. Требуется вход в ObjectStack Cloud.", + dialogDescPicker: "Выберите среду, в которой нужно обновить установленную копию. Требуется вход в ObjectOS Cloud.", majorNotice: "Это обновление основной версии (v{{fromMajor}} → v{{toMajor}}), оно может содержать несовместимые изменения.", releaseNotes: "Прочитать примечания к выпуску v{{version}}", success: "Обновлено до v{{version}}.", diff --git a/ui/packages/i18n/src/locales/zh.ts b/ui/packages/i18n/src/locales/zh.ts index 9efa75e303f..d0bdf4b4fce 100644 --- a/ui/packages/i18n/src/locales/zh.ts +++ b/ui/packages/i18n/src/locales/zh.ts @@ -2744,6 +2744,7 @@ const zh = { }, shell: { tenantHostHint: '您正在登录本系统', + poweredBy: '由 ObjectStack 提供技术支持', }, layout: { headline: '更快地构建强大的业务应用。', @@ -3981,7 +3982,7 @@ const zh = { cancel: '取消', }, bound: { - title: '已连接到 ObjectStack 云', + title: '已连接到 ObjectOS Cloud', runtime: '运行时', organization: '组织', approvedBy: '批准人', @@ -4084,13 +4085,13 @@ const zh = { install: { dialogTitle: '安装 {{name}}', dialogDescCurrent: '安装到当前环境({{host}})。', - dialogDescPicker: '选择要将此应用安装到的环境。需要先登录 ObjectStack Cloud。', + dialogDescPicker: '选择要将此应用安装到的环境。需要先登录 ObjectOS Cloud。', environment: '环境', environmentPlaceholder: '选择一个环境', includeSampleData: '包含示例数据', noEnvs: '当前组织没有可用的环境。', noPermission: '你没有权限在任何环境中安装应用。仅组织所有者和管理员可以安装 — 请联系你的组织管理员。', - signInFirst: '请先登录 ObjectStack Cloud,然后点击下方"在云端打开"。', + signInFirst: '请先登录 ObjectOS Cloud,然后点击下方"在云端打开"。', success: '安装成功。打开环境即可看到新应用。', localSuccess: '已将 v{{version}} 安装到本运行时。"{{name}}" 应该已经出现在应用切换器中。', localLoadsAtRestart: '已将 v{{version}} 安装到本运行时。"{{name}}" 将在运行时下次重启时加载,在此之前不会出现在应用切换器中。', @@ -4132,7 +4133,7 @@ const zh = { update: { dialogTitle: '更新 {{name}}', dialogDescCurrent: '更新当前环境({{host}})中已安装的应用。', - dialogDescPicker: '选择要更新已安装应用的环境。需要先登录 ObjectStack Cloud。', + dialogDescPicker: '选择要更新已安装应用的环境。需要先登录 ObjectOS Cloud。', majorNotice: '这是一次主版本更新(v{{fromMajor}} → v{{toMajor}}),可能包含不兼容的变更。', releaseNotes: '查看 v{{version}} 的版本说明', success: '已更新到 v{{version}}。',