From e10319208deedc1406a6364f6fcb6892d3af9d22 Mon Sep 17 00:00:00 2001 From: reoring Date: Mon, 9 Nov 2026 11:25:49 +0900 Subject: [PATCH] catalog listing: measure bounds in UTF-8 bytes like the Cloud store; shorten over-long Japanese alt text --- catalog/listing.yaml | 156 ++++++++++++++++++++-------------------- hack/catalog-listing.ts | 9 +-- 2 files changed, 83 insertions(+), 82 deletions(-) diff --git a/catalog/listing.yaml b/catalog/listing.yaml index 34970fd..65b0692 100644 --- a/catalog/listing.yaml +++ b/catalog/listing.yaml @@ -22,82 +22,82 @@ locales: 人とグループは Directory で管理し、サインインには組織のアカウントを使います。アプリの情報、公開範囲、コメントは Project の中に保存されます。AI を使った作成と修正は、Project で AI が有効な場合に使えます。 highlights: - - screenshot: home - locales: - en: - title: Find the apps your team uses - body: The home page shows your group's apps, apps shared from parent groups, and new apps across the company, each with its current status. Search by name, purpose, or group. - ja: - title: チームのアプリがすぐ見つかる - body: ホームに所属グループのアプリ、上位グループから共有されたアプリ、全社の新着が、いまの状態と一緒に並びます。名前・用途・組織で探せます。 - - screenshot: new-app - locales: - en: - title: Start the way that suits you - body: Describe what you need and build it with AI, let AI read your Excel ledgers and write a specification, pick a business template, or bring code you already have. - ja: - title: 始め方を選べる - body: やりたいことを書いて AI と作る、Excel 台帳を AI に読ませて仕様書から作る、業務テンプレートを選ぶ、作ったコードを持ち込む、から選べます。 - - screenshot: app-overview - locales: - en: - title: Everything about an app on one page - body: See the running version, the URL, screenshots, and what the app can do, then move to its guide, data, release history, settings, and logs. Editors can ask AI for a fix, which arrives as a proposal for the owners to review. - ja: - title: アプリのことが 1 ページで分かる - body: いま動いている版、URL、画面の写真、できることを確かめ、つかいかた・データ・更新履歴・設定・ログへ移れます。AI に直してもらった変更は、担当者が確認する提案として届きます。 - - screenshot: share - locales: - en: - title: Share with the right people - body: Add people by name or email address and choose groups from the organisation tree; subgroups are included. Widening access asks for confirmation. No email is sent; you pass the invitation text on yourself. - ja: - title: 必要な人にだけ届ける - body: 名前やメールアドレスで人を加え、組織の一覧からグループを選びます。下のグループも含まれます。公開範囲を広げるときは確認が入ります。メールは送られないので、招待文は自分で相手に渡します。 - - screenshot: releases - locales: - en: - title: Versions you can roll back - body: Changes pushed to the app's repository roll out as a new version. The release history shows which version is running and the state of each one, and you can go back to an earlier version. - ja: - title: 前の版に戻せる - body: リポジトリに入った変更は新しい版として公開されます。更新履歴で、いま動いている版と各版の状態を確かめ、前の版に戻せます。 - - screenshot: data - locales: - en: - title: Look after the app's data - body: Browse the app's tables, search them, and add, edit, or delete rows. - ja: - title: アプリのデータを直接扱える - body: アプリの表を一覧して検索し、行の追加・編集・削除ができます。 +- screenshot: home + locales: + en: + title: Find the apps your team uses + body: The home page shows your group's apps, apps shared from parent groups, and new apps across the company, each with its current status. Search by name, purpose, or group. + ja: + title: チームのアプリがすぐ見つかる + body: ホームに所属グループのアプリ、上位グループから共有されたアプリ、全社の新着が、いまの状態と一緒に並びます。名前・用途・組織で探せます。 +- screenshot: new-app + locales: + en: + title: Start the way that suits you + body: Describe what you need and build it with AI, let AI read your Excel ledgers and write a specification, pick a business template, or bring code you already have. + ja: + title: 始め方を選べる + body: やりたいことを書いて AI と作る、Excel 台帳を AI に読ませて仕様書から作る、業務テンプレートを選ぶ、作ったコードを持ち込む、から選べます。 +- screenshot: app-overview + locales: + en: + title: Everything about an app on one page + body: See the running version, the URL, screenshots, and what the app can do, then move to its guide, data, release history, settings, and logs. Editors can ask AI for a fix, which arrives as a proposal for the owners to review. + ja: + title: アプリのことが 1 ページで分かる + body: いま動いている版、URL、画面の写真、できることを確かめ、つかいかた・データ・更新履歴・設定・ログへ移れます。AI に直してもらった変更は、担当者が確認する提案として届きます。 +- screenshot: share + locales: + en: + title: Share with the right people + body: Add people by name or email address and choose groups from the organisation tree; subgroups are included. Widening access asks for confirmation. No email is sent; you pass the invitation text on yourself. + ja: + title: 必要な人にだけ届ける + body: 名前やメールアドレスで人を加え、組織の一覧からグループを選びます。下のグループも含まれます。公開範囲を広げるときは確認が入ります。メールは送られないので、招待文は自分で相手に渡します。 +- screenshot: releases + locales: + en: + title: Versions you can roll back + body: Changes pushed to the app's repository roll out as a new version. The release history shows which version is running and the state of each one, and you can go back to an earlier version. + ja: + title: 前の版に戻せる + body: リポジトリに入った変更は新しい版として公開されます。更新履歴で、いま動いている版と各版の状態を確かめ、前の版に戻せます。 +- screenshot: data + locales: + en: + title: Look after the app's data + body: Browse the app's tables, search them, and add, edit, or delete rows. + ja: + title: アプリのデータを直接扱える + body: アプリの表を一覧して検索し、行の追加・編集・削除ができます。 screenshots: - - name: home - file: home.webp - alt: - en: "Builder Console home in Japanese: a search box, cards for the sales team's apps (quotes, expenses, daily reports) with status badges such as running and needs attention, apps shared from a parent group, and new company-wide apps." - ja: "Builder Console のホーム画面。検索欄の下に営業1課のアプリ(見積作成・経費精算・日報)が「公開中」「問題あり」などの状態つきで並び、上位グループから共有されたアプリと全社の新着が続きます。" - - name: new-app - file: new-app.webp - alt: - en: "Step 1 of 4 of creating a new app: a text box describing an equipment-lending app with the Build with AI button, a Create from Excel option, and business templates such as inventory and master data management." - ja: "新しいアプリ作成のステップ 1 / 4。備品の貸し出しを記録したいという説明と「AI と作る」ボタン、「Excel から作る」、在庫管理やマスター管理などのテンプレートが表示されています。" - - name: app-overview - file: app-overview.webp - alt: - en: "The page of the quote app: running status and version v3, access limited to two people and two groups, owner, the app URL with copy and open buttons, tabs for overview, guide, data, release history, settings and logs, and app screenshots." - ja: "見積作成アプリのページ。「公開中」と動いている版 v3、公開範囲「限定(2 人・営業1課、営業企画部)」、担当者、アプリの URL とコピー・開くボタン、概要・つかいかた・データ・更新履歴・設定・ログのタブ、画面の写真が並びます。" - - name: share - file: share.webp - alt: - en: "The share dialog: two people with edit and view access, members of two groups with view access, and general access set to Group with the sales team and the sales planning department checked in the organisation tree." - ja: "共有ダイアログ。編集できる人と閲覧できる人の 2 人、閲覧できる 2 つのグループが並び、一般的なアクセスは「グループ」で、組織の一覧から営業1課と営業企画部が選ばれています。" - - name: releases - file: releases.webp - alt: - en: "The release history of the quote app, newest first: v4 waiting, v3 marked as currently running, and v2 and v1 completed, each with a button to go back to that version." - ja: "見積作成アプリの更新履歴(新しい順)。v4 は待機中、v3 に「いま動いている」、v2 と v1 は完了で、どちらにも「この版に戻す」ボタンがあります。" - - name: data - file: data.webp - alt: - en: "The data tab showing a product table with about 12 rows (laptops, monitors, stationery, furniture) with category, unit price, stock, and notes, plus a table search box and an Add row button." - ja: "データタブ。商品の表(約 12 行)にノートPC・モニター・文具・家具などが分類・単価・在庫・メモと一緒に並び、表の検索欄と「行を追加」ボタンがあります。" +- name: home + file: home.webp + alt: + en: 'Builder Console home in Japanese: a search box, cards for the sales team''s apps (quotes, expenses, daily reports) with status badges such as running and needs attention, apps shared from a parent group, and new company-wide apps.' + ja: Builder Console のホーム画面。検索欄の下に営業1課のアプリ(見積作成・経費精算・日報)が「公開中」「問題あり」などの状態つきで並び、上位グループから共有されたアプリと全社の新着が続きます。 +- name: new-app + file: new-app.webp + alt: + en: 'Step 1 of 4 of creating a new app: a text box describing an equipment-lending app with the Build with AI button, a Create from Excel option, and business templates such as inventory and master data management.' + ja: 新しいアプリ作成のステップ 1 / 4。備品の貸し出しを記録したいという説明と「AI と作る」ボタン、「Excel から作る」、在庫管理やマスター管理などのテンプレートが表示されています。 +- name: app-overview + file: app-overview.webp + alt: + en: 'The page of the quote app: running status and version v3, access limited to two people and two groups, owner, the app URL with copy and open buttons, tabs for overview, guide, data, release history, settings and logs, and app screenshots.' + ja: 見積作成アプリのページ。動いている版 v3、公開範囲「限定(2 人・営業1課、営業企画部)」、担当者、アプリの URL、6 つのタブ、画面の写真が並びます。 +- name: share + file: share.webp + alt: + en: 'The share dialog: two people with edit and view access, members of two groups with view access, and general access set to Group with the sales team and the sales planning department checked in the organisation tree.' + ja: 共有ダイアログ。編集できる人と閲覧できる人の 2 人、閲覧できる 2 つのグループが並び、一般的なアクセスは「グループ」で、組織の一覧から営業1課と営業企画部が選ばれています。 +- name: releases + file: releases.webp + alt: + en: 'The release history of the quote app, newest first: v4 waiting, v3 marked as currently running, and v2 and v1 completed, each with a button to go back to that version.' + ja: 見積作成アプリの更新履歴(新しい順)。v4 は待機中、v3 に「いま動いている」、v2 と v1 は完了で、どちらにも「この版に戻す」ボタンがあります。 +- name: data + file: data.webp + alt: + en: The data tab showing a product table with about 12 rows (laptops, monitors, stationery, furniture) with category, unit price, stock, and notes, plus a table search box and an Add row button. + ja: データタブ。商品の表(約 12 行)にノートPC・モニター・文具・家具などが分類・単価・在庫・メモと一緒に並び、表の検索欄と「行を追加」ボタンがあります。 diff --git a/hack/catalog-listing.ts b/hack/catalog-listing.ts index 12cc073..f615dda 100644 --- a/hack/catalog-listing.ts +++ b/hack/catalog-listing.ts @@ -60,8 +60,9 @@ const localeTag = /^[a-z]{2,3}$/ const dnsLabel = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/ const versionName = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/ +// Limits are UTF-8 bytes, as the Cloud store and the CRD measure them. function bound(value: string | undefined, max: number, what: string): void { - if (value !== undefined && value.length > max) throw new Error(`${what} exceeds ${max} characters`) + if (value !== undefined && Buffer.byteLength(value) > max) throw new Error(`${what} exceeds ${max} UTF-8 bytes`) } function localizedText(text: Record | undefined, max: number, what: string): void { @@ -70,7 +71,7 @@ function localizedText(text: Record | undefined, max: number, wh if (Object.keys(text).length > 16) throw new Error(`${what} has more than 16 locales`) for (const [tag, value] of Object.entries(text)) { if (!localeTag.test(tag)) throw new Error(`${what} locale "${tag}" must be a BCP 47 primary tag`) - if (!value || value.length > max) throw new Error(`${what} locale "${tag}" must be 1-${max} characters`) + if (!value || Buffer.byteLength(value) > max) throw new Error(`${what} locale "${tag}" must be 1-${max} UTF-8 bytes`) } } @@ -95,7 +96,7 @@ export function validateListing(listing: Listing): void { if (screenshots.length > 8) throw new Error('listing has more than 8 screenshots') const names = new Set() for (const media of screenshots) { - if (!dnsLabel.test(media.name) || media.name.length > 63 || names.has(media.name)) { + if (!dnsLabel.test(media.name) || Buffer.byteLength(media.name) > 63 || names.has(media.name)) { throw new Error(`screenshot name "${media.name}" is invalid or duplicated`) } names.add(media.name) @@ -113,7 +114,7 @@ export function validateListing(listing: Listing): void { if (!highlight.locales || !(FALLBACK_LOCALE in highlight.locales)) throw new Error(`highlight ${index} requires the "${FALLBACK_LOCALE}" locale`) if (Object.keys(highlight.locales).length > 16) throw new Error(`highlight ${index} has more than 16 locales`) for (const [tag, locale] of Object.entries(highlight.locales)) { - if (!localeTag.test(tag) || !locale.title || locale.title.length > 80 || (locale.body ?? '').length > 400) { + if (!localeTag.test(tag) || !locale.title || Buffer.byteLength(locale.title) > 80 || Buffer.byteLength(locale.body ?? '') > 400) { throw new Error(`highlight ${index} locale "${tag}" is invalid`) } }